Skip to content

SHARED axiolid_capi.so has no SONAME/IMPORTED_NO_SONAME: downstream consumer NEEDED entry is an unresolvable relative path #70

Description

@GeneralPawz

Problem

libaxiolid_capi.so (the SHARED Rust cdylib artifact) has no embedded SONAME, confirmed via readelf -d: no SONAME entry present. Rust's crate-type = ["cdylib"] does not set one by default, and neither native/CMakeLists.txt's axiolid_shared IMPORTED target nor the installed native/cmake/AxiolidTargets.cmake.in template sets IMPORTED_NO_SONAME to compensate.

Without a SONAME and without IMPORTED_NO_SONAME, CMake writes the resolved IMPORTED_LOCATION path into the linker's DT_NEEDED entry relativized to the consumer's build directory, not as an absolute path and not as a bare filename. Confirmed on a real downstream consumer build (IfcOpenShell's geometry_kernel_axiolid plugin, AXIOLID_LINKAGE=SHARED):

$ readelf -d ifcopenshell_geometry_kernel_axiolid.so | grep -E 'NEEDED.*axiolid|RUNPATH'
 0x0000000000000001 (NEEDED)  Shared library: [../../_deps/axiolid_source-build/cargo-target/release/libaxiolid_capi.so]
 0x000000000000001d (RUNPATH) Library runpath: [.../ifcgeom:.../ifcparse:.../plugin:.../cargo-target/release:]

Per ELF/glibc dynamic-linker semantics, a DT_NEEDED entry containing a / is loaded as a literal filesystem path and is never subject to RPATH/RUNPATH search -- RUNPATH only applies to bare (slash-free) sonames. The RUNPATH entry above is correct and complete, but it is never consulted, because the NEEDED entry itself is a relative path baked in at build time. The library therefore fails to load at runtime the moment the working directory differs from the exact relative offset recorded at link time (i.e. essentially always, outside the original build tree):

$ LD_LIBRARY_PATH=.../cargo-target/release ldd ifcopenshell_geometry_kernel_axiolid.so
	../../_deps/axiolid_source-build/cargo-target/release/libaxiolid_capi.so => not found

This affects every SHARED-linkage downstream consumer on Linux (and likely macOS, same ELF/Mach-O soname mechanics differ but the missing-IMPORTED_NO_SONAME root cause is platform-independent for the FetchContent/add_subdirectory consumption path). It does not affect STATIC linkage (no shared-object loading involved) and does not affect the packaged-archive install path if that path independently sets rpath correctly at install time -- this needs verification as part of the fix, since the same missing-SONAME condition applies to the installed artifact too.

Root cause

Two related gaps in native/CMakeLists.txt:

  1. The axiolid_shared IMPORTED target (add_library(axiolid_shared SHARED IMPORTED GLOBAL)) does not set IMPORTED_NO_SONAME TRUE, so CMake assumes the artifact has a real SONAME and treats IMPORTED_LOCATION as informational for copying, not as the literal value to embed in the consumer's NEEDED entry.
  2. The installed native/cmake/AxiolidTargets.cmake.in template's Axiolid::axiolid_shared target has the same gap.

Fix sketch

Set IMPORTED_NO_SONAME TRUE on both the build-tree axiolid_shared target and the installed Axiolid::axiolid_shared target. This tells CMake the artifact has no soname, so it must embed the actual resolved absolute path in DT_NEEDED instead of relativizing/soname-shortening it -- matching how CMake handles any other soname-less shared library.

Verification needed

  • A from-scratch downstream SHARED consumer build (already reachable via the ifcopenshell/axiolid_kernel work) with readelf -d confirming the NEEDED entry is either a bare filename resolvable via RUNPATH, or an absolute path -- not a relative path with embedded /.
  • ldd/actual dynamic load succeeding from a working directory other than the build tree.
  • Confirm the packaged-archive install path (scripts/package-native.py + AxiolidTargets.cmake.in) is unaffected or has the same fix applied.

Not in scope

The already-fixed native/CMakeLists.txt Ninja-generator BYPRODUCTS issue (#69) -- unrelated root cause, same file, land separately to keep the diffs reviewable independently.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

needs-triageFiled but not yet accepted, sized, or scheduled

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions