diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 07a19d9e548ac..6c71e7a1b3b58 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -189,11 +189,6 @@ toolchains::GenericUnix::constructInvocation(const DynamicLinkJobAction &job, Arguments.push_back(context.Args.MakeArgString(A->getValue())); } - if (getTriple().getOS() == llvm::Triple::Linux && - job.getKind() == LinkKind::Executable) { - Arguments.push_back("-pie"); - } - bool staticExecutable = false; bool staticStdlib = false; @@ -205,6 +200,11 @@ toolchains::GenericUnix::constructInvocation(const DynamicLinkJobAction &job, staticStdlib = true; } + if (getTriple().getOS() == llvm::Triple::Linux && + job.getKind() == LinkKind::Executable && !staticExecutable) { + Arguments.push_back("-pie"); + } + SmallVector RuntimeLibPaths; getRuntimeLibraryPaths(RuntimeLibPaths, context.Args, context.OI.SDKPath, /*Shared=*/!(staticExecutable || staticStdlib)); diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 001d97d517405..2d1895638b43b 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -78,6 +78,7 @@ endif(LLVM_ENABLE_ASSERTIONS) # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES + StaticBinaryELF.cpp SwiftRT-COFF.cpp SwiftRT-ELF.cpp ${swift_runtime_sources} @@ -102,35 +103,43 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_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}) - add_dependencies(stdlib ${FragileSupportLibrary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" - COMPONENT stdlib) - endforeach() + add_swift_target_library(swiftImageInspectionStatic TARGET_LIBRARY STATIC + StaticBinaryELF.cpp ImageInspectionELF.cpp + C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} -DELF_STATIC_LIB + LINK_FLAGS ${swift_runtime_linker_flags} + INSTALL_IN_COMPONENT stdlib) - 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}) - add_dependencies(stdlib ${FragileSupportLibraryPrimary}) - swift_install_in_component(FILES $ - DESTINATION "lib/swift_static/${lowercase_sdk}" - 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}) + add_dependencies(stdlib ${FragileSupportLibrary}) + swift_install_in_component(FILES $ + DESTINATION "lib/swift_static/${lowercase_sdk}/${arch}" + COMPONENT stdlib) + endforeach() + + 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}) + add_dependencies(stdlib ${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") @@ -149,10 +158,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_dependencies(stdlib static_binary_magic) add_swift_target_library(swiftImageInspectionSharedObject OBJECT_LIBRARY diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp index 331ee3614f54f..875fa2d8f9c78 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,8 @@ 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. SWIFT_RUNTIME_EXPORT void swift_addNewDSOImage(const void *addr) { const swift::MetadataSections *sections = @@ -131,6 +133,12 @@ void swift_addNewDSOImage(const void *addr) { } } +#ifndef ELF_STATIC_LIB + +// For shared executables only, static executables use the version defined in +// StaticBinaryELF.cpp. +// libswiftImageInspectionShared.a contains the below function and +// libswiftImageInspectionStatic.a contains the version 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 new file mode 100644 index 0000000000000..7d6a5fbcf5d8e --- /dev/null +++ b/stdlib/public/runtime/StaticBinaryELF.cpp @@ -0,0 +1,326 @@ +//===--- StaticBinaryELF.cpp ----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Parse a static ELF binary to implement lookupSymbol() address lookup. +// +//===----------------------------------------------------------------------===// + +#if defined(__ELF__) + +#include "ImageInspection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "swift/Basic/Lazy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace llvm; +using elfType = object::ELFType; + +extern const elfType::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: + // mmap a section of a file that might not be page aligned. + class Mapping { + public: + void *mapping; + size_t mapLength; + off_t diff; + + Mapping(int fd, size_t fileSize, off_t offset, size_t length) { + assert(fd >= 0 && offset + length <= fileSize); + + long pageSize = sysconf(_SC_PAGESIZE); + long pageMask = ~(pageSize - 1); + + off_t alignedOffset = offset & pageMask; + diff = (offset - alignedOffset); + mapLength = diff + length; + mapping = mmap(nullptr, mapLength, PROT_READ, MAP_PRIVATE, fd, + alignedOffset); + if (mapping == MAP_FAILED) { + mapping = nullptr; + mapLength = 0; + diff = 0; + } + } + + template + ArrayRef data() { + size_t elements = (mapLength - diff) / sizeof(T); + const T *data = reinterpret_cast(reinterpret_cast(mapping) + + diff); + return ArrayRef(data, elements); + } + + ~Mapping() { + if (mapping && mapLength > 0) { + munmap(mapping, mapLength); + } + } + }; + + string fullPathName; + const elfType::Phdr *programHeaders = nullptr; + Optional symbolTable; + Optional stringTable; + +public: + StaticBinaryELF() { + getExecutablePathName(); + + programHeaders = reinterpret_cast(getauxval(AT_PHDR)); + if (programHeaders == nullptr) { + return; + } + // If an interpreter is set in the program headers then this is a + // dynamic executable and therefore not valid. + for (size_t idx = 0; idx < elfHeader.e_phnum; idx++) { + if (programHeaders[idx].p_type == PT_INTERP) { + programHeaders = nullptr; + return; + } + } + + if (!fullPathName.empty()) { + mmapExecutable(); + } + } + + const char *getPathName() { + return fullPathName.empty() ? nullptr : fullPathName.c_str(); + } + + void *getSectionLoadAddress(const void *addr) { + if (programHeaders) { + auto searchAddr = reinterpret_cast(addr); + + for (size_t idx = 0; idx < elfHeader.e_phnum; idx++) { + auto header = &programHeaders[idx]; + if (header->p_type == PT_LOAD && searchAddr >= header->p_vaddr + && searchAddr <= (header->p_vaddr + header->p_memsz)) { + uintptr_t ptr = header->p_vaddr; + return reinterpret_cast(ptr); + } + } + } + return nullptr; + } + + // Lookup a function symbol by address. + const elfType::Sym *findSymbol(const void *addr) { + if (symbolTable.hasValue()) { + auto searchAddr = reinterpret_cast(addr); + auto symbols = symbolTable->data(); + const elfType::Sym *bestMatch = nullptr; + unsigned long bestDistance = ULONG_MAX; + + for (size_t idx = 0; idx < symbols.size(); idx++) { + auto symbol = &symbols[idx]; + if (symbol->getType() == STT_FUNC + && searchAddr >= symbol->getValue()) { + + auto tmpDistance = searchAddr - symbol->getValue(); + 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; + } + + const char *symbolName(const elfType::Sym *symbol) { + if (stringTable.hasValue()) { + auto strings = stringTable->data(); + if (symbol->st_name < strings.size()) { + return &strings[symbol->st_name]; + } + } + return nullptr; + } + +private: +#if defined(__linux__) + // This is Linux specific - find the full path of the executable + // by looking in /proc/self/maps for a mapping holding the current + // address space. For a static binary it should only be mapping one + // file anyway. Don't use /proc/self/exe as the symlink will be removed + // if the main thread terminates - see proc(5). + void getExecutablePathName() { + uintptr_t address = (uintptr_t)&elfHeader; + + FILE *fp = fopen("/proc/self/maps", "r"); + if (!fp) { + perror("Unable to open /proc/self/maps"); + } else { + char *line = nullptr; + size_t size = 0; + // Format is: addrLo-addrHi perms offset dev inode pathname. + // If the executable has been deleted the last column will be '(deleted)'. + StringRef deleted = StringRef("(deleted)"); + + while (getdelim(&line, &size, '\n', fp) != -1) { + 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(addrHigh.str().c_str(), + nullptr, 16); + if (high == 0 || high > UINTPTR_MAX || address > (uintptr_t)high) { + continue; + } + + auto fname = entry.substr(entry.find('/')); + if (fname.empty() || fname.endswith(deleted)) { + continue; + } + + fullPathName = fname.str(); + break; + } + if (line) { + free(line); + } + fclose(fp); + } + } +#else +#error("To support -static-executable on this ELF platform implement getExecutablePathName()") +#endif + + // Parse the ELF binary using mmap for the section headers, symbol table and + // string table. + void mmapExecutable() { + struct stat buf; + int fd = open(fullPathName.c_str(), O_RDONLY); + if (fd < 0) { + return; + } + + if (fstat(fd, &buf) != 0) { + close(fd); + return; + } + + // Map in the section headers. + size_t sectionHeadersSize = (elfHeader.e_shentsize * elfHeader.e_shnum); + Mapping sectionHeaders = Mapping(fd, buf.st_size, elfHeader.e_shoff, + sectionHeadersSize); + if (sectionHeaders.mapping) { + auto headers = sectionHeaders.data(); + auto section = findSectionHeader(headers, elfType::Word(SHT_SYMTAB)); + if (section) { + assert(section->sh_entsize == sizeof(elfType::Sym)); + symbolTable.emplace(fd, buf.st_size, section->sh_offset, + section->sh_size); + if (symbolTable->mapping == nullptr) { + symbolTable.reset(); + } + } + section = findSectionHeader(headers, elfType::Word(SHT_STRTAB)); + if (section) { + stringTable.emplace(fd, buf.st_size, section->sh_offset, + section->sh_size); + if (stringTable->mapping == nullptr) { + stringTable.reset(); + } + } + } + close(fd); + return; + } + + // Find the section header of a specified type in the section headers table. + const elfType::Shdr *findSectionHeader(ArrayRef headers, + elfType::Word sectionType) { + assert(elfHeader.e_shnum == headers.size()); + for (size_t idx = 0; idx < headers.size(); idx++) { + if (idx == elfHeader.e_shstrndx) { + continue; + } + auto header = &headers[idx]; + if (header->sh_type == sectionType) { + return header; + } + } + return nullptr; + } +}; + + +static swift::Lazy TheBinary; + +int +swift::lookupSymbol(const void *address, SymbolInfo *info) { + // The pointers returned point into the mmap()'d binary so keep the + // object once instantiated. + auto &binary = TheBinary.get(); + + info->fileName = binary.getPathName(); + info->baseAddress = binary.getSectionLoadAddress(address); + + auto symbol = binary.findSymbol(address); + if (symbol != nullptr) { + auto address = reinterpret_cast(symbol->getValue()); + info->symbolAddress = reinterpret_cast(address); + info->symbolName.reset(binary.symbolName(symbol)); + } else { + info->symbolAddress = nullptr; + info->symbolName = nullptr; + } + + return 1; +} + +#endif // defined(__ELF__) 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 22da506455703..7d259f673d905 100644 --- a/utils/static-executable-args.lnk +++ b/utils/static-executable-args.lnk @@ -1,17 +1,10 @@ -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 -lstdc++ -lm