From 8241405ad42a8ce85c2a61baff5f96948f90cfa6 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Wed, 14 Oct 2015 19:54:03 +0000 Subject: [PATCH] [libcxx] Make it drastically simpler to link libc++. Summary: Currently on most platforms you have to manually link the c++ abi library used with libc++ whenever you use libc++. So your typical libc++ command like invocation might look like: ``` clang++ -stdlib=libc++ foo.cpp -lc++abi ``` Having to manually link `libc++abi.so` makes it harder for libc++ to be used generically. This patch fixes that by generating a linker script for `libc++.so` that correctly links the ABI library. On linux the linker script for libc++abi would look like: ``` # libc++.so INPUT(libc++.so.1 -lc++abi) ``` With the linker script you can now use libc++ using only `-stdlib=libc++`. This is the technique that is used on FreeBSD in ordered to link cxxrt and I think it's the best approach to make our users lives simpler. The CMake option used to enable this is `LIBCXX_ENABLE_ABI_LINKER_SCRIPT`. In future I would like to enable this by default on all platforms except for Darwin. Reviewers: mclow.lists, danalbert, rsmith, jroelofs, EricWF Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D12508 llvm-svn: 250319 --- libcxx/CMakeLists.txt | 20 +++++ libcxx/lib/CMakeLists.txt | 29 +++++++ libcxx/test/CMakeLists.txt | 5 +- .../utils/gen_link_script/gen_link_script.py | 79 +++++++++++++++++++ 4 files changed, 131 insertions(+), 2 deletions(-) create mode 100755 libcxx/utils/gen_link_script/gen_link_script.py diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt index e70f869a78f5..1d46e28528e7 100644 --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -69,6 +69,11 @@ set_property(CACHE LIBCXX_CXX_ABI PROPERTY STRINGS ;${CXXABIS}) option(LIBCXX_ENABLE_STATIC_ABI_LIBRARY "Statically link the ABI library" OFF) +# Generate and install a linker script inplace of libc++.so. The linker script +# will link libc++ to the correct ABI library. +option(LIBCXX_ENABLE_ABI_LINKER_SCRIPT + "Use and install a linker script for the given ABI library" OFF) + # Build libc++abi with libunwind. We need this option to determine whether to # link with libunwind or libgcc_s while running the test cases. option(LIBCXXABI_USE_LLVM_UNWINDER "Build and use the LLVM unwinder." OFF) @@ -153,6 +158,21 @@ if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY) endif() endif() +if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT) + if (APPLE) + message(FATAL_ERROR "LIBCXX_ENABLE_ABI_LINKER_SCRIPT cannot be used on APPLE targets") + endif() + if (NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR "LIBCXX_ENABLE_ABI_LINKER_SCRIPT requires python but it was not found.") + endif() +endif() + +if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY AND LIBCXX_ENABLE_ABI_LINKER_SCRIPT) + message(FATAL_ERROR "Conflicting options given. + LIBCXX_ENABLE_STATIC_ABI_LIBRARY cannot be specified with + LIBCXX_ENABLE_ABI_LINKER_SCRIPT") +endif() + #=============================================================================== # Configure System #=============================================================================== diff --git a/libcxx/lib/CMakeLists.txt b/libcxx/lib/CMakeLists.txt index 1a1c55b1927a..a3b50a7c4097 100644 --- a/libcxx/lib/CMakeLists.txt +++ b/libcxx/lib/CMakeLists.txt @@ -133,11 +133,40 @@ set_target_properties(cxx SOVERSION "${LIBCXX_ABI_VERSION}" ) +# Generate a linker script inplace of a libc++.so symlink. Rerun this command +# after cxx builds. +if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT) + # Get the name of the ABI library and handle the case where CXXABI_LIBNAME + # is a target name and not a library. Ex cxxabi_shared. + set(SCRIPT_ABI_LIBNAME "${CXXABI_LIBNAME}") + if (SCRIPT_ABI_LIBNAME STREQUAL "cxxabi_shared") + set("${SCRIPT_ABI_LIBNAME}" "c++abi") + endif() + # Generate a linker script inplace of a libc++.so symlink. Rerun this command + # after cxx builds. + add_custom_command(TARGET cxx POST_BUILD + COMMAND + ${PYTHON_EXECUTABLE} ${LIBCXX_SOURCE_DIR}/utils/gen_link_script/gen_link_script.py + ARGS + "$" + "${SCRIPT_ABI_LIBNAME}" + WORKING_DIRECTORY ${LIBCXX_BUILD_DIR} + ) +endif() + + if (LIBCXX_INSTALL_LIBRARY) install(TARGETS cxx LIBRARY DESTINATION lib${LIBCXX_LIBDIR_SUFFIX} COMPONENT libcxx ARCHIVE DESTINATION lib${LIBCXX_LIBDIR_SUFFIX} COMPONENT libcxx ) + # NOTE: This install command must go after the cxx install command otherwise + # it will not be executed after the library symlinks are installed. + if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT) + install(FILES "$" + DESTINATION lib${LIBCXX_LIBDIR_SUFFIX} + COMPONENT libcxx) + endif() endif() if (NOT CMAKE_CONFIGURATION_TYPES AND (LIBCXX_INSTALL_LIBRARY OR diff --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt index 2e2cf61f111a..b67b3b43f83e 100644 --- a/libcxx/test/CMakeLists.txt +++ b/libcxx/test/CMakeLists.txt @@ -17,10 +17,11 @@ pythonize_bool(LIBCXX_GENERATE_COVERAGE) pythonize_bool(LIBCXXABI_USE_LLVM_UNWINDER) # The tests shouldn't link to any ABI library when it has been linked into -# libc++ statically. -if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY) +# libc++ statically or via a linker script. +if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY OR LIBCXX_ENABLE_ABI_LINKER_SCRIPT) set(LIBCXX_CXX_ABI_LIBNAME "none") endif() + set(LIBCXX_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING "TargetInfo to use when setting up test environment.") set(LIBCXX_EXECUTOR "None" CACHE STRING diff --git a/libcxx/utils/gen_link_script/gen_link_script.py b/libcxx/utils/gen_link_script/gen_link_script.py new file mode 100755 index 000000000000..5de18f9129c6 --- /dev/null +++ b/libcxx/utils/gen_link_script/gen_link_script.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +import os +import sys + +def print_and_exit(msg): + sys.stderr.write(msg + '\n') + sys.exit(1) + +def usage_and_exit(): + print_and_exit("Usage: ./gen_link_script.py [--help] [--dryrun] ") + +def help_and_exit(): + help_msg = \ +"""Usage + + gen_link_script.py [--help] [--dryrun] + + Generate a linker script that links libc++ to the proper ABI library. + The script replaces the specified libc++ symlink. + An example script for c++abi would look like "INPUT(libc++.so.1 -lc++abi)". + +Arguments + - The top level symlink to the versioned libc++ shared + library. This file is replaced with a linker script. + - The name of the ABI library to use in the linker script. + The name must be one of [c++abi, stdc++, supc++, cxxrt]. + +Exit Status: + 0 if OK, + 1 if the action failed. +""" + print_and_exit(help_msg) + +def parse_args(): + args = list(sys.argv) + del args[0] + if len(args) == 0: + usage_and_exit() + if args[0] == '--help': + help_and_exit() + dryrun = '--dryrun' == args[0] + if dryrun: + del args[0] + if len(args) != 2: + usage_and_exit() + symlink_file = args[0] + abi_libname = args[1] + return dryrun, symlink_file, abi_libname + +def main(): + dryrun, symlink_file, abi_libname = parse_args() + + # Check that the given libc++.so file is a valid symlink. + if not os.path.islink(symlink_file): + print_and_exit("symlink file %s is not a symlink" % symlink_file) + + # Read the symlink so we know what libc++ to link to in the linker script. + linked_libcxx = os.readlink(symlink_file) + + # Check that the abi_libname is one of the supported values. + supported_abi_list = ['c++abi', 'stdc++', 'supc++', 'cxxrt'] + if abi_libname not in supported_abi_list: + print_and_exit("abi name '%s' is not supported: Use one of %r" % + (abi_libname, supported_abi_list)) + + # Generate the linker script contents and print the script and destination + # information. + contents = "INPUT(%s -l%s)" % (linked_libcxx, abi_libname) + print("GENERATING SCRIPT: '%s' as file %s" % (contents, symlink_file)) + + # Remove the existing libc++ symlink and replace it with the script. + if not dryrun: + os.unlink(symlink_file) + with open(symlink_file, 'w') as f: + f.write(contents + "\n") + + +if __name__ == '__main__': + main()