From a2e6306c272f514232dcccc2e49d9360b50b59f9 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Thu, 13 Aug 2026 11:05:56 -0400 Subject: [PATCH] GenEx/LINK_LIBRARY: Add NEEDED_LIBRARY support on Linux --- .../dev/link-library-needed-library-linux.rst | 6 +++++ .../LINK_LIBRARY_PREDEFINED_FEATURES.rst | 13 ++++++++++ Modules/Platform/Linker/GNU.cmake | 25 +++++++++++++++++++ Modules/Platform/Linker/Linux-GNU.cmake | 3 +++ .../RunCMakeTest.cmake | 5 ++++ .../linux_library.cmake | 10 ++++++++ .../linux_library_external.cmake | 4 +++ 7 files changed, 66 insertions(+) create mode 100644 Help/release/dev/link-library-needed-library-linux.rst create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library.cmake create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library_external.cmake diff --git a/Help/release/dev/link-library-needed-library-linux.rst b/Help/release/dev/link-library-needed-library-linux.rst new file mode 100644 index 0000000000..cd601b4a64 --- /dev/null +++ b/Help/release/dev/link-library-needed-library-linux.rst @@ -0,0 +1,6 @@ +link-library-needed-library-linux +---------------------------------- + +* The :genex:`$` generator expression feature + is now supported on Linux with GNU-compatible linkers (GNU ld, gold, LLD, + MOLD). diff --git a/Help/variable/include/LINK_LIBRARY_PREDEFINED_FEATURES.rst b/Help/variable/include/LINK_LIBRARY_PREDEFINED_FEATURES.rst index 959a3077a4..38f06e60fe 100644 --- a/Help/variable/include/LINK_LIBRARY_PREDEFINED_FEATURES.rst +++ b/Help/variable/include/LINK_LIBRARY_PREDEFINED_FEATURES.rst @@ -101,6 +101,19 @@ Uses the ``-needed_library`` or ``-needed-l`` option as appropriate, with the same linker constraints as ``NEEDED_FRAMEWORK``. + Linux + .. versionadded:: 4.5 + + Wraps the library with ``--no-as-needed`` / ``--as-needed`` to force + an entry in the binary's ``DT_NEEDED`` list regardless of whether any + symbols from the library are directly referenced. When the linker + supports ``--push-state`` / ``--pop-state`` (GNU ld >= 2.25, gold, + LLD, MOLD), the state-stacking form is used instead to correctly + restore the pre-existing ``--as-needed`` state rather than + unconditionally enabling it after the library. This feature is only + meaningful for shared libraries; applying it to a static library + produces a developer warning and has no effect. + ``REEXPORT_LIBRARY`` This is similar to the ``REEXPORT_FRAMEWORK`` feature, except it is for use with non-framework targets or libraries (Apple platforms only). diff --git a/Modules/Platform/Linker/GNU.cmake b/Modules/Platform/Linker/GNU.cmake index 0929f92be1..fec47ea7f0 100644 --- a/Modules/Platform/Linker/GNU.cmake +++ b/Modules/Platform/Linker/GNU.cmake @@ -57,6 +57,31 @@ function(__cmake_set_whole_archive_feature __linker __linker_options) set(CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED "${CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED}" PARENT_SCOPE) endfunction() +# NEEDED_LIBRARY Feature for LINK_LIBRARY generator expression +## Force a shared library into DT_NEEDED even when no symbols are referenced +function(__cmake_set_needed_library_feature __linker __linker_options) + unset(__lang) + if(ARGC EQUAL "3") + set(__lang "${ARGV2}_") + endif() + + __cmake_check_linker_pushpop_state("${__linker}" "${__linker_options}" ${ARGV2}) + + ## NEEDED_LIBRARY: Force DT_NEEDED entry for a shared library + if(CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED) + set(CMAKE_${__lang}LINK_LIBRARY_USING_NEEDED_LIBRARY "LINKER:--push-state,--no-as-needed" + "" + "LINKER:--pop-state" PARENT_SCOPE) + else() + set(CMAKE_${__lang}LINK_LIBRARY_USING_NEEDED_LIBRARY "LINKER:--no-as-needed" + "" + "LINKER:--as-needed" PARENT_SCOPE) + endif() + set(CMAKE_${__lang}LINK_LIBRARY_USING_NEEDED_LIBRARY_SUPPORTED TRUE PARENT_SCOPE) + set(CMAKE_${__lang}LINK_LIBRARY_NEEDED_LIBRARY_ATTRIBUTES LIBRARY_TYPE=SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT PARENT_SCOPE) + set(CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED "${CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED}" PARENT_SCOPE) +endfunction() ## Configure system linker __cmake_set_whole_archive_feature("${CMAKE_LINKER}" "") +__cmake_set_needed_library_feature("${CMAKE_LINKER}" "") diff --git a/Modules/Platform/Linker/Linux-GNU.cmake b/Modules/Platform/Linker/Linux-GNU.cmake index ed953bde41..716c97bc97 100644 --- a/Modules/Platform/Linker/Linux-GNU.cmake +++ b/Modules/Platform/Linker/Linux-GNU.cmake @@ -13,5 +13,8 @@ macro(__linux_linker_gnu lang) __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" "${lang}") + __cmake_set_needed_library_feature("${CMAKE_${lang}_COMPILER_LINKER}" + "${CMAKE_${lang}_COMPILER_LINKER_ARCHITECTURE_FLAGS}" + "${lang}") endif() endmacro() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake index 7a4d202f79..6a831cfac2 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake @@ -123,6 +123,11 @@ if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "12") run_cmake_target(apple_library needed_library main-needed_library) endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")) + run_cmake(linux_library_external) + run_cmake_target(linux_library_external build external) + run_cmake_with_options(linux_library "-DRunCMake_BINARY_DIR=${RunCMake_BINARY_DIR}") + run_cmake_target(linux_library needed_library main-needed_library) endif() # WHOLE_ARCHIVE feature diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library.cmake new file mode 100644 index 0000000000..fe79314f65 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library.cmake @@ -0,0 +1,10 @@ + +enable_language(C) + +add_library(lib SHARED base.c lib.c) + +# feature NEEDED_LIBRARY +add_executable(main-needed_library main.c) +target_link_directories(main-needed_library PRIVATE "${RunCMake_BINARY_DIR}/linux_library_external-build" + "${RunCMake_BINARY_DIR}/linux_library_external-build/$") +target_link_libraries(main-needed_library PRIVATE "$") diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library_external.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library_external.cmake new file mode 100644 index 0000000000..f5a566f434 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/linux_library_external.cmake @@ -0,0 +1,4 @@ + +enable_language(C) + +add_library(external SHARED unref.c)