Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -67,6 +67,11 @@ 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) @@ -149,6 +154,21 @@ 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 #=============================================================================== Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -133,11 +133,39 @@ SOVERSION "1" ) +# 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 Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -23,10 +23,11 @@ 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 Index: utils/gen_link_script/gen_link_script.py =================================================================== --- /dev/null +++ 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()