Skip to content

Conversation

@jcar87
Copy link
Contributor

@jcar87 jcar87 commented Feb 4, 2026

Changelog: (Feature | Fix | Bugfix): TBC
Docs: TBC https://github.com/conan-io/docs/pull/XXXX

When linking executables, the GNU Linker expects to be able to find all indirect/transitive shared libraries - including fully private/implementation libraries. As documented in the linker:

--allow-shlib-undefined
--no-allow-shlib-undefined
Allows or disallows undefined symbols in shared libraries. This switch is similar to --no-undefined except that it determines the behaviour when the undefined symbols are in a shared library rather than a regular object file. It does not affect how undefined symbols in regular object files are handled.
The default behaviour is to report errors for any undefined symbols referenced in shared libraries if the linker is being used to create an executable, but to allow them if the linker is being used to create a shared library.

This behaviour is exclusive to this linker (otherwise known as ld.bfd). To my knowledge, this is not the case with gold, lld, mold, or the Darwin linker on macOS.

CMake will pass -rpath / -rpath-link when the libraries are modelled as targets and CMake knows their locations. This is a behaviour of CMake automatically. Meson has a similar behaviour.
Both -rpath and -rpath-link behave slightly differently.

  • -rpath is "stamped" in the resulting executable or library as an ELF dynamic tag entry (DT_RPATH or DT_RUNPATH), as well as being used to search for indirect shared library dependencies.
  • -rpath-link is only used at link time, not stamped into the resulting binaries.

Locations from both are searched, however, they behave differently when there is a sysroot that is NOT / - either implicitly or explicitly.

  • explicit: --sysroot is passed as a linker flag
  • implicit: gcc was built with its own sysroot (that is not /) - this is typical for cross-compilers, or for gcc toolchains built as cross-compilers
    In both cases, the linker will re-root the -rpath entries with the sysroot, so if we have -rpath /foo/bar and --sysroot /path/to/sysroot, the linker would search in /path/to/sysroot/foo/bar. If these rpath entries point to folders inside the Conan cache in the build machine, the libraries will never be found in those locations - so we must ensure that -rpath-link is passed correctly - those locations are NOT re-rooted.

We have issues in different toolchains:

  • CMakeDeps: CMake will -rpath correctly due to the Conan-generated target properties. This works great when NOT cross-building (or when cross-building and the sysroot is /), but does not work when there is a separate sysroot. This is a known defect of CMakeDeps, the newer CMakeConfigDeps does not have this issue.
  • MesonToolchain: much like CMake, meson passes -rpath/-rpath-link based on the information it has about the location of dependencies. However, newer versions of Meson have stopped passing -rpath-link.

This does not appear to be an issue with Autotools because in those cases, all transitive libraries are linked with -l, in which case the search is different (in -L rather than -rpath/-rpath-link).

Proposal:

  • To make life easier when (cross) building with a sysroot - add two confs that can be set in a profile, tools.cmake.cmaketoolchain:add_rpath_link" and tools.meson.mesontoolchain:add_rpath_link, where Conan will pass -rpath-link entries for library directories for all host dependencies in the graph.

  • Refer to the issue that supports this Pull Request.

  • If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.

  • I've read the Contributing guide.

  • I've followed the PEP8 style guides for Python code.

  • I've opened another PR in the Conan docs repo to the develop branch, documenting this one.

@pytest.mark.skipif(platform.system() != "Linux", reason="Linux/gcc required for -rpath/-rpath-link testing")
@pytest.mark.tool("cmake", "3.27")
@pytest.mark.parametrize("use_cmake_config_deps", [True, False])
def test_cmake_transitive_rpath_private_internal(use_cmake_config_deps):
Copy link
Member

Choose a reason for hiding this comment

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

This is the test for CMakeConfigDeps issue with the package interface target usage.

for req in host_req:
cppinfo = req.cpp_info.aggregated_components()
runtime_dirs.extend(cppinfo.libdirs)
# TODO: check if this handles spaces in paths correctly
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I need to visually check where and how these flags are passed - but it does work with path with spaces, since that's what the test is using

""")

def context(self):
add_rpath_link = self._toolchain.add_rpath_link or self._conanfile.conf.get("tools.build:add_rpath_link", check_type=bool)
Copy link
Member

Choose a reason for hiding this comment

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

Is it expected that the user adds checks? Like:

def generate(self):
    tc = CMakeToolchain(self)
    if self.settings.os == "Linux" and self.settings.compiler == "gcc":  # assume LD
         tc.add_rpath_link = True
    tc.generate()

Otherwise, this could break compilers/linkers by injecting an unknown -rpath-link to all compilers, including msvc for example?

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.

2 participants