Skip to content

Add an option to link a static wolfSSL into libwolfpkcs11 - #258

Merged
LinuxJedi merged 5 commits into
wolfSSL:masterfrom
Frauschi:static-wolfssl
Oct 19, 2026
Merged

LinuxJedi merged 5 commits into
wolfSSL:masterfrom
Frauschi:static-wolfssl

Conversation

@Frauschi

Copy link
Copy Markdown
Member

Summary

Adds a build option that links a static wolfSSL into libwolfpkcs11.so, so the module ships as one library with no run-time dependency on libwolfssl. The embedded wolfSSL is hidden, so an application that uses its own wolfSSL, even one built with a different configuration, can still load the module through dlopen() or wc_Pkcs11_Initialize().

  • Autotools: --with-static-wolfssl=/path/to/libwolfssl.a
  • CMake: -DWOLFPKCS11_STATIC_WOLFSSL=yes against a wolfSSL built with -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON

The default build is unchanged.

How it works

  • The library is linked with --exclude-libs,ALL, so none of the wolfSSL symbols are exported. Without it, the module's wolfSSL calls bind to the application's libwolfssl.so despite RTLD_LOCAL, which crashes when the two configurations differ.
  • -Bsymbolic binds the module's own C_* calls to itself. -z text and -z defs turn a non-PIC archive or a function missing from the archive into a link error, instead of a module that quietly resolves against the host's wolfSSL.
  • The option needs an ELF linker and a static wolfSSL, and builds only the shared library. It rejects wolfTPM, because libwolftpm links its own wolfSSL and the TPM crypto callback would be registered in the wrong copy.
  • In this mode the installed CMake package and wolfpkcs11-config no longer refer to wolfSSL.

Fixes found along the way

  • CMake exported about 200 internal symbols. The autotools build compiles with hidden visibility and the CMake build did not. CMake now builds with hidden visibility and defines HAVE_VISIBILITY.
  • The PKCS#11 v3.0/v3.2 functions were not exported by the autotools build (C_LoginUser, C_Message*, C_EncapsulateKey, ...), because their prototypes lacked WP11_API. Both builds now export the same 92 C_* functions and nothing else.
  • CMake debug_test exits 77 in non-debug builds, and CTest counted that as a failure. It is now registered with skip code 77.
  • The autotools CMake package pointed at NONE/include when configure ran without --prefix, so find_package(wolfpkcs11) failed. The install paths are now expanded with the default prefix.
  • The wolfSSL interop test never called wolfCrypt_Init() and only worked because the module did it on the shared libwolfcrypt. It now initializes wolfCrypt itself, loads the module only through wc_Pkcs11_Initialize(), and takes the module to test from WOLFPKCS11_MODULE. The debug probe also no longer unloads the module before the test loads it, so a debug module logs again.

Tests

  • New static-wolfssl.yml workflow with an autotools job and a CMake job. Each builds against a static wolfSSL and checks that the library has no libwolfssl dependency and no text relocations, and that it exports only C_* (including the v3.0 entry points). It then runs the test suite and the wolfSSL interop test against the library, installs it, and builds and runs a small find_package(wolfpkcs11) consumer with no wolfSSL on the search path.
  • Eleven tests set allocators with wolfSSL_SetAllocators(). In static mode that call reaches the test's wolfSSL, not the module's, so these tests are built but not run there. The default build still runs them.
  • The interop test target refuses an external WOLFPKCS11_MODULE that depends on libwolfssl. The check uses readelf and is skipped for the default in-tree module, so the default interop test still runs without binutils.

@Frauschi Frauschi self-assigned this Oct 19, 2026
Copilot AI balanced review requested due to automatic review settings October 19, 2026 11:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cryptographic symbol isolation and cross-build packaging require human validation of linker compatibility and interoperability results.

No open issues 🎉

What changed in this PR

Adds optional static wolfSSL embedding so libwolfpkcs11 can operate without a runtime libwolfssl dependency.

Changes:

  • Adds Autotools and CMake options with symbol isolation and dependency-free package metadata.
  • Corrects public symbol visibility and interoperability test initialization.
  • Adds CI coverage for static embedding and installed-package consumers.
File Description
wolfpkcs11/​pkcs11.h Exports v3.0 and v3.2 functions.
tests/​wolfssl-interoperability/​pkcs11_interop.c Initializes wolfCrypt and preserves the module handle.
tests/​wolfssl-interoperability/​Makefile Supports external modules and checks dependencies.
tests/​include.am Excludes allocator-sensitive tests in embedded mode.
src/​include.am Adds isolation flags and archive dependency.
README.md Documents static embedding requirements and usage.
configure.ac Adds static embedding configuration and fixes install paths.
CMakeLists.txt Adds embedding, hidden visibility, and debug-test skipping.
cmake/​wolfpkcs11Targets.cmake.in Omits the embedded wolfSSL link dependency.
cmake/​wolfpkcs11Config.cmake.in Skips wolfSSL discovery for embedded builds.
cmake/​consumer/​main.c Checks installed API access.
cmake/​consumer/​CMakeLists.txt Builds an installed-package consumer.
.github/​workflows/​static-wolfssl.yml Tests both build systems, isolation, and packaging.

Review effort: Balanced

