diff --git a/llvm/tools/llvm-libgcc/CMakeLists.txt b/llvm/tools/llvm-libgcc/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libgcc/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.13.4) + +# If we are not building as a part of LLVM, build llvm-libgcc as a standalone +# project, using compiler-rt and libunwind external libraries. +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(LLVM_LIBGCC) + if(NOT LLVM_LIBGCC_LIBUNWIND_DIR) + message(FATAL_ERROR + "We're building llvm-libgcc as a standalone project, but we didn't " + " supply a path to LLVM_LIBGCC_LIBUNWIND_DIR. It's not possible to build " + "llvm-libgcc without this path!") + endif() +elseif(NOT LLVM_ENABLE_LIBGCC) + return() +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") + +set(LLVM_LIBGCC_SYSROOT "${CMAKE_INSTALL_PREFIX}" CACHE PATH + "Assumes this path as the root directory when setting symlinks.") + +set(LLVM_LIBGCC_COMPILER_RT_DIR "" CACHE PATH + "Absolute path to a pre-existing compiler-rt builtins.") +find_package(CompilerRT REQUIRED) +string(REGEX REPLACE "^${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/*" "" + LLVM_LIBGCC_COMPILER_RT "${LLVM_LIBGCC_COMPILER_RT}") + +set(LLVM_LIBGCC_LIBUNWIND_DIR "" CACHE PATH + "Absolute path to a pre-existing libunwind.a.") +find_package(LLVMLibunwind REQUIRED) +string(REGEX REPLACE "^${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/*" "" + LLVM_LIBGCC_UNWIND_STATIC "${LLVM_LIBGCC_UNWIND_STATIC}") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib${LLVMLIB_DIR_SUFFIX}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib${LLVMLIB_DIR_SUFFIX}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib${LLVMLIB_DIR_SUFFIX}") + +add_subdirectory(lib) diff --git a/llvm/tools/llvm-libgcc/FindCompilerRT.cmake b/llvm/tools/llvm-libgcc/FindCompilerRT.cmake new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libgcc/FindCompilerRT.cmake @@ -0,0 +1,101 @@ +include(FindPackageHandleStandardArgs) +include(CheckLibraryExists) + +function(building_compiler_rt) + if(LLVM_LIBGCC_COMPILER_RT_DIR) + return() + endif() + + list(FIND LLVM_ENABLE_PROJECTS compiler-rt LLVM_LIBGCC_HAS_COMPILER_RT) + if(NOT LLVM_LIBGCC_HAS_COMPILER_RT) + return() + endif() + + if(NOT COMPILER_RT_BUILD_BUILTINS) + message(SEND_ERROR + "When building llvm-libgcc alongside compiler-rt, we need to set " + "-DCOMPILER_RT_BUILD_BUILTINS=On so we can export all the necessary " + "symbols.") + return() + endif() + + if(NOT COMPILER_RT_BUILTINS_HIDE_SYMBOLS) + message(SEND_ERROR + "When building llvm-libgcc alongside compiler-rt, we need to set " + "-DCOMPILER_RT_BUILTINS_HIDE_SYMBOLS=Off so we can export all the " + "necessary symbols.") + return() + endif() + + set(LLVM_LIBGCC_COMPILER_RT builtins) + set(CompilerRT_FOUND ON) + message(STATUS "llvm-libgcc is using in-tree compiler-rt") +endfunction() + +function(find_external_compiler_rt) + if(NOT ${CMAKE_CHECK_CXX_COMPILER_ID} MATCHES Clang AND + NOT LLVM_LIBGCC_COMPILER_RT_DIR) + message(STATUS + "When building llvm-libgcc without compiler-rt (e.g. as a standalone " + "project), we either need to be using Clang, or " + "'LLVM_LIBGCC_COMPILER_RT_INSTALL_DIRECTORY' must be set to a directory.") + return() + endif() + + if(LLVM_LIBGCC_COMPILER_RT_DIR) + set(LLVM_LIBGCC_PATH_TO_RTLIB + "${LLVM_LIBGCC_COMPILER_RT_DIR}/libclang_rt.builtins.a") + + check_library_exists("${LLVM_LIBGCC_PATH_TO_RTLIB}" emutls_key_destructor + LLVM_LIBGCC_HAS_EXTERNAL_RTLIB) + if (NOT LLVM_LIBGCC_HAS_EXTERNAL_RTLIB) + message(SEND_ERROR + "When using LLVM_LIBGCC_COMPILER_RT_DIR='${LLVM_LIBGCC_COMPILER_RT_DIR}', " + "we couldn't path '${LLVM_LIBGCC_PATH_TO_RTLIB}'. There could be a few " + "reasons for this (e.g. typo in path or wrong permissions for file), " + "and a manual inspection is necessary.") + return() + endif() + + set(LLVM_LIBGCC_COMPILER_RT "${LLVM_LIBGCC_PATH_TO_RTLIB}" PARENT_SCOPE) + return() + endif() + + execute_process(COMMAND "${CMAKE_CXX_COMPILER}" + ${CMAKE_CXX_FLAGS} + --rtlib=compiler-rt + --print-libgcc-file-name + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LLVM_LIBGCC_COMPILER_RT) + string(STRIP "${LLVM_LIBGCC_COMPILER_RT}" LLVM_LIBGCC_COMPILER_RT) + + if(HAD_ERROR) + message(STATUS "llvm-libgcc unable to find compiler-rt builtins: got return code ${HAD_ERROR}") + unset(LLVM_LIBGCC_COMPILER_RT) + return() + endif() + + if(NOT EXISTS "${LLVM_LIBGCC_COMPILER_RT}") + message(STATUS "llvm-libgcc unable to find path '${LLVM_LIBGCC_COMPILER_RT}'") + unset(LLVM_LIBGCC_COMPILER_RT) + endif() + + set(LLVM_LIBGCC_COMPILER_RT "${LLVM_LIBGCC_COMPILER_RT}" PARENT_SCOPE) +endfunction() + +set(CompilerRT_FOUND OFF) +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + building_compiler_rt() +endif() + +if(NOT CompilerRT_FOUND) + find_external_compiler_rt() + if(LLVM_LIBGCC_COMPILER_RT) + string(REGEX REPLACE "^${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/*" "" + LLVM_LIBGCC_COMPILER_RT "${LLVM_LIBGCC_COMPILER_RT}") + message(STATUS "llvm-libgcc is using a pre-installed compiler-rt: ${LLVM_LIBGCC_COMPILER_RT}") + set(CompilerRT_FOUND ON) + endif() +endif() + +find_package_handle_standard_args(CompilerRT REQUIRED_VARS CompilerRT_FOUND) diff --git a/llvm/tools/llvm-libgcc/FindLLVMLibunwind.cmake b/llvm/tools/llvm-libgcc/FindLLVMLibunwind.cmake new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libgcc/FindLLVMLibunwind.cmake @@ -0,0 +1,56 @@ +include(FindPackageHandleStandardArgs) + +function(building_libunwind) + list(FIND LLVM_ENABLE_PROJECTS libunwind LLVM_LIBGCC_HAS_LIBUNWIND) + if(NOT LLVM_LIBGCC_HAS_LIBUNWIND) + return() + endif() + + if(NOT LIBUNWIND_USE_COMPILER_RT) + message(SEND_ERROR + "When we're building llvm-libgcc alongside libunwind, we need to set " + "'-DLIBUNWIND_USE_COMPILER_RT=On'.") + endif() + + if(LIBUNWIND_INSTALL_SHARED_LIBRARY) + message(SEND_ERROR + "When we're building llvm-libgcc alongside libunwind, llvm-libgcc will " + "be the owner of libunwind.so. To prevent the projects from clashing, we " + "need to set '-DLIBUNWIND_INSTALL_SHARED_LIBRARY=Off'.") + endif() + + if(NOT (LIBUNWIND_SUPPORTS_FNO_EXCEPTIONS_FLAG AND + LIBUNWIND_SUPPORTS_FUNWIND_TABLES_FLAG)) + message(SEND_ERROR + "Compiler doesn't support generation of unwind tables if exception " + "support is disabled. Building libunwind DSO with runtime dependency " + "on C++ ABI library is not supported.") + endif() + + set(LLVM_LIBGCC_UNWIND_STATIC unwind_static PARENT_SCOPE) + set(LLVMLibunwind_FOUND ON PARENT_SCOPE) + message(STATUS "llvm-libgcc is using in-tree libunwind") +endfunction() + +function(find_external_libunwind) + if (NOT EXISTS "${LLVM_LIBGCC_LIBUNWIND_DIR}/libunwind.a") + message(SEND_ERROR + "When using LLVM_LIBGCC_LIBUNWIND_DIR='${LLVM_LIBGCC_LIBUNWIND_DIR}', we " + "couldn't find '${LLVM_LIBGCC_LIBUNWIND_DIR}/libunwind.a'. There could " + "be a few reasons for this (e.g. typo in path or wrong permissions for " + "file), and a manual inspection is necessary.") + endif() + + set(LLVM_LIBGCC_UNWIND_STATIC "${LLVM_LIBGCC_LIBUNWIND_DIR}/libunwind.a" PARENT_SCOPE) + set(LLVMLibunwind_FOUND ON PARENT_SCOPE) +endfunction() + +set(LLVMLibunwind_FOUND OFF) +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + building_libunwind() +else() + find_external_libunwind() + message(STATUS "llvm-libgcc is using a pre-built libunwind: ${LLVM_LIBGCC_UNWIND_STATIC}") +endif() + +find_package_handle_standard_args(LLVMLibunwind REQUIRED_VARS LLVMLibunwind_FOUND) diff --git a/llvm/tools/llvm-libgcc/docs/LLVMLibgcc.rst b/llvm/tools/llvm-libgcc/docs/LLVMLibgcc.rst new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libgcc/docs/LLVMLibgcc.rst @@ -0,0 +1,263 @@ +.. llvm-libgcc: + +=========== +llvm-libgcc +=========== + +.. contents:: + :local: + +**Note that these instructions assume a Linux and bash-friendly environment. +YMMV if you’re on a non Linux-based platform.** + +.. _introduction: + +Motivation +============ + +Enabling libunwind as a replacement for libgcc on Linux has proven to be +challenging since libgcc_s.so is a required dependency in the [Linux standard +base][5]. Some software is transitively dependent on libgcc because glibc makes +hardcoded calls to functions in libgcc_s. For example, the function +``__GI___backtrace`` eventually makes its way to a [hardcoded dlopen to libgcc_s' +_Unwind_Backtrace][1]. Since libgcc_{eh.a,s.so} and libunwind have the same ABI, +but different implementations, the two libraries end up [cross-talking, which +ultimately results in a segfault][2]. + +To solve this problem, libunwind needs libgcc "front" that is, link the +necessary functions from compiler-rt and libunwind into an archive and shared +object that advertise themselves as ``libgcc.a``, ``libgcc_eh.a``, and +``libgcc_s.so``, so that glibc’s baked calls are diverted to the correct objects +in memory. Fortunately for us, compiler-rt and libunwind use the same ABI as the +libgcc family, so the problem is solvable at the llvm-project configuration +level: no program source needs to be edited. Thus, the end result is for a +distro manager to configure their LLVM build with a flag that indicates they +want to archive compiler-rt/unwind as libgcc. We achieve this by compiling +libunwind with all the symbols necessary for compiler-rt to emulate the libgcc +family, and then generate symlinks named for our "libgcc" that point to their +corresponding libunwind counterparts. + +.. _alternatives + +Alternatives +============ + +We alternatively considered patching glibc so that the source doesn't directly +refer to libgcc, but rather _defaults_ to libgcc, so that a system preferring +compiler-rt/libunwind can point to these libraries at the config stage instead. +Even if we modified the Linux standard base, this alternative won't work because +binaries that are built using libgcc will still end up having cross-talk between +the differing implementations. + +.. _target audience: + +Target audience +=============== + +llvm-libgcc is not for the casual LLVM user. It is intended to be used by distro +managers who want to replace libgcc with compiler-rt and libunwind, but cannot +fully abandon the libgcc family (e.g. because they are dependent on glibc). Such +managers must have worked out their compatibility requirements ahead of using +llvm-libgcc. + +.. _cmake options: + +CMake options +============= + +.. option:: `LLVM_LIBGCC_TARGET` + + **Required** + + The target for which llvm-libgcc is being built. This can't be inferred from + ``LLVM_TARGETS_TO_BUILD``, and presently only supports one target at a time. + +.. option:: `LLVM_LIBGCC_SYSROOT` + + **Default value**: ``${CMAKE_INSTALL_PREFIX}`` + + Transforms an absolute path into a relative path starting from the specified + directory. This variable is only used when performing an out-of-tree build. + +.. option:: `LLVM_LIBGCC_COMPILER_RT_INSTALL_DIRECTORY` + + Path to compiler-rt. Overrides building compiler-rt in-tree, and is required + when building out-of-tree when ``CMAKE_CXX_COMPILER`` isn't Clang. + +The following CMake options are not used by llvm-libgcc but are required when +building in-tree. + +.. option:: `COMPILER_RT_BUILD_BUILTINS=On` + +.. option:: `COMPILER_RT_BUILTINS_HIDE_SYMBOLS=Off` + + Necessary for exposing symbols that libgcc_s exposes, but compiler-rt doesn't + by default. This variable should be set when building libunwind out-of-tree + too. + +.. option:: `LIBUNWIND_USE_COMPILER_RT=On` + +.. option:: `LIBUNWIND_INSTALL_SHARED_LIBRARY` + + llvm-libgcc generates both `libunwind.so` and `libgcc_s.so`, so we want to + avoid having libunwind generate this too (otherwise there would be a clash). + This variable should be set when building libunwind out-of-tree too. + +.. _building llvm-libgcc out of tree + +Building llvm-libgcc out of tree +================================ + +Building llvm-libgcc out of tree requires a compiler that supports compiler-rt +when its builtin symbols are exported. This usually means having two build trees +instead of the usual one. + +.. code-block:: bash + + # Assumes $(PWD) is /path/to/llvm-project + $ mkdir -p build + $ cd build + +.. _Building Clang + +Building Clang +-------------- + +The first build tree is a mostly conventional build tree and gets you a Clang +build with these compiler-rt symbols exposed. + +.. code-block:: bash + # Assumes $(PWD) is /path/to/llvm-project + $ cmake -GNinja -S llvm -B build-primary \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CROSSCOMPILING=On \ + -DCMAKE_INSTALL_PREFIX=/tmp/aarch64-unknown-linux-gnu \ + -DLLVM_ENABLE_PROJECTS='clang' \ + -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \ + -DLLVM_TARGETS_TO_BUILD=AArch64 \ + -DLLVM_DEFAULT_TARGET_TRIPLE=aarch64-unknown-linux-gnu \ + -DLLVM_TABLEGEN=`which llvm-tblgen` \ + -DCOMPILER_RT_BUILTINS_HIDE_SYMBOLS=Off \ + -DLIBUNWIND_ENABLE_SHARED=Off \ + -DLIBUNWIND_USE_COMPILER_RT=On \ + -DLIBUNWIND_HAS_COMMENT_LIB_PRAGMA=Off \ + -DLIBUNWIND_HAS_DL_LIB=Off \ + -DLIBUNWIND_HAS_PTHREAD_LIB=Off + $ ninja -C build-primary install + +``LIBUNWIND_HAS_COMMENT_LIB_PRAGMA``, ``LIBUNWIND_HAS_DL_LIB``, and +``LIBUNWIND_HAS_PTHREAD_LIB`` are not strictly necessary, but are useful to +prevent requiring your libunwind dependents to also link with libdl and +libpthread. + +.. _Building llvm-libgcc + +Building llvm-libgcc +------------------ + +Now that we've built the prerequisites, we can build llvm-libgcc in a second +tree. We build llvm-libgcc for x86_64 in this example, but it's possible to +replicate for any architecture that has a `version script`_ in the +``version-scripts`` directory (presently i686, x86_64, armhf, and aarch64). + +.. _version script: https://sourceware.org/binutils/docs/ld/VERSION.html + +.. code-block:: bash + + # Assumes $(PWD) is /path/to/llvm-project + $ export LLVM_LIBGCC_SYSROOT=/tmp/llvm-libgcc-experiment + $ cmake -GNinja -S llvm/tools/llvm-libgcc -B build-libgcc \ + -DCMAKE_C_COMPILER="${LLVM_LIBGCC_SYSROOT}/bin/clang" \ + -DCMAKE_CXX_COMPILER="${LLVM_LIBGCC_SYSROOT}/bin/clang++" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${LLVM_LIBGCC_SYSROOT}" \ + -DLLVM_LIBGCC_LIBUNWIND_DIR="${LLVM_LIBGCC_SYSROOT}/lib/aarch64-unknown-linux-gnu" + $ ninja -C build-libgcc install + +``${LLVM_LIBGCC_SYSROOT}/lib/aarch64-unknown-linux-gnu`` is the path to +``libunwind.a``. The target triple is determined by setting the flag +``LLVM_RUNTIME_TARGETS`` in the Clang build: make sure they match. + +## Verifying your results + +This gets you a copy of libunwind with the libgcc symbols. You can verify this +using ``readelf``. + +.. code-block:: bash + + $ llvm-readelf -W --dyn-syms "${LLVM_LIBGCC_SYSROOT}/lib/libunwind.so" | grep FUNC | grep GCC_3.0 + + +Roughly sixty symbols should appear, all suffixed with ``@@GCC_3.0``. You can +replace ``GCC_3.0`` with any of the supported version names in the version +script you’re exporting to verify that the symbols are exported. + + +.. _supported platforms: + +Supported platforms +=================== + +llvm-libgcc currently supports the following target triples: + +* ``aarch64-*-*-*`` +* ``armv7a-*-*-gnueabihf`` +* ``i386-*-*-*`` +* ``x86_64-*-*-*`` + +If you would like to support another triple (e.g. ``powerpc64-*-*-*``), you'll +need to generate a new version script, and then edit ``lib/gcc_s.ver``. + +.. _Generating a new version script + +Generating a new version script +------------------------------- + +To generate a new version script, we need to generate the list of symbols that +exist in the set (``clang-builtins.a`` ∪ ``libunwind.a``) ∩ ``libgcc_s.so.1``. +The prerequisites for generating a version script are a binaries for the three +aforementioned libraries targeting your architecture (without having built +llvm-libgcc). + +Once these libraries are in place, to generate a new version script, run the +following command. + +.. code-block:: bash + + /path/to/llvm-project + $ export ARCH=powerpc64 + $ llvm/tools/llvm-libgcc/generate_version_script.py \ + --compiler_rt=/path/to/libclang_rt.builtins-${ARCH}.a \ + --libunwind=/path/to/libunwind.a \ + --libgcc_s=/path/to/libgcc_s.so.1 \ + --output=${ARCH} + +This will generate a new version script a la +``/path/to/llvm-project/llvm/tools/llvm-libgcc/gcc_s-${ARCH}.ver``, which we use +in the next section. + +.. _Editing ``lib/gcc_s.ver`` + +Editing ``lib/gcc_s.ver`` +------------------------- + +Our freshly generated version script is unique to the specific architecture that +it was generated for, but a lot of the symbols are shared among many platforms. +As such, we don't check in unique version scripts, but rather have a single +version script that's run through the C preprocessor to prune symbols we won't +be using in ``lib/gcc_s.ver``. + +Working out which symbols are common is largely a manual process at the moment, +because some symbols may be shared across different architectures, but not in +the same versions of libgcc. As such, a symbol appearing in ``lib/gcc_s.ver`` +doesn't guarantee that the symbol is available for our new architecture: we need +to verify that the versions are the same, and if they're not, add the symbol to +the new version section, with the appropriate include guards. + +There are a few macros that aim to improve readability. + +* ``ARM_GNUEABIHF``, which targets exactly ``arm-*-*-gnueabihf``. +* ``GLOBAL_X86``, which should be used to target both x86 and x86_64, regardless + of the triple. +* ``GLOBAL_32BIT``, which is be used to target 32-bit platforms. +* ``GLOBAL_64BIT``, which is be used to target 64-bit platforms. diff --git a/llvm/tools/llvm-libgcc/generate_version_script.py b/llvm/tools/llvm-libgcc/generate_version_script.py new file mode 100755 --- /dev/null +++ b/llvm/tools/llvm-libgcc/generate_version_script.py @@ -0,0 +1,128 @@ +#!/usr/bin/python + +from collections import defaultdict +from itertools import chain +import argparse, subprocess, sys, os + + +def split_suffix(symbol): + """ + Splits a symbol such as `__gttf2@GCC_3.0` into a triple representing its + function name (__gttf2), version name (GCC_3.0), and version number (300). + + The version number acts as a priority. Since earlier versions are more + accessible and are likely to be used more, the lower the number is, the higher + its priortiy. A symbol that has a '@@' instead of '@' has been designated by + the linker as the default symbol, and is awarded a priority of -1. + """ + if '@' not in symbol: + return None + data = [i for i in filter(lambda s: s, symbol.split('@'))] + _, version = data[-1].split('_') + version = version.replace('.', '') + priority = -1 if '@@' in symbol else int(version + '0' * + (3 - len(version))) + return data[0], data[1], priority + + +def invert_mapping(symbol_map): + """Transforms a map from Key->Value to Value->Key.""" + store = defaultdict(list) + for symbol, (version, _) in symbol_map.items(): + store[version].append(symbol) + result = [] + for k, v in store.items(): + v.sort() + result.append((k, v)) + result.sort(key=lambda x: x[0]) + return result + + +def intersection(llvm, gcc): + """ + Finds the intersection between the symbols extracted from compiler-rt.a/libunwind.a + and libgcc_s.so.1. + """ + common_symbols = {} + for i in gcc: + suffix_triple = split_suffix(i) + if not suffix_triple: + continue + + symbol, version_name, version_number = suffix_triple + if symbol in llvm: + if symbol not in common_symbols: + common_symbols[symbol] = (version_name, version_number) + continue + if version_number < common_symbols[symbol][1]: + common_symbols[symbol] = (version_name, version_number) + return invert_mapping(common_symbols) + + +def find_function_names(path): + """ + Runs readelf on a binary and reduces to only defined functions. Equivalent to + `llvm-readelf --wide ${path} | grep 'FUNC' | grep -v 'UND' | awk '{print $8}'`. + """ + result = subprocess.run(args=['llvm-readelf', '-su', path], + capture_output=True) + + if result.returncode != 0: + print(result.stderr.decode('utf-8'), file=sys.stderr) + sys.exit(1) + + stdout = result.stdout.decode('utf-8') + stdout = filter(lambda x: 'FUNC' in x and 'UND' not in x, + stdout.split('\n')) + stdout = chain( + map(lambda x: filter(None, x), (i.split(' ') for i in stdout))) + + return [list(i)[7] for i in stdout] + + +def to_file(versioned_symbols): + path = f'{os.path.dirname(os.path.realpath(__file__))}/new-gcc_s-symbols' + with open(path, 'w') as f: + f.write('Do not check this version script in: you should instead work ' + 'out which symbols are missing in `lib/gcc_s.ver` and then ' + 'integrate them into `lib/gcc_s.ver`. For more information, ' + 'please see `doc/LLVMLibgcc.rst`.\n') + for version, symbols in versioned_symbols: + f.write(f'{version} {{\n') + for i in symbols: + f.write(f' {i};\n') + f.write('};\n\n') + + +def read_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--compiler_rt', + type=str, + help='Path to `libclang_rt.builtins-${ARCH}.a`.', + required=True) + parser.add_argument('--libunwind', + type=str, + help='Path to `libunwind.a`.', + required=True) + parser.add_argument( + '--libgcc_s', + type=str, + help= + 'Path to `libgcc_s.so.1`. Note that unlike the other two arguments, this is a dynamic library.', + required=True) + return parser.parse_args() + + +def main(): + args = read_args() + llvm = find_function_names(args.compiler_rt) + find_function_names( + args.libunwind) + gcc = find_function_names(args.libgcc_s) + versioned_symbols = intersection(llvm, gcc) + # TODO(cjdb): work out a way to integrate new symbols in with the existing + # ones + to_file(versioned_symbols) + + +if __name__ == '__main__': + main() diff --git a/llvm/tools/llvm-libgcc/lib/CMakeLists.txt b/llvm/tools/llvm-libgcc/lib/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libgcc/lib/CMakeLists.txt @@ -0,0 +1,76 @@ +include(CheckLibraryExists) + +add_custom_target(gcc_s_ver ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gcc_s.ver") +set(LLVM_LIBGCC_GCC_S_VER "${CMAKE_CURRENT_BINARY_DIR}/gcc_s.ver") + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gcc_s.ver" + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/gcc_s.ver" + "${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/${LLVM_LIBGCC_UNWIND_STATIC}" + "${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/${LLVM_LIBGCC_COMPILER_RT}" + COMMAND + "${CMAKE_C_COMPILER}" + "-E" + "-xc" "${CMAKE_CURRENT_SOURCE_DIR}/gcc_s.ver" + "-o" "${CMAKE_CURRENT_BINARY_DIR}/gcc_s.ver" +) + +add_library(unwind_shared SHARED blank.c) +add_dependencies(unwind_shared gcc_s_ver) +set_target_properties(unwind_shared + PROPERTIES + LINKER_LANGUAGE C + OUTPUT_NAME "unwind" + VERSION "1.0" + SOVERSION "1" + POSITION_INDEPENDENT_CODE ON) +target_link_options( + unwind_shared PRIVATE + -Wl,-nostdlib + -Wl,--whole-archive + -Wl,${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/${LLVM_LIBGCC_UNWIND_STATIC} + -Wl,${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/${LLVM_LIBGCC_COMPILER_RT} + -Wl,--version-script,${LLVM_LIBGCC_GCC_S_VER} + -Wl,--no-whole-archive +) +check_library_exists(m sin "" LLVM_LIBGCC_HAS_LIBM) +check_library_exists(c printf "" LLVM_LIBGCC_HAS_LIBC) +target_link_libraries( + unwind_shared PRIVATE + $<$:m> + $<$:c> +) + +add_custom_target(synth_libgcc ALL + DEPENDS "${LLVM_LIBGCC_SYSROOT}/lib${LLVMLIB_DIR_SUFFIX}/${LLVM_LIBGCC_COMPILER_RT}" unwind_shared + COMMAND ${CMAKE_COMMAND} -E create_symlink + "${LLVM_LIBGCC_COMPILER_RT}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc.a") +add_custom_target(synth_libgcc_eh ALL + COMMAND ${CMAKE_COMMAND} -E create_symlink + "${LLVM_LIBGCC_UNWIND_STATIC}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc_eh.a") +add_custom_target(synth_libgcc_s ALL + DEPENDS unwind_shared + COMMAND ${CMAKE_COMMAND} -E create_symlink + "libunwind.so" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc_s.so") +add_custom_target(synth_libgcc_s1 ALL + DEPENDS unwind_shared + COMMAND ${CMAKE_COMMAND} -E create_symlink + "libunwind.so.1" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc_s.so.1") + +add_custom_target(unwind + DEPENDS + unwind_shared + synth_libgcc + synth_libgcc_eh + synth_libgcc_s + synth_libgcc_s1) +set(LLVM_LIBGCC_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX} CACHE PATH "") +install(TARGETS unwind_shared + LIBRARY DESTINATION "${LLVM_LIBGCC_INSTALL_LIBRARY_DIR}" COMPONENT unwind + ARCHIVE DESTINATION "${LLVM_LIBGCC_INSTALL_LIBRARY_DIR}" COMPONENT unwind + RUNTIME DESTINATION bin COMPONENT unwind) +install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc.a" TYPE LIB) +install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc_eh.a" TYPE LIB) +install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc_s.so" TYPE LIB) +install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libgcc_s.so.1" TYPE LIB) diff --git a/llvm/tools/llvm-libgcc/lib/blank.c b/llvm/tools/llvm-libgcc/lib/blank.c new file mode 100644 diff --git a/llvm/tools/llvm-libgcc/lib/gcc_s.ver b/llvm/tools/llvm-libgcc/lib/gcc_s.ver new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libgcc/lib/gcc_s.ver @@ -0,0 +1,167 @@ +// Detect if we're using arm-*-*-gnueabihf +#if defined(__arm__) && \ + defined(__ARM_ARCH_7A__) && defined(__ARM_EABI__) && \ + defined(__ARM_FP) && (__ARM_FP >= 0x04) + #define ARM_GNUEABIHF +#endif + +#if !defined(__x86_64__) && \ + !defined(__aarch64__) && \ + !defined(__i386__) && \ + !defined(ARM_GNUEABIHF) + #error The only platforms that are currently supported are x86_64, i386, arm-gnueabihf, and aarch64. +#endif + +#if defined(__x86_64__) || defined(__i386__) + #define GLOBAL_X86 +#endif + +#if __SIZEOF_POINTER__ >= 8 + #define GLOBAL_64BIT +#else + #define GLOBAL_32BIT +#endif + +GCC_3.0 { + __absvdi2; __absvsi2; __addvdi3; __addvsi3; __clear_cache; __ffsdi2; + __fixunsdfdi; __fixunssfdi; __mulvdi3; __mulvsi3; __negvdi2; __negvsi2; + __subvdi3; __subvsi3; + _Unwind_DeleteException; + _Unwind_ForcedUnwind; + _Unwind_GetDataRelBase; + _Unwind_GetLanguageSpecificData; + _Unwind_GetRegionStart; + _Unwind_GetTextRelBase; + _Unwind_RaiseException; + _Unwind_Resume; +}; + +GCC_3.3 { _Unwind_GetCFA; _Unwind_Resume_or_Rethrow; }; +GCC_3.3.1 { __gcc_personality_v0; }; +GCC_3.4 { __clzdi2; __ctzdi2; __paritydi2; __popcountdi2; }; +GCC_3.4.2 { __enable_execute_stack; }; +GCC_4.0.0 { __divdc3; __divsc3; __muldc3; __mulsc3; __powidf2; __powisf2; }; +GCC_4.3.0 { __bswapdi2; __bswapsi2; __emutls_get_address; }; + +#if defined(GLOBAL_32BIT) + GCC_3.0 { + __ashldi3; __ashrdi3; __cmpdi2; __fixdfdi; __fixsfdi; __fixunsdfsi; + __fixunssfsi; __floatdidf; __floatdisf; __lshrdi3; __muldi3; __negdi2; + __ucmpdi2; __udivmoddi4; + }; + + GCC_3.4 { __clzsi2; __ctzsi2; __paritysi2; __popcountsi2; }; + GCC_4.2.0 { __floatundidf; __floatundisf; }; + GCC_4.3.0 { __ffssi2; }; + GCC_7.0.0 { __divmoddi4; }; + GLIBC_2.0 { __divdi3; __moddi3; __udivdi3; __umoddi3; }; +#elif defined(GLOBAL_64BIT) + GCC_3.0 { + __ashlti3; __ashrti3; __cmpti2; __divti3; __ffsti2; __fixdfti; + __fixsfti; __fixunssfti; __floattidf; __lshrti3; __modti3; __multi3; + __negti2; __ucmpti2; __udivmodti4; __udivti3; __umodti3; __fixunsdfti; + __floattisf; + }; + + GCC_3.4 { __clzti2; __ctzti2; __parityti2; __popcountti2; }; + GCC_3.4.4 { __absvti2; __addvti3; __mulvti3; __negvti2; __subvti3; }; + GCC_4.2.0 { __floatuntidf; __floatuntisf; }; + GCC_7.0.0 { __divmodti4; }; +#endif + +#if defined(GLOBAL_X86) + GCC_3.0 { + __deregister_frame_info_bases; __fixunsxfdi; __register_frame_info_bases; + __register_frame_info_table_bases; + }; + + GCC_4.0.0 { __divxc3; __mulxc3; __powixf2; }; + GCC_4.8.0 { __cpu_indicator_init; }; +#endif + +#if !defined(ARM_GNUEABIHF) + GCC_3.0 { + _Unwind_Find_FDE; _Unwind_GetGR; _Unwind_GetIP; _Unwind_SetGR; _Unwind_SetIP; + }; + + GCC_3.3 { _Unwind_Backtrace; _Unwind_FindEnclosingFunction; }; + GCC_4.2.0 { _Unwind_GetIPInfo; }; +#else // defined(ARM_GNUEABIHF) + GCC_3.0 { + __adddf3; __addsf3; __divdf3; __divsf3; __divsi3; __eqdf2; + __eqsf2; __extendsfdf2; __fixdfsi; __fixsfsi; __floatsidf; __floatsisf; + __gedf2; __gesf2; __gtdf2; __gtsf2; __ledf2; __lesf2; + __ltdf2; __ltsf2; __modsi3; __muldf3; __mulsf3; __nedf2; + __negdf2; __negsf2; __nesf2; __subdf3; __subsf3; __truncdfsf2; + __udivsi3; __umodsi3; + }; + + GCC_3.3.4 { __unorddf2; __unordsf2; }; + + GCC_3.5 { + __aeabi_cdcmpeq; __aeabi_cdcmple; __aeabi_cdrcmple; __aeabi_cfcmpeq; + __aeabi_cfcmple; __aeabi_cfrcmple; __aeabi_d2f; __aeabi_d2iz; + __aeabi_d2lz; __aeabi_d2uiz; __aeabi_d2ulz; __aeabi_dadd; + __aeabi_dcmpeq; __aeabi_dcmpge; __aeabi_dcmpgt; __aeabi_dcmple; + __aeabi_dcmplt; __aeabi_dcmpun; __aeabi_ddiv; __aeabi_dmul; + __aeabi_dneg; __aeabi_drsub; __aeabi_dsub; __aeabi_f2d; + __aeabi_f2iz; __aeabi_f2lz; __aeabi_f2uiz; __aeabi_f2ulz; + __aeabi_fadd; __aeabi_fcmpeq; __aeabi_fcmpge; __aeabi_fcmpgt; + __aeabi_fcmple; __aeabi_fcmplt; __aeabi_fcmpun; __aeabi_fdiv; + __aeabi_fmul; __aeabi_fneg; __aeabi_frsub; __aeabi_fsub; + __aeabi_i2d; __aeabi_i2f; __aeabi_idiv; __aeabi_idiv0; + __aeabi_idivmod; __aeabi_l2d; __aeabi_l2f; __aeabi_lasr; + __aeabi_lcmp; __aeabi_ldiv0; __aeabi_ldivmod; __aeabi_llsl; + __aeabi_llsr; __aeabi_lmul; __aeabi_ui2d; __aeabi_ui2f; + __aeabi_uidiv; __aeabi_uidivmod; __aeabi_ul2d; __aeabi_ul2f; + __aeabi_ulcmp; __aeabi_uldivmod; __aeabi_unwind_cpp_pr0; + __aeabi_unwind_cpp_pr1; __aeabi_unwind_cpp_pr2; + __gnu_unwind_frame; + _Unwind_Complete; + _Unwind_VRS_Get; + _Unwind_VRS_Pop; + _Unwind_VRS_Set; + }; + + GCC_4.2.0 { __floatunsidf; __floatunsisf; }; + GCC_4.3.0 { _Unwind_Backtrace; }; +#endif + +#if defined(__aarch64__) + GCC_3.0 { + __addtf3; __divtf3; __eqtf2; __extenddftf2; __extendsftf2; + __fixtfdi; __fixtfsi; __fixtfti; __fixunstfdi; __fixunstfsi; + __fixunstfti; __floatditf; __floatsitf; __floattitf; __getf2; + __gttf2; __letf2; __lttf2; __multf3; __netf2; + __subtf3; __trunctfdf2; __trunctfsf2; + }; + + GCC_4.0.0 { __powitf2; __divtc3; __multc3; }; + GCC_4.2.0 { __floatunditf; __floatunsitf; __floatuntitf; }; + GCC_4.5.0 { __unordtf2; }; +#endif + +#if defined(__aarch64__) || defined(__i386__) + GLIBC_2.0 { __deregister_frame; __register_frame; }; +#endif + +#if defined(__i386__) + GCC_3.0 { __fixunsxfsi; __fixxfdi; __floatdixf; }; + GCC_4.2.0 { __floatundixf; }; + + GLIBC_2.0 { + __register_frame_info; __register_frame_info_table; __register_frame_table; + __deregister_frame_info; + }; +#endif + +#if defined(__x86_64__) + GCC_3.0 { + __register_frame_info; __register_frame_info_table; __register_frame_table; + __deregister_frame; __deregister_frame_info; __register_frame; + __fixunsxfti; __fixxfti; __floattixf; + }; + + GCC_4.2.0 { __floatuntixf; }; + GCC_4.3.0 { __divtc3; __multc3; }; +#endif