diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index fb3c8aa910c9e..0c5b2e03ab03e 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -149,6 +149,17 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, #endif } + bool staticExecutable = false; + bool staticStdlib = false; + + if (context.Args.hasFlag(options::OPT_static_executable, + options::OPT_no_static_executable, false)) { + staticExecutable = true; + } else if (context.Args.hasFlag(options::OPT_static_stdlib, + options::OPT_no_static_stdlib, false)) { + staticStdlib = true; + } + // Configure the toolchain. // By default, use the system clang++ to link. const char *Clang = "clang++"; @@ -167,7 +178,7 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, } if (getTriple().getOS() == llvm::Triple::Linux && - job.getKind() == LinkKind::Executable) { + job.getKind() == LinkKind::Executable && !staticExecutable) { Arguments.push_back("-pie"); } @@ -177,17 +188,6 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, Arguments.push_back(context.Args.MakeArgString(Target)); } - bool staticExecutable = false; - bool staticStdlib = false; - - if (context.Args.hasFlag(options::OPT_static_executable, - options::OPT_no_static_executable, false)) { - staticExecutable = true; - } else if (context.Args.hasFlag(options::OPT_static_stdlib, - options::OPT_no_static_stdlib, false)) { - staticStdlib = true; - } - SmallString<128> SharedRuntimeLibPath; getRuntimeLibraryPath(SharedRuntimeLibPath, context.Args, /*Shared=*/true); @@ -205,11 +205,13 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job, Arguments.push_back(context.Args.MakeArgString(SharedRuntimeLibPath)); } - SmallString<128> swiftrtPath = SharedRuntimeLibPath; - llvm::sys::path::append(swiftrtPath, - swift::getMajorArchitectureName(getTriple())); - llvm::sys::path::append(swiftrtPath, "swiftrt.o"); - Arguments.push_back(context.Args.MakeArgString(swiftrtPath)); + if (!staticExecutable) { + SmallString<128> swiftrtPath = SharedRuntimeLibPath; + llvm::sys::path::append(swiftrtPath, + swift::getMajorArchitectureName(getTriple())); + llvm::sys::path::append(swiftrtPath, "swiftrt.o"); + Arguments.push_back(context.Args.MakeArgString(swiftrtPath)); + } addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_Object); diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 6597190d10f99..ebf2345d59374 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -105,33 +105,44 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") LINK_FLAGS ${swift_runtime_linker_flags} INSTALL_IN_COMPONENT stdlib) - foreach(arch IN LISTS SWIFT_SDK_${sdk}_ARCHITECTURES) - set(FragileSupportLibrary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}) - set(LibraryLocation ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${arch}) - add_custom_command_target(swift_image_inspection_${arch}_static - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocation} - OUTPUT - "${LibraryLocation}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibrary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" - COMPONENT stdlib) - endforeach() + set(swift_elf_static_lib_compile_flags ${swift_runtime_library_compile_flags}) + list(APPEND swift_elf_static_lib_compile_flags -DELF_STATIC_LIB) + + add_swift_target_library(swiftImageInspectionStatic TARGET_LIBRARY STATIC + StaticBinaryELF.cpp ImageInspectionELF.cpp + C_COMPILE_FLAGS ${swift_elf_static_lib_compile_flags} + LINK_FLAGS ${swift_runtime_linker_flags} + INSTALL_IN_COMPONENT stdlib) + + foreach(linktype Shared Static) + foreach(arch IN LISTS SWIFT_SDK_${sdk}_ARCHITECTURES) + set(FragileSupportLibrary swiftImageInspection${linktype}-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}) + set(LibraryLocation ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}/${arch}) + add_custom_command_target(swift_image_inspection_${linktype}_${arch}_static + COMMAND + "${CMAKE_COMMAND}" -E copy $ ${LibraryLocation} + OUTPUT + "${LibraryLocation}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspection${linktype}${CMAKE_STATIC_LIBRARY_SUFFIX}" + DEPENDS + ${FragileSupportLibrary}) + swift_install_in_component(FILES $ + DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" + COMPONENT stdlib) + endforeach() - set(FragileSupportLibraryPrimary swiftImageInspectionShared-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) - set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) - add_custom_command_target(swift_image_inspection_static_primary_arch - COMMAND - "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} - OUTPUT - "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspectionShared${CMAKE_STATIC_LIBRARY_SUFFIX}" - DEPENDS - ${FragileSupportLibraryPrimary}) + set(FragileSupportLibraryPrimary swiftImageInspection${linktype}-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${SWIFT_PRIMARY_VARIANT_ARCH}) + set(LibraryLocationPrimary ${SWIFTSTATICLIB_DIR}/${lowercase_sdk}) + add_custom_command_target(swift_image_inspection_${linktype}_primary_arch + COMMAND + "${CMAKE_COMMAND}" -E copy $ ${LibraryLocationPrimary} + OUTPUT + "${LibraryLocationPrimary}/${CMAKE_STATIC_LIBRARY_PREFIX}swiftImageInspection${linktype}${CMAKE_STATIC_LIBRARY_SUFFIX}" + DEPENDS + ${FragileSupportLibraryPrimary}) swift_install_in_component(FILES $ DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) + endforeach() # Generate the static-executable-args.lnk file used for ELF systems (eg linux) set(linkfile "${lowercase_sdk}/static-executable-args.lnk") @@ -150,10 +161,12 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") DESTINATION "lib/swift_static/${lowercase_sdk}" COMPONENT stdlib) add_custom_target(static_binary_magic ALL DEPENDS ${static_binary_lnk_file_list}) - foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) - add_dependencies(static_binary_magic ${swift_image_inspection_${arch}_static}) + foreach(linktype Shared Static) + foreach(arch IN LISTS SWIFT_SDK_LINUX_ARCHITECTURES) + add_dependencies(static_binary_magic ${swift_image_inspection_${linktype}_${arch}_static}) + endforeach() + add_dependencies(static_binary_magic ${swift_image_inspection_${linktype}_primary_arch}) endforeach() - add_dependencies(static_binary_magic ${swift_image_inspection_static_primary_arch}) add_swift_target_library(swiftImageInspectionSharedObject OBJECT_LIBRARY TARGET_LIBRARY ImageInspectionELF.cpp diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp index fb5f49f6c0a87..5954b03eacaae 100644 --- a/stdlib/public/runtime/ImageInspectionELF.cpp +++ b/stdlib/public/runtime/ImageInspectionELF.cpp @@ -22,7 +22,10 @@ #include "ImageInspection.h" #include "ImageInspectionELF.h" + +#ifndef ELF_STATIC_LIB #include +#endif using namespace swift; @@ -56,6 +59,7 @@ void swift::initializeProtocolLookup() { sections = sections->next; } } + void swift::initializeProtocolConformanceLookup() { const swift::MetadataSections *sections = registered; while (true) { @@ -89,10 +93,10 @@ void swift::initializeTypeMetadataRecordLookup() { void swift::initializeDynamicReplacementLookup() { } -// As ELF images are loaded, ImageInspectionInit:sectionDataInit() will call -// addNewDSOImage() with an address in the image that can later be used via -// dladdr() to dlopen() the image after the appropriate initialize*Lookup() -// function has been called. +// As ELF images are loaded, SwiftRT-ELF:swift_image_constructor() will call +// addNewDSOImage() with a pointer to the MetadataSections in the image. This +// functionality is not required for static executables which are linked with +// swiftImageInspectionStatic. SWIFT_RUNTIME_EXPORT void swift_addNewDSOImage(const void *addr) { const swift::MetadataSections *sections = @@ -131,6 +135,10 @@ void swift_addNewDSOImage(const void *addr) { } } +#ifndef ELF_STATIC_LIB + +// For shared executables only, static executables use the version defined in +// StaticBinaryELF.cpp int swift::lookupSymbol(const void *address, SymbolInfo *info) { Dl_info dlinfo; if (dladdr(address, &dlinfo) == 0) { @@ -144,6 +152,8 @@ int swift::lookupSymbol(const void *address, SymbolInfo *info) { return 1; } +#endif + // This is only used for backward deployment hooks, which we currently only support for // MachO. Add a stub here to make sure it still compiles. void *swift::lookupSection(const char *segment, const char *section, size_t *outSize) { diff --git a/stdlib/public/runtime/StaticBinaryELF.cpp b/stdlib/public/runtime/StaticBinaryELF.cpp index 01b2e912bf71e..3bbec22e130cc 100644 --- a/stdlib/public/runtime/StaticBinaryELF.cpp +++ b/stdlib/public/runtime/StaticBinaryELF.cpp @@ -34,6 +34,11 @@ #include #include +// The functions in SwiftRT-ELF.cpp are only called via the ELF .init constructor +// and none of the functions are linked directly, so include the code inline as the +// linker wont link to it in a static executable. +#include "SwiftRT-ELF.cpp" + using namespace std; using namespace llvm; @@ -61,6 +66,19 @@ typedef Elf32_Section Elf_Section; extern const Elf_Ehdr elfHeader asm("__ehdr_start"); +// Create strong linkage to pthread_self, pthread_once and pthread_key_create +// as they are normally weak-linked and used to detect the presence of pthreads. +// Without this the calls just jump to 0x0. +__attribute__((__visibility__("hidden"))) + pthread_t (*__strong_pthread_self)(void) = pthread_self; + +__attribute__((__visibility__("hidden"))) +int (*__strong_pthread_once)(pthread_once_t *, void (*)(void)) = pthread_once; + +__attribute__((__visibility__("hidden"))) +int (*__strong_pthread_key_create)(pthread_key_t *, void (*)(void *)) = pthread_key_create; + + class StaticBinaryELF { private: @@ -159,15 +177,28 @@ class StaticBinaryELF { if (symbolTable.hasValue()) { auto searchAddr = reinterpret_cast(addr); auto symbols = symbolTable->data(); + const Elf_Sym *bestMatch = nullptr; + unsigned long bestDistance = ULONG_MAX; for (size_t idx = 0; idx < symbols.size(); idx++) { auto symbol = &symbols[idx]; if (ELF_ST_TYPE(symbol->st_info) == STT_FUNC - && searchAddr >= symbol->st_value - && searchAddr < (symbol->st_value + symbol->st_size)) { - return symbol; + && searchAddr >= symbol->st_value) { + + auto tmpDistance = searchAddr - symbol->st_value; + if (tmpDistance < symbol->st_size) { + return symbol; + } + // The searchAddress is past the end of this symbol's region, keep + // track of which symbol end address the searchAddress is closest to. + tmpDistance -= symbol->st_size; + if (tmpDistance < bestDistance) { + bestMatch = symbol; + tmpDistance = bestDistance; + } } } + return bestMatch; } return nullptr; } @@ -202,26 +233,30 @@ class StaticBinaryELF { StringRef deleted = StringRef("(deleted)"); while (getdelim(&line, &size, '\n', fp) != -1) { - StringRef entry = StringRef(line).rsplit('\n').first; - auto addrRange = entry.split(' ').first.split('-'); - unsigned long long low = strtoull(addrRange.first.str().c_str(), + StringRef entry = StringRef(line).drop_back(); + + auto indexOfDash = entry.find('-'); + auto indexOfSpace = entry.find(' '); + auto addrLow = entry.substr(0, indexOfDash); + auto addrHigh = entry.substr(indexOfDash + 1, indexOfSpace); + unsigned long long low = strtoull(addrLow.str().c_str(), nullptr, 16); if (low == 0 || low > UINTPTR_MAX || address < (uintptr_t)low) { continue; } - unsigned long long high = strtoull(addrRange.second.str().c_str(), + unsigned long long high = strtoull(addrHigh.str().c_str(), nullptr, 16); if (high == 0 || high > UINTPTR_MAX || address > (uintptr_t)high) { continue; } - auto fname = entry.split('/').second; + auto fname = entry.substr(entry.find('/')); if (fname.empty() || fname.endswith(deleted)) { continue; } - fullPathName = "/" + fname.str(); + fullPathName = fname.str(); break; } if (line) { @@ -312,7 +347,7 @@ swift::lookupSymbol(const void *address, SymbolInfo *info) { auto symbol = binary.findSymbol(address); if (symbol != nullptr) { info->symbolAddress = reinterpret_cast(symbol->st_value); - info->symbolName = binary.symbolName(symbol); + info->symbolName.reset(binary.symbolName(symbol)); } else { info->symbolAddress = nullptr; info->symbolName = nullptr; diff --git a/test/Driver/static-executable-linux.swift b/test/Driver/static-executable-linux.swift new file mode 100644 index 0000000000000..8f0589aeccf36 --- /dev/null +++ b/test/Driver/static-executable-linux.swift @@ -0,0 +1,10 @@ +// Build a static executable "hello world" program +// REQUIRES: OS=linux-gnu +// REQUIRES: static_stdlib +print("hello world!") +// RUN: %empty-directory(%t) +// RUN: %target-swiftc_driver -static-executable -o %t/static-executable %s +// RUN: %t/static-executable | %FileCheck %s +// RUN: file %t/static-executable | %FileCheck %s --check-prefix=FILE +// CHECK: hello world! +// FILE: , statically linked, diff --git a/utils/static-executable-args.lnk b/utils/static-executable-args.lnk index f3da0ed6b0ed0..36e3070c710c3 100644 --- a/utils/static-executable-args.lnk +++ b/utils/static-executable-args.lnk @@ -1,15 +1,8 @@ -static -lswiftCore --lswiftImageInspectionShared --Xlinker ---defsym=__import_pthread_self=pthread_self --Xlinker ---defsym=__import_pthread_once=pthread_once --Xlinker ---defsym=__import_pthread_key_create=pthread_key_create +-lswiftImageInspectionStatic -lpthread -latomic --licui18n --licuuc --licudata --ldl +-licui18nswift +-licuucswift +-licudataswift