The autotools build compiles with -fvisibility=hidden and gets
HAVE_VISIBILITY from gl_VISIBILITY, so WP11_LOCAL functions stay out of
the dynamic symbol table. The CMake build set neither, so
libwolfpkcs11.so exported about 200 internal functions next to the
PKCS#11 API. Build the library with hidden visibility on GCC and Clang
and define HAVE_VISIBILITY.

The PKCS#11 v3.0 and v3.2 functions were declared without WP11_API, so
hidden visibility would drop them, and the autotools build did not
export them either. Declare them with WP11_API so both builds export
the whole C_* API and nothing else.

Also register debug_test with skip code 77. It exits 77 when
DEBUG_WOLFPKCS11 is not defined, and CTest counted that as a failure, so
ctest failed on every non-debug CMake build.
The test never called wolfCrypt_Init() itself. It only worked because
the module, linked against the same libwolfcrypt, called
wolfCrypt_Init() in C_Initialize and so set up the crypto callback table
on the test's behalf. Against a module with its own wolfSSL,
wc_CryptoCb_RegisterDevice() fails with BUFFER_E. Initialize and clean
up wolfCrypt in main().

The test binary no longer links the module, which it only loads through
wc_Pkcs11_Initialize(), and WOLFPKCS11_MODULE selects the module to
test, defaulting to the one the Makefile builds.

The debug probe now keeps its handle open until cleanup. Closing it
right away unloaded the module whenever nothing else held it, which
reset the debug flag before wc_Pkcs11_Initialize() loaded the module
again, so the debug build of the module never logged.

The test and debug targets refuse a module that depends on libwolfssl.
Its wolfSSL calls would bind to the test's own wolfCrypt build, which
has a different configuration, and crash.
--with-static-wolfssl=FILE (autotools) and WOLFPKCS11_STATIC_WOLFSSL
(CMake) link a static, position independent libwolfssl into the shared
library, so it has no run time dependency on libwolfssl. Both need an
ELF linker, accept only a static wolfSSL and build only the shared
library.

The wolfSSL symbols are hidden with --exclude-libs,ALL. Without that the
embedded wolfSSL exports its whole API, and in a process that already
has a libwolfssl.so, such as a wolfSSL application loading the module
through wc_Pkcs11_Initialize(), the module's calls bind to the
application's copy even though it is loaded with RTLD_LOCAL. With a
different wolfSSL configuration that crashes. -Bsymbolic also binds the
module's own C_* functions to itself, and -z text turns text relocations
from an archive built without PIC into a link error. -z defs does the
same for a wolfSSL function missing from the archive, which the loader
would otherwise resolve against the application's libwolfssl. It also
rules out sanitizer builds whose runtime is not linked into shared
objects, such as clang's AddressSanitizer.

The archive comes first in LIBS, so libraries it needs can follow it,
and the library is relinked when the archive changes. The installed
CMake package and the wolfpkcs11-config script no longer refer to
wolfssl in this mode.

wolfTPM support is rejected in this mode, because libwolftpm links its
own wolfSSL and the TPM crypto callback would be registered there rather
than in the copy the module uses.

Eleven tests set allocators with wolfSSL_SetAllocators() to inject
allocation failures, track allocations or inspect freed memory. In this
mode that call configures the test's own wolfSSL rather than the
module's, so these tests are not run. The default build still runs them.

A new workflow builds both variants against a static wolfSSL, checks
that the library has no libwolfssl dependency or text relocations and
exports only C_* functions, including the v3.0 ones, then runs the tests
and the wolfSSL interop test against it.
configure expands libdir and includedir into WOLFPKCS11_LIBDIR_ABS and
WOLFPKCS11_INCLUDEDIR_ABS for the installed wolfpkcs11Targets.cmake. It
did so while prefix and exec_prefix still held NONE, because autoconf
only applies the default prefix when it writes config.status. Without
--prefix the package therefore pointed at NONE/lib and NONE/include, and
find_package(wolfpkcs11) failed on the missing include directory.
Expand them with the default prefix in place and restore both variables
afterwards.
Nothing in CI installed wolfPKCS11 or used the installed package, so the
static-mode changes to the CMake package files and wolfpkcs11-config,
and the expanded install paths, were never exercised.

Both jobs now install the library and build a small
find_package(wolfpkcs11) project from cmake/consumer with no wolfSSL on
the search path, then run it. The autotools job installs to the default
prefix, which covers the install paths written into the package, and
checks that wolfpkcs11-config --libs no longer names wolfssl.

@LinuxJedi LinuxJedi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call on the pkcs11.h fixes too!

@LinuxJedi
LinuxJedi merged commit 07ee752 into wolfSSL:master Oct 19, 2026
99 checks passed
@Frauschi
Frauschi deleted the static-wolfssl branch October 19, 2026 13:12
LinuxJedi added a commit that referenced this pull request Oct 21, 2026
* Set the version to 2.2.0 in configure.ac, CMakeLists.txt, the Doxyfiles
  and version.h, and add a Debian changelog entry.
* Bump the shared library version to 4.1.0, as the autotools build now
  exports the PKCS#11 v3.0 and v3.2 functions (PR #258).
* Add the 2.2 release notes to README.md, covering PR #203 through
  PR #259.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants