Index: vendor/lld/dist/CMakeLists.txt =================================================================== --- vendor/lld/dist/CMakeLists.txt (revision 353949) +++ vendor/lld/dist/CMakeLists.txt (revision 353950) @@ -1,226 +1,225 @@ # Check if lld is built as a standalone project. if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) project(lld) cmake_minimum_required(VERSION 3.4.3) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(LLD_BUILT_STANDALONE TRUE) find_program(LLVM_CONFIG_PATH "llvm-config" DOC "Path to llvm-config binary") if(NOT LLVM_CONFIG_PATH) message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH") endif() execute_process(COMMAND "${LLVM_CONFIG_PATH}" "--obj-root" "--includedir" "--cmakedir" "--src-root" RESULT_VARIABLE HAD_ERROR OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) if(HAD_ERROR) message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") endif() string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}") list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT) list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR) list(GET LLVM_CONFIG_OUTPUT 2 LLVM_CMAKE_PATH) list(GET LLVM_CONFIG_OUTPUT 3 MAIN_SRC_DIR) set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree") set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include") set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR) if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") message(FATAL_ERROR "LLVMConfig.cmake not found") endif() include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake") list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") include_directories("${LLVM_BINARY_DIR}/include" ${LLVM_INCLUDE_DIRS}) link_directories(${LLVM_LIBRARY_DIRS}) set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) include(AddLLVM) include(TableGen) include(HandleLLVMOptions) if(LLVM_INCLUDE_TESTS) - set(Python_ADDITIONAL_VERSIONS 2.7) include(FindPythonInterp) if(NOT PYTHONINTERP_FOUND) message(FATAL_ERROR "Unable to find Python interpreter, required for testing. Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") endif() if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7) message(FATAL_ERROR "Python 2.7 or newer is required") endif() # Check prebuilt llvm/utils. if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX}) set(LLVM_UTILS_PROVIDED ON) endif() if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) # Note: path not really used, except for checking if lit was found set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) if(NOT LLVM_UTILS_PROVIDED) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not) set(LLVM_UTILS_PROVIDED ON) set(LLD_TEST_DEPS FileCheck not) endif() set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest) if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt) add_subdirectory(${UNITTEST_DIR} utils/unittest) endif() else() # Seek installed Lit. find_program(LLVM_LIT NAMES llvm-lit lit.py lit PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit" DOC "Path to lit.py") endif() if(LLVM_LIT) # Define the default arguments to use with 'lit', and an option for the user # to override. set(LIT_ARGS_DEFAULT "-sv") if (MSVC OR XCODE) set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") endif() set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. if(WIN32 AND NOT CYGWIN) set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") endif() else() set(LLVM_INCLUDE_TESTS OFF) endif() endif() endif() set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include ) set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # Compute the LLD version from the LLVM version. string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION ${PACKAGE_VERSION}) message(STATUS "LLD version: ${LLD_VERSION}") string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" LLD_VERSION_MAJOR ${LLD_VERSION}) string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR ${LLD_VERSION}) # Determine LLD revision and repository. # TODO: Figure out a way to get the revision and the repository on windows. if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" ) execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR} OUTPUT_VARIABLE LLD_REVISION) execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR} OUTPUT_VARIABLE LLD_REPOSITORY) if ( LLD_REPOSITORY ) # Replace newline characters with spaces string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY}) # Remove leading spaces STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" ) # Remove trailing spaces string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY}) endif() if ( LLD_REVISION ) # Replace newline characters with spaces string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION}) # Remove leading spaces STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" ) # Remove trailing spaces string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION}) endif() endif () # Configure the Version.inc file. configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in ${CMAKE_CURRENT_BINARY_DIR}/include/lld/Common/Version.inc) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite " "the makefiles distributed with LLVM. Please create a directory and run cmake " "from there, passing the path to this source directory as the last argument. " "This process created the file `CMakeCache.txt' and the directory " "`CMakeFiles'. Please delete them.") endif() list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules") include(AddLLD) option(LLD_USE_VTUNE "Enable VTune user task tracking." OFF) if (LLD_USE_VTUNE) find_package(VTune) if (VTUNE_FOUND) include_directories(${VTune_INCLUDE_DIRS}) list(APPEND LLVM_COMMON_LIBS ${VTune_LIBRARIES}) add_definitions(-DLLD_HAS_VTUNE) endif() endif() option(LLD_BUILD_TOOLS "Build the lld tools. If OFF, just generate build targets." ON) if (MSVC) add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.' add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header. endif() include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include ) if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE ) endif() add_subdirectory(Common) add_subdirectory(lib) add_subdirectory(tools/lld) if (LLVM_INCLUDE_TESTS) add_subdirectory(test) add_subdirectory(unittests) endif() add_subdirectory(docs) add_subdirectory(COFF) add_subdirectory(ELF) add_subdirectory(MinGW) add_subdirectory(wasm) Index: vendor/lld/dist/COFF/CMakeLists.txt =================================================================== --- vendor/lld/dist/COFF/CMakeLists.txt (revision 353949) +++ vendor/lld/dist/COFF/CMakeLists.txt (revision 353950) @@ -1,48 +1,50 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(COFFOptionsTableGen) if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() add_lld_library(lldCOFF Chunks.cpp DebugTypes.cpp DLL.cpp Driver.cpp DriverUtils.cpp ICF.cpp InputFiles.cpp LTO.cpp MapFile.cpp MarkLive.cpp MinGW.cpp PDB.cpp SymbolTable.cpp Symbols.cpp Writer.cpp LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat Core DebugInfoCodeView + DebugInfoDWARF DebugInfoMSF DebugInfoPDB + Demangle LibDriver LTO MC Object Option Support WindowsManifest LINK_LIBS lldCommon ${LLVM_PTHREAD_LIB} DEPENDS COFFOptionsTableGen ${tablegen_deps} ) Index: vendor/lld/dist/COFF/Config.h =================================================================== --- vendor/lld/dist/COFF/Config.h (revision 353949) +++ vendor/lld/dist/COFF/Config.h (revision 353950) @@ -1,231 +1,236 @@ //===- Config.h -------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_CONFIG_H #define LLD_COFF_CONFIG_H #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" #include #include #include #include namespace lld { namespace coff { using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::WindowsSubsystem; using llvm::StringRef; class DefinedAbsolute; class DefinedRelative; class StringChunk; class Symbol; class InputFile; // Short aliases. static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; static const auto ARM64 = llvm::COFF::IMAGE_FILE_MACHINE_ARM64; static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT; static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386; // Represents an /export option. struct Export { StringRef name; // N in /export:N or /export:E=N StringRef extName; // E in /export:E=N Symbol *sym = nullptr; uint16_t ordinal = 0; bool noname = false; bool data = false; bool isPrivate = false; bool constant = false; // If an export is a form of /export:foo=dllname.bar, that means // that foo should be exported as an alias to bar in the DLL. // forwardTo is set to "dllname.bar" part. Usually empty. StringRef forwardTo; StringChunk *forwardChunk = nullptr; // True if this /export option was in .drectves section. bool directives = false; StringRef symbolName; StringRef exportName; // Name in DLL bool operator==(const Export &e) { return (name == e.name && extName == e.extName && ordinal == e.ordinal && noname == e.noname && data == e.data && isPrivate == e.isPrivate); } }; enum class DebugType { None = 0x0, CV = 0x1, /// CodeView PData = 0x2, /// Procedure Data Fixup = 0x4, /// Relocation Table }; enum class GuardCFLevel { Off, NoLongJmp, // Emit gfids but no longjmp tables Full, // Enable all protections. }; // Global configuration. struct Configuration { enum ManifestKind { SideBySide, Embed, No }; bool is64() { return machine == AMD64 || machine == ARM64; } llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN; size_t wordsize; bool verbose = false; WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; Symbol *entry = nullptr; bool noEntry = false; std::string outputFile; std::string importName; bool demangle = true; bool doGC = true; bool doICF = true; bool tailMerge; bool relocatable = true; bool forceMultiple = false; bool forceMultipleRes = false; bool forceUnresolved = false; bool debug = false; bool debugDwarf = false; bool debugGHashes = false; bool debugSymtab = false; bool showTiming = false; bool showSummary = false; unsigned debugTypes = static_cast(DebugType::None); std::vector natvisFiles; llvm::SmallString<128> pdbAltPath; llvm::SmallString<128> pdbPath; llvm::SmallString<128> pdbSourcePath; std::vector argv; // Symbols in this set are considered as live by the garbage collector. std::vector gcroot; std::set noDefaultLibs; bool noDefaultLibAll = false; // True if we are creating a DLL. bool dll = false; StringRef implib; std::vector exports; + bool hadExplicitExports; std::set delayLoads; std::map dllOrder; Symbol *delayLoadHelper = nullptr; bool saveTemps = false; // /guard:cf GuardCFLevel guardCF = GuardCFLevel::Off; // Used for SafeSEH. bool safeSEH = false; Symbol *sehTable = nullptr; Symbol *sehCount = nullptr; // Used for /opt:lldlto=N unsigned ltoo = 2; // Used for /opt:lldltojobs=N unsigned thinLTOJobs = 0; // Used for /opt:lldltopartitions=N unsigned ltoPartitions = 1; // Used for /opt:lldltocache=path StringRef ltoCache; // Used for /opt:lldltocachepolicy=policy llvm::CachePruningPolicy ltoCachePolicy; // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map merge; // Used for /section=.name,{DEKPRSW} to set section attributes. std::map section; // Options for manifest files. ManifestKind manifest = No; int manifestID = 1; StringRef manifestDependency; bool manifestUAC = true; std::vector manifestInput; StringRef manifestLevel = "'asInvoker'"; StringRef manifestUIAccess = "'false'"; StringRef manifestFile; // Used for /aligncomm. std::map alignComm; // Used for /failifmismatch. std::map> mustMatch; // Used for /alternatename. std::map alternateNames; // Used for /order. llvm::StringMap order; // Used for /lldmap. std::string mapFile; // Used for /thinlto-index-only: llvm::StringRef thinLTOIndexOnlyArg; // Used for /thinlto-object-prefix-replace: std::pair thinLTOPrefixReplace; // Used for /thinlto-object-suffix-replace: std::pair thinLTOObjectSuffixReplace; + // Used for /lto-obj-path: + llvm::StringRef ltoObjPath; + + uint64_t align = 4096; uint64_t imageBase = -1; uint64_t fileAlign = 512; uint64_t stackReserve = 1024 * 1024; uint64_t stackCommit = 4096; uint64_t heapReserve = 1024 * 1024; uint64_t heapCommit = 4096; uint32_t majorImageVersion = 0; uint32_t minorImageVersion = 0; uint32_t majorOSVersion = 6; uint32_t minorOSVersion = 0; uint32_t timestamp = 0; uint32_t functionPadMin = 0; bool dynamicBase = true; bool allowBind = true; bool nxCompat = true; bool allowIsolation = true; bool terminalServerAware = true; bool largeAddressAware = false; bool highEntropyVA = false; bool appContainer = false; bool mingw = false; bool warnMissingOrderSymbol = true; bool warnLocallyDefinedImported = true; bool warnDebugInfoUnusable = true; bool incremental = true; bool integrityCheck = false; bool killAt = false; bool repro = false; bool swaprunCD = false; bool swaprunNet = false; bool thinLTOEmitImportsFiles; bool thinLTOIndexOnly; }; extern Configuration *config; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/DLL.cpp =================================================================== --- vendor/lld/dist/COFF/DLL.cpp (revision 353949) +++ vendor/lld/dist/COFF/DLL.cpp (revision 353950) @@ -1,736 +1,736 @@ //===- DLL.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines various types of chunks for the DLL import or export // descriptor tables. They are inherently Windows-specific. // You need to read Microsoft PE/COFF spec to understand details // about the data structures. // // If you are not particularly interested in linking against Windows // DLL, you can skip this file, and you should still be able to // understand the rest of the linker. // //===----------------------------------------------------------------------===// #include "DLL.h" #include "Chunks.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::COFF; namespace lld { namespace coff { namespace { // Import table // A chunk for the import descriptor table. class HintNameChunk : public NonSectionChunk { public: HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {} size_t getSize() const override { // Starts with 2 byte Hint field, followed by a null-terminated string, // ends with 0 or 1 byte padding. return alignTo(name.size() + 3, 2); } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); write16le(buf, hint); memcpy(buf + 2, name.data(), name.size()); } private: StringRef name; uint16_t hint; }; // A chunk for the import descriptor table. class LookupChunk : public NonSectionChunk { public: explicit LookupChunk(Chunk *c) : hintName(c) { setAlignment(config->wordsize); } size_t getSize() const override { return config->wordsize; } void writeTo(uint8_t *buf) const override { if (config->is64()) write64le(buf, hintName->getRVA()); else write32le(buf, hintName->getRVA()); } Chunk *hintName; }; // A chunk for the import descriptor table. // This chunk represent import-by-ordinal symbols. // See Microsoft PE/COFF spec 7.1. Import Header for details. class OrdinalOnlyChunk : public NonSectionChunk { public: explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) { setAlignment(config->wordsize); } size_t getSize() const override { return config->wordsize; } void writeTo(uint8_t *buf) const override { // An import-by-ordinal slot has MSB 1 to indicate that // this is import-by-ordinal (and not import-by-name). if (config->is64()) { write64le(buf, (1ULL << 63) | ordinal); } else { write32le(buf, (1ULL << 31) | ordinal); } } uint16_t ordinal; }; // A chunk for the import descriptor table. class ImportDirectoryChunk : public NonSectionChunk { public: explicit ImportDirectoryChunk(Chunk *n) : dllName(n) {} size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); auto *e = (coff_import_directory_table_entry *)(buf); e->ImportLookupTableRVA = lookupTab->getRVA(); e->NameRVA = dllName->getRVA(); e->ImportAddressTableRVA = addressTab->getRVA(); } Chunk *dllName; Chunk *lookupTab; Chunk *addressTab; }; // A chunk representing null terminator in the import table. // Contents of this chunk is always null bytes. class NullChunk : public NonSectionChunk { public: explicit NullChunk(size_t n) : size(n) { hasData = false; } size_t getSize() const override { return size; } void writeTo(uint8_t *buf) const override { memset(buf, 0, size); } private: size_t size; }; static std::vector> binImports(const std::vector &imports) { // Group DLL-imported symbols by DLL name because that's how - // symbols are layed out in the import descriptor table. + // symbols are laid out in the import descriptor table. auto less = [](const std::string &a, const std::string &b) { return config->dllOrder[a] < config->dllOrder[b]; }; std::map, bool(*)(const std::string &, const std::string &)> m(less); for (DefinedImportData *sym : imports) m[sym->getDLLName().lower()].push_back(sym); std::vector> v; for (auto &kv : m) { // Sort symbols by name for each group. std::vector &syms = kv.second; std::sort(syms.begin(), syms.end(), [](DefinedImportData *a, DefinedImportData *b) { return a->getName() < b->getName(); }); v.push_back(std::move(syms)); } return v; } // Export table // See Microsoft PE/COFF spec 4.3 for details. // A chunk for the delay import descriptor table etnry. class DelayDirectoryChunk : public NonSectionChunk { public: explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {} size_t getSize() const override { return sizeof(delay_import_directory_table_entry); } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); auto *e = (delay_import_directory_table_entry *)(buf); e->Attributes = 1; e->Name = dllName->getRVA(); e->ModuleHandle = moduleHandle->getRVA(); e->DelayImportAddressTable = addressTab->getRVA(); e->DelayImportNameTable = nameTab->getRVA(); } Chunk *dllName; Chunk *moduleHandle; Chunk *addressTab; Chunk *nameTab; }; // Initial contents for delay-loaded functions. // This code calls __delayLoadHelper2 function to resolve a symbol -// and then overwrites its jump table slot with the result +// which then overwrites its jump table slot with the result // for subsequent function calls. static const uint8_t thunkX64[] = { 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_] 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ }; static const uint8_t tailMergeX64[] = { 0x51, // push rcx 0x52, // push rdx 0x41, 0x50, // push r8 0x41, 0x51, // push r9 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 0x48, 0x8B, 0xD0, // mov rdx, rax 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h 0x41, 0x59, // pop r9 0x41, 0x58, // pop r8 0x5A, // pop rdx 0x59, // pop rcx 0xFF, 0xE0, // jmp rax }; static const uint8_t thunkX86[] = { 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ }; static const uint8_t tailMergeX86[] = { 0x51, // push ecx 0x52, // push edx 0x50, // push eax 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR__dll 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 0x5A, // pop edx 0x59, // pop ecx 0xFF, 0xE0, // jmp eax }; static const uint8_t thunkARM[] = { 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_ 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_ 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_ }; static const uint8_t tailMergeARM[] = { 0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr} 0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16 0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7} 0x61, 0x46, // mov r1, ip 0x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR 0xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper2 0x84, 0x46, // mov ip, r0 0xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7} 0xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr} 0x60, 0x47, // bx ip }; static const uint8_t thunkARM64[] = { 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_ 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_ 0x00, 0x00, 0x00, 0x14, // b __tailMerge_ }; static const uint8_t tailMergeARM64[] = { 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! 0xfd, 0x03, 0x00, 0x91, // mov x29, sp 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32] 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48] 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64] 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80] 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112] 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144] 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176] 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112] 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80] 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64] 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48] 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32] 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16] 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208 0x00, 0x02, 0x1f, 0xd6, // br x16 }; // A chunk for the delay import thunk. class ThunkChunkX64 : public NonSectionChunk { public: ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} size_t getSize() const override { return sizeof(thunkX64); } void writeTo(uint8_t *buf) const override { memcpy(buf, thunkX64, sizeof(thunkX64)); write32le(buf + 3, imp->getRVA() - rva - 7); write32le(buf + 8, tailMerge->getRVA() - rva - 12); } Defined *imp = nullptr; Chunk *tailMerge = nullptr; }; class TailMergeChunkX64 : public NonSectionChunk { public: TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {} size_t getSize() const override { return sizeof(tailMergeX64); } void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeX64, sizeof(tailMergeX64)); write32le(buf + 39, desc->getRVA() - rva - 43); write32le(buf + 44, helper->getRVA() - rva - 48); } Chunk *desc = nullptr; Defined *helper = nullptr; }; class ThunkChunkX86 : public NonSectionChunk { public: ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} size_t getSize() const override { return sizeof(thunkX86); } void writeTo(uint8_t *buf) const override { memcpy(buf, thunkX86, sizeof(thunkX86)); write32le(buf + 1, imp->getRVA() + config->imageBase); write32le(buf + 6, tailMerge->getRVA() - rva - 10); } void getBaserels(std::vector *res) override { res->emplace_back(rva + 1); } Defined *imp = nullptr; Chunk *tailMerge = nullptr; }; class TailMergeChunkX86 : public NonSectionChunk { public: TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {} size_t getSize() const override { return sizeof(tailMergeX86); } void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeX86, sizeof(tailMergeX86)); write32le(buf + 4, desc->getRVA() + config->imageBase); write32le(buf + 9, helper->getRVA() - rva - 13); } void getBaserels(std::vector *res) override { res->emplace_back(rva + 4); } Chunk *desc = nullptr; Defined *helper = nullptr; }; class ThunkChunkARM : public NonSectionChunk { public: ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} size_t getSize() const override { return sizeof(thunkARM); } void writeTo(uint8_t *buf) const override { memcpy(buf, thunkARM, sizeof(thunkARM)); applyMOV32T(buf + 0, imp->getRVA() + config->imageBase); applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12); } void getBaserels(std::vector *res) override { res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T); } Defined *imp = nullptr; Chunk *tailMerge = nullptr; }; class TailMergeChunkARM : public NonSectionChunk { public: TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {} size_t getSize() const override { return sizeof(tailMergeARM); } void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeARM, sizeof(tailMergeARM)); applyMOV32T(buf + 14, desc->getRVA() + config->imageBase); applyBranch24T(buf + 22, helper->getRVA() - rva - 26); } void getBaserels(std::vector *res) override { res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T); } Chunk *desc = nullptr; Defined *helper = nullptr; }; class ThunkChunkARM64 : public NonSectionChunk { public: ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} size_t getSize() const override { return sizeof(thunkARM64); } void writeTo(uint8_t *buf) const override { memcpy(buf, thunkARM64, sizeof(thunkARM64)); applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12); applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0); applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8); } Defined *imp = nullptr; Chunk *tailMerge = nullptr; }; class TailMergeChunkARM64 : public NonSectionChunk { public: TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {} size_t getSize() const override { return sizeof(tailMergeARM64); } void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64)); applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12); applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0); applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52); } Chunk *desc = nullptr; Defined *helper = nullptr; }; // A chunk for the import descriptor table. class DelayAddressChunk : public NonSectionChunk { public: explicit DelayAddressChunk(Chunk *c) : thunk(c) { setAlignment(config->wordsize); } size_t getSize() const override { return config->wordsize; } void writeTo(uint8_t *buf) const override { if (config->is64()) { write64le(buf, thunk->getRVA() + config->imageBase); } else { uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. if (config->machine == ARMNT) bit = 1; write32le(buf, (thunk->getRVA() + config->imageBase) | bit); } } void getBaserels(std::vector *res) override { res->emplace_back(rva); } Chunk *thunk; }; // Export table // Read Microsoft PE/COFF spec 5.3 for details. // A chunk for the export descriptor table. class ExportDirectoryChunk : public NonSectionChunk { public: ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o) : maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n), ordinalTab(o) {} size_t getSize() const override { return sizeof(export_directory_table_entry); } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); auto *e = (export_directory_table_entry *)(buf); e->NameRVA = dllName->getRVA(); e->OrdinalBase = 0; e->AddressTableEntries = maxOrdinal + 1; e->NumberOfNamePointers = nameTabSize; e->ExportAddressTableRVA = addressTab->getRVA(); e->NamePointerRVA = nameTab->getRVA(); e->OrdinalTableRVA = ordinalTab->getRVA(); } uint16_t maxOrdinal; uint16_t nameTabSize; Chunk *dllName; Chunk *addressTab; Chunk *nameTab; Chunk *ordinalTab; }; class AddressTableChunk : public NonSectionChunk { public: explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {} size_t getSize() const override { return size * 4; } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); for (const Export &e : config->exports) { uint8_t *p = buf + e.ordinal * 4; uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. if (config->machine == ARMNT && !e.data) bit = 1; if (e.forwardChunk) { write32le(p, e.forwardChunk->getRVA() | bit); } else { write32le(p, cast(e.sym)->getRVA() | bit); } } } private: size_t size; }; class NamePointersChunk : public NonSectionChunk { public: explicit NamePointersChunk(std::vector &v) : chunks(v) {} size_t getSize() const override { return chunks.size() * 4; } void writeTo(uint8_t *buf) const override { for (Chunk *c : chunks) { write32le(buf, c->getRVA()); buf += 4; } } private: std::vector chunks; }; class ExportOrdinalChunk : public NonSectionChunk { public: explicit ExportOrdinalChunk(size_t i) : size(i) {} size_t getSize() const override { return size * 2; } void writeTo(uint8_t *buf) const override { for (Export &e : config->exports) { if (e.noname) continue; write16le(buf, e.ordinal); buf += 2; } } private: size_t size; }; } // anonymous namespace void IdataContents::create() { std::vector> v = binImports(imports); // Create .idata contents for each DLL. for (std::vector &syms : v) { // Create lookup and address tables. If they have external names, // we need to create hintName chunks to store the names. // If they don't (if they are import-by-ordinals), we store only // ordinal values to the table. size_t base = lookups.size(); for (DefinedImportData *s : syms) { uint16_t ord = s->getOrdinal(); if (s->getExternalName().empty()) { lookups.push_back(make(ord)); addresses.push_back(make(ord)); continue; } auto *c = make(s->getExternalName(), ord); lookups.push_back(make(c)); addresses.push_back(make(c)); hints.push_back(c); } // Terminate with null values. lookups.push_back(make(config->wordsize)); addresses.push_back(make(config->wordsize)); for (int i = 0, e = syms.size(); i < e; ++i) syms[i]->setLocation(addresses[base + i]); // Create the import table header. dllNames.push_back(make(syms[0]->getDLLName())); auto *dir = make(dllNames.back()); dir->lookupTab = lookups[base]; dir->addressTab = addresses[base]; dirs.push_back(dir); } // Add null terminator. dirs.push_back(make(sizeof(ImportDirectoryTableEntry))); } std::vector DelayLoadContents::getChunks() { std::vector v; v.insert(v.end(), dirs.begin(), dirs.end()); v.insert(v.end(), names.begin(), names.end()); v.insert(v.end(), hintNames.begin(), hintNames.end()); v.insert(v.end(), dllNames.begin(), dllNames.end()); return v; } std::vector DelayLoadContents::getDataChunks() { std::vector v; v.insert(v.end(), moduleHandles.begin(), moduleHandles.end()); v.insert(v.end(), addresses.begin(), addresses.end()); return v; } uint64_t DelayLoadContents::getDirSize() { return dirs.size() * sizeof(delay_import_directory_table_entry); } void DelayLoadContents::create(Defined *h) { helper = h; std::vector> v = binImports(imports); // Create .didat contents for each DLL. for (std::vector &syms : v) { // Create the delay import table header. dllNames.push_back(make(syms[0]->getDLLName())); auto *dir = make(dllNames.back()); size_t base = addresses.size(); Chunk *tm = newTailMergeChunk(dir); for (DefinedImportData *s : syms) { Chunk *t = newThunkChunk(s, tm); auto *a = make(t); addresses.push_back(a); thunks.push_back(t); StringRef extName = s->getExternalName(); if (extName.empty()) { names.push_back(make(s->getOrdinal())); } else { auto *c = make(extName, 0); names.push_back(make(c)); hintNames.push_back(c); } } thunks.push_back(tm); // Terminate with null values. addresses.push_back(make(8)); names.push_back(make(8)); for (int i = 0, e = syms.size(); i < e; ++i) syms[i]->setLocation(addresses[base + i]); auto *mh = make(8); mh->setAlignment(8); moduleHandles.push_back(mh); // Fill the delay import table header fields. dir->moduleHandle = mh; dir->addressTab = addresses[base]; dir->nameTab = names[base]; dirs.push_back(dir); } // Add null terminator. dirs.push_back(make(sizeof(delay_import_directory_table_entry))); } Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { switch (config->machine) { case AMD64: return make(dir, helper); case I386: return make(dir, helper); case ARMNT: return make(dir, helper); case ARM64: return make(dir, helper); default: llvm_unreachable("unsupported machine type"); } } Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, Chunk *tailMerge) { switch (config->machine) { case AMD64: return make(s, tailMerge); case I386: return make(s, tailMerge); case ARMNT: return make(s, tailMerge); case ARM64: return make(s, tailMerge); default: llvm_unreachable("unsupported machine type"); } } EdataContents::EdataContents() { uint16_t maxOrdinal = 0; for (Export &e : config->exports) maxOrdinal = std::max(maxOrdinal, e.ordinal); auto *dllName = make(sys::path::filename(config->outputFile)); auto *addressTab = make(maxOrdinal); std::vector names; for (Export &e : config->exports) if (!e.noname) names.push_back(make(e.exportName)); std::vector forwards; for (Export &e : config->exports) { if (e.forwardTo.empty()) continue; e.forwardChunk = make(e.forwardTo); forwards.push_back(e.forwardChunk); } auto *nameTab = make(names); auto *ordinalTab = make(names.size()); auto *dir = make(maxOrdinal, names.size(), dllName, addressTab, nameTab, ordinalTab); chunks.push_back(dir); chunks.push_back(dllName); chunks.push_back(addressTab); chunks.push_back(nameTab); chunks.push_back(ordinalTab); chunks.insert(chunks.end(), names.begin(), names.end()); chunks.insert(chunks.end(), forwards.begin(), forwards.end()); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/DebugTypes.cpp =================================================================== --- vendor/lld/dist/COFF/DebugTypes.cpp (revision 353949) +++ vendor/lld/dist/COFF/DebugTypes.cpp (revision 353950) @@ -1,268 +1,267 @@ //===- DebugTypes.cpp -----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "DebugTypes.h" #include "Driver.h" #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/Support/Path.h" -using namespace lld; -using namespace lld::coff; using namespace llvm; using namespace llvm::codeview; +namespace lld { +namespace coff { + namespace { // The TypeServerSource class represents a PDB type server, a file referenced by // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ // files, therefore there must be only once instance per OBJ lot. The file path // is discovered from the dependent OBJ's debug type stream. The // TypeServerSource object is then queued and loaded by the COFF Driver. The // debug type stream for such PDB files will be merged first in the final PDB, // before any dependent OBJ. class TypeServerSource : public TpiSource { public: explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s) : TpiSource(PDB, nullptr), session(s), mb(m) {} // Queue a PDB type server for loading in the COFF Driver static void enqueue(const ObjFile *dependentFile, const TypeServer2Record &ts); // Create an instance static Expected getInstance(MemoryBufferRef m); // Fetch the PDB instance loaded for a corresponding dependent OBJ. static Expected findFromFile(const ObjFile *dependentFile); static std::map> instances; // The interface to the PDB (if it was opened successfully) std::unique_ptr session; private: MemoryBufferRef mb; }; // This class represents the debug type stream of an OBJ file that depends on a // PDB type server (see TypeServerSource). class UseTypeServerSource : public TpiSource { public: UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) : TpiSource(UsingPDB, f), typeServerDependency(*ts) {} // Information about the PDB type server dependency, that needs to be loaded // in before merging this OBJ. TypeServer2Record typeServerDependency; }; // This class represents the debug type stream of a Microsoft precompiled // headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output // PDB, before any other OBJs that depend on this. Note that only MSVC generate // such files, clang does not. class PrecompSource : public TpiSource { public: PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {} }; // This class represents the debug type stream of an OBJ file that depends on a // Microsoft precompiled headers OBJ (see PrecompSource). class UsePrecompSource : public TpiSource { public: UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) : TpiSource(UsingPCH, f), precompDependency(*precomp) {} // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. PrecompRecord precompDependency; }; } // namespace static std::vector> GC; TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { GC.push_back(std::unique_ptr(this)); } -TpiSource *lld::coff::makeTpiSource(const ObjFile *f) { +TpiSource *makeTpiSource(const ObjFile *f) { return new TpiSource(TpiSource::Regular, f); } -TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, +TpiSource *makeUseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) { TypeServerSource::enqueue(f, *ts); return new UseTypeServerSource(f, ts); } -TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { +TpiSource *makePrecompSource(const ObjFile *f) { return new PrecompSource(f); } -TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, +TpiSource *makeUsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) { return new UsePrecompSource(f, precomp); } -namespace lld { -namespace coff { template <> const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { assert(source->kind == TpiSource::UsingPCH); return ((const UsePrecompSource *)source)->precompDependency; } template <> const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { assert(source->kind == TpiSource::UsingPDB); return ((const UseTypeServerSource *)source)->typeServerDependency; } -} // namespace coff -} // namespace lld std::map> TypeServerSource::instances; // Make a PDB path assuming the PDB is in the same folder as the OBJ static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) { StringRef localPath = !file->parentName.empty() ? file->parentName : file->getName(); SmallString<128> path = sys::path::parent_path(localPath); // Currently, type server PDBs are only created by MSVC cl, which only runs // on Windows, so we can assume type server paths are Windows style. sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows)); return path.str(); } // The casing of the PDB path stamped in the OBJ can differ from the actual path // on disk. With this, we ensure to always use lowercase as a key for the // PDBInputFile::Instances map, at least on Windows. static std::string normalizePdbPath(StringRef path) { #if defined(_WIN32) return path.lower(); #else // LINUX return path; #endif } // If existing, return the actual PDB path on disk. static Optional findPdbPath(StringRef pdbPath, const ObjFile *dependentFile) { // Ensure the file exists before anything else. In some cases, if the path // points to a removable device, Driver::enqueuePath() would fail with an // error (EAGAIN, "resource unavailable try again") which we want to skip // silently. if (llvm::sys::fs::exists(pdbPath)) return normalizePdbPath(pdbPath); std::string ret = getPdbBaseName(dependentFile, pdbPath); if (llvm::sys::fs::exists(ret)) return normalizePdbPath(ret); return None; } // Fetch the PDB instance that was already loaded by the COFF Driver. Expected TypeServerSource::findFromFile(const ObjFile *dependentFile) { const TypeServer2Record &ts = retrieveDependencyInfo(dependentFile->debugTypesObj); Optional p = findPdbPath(ts.Name, dependentFile); if (!p) return createFileError(ts.Name, errorCodeToError(std::error_code( ENOENT, std::generic_category()))); auto it = TypeServerSource::instances.find(*p); // The PDB file exists on disk, at this point we expect it to have been // inserted in the map by TypeServerSource::loadPDB() assert(it != TypeServerSource::instances.end()); std::pair &pdb = it->second; if (!pdb.second) return createFileError( *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str())); pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile(); pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); // Just because a file with a matching name was found doesn't mean it can be // used. The GUID must match between the PDB header and the OBJ // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. if (info.getGuid() != ts.getGuid()) return createFileError( ts.Name, make_error(pdb::pdb_error_code::signature_out_of_date)); return pdb.second; } // FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is // moved here. -Expected -lld::coff::findTypeServerSource(const ObjFile *f) { +Expected findTypeServerSource(const ObjFile *f) { Expected ts = TypeServerSource::findFromFile(f); if (!ts) return ts.takeError(); return ts.get()->session.get(); } // Queue a PDB type server for loading in the COFF Driver void TypeServerSource::enqueue(const ObjFile *dependentFile, const TypeServer2Record &ts) { // Start by finding where the PDB is located (either the record path or next // to the OBJ file) Optional p = findPdbPath(ts.Name, dependentFile); if (!p) return; auto it = TypeServerSource::instances.emplace( *p, std::pair{}); if (!it.second) return; // another OBJ already scheduled this PDB for load - driver->enqueuePath(*p, false); + driver->enqueuePath(*p, false, false); } // Create an instance of TypeServerSource or an error string if the PDB couldn't // be loaded. The error message will be displayed later, when the referring OBJ // will be merged in. NOTE - a PDB load failure is not a link error: some // debug info will simply be missing from the final PDB - that is the default // accepted behavior. -void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { +void loadTypeServerSource(llvm::MemoryBufferRef m) { std::string path = normalizePdbPath(m.getBufferIdentifier()); Expected ts = TypeServerSource::getInstance(m); if (!ts) TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; else TypeServerSource::instances[path] = {{}, *ts}; } Expected TypeServerSource::getInstance(MemoryBufferRef m) { std::unique_ptr iSession; Error err = pdb::NativeSession::createFromPdb( MemoryBuffer::getMemBuffer(m, false), iSession); if (err) return std::move(err); std::unique_ptr session( static_cast(iSession.release())); pdb::PDBFile &pdbFile = session->getPDBFile(); Expected info = pdbFile.getPDBInfoStream(); // All PDB Files should have an Info stream. if (!info) return info.takeError(); return new TypeServerSource(m, session.release()); } + +} // namespace coff +} // namespace lld Index: vendor/lld/dist/COFF/Driver.cpp =================================================================== --- vendor/lld/dist/COFF/Driver.cpp (revision 353949) +++ vendor/lld/dist/COFF/Driver.cpp (revision 353950) @@ -1,1891 +1,1976 @@ //===- Driver.cpp ---------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" #include "DebugTypes.h" #include "ICF.h" #include "InputFiles.h" #include "MarkLive.h" #include "MinGW.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" #include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/LTO/LTO.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFModuleDefinition.h" #include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Debug.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" #include #include #include using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; using llvm::sys::Process; namespace lld { namespace coff { static Timer inputFileTimer("Input File Reading", Timer::root()); Configuration *config; LinkerDriver *driver; bool link(ArrayRef args, bool canExitEarly, raw_ostream &diag) { errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().errorOS = &diag; - errorHandler().colorDiagnostics = diag.has_colors(); errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now" " (use /errorlimit:0 to see all errors)"; errorHandler().exitEarly = canExitEarly; - config = make(); + enableColors(diag.has_colors()); + config = make(); symtab = make(); - driver = make(); + driver->link(args); // Call exit() if we can to avoid calling destructors. if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); ObjFile::instances.clear(); ImportFile::instances.clear(); BitcodeFile::instances.clear(); memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); return !errorCount(); } // Parse options of the form "old;new". static std::pair getOldNewOptions(opt::InputArgList &args, unsigned id) { auto *arg = args.getLastArg(id); if (!arg) return {"", ""}; StringRef s = arg->getValue(); std::pair ret = s.split(';'); if (ret.second.empty()) error(arg->getSpelling() + " expects 'old;new' format, but got " + s); return ret; } // Drop directory components and replace extension with ".exe" or ".dll". static std::string getOutputPath(StringRef path) { auto p = path.find_last_of("\\/"); StringRef s = (p == StringRef::npos) ? path : path.substr(p + 1); const char* e = config->dll ? ".dll" : ".exe"; return (s.substr(0, s.rfind('.')) + e).str(); } // Returns true if S matches /crtend.?\.o$/. static bool isCrtend(StringRef s) { if (!s.endswith(".o")) return false; s = s.drop_back(2); if (s.endswith("crtend")) return true; return !s.empty() && s.drop_back().endswith("crtend"); } // ErrorOr is not default constructible, so it cannot be used as the type // parameter of a future. // FIXME: We could open the file in createFutureForFile and avoid needing to // return an error here, but for the moment that would cost us a file descriptor // (a limited resource on Windows) for the duration that the future is pending. using MBErrPair = std::pair, std::error_code>; // Create a std::future that opens and maps a file using the best strategy for // the host platform. static std::future createFutureForFile(std::string path) { #if _WIN32 // On Windows, file I/O is relatively slow so it is best to do this // asynchronously. auto strategy = std::launch::async; #else auto strategy = std::launch::deferred; #endif return std::async(strategy, [=]() { auto mbOrErr = MemoryBuffer::getFile(path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); if (!mbOrErr) return MBErrPair{nullptr, mbOrErr.getError()}; return MBErrPair{std::move(*mbOrErr), std::error_code()}; }); } // Symbol names are mangled by prepending "_" on x86. static StringRef mangle(StringRef sym) { assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); if (config->machine == I386) return saver.save("_" + sym); return sym; } static bool findUnderscoreMangle(StringRef sym) { Symbol *s = symtab->findMangle(mangle(sym)); return s && !isa(s); } MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr mb) { MemoryBufferRef mbref = *mb; make>(std::move(mb)); // take ownership if (driver->tar) driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()), mbref.getBuffer()); return mbref; } void LinkerDriver::addBuffer(std::unique_ptr mb, - bool wholeArchive) { + bool wholeArchive, bool lazy) { StringRef filename = mb->getBufferIdentifier(); MemoryBufferRef mbref = takeBuffer(std::move(mb)); filePaths.push_back(filename); // File type is detected by contents, not by file extension. switch (identify_magic(mbref.getBuffer())) { case file_magic::windows_resource: resources.push_back(mbref); break; case file_magic::archive: if (wholeArchive) { std::unique_ptr file = CHECK(Archive::create(mbref), filename + ": failed to parse archive"); + Archive *archive = file.get(); + make>(std::move(file)); // take ownership - for (MemoryBufferRef m : getArchiveMembers(file.get())) - addArchiveBuffer(m, "", filename, 0); + int memberIndex = 0; + for (MemoryBufferRef m : getArchiveMembers(archive)) + addArchiveBuffer(m, "", filename, memberIndex++); return; } symtab->addFile(make(mbref)); break; case file_magic::bitcode: - symtab->addFile(make(mbref, "", 0)); + if (lazy) + symtab->addFile(make(mbref)); + else + symtab->addFile(make(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - symtab->addFile(make(mbref)); + if (lazy) + symtab->addFile(make(mbref)); + else + symtab->addFile(make(mbref)); break; case file_magic::pdb: loadTypeServerSource(mbref); break; case file_magic::coff_cl_gl_object: error(filename + ": is not a native COFF file. Recompile without /GL"); break; case file_magic::pecoff_executable: if (filename.endswith_lower(".dll")) { error(filename + ": bad file type. Did you specify a DLL instead of an " "import library?"); break; } LLVM_FALLTHROUGH; default: error(mbref.getBufferIdentifier() + ": unknown file type"); break; } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { auto future = std::make_shared>(createFutureForFile(path)); std::string pathStr = path; enqueueTask([=]() { auto mbOrErr = future->get(); if (mbOrErr.second) { std::string msg = "could not open '" + pathStr + "': " + mbOrErr.second.message(); // Check if the filename is a typo for an option flag. OptTable thinks // that all args that are not known options and that start with / are // filenames, but e.g. `/nodefaultlibs` is more likely a typo for // the option `/nodefaultlib` than a reference to a file in the root // directory. std::string nearest; if (COFFOptTable().findNearest(pathStr, nearest) > 1) error(msg); else error(msg + "; did you mean '" + nearest + "'"); } else - driver->addBuffer(std::move(mbOrErr.first), wholeArchive); + driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); } void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, StringRef parentName, uint64_t offsetInArchive) { file_magic magic = identify_magic(mb.getBuffer()); if (magic == file_magic::coff_import_library) { InputFile *imp = make(mb); imp->parentName = parentName; symtab->addFile(imp); return; } InputFile *obj; if (magic == file_magic::coff_object) { obj = make(mb); } else if (magic == file_magic::bitcode) { obj = make(mb, parentName, offsetInArchive); } else { error("unknown file type: " + mb.getBufferIdentifier()); return; } obj->parentName = parentName; symtab->addFile(obj); log("Loaded " + toString(obj) + " for " + symName); } void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, - StringRef symName, + const Archive::Symbol &sym, StringRef parentName) { - auto reportBufferError = [=](Error &&e, - StringRef childName) { + auto reportBufferError = [=](Error &&e, StringRef childName) { fatal("could not get the buffer for the member defining symbol " + - symName + ": " + parentName + "(" + childName + "): " + + toCOFFString(sym) + ": " + parentName + "(" + childName + "): " + toString(std::move(e))); }; if (!c.getParent()->isThin()) { uint64_t offsetInArchive = c.getChildOffset(); Expected mbOrErr = c.getMemoryBufferRef(); if (!mbOrErr) reportBufferError(mbOrErr.takeError(), check(c.getFullName())); MemoryBufferRef mb = mbOrErr.get(); enqueueTask([=]() { - driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive); + driver->addArchiveBuffer(mb, toCOFFString(sym), parentName, + offsetInArchive); }); return; } std::string childName = CHECK( c.getFullName(), "could not get the filename for the member defining symbol " + - symName); + toCOFFString(sym)); auto future = std::make_shared>( createFutureForFile(childName)); enqueueTask([=]() { auto mbOrErr = future->get(); if (mbOrErr.second) reportBufferError(errorCodeToError(mbOrErr.second), childName); - driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName, - parentName, /* OffsetInArchive */ 0); + // Pass empty string as archive name so that the original filename is + // used as the buffer identifier. + driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), + toCOFFString(sym), "", /*OffsetInArchive=*/0); }); } static bool isDecorated(StringRef sym) { return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") || (!config->mingw && sym.contains('@')); } // Parses .drectve section contents and returns a list of files // specified by /defaultlib. void LinkerDriver::parseDirectives(InputFile *file) { StringRef s = file->getDirectives(); if (s.empty()) return; log("Directives: " + toString(file) + ": " + s); ArgParser parser; // .drectve is always tokenized using Windows shell rules. // /EXPORT: option can appear too many times, processing in fastpath. opt::InputArgList args; std::vector exports; std::tie(args, exports) = parser.parseDirectives(s); for (StringRef e : exports) { // If a common header file contains dllexported function // declarations, many object files may end up with having the // same /EXPORT options. In order to save cost of parsing them, // we dedup them first. if (!directivesExports.insert(e).second) continue; Export exp = parseExport(e); if (config->machine == I386 && config->mingw) { if (!isDecorated(exp.name)) exp.name = saver.save("_" + exp.name); if (!exp.extName.empty() && !isDecorated(exp.extName)) exp.extName = saver.save("_" + exp.extName); } exp.directives = true; config->exports.push_back(exp); } for (auto *arg : args) { switch (arg->getOption().getID()) { case OPT_aligncomm: parseAligncomm(arg->getValue()); break; case OPT_alternatename: parseAlternateName(arg->getValue()); break; case OPT_defaultlib: if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); break; case OPT_entry: config->entry = addUndefined(mangle(arg->getValue())); break; case OPT_failifmismatch: checkFailIfMismatch(arg->getValue(), file); break; case OPT_incl: addUndefined(arg->getValue()); break; case OPT_merge: parseMerge(arg->getValue()); break; case OPT_nodefaultlib: config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); break; case OPT_section: parseSection(arg->getValue()); break; case OPT_subsystem: parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion, &config->minorOSVersion); break; // Only add flags here that link.exe accepts in // `#pragma comment(linker, "/flag")`-generated sections. case OPT_editandcontinue: case OPT_guardsym: case OPT_throwingnew: break; default: error(arg->getSpelling() + " is not allowed in .drectve"); } } } // Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. StringRef LinkerDriver::doFindFile(StringRef filename) { bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); if (hasPathSep) return filename; bool hasExt = filename.contains('.'); for (StringRef dir : searchPaths) { SmallString<128> path = dir; sys::path::append(path, filename); if (sys::fs::exists(path.str())) return saver.save(path.str()); if (!hasExt) { path.append(".obj"); if (sys::fs::exists(path.str())) return saver.save(path.str()); } } return filename; } static Optional getUniqueID(StringRef path) { sys::fs::UniqueID ret; if (sys::fs::getUniqueID(path, ret)) return None; return ret; } // Resolves a file path. This never returns the same path // (in that case, it returns None). Optional LinkerDriver::findFile(StringRef filename) { StringRef path = doFindFile(filename); if (Optional id = getUniqueID(path)) { bool seen = !visitedFiles.insert(*id).second; if (seen) return None; } if (path.endswith_lower(".lib")) visitedLibs.insert(sys::path::filename(path)); return path; } // MinGW specific. If an embedded directive specified to link to // foo.lib, but it isn't found, try libfoo.a instead. StringRef LinkerDriver::doFindLibMinGW(StringRef filename) { if (filename.contains('/') || filename.contains('\\')) return filename; SmallString<128> s = filename; sys::path::replace_extension(s, ".a"); StringRef libName = saver.save("lib" + s.str()); return doFindFile(libName); } // Find library file from search path. StringRef LinkerDriver::doFindLib(StringRef filename) { // Add ".lib" to Filename if that has no file extension. bool hasExt = filename.contains('.'); if (!hasExt) filename = saver.save(filename + ".lib"); StringRef ret = doFindFile(filename); // For MinGW, if the find above didn't turn up anything, try // looking for a MinGW formatted library name. if (config->mingw && ret == filename) return doFindLibMinGW(filename); return ret; } // Resolves a library path. /nodefaultlib options are taken into // consideration. This never returns the same path (in that case, // it returns None). Optional LinkerDriver::findLib(StringRef filename) { if (config->noDefaultLibAll) return None; if (!visitedLibs.insert(filename.lower()).second) return None; StringRef path = doFindLib(filename); if (config->noDefaultLibs.count(path.lower())) return None; if (Optional id = getUniqueID(path)) if (!visitedFiles.insert(*id).second) return None; return path; } // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { Optional envOpt = Process::GetEnv("LIB"); if (!envOpt.hasValue()) return; StringRef env = saver.save(*envOpt); while (!env.empty()) { StringRef path; std::tie(path, env) = env.split(';'); searchPaths.push_back(path); } } Symbol *LinkerDriver::addUndefined(StringRef name) { Symbol *b = symtab->addUndefined(name); if (!b->isGCRoot) { b->isGCRoot = true; config->gcroot.push_back(b); } return b; } StringRef LinkerDriver::mangleMaybe(Symbol *s) { // If the plain symbol name has already been resolved, do nothing. Undefined *unmangled = dyn_cast(s); if (!unmangled) return ""; // Otherwise, see if a similar, mangled symbol exists in the symbol table. Symbol *mangled = symtab->findMangle(unmangled->getName()); if (!mangled) return ""; // If we find a similar mangled symbol, make this an alias to it and return // its name. log(unmangled->getName() + " aliased to " + mangled->getName()); unmangled->weakAlias = symtab->addUndefined(mangled->getName()); return mangled->getName(); } // Windows specific -- find default entry point name. // // There are four different entry point functions for Windows executables, // each of which corresponds to a user-defined "main" function. This function // infers an entry point from a user-defined "main" function. StringRef LinkerDriver::findDefaultEntry() { assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN && "must handle /subsystem before calling this"); if (config->mingw) return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? "WinMainCRTStartup" : "mainCRTStartup"); if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { if (findUnderscoreMangle("wWinMain")) { if (!findUnderscoreMangle("WinMain")) return mangle("wWinMainCRTStartup"); warn("found both wWinMain and WinMain; using latter"); } return mangle("WinMainCRTStartup"); } if (findUnderscoreMangle("wmain")) { if (!findUnderscoreMangle("main")) return mangle("wmainCRTStartup"); warn("found both wmain and main; using latter"); } return mangle("mainCRTStartup"); } WindowsSubsystem LinkerDriver::inferSubsystem() { if (config->dll) return IMAGE_SUBSYSTEM_WINDOWS_GUI; if (config->mingw) return IMAGE_SUBSYSTEM_WINDOWS_CUI; // Note that link.exe infers the subsystem from the presence of these // functions even if /entry: or /nodefaultlib are passed which causes them // to not be called. bool haveMain = findUnderscoreMangle("main"); bool haveWMain = findUnderscoreMangle("wmain"); bool haveWinMain = findUnderscoreMangle("WinMain"); bool haveWWinMain = findUnderscoreMangle("wWinMain"); if (haveMain || haveWMain) { if (haveWinMain || haveWWinMain) { warn(std::string("found ") + (haveMain ? "main" : "wmain") + " and " + (haveWinMain ? "WinMain" : "wWinMain") + "; defaulting to /subsystem:console"); } return IMAGE_SUBSYSTEM_WINDOWS_CUI; } if (haveWinMain || haveWWinMain) return IMAGE_SUBSYSTEM_WINDOWS_GUI; return IMAGE_SUBSYSTEM_UNKNOWN; } static uint64_t getDefaultImageBase() { if (config->is64()) return config->dll ? 0x180000000 : 0x140000000; return config->dll ? 0x10000000 : 0x400000; } static std::string createResponseFile(const opt::InputArgList &args, ArrayRef filePaths, ArrayRef searchPaths) { SmallString<0> data; raw_svector_ostream os(data); for (auto *arg : args) { switch (arg->getOption().getID()) { case OPT_linkrepro: + case OPT_reproduce: case OPT_INPUT: case OPT_defaultlib: case OPT_libpath: case OPT_manifest: case OPT_manifest_colon: case OPT_manifestdependency: case OPT_manifestfile: case OPT_manifestinput: case OPT_manifestuac: break; case OPT_implib: case OPT_pdb: case OPT_out: os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n"; break; default: os << toString(*arg) << "\n"; } } for (StringRef path : searchPaths) { std::string relPath = relativeToRoot(path); os << "/libpath:" << quote(relPath) << "\n"; } for (StringRef path : filePaths) os << quote(relativeToRoot(path)) << "\n"; return data.str(); } enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab }; static DebugKind parseDebugKind(const opt::InputArgList &args) { auto *a = args.getLastArg(OPT_debug, OPT_debug_opt); if (!a) return DebugKind::None; if (a->getNumValues() == 0) return DebugKind::Full; DebugKind debug = StringSwitch(a->getValue()) .CaseLower("none", DebugKind::None) .CaseLower("full", DebugKind::Full) .CaseLower("fastlink", DebugKind::FastLink) // LLD extensions .CaseLower("ghash", DebugKind::GHash) .CaseLower("dwarf", DebugKind::Dwarf) .CaseLower("symtab", DebugKind::Symtab) .Default(DebugKind::Unknown); if (debug == DebugKind::FastLink) { warn("/debug:fastlink unsupported; using /debug:full"); return DebugKind::Full; } if (debug == DebugKind::Unknown) { error("/debug: unknown option: " + Twine(a->getValue())); return DebugKind::None; } return debug; } static unsigned parseDebugTypes(const opt::InputArgList &args) { unsigned debugTypes = static_cast(DebugType::None); if (auto *a = args.getLastArg(OPT_debugtype)) { SmallVector types; StringRef(a->getValue()) .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); for (StringRef type : types) { unsigned v = StringSwitch(type.lower()) .Case("cv", static_cast(DebugType::CV)) .Case("pdata", static_cast(DebugType::PData)) .Case("fixup", static_cast(DebugType::Fixup)) .Default(0); if (v == 0) { warn("/debugtype: unknown option '" + type + "'"); continue; } debugTypes |= v; } return debugTypes; } // Default debug types debugTypes = static_cast(DebugType::CV); if (args.hasArg(OPT_driver)) debugTypes |= static_cast(DebugType::PData); if (args.hasArg(OPT_profile)) debugTypes |= static_cast(DebugType::Fixup); return debugTypes; } static std::string getMapFile(const opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file); if (!arg) return ""; if (arg->getOption().getID() == OPT_lldmap_file) return arg->getValue(); assert(arg->getOption().getID() == OPT_lldmap); StringRef outFile = config->outputFile; return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); } static std::string getImplibPath() { if (!config->implib.empty()) return config->implib; SmallString<128> out = StringRef(config->outputFile); sys::path::replace_extension(out, ".lib"); return out.str(); } +// The import name is calculated as follows: // -// The import name is caculated as the following: -// // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY // -----+----------------+---------------------+------------------ // LINK | {value} | {value}.{.dll/.exe} | {output name} // LIB | {value} | {value}.dll | {output name}.dll // static std::string getImportName(bool asLib) { SmallString<128> out; if (config->importName.empty()) { out.assign(sys::path::filename(config->outputFile)); if (asLib) sys::path::replace_extension(out, ".dll"); } else { out.assign(config->importName); if (!sys::path::has_extension(out)) sys::path::replace_extension(out, (config->dll || asLib) ? ".dll" : ".exe"); } return out.str(); } static void createImportLibrary(bool asLib) { std::vector exports; for (Export &e1 : config->exports) { COFFShortExport e2; e2.Name = e1.name; e2.SymbolName = e1.symbolName; e2.ExtName = e1.extName; e2.Ordinal = e1.ordinal; e2.Noname = e1.noname; e2.Data = e1.data; e2.Private = e1.isPrivate; e2.Constant = e1.constant; exports.push_back(e2); } auto handleError = [](Error &&e) { handleAllErrors(std::move(e), [](ErrorInfoBase &eib) { error(eib.message()); }); }; std::string libName = getImportName(asLib); std::string path = getImplibPath(); if (!config->incremental) { handleError(writeImportLibrary(libName, path, exports, config->machine, config->mingw)); return; } // If the import library already exists, replace it only if the contents // have changed. ErrorOr> oldBuf = MemoryBuffer::getFile( path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); if (!oldBuf) { handleError(writeImportLibrary(libName, path, exports, config->machine, config->mingw)); return; } SmallString<128> tmpName; if (std::error_code ec = sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName)) fatal("cannot create temporary file for import library " + path + ": " + ec.message()); if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine, config->mingw)) { handleError(std::move(e)); return; } std::unique_ptr newBuf = check(MemoryBuffer::getFile( tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { oldBuf->reset(); handleError(errorCodeToError(sys::fs::rename(tmpName, path))); } else { sys::fs::remove(tmpName); } } static void parseModuleDefs(StringRef path) { std::unique_ptr mb = CHECK( MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); COFFModuleDefinition m = check(parseCOFFModuleDefinition( mb->getMemBufferRef(), config->machine, config->mingw)); if (config->outputFile.empty()) config->outputFile = saver.save(m.OutputFile); config->importName = saver.save(m.ImportName); if (m.ImageBase) config->imageBase = m.ImageBase; if (m.StackReserve) config->stackReserve = m.StackReserve; if (m.StackCommit) config->stackCommit = m.StackCommit; if (m.HeapReserve) config->heapReserve = m.HeapReserve; if (m.HeapCommit) config->heapCommit = m.HeapCommit; if (m.MajorImageVersion) config->majorImageVersion = m.MajorImageVersion; if (m.MinorImageVersion) config->minorImageVersion = m.MinorImageVersion; if (m.MajorOSVersion) config->majorOSVersion = m.MajorOSVersion; if (m.MinorOSVersion) config->minorOSVersion = m.MinorOSVersion; for (COFFShortExport e1 : m.Exports) { Export e2; // In simple cases, only Name is set. Renamed exports are parsed // and set as "ExtName = Name". If Name has the form "OtherDll.Func", // it shouldn't be a normal exported function but a forward to another // DLL instead. This is supported by both MS and GNU linkers. if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) { e2.name = saver.save(e1.ExtName); e2.forwardTo = saver.save(e1.Name); config->exports.push_back(e2); continue; } e2.name = saver.save(e1.Name); e2.extName = saver.save(e1.ExtName); e2.ordinal = e1.Ordinal; e2.noname = e1.Noname; e2.data = e1.Data; e2.isPrivate = e1.Private; e2.constant = e1.Constant; config->exports.push_back(e2); } } void LinkerDriver::enqueueTask(std::function task) { taskQueue.push_back(std::move(task)); } bool LinkerDriver::run() { ScopedTimer t(inputFileTimer); bool didWork = !taskQueue.empty(); while (!taskQueue.empty()) { taskQueue.front()(); taskQueue.pop_front(); } return didWork; } // Parse an /order file. If an option is given, the linker places // COMDAT sections in the same order as their names appear in the // given file. static void parseOrderFile(StringRef arg) { // For some reason, the MSVC linker requires a filename to be // preceded by "@". if (!arg.startswith("@")) { error("malformed /order option: '@' missing"); return; } // Get a list of all comdat sections for error checking. DenseSet set; for (Chunk *c : symtab->getChunks()) if (auto *sec = dyn_cast(c)) if (sec->sym) set.insert(sec->sym->getName()); // Open a file. StringRef path = arg.substr(1); std::unique_ptr mb = CHECK( MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); // Parse a file. An order file contains one symbol per line. // All symbols that were not present in a given order file are // considered to have the lowest priority 0 and are placed at // end of an output section. for (std::string s : args::getLines(mb->getMemBufferRef())) { if (config->machine == I386 && !isDecorated(s)) s = "_" + s; if (set.count(s) == 0) { if (config->warnMissingOrderSymbol) warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]"); } else config->order[s] = INT_MIN + config->order.size(); } } static void markAddrsig(Symbol *s) { if (auto *d = dyn_cast_or_null(s)) if (SectionChunk *c = dyn_cast_or_null(d->getChunk())) c->keepUnique = true; } static void findKeepUniqueSections() { // Exported symbols could be address-significant in other executables or DSOs, // so we conservatively mark them as address-significant. for (Export &r : config->exports) markAddrsig(r.sym); // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. for (ObjFile *obj : ObjFile::instances) { ArrayRef syms = obj->getSymbols(); if (obj->addrsigSec) { ArrayRef contents; cantFail( obj->getCOFFObj()->getSectionContents(obj->addrsigSec, contents)); const uint8_t *cur = contents.begin(); while (cur != contents.end()) { unsigned size; const char *err; uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err); if (err) fatal(toString(obj) + ": could not decode addrsig section: " + err); if (symIndex >= syms.size()) fatal(toString(obj) + ": invalid symbol index in addrsig section"); markAddrsig(syms[symIndex]); cur += size; } } else { // If an object file does not have an address-significance table, // conservatively mark all of its symbols as address-significant. for (Symbol *s : syms) markAddrsig(s); } } } // link.exe replaces each %foo% in altPath with the contents of environment // variable foo, and adds the two magic env vars _PDB (expands to the basename // of pdb's output path) and _EXT (expands to the extension of the output // binary). // lld only supports %_PDB% and %_EXT% and warns on references to all other env // vars. static void parsePDBAltPath(StringRef altPath) { SmallString<128> buf; StringRef pdbBasename = sys::path::filename(config->pdbPath, sys::path::Style::windows); StringRef binaryExtension = sys::path::extension(config->outputFile, sys::path::Style::windows); if (!binaryExtension.empty()) binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. // Invariant: // +--------- cursor ('a...' might be the empty string). // | +----- firstMark // | | +- secondMark // v v v // a...%...%... size_t cursor = 0; while (cursor < altPath.size()) { size_t firstMark, secondMark; if ((firstMark = altPath.find('%', cursor)) == StringRef::npos || (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) { // Didn't find another full fragment, treat rest of string as literal. buf.append(altPath.substr(cursor)); break; } // Found a full fragment. Append text in front of first %, and interpret // text between first and second % as variable name. buf.append(altPath.substr(cursor, firstMark - cursor)); StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1); if (var.equals_lower("%_pdb%")) buf.append(pdbBasename); else if (var.equals_lower("%_ext%")) buf.append(binaryExtension); else { warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + var + " as literal"); buf.append(var); } cursor = secondMark + 1; } config->pdbAltPath = buf; } -/// Check that at most one resource obj file was used. +/// Convert resource files and potentially merge input resource object +/// trees into one resource tree. /// Call after ObjFile::Instances is complete. -static void diagnoseMultipleResourceObjFiles() { - // The .rsrc$01 section in a resource obj file contains a tree description - // of resources. Merging multiple resource obj files would require merging - // the trees instead of using usual linker section merging semantics. - // Since link.exe disallows linking more than one resource obj file with - // LNK4078, mirror that. The normal use of resource files is to give the - // linker many .res files, which are then converted to a single resource obj - // file internally, so this is not a big restriction in practice. - ObjFile *resourceObjFile = nullptr; +void LinkerDriver::convertResources() { + std::vector resourceObjFiles; + for (ObjFile *f : ObjFile::instances) { - if (!f->isResourceObjFile) - continue; + if (f->isResourceObjFile()) + resourceObjFiles.push_back(f); + } - if (!resourceObjFile) { - resourceObjFile = f; - continue; - } - - error(toString(f) + + if (!config->mingw && + (resourceObjFiles.size() > 1 || + (resourceObjFiles.size() == 1 && !resources.empty()))) { + error((!resources.empty() ? "internal .obj file created from .res files" + : toString(resourceObjFiles[1])) + ": more than one resource obj file not allowed, already got " + - toString(resourceObjFile)); + toString(resourceObjFiles.front())); + return; } + + if (resources.empty() && resourceObjFiles.size() <= 1) { + // No resources to convert, and max one resource object file in + // the input. Keep that preconverted resource section as is. + for (ObjFile *f : resourceObjFiles) + f->includeResourceChunks(); + return; + } + ObjFile *f = make(convertResToCOFF(resources, resourceObjFiles)); + symtab->addFile(f); + f->includeResourceChunks(); } // In MinGW, if no symbols are chosen to be exported, then all symbols are // automatically exported by default. This behavior can be forced by the // -export-all-symbols option, so that it happens even when exports are // explicitly specified. The automatic behavior can be disabled using the // -exclude-all-symbols option, so that lld-link behaves like link.exe rather // than MinGW in the case that nothing is explicitly exported. void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!config->dll) return; if (!args.hasArg(OPT_export_all_symbols)) { if (!config->exports.empty()) return; if (args.hasArg(OPT_exclude_all_symbols)) return; } AutoExporter exporter; for (auto *arg : args.filtered(OPT_wholearchive_file)) if (Optional path = doFindFile(arg->getValue())) exporter.addWholeArchive(*path); symtab->forEachSymbol([&](Symbol *s) { auto *def = dyn_cast(s); if (!exporter.shouldExport(def)) return; Export e; e.name = def->getName(); e.sym = def; if (Chunk *c = def->getChunk()) if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) e.data = true; config->exports.push_back(e); }); } +// lld has a feature to create a tar file containing all input files as well as +// all command line options, so that other people can run lld again with exactly +// the same inputs. This feature is accessible via /linkrepro and /reproduce. +// +// /linkrepro and /reproduce are very similar, but /linkrepro takes a directory +// name while /reproduce takes a full path. We have /linkrepro for compatibility +// with Microsoft link.exe. +Optional getReproduceFile(const opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return std::string(arg->getValue()); + + if (auto *arg = args.getLastArg(OPT_linkrepro)) { + SmallString<64> path = StringRef(arg->getValue()); + sys::path::append(path, "repro.tar"); + return path.str().str(); + } + + return None; +} + void LinkerDriver::link(ArrayRef argsArr) { // Needed for LTO. InitializeAllTargetInfos(); InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. if (argsArr.size() > 1 && StringRef(argsArr[1]).equals_lower("/lib")) { if (llvm::libDriverMain(argsArr.slice(1)) != 0) fatal("lib failed"); return; } // Parse command line options. ArgParser parser; - opt::InputArgList args = parser.parseLINK(argsArr); + opt::InputArgList args = parser.parse(argsArr); // Parse and evaluate -mllvm options. std::vector v; v.push_back("lld-link (LLVM option parsing)"); for (auto *arg : args.filtered(OPT_mllvm)) v.push_back(arg->getValue()); cl::ParseCommandLineOptions(v.size(), v.data()); // Handle /errorlimit early, because error() depends on it. if (auto *arg = args.getLastArg(OPT_errorlimit)) { int n = 20; StringRef s = arg->getValue(); if (s.getAsInteger(10, n)) error(arg->getSpelling() + " number expected, but got " + s); errorHandler().errorLimit = n; } // Handle /help if (args.hasArg(OPT_help)) { printHelp(argsArr[0]); return; } lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true); if (args.hasArg(OPT_show_timing)) config->showTiming = true; config->showSummary = args.hasArg(OPT_summary); ScopedTimer t(Timer::root()); // Handle --version, which is an lld extension. This option is a bit odd // because it doesn't start with "/", but we deliberately chose "--" to // avoid conflict with /version and for compatibility with clang-cl. if (args.hasArg(OPT_dash_dash_version)) { outs() << getLLDVersion() << "\n"; return; } // Handle /lldmingw early, since it can potentially affect how other // options are handled. config->mingw = args.hasArg(OPT_lldmingw); - if (auto *arg = args.getLastArg(OPT_linkrepro)) { - SmallString<64> path = StringRef(arg->getValue()); - sys::path::append(path, "repro.tar"); - + // Handle /linkrepro and /reproduce. + if (Optional path = getReproduceFile(args)) { Expected> errOrWriter = - TarWriter::create(path, "repro"); + TarWriter::create(*path, sys::path::stem(*path)); if (errOrWriter) { tar = std::move(*errOrWriter); } else { - error("/linkrepro: failed to open " + path + ": " + + error("/linkrepro: failed to open " + *path + ": " + toString(errOrWriter.takeError())); } } if (!args.hasArg(OPT_INPUT)) { if (args.hasArg(OPT_deffile)) config->noEntry = true; else fatal("no input files"); } // Construct search path list. searchPaths.push_back(""); for (auto *arg : args.filtered(OPT_libpath)) searchPaths.push_back(arg->getValue()); - addLibSearchPaths(); + if (!args.hasArg(OPT_lldignoreenv)) + addLibSearchPaths(); // Handle /ignore for (auto *arg : args.filtered(OPT_ignore)) { SmallVector vec; StringRef(arg->getValue()).split(vec, ','); for (StringRef s : vec) { if (s == "4037") config->warnMissingOrderSymbol = false; else if (s == "4099") config->warnDebugInfoUnusable = false; else if (s == "4217") config->warnLocallyDefinedImported = false; // Other warning numbers are ignored. } } // Handle /out if (auto *arg = args.getLastArg(OPT_out)) config->outputFile = arg->getValue(); // Handle /verbose if (args.hasArg(OPT_verbose)) config->verbose = true; errorHandler().verbose = config->verbose; // Handle /force or /force:unresolved if (args.hasArg(OPT_force, OPT_force_unresolved)) config->forceUnresolved = true; // Handle /force or /force:multiple if (args.hasArg(OPT_force, OPT_force_multiple)) config->forceMultiple = true; // Handle /force or /force:multipleres if (args.hasArg(OPT_force, OPT_force_multipleres)) config->forceMultipleRes = true; // Handle /debug DebugKind debug = parseDebugKind(args); if (debug == DebugKind::Full || debug == DebugKind::Dwarf || debug == DebugKind::GHash) { config->debug = true; config->incremental = true; } // Handle /demangle config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); // Handle /debugtype config->debugTypes = parseDebugTypes(args); // Handle /pdb bool shouldCreatePDB = (debug == DebugKind::Full || debug == DebugKind::GHash); if (shouldCreatePDB) { if (auto *arg = args.getLastArg(OPT_pdb)) config->pdbPath = arg->getValue(); if (auto *arg = args.getLastArg(OPT_pdbaltpath)) config->pdbAltPath = arg->getValue(); if (args.hasArg(OPT_natvis)) config->natvisFiles = args.getAllArgValues(OPT_natvis); if (auto *arg = args.getLastArg(OPT_pdb_source_path)) config->pdbSourcePath = arg->getValue(); } // Handle /noentry if (args.hasArg(OPT_noentry)) { if (args.hasArg(OPT_dll)) config->noEntry = true; else error("/noentry must be specified with /dll"); } // Handle /dll if (args.hasArg(OPT_dll)) { config->dll = true; config->manifestID = 2; } // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase // because we need to explicitly check whether that option or its inverse was // present in the argument list in order to handle /fixed. auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); if (dynamicBaseArg && dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) config->dynamicBase = false; // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the // default setting for any other project type.", but link.exe defaults to // /FIXED:NO for exe outputs as well. Match behavior, not docs. bool fixed = args.hasFlag(OPT_fixed, OPT_fixed_no, false); if (fixed) { if (dynamicBaseArg && dynamicBaseArg->getOption().getID() == OPT_dynamicbase) { error("/fixed must not be specified with /dynamicbase"); } else { config->relocatable = false; config->dynamicBase = false; } } // Handle /appcontainer config->appContainer = args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); // Handle /machine if (auto *arg = args.getLastArg(OPT_machine)) { config->machine = getMachineType(arg->getValue()); if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) fatal(Twine("unknown /machine argument: ") + arg->getValue()); } // Handle /nodefaultlib: for (auto *arg : args.filtered(OPT_nodefaultlib)) config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); // Handle /nodefaultlib if (args.hasArg(OPT_nodefaultlib_all)) config->noDefaultLibAll = true; // Handle /base if (auto *arg = args.getLastArg(OPT_base)) parseNumbers(arg->getValue(), &config->imageBase); // Handle /filealign if (auto *arg = args.getLastArg(OPT_filealign)) { parseNumbers(arg->getValue(), &config->fileAlign); if (!isPowerOf2_64(config->fileAlign)) error("/filealign: not a power of two: " + Twine(config->fileAlign)); } // Handle /stack if (auto *arg = args.getLastArg(OPT_stack)) parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit); // Handle /guard:cf if (auto *arg = args.getLastArg(OPT_guard)) parseGuard(arg->getValue()); // Handle /heap if (auto *arg = args.getLastArg(OPT_heap)) parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit); // Handle /version if (auto *arg = args.getLastArg(OPT_version)) parseVersion(arg->getValue(), &config->majorImageVersion, &config->minorImageVersion); // Handle /subsystem if (auto *arg = args.getLastArg(OPT_subsystem)) parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion, &config->minorOSVersion); // Handle /timestamp if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) { if (arg->getOption().getID() == OPT_repro) { config->timestamp = 0; config->repro = true; } else { config->repro = false; StringRef value(arg->getValue()); if (value.getAsInteger(0, config->timestamp)) fatal(Twine("invalid timestamp: ") + value + ". Expected 32-bit integer"); } } else { config->repro = false; config->timestamp = time(nullptr); } // Handle /alternatename for (auto *arg : args.filtered(OPT_alternatename)) parseAlternateName(arg->getValue()); // Handle /include for (auto *arg : args.filtered(OPT_incl)) addUndefined(arg->getValue()); // Handle /implib if (auto *arg = args.getLastArg(OPT_implib)) config->implib = arg->getValue(); // Handle /opt. bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); unsigned icfLevel = args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on unsigned tailMerge = 1; for (auto *arg : args.filtered(OPT_opt)) { std::string str = StringRef(arg->getValue()).lower(); SmallVector vec; StringRef(str).split(vec, ','); for (StringRef s : vec) { if (s == "ref") { doGC = true; } else if (s == "noref") { doGC = false; } else if (s == "icf" || s.startswith("icf=")) { icfLevel = 2; } else if (s == "noicf") { icfLevel = 0; } else if (s == "lldtailmerge") { tailMerge = 2; } else if (s == "nolldtailmerge") { tailMerge = 0; } else if (s.startswith("lldlto=")) { StringRef optLevel = s.substr(7); if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3) error("/opt:lldlto: invalid optimization level: " + optLevel); } else if (s.startswith("lldltojobs=")) { StringRef jobs = s.substr(11); if (jobs.getAsInteger(10, config->thinLTOJobs) || config->thinLTOJobs == 0) error("/opt:lldltojobs: invalid job count: " + jobs); } else if (s.startswith("lldltopartitions=")) { StringRef n = s.substr(17); if (n.getAsInteger(10, config->ltoPartitions) || config->ltoPartitions == 0) error("/opt:lldltopartitions: invalid partition count: " + n); } else if (s != "lbr" && s != "nolbr") error("/opt: unknown option: " + s); } } // Limited ICF is enabled if GC is enabled and ICF was never mentioned // explicitly. // FIXME: LLD only implements "limited" ICF, i.e. it only merges identical // code. If the user passes /OPT:ICF explicitly, LLD should merge identical // comdat readonly data. if (icfLevel == 1 && !doGC) icfLevel = 0; config->doGC = doGC; config->doICF = icfLevel > 0; config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2; // Handle /lldsavetemps if (args.hasArg(OPT_lldsavetemps)) config->saveTemps = true; // Handle /kill-at if (args.hasArg(OPT_kill_at)) config->killAt = true; // Handle /lldltocache if (auto *arg = args.getLastArg(OPT_lldltocache)) config->ltoCache = arg->getValue(); // Handle /lldsavecachepolicy if (auto *arg = args.getLastArg(OPT_lldltocachepolicy)) config->ltoCachePolicy = CHECK( parseCachePruningPolicy(arg->getValue()), Twine("/lldltocachepolicy: invalid cache policy: ") + arg->getValue()); // Handle /failifmismatch for (auto *arg : args.filtered(OPT_failifmismatch)) checkFailIfMismatch(arg->getValue(), nullptr); // Handle /merge for (auto *arg : args.filtered(OPT_merge)) parseMerge(arg->getValue()); // Add default section merging rules after user rules. User rules take // precedence, but we will emit a warning if there is a conflict. parseMerge(".idata=.rdata"); parseMerge(".didat=.rdata"); parseMerge(".edata=.rdata"); parseMerge(".xdata=.rdata"); parseMerge(".bss=.data"); if (config->mingw) { parseMerge(".ctors=.rdata"); parseMerge(".dtors=.rdata"); parseMerge(".CRT=.rdata"); } // Handle /section for (auto *arg : args.filtered(OPT_section)) parseSection(arg->getValue()); + // Handle /align + if (auto *arg = args.getLastArg(OPT_align)) { + parseNumbers(arg->getValue(), &config->align); + if (!isPowerOf2_64(config->align)) + error("/align: not a power of two: " + StringRef(arg->getValue())); + } + // Handle /aligncomm for (auto *arg : args.filtered(OPT_aligncomm)) parseAligncomm(arg->getValue()); // Handle /manifestdependency. This enables /manifest unless /manifest:no is // also passed. if (auto *arg = args.getLastArg(OPT_manifestdependency)) { config->manifestDependency = arg->getValue(); config->manifest = Configuration::SideBySide; } // Handle /manifest and /manifest: if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) { if (arg->getOption().getID() == OPT_manifest) config->manifest = Configuration::SideBySide; else parseManifest(arg->getValue()); } // Handle /manifestuac if (auto *arg = args.getLastArg(OPT_manifestuac)) parseManifestUAC(arg->getValue()); // Handle /manifestfile if (auto *arg = args.getLastArg(OPT_manifestfile)) config->manifestFile = arg->getValue(); // Handle /manifestinput for (auto *arg : args.filtered(OPT_manifestinput)) config->manifestInput.push_back(arg->getValue()); if (!config->manifestInput.empty() && config->manifest != Configuration::Embed) { fatal("/manifestinput: requires /manifest:embed"); } config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || args.hasArg(OPT_thinlto_index_only_arg); config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_arg); config->thinLTOPrefixReplace = getOldNewOptions(args, OPT_thinlto_prefix_replace); config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace); + config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path); // Handle miscellaneous boolean flags. config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); config->allowIsolation = args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); config->incremental = args.hasFlag(OPT_incremental, OPT_incremental_no, !config->doGC && !config->doICF && !args.hasArg(OPT_order) && !args.hasArg(OPT_profile)); config->integrityCheck = args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); for (auto *arg : args.filtered(OPT_swaprun)) parseSwaprun(arg->getValue()); config->terminalServerAware = !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); config->debugDwarf = debug == DebugKind::Dwarf; config->debugGHashes = debug == DebugKind::GHash; config->debugSymtab = debug == DebugKind::Symtab; config->mapFile = getMapFile(args); if (config->incremental && args.hasArg(OPT_profile)) { warn("ignoring '/incremental' due to '/profile' specification"); config->incremental = false; } if (config->incremental && args.hasArg(OPT_order)) { warn("ignoring '/incremental' due to '/order' specification"); config->incremental = false; } if (config->incremental && config->doGC) { warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to " "disable"); config->incremental = false; } if (config->incremental && config->doICF) { warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to " "disable"); config->incremental = false; } if (errorCount()) return; std::set wholeArchives; for (auto *arg : args.filtered(OPT_wholearchive_file)) if (Optional path = doFindFile(arg->getValue())) if (Optional id = getUniqueID(*path)) wholeArchives.insert(*id); // A predicate returning true if a given path is an argument for // /wholearchive:, or /wholearchive is enabled globally. // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" // needs to be handled as "/wholearchive:foo.obj foo.obj". auto isWholeArchive = [&](StringRef path) -> bool { if (args.hasArg(OPT_wholearchive_flag)) return true; if (Optional id = getUniqueID(path)) return wholeArchives.count(*id); return false; }; - // Create a list of input files. Files can be given as arguments - // for /defaultlib option. - for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional path = findFile(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path)); + // Create a list of input files. These can be given as OPT_INPUT options + // and OPT_wholearchive_file options, and we also need to track OPT_start_lib + // and OPT_end_lib. + bool inLib = false; + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_end_lib: + if (!inLib) + error("stray " + arg->getSpelling()); + inLib = false; + break; + case OPT_start_lib: + if (inLib) + error("nested " + arg->getSpelling()); + inLib = true; + break; + case OPT_wholearchive_file: + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, true, inLib); + break; + case OPT_INPUT: + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path), inLib); + break; + default: + // Ignore other options. + break; + } + } + // Process files specified as /defaultlib. These should be enequeued after + // other files, which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); // Windows specific -- Create a resource file containing a manifest file. if (config->manifest == Configuration::Embed) - addBuffer(createManifestRes(), false); + addBuffer(createManifestRes(), false, false); // Read all input files given via the command line. run(); if (errorCount()) return; // We should have inferred a machine type by now from the input files, but if // not we assume x64. if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); config->machine = AMD64; } config->wordsize = config->is64() ? 8 : 4; // Handle /safeseh, x86 only, on by default, except for mingw. if (config->machine == I386 && args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw)) config->safeSEH = true; // Handle /functionpadmin for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg, config->machine); - // Input files can be Windows resource files (.res files). We use - // WindowsResource to convert resource files to a regular COFF file, - // then link the resulting file normally. - if (!resources.empty()) - symtab->addFile(make(convertResToCOFF(resources))); - if (tar) tar->append("response.txt", createResponseFile(args, filePaths, ArrayRef(searchPaths).slice(1))); // Handle /largeaddressaware config->largeAddressAware = args.hasFlag( OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64()); // Handle /highentropyva config->highEntropyVA = config->is64() && args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); if (!config->dynamicBase && (config->machine == ARMNT || config->machine == ARM64)) error("/dynamicbase:no is not compatible with " + machineToStr(config->machine)); // Handle /export for (auto *arg : args.filtered(OPT_export)) { Export e = parseExport(arg->getValue()); if (config->machine == I386) { if (!isDecorated(e.name)) e.name = saver.save("_" + e.name); if (!e.extName.empty() && !isDecorated(e.extName)) e.extName = saver.save("_" + e.extName); } config->exports.push_back(e); } // Handle /def if (auto *arg = args.getLastArg(OPT_deffile)) { // parseModuleDefs mutates Config object. parseModuleDefs(arg->getValue()); } // Handle generation of import library from a def file. if (!args.hasArg(OPT_INPUT)) { fixupExports(); createImportLibrary(/*asLib=*/true); return; } // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. Must happen before /entry handling, // and after the early return when just writing an import library. if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { config->subsystem = inferSubsystem(); if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) fatal("subsystem must be defined"); } // Handle /entry and /dll if (auto *arg = args.getLastArg(OPT_entry)) { config->entry = addUndefined(mangle(arg->getValue())); } else if (!config->entry && !config->noEntry) { if (args.hasArg(OPT_dll)) { StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12" : "_DllMainCRTStartup"; config->entry = addUndefined(s); } else { // Windows specific -- If entry point name is not given, we need to // infer that from user-defined entry name. StringRef s = findDefaultEntry(); if (s.empty()) fatal("entry point must be defined"); config->entry = addUndefined(s); log("Entry name inferred: " + s); } } // Handle /delayload for (auto *arg : args.filtered(OPT_delayload)) { config->delayLoads.insert(StringRef(arg->getValue()).lower()); if (config->machine == I386) { config->delayLoadHelper = addUndefined("___delayLoadHelper2@8"); } else { config->delayLoadHelper = addUndefined("__delayLoadHelper2"); } } // Set default image name if neither /out or /def set it. if (config->outputFile.empty()) { config->outputFile = getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue()); } // Fail early if an output file is not writable. if (auto e = tryCreateFile(config->outputFile)) { error("cannot open output file " + config->outputFile + ": " + e.message()); return; } if (shouldCreatePDB) { // Put the PDB next to the image if no /pdb flag was passed. if (config->pdbPath.empty()) { config->pdbPath = config->outputFile; sys::path::replace_extension(config->pdbPath, ".pdb"); } // The embedded PDB path should be the absolute path to the PDB if no // /pdbaltpath flag was passed. if (config->pdbAltPath.empty()) { config->pdbAltPath = config->pdbPath; // It's important to make the path absolute and remove dots. This path // will eventually be written into the PE header, and certain Microsoft // tools won't work correctly if these assumptions are not held. sys::fs::make_absolute(config->pdbAltPath); sys::path::remove_dots(config->pdbAltPath); } else { // Don't do this earlier, so that Config->OutputFile is ready. parsePDBAltPath(config->pdbAltPath); } } // Set default image base if /base is not given. if (config->imageBase == uint64_t(-1)) config->imageBase = getDefaultImageBase(); symtab->addSynthetic(mangle("__ImageBase"), nullptr); if (config->machine == I386) { symtab->addAbsolute("___safe_se_handler_table", 0); symtab->addAbsolute("___safe_se_handler_count", 0); } symtab->addAbsolute(mangle("__guard_fids_count"), 0); symtab->addAbsolute(mangle("__guard_fids_table"), 0); symtab->addAbsolute(mangle("__guard_flags"), 0); symtab->addAbsolute(mangle("__guard_iat_count"), 0); symtab->addAbsolute(mangle("__guard_iat_table"), 0); symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); // Needed for MSVC 2017 15.5 CRT. symtab->addAbsolute(mangle("__enclave_config"), 0); if (config->mingw) { symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); } // This code may add new undefined symbols to the link, which may enqueue more // symbol resolution tasks, so we need to continue executing tasks until we // converge. do { // Windows specific -- if entry point is not found, // search for its mangled names. if (config->entry) mangleMaybe(config->entry); // Windows specific -- Make sure we resolve all dllexported symbols. for (Export &e : config->exports) { if (!e.forwardTo.empty()) continue; e.sym = addUndefined(e.name); if (!e.directives) e.symbolName = mangleMaybe(e.sym); } // Add weak aliases. Weak aliases is a mechanism to give remaining // undefined symbols final chance to be resolved successfully. for (auto pair : config->alternateNames) { StringRef from = pair.first; StringRef to = pair.second; Symbol *sym = symtab->find(from); if (!sym) continue; if (auto *u = dyn_cast(sym)) if (!u->weakAlias) u->weakAlias = symtab->addUndefined(to); } + // If any inputs are bitcode files, the LTO code generator may create + // references to library functions that are not explicit in the bitcode + // file's symbol table. If any of those library functions are defined in a + // bitcode file in an archive member, we need to arrange to use LTO to + // compile those archive members by adding them to the link beforehand. + if (!BitcodeFile::instances.empty()) + for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) + symtab->addLibcall(s); + // Windows specific -- if __load_config_used can be resolved, resolve it. if (symtab->findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); } while (run()); - if (errorCount()) - return; - - // Do LTO by compiling bitcode input files to a set of native COFF files then - // link those files (unless -thinlto-index-only was given, in which case we - // resolve symbols and write indices, but don't generate native code or link). - symtab->addCombinedLTOObjects(); - - // If -thinlto-index-only is given, we should create only "index - // files" and not object files. Index file creation is already done - // in addCombinedLTOObject, so we are done if that's the case. - if (config->thinLTOIndexOnly) - return; - - // If we generated native object files from bitcode files, this resolves - // references to the symbols we use from them. - run(); - if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional for (auto *arg : args.filtered(OPT_include_optional)) - if (dyn_cast_or_null(symtab->find(arg->getValue()))) + if (dyn_cast_or_null(symtab->find(arg->getValue()))) addUndefined(arg->getValue()); while (run()); } if (config->mingw) { // Load any further object files that might be needed for doing automatic // imports. // // For cases with no automatically imported symbols, this iterates once // over the symbol table and doesn't do anything. // // For the normal case with a few automatically imported symbols, this // should only need to be run once, since each new object file imported // is an import library and wouldn't add any new undefined references, // but there's nothing stopping the __imp_ symbols from coming from a // normal object file as well (although that won't be used for the // actual autoimport later on). If this pass adds new undefined references, // we won't iterate further to resolve them. symtab->loadMinGWAutomaticImports(); run(); } - // Make sure we have resolved all symbols. - symtab->reportRemainingUndefines(); + // At this point, we should not have any symbols that cannot be resolved. + // If we are going to do codegen for link-time optimization, check for + // unresolvable symbols first, so we don't spend time generating code that + // will fail to link anyway. + if (!BitcodeFile::instances.empty() && !config->forceUnresolved) + symtab->reportUnresolvable(); if (errorCount()) return; + // Do LTO by compiling bitcode input files to a set of native COFF files then + // link those files (unless -thinlto-index-only was given, in which case we + // resolve symbols and write indices, but don't generate native code or link). + symtab->addCombinedLTOObjects(); + + // If -thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in addCombinedLTOObject, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return; + + // If we generated native object files from bitcode files, this resolves + // references to the symbols we use from them. + run(); + + // Resolve remaining undefined symbols and warn about imported locals. + symtab->resolveRemainingUndefines(); + if (errorCount()) + return; + + config->hadExplicitExports = !config->exports.empty(); if (config->mingw) { // In MinGW, all symbols are automatically exported if no symbols // are chosen to be exported. maybeExportMinGWSymbols(args); // Make sure the crtend.o object is the last object file. This object // file can contain terminating section chunks that need to be placed // last. GNU ld processes files and static libraries explicitly in the // order provided on the command line, while lld will pull in needed // files from static libraries only after the last object file on the // command line. for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end(); i != e; i++) { ObjFile *file = *i; if (isCrtend(file->getName())) { ObjFile::instances.erase(i); ObjFile::instances.push_back(file); break; } } } // Windows specific -- when we are creating a .dll file, we also - // need to create a .lib file. + // need to create a .lib file. In MinGW mode, we only do that when the + // -implib option is given explicitly, for compatibility with GNU ld. if (!config->exports.empty() || config->dll) { fixupExports(); - createImportLibrary(/*asLib=*/false); + if (!config->mingw || !config->implib.empty()) + createImportLibrary(/*asLib=*/false); assignExportOrdinals(); } // Handle /output-def (MinGW specific). if (auto *arg = args.getLastArg(OPT_output_def)) writeDefFile(arg->getValue()); // Set extra alignment for .comm symbols for (auto pair : config->alignComm) { StringRef name = pair.first; uint32_t alignment = pair.second; Symbol *sym = symtab->find(name); if (!sym) { warn("/aligncomm symbol " + name + " not found"); continue; } // If the symbol isn't common, it must have been replaced with a regular // symbol, which will carry its own alignment. auto *dc = dyn_cast(sym); if (!dc) continue; CommonChunk *c = dc->getChunk(); c->setAlignment(std::max(c->getAlignment(), alignment)); } // Windows specific -- Create a side-by-side manifest file. if (config->manifest == Configuration::SideBySide) createSideBySideManifest(); // Handle /order. We want to do this at this moment because we // need a complete list of comdat sections to warn on nonexistent // functions. if (auto *arg = args.getLastArg(OPT_order)) parseOrderFile(arg->getValue()); // Identify unreferenced COMDAT sections. if (config->doGC) markLive(symtab->getChunks()); // Needs to happen after the last call to addFile(). - diagnoseMultipleResourceObjFiles(); + convertResources(); // Identify identical COMDAT sections to merge them. if (config->doICF) { findKeepUniqueSections(); doICF(symtab->getChunks()); } // Write the result. writeResult(); // Stop early so we can print the results. Timer::root().stop(); if (config->showTiming) Timer::root().print(); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Driver.h =================================================================== --- vendor/lld/dist/COFF/Driver.h (revision 353949) +++ vendor/lld/dist/COFF/Driver.h (revision 353950) @@ -1,202 +1,208 @@ //===- Driver.h -------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_DRIVER_H #define LLD_COFF_DRIVER_H #include "Config.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" #include #include #include namespace lld { namespace coff { class LinkerDriver; extern LinkerDriver *driver; using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; using llvm::Optional; class COFFOptTable : public llvm::opt::OptTable { public: COFFOptTable(); }; class ArgParser { public: - // Concatenate LINK environment variable and given arguments and parse them. - llvm::opt::InputArgList parseLINK(std::vector args); + // Parses command line options. + llvm::opt::InputArgList parse(llvm::ArrayRef args); // Tokenizes a given string and then parses as command line options. llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); } // Tokenizes a given string and then parses as command line options in // .drectve section. /EXPORT options are returned in second element // to be processed in fastpath. std::pair> parseDirectives(StringRef s); private: - // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef args); + // Concatenate LINK environment variable. + void addLINK(SmallVector &argv); std::vector tokenize(StringRef s); COFFOptTable table; }; class LinkerDriver { public: void link(llvm::ArrayRef args); // Used by the resolver to parse .drectve section contents. void parseDirectives(InputFile *file); // Used by ArchiveFile to enqueue members. - void enqueueArchiveMember(const Archive::Child &c, StringRef symName, + void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym, StringRef parentName); MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive); + void enqueuePath(StringRef path, bool wholeArchive, bool lazy); private: std::unique_ptr tar; // for /linkrepro // Opens a file. Path has to be resolved already. MemoryBufferRef openFile(StringRef path); // Searches a file from search paths. Optional findFile(StringRef filename); Optional findLib(StringRef filename); StringRef doFindFile(StringRef filename); StringRef doFindLib(StringRef filename); StringRef doFindLibMinGW(StringRef filename); // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); // Library search path. The first element is always "" (current directory). std::vector searchPaths; + // Convert resource files and potentially merge input resource object + // trees into one resource tree. + void convertResources(); + void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args); // We don't want to add the same file more than once. // Files are uniquified by their filesystem and file number. std::set visitedFiles; std::set visitedLibs; Symbol *addUndefined(StringRef sym); StringRef mangleMaybe(Symbol *s); // Windows specific -- "main" is not the only main function in Windows. // You can choose one from these four -- {w,}{WinMain,main}. // There are four different entry point functions for them, // {w,}{WinMain,main}CRTStartup, respectively. The linker needs to // choose the right one depending on which "main" function is defined. // This function looks up the symbol table and resolve corresponding // entry point name. StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr mb, bool wholeArchive); + void addBuffer(std::unique_ptr mb, bool wholeArchive, + bool lazy); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, StringRef parentName, uint64_t offsetInArchive); void enqueueTask(std::function task); bool run(); std::list> taskQueue; std::vector filePaths; std::vector resources; llvm::StringSet<> directivesExports; }; // Functions below this line are defined in DriverUtils.cpp. void printHelp(const char *argv0); // Parses a string in the form of "[,]". void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); void parseGuard(StringRef arg); // Parses a string in the form of "[.]". // Minor's default value is 0. void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); // Parses a string in the form of "[,[.]]". void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, uint32_t *minor); void parseAlternateName(StringRef); void parseMerge(StringRef); void parseSection(StringRef); void parseAligncomm(StringRef); // Parses a string in the form of "[:]" void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); // Parses a string in the form of "EMBED[,=]|NO". void parseManifest(StringRef arg); // Parses a string in the form of "level=|uiAccess=" void parseManifestUAC(StringRef arg); // Parses a string in the form of "cd|net[,(cd|net)]*" void parseSwaprun(StringRef arg); // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes(); void createSideBySideManifest(); // Used for dllexported symbols. Export parseExport(StringRef arg); void fixupExports(); void assignExportOrdinals(); // Parses a string in the form of "key=value" and check // if value matches previous values for the key. // This feature used in the directive section to reject // incompatible objects. void checkFailIfMismatch(StringRef arg, InputFile *source); // Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef mbs); +MemoryBufferRef convertResToCOFF(ArrayRef mbs, + ArrayRef objs); void runMSVCLinker(std::string rsp, ArrayRef objects); // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, #include "Options.inc" #undef OPTION }; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/DriverUtils.cpp =================================================================== --- vendor/lld/dist/COFF/DriverUtils.cpp (revision 353949) +++ vendor/lld/dist/COFF/DriverUtils.cpp (revision 353950) @@ -1,897 +1,916 @@ //===- DriverUtils.cpp ----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains utility functions for the driver. Because there // are so many small functions, we created this separate file to make // Driver.cpp less cluttered. // //===----------------------------------------------------------------------===// #include "Config.h" #include "Driver.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Object/WindowsResource.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include "llvm/WindowsManifest/WindowsManifestMerger.h" #include using namespace llvm::COFF; using namespace llvm; using llvm::sys::Process; namespace lld { namespace coff { namespace { const uint16_t SUBLANG_ENGLISH_US = 0x0409; const uint16_t RT_MANIFEST = 24; class Executor { public: explicit Executor(StringRef s) : prog(saver.save(s)) {} void add(StringRef s) { args.push_back(saver.save(s)); } void add(std::string &s) { args.push_back(saver.save(s)); } void add(Twine s) { args.push_back(saver.save(s)); } void add(const char *s) { args.push_back(saver.save(s)); } void run() { ErrorOr exeOrErr = sys::findProgramByName(prog); if (auto ec = exeOrErr.getError()) fatal("unable to find " + prog + " in PATH: " + ec.message()); StringRef exe = saver.save(*exeOrErr); args.insert(args.begin(), exe); if (sys::ExecuteAndWait(args[0], args) != 0) fatal("ExecuteAndWait failed: " + llvm::join(args.begin(), args.end(), " ")); } private: StringRef prog; std::vector args; }; } // anonymous namespace // Parses a string in the form of "[,]". void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { StringRef s1, s2; std::tie(s1, s2) = arg.split(','); if (s1.getAsInteger(0, *addr)) fatal("invalid number: " + s1); if (size && !s2.empty() && s2.getAsInteger(0, *size)) fatal("invalid number: " + s2); } // Parses a string in the form of "[.]". // If second number is not present, Minor is set to 0. void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { StringRef s1, s2; std::tie(s1, s2) = arg.split('.'); if (s1.getAsInteger(0, *major)) fatal("invalid number: " + s1); *minor = 0; if (!s2.empty() && s2.getAsInteger(0, *minor)) fatal("invalid number: " + s2); } void parseGuard(StringRef fullArg) { SmallVector splitArgs; fullArg.split(splitArgs, ","); for (StringRef arg : splitArgs) { if (arg.equals_lower("no")) config->guardCF = GuardCFLevel::Off; else if (arg.equals_lower("nolongjmp")) config->guardCF = GuardCFLevel::NoLongJmp; else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) config->guardCF = GuardCFLevel::Full; else fatal("invalid argument to /guard: " + arg); } } // Parses a string in the form of "[,[.]]". void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, uint32_t *minor) { StringRef sysStr, ver; std::tie(sysStr, ver) = arg.split(','); std::string sysStrLower = sysStr.lower(); *sys = StringSwitch(sysStrLower) .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) .Case("default", IMAGE_SUBSYSTEM_UNKNOWN) .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) .Case("native", IMAGE_SUBSYSTEM_NATIVE) .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI) .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) .Default(IMAGE_SUBSYSTEM_UNKNOWN); if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default") fatal("unknown subsystem: " + sysStr); if (!ver.empty()) parseVersion(ver, major, minor); } // Parse a string of the form of "=". // Results are directly written to Config. void parseAlternateName(StringRef s) { StringRef from, to; std::tie(from, to) = s.split('='); if (from.empty() || to.empty()) fatal("/alternatename: invalid argument: " + s); auto it = config->alternateNames.find(from); if (it != config->alternateNames.end() && it->second != to) fatal("/alternatename: conflicts: " + s); config->alternateNames.insert(it, std::make_pair(from, to)); } // Parse a string of the form of "=". // Results are directly written to Config. void parseMerge(StringRef s) { StringRef from, to; std::tie(from, to) = s.split('='); if (from.empty() || to.empty()) fatal("/merge: invalid argument: " + s); if (from == ".rsrc" || to == ".rsrc") fatal("/merge: cannot merge '.rsrc' with any section"); if (from == ".reloc" || to == ".reloc") fatal("/merge: cannot merge '.reloc' with any section"); auto pair = config->merge.insert(std::make_pair(from, to)); bool inserted = pair.second; if (!inserted) { StringRef existing = pair.first->second; if (existing != to) warn(s + ": already merged into " + existing); } } static uint32_t parseSectionAttributes(StringRef s) { uint32_t ret = 0; for (char c : s.lower()) { switch (c) { case 'd': ret |= IMAGE_SCN_MEM_DISCARDABLE; break; case 'e': ret |= IMAGE_SCN_MEM_EXECUTE; break; case 'k': ret |= IMAGE_SCN_MEM_NOT_CACHED; break; case 'p': ret |= IMAGE_SCN_MEM_NOT_PAGED; break; case 'r': ret |= IMAGE_SCN_MEM_READ; break; case 's': ret |= IMAGE_SCN_MEM_SHARED; break; case 'w': ret |= IMAGE_SCN_MEM_WRITE; break; default: fatal("/section: invalid argument: " + s); } } return ret; } // Parses /section option argument. void parseSection(StringRef s) { StringRef name, attrs; std::tie(name, attrs) = s.split(','); if (name.empty() || attrs.empty()) fatal("/section: invalid argument: " + s); config->section[name] = parseSectionAttributes(attrs); } // Parses /aligncomm option argument. void parseAligncomm(StringRef s) { StringRef name, align; std::tie(name, align) = s.split(','); if (name.empty() || align.empty()) { error("/aligncomm: invalid argument: " + s); return; } int v; if (align.getAsInteger(0, v)) { error("/aligncomm: invalid argument: " + s); return; } config->alignComm[name] = std::max(config->alignComm[name], 1 << v); } // Parses /functionpadmin option argument. void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { StringRef arg = a->getNumValues() ? a->getValue() : ""; if (!arg.empty()) { // Optional padding in bytes is given. if (arg.getAsInteger(0, config->functionPadMin)) error("/functionpadmin: invalid argument: " + arg); return; } // No optional argument given. // Set default padding based on machine, similar to link.exe. // There is no default padding for ARM platforms. if (machine == I386) { config->functionPadMin = 5; } else if (machine == AMD64) { config->functionPadMin = 6; } else { error("/functionpadmin: invalid argument for this machine: " + arg); } } // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to Config. void parseManifest(StringRef arg) { if (arg.equals_lower("no")) { config->manifest = Configuration::No; return; } if (!arg.startswith_lower("embed")) fatal("invalid option " + arg); config->manifest = Configuration::Embed; arg = arg.substr(strlen("embed")); if (arg.empty()) return; if (!arg.startswith_lower(",id=")) fatal("invalid option " + arg); arg = arg.substr(strlen(",id=")); if (arg.getAsInteger(0, config->manifestID)) fatal("invalid option " + arg); } // Parses a string in the form of "level=|uiAccess=|NO". // Results are directly written to Config. void parseManifestUAC(StringRef arg) { if (arg.equals_lower("no")) { config->manifestUAC = false; return; } for (;;) { arg = arg.ltrim(); if (arg.empty()) return; if (arg.startswith_lower("level=")) { arg = arg.substr(strlen("level=")); std::tie(config->manifestLevel, arg) = arg.split(" "); continue; } if (arg.startswith_lower("uiaccess=")) { arg = arg.substr(strlen("uiaccess=")); std::tie(config->manifestUIAccess, arg) = arg.split(" "); continue; } fatal("invalid option " + arg); } } // Parses a string in the form of "cd|net[,(cd|net)]*" // Results are directly written to Config. void parseSwaprun(StringRef arg) { do { StringRef swaprun, newArg; std::tie(swaprun, newArg) = arg.split(','); if (swaprun.equals_lower("cd")) config->swaprunCD = true; else if (swaprun.equals_lower("net")) config->swaprunNet = true; else if (swaprun.empty()) error("/swaprun: missing argument"); else error("/swaprun: invalid argument: " + swaprun); // To catch trailing commas, e.g. `/spawrun:cd,` if (newArg.empty() && arg.endswith(",")) error("/swaprun: missing argument"); arg = newArg; } while (!arg.empty()); } // An RAII temporary file class that automatically removes a temporary file. namespace { class TemporaryFile { public: TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") { SmallString<128> s; if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s)) fatal("cannot create a temporary file: " + ec.message()); path = s.str(); if (!contents.empty()) { std::error_code ec; - raw_fd_ostream os(path, ec, sys::fs::F_None); + raw_fd_ostream os(path, ec, sys::fs::OF_None); if (ec) fatal("failed to open " + path + ": " + ec.message()); os << contents; } } TemporaryFile(TemporaryFile &&obj) { std::swap(path, obj.path); } ~TemporaryFile() { if (path.empty()) return; if (sys::fs::remove(path)) fatal("failed to remove " + path); } // Returns a memory buffer of this temporary file. // Note that this function does not leave the file open, // so it is safe to remove the file immediately after this function // is called (you cannot remove an opened file on Windows.) std::unique_ptr getMemoryBuffer() { // IsVolatile=true forces MemoryBuffer to not use mmap(). return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false, /*IsVolatile=*/true), "could not open " + path); } std::string path; }; } static std::string createDefaultXml() { std::string ret; raw_string_ostream os(ret); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. os << "\n" << "\n"; if (config->manifestUAC) { os << " \n" << " \n" << " \n" << " \n" << " \n" << " \n" << " \n"; } if (!config->manifestDependency.empty()) { os << " \n" << " \n" << " manifestDependency << " />\n" << " \n" << " \n"; } os << "\n"; return os.str(); } static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { std::unique_ptr defaultXmlCopy = MemoryBuffer::getMemBufferCopy(defaultXml); windows_manifest::WindowsManifestMerger merger; if (auto e = merger.merge(*defaultXmlCopy.get())) fatal("internal manifest tool failed on default xml: " + toString(std::move(e))); for (StringRef filename : config->manifestInput) { std::unique_ptr manifest = check(MemoryBuffer::getFile(filename)); if (auto e = merger.merge(*manifest.get())) fatal("internal manifest tool failed on file " + filename + ": " + toString(std::move(e))); } return merger.getMergedManifest().get()->getBuffer(); } static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { // Create the default manifest file as a temporary file. TemporaryFile Default("defaultxml", "manifest"); std::error_code ec; - raw_fd_ostream os(Default.path, ec, sys::fs::F_Text); + raw_fd_ostream os(Default.path, ec, sys::fs::OF_Text); if (ec) fatal("failed to open " + Default.path + ": " + ec.message()); os << defaultXml; os.close(); // Merge user-supplied manifests if they are given. Since libxml2 is not // enabled, we must shell out to Microsoft's mt.exe tool. TemporaryFile user("user", "manifest"); Executor e("mt.exe"); e.add("/manifest"); e.add(Default.path); for (StringRef filename : config->manifestInput) { e.add("/manifest"); e.add(filename); } e.add("/nologo"); e.add("/out:" + StringRef(user.path)); e.run(); return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) .get() ->getBuffer(); } static std::string createManifestXml() { std::string defaultXml = createDefaultXml(); if (config->manifestInput.empty()) return defaultXml; if (windows_manifest::isAvailable()) return createManifestXmlWithInternalMt(defaultXml); return createManifestXmlWithExternalMt(defaultXml); } static std::unique_ptr createMemoryBufferForManifestRes(size_t manifestSize) { size_t resSize = alignTo( object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix) + manifestSize, object::WIN_RES_DATA_ALIGNMENT); return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile + ".manifest.res"); } static void writeResFileHeader(char *&buf) { memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); buf += sizeof(COFF::WinResMagic); memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); buf += object::WIN_RES_NULL_ENTRY_SIZE; } static void writeResEntryHeader(char *&buf, size_t manifestSize) { // Write the prefix. auto *prefix = reinterpret_cast(buf); prefix->DataSize = manifestSize; prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix); buf += sizeof(object::WinResHeaderPrefix); // Write the Type/Name IDs. auto *iDs = reinterpret_cast(buf); iDs->setType(RT_MANIFEST); iDs->setName(config->manifestID); buf += sizeof(object::WinResIDs); // Write the suffix. auto *suffix = reinterpret_cast(buf); suffix->DataVersion = 0; suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; suffix->Language = SUBLANG_ENGLISH_US; suffix->Version = 0; suffix->Characteristics = 0; buf += sizeof(object::WinResHeaderSuffix); } // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes() { std::string manifest = createManifestXml(); std::unique_ptr res = createMemoryBufferForManifestRes(manifest.size()); char *buf = res->getBufferStart(); writeResFileHeader(buf); writeResEntryHeader(buf, manifest.size()); // Copy the manifest data into the .res file. std::copy(manifest.begin(), manifest.end(), buf); return std::move(res); } void createSideBySideManifest() { std::string path = config->manifestFile; if (path == "") path = config->outputFile + ".manifest"; std::error_code ec; - raw_fd_ostream out(path, ec, sys::fs::F_Text); + raw_fd_ostream out(path, ec, sys::fs::OF_Text); if (ec) fatal("failed to create manifest: " + ec.message()); out << createManifestXml(); } // Parse a string in the form of // "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "=.". // Used for parsing /export arguments. Export parseExport(StringRef arg) { Export e; StringRef rest; std::tie(e.name, rest) = arg.split(","); if (e.name.empty()) goto err; if (e.name.contains('=')) { StringRef x, y; std::tie(x, y) = e.name.split("="); // If "=.". if (y.contains(".")) { e.name = x; e.forwardTo = y; return e; } e.extName = x; e.name = y; if (e.name.empty()) goto err; } // If "=[,@ordinal[,NONAME]][,DATA][,PRIVATE]" while (!rest.empty()) { StringRef tok; std::tie(tok, rest) = rest.split(","); if (tok.equals_lower("noname")) { if (e.ordinal == 0) goto err; e.noname = true; continue; } if (tok.equals_lower("data")) { e.data = true; continue; } if (tok.equals_lower("constant")) { e.constant = true; continue; } if (tok.equals_lower("private")) { e.isPrivate = true; continue; } if (tok.startswith("@")) { int32_t ord; if (tok.substr(1).getAsInteger(0, ord)) goto err; if (ord <= 0 || 65535 < ord) goto err; e.ordinal = ord; continue; } goto err; } return e; err: fatal("invalid /export: " + arg); } static StringRef undecorate(StringRef sym) { if (config->machine != I386) return sym; // In MSVC mode, a fully decorated stdcall function is exported // as-is with the leading underscore (with type IMPORT_NAME). // In MinGW mode, a decorated stdcall function gets the underscore // removed, just like normal cdecl functions. if (sym.startswith("_") && sym.contains('@') && !config->mingw) return sym; return sym.startswith("_") ? sym.substr(1) : sym; } // Convert stdcall/fastcall style symbols into unsuffixed symbols, // with or without a leading underscore. (MinGW specific.) static StringRef killAt(StringRef sym, bool prefix) { if (sym.empty()) return sym; // Strip any trailing stdcall suffix sym = sym.substr(0, sym.find('@', 1)); if (!sym.startswith("@")) { if (prefix && !sym.startswith("_")) return saver.save("_" + sym); return sym; } // For fastcall, remove the leading @ and replace it with an // underscore, if prefixes are used. sym = sym.substr(1); if (prefix) sym = saver.save("_" + sym); return sym; } // Performs error checking on all /export arguments. // It also sets ordinals. void fixupExports() { // Symbol ordinals must be unique. std::set ords; for (Export &e : config->exports) { if (e.ordinal == 0) continue; if (!ords.insert(e.ordinal).second) fatal("duplicate export ordinal: " + e.name); } for (Export &e : config->exports) { if (!e.forwardTo.empty()) { e.exportName = undecorate(e.name); } else { e.exportName = undecorate(e.extName.empty() ? e.name : e.extName); } } if (config->killAt && config->machine == I386) { for (Export &e : config->exports) { e.name = killAt(e.name, true); e.exportName = killAt(e.exportName, false); e.extName = killAt(e.extName, true); e.symbolName = killAt(e.symbolName, true); } } // Uniquefy by name. DenseMap map(config->exports.size()); std::vector v; for (Export &e : config->exports) { auto pair = map.insert(std::make_pair(e.exportName, &e)); bool inserted = pair.second; if (inserted) { v.push_back(e); continue; } Export *existing = pair.first->second; if (e == *existing || e.name != existing->name) continue; warn("duplicate /export option: " + e.name); } config->exports = std::move(v); // Sort by name. std::sort(config->exports.begin(), config->exports.end(), [](const Export &a, const Export &b) { return a.exportName < b.exportName; }); } void assignExportOrdinals() { // Assign unique ordinals if default (= 0). uint16_t max = 0; for (Export &e : config->exports) max = std::max(max, e.ordinal); for (Export &e : config->exports) if (e.ordinal == 0) e.ordinal = ++max; } // Parses a string in the form of "key=value" and check // if value matches previous values for the same key. void checkFailIfMismatch(StringRef arg, InputFile *source) { StringRef k, v; std::tie(k, v) = arg.split('='); if (k.empty() || v.empty()) fatal("/failifmismatch: invalid argument: " + arg); std::pair existing = config->mustMatch[k]; if (!existing.first.empty() && v != existing.first) { std::string sourceStr = source ? toString(source) : "cmd-line"; std::string existingStr = existing.second ? toString(existing.second) : "cmd-line"; fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " + existingStr + " has value " + existing.first + "\n>>> " + sourceStr + " has value " + v); } config->mustMatch[k] = {v, source}; } // Convert Windows resource files (.res files) to a .obj file. // Does what cvtres.exe does, but in-process and cross-platform. -MemoryBufferRef convertResToCOFF(ArrayRef mbs) { - object::WindowsResourceParser parser; +MemoryBufferRef convertResToCOFF(ArrayRef mbs, + ArrayRef objs) { + object::WindowsResourceParser parser(/* MinGW */ config->mingw); + std::vector duplicates; for (MemoryBufferRef mb : mbs) { std::unique_ptr bin = check(object::createBinary(mb)); object::WindowsResource *rf = dyn_cast(bin.get()); if (!rf) fatal("cannot compile non-resource file as resource"); - std::vector duplicates; if (auto ec = parser.parse(rf, duplicates)) fatal(toString(std::move(ec))); + } - for (const auto &dupeDiag : duplicates) - if (config->forceMultipleRes) - warn(dupeDiag); - else - error(dupeDiag); + // Note: This processes all .res files before all objs. Ideally they'd be + // handled in the same order they were linked (to keep the right one, if + // there are duplicates that are tolerated due to forceMultipleRes). + for (ObjFile *f : objs) { + object::ResourceSectionRef rsf; + if (auto ec = rsf.load(f->getCOFFObj())) + fatal(toString(f) + ": " + toString(std::move(ec))); + + if (auto ec = parser.parse(rsf, f->getName(), duplicates)) + fatal(toString(std::move(ec))); } + if (config->mingw) + parser.cleanUpManifests(duplicates); + + for (const auto &dupeDiag : duplicates) + if (config->forceMultipleRes) + warn(dupeDiag); + else + error(dupeDiag); + Expected> e = llvm::object::writeWindowsResourceCOFF(config->machine, parser, config->timestamp); if (!e) fatal("failed to write .res to COFF: " + toString(e.takeError())); MemoryBufferRef mbref = **e; make>(std::move(*e)); // take ownership return mbref; } // Create OptTable // Create prefix string literals used in Options.td #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td static const llvm::opt::OptTable::Info infoTable[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, #include "Options.inc" #undef OPTION }; COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} // Set color diagnostics according to --color-diagnostics={auto,always,never} // or --no-color-diagnostics flags. static void handleColorDiagnostics(opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, OPT_no_color_diagnostics); if (!arg) return; if (arg->getOption().getID() == OPT_color_diagnostics) { - errorHandler().colorDiagnostics = true; + enableColors(true); } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { - errorHandler().colorDiagnostics = false; + enableColors(false); } else { StringRef s = arg->getValue(); if (s == "always") - errorHandler().colorDiagnostics = true; + enableColors(true); else if (s == "never") - errorHandler().colorDiagnostics = false; + enableColors(false); else if (s != "auto") error("unknown option: --color-diagnostics=" + s); } } static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { StringRef s = arg->getValue(); if (s != "windows" && s != "posix") error("invalid response file quoting: " + s); if (s == "windows") return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } // The COFF linker always defaults to Windows quoting. return cl::TokenizeWindowsCommandLine; } // Parses a given list of options. opt::InputArgList ArgParser::parse(ArrayRef argv) { // Make InputArgList from string vectors. unsigned missingIndex; unsigned missingCount; // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but - // --rsp-quoting. + // --rsp-quoting and /lldignoreenv. + // (This means --rsp-quoting can't be added through %LINK%.) opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); - // Expand response files (arguments in the form of @) - // and then parse the argument again. + + // Expand response files (arguments in the form of @) and insert + // flags from %LINK% and %_LINK_%, and then parse the argument again. SmallVector expandedArgv(argv.data(), argv.data() + argv.size()); + if (!args.hasArg(OPT_lldignoreenv)) + addLINK(expandedArgv); cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, missingCount); // Print the real command line if response files are expanded. if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { std::string msg = "Command line:"; for (const char *s : expandedArgv) msg += " " + std::string(s); message(msg); } // Save the command line after response file expansion so we can write it to // the PDB if necessary. config->argv = {expandedArgv.begin(), expandedArgv.end()}; // Handle /WX early since it converts missing argument warnings to errors. errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); if (missingCount) fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); handleColorDiagnostics(args); for (auto *arg : args.filtered(OPT_UNKNOWN)) { std::string nearest; if (table.findNearest(arg->getAsString(args), nearest) > 1) warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); else warn("ignoring unknown argument '" + arg->getAsString(args) + "', did you mean '" + nearest + "'"); } if (args.hasArg(OPT_lib)) warn("ignoring /lib since it's not the first argument"); return args; } // Tokenizes and parses a given string as command line in .drective section. // /EXPORT options are processed in fastpath. std::pair> ArgParser::parseDirectives(StringRef s) { std::vector exports; SmallVector rest; for (StringRef tok : tokenize(s)) { if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:")) exports.push_back(tok.substr(strlen("/export:"))); else rest.push_back(tok.data()); } // Make InputArgList from unparsed string vectors. unsigned missingIndex; unsigned missingCount; opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount); if (missingCount) fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); for (auto *arg : args.filtered(OPT_UNKNOWN)) warn("ignoring unknown argument: " + arg->getAsString(args)); return {std::move(args), std::move(exports)}; } // link.exe has an interesting feature. If LINK or _LINK_ environment // variables exist, their contents are handled as command line strings. // So you can pass extra arguments using them. -opt::InputArgList ArgParser::parseLINK(std::vector argv) { +void ArgParser::addLINK(SmallVector &argv) { // Concatenate LINK env and command line arguments, and then parse them. if (Optional s = Process::GetEnv("LINK")) { std::vector v = tokenize(*s); argv.insert(std::next(argv.begin()), v.begin(), v.end()); } if (Optional s = Process::GetEnv("_LINK_")) { std::vector v = tokenize(*s); argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - return parse(argv); } std::vector ArgParser::tokenize(StringRef s) { SmallVector tokens; cl::TokenizeWindowsCommandLine(s, saver, tokens); return std::vector(tokens.begin(), tokens.end()); } void printHelp(const char *argv0) { COFFOptTable().PrintHelp(outs(), (std::string(argv0) + " [options] file...").c_str(), "LLVM Linker", false); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/ICF.cpp =================================================================== --- vendor/lld/dist/COFF/ICF.cpp (revision 353949) +++ vendor/lld/dist/COFF/ICF.cpp (revision 353950) @@ -1,317 +1,317 @@ //===- ICF.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // ICF is short for Identical Code Folding. That is a size optimization to // identify and merge two or more read-only sections (typically functions) // that happened to have the same contents. It usually reduces output size // by a few percent. // // On Windows, ICF is enabled by default. // -// See ELF/ICF.cpp for the details about the algortihm. +// See ELF/ICF.cpp for the details about the algorithm. // //===----------------------------------------------------------------------===// #include "ICF.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/xxhash.h" #include #include #include using namespace llvm; namespace lld { namespace coff { static Timer icfTimer("ICF", Timer::root()); class ICF { public: void run(ArrayRef v); private: void segregate(size_t begin, size_t end, bool constant); bool assocEquals(const SectionChunk *a, const SectionChunk *b); bool equalsConstant(const SectionChunk *a, const SectionChunk *b); bool equalsVariable(const SectionChunk *a, const SectionChunk *b); bool isEligible(SectionChunk *c); size_t findBoundary(size_t begin, size_t end); void forEachClassRange(size_t begin, size_t end, std::function fn); void forEachClass(std::function fn); std::vector chunks; int cnt = 0; std::atomic repeat = {false}; }; // Returns true if section S is subject of ICF. // // Microsoft's documentation // (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April // 2017) says that /opt:icf folds both functions and read-only data. // Despite that, the MSVC linker folds only functions. We found // a few instances of programs that are not safe for data merging. // Therefore, we merge only functions just like the MSVC tool. However, we also // merge read-only sections in a couple of cases where the address of the // section is insignificant to the user program and the behaviour matches that // of the Visual C++ linker. bool ICF::isEligible(SectionChunk *c) { - // Non-comdat chunks, dead chunks, and writable chunks are not elegible. + // Non-comdat chunks, dead chunks, and writable chunks are not eligible. bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; if (!c->isCOMDAT() || !c->live || writable) return false; // Code sections are eligible. if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; // .pdata and .xdata unwind info sections are eligible. StringRef outSecName = c->getSectionName().split('$').first; if (outSecName == ".pdata" || outSecName == ".xdata") return true; // So are vtables. if (c->sym && c->sym->getName().startswith("??_7")) return true; // Anything else not in an address-significance table is eligible. return !c->keepUnique; } // Split an equivalence class into smaller classes. void ICF::segregate(size_t begin, size_t end, bool constant) { while (begin < end) { // Divide [Begin, End) into two. Let Mid be the start index of the // second group. auto bound = std::stable_partition( chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) { if (constant) return equalsConstant(chunks[begin], s); return equalsVariable(chunks[begin], s); }); size_t mid = bound - chunks.begin(); // Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an // equivalence class ID because every group ends with a unique index. for (size_t i = begin; i < mid; ++i) chunks[i]->eqClass[(cnt + 1) % 2] = mid; // If we created a group, we need to iterate the main loop again. if (mid != end) repeat = true; begin = mid; } } // Returns true if two sections' associative children are equal. bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { auto childClasses = [&](const SectionChunk *sc) { std::vector classes; for (const SectionChunk &c : sc->children()) if (!c.getSectionName().startswith(".debug") && c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y") classes.push_back(c.eqClass[cnt % 2]); return classes; }; return childClasses(a) == childClasses(b); } // Compare "non-moving" part of two sections, namely everything // except relocation targets. bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) { if (a->relocsSize != b->relocsSize) return false; // Compare relocations. auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { if (r1.Type != r2.Type || r1.VirtualAddress != r2.VirtualAddress) { return false; } Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); if (b1 == b2) return true; if (auto *d1 = dyn_cast(b1)) if (auto *d2 = dyn_cast(b2)) return d1->getValue() == d2->getValue() && d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(), b->getRelocs().begin(), eq)) return false; // Compare section attributes and contents. return a->getOutputCharacteristics() == b->getOutputCharacteristics() && a->getSectionName() == b->getSectionName() && a->header->SizeOfRawData == b->header->SizeOfRawData && a->checksum == b->checksum && a->getContents() == b->getContents() && assocEquals(a, b); } // Compare "moving" part of two sections, namely relocation targets. bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) { // Compare relocations. auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); if (b1 == b2) return true; if (auto *d1 = dyn_cast(b1)) if (auto *d2 = dyn_cast(b2)) return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; return std::equal(a->getRelocs().begin(), a->getRelocs().end(), b->getRelocs().begin(), eq) && assocEquals(a, b); } // Find the first Chunk after Begin that has a different class from Begin. size_t ICF::findBoundary(size_t begin, size_t end) { for (size_t i = begin + 1; i < end; ++i) if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2]) return i; return end; } void ICF::forEachClassRange(size_t begin, size_t end, std::function fn) { while (begin < end) { size_t mid = findBoundary(begin, end); fn(begin, mid); begin = mid; } } // Call Fn on each class group. void ICF::forEachClass(std::function fn) { // If the number of sections are too small to use threading, // call Fn sequentially. if (chunks.size() < 1024) { forEachClassRange(0, chunks.size(), fn); ++cnt; return; } // Shard into non-overlapping intervals, and call Fn in parallel. // The sharding must be completed before any calls to Fn are made // so that Fn can modify the Chunks in its shard without causing data // races. const size_t numShards = 256; size_t step = chunks.size() / numShards; size_t boundaries[numShards + 1]; boundaries[0] = 0; boundaries[numShards] = chunks.size(); parallelForEachN(1, numShards, [&](size_t i) { boundaries[i] = findBoundary((i - 1) * step, chunks.size()); }); parallelForEachN(1, numShards + 1, [&](size_t i) { if (boundaries[i - 1] < boundaries[i]) { forEachClassRange(boundaries[i - 1], boundaries[i], fn); } }); ++cnt; } // Merge identical COMDAT sections. // Two sections are considered the same if their section headers, // contents and relocations are all the same. void ICF::run(ArrayRef vec) { ScopedTimer t(icfTimer); // Collect only mergeable sections and group by hash value. uint32_t nextId = 1; for (Chunk *c : vec) { if (auto *sc = dyn_cast(c)) { if (isEligible(sc)) chunks.push_back(sc); else sc->eqClass[0] = nextId++; } } // Make sure that ICF doesn't merge sections that are being handled by string // tail merging. for (MergeChunk *mc : MergeChunk::instances) if (mc) for (SectionChunk *sc : mc->sections) sc->eqClass[0] = nextId++; // Initially, we use hash values to partition sections. parallelForEach(chunks, [&](SectionChunk *sc) { sc->eqClass[0] = xxHash64(sc->getContents()); }); // Combine the hashes of the sections referenced by each section into its // hash. for (unsigned cnt = 0; cnt != 2; ++cnt) { parallelForEach(chunks, [&](SectionChunk *sc) { uint32_t hash = sc->eqClass[cnt % 2]; for (Symbol *b : sc->symbols()) if (auto *sym = dyn_cast_or_null(b)) hash += sym->getChunk()->eqClass[cnt % 2]; - // Set MSB to 1 to avoid collisions with non-hash classs. + // Set MSB to 1 to avoid collisions with non-hash classes. sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31); }); } // From now on, sections in Chunks are ordered so that sections in // the same group are consecutive in the vector. llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) { return a->eqClass[0] < b->eqClass[0]; }); // Compare static contents and assign unique IDs for each static content. forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); }); // Split groups by comparing relocations until convergence is obtained. do { repeat = false; forEachClass( [&](size_t begin, size_t end) { segregate(begin, end, false); }); } while (repeat); log("ICF needed " + Twine(cnt) + " iterations"); - // Merge sections in the same classs. + // Merge sections in the same classes. forEachClass([&](size_t begin, size_t end) { if (end - begin == 1) return; log("Selected " + chunks[begin]->getDebugName()); for (size_t i = begin + 1; i < end; ++i) { log(" Removed " + chunks[i]->getDebugName()); chunks[begin]->replace(chunks[i]); } }); } // Entry point to ICF. void doICF(ArrayRef chunks) { ICF().run(chunks); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/InputFiles.cpp =================================================================== --- vendor/lld/dist/COFF/InputFiles.cpp (revision 353949) +++ vendor/lld/dist/COFF/InputFiles.cpp (revision 353950) @@ -1,881 +1,963 @@ //===- InputFiles.cpp -----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" #include "Driver.h" #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm-c/lto.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Target/TargetOptions.h" #include #include #include using namespace llvm; using namespace llvm::COFF; using namespace llvm::codeview; using namespace llvm::object; using namespace llvm::support::endian; using llvm::Triple; using llvm::support::ulittle32_t; namespace lld { + +// Returns the last element of a path, which is supposed to be a filename. +static StringRef getBasename(StringRef path) { + return sys::path::filename(path, sys::path::Style::windows); +} + +// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". +std::string toString(const coff::InputFile *file) { + if (!file) + return ""; + if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) + return file->getName(); + + return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + + ")") + .str(); +} + namespace coff { std::vector ObjFile::instances; std::vector ImportFile::instances; std::vector BitcodeFile::instances; /// Checks that Source is compatible with being a weak alias to Target. /// If Source is Undefined and has no weak alias set, makes it a weak /// alias to Target. static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, Symbol *source, Symbol *target) { if (auto *u = dyn_cast(source)) { if (u->weakAlias && u->weakAlias != target) { // Weak aliases as produced by GCC are named in the form // .weak.., where is the name // of another symbol emitted near the weak symbol. // Just use the definition from the first object file that defined // this weak symbol. if (config->mingw) return; symtab->reportDuplicate(source, f); } u->weakAlias = target; } } +static bool ignoredSymbolName(StringRef name) { + return name == "@feat.00" || name == "@comp.id"; +} + ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. file = CHECK(Archive::create(mb), this); // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &sym : file->symbols()) - symtab->addLazy(this, sym); + symtab->addLazyArchive(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. -void ArchiveFile::addMember(const Archive::Symbol *sym) { +void ArchiveFile::addMember(const Archive::Symbol &sym) { const Archive::Child &c = - CHECK(sym->getMember(), - "could not get the member for symbol " + sym->getName()); + CHECK(sym.getMember(), + "could not get the member for symbol " + toCOFFString(sym)); // Return an empty buffer if we have already returned the same buffer. if (!seen.insert(c.getChildOffset()).second) return; - driver->enqueueArchiveMember(c, sym->getName(), getName()); + driver->enqueueArchiveMember(c, sym, getName()); } std::vector getArchiveMembers(Archive *file) { std::vector v; Error err = Error::success(); for (const ErrorOr &cOrErr : file->children(err)) { Archive::Child c = CHECK(cOrErr, file->getFileName() + ": could not get the child of the archive"); MemoryBufferRef mbref = CHECK(c.getMemoryBufferRef(), file->getFileName() + ": could not get the buffer for a child of the archive"); v.push_back(mbref); } if (err) fatal(file->getFileName() + ": Archive::children failed: " + toString(std::move(err))); return v; } +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; + + InputFile *file; + if (isBitcode(mb)) + file = make(mb, "", 0, std::move(symbols)); + else + file = make(mb, std::move(symbols)); + mb = {}; + symtab->addFile(file); +} + +void LazyObjFile::parse() { + if (isBitcode(this->mb)) { + // Bitcode file. + std::unique_ptr obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (!sym.isUndefined()) + symtab->addLazyObject(this, sym.getName()); + } + return; + } + + // Native object file. + std::unique_ptr coffObjPtr = CHECK(createBinary(mb), this); + COFFObjectFile *coffObj = cast(coffObjPtr.get()); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + if (coffSym.isUndefined() || !coffSym.isExternal() || + coffSym.isWeakExternal()) + continue; + StringRef name; + coffObj->getSymbolName(coffSym, name); + if (coffSym.isAbsolute() && ignoredSymbolName(name)) + continue; + symtab->addLazyObject(this, name); + i += coffSym.getNumberOfAuxSymbols(); + } +} + void ObjFile::parse() { // Parse a memory buffer as a COFF file. std::unique_ptr bin = CHECK(createBinary(mb), this); if (auto *obj = dyn_cast(bin.get())) { bin.release(); coffObj.reset(obj); } else { fatal(toString(this) + " is not a COFF file"); } // Read section and symbol tables. initializeChunks(); initializeSymbols(); initializeFlags(); initializeDependencies(); } const coff_section* ObjFile::getSection(uint32_t i) { const coff_section *sec; if (auto ec = coffObj->getSection(i, sec)) fatal("getSection failed: #" + Twine(i) + ": " + ec.message()); return sec; } // We set SectionChunk pointers in the SparseChunks vector to this value // temporarily to mark comdat sections as having an unknown resolution. As we // walk the object file's symbol table, once we visit either a leader symbol or // an associative section definition together with the parent comdat's leader, // we set the pointer to either nullptr (to mark the section as discarded) or a // valid SectionChunk for that section. static SectionChunk *const pendingComdat = reinterpret_cast(1); void ObjFile::initializeChunks() { uint32_t numSections = coffObj->getNumberOfSections(); chunks.reserve(numSections); sparseChunks.resize(numSections + 1); for (uint32_t i = 1; i < numSections + 1; ++i) { const coff_section *sec = getSection(i); if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT) sparseChunks[i] = pendingComdat; else sparseChunks[i] = readSection(i, nullptr, ""); } } SectionChunk *ObjFile::readSection(uint32_t sectionNumber, const coff_aux_section_definition *def, StringRef leaderName) { const coff_section *sec = getSection(sectionNumber); StringRef name; if (Expected e = coffObj->getSectionName(sec)) name = *e; else fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " + toString(e.takeError())); if (name == ".drectve") { ArrayRef data; cantFail(coffObj->getSectionContents(sec, data)); directives = StringRef((const char *)data.data(), data.size()); return nullptr; } if (name == ".llvm_addrsig") { addrsigSec = sec; return nullptr; } // Object files may have DWARF debug info or MS CodeView debug info // (or both). // // DWARF sections don't need any special handling from the perspective // of the linker; they are just a data section containing relocations. // We can just link them to complete debug info. // // CodeView needs linker support. We need to interpret debug info, // and then write it to a separate .pdb file. // Ignore DWARF debug info unless /debug is given. if (!config->debug && name.startswith(".debug_")) return nullptr; if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) return nullptr; auto *c = make(this, sec); if (def) c->checksum = def->CheckSum; - // link.exe uses the presence of .rsrc$01 for LNK4078, so match that. - if (name == ".rsrc$01") - isResourceObjFile = true; - // CodeView sections are stored to a different vector because they are not // linked in the regular manner. if (c->isCodeView()) debugChunks.push_back(c); else if (name == ".gfids$y") guardFidChunks.push_back(c); else if (name == ".gljmp$y") guardLJmpChunks.push_back(c); else if (name == ".sxdata") sXDataChunks.push_back(c); else if (config->tailMerge && sec->NumberOfRelocations == 0 && name == ".rdata" && leaderName.startswith("??_C@")) // COFF sections that look like string literal sections (i.e. no // relocations, in .rdata, leader symbol name matches the MSVC name mangling // for string literals) are subject to string tail merging. MergeChunk::addSection(c); + else if (name == ".rsrc" || name.startswith(".rsrc$")) + resourceChunks.push_back(c); else chunks.push_back(c); return c; } +void ObjFile::includeResourceChunks() { + chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end()); +} + void ObjFile::readAssociativeDefinition( COFFSymbolRef sym, const coff_aux_section_definition *def) { readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); } void ObjFile::readAssociativeDefinition(COFFSymbolRef sym, const coff_aux_section_definition *def, uint32_t parentIndex) { SectionChunk *parent = sparseChunks[parentIndex]; int32_t sectionNumber = sym.getSectionNumber(); auto diag = [&]() { StringRef name, parentName; coffObj->getSymbolName(sym, name); const coff_section *parentSec = getSection(parentIndex); if (Expected e = coffObj->getSectionName(parentSec)) parentName = *e; error(toString(this) + ": associative comdat " + name + " (sec " + Twine(sectionNumber) + ") has invalid reference to section " + parentName + " (sec " + Twine(parentIndex) + ")"); }; if (parent == pendingComdat) { // This can happen if an associative comdat refers to another associative // comdat that appears after it (invalid per COFF spec) or to a section // without any symbols. diag(); return; } // Check whether the parent is prevailing. If it is, so are we, and we read // the section; otherwise mark it as discarded. if (parent) { SectionChunk *c = readSection(sectionNumber, def, ""); sparseChunks[sectionNumber] = c; if (c) { c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE; parent->addAssociative(c); } } else { sparseChunks[sectionNumber] = nullptr; } } void ObjFile::recordPrevailingSymbolForMingw( COFFSymbolRef sym, DenseMap &prevailingSectionMap) { // For comdat symbols in executable sections, where this is the copy // of the section chunk we actually include instead of discarding it, // add the symbol to a map to allow using it for implicitly // associating .[px]data$ sections to it. int32_t sectionNumber = sym.getSectionNumber(); SectionChunk *sc = sparseChunks[sectionNumber]; if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { StringRef name; coffObj->getSymbolName(sym, name); if (getMachineType() == I386) name.consume_front("_"); prevailingSectionMap[name] = sectionNumber; } } void ObjFile::maybeAssociateSEHForMingw( COFFSymbolRef sym, const coff_aux_section_definition *def, const DenseMap &prevailingSectionMap) { StringRef name; coffObj->getSymbolName(sym, name); if (name.consume_front(".pdata$") || name.consume_front(".xdata$") || name.consume_front(".eh_frame$")) { // For MinGW, treat .[px]data$ and .eh_frame$ as implicitly // associative to the symbol . auto parentSym = prevailingSectionMap.find(name); if (parentSym != prevailingSectionMap.end()) readAssociativeDefinition(sym, def, parentSym->second); } } Symbol *ObjFile::createRegular(COFFSymbolRef sym) { SectionChunk *sc = sparseChunks[sym.getSectionNumber()]; if (sym.isExternal()) { StringRef name; coffObj->getSymbolName(sym, name); if (sc) - return symtab->addRegular(this, name, sym.getGeneric(), sc); + return symtab->addRegular(this, name, sym.getGeneric(), sc, + sym.getValue()); // For MinGW symbols named .weak.* that point to a discarded section, // don't create an Undefined symbol. If nothing ever refers to the symbol, // everything should be fine. If something actually refers to the symbol // (e.g. the undefined weak alias), linking will fail due to undefined // references at the end. if (config->mingw && name.startswith(".weak.")) return nullptr; return symtab->addUndefined(name, this, false); } if (sc) return make(this, /*Name*/ "", /*IsCOMDAT*/ false, /*IsExternal*/ false, sym.getGeneric(), sc); return nullptr; } void ObjFile::initializeSymbols() { uint32_t numSymbols = coffObj->getNumberOfSymbols(); symbols.resize(numSymbols); SmallVector, 8> weakAliases; std::vector pendingIndexes; pendingIndexes.reserve(numSymbols); DenseMap prevailingSectionMap; std::vector comdatDefs( coffObj->getNumberOfSections() + 1); for (uint32_t i = 0; i < numSymbols; ++i) { COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); bool prevailingComdat; if (coffSym.isUndefined()) { symbols[i] = createUndefined(coffSym); } else if (coffSym.isWeakExternal()) { symbols[i] = createUndefined(coffSym); uint32_t tagIndex = coffSym.getAux()->TagIndex; weakAliases.emplace_back(symbols[i], tagIndex); } else if (Optional optSym = createDefined(coffSym, comdatDefs, prevailingComdat)) { symbols[i] = *optSym; if (config->mingw && prevailingComdat) recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap); } else { // createDefined() returns None if a symbol belongs to a section that // was pending at the point when the symbol was read. This can happen in // two cases: // 1) section definition symbol for a comdat leader; // 2) symbol belongs to a comdat section associated with another section. // In both of these cases, we can expect the section to be resolved by // the time we finish visiting the remaining symbols in the symbol // table. So we postpone the handling of this symbol until that time. pendingIndexes.push_back(i); } i += coffSym.getNumberOfAuxSymbols(); } for (uint32_t i : pendingIndexes) { COFFSymbolRef sym = check(coffObj->getSymbol(i)); if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) readAssociativeDefinition(sym, def); else if (config->mingw) maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); } if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { StringRef name; coffObj->getSymbolName(sym, name); log("comdat section " + name + " without leader and unassociated, discarding"); continue; } symbols[i] = createRegular(sym); } for (auto &kv : weakAliases) { Symbol *sym = kv.first; uint32_t idx = kv.second; checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); } } Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { StringRef name; coffObj->getSymbolName(sym, name); return symtab->addUndefined(name, this, sym.isWeakExternal()); } void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, bool &prevailing, DefinedRegular *leader) { if (prevailing) return; // There's already an existing comdat for this symbol: `Leader`. // Use the comdats's selection field to determine if the new // symbol in `Sym` should be discarded, produce a duplicate symbol // error, etc. SectionChunk *leaderChunk = nullptr; COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY; if (leader->data) { leaderChunk = leader->getChunk(); leaderSelection = leaderChunk->selection; } else { // FIXME: comdats from LTO files don't know their selection; treat them // as "any". selection = leaderSelection; } if ((selection == IMAGE_COMDAT_SELECT_ANY && leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) || (selection == IMAGE_COMDAT_SELECT_LARGEST && leaderSelection == IMAGE_COMDAT_SELECT_ANY)) { // cl.exe picks "any" for vftables when building with /GR- and // "largest" when building with /GR. To be able to link object files // compiled with each flag, "any" and "largest" are merged as "largest". leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST; } // Other than that, comdat selections must match. This is a bit more // strict than link.exe which allows merging "any" and "largest" if "any" // is the first symbol the linker sees, and it allows merging "largest" // with everything (!) if "largest" is the first symbol the linker sees. // Making this symmetric independent of which selection is seen first // seems better though. // (This behavior matches ModuleLinker::getComdatResult().) if (selection != leaderSelection) { log(("conflicting comdat type for " + toString(*leader) + ": " + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + " and " + Twine((int)selection) + " in " + toString(this)) .str()); symtab->reportDuplicate(leader, this); return; } switch (selection) { case IMAGE_COMDAT_SELECT_NODUPLICATES: symtab->reportDuplicate(leader, this); break; case IMAGE_COMDAT_SELECT_ANY: // Nothing to do. break; case IMAGE_COMDAT_SELECT_SAME_SIZE: if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) symtab->reportDuplicate(leader, this); break; case IMAGE_COMDAT_SELECT_EXACT_MATCH: { SectionChunk newChunk(this, getSection(sym)); // link.exe only compares section contents here and doesn't complain // if the two comdat sections have e.g. different alignment. // Match that. if (leaderChunk->getContents() != newChunk.getContents()) - symtab->reportDuplicate(leader, this); + symtab->reportDuplicate(leader, this, &newChunk, sym.getValue()); break; } case IMAGE_COMDAT_SELECT_ASSOCIATIVE: // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE. // (This means lld-link doesn't produce duplicate symbol errors for // associative comdats while link.exe does, but associate comdats // are never extern in practice.) llvm_unreachable("createDefined not called for associative comdats"); case IMAGE_COMDAT_SELECT_LARGEST: if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) { // Replace the existing comdat symbol with the new one. StringRef name; coffObj->getSymbolName(sym, name); // FIXME: This is incorrect: With /opt:noref, the previous sections // make it into the final executable as well. Correct handling would // be to undo reading of the whole old section that's being replaced, // or doing one pass that determines what the final largest comdat // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading // only the largest one. replaceSymbol(leader, this, name, /*IsCOMDAT*/ true, /*IsExternal*/ true, sym.getGeneric(), nullptr); prevailing = true; } break; case IMAGE_COMDAT_SELECT_NEWEST: llvm_unreachable("should have been rejected earlier"); } } Optional ObjFile::createDefined( COFFSymbolRef sym, std::vector &comdatDefs, bool &prevailing) { prevailing = false; auto getName = [&]() { StringRef s; coffObj->getSymbolName(sym, s); return s; }; if (sym.isCommon()) { auto *c = make(sym); chunks.push_back(c); return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(), c); } if (sym.isAbsolute()) { StringRef name = getName(); + if (name == "@feat.00") + feat00Flags = sym.getValue(); // Skip special symbols. - if (name == "@comp.id") + if (ignoredSymbolName(name)) return nullptr; - if (name == "@feat.00") { - feat00Flags = sym.getValue(); - return nullptr; - } if (sym.isExternal()) return symtab->addAbsolute(name, sym); return make(name, sym); } int32_t sectionNumber = sym.getSectionNumber(); if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) return nullptr; if (llvm::COFF::isReservedSectionNumber(sectionNumber)) fatal(toString(this) + ": " + getName() + " should not refer to special section " + Twine(sectionNumber)); if ((uint32_t)sectionNumber >= sparseChunks.size()) fatal(toString(this) + ": " + getName() + " should not refer to non-existent section " + Twine(sectionNumber)); // Comdat handling. // A comdat symbol consists of two symbol table entries. // The first symbol entry has the name of the section (e.g. .text), fixed - // values for the other fields, and one auxilliary record. + // values for the other fields, and one auxiliary record. // The second symbol entry has the name of the comdat symbol, called the // "comdat leader". // When this function is called for the first symbol entry of a comdat, // it sets comdatDefs and returns None, and when it's called for the second // symbol entry it reads comdatDefs and then sets it back to nullptr. // Handle comdat leader. if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) { comdatDefs[sectionNumber] = nullptr; DefinedRegular *leader; if (sym.isExternal()) { std::tie(leader, prevailing) = symtab->addComdat(this, getName(), sym.getGeneric()); } else { leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, /*IsExternal*/ false, sym.getGeneric()); prevailing = true; } if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES || // Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe // doesn't understand IMAGE_COMDAT_SELECT_NEWEST either. def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) { fatal("unknown comdat type " + std::to_string((int)def->Selection) + " for " + getName() + " in " + toString(this)); } COMDATType selection = (COMDATType)def->Selection; if (leader->isCOMDAT) handleComdatSelection(sym, selection, prevailing, leader); if (prevailing) { SectionChunk *c = readSection(sectionNumber, def, getName()); sparseChunks[sectionNumber] = c; c->sym = cast(leader); c->selection = selection; cast(leader)->data = &c->repl; } else { sparseChunks[sectionNumber] = nullptr; } return leader; } // Prepare to handle the comdat leader symbol by setting the section's // ComdatDefs pointer if we encounter a non-associative comdat. if (sparseChunks[sectionNumber] == pendingComdat) { if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) comdatDefs[sectionNumber] = def; } return None; } return createRegular(sym); } MachineTypes ObjFile::getMachineType() { if (coffObj) return static_cast(coffObj->getMachine()); return IMAGE_FILE_MACHINE_UNKNOWN; } ArrayRef ObjFile::getDebugSection(StringRef secName) { if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName)) return sec->consumeDebugMagic(); return {}; } -// OBJ files systematically store critical informations in a .debug$S stream, +// OBJ files systematically store critical information in a .debug$S stream, // even if the TU was compiled with no debug info. At least two records are // always there. S_OBJNAME stores a 32-bit signature, which is loaded into the // PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is // currently used to initialize the hotPatchable member. void ObjFile::initializeFlags() { ArrayRef data = getDebugSection(".debug$S"); if (data.empty()) return; DebugSubsectionArray subsections; BinaryStreamReader reader(data, support::little); ExitOnError exitOnErr; exitOnErr(reader.readArray(subsections, data.size())); for (const DebugSubsectionRecord &ss : subsections) { if (ss.kind() != DebugSubsectionKind::Symbols) continue; unsigned offset = 0; // Only parse the first two records. We are only looking for S_OBJNAME // and S_COMPILE3, and they usually appear at the beginning of the // stream. for (unsigned i = 0; i < 2; ++i) { Expected sym = readSymbolFromStream(ss.getRecordData(), offset); if (!sym) { consumeError(sym.takeError()); return; } if (sym->kind() == SymbolKind::S_COMPILE3) { auto cs = cantFail(SymbolDeserializer::deserializeAs(sym.get())); hotPatchable = (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None; } if (sym->kind() == SymbolKind::S_OBJNAME) { auto objName = cantFail(SymbolDeserializer::deserializeAs( sym.get())); pchSignature = objName.Signature; } offset += sym->length(); } } } // Depending on the compilation flags, OBJs can refer to external files, // necessary to merge this OBJ into the final PDB. We currently support two // types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu. // And PDB type servers, when compiling with /Zi. This function extracts these // dependencies and makes them available as a TpiSource interface (see // DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular // output even with /Yc and /Yu and with /Zi. void ObjFile::initializeDependencies() { if (!config->debug) return; bool isPCH = false; ArrayRef data = getDebugSection(".debug$P"); if (!data.empty()) isPCH = true; else data = getDebugSection(".debug$T"); if (data.empty()) return; CVTypeArray types; BinaryStreamReader reader(data, support::little); cantFail(reader.readArray(types, reader.getLength())); CVTypeArray::Iterator firstType = types.begin(); if (firstType == types.end()) return; debugTypes.emplace(types); if (isPCH) { debugTypesObj = makePrecompSource(this); return; } if (firstType->kind() == LF_TYPESERVER2) { TypeServer2Record ts = cantFail( TypeDeserializer::deserializeAs(firstType->data())); debugTypesObj = makeUseTypeServerSource(this, &ts); return; } if (firstType->kind() == LF_PRECOMP) { PrecompRecord precomp = cantFail( TypeDeserializer::deserializeAs(firstType->data())); debugTypesObj = makeUsePrecompSource(this, &precomp); return; } debugTypesObj = makeTpiSource(this); } +// Used only for DWARF debug info, which is not common (except in MinGW +// environments). This returns an optional pair of file name and line +// number for where the variable was defined. +Optional> +ObjFile::getVariableLocation(StringRef var) { + if (!dwarf) { + dwarf = make(DWARFContext::create(*getCOFFObj())); + if (!dwarf) + return None; + } + if (config->machine == I386) + var.consume_front("_"); + Optional> ret = dwarf->getVariableLoc(var); + if (!ret) + return None; + return std::make_pair(saver.save(ret->first), ret->second); +} + +// Used only for DWARF debug info, which is not common (except in MinGW +// environments). +Optional ObjFile::getDILineInfo(uint32_t offset, + uint32_t sectionIndex) { + if (!dwarf) { + dwarf = make(DWARFContext::create(*getCOFFObj())); + if (!dwarf) + return None; + } + + return dwarf->getDILineInfo(offset, sectionIndex); +} + StringRef ltrim1(StringRef s, const char *chars) { if (!s.empty() && strchr(chars, s[0])) return s.substr(1); return s; } void ImportFile::parse() { const char *buf = mb.getBufferStart(); const auto *hdr = reinterpret_cast(buf); // Check if the total size is valid. if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData) fatal("broken import library"); // Read names and create an __imp_ symbol. StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); StringRef impName = saver.save("__imp_" + name); const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; dllName = StringRef(nameStart); StringRef extName; switch (hdr->getNameType()) { case IMPORT_ORDINAL: extName = ""; break; case IMPORT_NAME: extName = name; break; case IMPORT_NAME_NOPREFIX: extName = ltrim1(name, "?@_"); break; case IMPORT_NAME_UNDECORATE: extName = ltrim1(name, "?@_"); extName = extName.substr(0, extName.find('@')); break; } this->hdr = hdr; externalName = extName; impSym = symtab->addImportData(impName, this); // If this was a duplicate, we logged an error but may continue; // in this case, impSym is nullptr. if (!impSym) return; if (hdr->getType() == llvm::COFF::IMPORT_CONST) static_cast(symtab->addImportData(name, this)); // If type is function, we need to create a thunk which jump to an // address pointed by the __imp_ symbol. (This allows you to call // DLL functions just like regular non-DLL functions.) if (hdr->getType() == llvm::COFF::IMPORT_CODE) thunkSym = symtab->addImportThunk( name, cast_or_null(impSym), hdr->Machine); } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : InputFile(BitcodeKind, mb) { + uint64_t offsetInArchive, + std::vector &&symbols) + : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) { std::string path = mb.getBufferIdentifier().str(); if (config->thinLTOIndexOnly) path = replaceThinLTOSuffix(mb.getBufferIdentifier()); // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two archives define two members with the same name, this // causes a collision which result in only one of the objects being taken // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. MemoryBufferRef mbref( mb.getBuffer(), saver.save(archiveName + path + (archiveName.empty() ? "" : utostr(offsetInArchive)))); obj = check(lto::InputFile::create(mbref)); } void BitcodeFile::parse() { std::vector> comdat(obj->getComdatTable().size()); for (size_t i = 0; i != obj->getComdatTable().size(); ++i) // FIXME: lto::InputFile doesn't keep enough data to do correct comdat // selection handling. comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i])); for (const lto::InputFile::Symbol &objSym : obj->symbols()) { StringRef symName = saver.save(objSym.getName()); int comdatIndex = objSym.getComdatIndex(); Symbol *sym; if (objSym.isUndefined()) { sym = symtab->addUndefined(symName, this, false); } else if (objSym.isCommon()) { sym = symtab->addCommon(this, symName, objSym.getCommonSize()); } else if (objSym.isWeak() && objSym.isIndirect()) { // Weak external. sym = symtab->addUndefined(symName, this, true); std::string fallback = objSym.getCOFFWeakExternalFallback(); Symbol *alias = symtab->addUndefined(saver.save(fallback)); checkAndSetWeakAlias(symtab, this, sym, alias); } else if (comdatIndex != -1) { if (symName == obj->getComdatTable()[comdatIndex]) sym = comdat[comdatIndex].first; else if (comdat[comdatIndex].second) sym = symtab->addRegular(this, symName); else sym = symtab->addUndefined(symName, this, false); } else { sym = symtab->addRegular(this, symName); } symbols.push_back(sym); if (objSym.isUsed()) config->gcroot.push_back(sym); } directives = obj->getCOFFLinkerOpts(); } MachineTypes BitcodeFile::getMachineType() { switch (Triple(obj->getTargetTriple()).getArch()) { case Triple::x86_64: return AMD64; case Triple::x86: return I386; case Triple::arm: return ARMNT; case Triple::aarch64: return ARM64; default: return IMAGE_FILE_MACHINE_UNKNOWN; } } std::string replaceThinLTOSuffix(StringRef path) { StringRef suffix = config->thinLTOObjectSuffixReplace.first; StringRef repl = config->thinLTOObjectSuffixReplace.second; if (path.consume_back(suffix)) return (path + repl).str(); return path; } + } // namespace coff } // namespace lld - -// Returns the last element of a path, which is supposed to be a filename. -static StringRef getBasename(StringRef path) { - return sys::path::filename(path, sys::path::Style::windows); -} - -// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". -std::string lld::toString(const coff::InputFile *file) { - if (!file) - return ""; - if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) - return file->getName(); - - return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + - ")") - .str(); -} Index: vendor/lld/dist/COFF/InputFiles.h =================================================================== --- vendor/lld/dist/COFF/InputFiles.h (revision 353949) +++ vendor/lld/dist/COFF/InputFiles.h (revision 353950) @@ -1,321 +1,366 @@ //===- InputFiles.h ---------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_INPUT_FILES_H #define LLD_COFF_INPUT_FILES_H #include "Config.h" +#include "lld/Common/DWARF.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" #include #include #include namespace llvm { +struct DILineInfo; namespace pdb { class DbiModuleDescriptorBuilder; } } namespace lld { namespace coff { std::vector getArchiveMembers(llvm::object::Archive *file); using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; using llvm::object::Archive; using llvm::object::COFFObjectFile; using llvm::object::COFFSymbolRef; using llvm::object::coff_import_header; using llvm::object::coff_section; class Chunk; class Defined; class DefinedImportData; class DefinedImportThunk; class DefinedRegular; -class Lazy; class SectionChunk; class Symbol; class Undefined; class TpiSource; // The root class of input files. class InputFile { public: - enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; + enum Kind { + ArchiveKind, + ObjectKind, + LazyObjectKind, + ImportKind, + BitcodeKind + }; Kind kind() const { return fileKind; } virtual ~InputFile() {} // Returns the filename. StringRef getName() const { return mb.getBufferIdentifier(); } // Reads a file (the constructor doesn't do that). virtual void parse() = 0; // Returns the CPU type this file was compiled to. virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; } MemoryBufferRef mb; // An archive file name if this file is created from an archive. StringRef parentName; // Returns .drectve section contents if exist. StringRef getDirectives() { return directives; } protected: InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {} StringRef directives; private: const Kind fileKind; }; // .lib or .a file. class ArchiveFile : public InputFile { public: explicit ArchiveFile(MemoryBufferRef m); static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } void parse() override; // Enqueues an archive member load for the given symbol. If we've already // enqueued a load for the same archive member, this function does nothing, // which ensures that we don't load the same member more than once. - void addMember(const Archive::Symbol *sym); + void addMember(const Archive::Symbol &sym); private: std::unique_ptr file; llvm::DenseSet seen; }; +// .obj or .o file between -start-lib and -end-lib. +class LazyObjFile : public InputFile { +public: + explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {} + static bool classof(const InputFile *f) { + return f->kind() == LazyObjectKind; + } + // Makes this object file part of the link. + void fetch(); + // Adds the symbols in this file to the symbol table as LazyObject symbols. + void parse() override; + +private: + std::vector symbols; +}; + // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + explicit ObjFile(MemoryBufferRef m, std::vector &&symbols) + : InputFile(ObjectKind, m), symbols(std::move(symbols)) {} static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; ArrayRef getChunks() { return chunks; } ArrayRef getDebugChunks() { return debugChunks; } ArrayRef getSXDataChunks() { return sXDataChunks; } ArrayRef getGuardFidChunks() { return guardFidChunks; } ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } ArrayRef getSymbols() { return symbols; } ArrayRef getDebugSection(StringRef secName); // Returns a Symbol object for the symbolIndex'th symbol in the // underlying object file. Symbol *getSymbol(uint32_t symbolIndex) { return symbols[symbolIndex]; } // Returns the underlying COFF file. COFFObjectFile *getCOFFObj() { return coffObj.get(); } // Add a symbol for a range extension thunk. Return the new symbol table // index. This index can be used to modify a relocation. uint32_t addRangeThunkSymbol(Symbol *thunk) { symbols.push_back(thunk); return symbols.size() - 1; } + void includeResourceChunks(); + + bool isResourceObjFile() const { return !resourceChunks.empty(); } + static std::vector instances; // Flags in the absolute @feat.00 symbol if it is present. These usually // indicate if an object was compiled with certain security features enabled // like stack guard, safeseh, /guard:cf, or other things. uint32_t feat00Flags = 0; // True if this object file is compatible with SEH. COFF-specific and // x86-only. COFF spec 5.10.1. The .sxdata section. bool hasSafeSEH() { return feat00Flags & 0x1; } // True if this file was compiled with /guard:cf. bool hasGuardCF() { return feat00Flags & 0x800; } // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like // source files and section contributions are also recorded here. Will be null // if we are not producing a PDB. llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr; const coff_section *addrsigSec = nullptr; // When using Microsoft precompiled headers, this is the PCH's key. // The same key is used by both the precompiled object, and objects using the // precompiled object. Any difference indicates out-of-date objects. llvm::Optional pchSignature; - // Whether this is an object file created from .res files. - bool isResourceObjFile = false; - // Whether this file was compiled with /hotpatch. bool hotPatchable = false; // Whether the object was already merged into the final PDB. bool mergedIntoPDB = false; // If the OBJ has a .debug$T stream, this tells how it will be handled. TpiSource *debugTypesObj = nullptr; // The .debug$T stream if there's one. llvm::Optional debugTypes; + llvm::Optional> + getVariableLocation(StringRef var); + + llvm::Optional getDILineInfo(uint32_t offset, + uint32_t sectionIndex); + private: const coff_section* getSection(uint32_t i); const coff_section *getSection(COFFSymbolRef sym) { return getSection(sym.getSectionNumber()); } void initializeChunks(); void initializeSymbols(); void initializeFlags(); void initializeDependencies(); SectionChunk * readSection(uint32_t sectionNumber, const llvm::object::coff_aux_section_definition *def, StringRef leaderName); void readAssociativeDefinition( COFFSymbolRef coffSym, const llvm::object::coff_aux_section_definition *def); void readAssociativeDefinition( COFFSymbolRef coffSym, const llvm::object::coff_aux_section_definition *def, uint32_t parentSection); void recordPrevailingSymbolForMingw( COFFSymbolRef coffSym, llvm::DenseMap &prevailingSectionMap); void maybeAssociateSEHForMingw( COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def, const llvm::DenseMap &prevailingSectionMap); // Given a new symbol Sym with comdat selection Selection, if the new // symbol is not (yet) Prevailing and the existing comdat leader set to // Leader, emits a diagnostic if the new symbol and its selection doesn't // match the existing symbol and its selection. If either old or new // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace // the existing leader. In that case, Prevailing is set to true. void handleComdatSelection(COFFSymbolRef sym, llvm::COFF::COMDATType &selection, bool &prevailing, DefinedRegular *leader); llvm::Optional createDefined(COFFSymbolRef sym, std::vector &comdatDefs, bool &prevailingComdat); Symbol *createRegular(COFFSymbolRef sym); Symbol *createUndefined(COFFSymbolRef sym); std::unique_ptr coffObj; // List of all chunks defined by this file. This includes both section // chunks and non-section chunks for common symbols. std::vector chunks; + std::vector resourceChunks; + // CodeView debug info sections. std::vector debugChunks; // Chunks containing symbol table indices of exception handlers. Only used for // 32-bit x86. std::vector sXDataChunks; // Chunks containing symbol table indices of address taken symbols and longjmp // targets. These are not linked into the final binary when /guard:cf is set. std::vector guardFidChunks; std::vector guardLJmpChunks; // This vector contains the same chunks as Chunks, but they are // indexed such that you can get a SectionChunk by section index. // Nonexistent section indices are filled with null pointers. // (Because section number is 1-based, the first slot is always a // null pointer.) std::vector sparseChunks; // This vector contains a list of all symbols defined or referenced by this // file. They are indexed such that you can get a Symbol by symbol // index. Nonexistent indices (which are occupied by auxiliary // symbols in the real symbol table) are filled with null pointers. std::vector symbols; + + DWARFCache *dwarf = nullptr; }; // This type represents import library members that contain DLL names // and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7 // for details about the format. class ImportFile : public InputFile { public: explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {} static bool classof(const InputFile *f) { return f->kind() == ImportKind; } static std::vector instances; Symbol *impSym = nullptr; Symbol *thunkSym = nullptr; std::string dllName; private: void parse() override; public: StringRef externalName; const coff_import_header *hdr; Chunk *location = nullptr; // We want to eliminate dllimported symbols if no one actually refers them. // These "Live" bits are used to keep track of which import library members // are actually in use. // // If the Live bit is turned off by MarkLive, Writer will ignore dllimported // symbols provided by this import library member. We also track whether the // imported symbol is used separately from whether the thunk is used in order // to avoid creating unnecessary thunks. bool live = !config->doGC; bool thunkLive = !config->doGC; }; // Used for LTO. class BitcodeFile : public InputFile { public: BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); + uint64_t offsetInArchive) + : BitcodeFile(mb, archiveName, offsetInArchive, {}) {} + explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive, + std::vector &&symbols); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; static std::vector instances; std::unique_ptr obj; private: void parse() override; std::vector symbols; }; + +inline bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; +} std::string replaceThinLTOSuffix(StringRef path); } // namespace coff std::string toString(const coff::InputFile *file); } // namespace lld #endif Index: vendor/lld/dist/COFF/LTO.cpp =================================================================== --- vendor/lld/dist/COFF/LTO.cpp (revision 353949) +++ vendor/lld/dist/COFF/LTO.cpp (revision 353950) @@ -1,206 +1,211 @@ //===- LTO.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "LTO.h" #include "Config.h" #include "InputFiles.h" #include "Symbols.h" #include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/LTO/Caching.h" #include "llvm/LTO/Config.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; using namespace llvm::object; -using namespace lld; -using namespace lld::coff; +namespace lld { +namespace coff { // Creates an empty file to and returns a raw_fd_ostream to write to it. static std::unique_ptr openFile(StringRef file) { std::error_code ec; auto ret = - llvm::make_unique(file, ec, sys::fs::OpenFlags::F_None); + std::make_unique(file, ec, sys::fs::OpenFlags::OF_None); if (ec) { error("cannot open " + file + ": " + ec.message()); return nullptr; } return ret; } static std::string getThinLTOOutputFile(StringRef path) { return lto::getThinLTOOutputFile(path, config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second); } static lto::Config createConfig() { lto::Config c; c.Options = initTargetOptionsFromCodeGenFlags(); // Always emit a section per function/datum with LTO. LLVM LTO should get most // of the benefit of linker GC, but there are still opportunities for ICF. c.Options.FunctionSections = true; c.Options.DataSections = true; // Use static reloc model on 32-bit x86 because it usually results in more // compact code, and because there are also known code generation bugs when // using the PIC model (see PR34306). if (config->machine == COFF::IMAGE_FILE_MACHINE_I386) c.RelocModel = Reloc::Static; else c.RelocModel = Reloc::PIC_; c.DisableVerify = true; c.DiagHandler = diagnosticHandler; c.OptLevel = config->ltoo; c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); c.CGOptLevel = args::getCGOptLevel(config->ltoo); if (config->saveTemps) checkError(c.addSaveTemps(std::string(config->outputFile) + ".", /*UseInputModulePath*/ true)); return c; } BitcodeCompiler::BitcodeCompiler() { // Initialize indexFile. if (!config->thinLTOIndexOnlyArg.empty()) indexFile = openFile(config->thinLTOIndexOnlyArg); // Initialize ltoObj. lto::ThinBackend backend; if (config->thinLTOIndexOnly) { auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; backend = lto::createWriteIndexesThinBackend( config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second, config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); } else if (config->thinLTOJobs != 0) { backend = lto::createInProcessThinBackend(config->thinLTOJobs); } - ltoObj = llvm::make_unique(createConfig(), backend, + ltoObj = std::make_unique(createConfig(), backend, config->ltoPartitions); } BitcodeCompiler::~BitcodeCompiler() = default; static void undefine(Symbol *s) { replaceSymbol(s, s->getName()); } void BitcodeCompiler::add(BitcodeFile &f) { lto::InputFile &obj = *f.obj; unsigned symNum = 0; std::vector symBodies = f.getSymbols(); std::vector resols(symBodies.size()); if (config->thinLTOIndexOnly) thinIndices.insert(obj.getName()); // Provide a resolution to the LTO API for each symbol. for (const lto::InputFile::Symbol &objSym : obj.symbols()) { Symbol *sym = symBodies[symNum]; lto::SymbolResolution &r = resols[symNum]; ++symNum; // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile // reports two symbols for module ASM defined. Without this check, lld // flags an undefined in IR with a definition in ASM as prevailing. // Once IRObjectFile is fixed to report only one symbol this hack can // be removed. r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; r.VisibleToRegularObj = sym->isUsedInRegularObj; if (r.Prevailing) undefine(sym); } checkError(ltoObj->add(std::move(f.obj), resols)); } // Merge all the bitcode files we have seen, codegen the result // and return the resulting objects. std::vector BitcodeCompiler::compile() { unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); // The /lldltocache option specifies the path to a directory in which to cache // native object files for ThinLTO incremental builds. If a path was // specified, configure LTO to use it as the cache directory. lto::NativeObjectCache cache; if (!config->ltoCache.empty()) cache = check(lto::localCache( config->ltoCache, [&](size_t task, std::unique_ptr mb) { files[task] = std::move(mb); })); checkError(ltoObj->run( [&](size_t task) { - return llvm::make_unique( - llvm::make_unique(buf[task])); + return std::make_unique( + std::make_unique(buf[task])); }, cache)); // Emit empty index files for non-indexed files for (StringRef s : thinIndices) { std::string path = getThinLTOOutputFile(s); openFile(path + ".thinlto.bc"); if (config->thinLTOEmitImportsFiles) openFile(path + ".imports"); } // ThinLTO with index only option is required to generate only the index // files. After that, we exit from linker and ThinLTO backend runs in a // distributed environment. if (config->thinLTOIndexOnly) { + if (!config->ltoObjPath.empty()) + saveBuffer(buf[0], config->ltoObjPath); if (indexFile) indexFile->close(); return {}; } if (!config->ltoCache.empty()) pruneCache(config->ltoCache, config->ltoCachePolicy); std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) { if (buf[i].empty()) continue; if (config->saveTemps) { if (i == 0) saveBuffer(buf[i], config->outputFile + ".lto.obj"); else saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj"); } ret.emplace_back(buf[i].data(), buf[i].size()); } for (std::unique_ptr &file : files) if (file) ret.push_back(file->getBuffer()); return ret; } + +} // namespace coff +} // namespace lld Index: vendor/lld/dist/COFF/MapFile.cpp =================================================================== --- vendor/lld/dist/COFF/MapFile.cpp (revision 353949) +++ vendor/lld/dist/COFF/MapFile.cpp (revision 353950) @@ -1,124 +1,127 @@ //===- MapFile.cpp --------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the /lldmap option. It shows lists in order and // hierarchically the output sections, input sections, input files and // symbol: // // Address Size Align Out File Symbol // 00201000 00000015 4 .text // 00201000 0000000e 4 test.o:(.text) // 0020100e 00000000 0 local // 00201005 00000000 0 f(int) // //===----------------------------------------------------------------------===// #include "MapFile.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Threads.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; -using namespace lld; -using namespace lld::coff; +namespace lld { +namespace coff { using SymbolMapTy = DenseMap>; -static const std::string indent8 = " "; // 8 spaces -static const std::string indent16 = " "; // 16 spaces +static constexpr char indent8[] = " "; // 8 spaces +static constexpr char indent16[] = " "; // 16 spaces // Print out the first three columns of a line. static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, uint64_t align) { os << format("%08llx %08llx %5lld ", addr, size, align); } // Returns a list of all symbols that we want to print out. static std::vector getSymbols() { std::vector v; for (ObjFile *file : ObjFile::instances) for (Symbol *b : file->getSymbols()) if (auto *sym = dyn_cast_or_null(b)) if (sym && !sym->getCOFFSymbol().isSectionDefinition()) v.push_back(sym); return v; } // Returns a map from sections to their symbols. static SymbolMapTy getSectionSyms(ArrayRef syms) { SymbolMapTy ret; for (DefinedRegular *s : syms) ret[s->getChunk()].push_back(s); // Sort symbols by address. for (auto &it : ret) { SmallVectorImpl &v = it.second; std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) { return a->getRVA() < b->getRVA(); }); } return ret; } // Construct a map from symbols to their stringified representations. static DenseMap getSymbolStrings(ArrayRef syms) { std::vector str(syms.size()); parallelForEachN((size_t)0, syms.size(), [&](size_t i) { raw_string_ostream os(str[i]); writeHeader(os, syms[i]->getRVA(), 0, 0); os << indent16 << toString(*syms[i]); }); DenseMap ret; for (size_t i = 0, e = syms.size(); i < e; ++i) ret[syms[i]] = std::move(str[i]); return ret; } -void coff::writeMapFile(ArrayRef outputSections) { +void writeMapFile(ArrayRef outputSections) { if (config->mapFile.empty()) return; std::error_code ec; - raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None); + raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); if (ec) fatal("cannot open " + config->mapFile + ": " + ec.message()); // Collect symbol info that we want to print out. std::vector syms = getSymbols(); SymbolMapTy sectionSyms = getSectionSyms(syms); DenseMap symStr = getSymbolStrings(syms); // Print out the header line. os << "Address Size Align Out In Symbol\n"; // Print out file contents. for (OutputSection *sec : outputSections) { writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); os << sec->name << '\n'; for (Chunk *c : sec->chunks) { auto *sc = dyn_cast(c); if (!sc) continue; writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment()); os << indent8 << sc->file->getName() << ":(" << sc->getSectionName() << ")\n"; for (DefinedRegular *sym : sectionSyms[sc]) os << symStr[sym] << '\n'; } } } + +} // namespace coff +} // namespace lld Index: vendor/lld/dist/COFF/MinGW.cpp =================================================================== --- vendor/lld/dist/COFF/MinGW.cpp (revision 353949) +++ vendor/lld/dist/COFF/MinGW.cpp (revision 353950) @@ -1,166 +1,170 @@ //===- MinGW.cpp ----------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MinGW.h" #include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" -using namespace lld; -using namespace lld::coff; using namespace llvm; using namespace llvm::COFF; +namespace lld { +namespace coff { + AutoExporter::AutoExporter() { excludeLibs = { "libgcc", "libgcc_s", "libstdc++", "libmingw32", "libmingwex", "libg2c", "libsupc++", "libobjc", "libgcj", "libclang_rt.builtins", "libclang_rt.builtins-aarch64", "libclang_rt.builtins-arm", "libclang_rt.builtins-i386", "libclang_rt.builtins-x86_64", "libc++", "libc++abi", "libunwind", "libmsvcrt", "libucrtbase", }; excludeObjects = { "crt0.o", "crt1.o", "crt1u.o", "crt2.o", "crt2u.o", "dllcrt1.o", "dllcrt2.o", "gcrt0.o", "gcrt1.o", "gcrt2.o", "crtbegin.o", "crtend.o", }; excludeSymbolPrefixes = { // Import symbols "__imp_", "__IMPORT_DESCRIPTOR_", // Extra import symbols from GNU import libraries "__nm_", // C++ symbols "__rtti_", "__builtin_", - // Artifical symbols such as .refptr + // Artificial symbols such as .refptr ".", }; excludeSymbolSuffixes = { "_iname", "_NULL_THUNK_DATA", }; if (config->machine == I386) { excludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", "__pei386_runtime_relocator", "_do_pseudo_reloc", "_impure_ptr", "__impure_ptr", "__fmode", "_environ", "___dso_handle", // These are the MinGW names that differ from the standard // ones (lacking an extra underscore). "_DllMain@12", "_DllEntryPoint@12", "_DllMainCRTStartup@12", }; excludeSymbolPrefixes.insert("__head_"); } else { excludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", "_pei386_runtime_relocator", "do_pseudo_reloc", "impure_ptr", "_impure_ptr", "_fmode", "environ", "__dso_handle", // These are the MinGW names that differ from the standard // ones (lacking an extra underscore). "DllMain", "DllEntryPoint", "DllMainCRTStartup", }; excludeSymbolPrefixes.insert("_head_"); } } void AutoExporter::addWholeArchive(StringRef path) { StringRef libName = sys::path::filename(path); // Drop the file extension, to match the processing below. libName = libName.substr(0, libName.rfind('.')); excludeLibs.erase(libName); } bool AutoExporter::shouldExport(Defined *sym) const { if (!sym || !sym->isLive() || !sym->getChunk()) return false; // Only allow the symbol kinds that make sense to export; in particular, // disallow import symbols. if (!isa(sym) && !isa(sym)) return false; if (excludeSymbols.count(sym->getName())) return false; for (StringRef prefix : excludeSymbolPrefixes.keys()) if (sym->getName().startswith(prefix)) return false; for (StringRef suffix : excludeSymbolSuffixes.keys()) if (sym->getName().endswith(suffix)) return false; // If a corresponding __imp_ symbol exists and is defined, don't export it. if (symtab->find(("__imp_" + sym->getName()).str())) return false; // Check that file is non-null before dereferencing it, symbols not // originating in regular object files probably shouldn't be exported. if (!sym->getFile()) return false; StringRef libName = sys::path::filename(sym->getFile()->parentName); // Drop the file extension. libName = libName.substr(0, libName.rfind('.')); if (!libName.empty()) return !excludeLibs.count(libName); StringRef fileName = sys::path::filename(sym->getFile()->getName()); return !excludeObjects.count(fileName); } -void coff::writeDefFile(StringRef name) { +void writeDefFile(StringRef name) { std::error_code ec; - raw_fd_ostream os(name, ec, sys::fs::F_None); + raw_fd_ostream os(name, ec, sys::fs::OF_None); if (ec) fatal("cannot open " + name + ": " + ec.message()); os << "EXPORTS\n"; for (Export &e : config->exports) { os << " " << e.exportName << " " << "@" << e.ordinal; if (auto *def = dyn_cast_or_null(e.sym)) { if (def && def->getChunk() && !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) os << " DATA"; } os << "\n"; } } + +} // namespace coff +} // namespace lld Index: vendor/lld/dist/COFF/Options.td =================================================================== --- vendor/lld/dist/COFF/Options.td (revision 353949) +++ vendor/lld/dist/COFF/Options.td (revision 353950) @@ -1,225 +1,242 @@ include "llvm/Option/OptParser.td" // link.exe accepts options starting with either a dash or a slash. // Flag that takes no arguments. class F : Flag<["/", "-", "/?", "-?"], name>; // Flag that takes one argument after ":". class P : Joined<["/", "-", "/?", "-?"], name#":">, HelpText; // Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the // flag on and using it suffixed by ":no" turns it off. multiclass B { def "" : F, HelpText; def _no : F, HelpText; } def align : P<"align", "Section alignment">; def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; def base : P<"base", "Base address of the program">; def color_diagnostics: Flag<["--"], "color-diagnostics">, - HelpText<"Use colors in diagnostics">; + HelpText<"Use colors in diagnostics">; def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, - HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; + HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; def entry : P<"entry", "Name of entry point symbol">; def errorlimit : P<"errorlimit", "Maximum number of errors to emit before stopping (0 = no limit)">; def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; def filealign : P<"filealign", "Section alignment in the output file">; def functionpadmin : F<"functionpadmin">; -def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">; +def functionpadmin_opt : P<"functionpadmin", + "Prepares an image for hotpatching">; def guard : P<"guard", "Control flow guard">; def heap : P<"heap", "Size of the heap">; def ignore : P<"ignore", "Specify warning codes to ignore">; def implib : P<"implib", "Import library name">; def lib : F<"lib">, HelpText<"Act like lib.exe; must be first argument if present">; def libpath : P<"libpath", "Additional library search path">; -def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; -def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">; -def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">; +def linkrepro : P<"linkrepro", + "Dump linker invocation and input files for debugging">; +def lldignoreenv : F<"lldignoreenv">, + HelpText<"Ignore environment variables like %LIB%">; +def lldltocache : P<"lldltocache", + "Path to ThinLTO cached object file directory">; +def lldltocachepolicy : P<"lldltocachepolicy", + "Pruning policy for the ThinLTO cache">; def lldsavetemps : F<"lldsavetemps">, HelpText<"Save temporary files instead of deleting them">; def machine : P<"machine", "Specify target platform">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; def nodefaultlib : P<"nodefaultlib", "Remove a default library">; def opt : P<"opt", "Control optimizations">; def order : P<"order", "Put functions in order">; def out : P<"out", "Path to file to write output">; def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; def no_color_diagnostics: F<"no-color-diagnostics">, - HelpText<"Do not use colors in diagnostics">; + HelpText<"Do not use colors in diagnostics">; def pdb : P<"pdb", "PDB file path">; def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; def section : P<"section", "Specify section attributes">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; def timestamp : P<"timestamp", "Specify the PE header timestamp">; def version : P<"version", "Specify a version number in the PE header">; -def wholearchive_file : P<"wholearchive", "Include all object files from this archive">; +def wholearchive_file : P<"wholearchive", + "Include all object files from this library">; def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, Alias; def manifest : F<"manifest">, HelpText<"Create .manifest file">; def manifest_colon : P< "manifest", "NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">; def manifestuac : P<"manifestuac", "User access control">; def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">; def manifestdependency : P< "manifestdependency", "Attributes for element in manifest file; implies /manifest">; def manifestinput : P< "manifestinput", "Additional manifest inputs; only valid with /manifest:embed">; // We cannot use multiclass P because class name "incl" is different // from its command line option name. We do this because "include" is // a reserved keyword in tablegen. def incl : Joined<["/", "-", "/?", "-?"], "include:">, HelpText<"Force symbol to be added to symbol table as undefined one">; // "def" is also a keyword. def deffile : Joined<["/", "-", "/?", "-?"], "def:">, HelpText<"Use module-definition file">; def debug : F<"debug">, HelpText<"Embed a symbol table in the image">; def debug_opt : P<"debug", "Embed a symbol table in the image with option">; def debugtype : P<"debugtype", "Debug Info Options">; def dll : F<"dll">, HelpText<"Create a DLL">; def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">; def nodefaultlib_all : F<"nodefaultlib">, HelpText<"Remove all default libraries">; def noentry : F<"noentry">, HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">; def profile : F<"profile">; def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; +def reproduce : P<"reproduce", + "Dump linker invocation and input files for debugging">; def swaprun : P<"swaprun", "Comma-separated list of 'cd' or 'net'">; def swaprun_cd : F<"swaprun:cd">, Alias, AliasArgs<["cd"]>, HelpText<"Make loader run output binary from swap instead of from CD">; def swaprun_net : F<"swaprun:net">, Alias, AliasArgs<["net"]>, HelpText<"Make loader run output binary from swap instead of from network">; def verbose : F<"verbose">; -def wholearchive_flag : F<"wholearchive">; +def wholearchive_flag : F<"wholearchive">, + HelpText<"Include all object files from all libraries">; def force : F<"force">, - HelpText<"Allow undefined and multiply defined symbols when creating executables">; + HelpText<"Allow undefined and multiply defined symbols">; def force_unresolved : F<"force:unresolved">, HelpText<"Allow undefined symbols when creating executables">; def force_multiple : F<"force:multiple">, HelpText<"Allow multiply defined symbols when creating executables">; def force_multipleres : F<"force:multipleres">, HelpText<"Allow multiply defined resources when creating executables">; defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">; defm allowbind : B<"allowbind", "Enable DLL binding (default)", "Disable DLL binding">; defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)", "Disable DLL isolation">; defm appcontainer : B<"appcontainer", "Image can only be run in an app container", "Image can run outside an app container (default)">; defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)", "Disable ASLR (default when /fixed)">; defm fixed : B<"fixed", "Disable base relocations", "Enable base relocations (default)">; defm highentropyva : B<"highentropyva", "Enable 64-bit ASLR (default on 64-bit)", "Disable 64-bit ASLR">; defm incremental : B<"incremental", "Keep original import library if contents are unchanged", "Overwrite import library even if contents are unchanged">; defm integritycheck : B<"integritycheck", "Set FORCE_INTEGRITY bit in PE header", "No effect (default)">; defm largeaddressaware : B<"largeaddressaware", "Enable large addresses (default on 64-bit)", "Disable large addresses (default on 32-bit)">; defm nxcompat : B<"nxcompat", "Enable data execution prevention (default)", "Disable data execution provention">; defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler (only for x86)", "Don't produce an image with Safe Exception Handler">; defm tsaware : B<"tsaware", "Create Terminal Server aware executable (default)", "Create non-Terminal Server aware executable">; def help : F<"help">; // /?? and -?? must be before /? and -? to not confuse lib/Options. def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; // LLD extensions +def end_lib : F<"end-lib">, + HelpText<"Ends group of objects treated as if they were in a library">; def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; defm demangle : B<"demangle", "Demangle symbols in output (default)", "Do not demangle symbols in output">; def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">, HelpText<"Add symbol as undefined, but allow it to remain undefined">; def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def pdb_source_path : P<"pdbsourcepath", - "Base path used to make relative source file path absolute in PDB">; + "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def start_lib : F<"start-lib">, + HelpText<"Starts group of objects treated as if they were in a library">; def thinlto_emit_imports_files : F<"thinlto-emit-imports-files">, HelpText<"Emit .imports files with -thinlto-index-only">; def thinlto_index_only : F<"thinlto-index-only">, HelpText<"Instead of linking, emit ThinLTO index files">; def thinlto_index_only_arg : P< "thinlto-index-only", "-thinlto-index-only and also write native module names to file">; def thinlto_object_suffix_replace : P< "thinlto-object-suffix-replace", "'old;new' replace old suffix with new suffix in ThinLTO index">; def thinlto_prefix_replace: P< "thinlto-prefix-replace", "'old;new' replace old prefix with new prefix in ThinLTO outputs">; +def lto_obj_path : P< + "lto-obj-path", + "output native object for merged LTO unit to this path">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; defm threads: B<"threads", "Run the linker multi-threaded (default)", "Do not run the linker multi-threaded">; // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">; def show_timing : F<"time">; def summary : F<"summary">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. //============================================================================== class QF : Joined<["/", "-", "/?", "-?"], name#":">; def ignoreidl : F<"ignoreidl">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; def editandcontinue : F<"editandcontinue">; def fastfail : F<"fastfail">; def delay : QF<"delay">; def errorreport : QF<"errorreport">; def idlout : QF<"idlout">; def maxilksize : QF<"maxilksize">; def tlbid : QF<"tlbid">; def tlbout : QF<"tlbout">; def verbose_all : QF<"verbose">; def guardsym : QF<"guardsym">; Index: vendor/lld/dist/COFF/PDB.cpp =================================================================== --- vendor/lld/dist/COFF/PDB.cpp (revision 353949) +++ vendor/lld/dist/COFF/PDB.cpp (revision 353950) @@ -1,1836 +1,1831 @@ //===- PDB.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "PDB.h" #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" #include "Driver.h" #include "SymbolTable.h" #include "Symbols.h" #include "TypeMerger.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Timer.h" #include "lld/Common/Threads.h" #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" #include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/Object/COFF.h" #include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/CRC.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include -using namespace lld; -using namespace lld::coff; using namespace llvm; using namespace llvm::codeview; using llvm::object::coff_section; +namespace lld { +namespace coff { + static ExitOnError exitOnErr; static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer); static Timer typeMergingTimer("Type Merging", addObjectsTimer); static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer); static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer); static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer); static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer); namespace { class DebugSHandler; class PDBLinker { friend DebugSHandler; public: PDBLinker(SymbolTable *symtab) : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. pdbStrTab.insert(""); } /// Emit the basic PDB structure: initial streams, headers, etc. void initialize(llvm::codeview::DebugInfo *buildId); /// Add natvis files specified on the command line. void addNatvisFiles(); /// Link CodeView from each object file in the symbol table into the PDB. void addObjectsToPDB(); /// Link info for each import file in the symbol table into the PDB. void addImportFilesToPDB(ArrayRef outputSections); /// Link CodeView from a single object file into the target (output) PDB. /// When a precompiled headers object is linked, its TPI map might be provided /// externally. void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr); /// Produce a mapping from the type and item indices used in the object /// file to those in the destination PDB. /// /// If the object file uses a type server PDB (compiled with /Zi), merge TPI /// and IPI from the type server PDB and return a map for it. Each unique type /// server PDB is merged at most once, so this may return an existing index /// mapping. /// /// If the object does not use a type server PDB (compiled with /Z7), we merge /// all the type and item records from the .debug$S stream and fill in the /// caller-provided objectIndexMap. Expected mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap); /// Reads and makes available a PDB. Expected maybeMergeTypeServerPDB(ObjFile *file); /// Merges a precompiled headers TPI map into the current TPI map. The /// precompiled headers object will also be loaded and remapped in the /// process. Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap); /// Reads and makes available a precompiled headers object. /// /// This is a requirement for objects compiled with cl.exe /Yu. In that /// case, the referenced object (which was compiled with /Yc) has to be loaded /// first. This is mainly because the current object's TPI stream has external /// references to the precompiled headers object. /// /// If the precompiled headers object was already loaded, this function will /// simply return its (remapped) TPI map. Expected aquirePrecompObj(ObjFile *file); /// Adds a precompiled headers object signature -> TPI mapping. std::pair registerPrecompiledHeaders(uint32_t signature); void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, std::vector &stringTableRefs, BinaryStreamRef symData); /// Add the section map and section contributions to the PDB. void addSections(ArrayRef outputSections, ArrayRef sectionTable); /// Write the PDB to disk and store the Guid generated for it in *Guid. void commit(codeview::GUID *guid); // Print statistics regarding the final PDB void printStats(); private: BumpPtrAllocator alloc; SymbolTable *symtab; pdb::PDBFileBuilder builder; TypeMerger tMerger; /// PDBs use a single global string table for filenames in the file checksum /// table. DebugStringTableSubsection pdbStrTab; llvm::SmallString<128> nativePath; std::vector sectionMap; /// Type index mappings of type server PDBs that we've loaded so far. std::map typeServerIndexMappings; /// Type index mappings of precompiled objects type map that we've loaded so /// far. std::map precompTypeIndexMappings; // For statistics uint64_t globalSymbols = 0; uint64_t moduleSymbols = 0; uint64_t publicSymbols = 0; }; class DebugSHandler { PDBLinker &linker; /// The object file whose .debug$S sections we're processing. ObjFile &file; /// The result of merging type indices. const CVIndexMap &indexMap; /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by /// index from other records in the .debug$S section. All of these strings /// need to be added to the global PDB string table, and all references to /// these strings need to have their indices re-written to refer to the /// global PDB string table. DebugStringTableSubsectionRef cVStrTab; /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to /// by other records in the .debug$S section and need to be merged into the /// PDB. DebugChecksumsSubsectionRef checksums; /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per /// object file. DebugInlineeLinesSubsectionRef inlineeLines; /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of /// these and they need not appear in any specific order. However, they /// contain string table references which need to be re-written, so we /// collect them all here and re-write them after all subsections have been /// discovered and processed. std::vector newFpoFrames; /// Pointers to raw memory that we determine have string table references /// that need to be re-written. We first process all .debug$S subsections /// to ensure that we can handle subsections written in any order, building /// up this list as we go. At the end, we use the string table (which must /// have been discovered by now else it is an error) to re-write these /// references. std::vector stringTableReferences; public: DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap) : linker(linker), file(file), indexMap(indexMap) {} void handleDebugS(lld::coff::SectionChunk &debugS); std::shared_ptr mergeInlineeLines(DebugChecksumsSubsection *newChecksums); void finish(); }; } // Visual Studio's debugger requires absolute paths in various places in the // PDB to work without additional configuration: // https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box static void pdbMakeAbsolute(SmallVectorImpl &fileName) { // The default behavior is to produce paths that are valid within the context // of the machine that you perform the link on. If the linker is running on // a POSIX system, we will output absolute POSIX paths. If the linker is // running on a Windows system, we will output absolute Windows paths. If the // user desires any other kind of behavior, they should explicitly pass // /pdbsourcepath, in which case we will treat the exact string the user // passed in as the gospel and not normalize, canonicalize it. if (sys::path::is_absolute(fileName, sys::path::Style::windows) || sys::path::is_absolute(fileName, sys::path::Style::posix)) return; // It's not absolute in any path syntax. Relative paths necessarily refer to // the local file system, so we can make it native without ending up with a // nonsensical path. if (config->pdbSourcePath.empty()) { sys::path::native(fileName); sys::fs::make_absolute(fileName); return; } // Try to guess whether /PDBSOURCEPATH is a unix path or a windows path. // Since PDB's are more of a Windows thing, we make this conservative and only // decide that it's a unix path if we're fairly certain. Specifically, if // it starts with a forward slash. SmallString<128> absoluteFileName = config->pdbSourcePath; sys::path::Style guessedStyle = absoluteFileName.startswith("/") ? sys::path::Style::posix : sys::path::Style::windows; sys::path::append(absoluteFileName, guessedStyle, fileName); sys::path::native(absoluteFileName, guessedStyle); sys::path::remove_dots(absoluteFileName, true, guessedStyle); fileName = std::move(absoluteFileName); } // A COFF .debug$H section is currently a clang extension. This function checks // if a .debug$H section is in a format that we expect / understand, so that we // can ignore any sections which are coincidentally also named .debug$H but do // not contain a format we recognize. static bool canUseDebugH(ArrayRef debugH) { if (debugH.size() < sizeof(object::debug_h_header)) return false; auto *header = reinterpret_cast(debugH.data()); debugH = debugH.drop_front(sizeof(object::debug_h_header)); return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && header->Version == 0 && header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && (debugH.size() % 8 == 0); } static Optional> getDebugH(ObjFile *file) { SectionChunk *sec = SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); if (!sec) return llvm::None; ArrayRef contents = sec->getContents(); if (!canUseDebugH(contents)) return None; return contents; } static ArrayRef getHashesFromDebugH(ArrayRef debugH) { assert(canUseDebugH(debugH)); debugH = debugH.drop_front(sizeof(object::debug_h_header)); uint32_t count = debugH.size() / sizeof(GloballyHashedType); return {reinterpret_cast(debugH.data()), count}; } static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, TypeCollection &typeTable) { // Start the TPI or IPI stream header. tpiBuilder.setVersionHeader(pdb::PdbTpiV80); // Flatten the in memory type table and hash each type. typeTable.ForEachRecord([&](TypeIndex ti, const CVType &type) { auto hash = pdb::hashTypeRecord(type); if (auto e = hash.takeError()) fatal("type hashing error"); tpiBuilder.addTypeRecord(type.RecordData, *hash); }); } Expected PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { ScopedTimer t(typeMergingTimer); if (!file->debugTypesObj) return *objectIndexMap; // no Types stream // Precompiled headers objects need to save the index map for further // reference by other objects which use the precompiled headers. if (file->debugTypesObj->kind == TpiSource::PCH) { uint32_t pchSignature = file->pchSignature.getValueOr(0); if (pchSignature == 0) fatal("No signature found for the precompiled headers OBJ (" + file->getName() + ")"); // When a precompiled headers object comes first on the command-line, we // update the mapping here. Otherwise, if an object referencing the // precompiled headers object comes first, the mapping is created in // aquirePrecompObj(), thus we would skip this block. if (!objectIndexMap->isPrecompiledTypeMap) { auto r = registerPrecompiledHeaders(pchSignature); if (r.second) fatal( "A precompiled headers OBJ with the same signature was already " "provided! (" + file->getName() + ")"); objectIndexMap = &r.first; } } if (file->debugTypesObj->kind == TpiSource::UsingPDB) { // Look through type servers. If we've already seen this type server, // don't merge any type information. return maybeMergeTypeServerPDB(file); } CVTypeArray &types = *file->debugTypes; if (file->debugTypesObj->kind == TpiSource::UsingPCH) { // This object was compiled with /Yu, so process the corresponding // precompiled headers object (/Yc) first. Some type indices in the current // object are referencing data in the precompiled headers object, so we need // both to be loaded. Error e = mergeInPrecompHeaderObj(file, objectIndexMap); if (e) return std::move(e); // Drop LF_PRECOMP record from the input stream, as it has been replaced // with the precompiled headers Type stream in the mergeInPrecompHeaderObj() // call above. Note that we can't just call Types.drop_front(), as we // explicitly want to rebase the stream. CVTypeArray::Iterator firstType = types.begin(); types.setUnderlyingStream( types.getUnderlyingStream().drop_front(firstType->RecordData.size())); } // Fill in the temporary, caller-provided ObjectIndexMap. if (config->debugGHashes) { ArrayRef hashes; std::vector ownedHashes; if (Optional> debugH = getDebugH(file)) hashes = getHashesFromDebugH(*debugH); else { ownedHashes = GloballyHashedType::hashTypes(types); hashes = ownedHashes; } if (auto err = mergeTypeAndIdRecords( tMerger.globalIDTable, tMerger.globalTypeTable, objectIndexMap->tpiMap, types, hashes, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } else { if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable, objectIndexMap->tpiMap, types, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } return *objectIndexMap; } Expected PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) { Expected pdbSession = findTypeServerSource(file); if (!pdbSession) return pdbSession.takeError(); pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile(); pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap()); CVIndexMap &indexMap = it.first->second; if (!it.second) return indexMap; // already merged // Mark this map as a type server map. indexMap.isTypeServerMap = true; Expected expectedTpi = pdbFile.getPDBTpiStream(); if (auto e = expectedTpi.takeError()) fatal("Type server does not have TPI stream: " + toString(std::move(e))); pdb::TpiStream *maybeIpi = nullptr; if (pdbFile.hasPDBIpiStream()) { Expected expectedIpi = pdbFile.getPDBIpiStream(); if (auto e = expectedIpi.takeError()) fatal("Error getting type server IPI stream: " + toString(std::move(e))); maybeIpi = &*expectedIpi; } if (config->debugGHashes) { // PDBs do not actually store global hashes, so when merging a type server // PDB we have to synthesize global hashes. To do this, we first synthesize // global hashes for the TPI stream, since it is independent, then we // synthesize hashes for the IPI stream, using the hashes for the TPI stream // as inputs. auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); Optional endPrecomp; // Merge TPI first, because the IPI stream will reference type indices. if (auto err = mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap, expectedTpi->typeArray(), tpiHashes, endPrecomp)) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); // Merge IPI. if (maybeIpi) { auto ipiHashes = GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); if (auto err = mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap, indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes)) fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); } } else { // Merge TPI first, because the IPI stream will reference type indices. if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap, expectedTpi->typeArray())) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); // Merge IPI. if (maybeIpi) { if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap, indexMap.ipiMap, maybeIpi->typeArray())) fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); } } return indexMap; } Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap) { const PrecompRecord &precomp = retrieveDependencyInfo(file->debugTypesObj); Expected e = aquirePrecompObj(file); if (!e) return e.takeError(); const CVIndexMap &precompIndexMap = *e; assert(precompIndexMap.isPrecompiledTypeMap); if (precompIndexMap.tpiMap.empty()) return Error::success(); assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size()); // Use the previously remapped index map from the precompiled headers. objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(), precompIndexMap.tpiMap.begin() + precomp.getTypesCount()); return Error::success(); } static bool equals_path(StringRef path1, StringRef path2) { #if defined(_WIN32) return path1.equals_lower(path2); #else return path1.equals(path2); #endif } - // Find by name an OBJ provided on the command line -static ObjFile *findObjByName(StringRef fileNameOnly) { - SmallString<128> currentPath; - +static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly, + uint32_t precompSignature) { for (ObjFile *f : ObjFile::instances) { StringRef currentFileName = sys::path::filename(f->getName()); - // Compare based solely on the file name (link.exe behavior) - if (equals_path(currentFileName, fileNameOnly)) + if (f->pchSignature.hasValue() && + f->pchSignature.getValue() == precompSignature && + equals_path(fileNameOnly, currentFileName)) return f; } return nullptr; } std::pair PDBLinker::registerPrecompiledHeaders(uint32_t signature) { auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()}); CVIndexMap &indexMap = insertion.first->second; if (!insertion.second) return {indexMap, true}; // Mark this map as a precompiled types map. indexMap.isPrecompiledTypeMap = true; return {indexMap, false}; } Expected PDBLinker::aquirePrecompObj(ObjFile *file) { const PrecompRecord &precomp = retrieveDependencyInfo(file->debugTypesObj); // First, check if we already loaded the precompiled headers object with this // signature. Return the type index mapping if we've already seen it. auto r = registerPrecompiledHeaders(precomp.getSignature()); if (r.second) return r.first; CVIndexMap &indexMap = r.first; // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, // the paths embedded in the OBJs are in the Windows format. SmallString<128> precompFileName = sys::path::filename( precomp.getPrecompFilePath(), sys::path::Style::windows); // link.exe requires that a precompiled headers object must always be provided // on the command-line, even if that's not necessary. - auto precompFile = findObjByName(precompFileName); + auto precompFile = + findObjWithPrecompSignature(precompFileName, precomp.Signature); if (!precompFile) return createFileError( - precompFileName.str(), - make_error(pdb::pdb_error_code::external_cmdline_ref)); + precomp.getPrecompFilePath().str(), + make_error(pdb::pdb_error_code::no_matching_pch)); addObjFile(precompFile, &indexMap); - if (!precompFile->pchSignature) - fatal(precompFile->getName() + " is not a precompiled headers object"); - - if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0)) - return createFileError( - precomp.getPrecompFilePath().str(), - make_error(pdb::pdb_error_code::signature_out_of_date)); - return indexMap; } static bool remapTypeIndex(TypeIndex &ti, ArrayRef typeIndexMap) { if (ti.isSimple()) return true; if (ti.toArrayIndex() >= typeIndexMap.size()) return false; ti = typeIndexMap[ti.toArrayIndex()]; return true; } static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind, MutableArrayRef recordBytes, const CVIndexMap &indexMap, ArrayRef typeRefs) { MutableArrayRef contents = recordBytes.drop_front(sizeof(RecordPrefix)); for (const TiReference &ref : typeRefs) { unsigned byteSize = ref.Count * sizeof(TypeIndex); if (contents.size() < ref.Offset + byteSize) fatal("symbol record too short"); // This can be an item index or a type index. Choose the appropriate map. ArrayRef typeOrItemMap = indexMap.tpiMap; bool isItemIndex = ref.Kind == TiRefKind::IndexRef; if (isItemIndex && indexMap.isTypeServerMap) typeOrItemMap = indexMap.ipiMap; MutableArrayRef tIs( reinterpret_cast(contents.data() + ref.Offset), ref.Count); for (TypeIndex &ti : tIs) { if (!remapTypeIndex(ti, typeOrItemMap)) { log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " + file->getName() + " with bad " + (isItemIndex ? "item" : "type") + " index 0x" + utohexstr(ti.getIndex())); ti = TypeIndex(SimpleTypeKind::NotTranslated); continue; } } } } static void recordStringTableReferenceAtOffset(MutableArrayRef contents, uint32_t offset, std::vector &strTableRefs) { contents = contents.drop_front(offset).take_front(sizeof(support::ulittle32_t)); ulittle32_t *index = reinterpret_cast(contents.data()); strTableRefs.push_back(index); } static void recordStringTableReferences(SymbolKind kind, MutableArrayRef contents, std::vector &strTableRefs) { // For now we only handle S_FILESTATIC, but we may need the same logic for // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any // PDBs that contain these types of records, so because of the uncertainty // they are omitted here until we can prove that it's necessary. switch (kind) { case SymbolKind::S_FILESTATIC: // FileStaticSym::ModFileOffset recordStringTableReferenceAtOffset(contents, 8, strTableRefs); break; case SymbolKind::S_DEFRANGE: case SymbolKind::S_DEFRANGE_SUBFIELD: log("Not fixing up string table reference in S_DEFRANGE / " "S_DEFRANGE_SUBFIELD record"); break; default: break; } } static SymbolKind symbolKind(ArrayRef recordData) { const RecordPrefix *prefix = reinterpret_cast(recordData.data()); return static_cast(uint16_t(prefix->RecordKind)); } /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 static void translateIdSymbols(MutableArrayRef &recordData, TypeCollection &iDTable) { RecordPrefix *prefix = reinterpret_cast(recordData.data()); SymbolKind kind = symbolKind(recordData); if (kind == SymbolKind::S_PROC_ID_END) { prefix->RecordKind = SymbolKind::S_END; return; } // In an object file, GPROC32_ID has an embedded reference which refers to the // single object file type index namespace. This has already been translated // to the PDB file's ID stream index space, but we need to convert this to a // symbol that refers to the type stream index space. So we remap again from // ID index space to type index space. if (kind == SymbolKind::S_GPROC32_ID || kind == SymbolKind::S_LPROC32_ID) { SmallVector refs; auto content = recordData.drop_front(sizeof(RecordPrefix)); CVSymbol sym(recordData); discoverTypeIndicesInSymbol(sym, refs); assert(refs.size() == 1); assert(refs.front().Count == 1); TypeIndex *ti = reinterpret_cast(content.data() + refs[0].Offset); // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in // the IPI stream, whose `FunctionType` member refers to the TPI stream. // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and // in both cases we just need the second type index. if (!ti->isSimple() && !ti->isNoneType()) { CVType funcIdData = iDTable.getType(*ti); SmallVector indices; discoverTypeIndices(funcIdData, indices); assert(indices.size() == 2); *ti = indices[1]; } kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 : SymbolKind::S_LPROC32; prefix->RecordKind = uint16_t(kind); } } /// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. /// The object file may not be aligned. static MutableArrayRef copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef &alignedMem) { size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); assert(size >= 4 && "record too short"); assert(size <= MaxRecordLength && "record too long"); assert(alignedMem.size() >= size && "didn't preallocate enough"); // Copy the symbol record and zero out any padding bytes. MutableArrayRef newData = alignedMem.take_front(size); alignedMem = alignedMem.drop_front(size); memcpy(newData.data(), sym.data().data(), sym.length()); memset(newData.data() + sym.length(), 0, size - sym.length()); // Update the record prefix length. It should point to the beginning of the // next record. auto *prefix = reinterpret_cast(newData.data()); prefix->RecordLen = size - 2; return newData; } struct ScopeRecord { ulittle32_t ptrParent; ulittle32_t ptrEnd; }; struct SymbolScope { ScopeRecord *openingRecord; uint32_t scopeOffset; }; static void scopeStackOpen(SmallVectorImpl &stack, uint32_t curOffset, CVSymbol &sym) { assert(symbolOpensScope(sym.kind())); SymbolScope s; s.scopeOffset = curOffset; s.openingRecord = const_cast( reinterpret_cast(sym.content().data())); s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset; stack.push_back(s); } static void scopeStackClose(SmallVectorImpl &stack, uint32_t curOffset, InputFile *file) { if (stack.empty()) { warn("symbol scopes are not balanced in " + file->getName()); return; } SymbolScope s = stack.pop_back_val(); s.openingRecord->ptrEnd = curOffset; } static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { switch (sym.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_CONSTANT: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place // since they are synthesized by the linker in response to S_GPROC32 and // S_LPROC32, but if we do see them, don't put them in the module stream I // guess. case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: return false; // S_UDT records go in the module stream if it is not a global S_UDT. case SymbolKind::S_UDT: return !isGlobalScope; // S_GDATA32 does not go in the module stream, but S_LDATA32 does. case SymbolKind::S_LDATA32: default: return true; } } static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) { switch (sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_GDATA32: // S_LDATA32 goes in both the module stream and the globals stream. case SymbolKind::S_LDATA32: case SymbolKind::S_GPROC32: case SymbolKind::S_LPROC32: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place // since they are synthesized by the linker in response to S_GPROC32 and // S_LPROC32, but if we do see them, copy them straight through. case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: return true; // S_UDT records go in the globals stream if it is a global S_UDT. case SymbolKind::S_UDT: return isGlobalScope; default: return false; } } static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, unsigned symOffset, const CVSymbol &sym) { switch (sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_UDT: case SymbolKind::S_GDATA32: case SymbolKind::S_LDATA32: case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: builder.addGlobalSymbol(sym); break; case SymbolKind::S_GPROC32: case SymbolKind::S_LPROC32: { SymbolRecordKind k = SymbolRecordKind::ProcRefSym; if (sym.kind() == SymbolKind::S_LPROC32) k = SymbolRecordKind::LocalProcRef; ProcRefSym ps(k); ps.Module = modIndex; // For some reason, MSVC seems to add one to this value. ++ps.Module; ps.Name = getSymbolName(sym); ps.SumName = 0; ps.SymOffset = symOffset; builder.addGlobalSymbol(ps); break; } default: llvm_unreachable("Invalid symbol kind!"); } } void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, std::vector &stringTableRefs, BinaryStreamRef symData) { ArrayRef symsBuffer; cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); SmallVector scopes; // Iterate every symbol to check if any need to be realigned, and if so, how // much space we need to allocate for them. bool needsRealignment = false; unsigned totalRealignedSize = 0; auto ec = forEachCodeViewRecord( symsBuffer, [&](CVSymbol sym) -> llvm::Error { unsigned realignedSize = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); needsRealignment |= realignedSize != sym.length(); totalRealignedSize += realignedSize; return Error::success(); }); // If any of the symbol record lengths was corrupt, ignore them all, warn // about it, and move on. if (ec) { warn("corrupt symbol records in " + file->getName()); consumeError(std::move(ec)); return; } // If any symbol needed realignment, allocate enough contiguous memory for // them all. Typically symbol subsections are small enough that this will not // cause fragmentation. MutableArrayRef alignedSymbolMem; if (needsRealignment) { void *alignedData = alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); alignedSymbolMem = makeMutableArrayRef( reinterpret_cast(alignedData), totalRealignedSize); } // Iterate again, this time doing the real work. unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset(); ArrayRef bulkSymbols; cantFail(forEachCodeViewRecord( symsBuffer, [&](CVSymbol sym) -> llvm::Error { // Align the record if required. MutableArrayRef recordBytes; if (needsRealignment) { recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem); sym = CVSymbol(recordBytes); } else { // Otherwise, we can actually mutate the symbol directly, since we // copied it to apply relocations. recordBytes = makeMutableArrayRef( const_cast(sym.data().data()), sym.length()); } // Discover type index references in the record. Skip it if we don't // know where they are. SmallVector typeRefs; if (!discoverTypeIndicesInSymbol(sym, typeRefs)) { log("ignoring unknown symbol record with kind 0x" + utohexstr(sym.kind())); return Error::success(); } // Re-map all the type index references. remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap, typeRefs); // An object file may have S_xxx_ID symbols, but these get converted to // "real" symbols in a PDB. translateIdSymbols(recordBytes, tMerger.getIDTable()); sym = CVSymbol(recordBytes); // If this record refers to an offset in the object file's string table, // add that item to the global PDB string table and re-write the index. recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs); // Fill in "Parent" and "End" fields by maintaining a stack of scopes. if (symbolOpensScope(sym.kind())) scopeStackOpen(scopes, curSymOffset, sym); else if (symbolEndsScope(sym.kind())) scopeStackClose(scopes, curSymOffset, file); // Add the symbol to the globals stream if necessary. Do this before // adding the symbol to the module since we may need to get the next // symbol offset, and writing to the module's symbol stream will update // that offset. if (symbolGoesInGlobalsStream(sym, scopes.empty())) { addGlobalSymbol(builder.getGsiBuilder(), file->moduleDBI->getModuleIndex(), curSymOffset, sym); ++globalSymbols; } if (symbolGoesInModuleStream(sym, scopes.empty())) { // Add symbols to the module in bulk. If this symbol is contiguous // with the previous run of symbols to add, combine the ranges. If // not, close the previous range of symbols and start a new one. if (sym.data().data() == bulkSymbols.end()) { bulkSymbols = makeArrayRef(bulkSymbols.data(), bulkSymbols.size() + sym.length()); } else { file->moduleDBI->addSymbolsInBulk(bulkSymbols); bulkSymbols = recordBytes; } curSymOffset += sym.length(); ++moduleSymbols; } return Error::success(); })); // Add any remaining symbols we've accumulated. file->moduleDBI->addSymbolsInBulk(bulkSymbols); } // Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef relocateDebugChunk(BumpPtrAllocator &alloc, SectionChunk &debugChunk) { uint8_t *buffer = alloc.Allocate(debugChunk.getSize()); assert(debugChunk.getOutputSectionIdx() == 0 && "debug sections should not be in output sections"); debugChunk.writeTo(buffer); return makeArrayRef(buffer, debugChunk.getSize()); } static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { OutputSection *os = c ? c->getOutputSection() : nullptr; pdb::SectionContrib sc; memset(&sc, 0, sizeof(sc)); sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex; sc.Off = c && os ? c->getRVA() - os->getRVA() : 0; sc.Size = c ? c->getSize() : -1; if (auto *secChunk = dyn_cast_or_null(c)) { sc.Characteristics = secChunk->header->Characteristics; sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); ArrayRef contents = secChunk->getContents(); JamCRC crc(0); - ArrayRef charContents = makeArrayRef( - reinterpret_cast(contents.data()), contents.size()); - crc.update(charContents); + crc.update(contents); sc.DataCrc = crc.getCRC(); } else { sc.Characteristics = os ? os->header.Characteristics : 0; sc.Imod = modi; } sc.RelocCrc = 0; // FIXME return sc; } static uint32_t translateStringTableIndex(uint32_t objIndex, const DebugStringTableSubsectionRef &objStrTable, DebugStringTableSubsection &pdbStrTable) { auto expectedString = objStrTable.getString(objIndex); if (!expectedString) { warn("Invalid string table reference"); consumeError(expectedString.takeError()); return 0; } return pdbStrTable.insert(*expectedString); } void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { DebugSubsectionArray subsections; ArrayRef relocatedDebugContents = SectionChunk::consumeDebugMagic( relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName()); BinaryStreamReader reader(relocatedDebugContents, support::little); exitOnErr(reader.readArray(subsections, relocatedDebugContents.size())); for (const DebugSubsectionRecord &ss : subsections) { // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++ // runtime have subsections with this bit set. if (uint32_t(ss.kind()) & codeview::SubsectionIgnoreFlag) continue; switch (ss.kind()) { case DebugSubsectionKind::StringTable: { assert(!cVStrTab.valid() && "Encountered multiple string table subsections!"); exitOnErr(cVStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: assert(!checksums.valid() && "Encountered multiple checksum subsections!"); exitOnErr(checksums.initialize(ss.getRecordData())); break; case DebugSubsectionKind::Lines: // We can add the relocated line table directly to the PDB without // modification because the file checksum offsets will stay the same. file.moduleDBI->addDebugSubsection(ss); break; case DebugSubsectionKind::InlineeLines: assert(!inlineeLines.valid() && "Encountered multiple inlinee lines subsections!"); exitOnErr(inlineeLines.initialize(ss.getRecordData())); break; case DebugSubsectionKind::FrameData: { // We need to re-write string table indices here, so save off all // frame data subsections until we've processed the entire list of // subsections so that we can be sure we have the string table. DebugFrameDataSubsectionRef fds; exitOnErr(fds.initialize(ss.getRecordData())); newFpoFrames.push_back(std::move(fds)); break; } case DebugSubsectionKind::Symbols: { linker.mergeSymbolRecords(&file, indexMap, stringTableReferences, ss.getRecordData()); break; } case DebugSubsectionKind::CrossScopeImports: case DebugSubsectionKind::CrossScopeExports: // These appear to relate to cross-module optimization, so we might use // these for ThinLTO. break; case DebugSubsectionKind::ILLines: case DebugSubsectionKind::FuncMDTokenMap: case DebugSubsectionKind::TypeMDTokenMap: case DebugSubsectionKind::MergedAssemblyInput: // These appear to relate to .Net assembly info. break; case DebugSubsectionKind::CoffSymbolRVA: // Unclear what this is for. break; default: warn("ignoring unknown debug$S subsection kind 0x" + utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file)); break; } } } static Expected getFileName(const DebugStringTableSubsectionRef &strings, const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) { auto iter = checksums.getArray().at(fileID); if (iter == checksums.getArray().end()) return make_error(cv_error_code::no_records); uint32_t offset = iter->FileNameOffset; return strings.getString(offset); } std::shared_ptr DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) { auto newInlineeLines = std::make_shared( *newChecksums, inlineeLines.hasExtraFiles()); for (const InlineeSourceLine &line : inlineeLines) { TypeIndex inlinee = line.Header->Inlinee; uint32_t fileID = line.Header->FileID; uint32_t sourceLine = line.Header->SourceLineNum; ArrayRef typeOrItemMap = indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap; if (!remapTypeIndex(inlinee, typeOrItemMap)) { log("ignoring inlinee line record in " + file.getName() + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); continue; } SmallString<128> filename = exitOnErr(getFileName(cVStrTab, checksums, fileID)); pdbMakeAbsolute(filename); newInlineeLines->addInlineSite(inlinee, filename, sourceLine); if (inlineeLines.hasExtraFiles()) { for (uint32_t extraFileId : line.ExtraFiles) { filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId)); pdbMakeAbsolute(filename); newInlineeLines->addExtraFile(filename); } } } return newInlineeLines; } void DebugSHandler::finish() { pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder(); // We should have seen all debug subsections across the entire object file now // which means that if a StringTable subsection and Checksums subsection were // present, now is the time to handle them. if (!cVStrTab.valid()) { if (checksums.valid()) fatal(".debug$S sections with a checksums subsection must also contain a " "string table subsection"); if (!stringTableReferences.empty()) warn("No StringTable subsection was encountered, but there are string " "table references"); return; } // Rewrite string table indices in the Fpo Data and symbol records to refer to // the global PDB string table instead of the object file string table. for (DebugFrameDataSubsectionRef &fds : newFpoFrames) { const ulittle32_t *reloc = fds.getRelocPtr(); for (codeview::FrameData fd : fds) { fd.RvaStart += *reloc; fd.FrameFunc = translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab); dbiBuilder.addNewFpoData(fd); } } for (ulittle32_t *ref : stringTableReferences) *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab); // Make a new file checksum table that refers to offsets in the PDB-wide // string table. Generally the string table subsection appears after the // checksum table, so we have to do this after looping over all the // subsections. - auto newChecksums = make_unique(linker.pdbStrTab); + auto newChecksums = std::make_unique(linker.pdbStrTab); for (FileChecksumEntry &fc : checksums) { SmallString<128> filename = exitOnErr(cVStrTab.getString(fc.FileNameOffset)); pdbMakeAbsolute(filename); exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); } // Rewrite inlinee item indices if present. if (inlineeLines.valid()) file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get())); file.moduleDBI->addDebugSubsection(std::move(newChecksums)); } void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { if (file->mergedIntoPDB) return; file->mergedIntoPDB = true; // Before we can process symbol substreams from .debug$S, we need to process // type information, file checksums, and the string table. Add type info to // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. CVIndexMap objectIndexMap; auto indexMapResult = mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap); // If the .debug$T sections fail to merge, assume there is no debug info. if (!indexMapResult) { if (!config->warnDebugInfoUnusable) { consumeError(indexMapResult.takeError()); return; } warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" + ">>> failed to load reference " + StringRef(toString(indexMapResult.takeError()))); return; } ScopedTimer t(symbolMergingTimer); pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); DebugSHandler dsh(*this, *file, *indexMapResult); // Now do all live .debug$S and .debug$F sections. for (SectionChunk *debugChunk : file->getDebugChunks()) { if (!debugChunk->live || debugChunk->getSize() == 0) continue; if (debugChunk->getSectionName() == ".debug$S") { dsh.handleDebugS(*debugChunk); continue; } if (debugChunk->getSectionName() == ".debug$F") { ArrayRef relocatedDebugContents = relocateDebugChunk(alloc, *debugChunk); FixedStreamArray fpoRecords; BinaryStreamReader reader(relocatedDebugContents, support::little); uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData); exitOnErr(reader.readArray(fpoRecords, count)); // These are already relocated and don't refer to the string table, so we // can just copy it. for (const object::FpoData &fd : fpoRecords) dbiBuilder.addOldFpoData(fd); continue; } } // Do any post-processing now that all .debug$S sections have been processed. dsh.finish(); } // Add a module descriptor for every object file. We need to put an absolute // path to the object into the PDB. If this is a plain object, we make its // path absolute. If it's an object in an archive, we make the archive path // absolute. static void createModuleDBI(pdb::PDBFileBuilder &builder) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); SmallString<128> objName; for (ObjFile *file : ObjFile::instances) { bool inArchive = !file->parentName.empty(); objName = inArchive ? file->parentName : file->getName(); pdbMakeAbsolute(objName); StringRef modName = inArchive ? file->getName() : StringRef(objName); file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); file->moduleDBI->setObjFileName(objName); ArrayRef chunks = file->getChunks(); uint32_t modi = file->moduleDBI->getModuleIndex(); for (Chunk *c : chunks) { auto *secChunk = dyn_cast(c); if (!secChunk || !secChunk->live) continue; pdb::SectionContrib sc = createSectionContrib(secChunk, modi); file->moduleDBI->setFirstSectionContrib(sc); break; } } } static PublicSym32 createPublic(Defined *def) { PublicSym32 pub(SymbolKind::S_PUB32); pub.Name = def->getName(); if (auto *d = dyn_cast(def)) { if (d->getCOFFSymbol().isFunctionDefinition()) pub.Flags = PublicSymFlags::Function; } else if (isa(def)) { pub.Flags = PublicSymFlags::Function; } OutputSection *os = def->getChunk()->getOutputSection(); assert(os && "all publics should be in final image"); pub.Offset = def->getRVA() - os->getRVA(); pub.Segment = os->sectionIndex; return pub; } // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { ScopedTimer t1(addObjectsTimer); createModuleDBI(builder); for (ObjFile *file : ObjFile::instances) addObjFile(file); builder.getStringTableBuilder().setStrings(pdbStrTab); t1.stop(); // Construct TPI and IPI stream contents. ScopedTimer t2(tpiStreamLayoutTimer); addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); t2.stop(); ScopedTimer t3(globalsLayoutTimer); // Compute the public and global symbols. auto &gsiBuilder = builder.getGsiBuilder(); std::vector publics; symtab->forEachSymbol([&publics](Symbol *s) { // Only emit defined, live symbols that have a chunk. auto *def = dyn_cast(s); if (def && def->isLive() && def->getChunk()) publics.push_back(createPublic(def)); }); if (!publics.empty()) { publicSymbols = publics.size(); // Sort the public symbols and add them to the stream. parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) { return l.Name < r.Name; }); for (const PublicSym32 &pub : publics) gsiBuilder.addPublicSymbol(pub); } } void PDBLinker::printStats() { if (!config->showSummary) return; SmallString<256> buffer; raw_svector_ostream stream(buffer); stream << center_justify("Summary", 80) << '\n' << std::string(80, '-') << '\n'; auto print = [&](uint64_t v, StringRef s) { stream << format_decimal(v, 15) << " " << s << '\n'; }; print(ObjFile::instances.size(), "Input OBJ files (expanded from all cmd-line inputs)"); print(typeServerIndexMappings.size(), "PDB type server dependencies"); print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies"); print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(), "Merged TPI records"); print(pdbStrTab.size(), "Output PDB strings"); print(globalSymbols, "Global symbol records"); print(moduleSymbols, "Module symbol records"); print(publicSymbols, "Public symbol records"); message(buffer); } void PDBLinker::addNatvisFiles() { for (StringRef file : config->natvisFiles) { ErrorOr> dataOrErr = MemoryBuffer::getFile(file); if (!dataOrErr) { warn("Cannot open input file: " + file); continue; } builder.addInjectedSource(file, std::move(*dataOrErr)); } } static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) { switch (machine) { case COFF::IMAGE_FILE_MACHINE_AMD64: return codeview::CPUType::X64; case COFF::IMAGE_FILE_MACHINE_ARM: return codeview::CPUType::ARM7; case COFF::IMAGE_FILE_MACHINE_ARM64: return codeview::CPUType::ARM64; case COFF::IMAGE_FILE_MACHINE_ARMNT: return codeview::CPUType::ARMNT; case COFF::IMAGE_FILE_MACHINE_I386: return codeview::CPUType::Intel80386; default: llvm_unreachable("Unsupported CPU Type"); } } // Mimic MSVC which surrounds arguments containing whitespace with quotes. // Double double-quotes are handled, so that the resulting string can be // executed again on the cmd-line. static std::string quote(ArrayRef args) { std::string r; r.reserve(256); for (StringRef a : args) { if (!r.empty()) r.push_back(' '); bool hasWS = a.find(' ') != StringRef::npos; bool hasQ = a.find('"') != StringRef::npos; if (hasWS || hasQ) r.push_back('"'); if (hasQ) { SmallVector s; a.split(s, '"'); r.append(join(s, "\"\"")); } else { r.append(a); } if (hasWS || hasQ) r.push_back('"'); } return r; } static void fillLinkerVerRecord(Compile3Sym &cs) { cs.Machine = toCodeViewMachine(config->machine); // Interestingly, if we set the string to 0.0.0.0, then when trying to view // local variables WinDbg emits an error that private symbols are not present. // By setting this to a valid MSVC linker version string, local variables are // displayed properly. As such, even though it is not representative of // LLVM's version information, we need this for compatibility. cs.Flags = CompileSym3Flags::None; cs.VersionBackendBuild = 25019; cs.VersionBackendMajor = 14; cs.VersionBackendMinor = 10; cs.VersionBackendQFE = 0; // MSVC also sets the frontend to 0.0.0.0 since this is specifically for the // linker module (which is by definition a backend), so we don't need to do // anything here. Also, it seems we can use "LLVM Linker" for the linker name // without any problems. Only the backend version has to be hardcoded to a // magic number. cs.VersionFrontendBuild = 0; cs.VersionFrontendMajor = 0; cs.VersionFrontendMinor = 0; cs.VersionFrontendQFE = 0; cs.Version = "LLVM Linker"; cs.setLanguage(SourceLanguage::Link); } static void addCommonLinkerModuleSymbols(StringRef path, pdb::DbiModuleDescriptorBuilder &mod, BumpPtrAllocator &allocator) { ObjNameSym ons(SymbolRecordKind::ObjNameSym); EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); fillLinkerVerRecord(cs); ons.Name = "* Linker *"; ons.Signature = 0; ArrayRef args = makeArrayRef(config->argv).drop_front(); std::string argStr = quote(args); ebs.Fields.push_back("cwd"); SmallString<64> cwd; if (config->pdbSourcePath.empty()) sys::fs::current_path(cwd); else cwd = config->pdbSourcePath; ebs.Fields.push_back(cwd); ebs.Fields.push_back("exe"); SmallString<64> exe = config->argv[0]; pdbMakeAbsolute(exe); ebs.Fields.push_back(exe); ebs.Fields.push_back("pdb"); ebs.Fields.push_back(path); ebs.Fields.push_back("cmd"); ebs.Fields.push_back(argStr); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, allocator, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( cs, allocator, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( ebs, allocator, CodeViewContainer::Pdb)); } static void addLinkerModuleCoffGroup(PartialSection *sec, pdb::DbiModuleDescriptorBuilder &mod, OutputSection &os, BumpPtrAllocator &allocator) { // If there's a section, there's at least one chunk assert(!sec->chunks.empty()); const Chunk *firstChunk = *sec->chunks.begin(); const Chunk *lastChunk = *sec->chunks.rbegin(); // Emit COFF group CoffGroupSym cgs(SymbolRecordKind::CoffGroupSym); cgs.Name = sec->name; cgs.Segment = os.sectionIndex; cgs.Offset = firstChunk->getRVA() - os.getRVA(); cgs.Size = lastChunk->getRVA() + lastChunk->getSize() - firstChunk->getRVA(); cgs.Characteristics = sec->characteristics; // Somehow .idata sections & sections groups in the debug symbol stream have // the "write" flag set. However the section header for the corresponding // .idata section doesn't have it. if (cgs.Name.startswith(".idata")) cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( cgs, allocator, CodeViewContainer::Pdb)); } static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, OutputSection &os, BumpPtrAllocator &allocator) { SectionSym sym(SymbolRecordKind::SectionSym); sym.Alignment = 12; // 2^12 = 4KB sym.Characteristics = os.header.Characteristics; sym.Length = os.getVirtualSize(); sym.Name = os.name; sym.Rva = os.getRVA(); sym.SectionNumber = os.sectionIndex; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( sym, allocator, CodeViewContainer::Pdb)); // Skip COFF groups in MinGW because it adds a significant footprint to the // PDB, due to each function being in its own section if (config->mingw) return; // Output COFF groups for individual chunks of this section. for (PartialSection *sec : os.contribSections) { addLinkerModuleCoffGroup(sec, mod, os, allocator); } } // Add all import files as modules to the PDB. void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { if (ImportFile::instances.empty()) return; std::map dllToModuleDbi; for (ImportFile *file : ImportFile::instances) { if (!file->live) continue; if (!file->thunkSym) continue; if (!file->thunkLive) continue; std::string dll = StringRef(file->dllName).lower(); llvm::pdb::DbiModuleDescriptorBuilder *&mod = dllToModuleDbi[dll]; if (!mod) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); SmallString<128> libPath = file->parentName; pdbMakeAbsolute(libPath); sys::path::native(libPath); // Name modules similar to MSVC's link.exe. // The first module is the simple dll filename llvm::pdb::DbiModuleDescriptorBuilder &firstMod = exitOnErr(dbiBuilder.addModuleInfo(file->dllName)); firstMod.setObjFileName(libPath); pdb::SectionContrib sc = createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); firstMod.setFirstSectionContrib(sc); // The second module is where the import stream goes. mod = &exitOnErr(dbiBuilder.addModuleInfo("Import:" + file->dllName)); mod->setObjFileName(libPath); } DefinedImportThunk *thunk = cast(file->thunkSym); Chunk *thunkChunk = thunk->getChunk(); OutputSection *thunkOS = thunkChunk->getOutputSection(); ObjNameSym ons(SymbolRecordKind::ObjNameSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); Thunk32Sym ts(SymbolRecordKind::Thunk32Sym); ScopeEndSym es(SymbolRecordKind::ScopeEndSym); ons.Name = file->dllName; ons.Signature = 0; fillLinkerVerRecord(cs); ts.Name = thunk->getName(); ts.Parent = 0; ts.End = 0; ts.Next = 0; ts.Thunk = ThunkOrdinal::Standard; ts.Length = thunkChunk->getSize(); ts.Segment = thunkOS->sectionIndex; ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, alloc, CodeViewContainer::Pdb)); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( cs, alloc, CodeViewContainer::Pdb)); SmallVector scopes; CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol( ts, alloc, CodeViewContainer::Pdb); scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym); mod->addSymbol(newSym); newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc, CodeViewContainer::Pdb); scopeStackClose(scopes, mod->getNextSymbolOffset(), file); mod->addSymbol(newSym); pdb::SectionContrib sc = createSectionContrib(thunk->getChunk(), mod->getModuleIndex()); mod->setFirstSectionContrib(sc); } } // Creates a PDB file. -void coff::createPDB(SymbolTable *symtab, +void createPDB(SymbolTable *symtab, ArrayRef outputSections, ArrayRef sectionTable, llvm::codeview::DebugInfo *buildId) { ScopedTimer t1(totalPdbLinkTimer); PDBLinker pdb(symtab); pdb.initialize(buildId); pdb.addObjectsToPDB(); pdb.addImportFilesToPDB(outputSections); pdb.addSections(outputSections, sectionTable); pdb.addNatvisFiles(); ScopedTimer t2(diskCommitTimer); codeview::GUID guid; pdb.commit(&guid); memcpy(&buildId->PDB70.Signature, &guid, 16); t2.stop(); t1.stop(); pdb.printStats(); } void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { exitOnErr(builder.initialize(4096)); // 4096 is blocksize buildId->Signature.CVSignature = OMF::Signature::PDB70; // Signature is set to a hash of the PDB contents when the PDB is done. memset(buildId->PDB70.Signature, 0, 16); buildId->PDB70.Age = 1; // Create streams in MSF for predefined streams, namely // PDB, TPI, DBI and IPI. for (int i = 0; i < (int)pdb::kSpecialStreamCount; ++i) exitOnErr(builder.getMsfBuilder().addStream(0)); // Add an Info stream. auto &infoBuilder = builder.getInfoBuilder(); infoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); infoBuilder.setHashPDBContentsToGUID(true); // Add an empty DBI stream. pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); dbiBuilder.setAge(buildId->PDB70.Age); dbiBuilder.setVersionHeader(pdb::PdbDbiV70); dbiBuilder.setMachineType(config->machine); // Technically we are not link.exe 14.11, but there are known cases where // debugging tools on Windows expect Microsoft-specific version numbers or // they fail to work at all. Since we know we produce PDBs that are // compatible with LINK 14.11, we set that version number here. dbiBuilder.setBuildNumber(14, 11); } void PDBLinker::addSections(ArrayRef outputSections, ArrayRef sectionTable) { // It's not entirely clear what this is, but the * Linker * module uses it. pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); nativePath = config->pdbPath; pdbMakeAbsolute(nativePath); uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); linkerModule.setPdbFilePathNI(pdbFilePathNI); addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc); // Add section contributions. They must be ordered by ascending RVA. for (OutputSection *os : outputSections) { addLinkerModuleSectionSymbol(linkerModule, *os, alloc); for (Chunk *c : os->chunks) { pdb::SectionContrib sc = createSectionContrib(c, linkerModule.getModuleIndex()); builder.getDbiBuilder().addSectionContrib(sc); } } // The * Linker * first section contrib is only used along with /INCREMENTAL, // to provide trampolines thunks for incremental function patching. Set this // as "unused" because LLD doesn't support /INCREMENTAL link. pdb::SectionContrib sc = createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); linkerModule.setFirstSectionContrib(sc); // Add Section Map stream. ArrayRef sections = { (const object::coff_section *)sectionTable.data(), sectionTable.size() / sizeof(object::coff_section)}; sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections); dbiBuilder.setSectionMap(sectionMap); // Add COFF section header stream. exitOnErr( dbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, sectionTable)); } void PDBLinker::commit(codeview::GUID *guid) { + ExitOnError exitOnErr((config->pdbPath + ": ").str()); // Write to a file. exitOnErr(builder.commit(config->pdbPath, guid)); } static uint32_t getSecrelReloc() { switch (config->machine) { case AMD64: return COFF::IMAGE_REL_AMD64_SECREL; case I386: return COFF::IMAGE_REL_I386_SECREL; case ARMNT: return COFF::IMAGE_REL_ARM_SECREL; case ARM64: return COFF::IMAGE_REL_ARM64_SECREL; default: llvm_unreachable("unknown machine type"); } } // Try to find a line table for the given offset Addr into the given chunk C. // If a line table was found, the line table, the string and checksum tables // that are used to interpret the line table, and the offset of Addr in the line // table are stored in the output arguments. Returns whether a line table was // found. static bool findLineTable(const SectionChunk *c, uint32_t addr, DebugStringTableSubsectionRef &cVStrTab, DebugChecksumsSubsectionRef &checksums, DebugLinesSubsectionRef &lines, uint32_t &offsetInLinetable) { ExitOnError exitOnErr; uint32_t secrelReloc = getSecrelReloc(); for (SectionChunk *dbgC : c->file->getDebugChunks()) { if (dbgC->getSectionName() != ".debug$S") continue; // Build a mapping of SECREL relocations in dbgC that refer to `c`. DenseMap secrels; for (const coff_relocation &r : dbgC->getRelocs()) { if (r.Type != secrelReloc) continue; if (auto *s = dyn_cast_or_null( c->file->getSymbols()[r.SymbolTableIndex])) if (s->getChunk() == c) secrels[r.VirtualAddress] = s->getValue(); } ArrayRef contents = SectionChunk::consumeDebugMagic(dbgC->getContents(), ".debug$S"); DebugSubsectionArray subsections; BinaryStreamReader reader(contents, support::little); exitOnErr(reader.readArray(subsections, contents.size())); for (const DebugSubsectionRecord &ss : subsections) { switch (ss.kind()) { case DebugSubsectionKind::StringTable: { assert(!cVStrTab.valid() && "Encountered multiple string table subsections!"); exitOnErr(cVStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: assert(!checksums.valid() && "Encountered multiple checksum subsections!"); exitOnErr(checksums.initialize(ss.getRecordData())); break; case DebugSubsectionKind::Lines: { ArrayRef bytes; auto ref = ss.getRecordData(); exitOnErr(ref.readLongestContiguousChunk(0, bytes)); size_t offsetInDbgC = bytes.data() - dbgC->getContents().data(); // Check whether this line table refers to C. auto i = secrels.find(offsetInDbgC); if (i == secrels.end()) break; // Check whether this line table covers Addr in C. DebugLinesSubsectionRef linesTmp; exitOnErr(linesTmp.initialize(BinaryStreamReader(ref))); uint32_t offsetInC = i->second + linesTmp.header()->RelocOffset; if (addr < offsetInC || addr >= offsetInC + linesTmp.header()->CodeSize) break; assert(!lines.header() && "Encountered multiple line tables for function!"); exitOnErr(lines.initialize(BinaryStreamReader(ref))); offsetInLinetable = addr - offsetInC; break; } default: break; } if (cVStrTab.valid() && checksums.valid() && lines.header()) return true; } } return false; } // Use CodeView line tables to resolve a file and line number for the given -// offset into the given chunk and return them, or {"", 0} if a line table was +// offset into the given chunk and return them, or None if a line table was // not found. -std::pair coff::getFileLine(const SectionChunk *c, - uint32_t addr) { +Optional> +getFileLineCodeView(const SectionChunk *c, uint32_t addr) { ExitOnError exitOnErr; DebugStringTableSubsectionRef cVStrTab; DebugChecksumsSubsectionRef checksums; DebugLinesSubsectionRef lines; uint32_t offsetInLinetable; if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) - return {"", 0}; + return None; Optional nameIndex; Optional lineNumber; for (LineColumnEntry &entry : lines) { for (const LineNumberEntry &ln : entry.LineNumbers) { LineInfo li(ln.Flags); if (ln.Offset > offsetInLinetable) { if (!nameIndex) { nameIndex = entry.NameIndex; lineNumber = li.getStartLine(); } StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); - return {filename, *lineNumber}; + return std::make_pair(filename, *lineNumber); } nameIndex = entry.NameIndex; lineNumber = li.getStartLine(); } } if (!nameIndex) - return {"", 0}; + return None; StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); - return {filename, *lineNumber}; + return std::make_pair(filename, *lineNumber); } + +} // namespace coff +} // namespace lld Index: vendor/lld/dist/COFF/PDB.h =================================================================== --- vendor/lld/dist/COFF/PDB.h (revision 353949) +++ vendor/lld/dist/COFF/PDB.h (revision 353950) @@ -1,37 +1,38 @@ //===- PDB.h ----------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_PDB_H #define LLD_COFF_PDB_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" namespace llvm { namespace codeview { union DebugInfo; } } namespace lld { namespace coff { class OutputSection; class SectionChunk; class SymbolTable; void createPDB(SymbolTable *symtab, llvm::ArrayRef outputSections, llvm::ArrayRef sectionTable, llvm::codeview::DebugInfo *buildId); -std::pair getFileLine(const SectionChunk *c, - uint32_t addr); -} -} +llvm::Optional> +getFileLineCodeView(const SectionChunk *c, uint32_t addr); +} // namespace coff +} // namespace lld #endif Index: vendor/lld/dist/COFF/SymbolTable.cpp =================================================================== --- vendor/lld/dist/COFF/SymbolTable.cpp (revision 353949) +++ vendor/lld/dist/COFF/SymbolTable.cpp (revision 353950) @@ -1,603 +1,805 @@ //===- SymbolTable.cpp ----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SymbolTable.h" #include "Config.h" #include "Driver.h" #include "LTO.h" #include "PDB.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Timer.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; namespace lld { namespace coff { static Timer ltoTimer("LTO", Timer::root()); SymbolTable *symtab; void SymbolTable::addFile(InputFile *file) { log("Reading " + toString(file)); file->parse(); MachineTypes mt = file->getMachineType(); if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { config->machine = mt; } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { error(toString(file) + ": machine type " + machineToStr(mt) + " conflicts with " + machineToStr(config->machine)); return; } if (auto *f = dyn_cast(file)) { ObjFile::instances.push_back(f); } else if (auto *f = dyn_cast(file)) { BitcodeFile::instances.push_back(f); } else if (auto *f = dyn_cast(file)) { ImportFile::instances.push_back(f); } driver->parseDirectives(file); } static void errorOrWarn(const Twine &s) { if (config->forceUnresolved) warn(s); else error(s); } +// Causes the file associated with a lazy symbol to be linked in. +static void forceLazy(Symbol *s) { + s->pendingArchiveLoad = true; + switch (s->kind()) { + case Symbol::Kind::LazyArchiveKind: { + auto *l = cast(s); + l->file->addMember(l->sym); + break; + } + case Symbol::Kind::LazyObjectKind: + cast(s)->file->fetch(); + break; + default: + llvm_unreachable( + "symbol passed to forceLazy is not a LazyArchive or LazyObject"); + } +} + // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { DefinedRegular *candidate = nullptr; for (Symbol *s : sc->file->getSymbols()) { auto *d = dyn_cast_or_null(s); - if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr || + if (!d || !d->data || d->file != sc->file || d->getChunk() != sc || + d->getValue() > addr || (candidate && d->getValue() < candidate->getValue())) continue; candidate = d; } return candidate; } +static std::vector getSymbolLocations(BitcodeFile *file) { + std::string res("\n>>> referenced by "); + StringRef source = file->obj->getSourceFileName(); + if (!source.empty()) + res += source.str() + "\n>>> "; + res += toString(file); + return {res}; +} + +static Optional> +getFileLineDwarf(const SectionChunk *c, uint32_t addr) { + Optional optionalLineInfo = + c->file->getDILineInfo(addr, c->getSectionNumber() - 1); + if (!optionalLineInfo) + return None; + const DILineInfo &lineInfo = *optionalLineInfo; + if (lineInfo.FileName == DILineInfo::BadString) + return None; + return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line); +} + +static Optional> +getFileLine(const SectionChunk *c, uint32_t addr) { + // MinGW can optionally use codeview, even if the default is dwarf. + Optional> fileLine = + getFileLineCodeView(c, addr); + // If codeview didn't yield any result, check dwarf in MinGW mode. + if (!fileLine && config->mingw) + fileLine = getFileLineDwarf(c, addr); + return fileLine; +} + // Given a file and the index of a symbol in that file, returns a description // of all references to that symbol from that file. If no debug information is // available, returns just the name of the file, else one string per actual // reference as described in the debug info. std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { struct Location { Symbol *sym; std::pair fileLine; }; std::vector locations; for (Chunk *c : file->getChunks()) { auto *sc = dyn_cast(c); if (!sc) continue; for (const coff_relocation &r : sc->getRelocs()) { if (r.SymbolTableIndex != symIndex) continue; - std::pair fileLine = + Optional> fileLine = getFileLine(sc, r.VirtualAddress); Symbol *sym = getSymbol(sc, r.VirtualAddress); - if (!fileLine.first.empty() || sym) - locations.push_back({sym, fileLine}); + if (fileLine) + locations.push_back({sym, *fileLine}); + else if (sym) + locations.push_back({sym, {"", 0}}); } } if (locations.empty()) return std::vector({"\n>>> referenced by " + toString(file)}); std::vector symbolLocations(locations.size()); size_t i = 0; for (Location loc : locations) { llvm::raw_string_ostream os(symbolLocations[i++]); os << "\n>>> referenced by "; if (!loc.fileLine.first.empty()) os << loc.fileLine.first << ":" << loc.fileLine.second << "\n>>> "; os << toString(file); if (loc.sym) os << ":(" << toString(*loc.sym) << ')'; } return symbolLocations; } +std::vector getSymbolLocations(InputFile *file, + uint32_t symIndex) { + if (auto *o = dyn_cast(file)) + return getSymbolLocations(o, symIndex); + if (auto *b = dyn_cast(file)) + return getSymbolLocations(b); + llvm_unreachable("unsupported file type passed to getSymbolLocations"); + return {}; +} + // For an undefined symbol, stores all files referencing it and the index of // the undefined symbol in each file. struct UndefinedDiag { Symbol *sym; struct File { - ObjFile *oFile; - uint64_t symIndex; + InputFile *file; + uint32_t symIndex; }; std::vector files; }; static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { std::string out; llvm::raw_string_ostream os(out); os << "undefined symbol: " << toString(*undefDiag.sym); const size_t maxUndefReferences = 10; size_t i = 0, numRefs = 0; for (const UndefinedDiag::File &ref : undefDiag.files) { std::vector symbolLocations = - getSymbolLocations(ref.oFile, ref.symIndex); + getSymbolLocations(ref.file, ref.symIndex); numRefs += symbolLocations.size(); for (const std::string &s : symbolLocations) { if (i >= maxUndefReferences) break; os << s; i++; } } if (i < numRefs) os << "\n>>> referenced " << numRefs - i << " more times"; errorOrWarn(os.str()); } void SymbolTable::loadMinGWAutomaticImports() { for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); if (!undef) continue; if (!sym->isUsedInRegularObj) continue; + if (undef->getWeakAlias()) + continue; StringRef name = undef->getName(); if (name.startswith("__imp_")) continue; - // If we have an undefined symbol, but we have a Lazy representing a - // symbol we could load from file, make sure to load that. - Lazy *l = dyn_cast_or_null(find(("__imp_" + name).str())); - if (!l || l->pendingArchiveLoad) + // If we have an undefined symbol, but we have a lazy symbol we could + // load, load it. + Symbol *l = find(("__imp_" + name).str()); + if (!l || l->pendingArchiveLoad || !l->isLazy()) continue; - log("Loading lazy " + l->getName() + " from " + l->file->getName() + + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + " for automatic import"); - l->pendingArchiveLoad = true; - l->file->addMember(&l->sym); + forceLazy(l); } } -bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { +Defined *SymbolTable::impSymbol(StringRef name) { if (name.startswith("__imp_")) - return false; - Defined *imp = dyn_cast_or_null(find(("__imp_" + name).str())); + return nullptr; + return dyn_cast_or_null(find(("__imp_" + name).str())); +} + +bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { + Defined *imp = impSymbol(name); if (!imp) return false; // Replace the reference directly to a variable with a reference // to the import address table instead. This obviously isn't right, // but we mark the symbol as isRuntimePseudoReloc, and a later pass // will add runtime pseudo relocations for every relocation against // this Symbol. The runtime pseudo relocation framework expects the // reference itself to point at the IAT entry. size_t impSize = 0; if (isa(imp)) { log("Automatically importing " + name + " from " + cast(imp)->getDLLName()); impSize = sizeof(DefinedImportData); } else if (isa(imp)) { log("Automatically importing " + name + " from " + toString(cast(imp)->file)); impSize = sizeof(DefinedRegular); } else { warn("unable to automatically import " + name + " from " + imp->getName() + " from " + toString(cast(imp)->file) + "; unexpected symbol type"); return false; } sym->replaceKeepingName(imp, impSize); sym->isRuntimePseudoReloc = true; // There may exist symbols named .refptr. which only consist // of a single pointer to . If it turns out is // automatically imported, we don't need to keep the .refptr. // pointer at all, but redirect all accesses to it to the IAT entry // for __imp_ instead, and drop the whole .refptr. chunk. DefinedRegular *refptr = dyn_cast_or_null(find((".refptr." + name).str())); if (refptr && refptr->getChunk()->getSize() == config->wordsize) { SectionChunk *sc = dyn_cast_or_null(refptr->getChunk()); if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { log("Replacing .refptr." + name + " with " + imp->getName()); refptr->getChunk()->live = false; refptr->replaceKeepingName(imp, impSize); } } return true; } -void SymbolTable::reportRemainingUndefines() { +/// Helper function for reportUnresolvable and resolveRemainingUndefines. +/// This function emits an "undefined symbol" diagnostic for each symbol in +/// undefs. If localImports is not nullptr, it also emits a "locally +/// defined symbol imported" diagnostic for symbols in localImports. +/// objFiles and bitcodeFiles (if not nullptr) are used to report where +/// undefined symbols are referenced. +static void +reportProblemSymbols(const SmallPtrSetImpl &undefs, + const DenseMap *localImports, + const std::vector objFiles, + const std::vector *bitcodeFiles) { + + // Return early if there is nothing to report (which should be + // the common case). + if (undefs.empty() && (!localImports || localImports->empty())) + return; + + for (Symbol *b : config->gcroot) { + if (undefs.count(b)) + errorOrWarn(": undefined symbol: " + toString(*b)); + if (localImports) + if (Symbol *imp = localImports->lookup(b)) + warn(": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); + } + + std::vector undefDiags; + DenseMap firstDiag; + + auto processFile = [&](InputFile *file, ArrayRef symbols) { + uint32_t symIndex = (uint32_t)-1; + for (Symbol *sym : symbols) { + ++symIndex; + if (!sym) + continue; + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, symIndex}}}); + } else { + undefDiags[it->second].files.push_back({file, symIndex}); + } + } + if (localImports) + if (Symbol *imp = localImports->lookup(sym)) + warn(toString(file) + + ": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); + } + }; + + for (ObjFile *file : objFiles) + processFile(file, file->getSymbols()); + + if (bitcodeFiles) + for (BitcodeFile *file : *bitcodeFiles) + processFile(file, file->getSymbols()); + + for (const UndefinedDiag &undefDiag : undefDiags) + reportUndefinedSymbol(undefDiag); +} + +void SymbolTable::reportUnresolvable() { SmallPtrSet undefs; + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) + continue; + if (undef->getWeakAlias()) + continue; + StringRef name = undef->getName(); + if (name.startswith("__imp_")) { + Symbol *imp = find(name.substr(strlen("__imp_"))); + if (imp && isa(imp)) + continue; + } + if (name.contains("_PchSym_")) + continue; + if (config->mingw && impSymbol(name)) + continue; + undefs.insert(sym); + } + + reportProblemSymbols(undefs, + /* localImports */ nullptr, ObjFile::instances, + &BitcodeFile::instances); +} + +void SymbolTable::resolveRemainingUndefines() { + SmallPtrSet undefs; DenseMap localImports; for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); if (!undef) continue; if (!sym->isUsedInRegularObj) continue; StringRef name = undef->getName(); // A weak alias may have been resolved, so check for that. if (Defined *d = undef->getWeakAlias()) { // We want to replace Sym with D. However, we can't just blindly // copy sizeof(SymbolUnion) bytes from D to Sym because D may be an // internal symbol, and internal symbols are stored as "unparented" // Symbols. For that reason we need to check which type of symbol we // are dealing with and copy the correct number of bytes. if (isa(d)) memcpy(sym, d, sizeof(DefinedRegular)); else if (isa(d)) memcpy(sym, d, sizeof(DefinedAbsolute)); else memcpy(sym, d, sizeof(SymbolUnion)); continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. if (name.startswith("__imp_")) { Symbol *imp = find(name.substr(strlen("__imp_"))); if (imp && isa(imp)) { auto *d = cast(imp); replaceSymbol(sym, name, d); localImportChunks.push_back(cast(sym)->getChunk()); localImports[sym] = d; continue; } } // We don't want to report missing Microsoft precompiled headers symbols. // A proper message will be emitted instead in PDBLinker::aquirePrecompObj if (name.contains("_PchSym_")) continue; if (config->mingw && handleMinGWAutomaticImport(sym, name)) continue; // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. if (config->forceUnresolved) replaceSymbol(sym, name, 0); undefs.insert(sym); } - if (undefs.empty() && localImports.empty()) - return; - - for (Symbol *b : config->gcroot) { - if (undefs.count(b)) - errorOrWarn(": undefined symbol: " + toString(*b)); - if (config->warnLocallyDefinedImported) - if (Symbol *imp = localImports.lookup(b)) - warn(": locally defined symbol imported: " + toString(*imp) + - " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); - } - - std::vector undefDiags; - DenseMap firstDiag; - - for (ObjFile *file : ObjFile::instances) { - size_t symIndex = (size_t)-1; - for (Symbol *sym : file->getSymbols()) { - ++symIndex; - if (!sym) - continue; - if (undefs.count(sym)) { - auto it = firstDiag.find(sym); - if (it == firstDiag.end()) { - firstDiag[sym] = undefDiags.size(); - undefDiags.push_back({sym, {{file, symIndex}}}); - } else { - undefDiags[it->second].files.push_back({file, symIndex}); - } - } - if (config->warnLocallyDefinedImported) - if (Symbol *imp = localImports.lookup(sym)) - warn(toString(file) + - ": locally defined symbol imported: " + toString(*imp) + - " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); - } - } - - for (const UndefinedDiag& undefDiag : undefDiags) - reportUndefinedSymbol(undefDiag); + reportProblemSymbols( + undefs, config->warnLocallyDefinedImported ? &localImports : nullptr, + ObjFile::instances, /* bitcode files no longer needed */ nullptr); } std::pair SymbolTable::insert(StringRef name) { bool inserted = false; Symbol *&sym = symMap[CachedHashStringRef(name)]; if (!sym) { sym = reinterpret_cast(make()); sym->isUsedInRegularObj = false; sym->pendingArchiveLoad = false; inserted = true; } return {sym, inserted}; } std::pair SymbolTable::insert(StringRef name, InputFile *file) { std::pair result = insert(name); if (!file || !isa(file)) result.first->isUsedInRegularObj = true; return result; } Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, bool isWeakAlias) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name, f); - if (wasInserted || (isa(s) && isWeakAlias)) { + if (wasInserted || (s->isLazy() && isWeakAlias)) { replaceSymbol(s, name); return s; } - if (auto *l = dyn_cast(s)) { - if (!s->pendingArchiveLoad) { - s->pendingArchiveLoad = true; - l->file->addMember(&l->sym); - } - } + if (s->isLazy()) + forceLazy(s); return s; } -void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) { +void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { StringRef name = sym.getName(); Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name); if (wasInserted) { - replaceSymbol(s, f, sym); + replaceSymbol(s, f, sym); return; } auto *u = dyn_cast(s); if (!u || u->weakAlias || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; - f->addMember(&sym); + f->addMember(sym); } -void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { - std::string msg = "duplicate symbol: " + toString(*existing) + " in " + - toString(existing->getFile()) + " and in " + - toString(newFile); +void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted) { + replaceSymbol(s, f, n); + return; + } + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) + return; + s->pendingArchiveLoad = true; + f->fetch(); +} +static std::string getSourceLocationBitcode(BitcodeFile *file) { + std::string res("\n>>> defined at "); + StringRef source = file->obj->getSourceFileName(); + if (!source.empty()) + res += source.str() + "\n>>> "; + res += toString(file); + return res; +} + +static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc, + uint32_t offset, StringRef name) { + Optional> fileLine; + if (sc) + fileLine = getFileLine(sc, offset); + if (!fileLine) + fileLine = file->getVariableLocation(name); + + std::string res; + llvm::raw_string_ostream os(res); + os << "\n>>> defined at "; + if (fileLine) + os << fileLine->first << ":" << fileLine->second << "\n>>> "; + os << toString(file); + return os.str(); +} + +static std::string getSourceLocation(InputFile *file, SectionChunk *sc, + uint32_t offset, StringRef name) { + if (auto *o = dyn_cast(file)) + return getSourceLocationObj(o, sc, offset, name); + if (auto *b = dyn_cast(file)) + return getSourceLocationBitcode(b); + return "\n>>> defined at " + toString(file); +} + +// Construct and print an error message in the form of: +// +// lld-link: error: duplicate symbol: foo +// >>> defined at bar.c:30 +// >>> bar.o +// >>> defined at baz.c:563 +// >>> baz.o +void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile, + SectionChunk *newSc, + uint32_t newSectionOffset) { + std::string msg; + llvm::raw_string_ostream os(msg); + os << "duplicate symbol: " << toString(*existing); + + DefinedRegular *d = cast(existing); + if (d && isa(d->getFile())) { + os << getSourceLocation(d->getFile(), d->getChunk(), d->getValue(), + existing->getName()); + } else { + os << getSourceLocation(existing->getFile(), nullptr, 0, ""); + } + os << getSourceLocation(newFile, newSc, newSectionOffset, + existing->getName()); + if (config->forceMultiple) - warn(msg); + warn(os.str()); else - error(msg); + error(os.str()); } Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, sym); else if (!isa(s)) reportDuplicate(s, nullptr); return s; } Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, va); else if (!isa(s)) reportDuplicate(s, nullptr); return s; } Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, c); else if (!isa(s)) reportDuplicate(s, nullptr); return s; } Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, - const coff_symbol_generic *sym, - SectionChunk *c) { + const coff_symbol_generic *sym, SectionChunk *c, + uint32_t sectionOffset) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, f); if (wasInserted || !isa(s)) replaceSymbol(s, f, n, /*IsCOMDAT*/ false, /*IsExternal*/ true, sym, c); else - reportDuplicate(s, f); + reportDuplicate(s, f, c, sectionOffset); return s; } std::pair SymbolTable::addComdat(InputFile *f, StringRef n, const coff_symbol_generic *sym) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, f); if (wasInserted || !isa(s)) { replaceSymbol(s, f, n, /*IsCOMDAT*/ true, /*IsExternal*/ true, sym, nullptr); return {cast(s), true}; } auto *existingSymbol = cast(s); if (!existingSymbol->isCOMDAT) reportDuplicate(s, f); return {existingSymbol, false}; } Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, const coff_symbol_generic *sym, CommonChunk *c) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, f); if (wasInserted || !isa(s)) replaceSymbol(s, f, n, size, sym, c); else if (auto *dc = dyn_cast(s)) if (size > dc->getSize()) replaceSymbol(s, f, n, size, sym, c); return s; } Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) { + if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, n, f); return s; } reportDuplicate(s, f); return nullptr; } Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, uint16_t machine) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) { + if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, name, id, machine); return s; } reportDuplicate(s, id->file); return nullptr; +} + +void SymbolTable::addLibcall(StringRef name) { + Symbol *sym = findUnderscore(name); + if (!sym) + return; + + if (auto *l = dyn_cast(sym)) { + MemoryBufferRef mb = l->getMemberBuffer(); + if (isBitcode(mb)) + addUndefined(sym->getName()); + } else if (LazyObject *o = dyn_cast(sym)) { + if (isBitcode(o->file->mb)) + addUndefined(sym->getName()); + } } std::vector SymbolTable::getChunks() { std::vector res; for (ObjFile *file : ObjFile::instances) { ArrayRef v = file->getChunks(); res.insert(res.end(), v.begin(), v.end()); } return res; } Symbol *SymbolTable::find(StringRef name) { return symMap.lookup(CachedHashStringRef(name)); } Symbol *SymbolTable::findUnderscore(StringRef name) { if (config->machine == I386) return find(("_" + name).str()); return find(name); } // Return all symbols that start with Prefix, possibly ignoring the first // character of Prefix or the first character symbol. std::vector SymbolTable::getSymsWithPrefix(StringRef prefix) { std::vector syms; for (auto pair : symMap) { StringRef name = pair.first.val(); if (name.startswith(prefix) || name.startswith(prefix.drop_front()) || name.drop_front().startswith(prefix) || name.drop_front().startswith(prefix.drop_front())) { syms.push_back(pair.second); } } return syms; } Symbol *SymbolTable::findMangle(StringRef name) { if (Symbol *sym = find(name)) if (!isa(sym)) return sym; // Efficient fuzzy string lookup is impossible with a hash table, so iterate // the symbol table once and collect all possibly matching symbols into this // vector. Then compare each possibly matching symbol with each possible // mangling. std::vector syms = getSymsWithPrefix(name); auto findByPrefix = [&syms](const Twine &t) -> Symbol * { std::string prefix = t.str(); for (auto *s : syms) if (s->getName().startswith(prefix)) return s; return nullptr; }; // For non-x86, just look for C++ functions. if (config->machine != I386) return findByPrefix("?" + name + "@@Y"); if (!name.startswith("_")) return nullptr; // Search for x86 stdcall function. if (Symbol *s = findByPrefix(name + "@")) return s; // Search for x86 fastcall function. if (Symbol *s = findByPrefix("@" + name.substr(1) + "@")) return s; // Search for x86 vectorcall function. if (Symbol *s = findByPrefix(name.substr(1) + "@@")) return s; // Search for x86 C++ non-member function. return findByPrefix("?" + name.substr(1) + "@@Y"); } Symbol *SymbolTable::addUndefined(StringRef name) { return addUndefined(name, nullptr, false); } std::vector SymbolTable::compileBitcodeFiles() { lto.reset(new BitcodeCompiler); for (BitcodeFile *f : BitcodeFile::instances) lto->add(*f); return lto->compile(); } void SymbolTable::addCombinedLTOObjects() { if (BitcodeFile::instances.empty()) return; ScopedTimer t(ltoTimer); for (StringRef object : compileBitcodeFiles()) { auto *obj = make(MemoryBufferRef(object, "lto.tmp")); obj->parse(); ObjFile::instances.push_back(obj); } } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/SymbolTable.h =================================================================== --- vendor/lld/dist/COFF/SymbolTable.h (revision 353949) +++ vendor/lld/dist/COFF/SymbolTable.h (revision 353950) @@ -1,131 +1,141 @@ //===- SymbolTable.h --------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_SYMBOL_TABLE_H #define LLD_COFF_SYMBOL_TABLE_H #include "InputFiles.h" #include "LTO.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/raw_ostream.h" namespace llvm { struct LTOCodeGenerator; } namespace lld { namespace coff { class Chunk; class CommonChunk; class Defined; class DefinedAbsolute; class DefinedRegular; class DefinedRelative; -class Lazy; +class LazyArchive; class SectionChunk; class Symbol; // SymbolTable is a bucket of all known symbols, including defined, // undefined, or lazy symbols (the last one is symbols in archive // files whose archive members are not yet loaded). // // We put all symbols of all files to a SymbolTable, and the // SymbolTable selects the "best" symbols if there are name // conflicts. For example, obviously, a defined symbol is better than // an undefined symbol. Or, if there's a conflict between a lazy and a // undefined, it'll read an archive member to read a real definition // to replace the lazy symbol. The logic is implemented in the // add*() functions, which are called by input files as they are parsed. // There is one add* function per symbol type. class SymbolTable { public: void addFile(InputFile *file); + // Emit errors for symbols that cannot be resolved. + void reportUnresolvable(); + // Try to resolve any undefined symbols and update the symbol table // accordingly, then print an error message for any remaining undefined - // symbols. - void reportRemainingUndefines(); + // symbols and warn about imported local symbols. + void resolveRemainingUndefines(); void loadMinGWAutomaticImports(); bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); // Returns a list of chunks of selected symbols. std::vector getChunks(); // Returns a symbol for a given name. Returns a nullptr if not found. Symbol *find(StringRef name); Symbol *findUnderscore(StringRef name); // Occasionally we have to resolve an undefined symbol to its // mangled symbol. This function tries to find a mangled name // for U from the symbol table, and if found, set the symbol as // a weak alias for U. Symbol *findMangle(StringRef name); // Build a set of COFF objects representing the combined contents of // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. void addCombinedLTOObjects(); std::vector compileBitcodeFiles(); // Creates an Undefined symbol for a given name. Symbol *addUndefined(StringRef name); Symbol *addSynthetic(StringRef n, Chunk *c); Symbol *addAbsolute(StringRef n, uint64_t va); Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); - void addLazy(ArchiveFile *f, const Archive::Symbol sym); + void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); + void addLazyObject(LazyObjFile *f, StringRef n); Symbol *addAbsolute(StringRef n, COFFSymbolRef s); Symbol *addRegular(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr, - SectionChunk *c = nullptr); + SectionChunk *c = nullptr, uint32_t sectionOffset = 0); std::pair addComdat(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr); Symbol *addCommon(InputFile *f, StringRef n, uint64_t size, const llvm::object::coff_symbol_generic *s = nullptr, CommonChunk *c = nullptr); Symbol *addImportData(StringRef n, ImportFile *f); Symbol *addImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); + void addLibcall(StringRef name); - void reportDuplicate(Symbol *existing, InputFile *newFile); + void reportDuplicate(Symbol *existing, InputFile *newFile, + SectionChunk *newSc = nullptr, + uint32_t newSectionOffset = 0); // A list of chunks which to be added to .rdata. std::vector localImportChunks; // Iterates symbols in non-determinstic hash table order. template void forEachSymbol(T callback) { for (auto &pair : symMap) callback(pair.second); } private: + /// Given a name without "__imp_" prefix, returns a defined symbol + /// with the "__imp_" prefix, if it exists. + Defined *impSymbol(StringRef name); /// Inserts symbol if not already present. std::pair insert(StringRef name); /// Same as insert(Name), but also sets isUsedInRegularObj. std::pair insert(StringRef name, InputFile *f); std::vector getSymsWithPrefix(StringRef prefix); llvm::DenseMap symMap; std::unique_ptr lto; }; extern SymbolTable *symtab; std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/Symbols.cpp =================================================================== --- vendor/lld/dist/COFF/Symbols.cpp (revision 353949) +++ vendor/lld/dist/COFF/Symbols.cpp (revision 353950) @@ -1,117 +1,146 @@ //===- Symbols.cpp --------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Symbols.h" #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; using namespace lld::coff; +namespace lld { + static_assert(sizeof(SymbolUnion) <= 48, "symbols should be optimized for memory usage"); // Returns a symbol name for an error message. -std::string lld::toString(coff::Symbol &b) { - if (config->demangle) - if (Optional s = lld::demangleMSVC(b.getName())) - return *s; - return b.getName(); +static std::string maybeDemangleSymbol(StringRef symName) { + if (config->demangle) { + std::string prefix; + StringRef prefixless = symName; + if (prefixless.consume_front("__imp_")) + prefix = "__declspec(dllimport) "; + StringRef demangleInput = prefixless; + if (config->machine == I386) + demangleInput.consume_front("_"); + std::string demangled = demangle(demangleInput); + if (demangled != demangleInput) + return prefix + demangle(demangleInput); + return (prefix + prefixless).str(); + } + return symName; } +std::string toString(coff::Symbol &b) { + return maybeDemangleSymbol(b.getName()); +} +std::string toCOFFString(const Archive::Symbol &b) { + return maybeDemangleSymbol(b.getName()); +} -namespace lld { namespace coff { StringRef Symbol::getName() { // COFF symbol names are read lazily for a performance reason. // Non-external symbol names are never used by the linker except for logging // or debugging. Their internal references are resolved not by name but by // symbol index. And because they are not external, no one can refer them by // name. Object files contain lots of non-external symbols, and creating // StringRefs for them (which involves lots of strlen() on the string table) // is a waste of time. if (nameData == nullptr) { auto *d = cast(this); StringRef nameStr; cast(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr); nameData = nameStr.data(); nameSize = nameStr.size(); assert(nameSize == nameStr.size() && "name length truncated"); } return StringRef(nameData, nameSize); } InputFile *Symbol::getFile() { if (auto *sym = dyn_cast(this)) return sym->file; - if (auto *sym = dyn_cast(this)) + if (auto *sym = dyn_cast(this)) return sym->file; + if (auto *sym = dyn_cast(this)) + return sym->file; return nullptr; } bool Symbol::isLive() const { if (auto *r = dyn_cast(this)) return r->getChunk()->live; if (auto *imp = dyn_cast(this)) return imp->file->live; if (auto *imp = dyn_cast(this)) return imp->wrappedSym->file->thunkLive; // Assume any other kind of symbol is live. return true; } // MinGW specific. void Symbol::replaceKeepingName(Symbol *other, size_t size) { StringRef origName = getName(); memcpy(this, other, size); nameData = origName.data(); nameSize = origName.size(); } COFFSymbolRef DefinedCOFF::getCOFFSymbol() { size_t symSize = cast(file)->getCOFFObj()->getSymbolTableEntrySize(); if (symSize == sizeof(coff_symbol16)) return COFFSymbolRef(reinterpret_cast(sym)); assert(symSize == sizeof(coff_symbol32)); return COFFSymbolRef(reinterpret_cast(sym)); } uint16_t DefinedAbsolute::numOutputSections; static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { if (machine == AMD64) return make(s); if (machine == I386) return make(s); if (machine == ARM64) return make(s); assert(machine == ARMNT); return make(s); } DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine) : Defined(DefinedImportThunkKind, name), wrappedSym(s), data(makeImportThunk(s, machine)) {} Defined *Undefined::getWeakAlias() { // A weak alias may be a weak alias to another symbol, so check recursively. for (Symbol *a = weakAlias; a; a = cast(a)->weakAlias) if (auto *d = dyn_cast(a)) return d; return nullptr; +} + +MemoryBufferRef LazyArchive::getMemberBuffer() { + Archive::Child c = + CHECK(sym.getMember(), + "could not get the member for symbol " + toCOFFString(sym)); + return CHECK(c.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + toCOFFString(sym)); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Symbols.h =================================================================== --- vendor/lld/dist/COFF/Symbols.h (revision 353949) +++ vendor/lld/dist/COFF/Symbols.h (revision 353950) @@ -1,435 +1,455 @@ //===- Symbols.h ------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_SYMBOLS_H #define LLD_COFF_SYMBOLS_H #include "Chunks.h" #include "Config.h" #include "lld/Common/LLVM.h" #include "lld/Common/Memory.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include #include #include namespace lld { + +std::string toString(coff::Symbol &b); + +// There are two different ways to convert an Archive::Symbol to a string: +// One for Microsoft name mangling and one for Itanium name mangling. +// Call the functions toCOFFString and toELFString, not just toString. +std::string toCOFFString(const coff::Archive::Symbol &b); + namespace coff { using llvm::object::Archive; using llvm::object::COFFSymbolRef; using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; class ArchiveFile; class InputFile; class ObjFile; class SymbolTable; // The base class for real symbol classes. class Symbol { public: enum Kind { // The order of these is significant. We start with the regular defined // symbols as those are the most prevalent and the zero tag is the cheapest // to set. Among the defined kinds, the lower the kind is preferred over // the higher kind when testing whether one symbol should take precedence // over another. DefinedRegularKind = 0, DefinedCommonKind, DefinedLocalImportKind, DefinedImportThunkKind, DefinedImportDataKind, DefinedAbsoluteKind, DefinedSyntheticKind, UndefinedKind, - LazyKind, + LazyArchiveKind, + LazyObjectKind, LastDefinedCOFFKind = DefinedCommonKind, LastDefinedKind = DefinedSyntheticKind, }; Kind kind() const { return static_cast(symbolKind); } // Returns the symbol name. StringRef getName(); void replaceKeepingName(Symbol *other, size_t size); // Returns the file from which this symbol was created. InputFile *getFile(); // Indicates that this symbol will be included in the final image. Only valid // after calling markLive. bool isLive() const; + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } + protected: friend SymbolTable; explicit Symbol(Kind k, StringRef n = "") : symbolKind(k), isExternal(true), isCOMDAT(false), writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false), isRuntimePseudoReloc(false), nameSize(n.size()), nameData(n.empty() ? nullptr : n.data()) {} const unsigned symbolKind : 8; unsigned isExternal : 1; public: // This bit is used by the \c DefinedRegular subclass. unsigned isCOMDAT : 1; // This bit is used by Writer::createSymbolAndStringTable() to prevent // symbols from being written to the symbol table more than once. unsigned writtenToSymtab : 1; // True if this symbol was referenced by a regular (non-bitcode) object. unsigned isUsedInRegularObj : 1; // True if we've seen both a lazy and an undefined symbol with this symbol // name, which means that we have enqueued an archive member load and should // not load any more archive members to resolve the same symbol. unsigned pendingArchiveLoad : 1; /// True if we've already added this symbol to the list of GC roots. unsigned isGCRoot : 1; unsigned isRuntimePseudoReloc : 1; protected: // Symbol name length. Assume symbol lengths fit in a 32-bit integer. uint32_t nameSize; const char *nameData; }; // The base class for any defined symbols, including absolute symbols, // etc. class Defined : public Symbol { public: Defined(Kind k, StringRef n) : Symbol(k, n) {} static bool classof(const Symbol *s) { return s->kind() <= LastDefinedKind; } // Returns the RVA (relative virtual address) of this symbol. The // writer sets and uses RVAs. uint64_t getRVA(); // Returns the chunk containing this symbol. Absolute symbols and __ImageBase // do not have chunks, so this may return null. Chunk *getChunk(); }; // Symbols defined via a COFF object file or bitcode file. For COFF files, this // stores a coff_symbol_generic*, and names of internal symbols are lazily // loaded through that. For bitcode files, Sym is nullptr and the name is stored // as a decomposed StringRef. class DefinedCOFF : public Defined { friend Symbol; public: DefinedCOFF(Kind k, InputFile *f, StringRef n, const coff_symbol_generic *s) : Defined(k, n), file(f), sym(s) {} static bool classof(const Symbol *s) { return s->kind() <= LastDefinedCOFFKind; } InputFile *getFile() { return file; } COFFSymbolRef getCOFFSymbol(); InputFile *file; protected: const coff_symbol_generic *sym; }; // Regular defined symbols read from object file symbol tables. class DefinedRegular : public DefinedCOFF { public: DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT, bool isExternal = false, const coff_symbol_generic *s = nullptr, SectionChunk *c = nullptr) : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) { this->isExternal = isExternal; this->isCOMDAT = isCOMDAT; } static bool classof(const Symbol *s) { return s->kind() == DefinedRegularKind; } uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; } SectionChunk *getChunk() const { return *data; } uint32_t getValue() const { return sym->Value; } SectionChunk **data; }; class DefinedCommon : public DefinedCOFF { public: DefinedCommon(InputFile *f, StringRef n, uint64_t size, const coff_symbol_generic *s = nullptr, CommonChunk *c = nullptr) : DefinedCOFF(DefinedCommonKind, f, n, s), data(c), size(size) { this->isExternal = true; } static bool classof(const Symbol *s) { return s->kind() == DefinedCommonKind; } uint64_t getRVA() { return data->getRVA(); } CommonChunk *getChunk() { return data; } private: friend SymbolTable; uint64_t getSize() const { return size; } CommonChunk *data; uint64_t size; }; // Absolute symbols. class DefinedAbsolute : public Defined { public: DefinedAbsolute(StringRef n, COFFSymbolRef s) : Defined(DefinedAbsoluteKind, n), va(s.getValue()) { isExternal = s.isExternal(); } DefinedAbsolute(StringRef n, uint64_t v) : Defined(DefinedAbsoluteKind, n), va(v) {} static bool classof(const Symbol *s) { return s->kind() == DefinedAbsoluteKind; } uint64_t getRVA() { return va - config->imageBase; } void setVA(uint64_t v) { va = v; } // Section index relocations against absolute symbols resolve to // this 16 bit number, and it is the largest valid section index // plus one. This variable keeps it. static uint16_t numOutputSections; private: uint64_t va; }; // This symbol is used for linker-synthesized symbols like __ImageBase and // __safe_se_handler_table. class DefinedSynthetic : public Defined { public: explicit DefinedSynthetic(StringRef name, Chunk *c) : Defined(DefinedSyntheticKind, name), c(c) {} static bool classof(const Symbol *s) { return s->kind() == DefinedSyntheticKind; } // A null chunk indicates that this is __ImageBase. Otherwise, this is some // other synthesized chunk, like SEHTableChunk. uint32_t getRVA() { return c ? c->getRVA() : 0; } Chunk *getChunk() { return c; } private: Chunk *c; }; // This class represents a symbol defined in an archive file. It is // created from an archive file header, and it knows how to load an // object file from an archive to replace itself with a defined -// symbol. If the resolver finds both Undefined and Lazy for -// the same name, it will ask the Lazy to load a file. -class Lazy : public Symbol { +// symbol. If the resolver finds both Undefined and LazyArchive for +// the same name, it will ask the LazyArchive to load a file. +class LazyArchive : public Symbol { public: - Lazy(ArchiveFile *f, const Archive::Symbol s) - : Symbol(LazyKind, s.getName()), file(f), sym(s) {} + LazyArchive(ArchiveFile *f, const Archive::Symbol s) + : Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {} - static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; } - ArchiveFile *file; + MemoryBufferRef getMemberBuffer(); -private: - friend SymbolTable; - -private: + ArchiveFile *file; const Archive::Symbol sym; }; +class LazyObject : public Symbol { +public: + LazyObject(LazyObjFile *f, StringRef n) + : Symbol(LazyObjectKind, n), file(f) {} + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } + LazyObjFile *file; +}; + // Undefined symbols. class Undefined : public Symbol { public: explicit Undefined(StringRef n) : Symbol(UndefinedKind, n) {} static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; } // An undefined symbol can have a fallback symbol which gives an // undefined symbol a second chance if it would remain undefined. // If it remains undefined, it'll be replaced with whatever the // Alias pointer points to. Symbol *weakAlias = nullptr; // If this symbol is external weak, try to resolve it to a defined // symbol by searching the chain of fallback symbols. Returns the symbol if // successful, otherwise returns null. Defined *getWeakAlias(); }; // Windows-specific classes. // This class represents a symbol imported from a DLL. This has two // names for internal use and external use. The former is used for // name resolution, and the latter is used for the import descriptor // table in an output. The former has "__imp_" prefix. class DefinedImportData : public Defined { public: DefinedImportData(StringRef n, ImportFile *f) : Defined(DefinedImportDataKind, n), file(f) { } static bool classof(const Symbol *s) { return s->kind() == DefinedImportDataKind; } uint64_t getRVA() { return file->location->getRVA(); } Chunk *getChunk() { return file->location; } void setLocation(Chunk *addressTable) { file->location = addressTable; } StringRef getDLLName() { return file->dllName; } StringRef getExternalName() { return file->externalName; } uint16_t getOrdinal() { return file->hdr->OrdinalHint; } ImportFile *file; }; // This class represents a symbol for a jump table entry which jumps // to a function in a DLL. Linker are supposed to create such symbols // without "__imp_" prefix for all function symbols exported from // DLLs, so that you can call DLL functions as regular functions with // a regular name. A function pointer is given as a DefinedImportData. class DefinedImportThunk : public Defined { public: DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); static bool classof(const Symbol *s) { return s->kind() == DefinedImportThunkKind; } uint64_t getRVA() { return data->getRVA(); } Chunk *getChunk() { return data; } DefinedImportData *wrappedSym; private: Chunk *data; }; // If you have a symbol "foo" in your object file, a symbol name // "__imp_foo" becomes automatically available as a pointer to "foo". // This class is for such automatically-created symbols. // Yes, this is an odd feature. We didn't intend to implement that. // This is here just for compatibility with MSVC. class DefinedLocalImport : public Defined { public: DefinedLocalImport(StringRef n, Defined *s) : Defined(DefinedLocalImportKind, n), data(make(s)) {} static bool classof(const Symbol *s) { return s->kind() == DefinedLocalImportKind; } uint64_t getRVA() { return data->getRVA(); } Chunk *getChunk() { return data; } private: LocalImportChunk *data; }; inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: return cast(this)->getRVA(); case DefinedSyntheticKind: return cast(this)->getRVA(); case DefinedImportDataKind: return cast(this)->getRVA(); case DefinedImportThunkKind: return cast(this)->getRVA(); case DefinedLocalImportKind: return cast(this)->getRVA(); case DefinedCommonKind: return cast(this)->getRVA(); case DefinedRegularKind: return cast(this)->getRVA(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); } llvm_unreachable("unknown symbol kind"); } inline Chunk *Defined::getChunk() { switch (kind()) { case DefinedRegularKind: return cast(this)->getChunk(); case DefinedAbsoluteKind: return nullptr; case DefinedSyntheticKind: return cast(this)->getChunk(); case DefinedImportDataKind: return cast(this)->getChunk(); case DefinedImportThunkKind: return cast(this)->getChunk(); case DefinedLocalImportKind: return cast(this)->getChunk(); case DefinedCommonKind: return cast(this)->getChunk(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the chunk of an undefined symbol."); } llvm_unreachable("unknown symbol kind"); } // A buffer class that is large enough to hold any Symbol-derived // object. We allocate memory using this class and instantiate a symbol // using the placement new. union SymbolUnion { alignas(DefinedRegular) char a[sizeof(DefinedRegular)]; alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; - alignas(Lazy) char e[sizeof(Lazy)]; + alignas(LazyArchive) char e[sizeof(LazyArchive)]; alignas(Undefined) char f[sizeof(Undefined)]; alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; + alignas(LazyObject) char j[sizeof(LazyObject)]; }; template void replaceSymbol(Symbol *s, ArgT &&... arg) { static_assert(std::is_trivially_destructible(), "Symbol types must be trivially destructible"); static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small"); static_assert(alignof(T) <= alignof(SymbolUnion), "SymbolUnion not aligned enough"); assert(static_cast(static_cast(nullptr)) == nullptr && "Not a Symbol"); new (s) T(std::forward(arg)...); } } // namespace coff -std::string toString(coff::Symbol &b); } // namespace lld #endif Index: vendor/lld/dist/COFF/Writer.cpp =================================================================== --- vendor/lld/dist/COFF/Writer.cpp (revision 353949) +++ vendor/lld/dist/COFF/Writer.cpp (revision 353950) @@ -1,1901 +1,1945 @@ //===- Writer.cpp ---------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Writer.h" #include "Config.h" #include "DLL.h" #include "InputFiles.h" #include "MapFile.h" #include "PDB.h" #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/xxhash.h" #include #include #include #include #include using namespace llvm; using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; -using namespace lld; -using namespace lld::coff; +namespace lld { +namespace coff { + /* To re-generate DOSProgram: $ cat > /tmp/DOSProgram.asm org 0 ; Copy cs to ds. push cs pop ds ; Point ds:dx at the $-terminated string. mov dx, str ; Int 21/AH=09h: Write string to standard output. mov ah, 0x9 int 0x21 ; Int 21/AH=4Ch: Exit with return code (in AL). mov ax, 0x4C01 int 0x21 str: db 'This program cannot be run in DOS mode.$' align 8, db 0 $ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin $ xxd -i /tmp/DOSProgram.bin */ static unsigned char dosProgram[] = { 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 }; static_assert(sizeof(dosProgram) % 8 == 0, "DOSProgram size must be multiple of 8"); static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram); static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8"); static const int numberOfDataDirectory = 16; // Global vector of all output sections. After output sections are finalized, // this can be indexed by Chunk::getOutputSection. static std::vector outputSections; OutputSection *Chunk::getOutputSection() const { return osidx == 0 ? nullptr : outputSections[osidx - 1]; } namespace { class DebugDirectoryChunk : public NonSectionChunk { public: DebugDirectoryChunk(const std::vector &r, bool writeRepro) : records(r), writeRepro(writeRepro) {} size_t getSize() const override { return (records.size() + int(writeRepro)) * sizeof(debug_directory); } void writeTo(uint8_t *b) const override { auto *d = reinterpret_cast(b); for (const Chunk *record : records) { OutputSection *os = record->getOutputSection(); uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA()); fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(), record->getRVA(), offs); ++d; } if (writeRepro) { // FIXME: The COFF spec allows either a 0-sized entry to just say // "the timestamp field is really a hash", or a 4-byte size field // followed by that many bytes containing a longer hash (with the // lowest 4 bytes usually being the timestamp in little-endian order). // Consider storing the full 8 bytes computed by xxHash64 here. fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); } } void setTimeDateStamp(uint32_t timeDateStamp) { for (support::ulittle32_t *tds : timeDateStamps) *tds = timeDateStamp; } private: void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size, uint64_t rva, uint64_t offs) const { d->Characteristics = 0; d->TimeDateStamp = 0; d->MajorVersion = 0; d->MinorVersion = 0; d->Type = debugType; d->SizeOfData = size; d->AddressOfRawData = rva; d->PointerToRawData = offs; timeDateStamps.push_back(&d->TimeDateStamp); } mutable std::vector timeDateStamps; const std::vector &records; bool writeRepro; }; class CVDebugRecordChunk : public NonSectionChunk { public: size_t getSize() const override { return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1; } void writeTo(uint8_t *b) const override { // Save off the DebugInfo entry to backfill the file signature (build id) // in Writer::writeBuildId buildId = reinterpret_cast(b); // variable sized field (PDB Path) char *p = reinterpret_cast(b + sizeof(*buildId)); if (!config->pdbAltPath.empty()) memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size()); p[config->pdbAltPath.size()] = '\0'; } mutable codeview::DebugInfo *buildId = nullptr; }; // PartialSection represents a group of chunks that contribute to an // OutputSection. Collating a collection of PartialSections of same name and // characteristics constitutes the OutputSection. class PartialSectionKey { public: StringRef name; unsigned characteristics; bool operator<(const PartialSectionKey &other) const { int c = name.compare(other.name); if (c == 1) return false; if (c == 0) return characteristics < other.characteristics; return true; } }; // The writer writes a SymbolTable result to a file. class Writer { public: Writer() : buffer(errorHandler().outputBuffer) {} void run(); private: void createSections(); void createMiscChunks(); void createImportTables(); void appendImportThunks(); void locateImportTables(); void createExportTable(); void mergeSections(); void removeUnusedSections(); void assignAddresses(); void finalizeAddresses(); void removeEmptySections(); void assignOutputSectionIndices(); void createSymbolAndStringTable(); void openFile(StringRef outputPath); template void writeHeader(); void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); void createGuardCFTables(); void markSymbolsForRVATable(ObjFile *file, ArrayRef symIdxChunks, SymbolRVASet &tableSymbols); void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, StringRef countSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); void sortExceptionTable(); void sortCRTSectionChunks(std::vector &chunks); void addSyntheticIdata(); void fixPartialSectionChars(StringRef name, uint32_t chars); bool fixGnuImportChunks(); PartialSection *createPartialSection(StringRef name, uint32_t outChars); PartialSection *findPartialSection(StringRef name, uint32_t outChars); llvm::Optional createSymbol(Defined *d); size_t addEntryToStringTable(StringRef str); OutputSection *findSection(StringRef name); void addBaserels(); void addBaserelBlocks(std::vector &v); uint32_t getSizeOfInitializedData(); std::unique_ptr &buffer; std::map partialSections; std::vector strtab; std::vector outputSymtab; IdataContents idata; Chunk *importTableStart = nullptr; uint64_t importTableSize = 0; + Chunk *edataStart = nullptr; + Chunk *edataEnd = nullptr; Chunk *iatStart = nullptr; uint64_t iatSize = 0; DelayLoadContents delayIdata; EdataContents edata; bool setNoSEHCharacteristic = false; DebugDirectoryChunk *debugDirectory = nullptr; std::vector debugRecords; CVDebugRecordChunk *buildId = nullptr; ArrayRef sectionTable; uint64_t fileSize; uint32_t pointerToSymbolTable = 0; uint64_t sizeOfImage; uint64_t sizeOfHeaders; OutputSection *textSec; OutputSection *rdataSec; OutputSection *buildidSec; OutputSection *dataSec; OutputSection *pdataSec; OutputSection *idataSec; OutputSection *edataSec; OutputSection *didatSec; OutputSection *rsrcSec; OutputSection *relocSec; OutputSection *ctorsSec; OutputSection *dtorsSec; // The first and last .pdata sections in the output file. // // We need to keep track of the location of .pdata in whichever section it // gets merged into so that we can sort its contents and emit a correct data // directory entry for the exception table. This is also the case for some // other sections (such as .edata) but because the contents of those sections // are entirely linker-generated we can keep track of their locations using // the chunks that the linker creates. All .pdata chunks come from input // files, so we need to keep track of them separately. Chunk *firstPdata = nullptr; Chunk *lastPdata; }; } // anonymous namespace -namespace lld { -namespace coff { - static Timer codeLayoutTimer("Code Layout", Timer::root()); static Timer diskCommitTimer("Commit Output File", Timer::root()); void writeResult() { Writer().run(); } void OutputSection::addChunk(Chunk *c) { chunks.push_back(c); } void OutputSection::insertChunkAtStart(Chunk *c) { chunks.insert(chunks.begin(), c); } void OutputSection::setPermissions(uint32_t c) { header.Characteristics &= ~permMask; header.Characteristics |= c; } void OutputSection::merge(OutputSection *other) { chunks.insert(chunks.end(), other->chunks.begin(), other->chunks.end()); other->chunks.clear(); contribSections.insert(contribSections.end(), other->contribSections.begin(), other->contribSections.end()); other->contribSections.clear(); } // Write the section header to a given buffer. void OutputSection::writeHeaderTo(uint8_t *buf) { auto *hdr = reinterpret_cast(buf); *hdr = header; if (stringTableOff) { // If name is too long, write offset into the string table as a name. sprintf(hdr->Name, "/%d", stringTableOff); } else { assert(!config->debug || name.size() <= COFF::NameSize || (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); strncpy(hdr->Name, name.data(), std::min(name.size(), (size_t)COFF::NameSize)); } } void OutputSection::addContributingPartialSection(PartialSection *sec) { contribSections.push_back(sec); } -} // namespace coff -} // namespace lld - // Check whether the target address S is in range from a relocation // of type relType at address P. static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { if (config->machine == ARMNT) { int64_t diff = AbsoluteDifference(s, p + 4) + margin; switch (relType) { case IMAGE_REL_ARM_BRANCH20T: return isInt<21>(diff); case IMAGE_REL_ARM_BRANCH24T: case IMAGE_REL_ARM_BLX23T: return isInt<25>(diff); default: return true; } } else if (config->machine == ARM64) { int64_t diff = AbsoluteDifference(s, p) + margin; switch (relType) { case IMAGE_REL_ARM64_BRANCH26: return isInt<28>(diff); case IMAGE_REL_ARM64_BRANCH19: return isInt<21>(diff); case IMAGE_REL_ARM64_BRANCH14: return isInt<16>(diff); default: return true; } } else { llvm_unreachable("Unexpected architecture"); } } // Return the last thunk for the given target if it is in range, // or create a new one. static std::pair getThunk(DenseMap &lastThunks, Defined *target, uint64_t p, uint16_t type, int margin) { Defined *&lastThunk = lastThunks[target->getRVA()]; if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin)) return {lastThunk, false}; Chunk *c; switch (config->machine) { case ARMNT: c = make(target); break; case ARM64: c = make(target); break; default: llvm_unreachable("Unexpected architecture"); } Defined *d = make("", c); lastThunk = d; return {d, true}; } // This checks all relocations, and for any relocation which isn't in range // it adds a thunk after the section chunk that contains the relocation. // If the latest thunk for the specific target is in range, that is used // instead of creating a new thunk. All range checks are done with the // specified margin, to make sure that relocations that originally are in // range, but only barely, also get thunks - in case other added thunks makes // the target go out of range. // // After adding thunks, we verify that all relocations are in range (with // no extra margin requirements). If this failed, we restart (throwing away // the previously created thunks) and retry with a wider margin. static bool createThunks(OutputSection *os, int margin) { bool addressesChanged = false; DenseMap lastThunks; DenseMap, uint32_t> thunkSymtabIndices; size_t thunksSize = 0; // Recheck Chunks.size() each iteration, since we can insert more // elements into it. for (size_t i = 0; i != os->chunks.size(); ++i) { SectionChunk *sc = dyn_cast_or_null(os->chunks[i]); if (!sc) continue; size_t thunkInsertionSpot = i + 1; // Try to get a good enough estimate of where new thunks will be placed. // Offset this by the size of the new thunks added so far, to make the // estimate slightly better. size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize; ObjFile *file = sc->file; std::vector> relocReplacements; ArrayRef originalRelocs = file->getCOFFObj()->getRelocations(sc->header); for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) { const coff_relocation &rel = originalRelocs[j]; Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex); // The estimate of the source address P should be pretty accurate, // but we don't know whether the target Symbol address should be // offset by thunksSize or not (or by some of thunksSize but not all of // it), giving us some uncertainty once we have added one thunk. uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize; Defined *sym = dyn_cast_or_null(relocTarget); if (!sym) continue; uint64_t s = sym->getRVA(); if (isInRange(rel.Type, s, p, margin)) continue; // If the target isn't in range, hook it up to an existing or new // thunk. Defined *thunk; bool wasNew; std::tie(thunk, wasNew) = getThunk(lastThunks, sym, p, rel.Type, margin); if (wasNew) { Chunk *thunkChunk = thunk->getChunk(); thunkChunk->setRVA( thunkInsertionRVA); // Estimate of where it will be located. os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk); thunkInsertionSpot++; thunksSize += thunkChunk->getSize(); thunkInsertionRVA += thunkChunk->getSize(); addressesChanged = true; } // To redirect the relocation, add a symbol to the parent object file's // symbol table, and replace the relocation symbol table index with the // new index. auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U}); uint32_t &thunkSymbolIndex = insertion.first->second; if (insertion.second) thunkSymbolIndex = file->addRangeThunkSymbol(thunk); relocReplacements.push_back({j, thunkSymbolIndex}); } // Get a writable copy of this section's relocations so they can be // modified. If the relocations point into the object file, allocate new // memory. Otherwise, this must be previously allocated memory that can be // modified in place. ArrayRef curRelocs = sc->getRelocs(); MutableArrayRef newRelocs; if (originalRelocs.data() == curRelocs.data()) { newRelocs = makeMutableArrayRef( bAlloc.Allocate(originalRelocs.size()), originalRelocs.size()); } else { newRelocs = makeMutableArrayRef( const_cast(curRelocs.data()), curRelocs.size()); } // Copy each relocation, but replace the symbol table indices which need // thunks. auto nextReplacement = relocReplacements.begin(); auto endReplacement = relocReplacements.end(); for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) { newRelocs[i] = originalRelocs[i]; if (nextReplacement != endReplacement && nextReplacement->first == i) { newRelocs[i].SymbolTableIndex = nextReplacement->second; ++nextReplacement; } } sc->setRelocs(newRelocs); } return addressesChanged; } // Verify that all relocations are in range, with no extra margin requirements. static bool verifyRanges(const std::vector chunks) { for (Chunk *c : chunks) { SectionChunk *sc = dyn_cast_or_null(c); if (!sc) continue; ArrayRef relocs = sc->getRelocs(); for (size_t j = 0, e = relocs.size(); j < e; ++j) { const coff_relocation &rel = relocs[j]; Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex); Defined *sym = dyn_cast_or_null(relocTarget); if (!sym) continue; uint64_t p = sc->getRVA() + rel.VirtualAddress; uint64_t s = sym->getRVA(); if (!isInRange(rel.Type, s, p, 0)) return false; } } return true; } // Assign addresses and add thunks if necessary. void Writer::finalizeAddresses() { assignAddresses(); if (config->machine != ARMNT && config->machine != ARM64) return; size_t origNumChunks = 0; for (OutputSection *sec : outputSections) { sec->origChunks = sec->chunks; origNumChunks += sec->chunks.size(); } int pass = 0; int margin = 1024 * 100; while (true) { // First check whether we need thunks at all, or if the previous pass of // adding them turned out ok. bool rangesOk = true; size_t numChunks = 0; for (OutputSection *sec : outputSections) { if (!verifyRanges(sec->chunks)) { rangesOk = false; break; } numChunks += sec->chunks.size(); } if (rangesOk) { if (pass > 0) log("Added " + Twine(numChunks - origNumChunks) + " thunks with " + "margin " + Twine(margin) + " in " + Twine(pass) + " passes"); return; } if (pass >= 10) fatal("adding thunks hasn't converged after " + Twine(pass) + " passes"); if (pass > 0) { // If the previous pass didn't work out, reset everything back to the // original conditions before retrying with a wider margin. This should // ideally never happen under real circumstances. for (OutputSection *sec : outputSections) sec->chunks = sec->origChunks; margin *= 2; } // Try adding thunks everywhere where it is needed, with a margin // to avoid things going out of range due to the added thunks. bool addressesChanged = false; for (OutputSection *sec : outputSections) addressesChanged |= createThunks(sec, margin); // If the verification above thought we needed thunks, we should have // added some. assert(addressesChanged); // Recalculate the layout for the whole image (and verify the ranges at // the start of the next round). assignAddresses(); pass++; } } // The main function of the writer. void Writer::run() { ScopedTimer t1(codeLayoutTimer); createImportTables(); createSections(); createMiscChunks(); appendImportThunks(); createExportTable(); mergeSections(); removeUnusedSections(); finalizeAddresses(); removeEmptySections(); assignOutputSectionIndices(); setSectionPermissions(); createSymbolAndStringTable(); if (fileSize > UINT32_MAX) fatal("image size (" + Twine(fileSize) + ") " + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); openFile(config->outputFile); if (config->is64()) { writeHeader(); } else { writeHeader(); } writeSections(); sortExceptionTable(); t1.stop(); if (!config->pdbPath.empty() && config->debug) { assert(buildId); createPDB(symtab, outputSections, sectionTable, buildId->buildId); } writeBuildId(); writeMapFile(outputSections); + if (errorCount()) + return; + ScopedTimer t2(diskCommitTimer); if (auto e = buffer->commit()) fatal("failed to write the output file: " + toString(std::move(e))); } static StringRef getOutputSectionName(StringRef name) { StringRef s = name.split('$').first; // Treat a later period as a separator for MinGW, for sections like // ".ctors.01234". return s.substr(0, s.find('.', 1)); } // For /order. static void sortBySectionOrder(std::vector &chunks) { auto getPriority = [](const Chunk *c) { if (auto *sec = dyn_cast(c)) if (sec->sym) return config->order.lookup(sec->sym->getName()); return 0; }; llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) { return getPriority(a) < getPriority(b); }); } // Change the characteristics of existing PartialSections that belong to the // section Name to Chars. void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) { for (auto it : partialSections) { PartialSection *pSec = it.second; StringRef curName = pSec->name; if (!curName.consume_front(name) || (!curName.empty() && !curName.startswith("$"))) continue; if (pSec->characteristics == chars) continue; PartialSection *destSec = createPartialSection(pSec->name, chars); destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(), pSec->chunks.end()); pSec->chunks.clear(); } } // Sort concrete section chunks from GNU import libraries. // // GNU binutils doesn't use short import files, but instead produces import // libraries that consist of object files, with section chunks for the .idata$* // sections. These are linked just as regular static libraries. Each import // library consists of one header object, one object file for every imported // symbol, and one trailer object. In order for the .idata tables/lists to // be formed correctly, the section chunks within each .idata$* section need // to be grouped by library, and sorted alphabetically within each library // (which makes sure the header comes first and the trailer last). bool Writer::fixGnuImportChunks() { uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; // Make sure all .idata$* section chunks are mapped as RDATA in order to // be sorted into the same sections as our own synthesized .idata chunks. fixPartialSectionChars(".idata", rdata); bool hasIdata = false; // Sort all .idata$* chunks, grouping chunks from the same library, // with alphabetical ordering of the object fils within a library. for (auto it : partialSections) { PartialSection *pSec = it.second; if (!pSec->name.startswith(".idata")) continue; if (!pSec->chunks.empty()) hasIdata = true; llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) { SectionChunk *sc1 = dyn_cast_or_null(s); SectionChunk *sc2 = dyn_cast_or_null(t); if (!sc1 || !sc2) { // if SC1, order them ascending. If SC2 or both null, // S is not less than T. return sc1 != nullptr; } // Make a string with "libraryname/objectfile" for sorting, achieving // both grouping by library and sorting of objects within a library, // at once. std::string key1 = (sc1->file->parentName + "/" + sc1->file->getName()).str(); std::string key2 = (sc2->file->parentName + "/" + sc2->file->getName()).str(); return key1 < key2; }); } return hasIdata; } // Add generated idata chunks, for imported symbols and DLLs, and a // terminator in .idata$2. void Writer::addSyntheticIdata() { uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; idata.create(); // Add the .idata content in the right section groups, to allow // chunks from other linked in object files to be grouped together. // See Microsoft PE/COFF spec 5.4 for details. auto add = [&](StringRef n, std::vector &v) { PartialSection *pSec = createPartialSection(n, rdata); pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end()); }; // The loader assumes a specific order of data. // Add each type in the correct order. add(".idata$2", idata.dirs); add(".idata$4", idata.lookups); add(".idata$5", idata.addresses); - add(".idata$6", idata.hints); + if (!idata.hints.empty()) + add(".idata$6", idata.hints); add(".idata$7", idata.dllNames); } // Locate the first Chunk and size of the import directory list and the // IAT. void Writer::locateImportTables() { uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) { if (!importDirs->chunks.empty()) importTableStart = importDirs->chunks.front(); for (Chunk *c : importDirs->chunks) importTableSize += c->getSize(); } if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) { if (!importAddresses->chunks.empty()) iatStart = importAddresses->chunks.front(); for (Chunk *c : importAddresses->chunks) iatSize += c->getSize(); } } +// Return whether a SectionChunk's suffix (the dollar and any trailing +// suffix) should be removed and sorted into the main suffixless +// PartialSection. +static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { + // On MinGW, comdat groups are formed by putting the comdat group name + // after the '$' in the section name. For .eh_frame$, that must + // still be sorted before the .eh_frame trailer from crtend.o, thus just + // strip the section name trailer. For other sections, such as + // .tls$$ (where non-comdat .tls symbols are otherwise stored in + // ".tls$"), they must be strictly sorted after .tls. And for the + // hypothetical case of comdat .CRT$XCU, we definitely need to keep the + // suffix for sorting. Thus, to play it safe, only strip the suffix for + // the standard sections. + if (!config->mingw) + return false; + if (!sc || !sc->isCOMDAT()) + return false; + return name.startswith(".text$") || name.startswith(".data$") || + name.startswith(".rdata$") || name.startswith(".pdata$") || + name.startswith(".xdata$") || name.startswith(".eh_frame$"); +} + // Create output section objects and add them to OutputSections. void Writer::createSections() { // First, create the builtin sections. const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA; const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA; const uint32_t code = IMAGE_SCN_CNT_CODE; const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE; const uint32_t r = IMAGE_SCN_MEM_READ; const uint32_t w = IMAGE_SCN_MEM_WRITE; const uint32_t x = IMAGE_SCN_MEM_EXECUTE; SmallDenseMap, OutputSection *> sections; auto createSection = [&](StringRef name, uint32_t outChars) { OutputSection *&sec = sections[{name, outChars}]; if (!sec) { sec = make(name, outChars); outputSections.push_back(sec); } return sec; }; // Try to match the section order used by link.exe. textSec = createSection(".text", code | r | x); createSection(".bss", bss | r | w); rdataSec = createSection(".rdata", data | r); buildidSec = createSection(".buildid", data | r); dataSec = createSection(".data", data | r | w); pdataSec = createSection(".pdata", data | r); idataSec = createSection(".idata", data | r); edataSec = createSection(".edata", data | r); didatSec = createSection(".didat", data | r); rsrcSec = createSection(".rsrc", data | r); relocSec = createSection(".reloc", data | discardable | r); ctorsSec = createSection(".ctors", data | r | w); dtorsSec = createSection(".dtors", data | r | w); // Then bin chunks by name and output characteristics. for (Chunk *c : symtab->getChunks()) { auto *sc = dyn_cast(c); if (sc && !sc->live) { if (config->verbose) sc->printDiscardedMessage(); continue; } StringRef name = c->getSectionName(); - // On MinGW, comdat groups are formed by putting the comdat group name - // after the '$' in the section name. Such a section name suffix shouldn't - // imply separate alphabetical sorting of those section chunks though. - if (config->mingw && sc && sc->isCOMDAT()) + if (shouldStripSectionSuffix(sc, name)) name = name.split('$').first; PartialSection *pSec = createPartialSection(name, c->getOutputCharacteristics()); pSec->chunks.push_back(c); } fixPartialSectionChars(".rsrc", data | r); + fixPartialSectionChars(".edata", data | r); // Even in non MinGW cases, we might need to link against GNU import // libraries. bool hasIdata = fixGnuImportChunks(); if (!idata.empty()) hasIdata = true; if (hasIdata) addSyntheticIdata(); // Process an /order option. if (!config->order.empty()) for (auto it : partialSections) sortBySectionOrder(it.second->chunks); if (hasIdata) locateImportTables(); // Then create an OutputSection for each section. // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. for (auto it : partialSections) { PartialSection *pSec = it.second; StringRef name = getOutputSectionName(pSec->name); uint32_t outChars = pSec->characteristics; if (name == ".CRT") { // In link.exe, there is a special case for the I386 target where .CRT // sections are treated as if they have output characteristics DATA | R if // their characteristics are DATA | R | W. This implements the same // special case for all architectures. outChars = data | r; log("Processing section " + pSec->name + " -> " + name); sortCRTSectionChunks(pSec->chunks); } OutputSection *sec = createSection(name, outChars); for (Chunk *c : pSec->chunks) sec->addChunk(c); sec->addContributingPartialSection(pSec); } // Finally, move some output sections to the end. auto sectionOrder = [&](const OutputSection *s) { // Move DISCARDABLE (or non-memory-mapped) sections to the end of file // because the loader cannot handle holes. Stripping can remove other // discardable ones than .reloc, which is first of them (created early). if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) return 2; // .rsrc should come at the end of the non-discardable sections because its // size may change by the Win32 UpdateResources() function, causing // subsequent sections to move (see https://crbug.com/827082). if (s == rsrcSec) return 1; return 0; }; llvm::stable_sort(outputSections, [&](const OutputSection *s, const OutputSection *t) { return sectionOrder(s) < sectionOrder(t); }); } void Writer::createMiscChunks() { for (MergeChunk *p : MergeChunk::instances) { if (p) { p->finalizeContents(); rdataSec->addChunk(p); } } // Create thunks for locally-dllimported symbols. if (!symtab->localImportChunks.empty()) { for (Chunk *c : symtab->localImportChunks) rdataSec->addChunk(c); } // Create Debug Information Chunks OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; if (config->debug || config->repro) { debugDirectory = make(debugRecords, config->repro); debugInfoSec->addChunk(debugDirectory); } if (config->debug) { // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We // output a PDB no matter what, and this chunk provides the only means of // allowing a debugger to match a PDB and an executable. So we need it even // if we're ultimately not going to write CodeView data to the PDB. buildId = make(); debugRecords.push_back(buildId); for (Chunk *c : debugRecords) debugInfoSec->addChunk(c); } // Create SEH table. x86-only. if (config->safeSEH) createSEHTable(); // Create /guard:cf tables if requested. if (config->guardCF != GuardCFLevel::Off) createGuardCFTables(); if (config->mingw) { createRuntimePseudoRelocs(); insertCtorDtorSymbols(); } } // Create .idata section for the DLL-imported symbol table. // The format of this section is inherently Windows-specific. // IdataContents class abstracted away the details for us, // so we just let it create chunks and add them to the section. void Writer::createImportTables() { // Initialize DLLOrder so that import entries are ordered in // the same order as in the command line. (That affects DLL // initialization order, and this ordering is MSVC-compatible.) for (ImportFile *file : ImportFile::instances) { if (!file->live) continue; std::string dll = StringRef(file->dllName).lower(); if (config->dllOrder.count(dll) == 0) config->dllOrder[dll] = config->dllOrder.size(); if (file->impSym && !isa(file->impSym)) fatal(toString(*file->impSym) + " was replaced"); DefinedImportData *impSym = cast_or_null(file->impSym); if (config->delayLoads.count(StringRef(file->dllName).lower())) { if (!file->thunkSym) fatal("cannot delay-load " + toString(file) + " due to import of data: " + toString(*impSym)); delayIdata.add(impSym); } else { idata.add(impSym); } } } void Writer::appendImportThunks() { if (ImportFile::instances.empty()) return; for (ImportFile *file : ImportFile::instances) { if (!file->live) continue; if (!file->thunkSym) continue; if (!isa(file->thunkSym)) fatal(toString(*file->thunkSym) + " was replaced"); DefinedImportThunk *thunk = cast(file->thunkSym); if (file->thunkLive) textSec->addChunk(thunk->getChunk()); } if (!delayIdata.empty()) { Defined *helper = cast(config->delayLoadHelper); delayIdata.create(helper); for (Chunk *c : delayIdata.getChunks()) didatSec->addChunk(c); for (Chunk *c : delayIdata.getDataChunks()) dataSec->addChunk(c); for (Chunk *c : delayIdata.getCodeChunks()) textSec->addChunk(c); } } void Writer::createExportTable() { - if (config->exports.empty()) - return; - for (Chunk *c : edata.chunks) - edataSec->addChunk(c); + if (!edataSec->chunks.empty()) { + // Allow using a custom built export table from input object files, instead + // of having the linker synthesize the tables. + if (config->hadExplicitExports) + warn("literal .edata sections override exports"); + } else if (!config->exports.empty()) { + for (Chunk *c : edata.chunks) + edataSec->addChunk(c); + } + if (!edataSec->chunks.empty()) { + edataStart = edataSec->chunks.front(); + edataEnd = edataSec->chunks.back(); + } } void Writer::removeUnusedSections() { // Remove sections that we can be sure won't get content, to avoid // allocating space for their section headers. auto isUnused = [this](OutputSection *s) { if (s == relocSec) return false; // This section is populated later. // MergeChunks have zero size at this point, as their size is finalized // later. Only remove sections that have no Chunks at all. return s->chunks.empty(); }; outputSections.erase( std::remove_if(outputSections.begin(), outputSections.end(), isUnused), outputSections.end()); } // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; outputSections.erase( std::remove_if(outputSections.begin(), outputSections.end(), isEmpty), outputSections.end()); } void Writer::assignOutputSectionIndices() { // Assign final output section indices, and assign each chunk to its output // section. uint32_t idx = 1; for (OutputSection *os : outputSections) { os->sectionIndex = idx; for (Chunk *c : os->chunks) c->setOutputSectionIdx(idx); ++idx; } // Merge chunks are containers of chunks, so assign those an output section // too. for (MergeChunk *mc : MergeChunk::instances) if (mc) for (SectionChunk *sc : mc->sections) if (sc && sc->live) sc->setOutputSectionIdx(mc->getOutputSectionIdx()); } size_t Writer::addEntryToStringTable(StringRef str) { assert(str.size() > COFF::NameSize); size_t offsetOfEntry = strtab.size() + 4; // +4 for the size field strtab.insert(strtab.end(), str.begin(), str.end()); strtab.push_back('\0'); return offsetOfEntry; } Optional Writer::createSymbol(Defined *def) { coff_symbol16 sym; switch (def->kind()) { case Symbol::DefinedAbsoluteKind: sym.Value = def->getRVA(); sym.SectionNumber = IMAGE_SYM_ABSOLUTE; break; case Symbol::DefinedSyntheticKind: // Relative symbols are unrepresentable in a COFF symbol table. return None; default: { // Don't write symbols that won't be written to the output to the symbol // table. Chunk *c = def->getChunk(); if (!c) return None; OutputSection *os = c->getOutputSection(); if (!os) return None; sym.Value = def->getRVA() - os->getRVA(); sym.SectionNumber = os->sectionIndex; break; } } + // Symbols that are runtime pseudo relocations don't point to the actual + // symbol data itself (as they are imported), but points to the IAT entry + // instead. Avoid emitting them to the symbol table, as they can confuse + // debuggers. + if (def->isRuntimePseudoReloc) + return None; + StringRef name = def->getName(); if (name.size() > COFF::NameSize) { sym.Name.Offset.Zeroes = 0; sym.Name.Offset.Offset = addEntryToStringTable(name); } else { memset(sym.Name.ShortName, 0, COFF::NameSize); memcpy(sym.Name.ShortName, name.data(), name.size()); } if (auto *d = dyn_cast(def)) { COFFSymbolRef ref = d->getCOFFSymbol(); sym.Type = ref.getType(); sym.StorageClass = ref.getStorageClass(); } else { sym.Type = IMAGE_SYM_TYPE_NULL; sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } sym.NumberOfAuxSymbols = 0; return sym; } void Writer::createSymbolAndStringTable() { // PE/COFF images are limited to 8 byte section names. Longer names can be // supported by writing a non-standard string table, but this string table is // not mapped at runtime and the long names will therefore be inaccessible. // link.exe always truncates section names to 8 bytes, whereas binutils always // preserves long section names via the string table. LLD adopts a hybrid // solution where discardable sections have long names preserved and // non-discardable sections have their names truncated, to ensure that any // section which is mapped at runtime also has its name mapped at runtime. for (OutputSection *sec : outputSections) { if (sec->name.size() <= COFF::NameSize) continue; if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; sec->setStringTableOff(addEntryToStringTable(sec->name)); } if (config->debugDwarf || config->debugSymtab) { for (ObjFile *file : ObjFile::instances) { for (Symbol *b : file->getSymbols()) { auto *d = dyn_cast_or_null(b); if (!d || d->writtenToSymtab) continue; d->writtenToSymtab = true; if (Optional sym = createSymbol(d)) outputSymtab.push_back(*sym); } } } if (outputSymtab.empty() && strtab.empty()) return; // We position the symbol table to be adjacent to the end of the last section. uint64_t fileOff = fileSize; pointerToSymbolTable = fileOff; fileOff += outputSymtab.size() * sizeof(coff_symbol16); fileOff += 4 + strtab.size(); fileSize = alignTo(fileOff, config->fileAlign); } void Writer::mergeSections() { if (!pdataSec->chunks.empty()) { firstPdata = pdataSec->chunks.front(); lastPdata = pdataSec->chunks.back(); } for (auto &p : config->merge) { StringRef toName = p.second; if (p.first == toName) continue; StringSet<> names; while (1) { if (!names.insert(toName).second) fatal("/merge: cycle found for section '" + p.first + "'"); auto i = config->merge.find(toName); if (i == config->merge.end()) break; toName = i->second; } OutputSection *from = findSection(p.first); OutputSection *to = findSection(toName); if (!from) continue; if (!to) { from->name = toName; continue; } to->merge(from); } } // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + sizeof(data_directory) * numberOfDataDirectory + sizeof(coff_section) * outputSections.size(); sizeOfHeaders += config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); - uint64_t rva = pageSize; // The first page is kept unmapped. fileSize = sizeOfHeaders; + // The first page is kept unmapped. + uint64_t rva = alignTo(sizeOfHeaders, config->align); + for (OutputSection *sec : outputSections) { if (sec == relocSec) addBaserels(); uint64_t rawSize = 0, virtualSize = 0; sec->header.VirtualAddress = rva; // If /FUNCTIONPADMIN is used, functions are padded in order to create a // hotpatchable image. const bool isCodeSection = (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && (sec->header.Characteristics & IMAGE_SCN_MEM_READ) && (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE); uint32_t padding = isCodeSection ? config->functionPadMin : 0; for (Chunk *c : sec->chunks) { if (padding && c->isHotPatchable()) virtualSize += padding; virtualSize = alignTo(virtualSize, c->getAlignment()); c->setRVA(rva + virtualSize); virtualSize += c->getSize(); if (c->hasData) rawSize = alignTo(virtualSize, config->fileAlign); } if (virtualSize > UINT32_MAX) error("section larger than 4 GiB: " + sec->name); sec->header.VirtualSize = virtualSize; sec->header.SizeOfRawData = rawSize; if (rawSize != 0) sec->header.PointerToRawData = fileSize; - rva += alignTo(virtualSize, pageSize); + rva += alignTo(virtualSize, config->align); fileSize += alignTo(rawSize, config->fileAlign); } - sizeOfImage = alignTo(rva, pageSize); + sizeOfImage = alignTo(rva, config->align); // Assign addresses to sections in MergeChunks. for (MergeChunk *mc : MergeChunk::instances) if (mc) mc->assignSubsectionRVAs(); } template void Writer::writeHeader() { // Write DOS header. For backwards compatibility, the first part of a PE/COFF // executable consists of an MS-DOS MZ executable. If the executable is run // under DOS, that program gets run (usually to just print an error message). // When run under Windows, the loader looks at AddressOfNewExeHeader and uses // the PE header instead. uint8_t *buf = buffer->getBufferStart(); auto *dos = reinterpret_cast(buf); buf += sizeof(dos_header); dos->Magic[0] = 'M'; dos->Magic[1] = 'Z'; dos->UsedBytesInTheLastPage = dosStubSize % 512; dos->FileSizeInPages = divideCeil(dosStubSize, 512); dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16; dos->AddressOfRelocationTable = sizeof(dos_header); dos->AddressOfNewExeHeader = dosStubSize; // Write DOS program. memcpy(buf, dosProgram, sizeof(dosProgram)); buf += sizeof(dosProgram); // Write PE magic memcpy(buf, PEMagic, sizeof(PEMagic)); buf += sizeof(PEMagic); // Write COFF header auto *coff = reinterpret_cast(buf); buf += sizeof(*coff); coff->Machine = config->machine; coff->NumberOfSections = outputSections.size(); coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; if (config->largeAddressAware) coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; if (!config->is64()) coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE; if (config->dll) coff->Characteristics |= IMAGE_FILE_DLL; if (!config->relocatable) coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; if (config->swaprunCD) coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; if (config->swaprunNet) coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; coff->SizeOfOptionalHeader = sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; // Write PE header auto *pe = reinterpret_cast(buf); buf += sizeof(*pe); pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; // If {Major,Minor}LinkerVersion is left at 0.0, then for some // reason signing the resulting PE file with Authenticode produces a // signature that fails to validate on Windows 7 (but is OK on 10). // Set it to 14.0, which is what VS2015 outputs, and which avoids // that problem. pe->MajorLinkerVersion = 14; pe->MinorLinkerVersion = 0; pe->ImageBase = config->imageBase; - pe->SectionAlignment = pageSize; + pe->SectionAlignment = config->align; pe->FileAlignment = config->fileAlign; pe->MajorImageVersion = config->majorImageVersion; pe->MinorImageVersion = config->minorImageVersion; pe->MajorOperatingSystemVersion = config->majorOSVersion; pe->MinorOperatingSystemVersion = config->minorOSVersion; pe->MajorSubsystemVersion = config->majorOSVersion; pe->MinorSubsystemVersion = config->minorOSVersion; pe->Subsystem = config->subsystem; pe->SizeOfImage = sizeOfImage; pe->SizeOfHeaders = sizeOfHeaders; if (!config->noEntry) { Defined *entry = cast(config->entry); pe->AddressOfEntryPoint = entry->getRVA(); // Pointer to thumb code must have the LSB set, so adjust it. if (config->machine == ARMNT) pe->AddressOfEntryPoint |= 1; } pe->SizeOfStackReserve = config->stackReserve; pe->SizeOfStackCommit = config->stackCommit; pe->SizeOfHeapReserve = config->heapReserve; pe->SizeOfHeapCommit = config->heapCommit; if (config->appContainer) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; if (config->dynamicBase) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; if (config->highEntropyVA) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; if (!config->allowBind) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; if (config->nxCompat) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; if (!config->allowIsolation) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; if (config->guardCF != GuardCFLevel::Off) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; if (config->integrityCheck) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; if (setNoSEHCharacteristic) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; if (config->terminalServerAware) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; pe->NumberOfRvaAndSize = numberOfDataDirectory; if (textSec->getVirtualSize()) { pe->BaseOfCode = textSec->getRVA(); pe->SizeOfCode = textSec->getRawSize(); } pe->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory auto *dir = reinterpret_cast(buf); buf += sizeof(*dir) * numberOfDataDirectory; - if (!config->exports.empty()) { - dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); - dir[EXPORT_TABLE].Size = edata.getSize(); + if (edataStart) { + dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA(); + dir[EXPORT_TABLE].Size = + edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA(); } if (importTableStart) { dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); dir[IMPORT_TABLE].Size = importTableSize; } if (iatStart) { dir[IAT].RelativeVirtualAddress = iatStart->getRVA(); dir[IAT].Size = iatSize; } if (rsrcSec->getVirtualSize()) { dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA(); dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize(); } if (firstPdata) { dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA(); dir[EXCEPTION_TABLE].Size = lastPdata->getRVA() + lastPdata->getSize() - firstPdata->getRVA(); } if (relocSec->getVirtualSize()) { dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); } if (Symbol *sym = symtab->findUnderscore("_tls_used")) { if (Defined *b = dyn_cast(sym)) { dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); dir[TLS_TABLE].Size = config->is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); } } if (debugDirectory) { dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); } if (Symbol *sym = symtab->findUnderscore("_load_config_used")) { if (auto *b = dyn_cast(sym)) { SectionChunk *sc = b->getChunk(); assert(b->getRVA() >= sc->getRVA()); uint64_t offsetInChunk = b->getRVA() - sc->getRVA(); if (!sc->hasData || offsetInChunk + 4 > sc->getSize()) fatal("_load_config_used is malformed"); ArrayRef secContents = sc->getContents(); uint32_t loadConfigSize = *reinterpret_cast(&secContents[offsetInChunk]); if (offsetInChunk + loadConfigSize > sc->getSize()) fatal("_load_config_used is too large"); dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA(); dir[LOAD_CONFIG_TABLE].Size = loadConfigSize; } } if (!delayIdata.empty()) { dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = delayIdata.getDirRVA(); dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize(); } // Write section table for (OutputSection *sec : outputSections) { sec->writeHeaderTo(buf); buf += sizeof(coff_section); } sectionTable = ArrayRef( buf - outputSections.size() * sizeof(coff_section), buf); if (outputSymtab.empty() && strtab.empty()) return; coff->PointerToSymbolTable = pointerToSymbolTable; uint32_t numberOfSymbols = outputSymtab.size(); coff->NumberOfSymbols = numberOfSymbols; auto *symbolTable = reinterpret_cast( buffer->getBufferStart() + coff->PointerToSymbolTable); for (size_t i = 0; i != numberOfSymbols; ++i) symbolTable[i] = outputSymtab[i]; // Create the string table, it follows immediately after the symbol table. // The first 4 bytes is length including itself. buf = reinterpret_cast(&symbolTable[numberOfSymbols]); write32le(buf, strtab.size() + 4); if (!strtab.empty()) memcpy(buf + 4, strtab.data(), strtab.size()); } void Writer::openFile(StringRef path) { buffer = CHECK( FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable), "failed to open " + path); } void Writer::createSEHTable() { SymbolRVASet handlers; for (ObjFile *file : ObjFile::instances) { if (!file->hasSafeSEH()) error("/safeseh: " + file->getName() + " is not compatible with SEH"); markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); } // Set the "no SEH" characteristic if there really were no handlers, or if // there is no load config object to point to the table of handlers. setNoSEHCharacteristic = handlers.empty() || !symtab->findUnderscore("_load_config_used"); maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", "__safe_se_handler_count"); } // Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set // cannot contain duplicates. Therefore, the set is uniqued by Chunk and the // symbol's offset into that Chunk. static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) { Chunk *c = s->getChunk(); if (auto *sc = dyn_cast(c)) c = sc->repl; // Look through ICF replacement. uint32_t off = s->getRVA() - (c ? c->getRVA() : 0); rvaSet.insert({c, off}); } // Given a symbol, add it to the GFIDs table if it is a live, defined, function // symbol in an executable section. static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, Symbol *s) { if (!s) return; switch (s->kind()) { case Symbol::DefinedLocalImportKind: case Symbol::DefinedImportDataKind: // Defines an __imp_ pointer, so it is data, so it is ignored. break; case Symbol::DefinedCommonKind: // Common is always data, so it is ignored. break; case Symbol::DefinedAbsoluteKind: case Symbol::DefinedSyntheticKind: // Absolute is never code, synthetic generally isn't and usually isn't // determinable. break; - case Symbol::LazyKind: + case Symbol::LazyArchiveKind: + case Symbol::LazyObjectKind: case Symbol::UndefinedKind: // Undefined symbols resolve to zero, so they don't have an RVA. Lazy // symbols shouldn't have relocations. break; case Symbol::DefinedImportThunkKind: // Thunks are always code, include them. addSymbolToRVASet(addressTakenSyms, cast(s)); break; case Symbol::DefinedRegularKind: { // This is a regular, defined, symbol from a COFF file. Mark the symbol as // address taken if the symbol type is function and it's in an executable // section. auto *d = cast(s); if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) { SectionChunk *sc = dyn_cast(d->getChunk()); if (sc && sc->live && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) addSymbolToRVASet(addressTakenSyms, d); } break; } } } // Visit all relocations from all section contributions of this object file and // mark the relocation target as address-taken. static void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols) { for (Chunk *c : file->getChunks()) { // We only care about live section chunks. Common chunks and other chunks // don't generally contain relocations. SectionChunk *sc = dyn_cast(c); if (!sc || !sc->live) continue; for (const coff_relocation &reloc : sc->getRelocs()) { if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32) // Ignore relative relocations on x86. On x86_64 they can't be ignored // since they're also used to compute absolute addresses. continue; Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex); maybeAddAddressTakenFunction(usedSymbols, ref); } } } // Create the guard function id table. This is a table of RVAs of all // address-taken functions. It is sorted and uniqued, just like the safe SEH // table. void Writer::createGuardCFTables() { SymbolRVASet addressTakenSyms; SymbolRVASet longJmpTargets; for (ObjFile *file : ObjFile::instances) { // If the object was compiled with /guard:cf, the address taken symbols // are in .gfids$y sections, and the longjmp targets are in .gljmp$y // sections. If the object was not compiled with /guard:cf, we assume there // were no setjmp targets, and that all code symbols with relocations are // possibly address-taken. if (file->hasGuardCF()) { markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); } else { markSymbolsWithRelocations(file, addressTakenSyms); } } // Mark the image entry as address-taken. if (config->entry) maybeAddAddressTakenFunction(addressTakenSyms, config->entry); // Mark exported symbols in executable sections as address-taken. for (Export &e : config->exports) maybeAddAddressTakenFunction(addressTakenSyms, e.sym); // Ensure sections referenced in the gfid table are 16-byte aligned. for (const ChunkAndOffset &c : addressTakenSyms) if (c.inputChunk->getAlignment() < 16) c.inputChunk->setAlignment(16); maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table", "__guard_fids_count"); // Add the longjmp target table unless the user told us not to. if (config->guardCF == GuardCFLevel::Full) maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); // Set __guard_flags, which will be used in the load config to indicate that // /guard:cf was enabled. uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | uint32_t(coff_guard_flags::HasFidTable); if (config->guardCF == GuardCFLevel::Full) guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); Symbol *flagSym = symtab->findUnderscore("__guard_flags"); cast(flagSym)->setVA(guardFlags); } // Take a list of input sections containing symbol table indices and add those // symbols to an RVA table. The challenge is that symbol RVAs are not known and // depend on the table size, so we can't directly build a set of integers. void Writer::markSymbolsForRVATable(ObjFile *file, ArrayRef symIdxChunks, SymbolRVASet &tableSymbols) { for (SectionChunk *c : symIdxChunks) { // Skip sections discarded by linker GC. This comes up when a .gfids section // is associated with something like a vtable and the vtable is discarded. // In this case, the associated gfids section is discarded, and we don't // mark the virtual member functions as address-taken by the vtable. if (!c->live) continue; // Validate that the contents look like symbol table indices. ArrayRef data = c->getContents(); if (data.size() % 4 != 0) { warn("ignoring " + c->getSectionName() + " symbol table index section in object " + toString(file)); continue; } // Read each symbol table index and check if that symbol was included in the // final link. If so, add it to the table symbol set. ArrayRef symIndices( reinterpret_cast(data.data()), data.size() / 4); ArrayRef objSymbols = file->getSymbols(); for (uint32_t symIndex : symIndices) { if (symIndex >= objSymbols.size()) { warn("ignoring invalid symbol table index in section " + c->getSectionName() + " in object " + toString(file)); continue; } if (Symbol *s = objSymbols[symIndex]) { if (s->isLive()) addSymbolToRVASet(tableSymbols, cast(s)); } } } } // Replace the absolute table symbol with a synthetic symbol pointing to // tableChunk so that we can emit base relocations for it and resolve section // relative relocations. void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, StringRef countSym) { if (tableSymbols.empty()) return; RVATableChunk *tableChunk = make(std::move(tableSymbols)); rdataSec->addChunk(tableChunk); Symbol *t = symtab->findUnderscore(tableSym); Symbol *c = symtab->findUnderscore(countSym); replaceSymbol(t, t->getName(), tableChunk); cast(c)->setVA(tableChunk->getSize() / 4); } // MinGW specific. Gather all relocations that are imported from a DLL even // though the code didn't expect it to, produce the table that the runtime // uses for fixing them up, and provide the synthetic symbols that the // runtime uses for finding the table. void Writer::createRuntimePseudoRelocs() { std::vector rels; for (Chunk *c : symtab->getChunks()) { auto *sc = dyn_cast(c); if (!sc || !sc->live) continue; sc->getRuntimePseudoRelocs(rels); } if (!rels.empty()) log("Writing " + Twine(rels.size()) + " runtime pseudo relocations"); PseudoRelocTableChunk *table = make(rels); rdataSec->addChunk(table); EmptyChunk *endOfList = make(); rdataSec->addChunk(endOfList); Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); replaceSymbol(headSym, headSym->getName(), table); replaceSymbol(endSym, endSym->getName(), endOfList); } // MinGW specific. // The MinGW .ctors and .dtors lists have sentinels at each end; // a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ // and __DTOR_LIST__ respectively. void Writer::insertCtorDtorSymbols() { AbsolutePointerChunk *ctorListHead = make(-1); AbsolutePointerChunk *ctorListEnd = make(0); AbsolutePointerChunk *dtorListHead = make(-1); AbsolutePointerChunk *dtorListEnd = make(0); ctorsSec->insertChunkAtStart(ctorListHead); ctorsSec->addChunk(ctorListEnd); dtorsSec->insertChunkAtStart(dtorListHead); dtorsSec->addChunk(dtorListEnd); Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__"); Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__"); replaceSymbol(ctorListSym, ctorListSym->getName(), ctorListHead); replaceSymbol(dtorListSym, dtorListSym->getName(), dtorListHead); } // Handles /section options to allow users to overwrite // section attributes. void Writer::setSectionPermissions() { for (auto &p : config->section) { StringRef name = p.first; uint32_t perm = p.second; for (OutputSection *sec : outputSections) if (sec->name == name) sec->setPermissions(perm); } } // Write section contents to a mmap'ed file. void Writer::writeSections() { // Record the number of sections to apply section index relocations // against absolute symbols. See applySecIdx in Chunks.cpp.. DefinedAbsolute::numOutputSections = outputSections.size(); uint8_t *buf = buffer->getBufferStart(); for (OutputSection *sec : outputSections) { uint8_t *secBuf = buf + sec->getFileOff(); // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as // ADD instructions). if (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) memset(secBuf, 0xCC, sec->getRawSize()); parallelForEach(sec->chunks, [&](Chunk *c) { c->writeTo(secBuf + c->getRVA() - sec->getRVA()); }); } } void Writer::writeBuildId() { // There are two important parts to the build ID. // 1) If building with debug info, the COFF debug directory contains a // timestamp as well as a Guid and Age of the PDB. // 2) In all cases, the PE COFF file header also contains a timestamp. // For reproducibility, instead of a timestamp we want to use a hash of the // PE contents. if (config->debug) { assert(buildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. } // At this point the only fields in the COFF file which remain unset are the // "timestamp" in the COFF file header, and the ones in the coff debug // directory. Now we can hash the file and write that hash to the various // timestamp fields in the file. StringRef outputFileData( reinterpret_cast(buffer->getBufferStart()), buffer->getBufferSize()); uint32_t timestamp = config->timestamp; uint64_t hash = 0; bool generateSyntheticBuildId = config->mingw && config->debug && config->pdbPath.empty(); if (config->repro || generateSyntheticBuildId) hash = xxHash64(outputFileData); if (config->repro) timestamp = static_cast(hash); if (generateSyntheticBuildId) { // For MinGW builds without a PDB file, we still generate a build id // to allow associating a crash dump to the executable. buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70; buildId->buildId->PDB70.Age = 1; memcpy(buildId->buildId->PDB70.Signature, &hash, 8); // xxhash only gives us 8 bytes, so put some fixed data in the other half. memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8); } if (debugDirectory) debugDirectory->setTimeDateStamp(timestamp); uint8_t *buf = buffer->getBufferStart(); buf += dosStubSize + sizeof(PEMagic); object::coff_file_header *coffHeader = reinterpret_cast(buf); coffHeader->TimeDateStamp = timestamp; } // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { if (!firstPdata) return; // We assume .pdata contains function table entries only. auto bufAddr = [&](Chunk *c) { OutputSection *os = c->getOutputSection(); return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - os->getRVA(); }; uint8_t *begin = bufAddr(firstPdata); uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); if (config->machine == AMD64) { struct Entry { ulittle32_t begin, end, unwind; }; parallelSort( MutableArrayRef((Entry *)begin, (Entry *)end), [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } if (config->machine == ARMNT || config->machine == ARM64) { struct Entry { ulittle32_t begin, unwind; }; parallelSort( MutableArrayRef((Entry *)begin, (Entry *)end), [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } errs() << "warning: don't know how to handle .pdata.\n"; } // The CRT section contains, among other things, the array of function // pointers that initialize every global variable that is not trivially // constructed. The CRT calls them one after the other prior to invoking // main(). // // As per C++ spec, 3.6.2/2.3, // "Variables with ordered initialization defined within a single // translation unit shall be initialized in the order of their definitions // in the translation unit" // // It is therefore critical to sort the chunks containing the function // pointers in the order that they are listed in the object file (top to // bottom), otherwise global objects might not be initialized in the // correct order. void Writer::sortCRTSectionChunks(std::vector &chunks) { auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) { auto sa = dyn_cast(a); auto sb = dyn_cast(b); assert(sa && sb && "Non-section chunks in CRT section!"); StringRef sAObj = sa->file->mb.getBufferIdentifier(); StringRef sBObj = sb->file->mb.getBufferIdentifier(); return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber(); }; llvm::stable_sort(chunks, sectionChunkOrder); if (config->verbose) { for (auto &c : chunks) { auto sc = dyn_cast(c); log(" " + sc->file->mb.getBufferIdentifier().str() + ", SectionID: " + Twine(sc->getSectionNumber())); } } } OutputSection *Writer::findSection(StringRef name) { for (OutputSection *sec : outputSections) if (sec->name == name) return sec; return nullptr; } uint32_t Writer::getSizeOfInitializedData() { uint32_t res = 0; for (OutputSection *s : outputSections) if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) res += s->getRawSize(); return res; } // Add base relocations to .reloc section. void Writer::addBaserels() { if (!config->relocatable) return; relocSec->chunks.clear(); std::vector v; for (OutputSection *sec : outputSections) { if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. for (Chunk *c : sec->chunks) c->getBaserels(&v); // Add the addresses to .reloc section. if (!v.empty()) addBaserelBlocks(v); v.clear(); } } // Add addresses to .reloc section. Note that addresses are grouped by page. void Writer::addBaserelBlocks(std::vector &v) { const uint32_t mask = ~uint32_t(pageSize - 1); uint32_t page = v[0].rva & mask; size_t i = 0, j = 1; for (size_t e = v.size(); j < e; ++j) { uint32_t p = v[j].rva & mask; if (p == page) continue; relocSec->addChunk(make(page, &v[i], &v[0] + j)); i = j; page = p; } if (i == j) return; relocSec->addChunk(make(page, &v[i], &v[0] + j)); } PartialSection *Writer::createPartialSection(StringRef name, uint32_t outChars) { PartialSection *&pSec = partialSections[{name, outChars}]; if (pSec) return pSec; pSec = make(name, outChars); return pSec; } PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { auto it = partialSections.find({name, outChars}); if (it != partialSections.end()) return it->second; return nullptr; } + +} // namespace coff +} // namespace lld Index: vendor/lld/dist/Common/CMakeLists.txt =================================================================== --- vendor/lld/dist/Common/CMakeLists.txt (revision 353949) +++ vendor/lld/dist/Common/CMakeLists.txt (revision 353950) @@ -1,60 +1,62 @@ if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc) find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc) set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc") set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake") if(lld_vc) set(lld_source_dir ${LLD_SOURCE_DIR}) endif() add_custom_command(OUTPUT "${version_inc}" DEPENDS "${lld_vc}" "${generate_vcs_version_script}" COMMAND ${CMAKE_COMMAND} "-DNAMES=LLD" "-DLLD_SOURCE_DIR=${LLD_SOURCE_DIR}" "-DHEADER_FILE=${version_inc}" -P "${generate_vcs_version_script}") # Mark the generated header as being generated. set_source_files_properties("${version_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) set_property(SOURCE Version.cpp APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC") add_lld_library(lldCommon Args.cpp + DWARF.cpp ErrorHandler.cpp Filesystem.cpp Memory.cpp Reproduce.cpp Strings.cpp TargetOptionsCommandFlags.cpp Threads.cpp Timer.cpp VCSVersion.inc Version.cpp ADDITIONAL_HEADER_DIRS ${LLD_INCLUDE_DIR}/lld/Common LINK_COMPONENTS Codegen Core + DebugInfoDWARF Demangle MC Option Support Target LINK_LIBS ${LLVM_PTHREAD_LIB} DEPENDS ${tablegen_deps} ) Index: vendor/lld/dist/Common/DWARF.cpp =================================================================== --- vendor/lld/dist/Common/DWARF.cpp (nonexistent) +++ vendor/lld/dist/Common/DWARF.cpp (revision 353950) @@ -0,0 +1,103 @@ +//===- DWARF.cpp ----------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/DWARF.h" +#include "lld/Common/ErrorHandler.h" + +using namespace llvm; + +namespace lld { + +DWARFCache::DWARFCache(std::unique_ptr d) + : dwarf(std::move(d)) { + for (std::unique_ptr &cu : dwarf->compile_units()) { + auto report = [](Error err) { + handleAllErrors(std::move(err), + [](ErrorInfoBase &info) { warn(info.message()); }); + }; + Expected expectedLT = + dwarf->getLineTableForUnit(cu.get(), report); + const DWARFDebugLine::LineTable *lt = nullptr; + if (expectedLT) + lt = *expectedLT; + else + report(expectedLT.takeError()); + if (!lt) + continue; + lineTables.push_back(lt); + + // Loop over variable records and insert them to variableLoc. + for (const auto &entry : cu->dies()) { + DWARFDie die(cu.get(), &entry); + // Skip all tags that are not variables. + if (die.getTag() != dwarf::DW_TAG_variable) + continue; + + // Skip if a local variable because we don't need them for generating + // error messages. In general, only non-local symbols can fail to be + // linked. + if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0)) + continue; + + // Get the source filename index for the variable. + unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0); + if (!lt->hasFileAtIndex(file)) + continue; + + // Get the line number on which the variable is declared. + unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0); + + // Here we want to take the variable name to add it into variableLoc. + // Variable can have regular and linkage name associated. At first, we try + // to get linkage name as it can be different, for example when we have + // two variables in different namespaces of the same object. Use common + // name otherwise, but handle the case when it also absent in case if the + // input object file lacks some debug info. + StringRef name = + dwarf::toString(die.find(dwarf::DW_AT_linkage_name), + dwarf::toString(die.find(dwarf::DW_AT_name), "")); + if (!name.empty()) + variableLoc.insert({name, {lt, file, line}}); + } + } +} + +// Returns the pair of file name and line number describing location of data +// object (variable, array, etc) definition. +Optional> +DWARFCache::getVariableLoc(StringRef name) { + // Return if we have no debug information about data object. + auto it = variableLoc.find(name); + if (it == variableLoc.end()) + return None; + + // Take file name string from line table. + std::string fileName; + if (!it->second.lt->getFileNameByIndex( + it->second.file, {}, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName)) + return None; + + return std::make_pair(fileName, it->second.line); +} + +// Returns source line information for a given offset +// using DWARF debug info. +Optional DWARFCache::getDILineInfo(uint64_t offset, + uint64_t sectionIndex) { + DILineInfo info; + for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) { + if (lt->getFileLineInfoForAddress( + {offset, sectionIndex}, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info)) + return info; + } + return None; +} + +} // namespace lld Index: vendor/lld/dist/Common/ErrorHandler.cpp =================================================================== --- vendor/lld/dist/Common/ErrorHandler.cpp (revision 353949) +++ vendor/lld/dist/Common/ErrorHandler.cpp (revision 353950) @@ -1,178 +1,209 @@ //===- ErrorHandler.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lld/Common/ErrorHandler.h" #include "lld/Common/Threads.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" #include #include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include #endif using namespace llvm; using namespace lld; // The functions defined in this file can be called from multiple threads, // but outs() or errs() are not thread-safe. We protect them using a mutex. static std::mutex mu; -// Prints "\n" or does nothing, depending on Msg contents of -// the previous call of this function. -static void newline(raw_ostream *errorOS, const Twine &msg) { - // True if the previous error message contained "\n". - // We want to separate multi-line error messages with a newline. - static bool flag; +// We want to separate multi-line messages with a newline. `sep` is "\n" +// if the last messages was multi-line. Otherwise "". +static StringRef sep; - if (flag) - *errorOS << "\n"; - flag = StringRef(msg.str()).contains('\n'); +static StringRef getSeparator(const Twine &msg) { + if (StringRef(msg.str()).contains('\n')) + return "\n"; + return ""; } ErrorHandler &lld::errorHandler() { static ErrorHandler handler; return handler; } +void lld::enableColors(bool enable) { + errorHandler().errorOS->enable_colors(enable); +} + void lld::exitLld(int val) { // Delete any temporary file, while keeping the memory mapping open. if (errorHandler().outputBuffer) errorHandler().outputBuffer->discard(); // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO // build allows us to get the output of -time-passes. llvm_shutdown(); outs().flush(); errs().flush(); _exit(val); } void lld::diagnosticHandler(const DiagnosticInfo &di) { SmallString<128> s; raw_svector_ostream os(s); DiagnosticPrinterRawOStream dp(os); di.print(dp); switch (di.getSeverity()) { case DS_Error: error(s); break; case DS_Warning: warn(s); break; case DS_Remark: case DS_Note: message(s); break; } } void lld::checkError(Error e) { handleAllErrors(std::move(e), [&](ErrorInfoBase &eib) { error(eib.message()); }); } -static std::string getLocation(std::string msg, std::string defaultMsg) { - static std::vector Regexes{ - std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"), +// This is for --vs-diagnostics. +// +// Normally, lld's error message starts with argv[0]. Therefore, it usually +// looks like this: +// +// ld.lld: error: ... +// +// This error message style is unfortunately unfriendly to Visual Studio +// IDE. VS interprets the first word of the first line as an error location +// and make it clickable, thus "ld.lld" in the above message would become a +// clickable text. When you click it, VS opens "ld.lld" executable file with +// a binary editor. +// +// As a workaround, we print out an error location instead of "ld.lld" if +// lld is running in VS diagnostics mode. As a result, error message will +// look like this: +// +// src/foo.c(35): error: ... +// +// This function returns an error location string. An error location is +// extracted from an error message using regexps. +std::string ErrorHandler::getLocation(const Twine &msg) { + if (!vsDiagnostics) + return logName; + + static std::regex regexes[] = { + std::regex( + R"(^undefined (?:\S+ )?symbol:.*\n)" + R"(>>> referenced by .+\((\S+):(\d+)\))"), + std::regex( + R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"), std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), std::regex( R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), std::regex( - R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"), + R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"), + std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"), std::regex( - R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), - std::regex( - R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"), + R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"), + std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), std::regex(R"((\S+):(\d+): unclosed quote)"), }; - std::smatch Match; - for (std::regex &Re : Regexes) { - if (std::regex_search(msg, Match, Re)) { - return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")" - : Match.str(1); - } - } - return defaultMsg; -} + std::string str = msg.str(); + for (std::regex &re : regexes) { + std::smatch m; + if (!std::regex_search(str, m, re)) + continue; -void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c, - const Twine &msg) { - - if (vsDiagnostics) { - // A Visual Studio-style error message starts with an error location. - // If a location cannot be extracted then we default to LogName. - *errorOS << getLocation(msg.str(), logName) << ": "; - } else { - *errorOS << logName << ": "; + assert(m.size() == 2 || m.size() == 3); + if (m.size() == 2) + return m.str(1); + return m.str(1) + "(" + m.str(2) + ")"; } - if (colorDiagnostics) { - errorOS->changeColor(c, true); - *errorOS << s; - errorOS->resetColor(); - } else { - *errorOS << s; - } + return logName; } void ErrorHandler::log(const Twine &msg) { - if (verbose) { - std::lock_guard lock(mu); - *errorOS << logName << ": " << msg << "\n"; - } + if (!verbose) + return; + std::lock_guard lock(mu); + *errorOS << logName << ": " << msg << "\n"; } void ErrorHandler::message(const Twine &msg) { std::lock_guard lock(mu); outs() << msg << "\n"; outs().flush(); } void ErrorHandler::warn(const Twine &msg) { if (fatalWarnings) { error(msg); return; } std::lock_guard lock(mu); - newline(errorOS, msg); - printHeader("warning: ", raw_ostream::MAGENTA, msg); - *errorOS << msg << "\n"; + *errorOS << sep << getLocation(msg) << ": " << Colors::MAGENTA + << "warning: " << Colors::RESET << msg << "\n"; + sep = getSeparator(msg); } void ErrorHandler::error(const Twine &msg) { + // If Visual Studio-style error message mode is enabled, + // this particular error is printed out as two errors. + if (vsDiagnostics) { + static std::regex re(R"(^(duplicate symbol: .*))" + R"((\n>>> defined at \S+:\d+.*\n>>>.*))" + R"((\n>>> defined at \S+:\d+.*\n>>>.*))"); + std::string str = msg.str(); + std::smatch m; + + if (std::regex_match(str, m, re)) { + error(m.str(1) + m.str(2)); + error(m.str(1) + m.str(3)); + return; + } + } + std::lock_guard lock(mu); - newline(errorOS, msg); if (errorLimit == 0 || errorCount < errorLimit) { - printHeader("error: ", raw_ostream::RED, msg); - *errorOS << msg << "\n"; + *errorOS << sep << getLocation(msg) << ": " << Colors::RED + << "error: " << Colors::RESET << msg << "\n"; } else if (errorCount == errorLimit) { - printHeader("error: ", raw_ostream::RED, msg); - *errorOS << errorLimitExceededMsg << "\n"; + *errorOS << sep << getLocation(msg) << ": " << Colors::RED + << "error: " << Colors::RESET << errorLimitExceededMsg << "\n"; if (exitEarly) exitLld(1); } + sep = getSeparator(msg); ++errorCount; } void ErrorHandler::fatal(const Twine &msg) { error(msg); exitLld(1); } Index: vendor/lld/dist/Common/Strings.cpp =================================================================== --- vendor/lld/dist/Common/Strings.cpp (revision 353949) +++ vendor/lld/dist/Common/Strings.cpp (revision 353950) @@ -1,103 +1,81 @@ //===- Strings.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lld/Common/Strings.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Support/GlobPattern.h" #include #include #include using namespace llvm; using namespace lld; -// Returns the demangled C++ symbol name for Name. -Optional lld::demangleItanium(StringRef name) { +// Returns the demangled C++ symbol name for name. +std::string lld::demangleItanium(StringRef name) { // itaniumDemangle can be used to demangle strings other than symbol // names which do not necessarily start with "_Z". Name can be - // either a C or C++ symbol. Don't call itaniumDemangle if the name + // either a C or C++ symbol. Don't call demangle if the name // does not look like a C++ symbol name to avoid getting unexpected // result for a C symbol that happens to match a mangled type name. if (!name.startswith("_Z")) - return None; + return name; - char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr); - if (!buf) - return None; - std::string s(buf); - free(buf); - return s; + return demangle(name); } -Optional lld::demangleMSVC(StringRef name) { - std::string prefix; - if (name.consume_front("__imp_")) - prefix = "__declspec(dllimport) "; - - // Demangle only C++ names. - if (!name.startswith("?")) - return None; - - char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr); - if (!buf) - return None; - std::string s(buf); - free(buf); - return prefix + s; -} - StringMatcher::StringMatcher(ArrayRef pat) { for (StringRef s : pat) { Expected pat = GlobPattern::create(s); if (!pat) error(toString(pat.takeError())); else patterns.push_back(*pat); } } bool StringMatcher::match(StringRef s) const { for (const GlobPattern &pat : patterns) if (pat.match(s)) return true; return false; } // Converts a hex string (e.g. "deadbeef") to a vector. std::vector lld::parseHex(StringRef s) { std::vector hex; while (!s.empty()) { StringRef b = s.substr(0, 2); s = s.substr(2); uint8_t h; if (!to_integer(b, h, 16)) { error("not a hexadecimal value: " + b); return {}; } hex.push_back(h); } return hex; } // Returns true if S is valid as a C language identifier. bool lld::isValidCIdentifier(StringRef s) { return !s.empty() && (isAlpha(s[0]) || s[0] == '_') && std::all_of(s.begin() + 1, s.end(), [](char c) { return c == '_' || isAlnum(c); }); } // Write the contents of the a buffer to a file void lld::saveBuffer(StringRef buffer, const Twine &path) { std::error_code ec; - raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None); + raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::OF_None); if (ec) error("cannot create " + path + ": " + ec.message()); os << buffer; } Index: vendor/lld/dist/Common/TargetOptionsCommandFlags.cpp =================================================================== --- vendor/lld/dist/Common/TargetOptionsCommandFlags.cpp (revision 353949) +++ vendor/lld/dist/Common/TargetOptionsCommandFlags.cpp (revision 353950) @@ -1,35 +1,39 @@ //===-- TargetOptionsCommandFlags.cpp ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file exists as a place for global variables defined in LLVM's // CodeGen/CommandFlags.inc. By putting the resulting object file in // an archive and linking with it, the definitions will automatically be // included when needed and skipped when already present. // //===----------------------------------------------------------------------===// #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/CodeGen/CommandFlags.inc" #include "llvm/Target/TargetOptions.h" // Define an externally visible version of // initTargetOptionsFromCodeGenFlags, so that its functionality can be // used without having to include llvm/CodeGen/CommandFlags.inc, which // would lead to multiple definitions of the command line flags. llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { return ::InitTargetOptionsFromCodeGenFlags(); } +llvm::Optional lld::getRelocModelFromCMModel() { + return getRelocModel(); +} + llvm::Optional lld::getCodeModelFromCMModel() { return getCodeModel(); } std::string lld::getCPUStr() { return ::getCPUStr(); } std::vector lld::getMAttrs() { return ::MAttrs; } Index: vendor/lld/dist/ELF/AArch64ErrataFix.cpp =================================================================== --- vendor/lld/dist/ELF/AArch64ErrataFix.cpp (revision 353949) +++ vendor/lld/dist/ELF/AArch64ErrataFix.cpp (revision 353950) @@ -1,651 +1,645 @@ //===- AArch64ErrataFix.cpp -----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file implements Section Patching for the purpose of working around -// errata in CPUs. The general principle is that an erratum sequence of one or +// the AArch64 Cortex-53 errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 +// versions of the core. +// +// The general principle is that an erratum sequence of one or // more instructions is detected in the instruction stream, one of the // instructions in the sequence is replaced with a branch to a patch sequence // of replacement instructions. At the end of the replacement sequence the // patch branches back to the instruction stream. // This technique is only suitable for fixing an erratum when: // - There is a set of necessary conditions required to trigger the erratum that // can be detected at static link time. // - There is a set of replacement instructions that can be used to remove at // least one of the necessary conditions that trigger the erratum. // - We can overwrite an instruction in the erratum sequence with a branch to // the replacement sequence. // - We can place the replacement sequence within range of the branch. - -// FIXME: -// - The implementation here only supports one patch, the AArch64 Cortex-53 -// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core. -// To keep the initial version simple there is no support for multiple -// architectures or selection of different patches. //===----------------------------------------------------------------------===// #include "AArch64ErrataFix.h" #include "Config.h" #include "LinkerScript.h" #include "OutputSections.h" #include "Relocations.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { // Helper functions to identify instructions and conditions needed to trigger // the Cortex-A53-843419 erratum. // ADRP // | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) | static bool isADRP(uint32_t instr) { return (instr & 0x9f000000) == 0x90000000; } // Load and store bit patterns from ARMv8-A ARM ARM. // Instructions appear in order of appearance starting from table in // C4.1.3 Loads and Stores. // All loads and stores have 1 (at bit postion 27), (0 at bit position 25). // | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) | static bool isLoadStoreClass(uint32_t instr) { return (instr & 0x0a000000) == 0x08000000; } // LDN/STN multiple no offset // | 0 Q 00 | 1100 | 0 L 00 | 0000 | opcode (4) | size (2) | Rn (5) | Rt (5) | // LDN/STN multiple post-indexed // | 0 Q 00 | 1100 | 1 L 0 | Rm (5)| opcode (4) | size (2) | Rn (5) | Rt (5) | // L == 0 for stores. // Utility routine to decode opcode field of LDN/STN multiple structure // instructions to find the ST1 instructions. // opcode == 0010 ST1 4 registers. // opcode == 0110 ST1 3 registers. // opcode == 0111 ST1 1 register. // opcode == 1010 ST1 2 registers. static bool isST1MultipleOpcode(uint32_t instr) { return (instr & 0x0000f000) == 0x00002000 || (instr & 0x0000f000) == 0x00006000 || (instr & 0x0000f000) == 0x00007000 || (instr & 0x0000f000) == 0x0000a000; } static bool isST1Multiple(uint32_t instr) { return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr); } // Writes to Rn (writeback). static bool isST1MultiplePost(uint32_t instr) { return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr); } // LDN/STN single no offset // | 0 Q 00 | 1101 | 0 L R 0 | 0000 | opc (3) S | size (2) | Rn (5) | Rt (5)| // LDN/STN single post-indexed // | 0 Q 00 | 1101 | 1 L R | Rm (5) | opc (3) S | size (2) | Rn (5) | Rt (5)| // L == 0 for stores // Utility routine to decode opcode field of LDN/STN single structure // instructions to find the ST1 instructions. // R == 0 for ST1 and ST3, R == 1 for ST2 and ST4. // opcode == 000 ST1 8-bit. // opcode == 010 ST1 16-bit. // opcode == 100 ST1 32 or 64-bit (Size determines which). static bool isST1SingleOpcode(uint32_t instr) { return (instr & 0x0040e000) == 0x00000000 || (instr & 0x0040e000) == 0x00004000 || (instr & 0x0040e000) == 0x00008000; } static bool isST1Single(uint32_t instr) { return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr); } // Writes to Rn (writeback). static bool isST1SinglePost(uint32_t instr) { return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr); } static bool isST1(uint32_t instr) { return isST1Multiple(instr) || isST1MultiplePost(instr) || isST1Single(instr) || isST1SinglePost(instr); } // Load/store exclusive // | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for Stores. static bool isLoadStoreExclusive(uint32_t instr) { return (instr & 0x3f000000) == 0x08000000; } static bool isLoadExclusive(uint32_t instr) { return (instr & 0x3f400000) == 0x08400000; } // Load register literal // | opc (2) 01 | 1 V 00 | imm19 | Rt (5) | static bool isLoadLiteral(uint32_t instr) { return (instr & 0x3b000000) == 0x18000000; } // Load/store no-allocate pair // (offset) // | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for stores. // Never writes to register static bool isSTNP(uint32_t instr) { return (instr & 0x3bc00000) == 0x28000000; } // Load/store register pair // (post-indexed) // | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP // Writes to Rn. static bool isSTPPost(uint32_t instr) { return (instr & 0x3bc00000) == 0x28800000; } // (offset) // | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | static bool isSTPOffset(uint32_t instr) { return (instr & 0x3bc00000) == 0x29000000; } // (pre-index) // | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // Writes to Rn. static bool isSTPPre(uint32_t instr) { return (instr & 0x3bc00000) == 0x29800000; } static bool isSTP(uint32_t instr) { return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr); } // Load/store register (unscaled immediate) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) | // V == 0 for Scalar, V == 1 for Simd/FP. static bool isLoadStoreUnscaled(uint32_t instr) { return (instr & 0x3b000c00) == 0x38000000; } // Load/store register (immediate post-indexed) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) | static bool isLoadStoreImmediatePost(uint32_t instr) { return (instr & 0x3b200c00) == 0x38000400; } // Load/store register (unprivileged) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) | static bool isLoadStoreUnpriv(uint32_t instr) { return (instr & 0x3b200c00) == 0x38000800; } // Load/store register (immediate pre-indexed) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) | static bool isLoadStoreImmediatePre(uint32_t instr) { return (instr & 0x3b200c00) == 0x38000c00; } // Load/store register (register offset) // | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt | static bool isLoadStoreRegisterOff(uint32_t instr) { return (instr & 0x3b200c00) == 0x38200800; } // Load/store register (unsigned immediate) // | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) | static bool isLoadStoreRegisterUnsigned(uint32_t instr) { return (instr & 0x3b000000) == 0x39000000; } // Rt is always in bit position 0 - 4. static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); } // Rn is always in bit position 5 - 9. static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; } // C4.1.2 Branches, Exception Generating and System instructions // | op0 (3) 1 | 01 op1 (4) | x (22) | // op0 == 010 101 op1 == 0xxx Conditional Branch. // op0 == 110 101 op1 == 1xxx Unconditional Branch Register. // op0 == x00 101 op1 == xxxx Unconditional Branch immediate. // op0 == x01 101 op1 == 0xxx Compare and branch immediate. // op0 == x01 101 op1 == 1xxx Test and branch immediate. static bool isBranch(uint32_t instr) { return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch. ((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg. ((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm. ((instr & 0x7c000000) == 0x34000000); // Compare and test branch. } static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) { return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) || isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) || isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr); } // Note that this function refers to v8.0 only and does not include the // additional load and store instructions added for in later revisions of // the architecture such as the Atomic memory operations introduced // in v8.1. static bool isV8NonStructureLoad(uint32_t instr) { if (isLoadExclusive(instr)) return true; if (isLoadLiteral(instr)) return true; else if (isV8SingleRegisterNonStructureLoadStore(instr)) { // For Load and Store single register, Loads are derived from a // combination of the Size, V and Opc fields. uint32_t size = (instr >> 30) & 0xff; uint32_t v = (instr >> 26) & 0x1; uint32_t opc = (instr >> 22) & 0x3; // For the load and store instructions that we are decoding. // Opc == 0 are all stores. // Opc == 1 with a couple of exceptions are loads. The exceptions are: // Size == 00 (0), V == 1, Opc == 10 (2) which is a store and // Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch. return opc != 0 && !(size == 0 && v == 1 && opc == 2) && !(size == 3 && v == 0 && opc == 2); } return false; } // The following decode instructions are only complete up to the instructions // needed for errata 843419. // Instruction with writeback updates the index register after the load/store. static bool hasWriteback(uint32_t instr) { return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) || isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) || isST1MultiplePost(instr); } // For the load and store class of instructions, a load can write to the // destination register, a load and a store can write to the base register when // the instruction has writeback. static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) { return (isV8NonStructureLoad(instr) && getRt(instr) == reg) || (hasWriteback(instr) && getRn(instr) == reg); } // Scanner for Cortex-A53 errata 843419 // Full details are available in the Cortex A53 MPCore revision 0 Software // Developers Errata Notice (ARM-EPM-048406). // // The instruction sequence that triggers the erratum is common in compiled // AArch64 code, however it is sensitive to the offset of the sequence within // a 4k page. This means that by scanning and fixing the patch after we have // assigned addresses we only need to disassemble and fix instances of the // sequence in the range of affected offsets. // // In summary the erratum conditions are a series of 4 instructions: // 1.) An ADRP instruction that writes to register Rn with low 12 bits of // address of instruction either 0xff8 or 0xffc. // 2.) A load or store instruction that can be: // - A single register load or store, of either integer or vector registers. // - An STP or STNP, of either integer or vector registers. // - An Advanced SIMD ST1 store instruction. // - Must not write to Rn, but may optionally read from it. // 3.) An optional instruction that is not a branch and does not write to Rn. // 4.) A load or store from the Load/store register (unsigned immediate) class // that uses Rn as the base address register. // // Note that we do not attempt to scan for Sequence 2 as described in the // Software Developers Errata Notice as this has been assessed to be extremely // unlikely to occur in compiled code. This matches gold and ld.bfd behavior. // Return true if the Instruction sequence Adrp, Instr2, and Instr4 match // the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.), // and 4.) in the Scanner for Cortex-A53 errata comment above. static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2, uint32_t instr4) { if (!isADRP(instr1)) return false; uint32_t rn = getRt(instr1); return isLoadStoreClass(instr2) && (isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) || isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) || isSTNP(instr2) || isST1(instr2)) && !doesLoadStoreWriteToReg(instr2, rn) && isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn; } // Scan the instruction sequence starting at Offset Off from the base of -// InputSection IS. We update Off in this function rather than in the caller as -// we can skip ahead much further into the section when we know how many +// InputSection isec. We update Off in this function rather than in the caller +// as we can skip ahead much further into the section when we know how many // instructions we've scanned. -// Return the offset of the load or store instruction in IS that we want to +// Return the offset of the load or store instruction in isec that we want to // patch or 0 if no patch required. static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, uint64_t limit) { uint64_t isecAddr = isec->getVA(0); - // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. + // Advance Off so that (isecAddr + Off) modulo 0x1000 is at least 0xff8. uint64_t initialPageOff = (isecAddr + off) & 0xfff; if (initialPageOff < 0xff8) off += 0xff8 - initialPageOff; bool optionalAllowed = limit - off > 12; if (off >= limit || limit - off < 12) { // Need at least 3 4-byte sized instructions to trigger erratum. off = limit; return 0; } uint64_t patchOff = 0; const uint8_t *buf = isec->data().begin(); const ulittle32_t *instBuf = reinterpret_cast(buf + off); uint32_t instr1 = *instBuf++; uint32_t instr2 = *instBuf++; uint32_t instr3 = *instBuf++; if (is843419ErratumSequence(instr1, instr2, instr3)) { patchOff = off + 8; } else if (optionalAllowed && !isBranch(instr3)) { uint32_t instr4 = *instBuf++; if (is843419ErratumSequence(instr1, instr2, instr4)) patchOff = off + 12; } if (((isecAddr + off) & 0xfff) == 0xff8) off += 4; else off += 0xffc; return patchOff; } -class lld::elf::Patch843419Section : public SyntheticSection { +class Patch843419Section : public SyntheticSection { public: Patch843419Section(InputSection *p, uint64_t off); void writeTo(uint8_t *buf) override; size_t getSize() const override { return 8; } uint64_t getLDSTAddr() const; // The Section we are patching. const InputSection *patchee; - // The offset of the instruction in the Patchee section we are patching. + // The offset of the instruction in the patchee section we are patching. uint64_t patcheeOffset; // A label for the start of the Patch that we can use as a relocation target. Symbol *patchSym; }; -lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) +Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4, ".text.patch"), patchee(p), patcheeOffset(off) { this->parent = p->getParent(); patchSym = addSyntheticLocal( saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, getSize(), *this); addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); } -uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { +uint64_t Patch843419Section::getLDSTAddr() const { return patchee->getVA(patcheeOffset); } -void lld::elf::Patch843419Section::writeTo(uint8_t *buf) { +void Patch843419Section::writeTo(uint8_t *buf) { // Copy the instruction that we will be replacing with a branch in the - // Patchee Section. + // patchee Section. write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); - // Apply any relocation transferred from the original PatcheeSection. + // Apply any relocation transferred from the original patchee section. // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc // also adds outSecOff so we need to subtract to avoid double counting. this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); // Return address is the next instruction after the one we have just copied. uint64_t s = getLDSTAddr() + 4; uint64_t p = patchSym->getVA() + 4; target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p); } void AArch64Err843419Patcher::init() { // The AArch64 ABI permits data in executable sections. We must avoid scanning // this data as if it were instructions to avoid false matches. We use the // mapping symbols in the InputObjects to identify this data, caching the // results in sectionMap so we don't have to recalculate it each pass. // The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe // half open intervals [Symbol Value, Next Symbol Value) of code and data // within sections. If there is no next symbol then the half open interval is // [Symbol Value, End of section). The type, code or data, is determined by // the mapping symbol name, $x for code, $d for data. auto isCodeMapSymbol = [](const Symbol *b) { return b->getName() == "$x" || b->getName().startswith("$x."); }; auto isDataMapSymbol = [](const Symbol *b) { return b->getName() == "$d" || b->getName().startswith("$d."); }; // Collect mapping symbols for every executable InputSection. for (InputFile *file : objectFiles) { auto *f = cast>(file); for (Symbol *b : f->getLocalSymbols()) { auto *def = dyn_cast(b); if (!def) continue; if (!isCodeMapSymbol(def) && !isDataMapSymbol(def)) continue; if (auto *sec = dyn_cast_or_null(def->section)) if (sec->flags & SHF_EXECINSTR) sectionMap[sec].push_back(def); } } // For each InputSection make sure the mapping symbols are in sorted in // ascending order and free from consecutive runs of mapping symbols with // the same type. For example we must remove the redundant $d.1 from $x.0 // $d.0 $d.1 $x.1. for (auto &kv : sectionMap) { std::vector &mapSyms = kv.second; - if (mapSyms.size() <= 1) - continue; llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) { return a->value < b->value; }); mapSyms.erase( std::unique(mapSyms.begin(), mapSyms.end(), [=](const Defined *a, const Defined *b) { - return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) || - (isDataMapSymbol(a) && isDataMapSymbol(b)); + return isCodeMapSymbol(a) == isCodeMapSymbol(b); }), mapSyms.end()); + // Always start with a Code Mapping Symbol. + if (!mapSyms.empty() && !isCodeMapSymbol(mapSyms.front())) + mapSyms.erase(mapSyms.begin()); } initialized = true; } // Insert the PatchSections we have created back into the // InputSectionDescription. As inserting patches alters the addresses of // InputSections that follow them, we try and place the patches after all the // executable sections, although we may need to insert them earlier if the // InputSectionDescription is larger than the maximum branch range. void AArch64Err843419Patcher::insertPatches( InputSectionDescription &isd, std::vector &patches) { uint64_t isecLimit; uint64_t prevIsecLimit = isd.sections.front()->outSecOff; uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); uint64_t outSecAddr = isd.sections.front()->getParent()->addr; // Set the outSecOff of patches to the place where we want to insert them. // We use a similar strategy to Thunk placement. Place patches roughly // every multiple of maximum branch range. auto patchIt = patches.begin(); auto patchEnd = patches.end(); for (const InputSection *isec : isd.sections) { isecLimit = isec->outSecOff + isec->getSize(); if (isecLimit > patchUpperBound) { while (patchIt != patchEnd) { if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit) break; (*patchIt)->outSecOff = prevIsecLimit; ++patchIt; } patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); } prevIsecLimit = isecLimit; } for (; patchIt != patchEnd; ++patchIt) { (*patchIt)->outSecOff = isecLimit; } - // merge all patch sections. We use the outSecOff assigned above to + // Merge all patch sections. We use the outSecOff assigned above to // determine the insertion point. This is ok as we only merge into an // InputSectionDescription once per pass, and at the end of the pass // assignAddresses() will recalculate all the outSecOff values. std::vector tmp; tmp.reserve(isd.sections.size() + patches.size()); auto mergeCmp = [](const InputSection *a, const InputSection *b) { - if (a->outSecOff < b->outSecOff) - return true; - if (a->outSecOff == b->outSecOff && isa(a) && - !isa(b)) - return true; - return false; + if (a->outSecOff != b->outSecOff) + return a->outSecOff < b->outSecOff; + return isa(a) && !isa(b); }; std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(), patches.end(), std::back_inserter(tmp), mergeCmp); isd.sections = std::move(tmp); } // Given an erratum sequence that starts at address adrpAddr, with an // instruction that we need to patch at patcheeOffset from the start of -// InputSection IS, create a Patch843419 Section and add it to the +// InputSection isec, create a Patch843419 Section and add it to the // Patches that we need to insert. static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, InputSection *isec, std::vector &patches) { // There may be a relocation at the same offset that we are patching. There // are four cases that we need to consider. // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this // instance of the erratum on a previous patch and altered the relocation. We // have nothing more to do. // Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that // we read will be transformed into a MOVZ later so we actually don't match // the sequence and have nothing more to do. // Case 3: A load/store register (unsigned immediate) class relocation. There // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and // they are both absolute. We need to add the same relocation to the patch, // and replace the relocation with a R_AARCH_JUMP26 branch relocation. // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch // relocation at the offset. auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) { return r.offset == patcheeOffset; }); if (relIt != isec->relocations.end() && (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE)) return; log("detected cortex-a53-843419 erratum sequence starting at " + utohexstr(adrpAddr) + " in unpatched output."); auto *ps = make(isec, patcheeOffset); patches.push_back(ps); auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) { return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym}; }; if (relIt != isec->relocations.end()) { ps->relocations.push_back( {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym}); *relIt = makeRelToPatch(patcheeOffset, ps->patchSym); } else isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym)); } // Scan all the instructions in InputSectionDescription, for each instance of // the erratum sequence create a Patch843419Section. We return the list of -// Patch843419Sections that need to be applied to ISD. +// Patch843419Sections that need to be applied to the InputSectionDescription. std::vector AArch64Err843419Patcher::patchInputSectionDescription( InputSectionDescription &isd) { std::vector patches; for (InputSection *isec : isd.sections) { // LLD doesn't use the erratum sequence in SyntheticSections. if (isa(isec)) continue; // Use sectionMap to make sure we only scan code and not inline data. // We have already sorted MapSyms in ascending order and removed consecutive // mapping symbols of the same type. Our range of executable instructions to // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value, // section size). std::vector &mapSyms = sectionMap[isec]; - auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) { - return ms->getName().startswith("$x"); - }); - + auto codeSym = mapSyms.begin(); while (codeSym != mapSyms.end()) { auto dataSym = std::next(codeSym); uint64_t off = (*codeSym)->value; uint64_t limit = (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value; while (off < limit) { uint64_t startAddr = isec->getVA(off); - if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit)) + if (uint64_t patcheeOffset = + scanCortexA53Errata843419(isec, off, limit)) implementPatch(startAddr, patcheeOffset, isec, patches); } if (dataSym == mapSyms.end()) break; codeSym = std::next(dataSym); } } return patches; } // For each InputSectionDescription make one pass over the executable sections // looking for the erratum sequence; creating a synthetic Patch843419Section // for each instance found. We insert these synthetic patch sections after the // executable code in each InputSectionDescription. // // PreConditions: // The Output and Input Sections have had their final addresses assigned. // // PostConditions: // Returns true if at least one patch was added. The addresses of the // Ouptut and Input Sections may have been changed. // Returns false if no patches were required and no changes were made. bool AArch64Err843419Patcher::createFixes() { - if (initialized == false) + if (!initialized) init(); bool addressesChanged = false; for (OutputSection *os : outputSections) { if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR)) continue; for (BaseCommand *bc : os->sectionCommands) if (auto *isd = dyn_cast(bc)) { std::vector patches = patchInputSectionDescription(*isd); if (!patches.empty()) { insertPatches(*isd, patches); addressesChanged = true; } } } return addressesChanged; } +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/ARMErrataFix.cpp =================================================================== --- vendor/lld/dist/ELF/ARMErrataFix.cpp (nonexistent) +++ vendor/lld/dist/ELF/ARMErrataFix.cpp (revision 353950) @@ -0,0 +1,528 @@ +//===- ARMErrataFix.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file implements Section Patching for the purpose of working around the +// Cortex-a8 erratum 657417 "A 32bit branch instruction that spans 2 4K regions +// can result in an incorrect instruction fetch or processor deadlock." The +// erratum affects all but r1p7, r2p5, r2p6, r3p1 and r3p2 revisions of the +// Cortex-A8. A high level description of the patching technique is given in +// the opening comment of AArch64ErrataFix.cpp. +//===----------------------------------------------------------------------===// + +#include "ARMErrataFix.h" + +#include "Config.h" +#include "LinkerScript.h" +#include "OutputSections.h" +#include "Relocations.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::support; +using namespace llvm::support::endian; + +namespace lld { +namespace elf { + +// The documented title for Erratum 657417 is: +// "A 32bit branch instruction that spans two 4K regions can result in an +// incorrect instruction fetch or processor deadlock". Graphically using a +// 32-bit B.w instruction encoded as a pair of halfwords 0xf7fe 0xbfff +// xxxxxx000 // Memory region 1 start +// target: +// ... +// xxxxxxffe f7fe // First halfword of branch to target: +// xxxxxx000 // Memory region 2 start +// xxxxxx002 bfff // Second halfword of branch to target: +// +// The specific trigger conditions that can be detected at link time are: +// - There is a 32-bit Thumb-2 branch instruction with an address of the form +// xxxxxxFFE. The first 2 bytes of the instruction are in 4KiB region 1, the +// second 2 bytes are in region 2. +// - The branch instruction is one of BLX, BL, B.w BCC.w +// - The instruction preceding the branch is a 32-bit non-branch instruction. +// - The target of the branch is in region 1. +// +// The linker mitigation for the fix is to redirect any branch that meets the +// erratum conditions to a patch section containing a branch to the target. +// +// As adding patch sections may move branches onto region boundaries the patch +// must iterate until no more patches are added. +// +// Example, before: +// 00000FFA func: NOP.w // 32-bit Thumb function +// 00000FFE B.W func // 32-bit branch spanning 2 regions, dest in 1st. +// Example, after: +// 00000FFA func: NOP.w // 32-bit Thumb function +// 00000FFE B.w __CortexA8657417_00000FFE +// 00001002 2 - bytes padding +// 00001004 __CortexA8657417_00000FFE: B.w func + +class Patch657417Section : public SyntheticSection { +public: + Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM); + + void writeTo(uint8_t *buf) override; + + size_t getSize() const override { return 4; } + + // Get the virtual address of the branch instruction at patcheeOffset. + uint64_t getBranchAddr() const; + + // The Section we are patching. + const InputSection *patchee; + // The offset of the instruction in the Patchee section we are patching. + uint64_t patcheeOffset; + // A label for the start of the Patch that we can use as a relocation target. + Symbol *patchSym; + // A decoding of the branch instruction at patcheeOffset. + uint32_t instr; + // True If the patch is to be written in ARM state, otherwise the patch will + // be written in Thumb state. + bool isARM; +}; + +// Return true if the half-word, when taken as the first of a pair of halfwords +// is the first half of a 32-bit instruction. +// Reference from ARM Architecure Reference Manual ARMv7-A and ARMv7-R edition +// section A6.3: 32-bit Thumb instruction encoding +// | HW1 | HW2 | +// | 1 1 1 | op1 (2) | op2 (7) | x (4) |op| x (15) | +// With op1 == 0b00, a 16-bit instruction is encoded. +// +// We test only the first halfword, looking for op != 0b00. +static bool is32bitInstruction(uint16_t hw) { + return (hw & 0xe000) == 0xe000 && (hw & 0x1800) != 0x0000; +} + +// Reference from ARM Architecure Reference Manual ARMv7-A and ARMv7-R edition +// section A6.3.4 Branches and miscellaneous control. +// | HW1 | HW2 | +// | 1 1 1 | 1 0 | op (7) | x (4) | 1 | op1 (3) | op2 (4) | imm8 (8) | +// op1 == 0x0 op != x111xxx | Conditional branch (Bcc.W) +// op1 == 0x1 | Branch (B.W) +// op1 == 1x0 | Branch with Link and Exchange (BLX.w) +// op1 == 1x1 | Branch with Link (BL.W) + +static bool isBcc(uint32_t instr) { + return (instr & 0xf800d000) == 0xf0008000 && + (instr & 0x03800000) != 0x03800000; +} + +static bool isB(uint32_t instr) { return (instr & 0xf800d000) == 0xf0009000; } + +static bool isBLX(uint32_t instr) { return (instr & 0xf800d000) == 0xf000c000; } + +static bool isBL(uint32_t instr) { return (instr & 0xf800d000) == 0xf000d000; } + +static bool is32bitBranch(uint32_t instr) { + return isBcc(instr) || isB(instr) || isBL(instr) || isBLX(instr); +} + +Patch657417Section::Patch657417Section(InputSection *p, uint64_t off, + uint32_t instr, bool isARM) + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4, + ".text.patch"), + patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) { + parent = p->getParent(); + patchSym = addSyntheticLocal( + saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, + isARM ? 0 : 1, getSize(), *this); + addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); +} + +uint64_t Patch657417Section::getBranchAddr() const { + return patchee->getVA(patcheeOffset); +} + +// Given a branch instruction instr at sourceAddr work out its destination +// address. This is only used when the branch instruction has no relocation. +static uint64_t getThumbDestAddr(uint64_t sourceAddr, uint32_t instr) { + uint8_t buf[4]; + write16le(buf, instr >> 16); + write16le(buf + 2, instr & 0x0000ffff); + int64_t offset; + if (isBcc(instr)) + offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP19); + else if (isB(instr)) + offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP24); + else + offset = target->getImplicitAddend(buf, R_ARM_THM_CALL); + return sourceAddr + offset + 4; +} + +void Patch657417Section::writeTo(uint8_t *buf) { + // The base instruction of the patch is always a 32-bit unconditional branch. + if (isARM) + write32le(buf, 0xea000000); + else + write32le(buf, 0x9000f000); + // If we have a relocation then apply it. For a SyntheticSection buf already + // has outSecOff added, but relocateAlloc also adds outSecOff so we need to + // subtract to avoid double counting. + if (!relocations.empty()) { + relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); + return; + } + + // If we don't have a relocation then we must calculate and write the offset + // ourselves. + // Get the destination offset from the addend in the branch instruction. + // We cannot use the instruction in the patchee section as this will have + // been altered to point to us! + uint64_t s = getThumbDestAddr(getBranchAddr(), instr); + uint64_t p = getVA(4); + target->relocateOne(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p); +} + +// Given a branch instruction spanning two 4KiB regions, at offset off from the +// start of isec, return true if the destination of the branch is within the +// first of the two 4Kib regions. +static bool branchDestInFirstRegion(const InputSection *isec, uint64_t off, + uint32_t instr, const Relocation *r) { + uint64_t sourceAddr = isec->getVA(0) + off; + assert((sourceAddr & 0xfff) == 0xffe); + uint64_t destAddr = sourceAddr; + // If there is a branch relocation at the same offset we must use this to + // find the destination address as the branch could be indirected via a thunk + // or the PLT. + if (r) { + uint64_t dst = (r->expr == R_PLT_PC) ? r->sym->getPltVA() : r->sym->getVA(); + // Account for Thumb PC bias, usually cancelled to 0 by addend of -4. + destAddr = dst + r->addend + 4; + } else { + // If there is no relocation, we must have an intra-section branch + // We must extract the offset from the addend manually. + destAddr = getThumbDestAddr(sourceAddr, instr); + } + + return (destAddr & 0xfffff000) == (sourceAddr & 0xfffff000); +} + +// Return true if a branch can reach a patch section placed after isec. +// The Bcc.w instruction has a range of 1 MiB, all others have 16 MiB. +static bool patchInRange(const InputSection *isec, uint64_t off, + uint32_t instr) { + + // We need the branch at source to reach a patch section placed immediately + // after isec. As there can be more than one patch in the patch section we + // add 0x100 as contingency to account for worst case of 1 branch every 4KiB + // for a 1 MiB range. + return target->inBranchRange( + isBcc(instr) ? R_ARM_THM_JUMP19 : R_ARM_THM_JUMP24, isec->getVA(off), + isec->getVA() + isec->getSize() + 0x100); +} + +struct ScanResult { + // Offset of branch within its InputSection. + uint64_t off; + // Cached decoding of the branch instruction. + uint32_t instr; + // Branch relocation at off. Will be nullptr if no relocation exists. + Relocation *rel; +}; + +// Detect the erratum sequence, returning the offset of the branch instruction +// and a decoding of the branch. If the erratum sequence is not found then +// return an offset of 0 for the branch. 0 is a safe value to use for no patch +// as there must be at least one 32-bit non-branch instruction before the +// branch so the minimum offset for a patch is 4. +static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off, + uint64_t limit) { + uint64_t isecAddr = isec->getVA(0); + // Advance Off so that (isecAddr + off) modulo 0x1000 is at least 0xffa. We + // need to check for a 32-bit instruction immediately before a 32-bit branch + // at 0xffe modulo 0x1000. + off = alignTo(isecAddr + off, 0x1000, 0xffa) - isecAddr; + if (off >= limit || limit - off < 8) { + // Need at least 2 4-byte sized instructions to trigger erratum. + off = limit; + return {0, 0, nullptr}; + } + + ScanResult scanRes = {0, 0, nullptr}; + const uint8_t *buf = isec->data().begin(); + // ARMv7-A Thumb 32-bit instructions are encoded 2 consecutive + // little-endian halfwords. + const ulittle16_t *instBuf = reinterpret_cast(buf + off); + uint16_t hw11 = *instBuf++; + uint16_t hw12 = *instBuf++; + uint16_t hw21 = *instBuf++; + uint16_t hw22 = *instBuf++; + if (is32bitInstruction(hw11) && is32bitInstruction(hw21)) { + uint32_t instr1 = (hw11 << 16) | hw12; + uint32_t instr2 = (hw21 << 16) | hw22; + if (!is32bitBranch(instr1) && is32bitBranch(instr2)) { + // Find a relocation for the branch if it exists. This will be used + // to determine the target. + uint64_t branchOff = off + 4; + auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) { + return r.offset == branchOff && + (r.type == R_ARM_THM_JUMP19 || r.type == R_ARM_THM_JUMP24 || + r.type == R_ARM_THM_CALL); + }); + if (relIt != isec->relocations.end()) + scanRes.rel = &(*relIt); + if (branchDestInFirstRegion(isec, branchOff, instr2, scanRes.rel)) { + if (patchInRange(isec, branchOff, instr2)) { + scanRes.off = branchOff; + scanRes.instr = instr2; + } else { + warn(toString(isec->file) + + ": skipping cortex-a8 657417 erratum sequence, section " + + isec->name + " is too large to patch"); + } + } + } + } + off += 0x1000; + return scanRes; +} + +void ARMErr657417Patcher::init() { + // The Arm ABI permits a mix of ARM, Thumb and Data in the same + // InputSection. We must only scan Thumb instructions to avoid false + // matches. We use the mapping symbols in the InputObjects to identify this + // data, caching the results in sectionMap so we don't have to recalculate + // it each pass. + + // The ABI Section 4.5.5 Mapping symbols; defines local symbols that describe + // half open intervals [Symbol Value, Next Symbol Value) of code and data + // within sections. If there is no next symbol then the half open interval is + // [Symbol Value, End of section). The type, code or data, is determined by + // the mapping symbol name, $a for Arm code, $t for Thumb code, $d for data. + auto isArmMapSymbol = [](const Symbol *s) { + return s->getName() == "$a" || s->getName().startswith("$a."); + }; + auto isThumbMapSymbol = [](const Symbol *s) { + return s->getName() == "$t" || s->getName().startswith("$t."); + }; + auto isDataMapSymbol = [](const Symbol *s) { + return s->getName() == "$d" || s->getName().startswith("$d."); + }; + + // Collect mapping symbols for every executable InputSection. + for (InputFile *file : objectFiles) { + auto *f = cast>(file); + for (Symbol *s : f->getLocalSymbols()) { + auto *def = dyn_cast(s); + if (!def) + continue; + if (!isArmMapSymbol(def) && !isThumbMapSymbol(def) && + !isDataMapSymbol(def)) + continue; + if (auto *sec = dyn_cast_or_null(def->section)) + if (sec->flags & SHF_EXECINSTR) + sectionMap[sec].push_back(def); + } + } + // For each InputSection make sure the mapping symbols are in sorted in + // ascending order and are in alternating Thumb, non-Thumb order. + for (auto &kv : sectionMap) { + std::vector &mapSyms = kv.second; + llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) { + return a->value < b->value; + }); + mapSyms.erase(std::unique(mapSyms.begin(), mapSyms.end(), + [=](const Defined *a, const Defined *b) { + return (isThumbMapSymbol(a) == + isThumbMapSymbol(b)); + }), + mapSyms.end()); + // Always start with a Thumb Mapping Symbol + if (!mapSyms.empty() && !isThumbMapSymbol(mapSyms.front())) + mapSyms.erase(mapSyms.begin()); + } + initialized = true; +} + +void ARMErr657417Patcher::insertPatches( + InputSectionDescription &isd, std::vector &patches) { + uint64_t spacing = 0x100000 - 0x7500; + uint64_t isecLimit; + uint64_t prevIsecLimit = isd.sections.front()->outSecOff; + uint64_t patchUpperBound = prevIsecLimit + spacing; + uint64_t outSecAddr = isd.sections.front()->getParent()->addr; + + // Set the outSecOff of patches to the place where we want to insert them. + // We use a similar strategy to initial thunk placement, using 1 MiB as the + // range of the Thumb-2 conditional branch with a contingency accounting for + // thunk generation. + auto patchIt = patches.begin(); + auto patchEnd = patches.end(); + for (const InputSection *isec : isd.sections) { + isecLimit = isec->outSecOff + isec->getSize(); + if (isecLimit > patchUpperBound) { + for (; patchIt != patchEnd; ++patchIt) { + if ((*patchIt)->getBranchAddr() - outSecAddr >= prevIsecLimit) + break; + (*patchIt)->outSecOff = prevIsecLimit; + } + patchUpperBound = prevIsecLimit + spacing; + } + prevIsecLimit = isecLimit; + } + for (; patchIt != patchEnd; ++patchIt) + (*patchIt)->outSecOff = isecLimit; + + // Merge all patch sections. We use the outSecOff assigned above to + // determine the insertion point. This is ok as we only merge into an + // InputSectionDescription once per pass, and at the end of the pass + // assignAddresses() will recalculate all the outSecOff values. + std::vector tmp; + tmp.reserve(isd.sections.size() + patches.size()); + auto mergeCmp = [](const InputSection *a, const InputSection *b) { + if (a->outSecOff != b->outSecOff) + return a->outSecOff < b->outSecOff; + return isa(a) && !isa(b); + }; + std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(), + patches.end(), std::back_inserter(tmp), mergeCmp); + isd.sections = std::move(tmp); +} + +// Given a branch instruction described by ScanRes redirect it to a patch +// section containing an unconditional branch instruction to the target. +// Ensure that this patch section is 4-byte aligned so that the branch cannot +// span two 4 KiB regions. Place the patch section so that it is always after +// isec so the branch we are patching always goes forwards. +static void implementPatch(ScanResult sr, InputSection *isec, + std::vector &patches) { + + log("detected cortex-a8-657419 erratum sequence starting at " + + utohexstr(isec->getVA(sr.off)) + " in unpatched output."); + Patch657417Section *psec; + // We have two cases to deal with. + // Case 1. There is a relocation at patcheeOffset to a symbol. The + // unconditional branch in the patch must have a relocation so that any + // further redirection via the PLT or a Thunk happens as normal. At + // patcheeOffset we redirect the existing relocation to a Symbol defined at + // the start of the patch section. + // + // Case 2. There is no relocation at patcheeOffset. We are unlikely to have + // a symbol that we can use as a target for a relocation in the patch section. + // Luckily we know that the destination cannot be indirected via the PLT or + // a Thunk so we can just write the destination directly. + if (sr.rel) { + // Case 1. We have an existing relocation to redirect to patch and a + // Symbol target. + + // Create a branch relocation for the unconditional branch in the patch. + // This can be redirected via the PLT or Thunks. + RelType patchRelType = R_ARM_THM_JUMP24; + int64_t patchRelAddend = sr.rel->addend; + bool destIsARM = false; + if (isBL(sr.instr) || isBLX(sr.instr)) { + // The final target of the branch may be ARM or Thumb, if the target + // is ARM then we write the patch in ARM state to avoid a state change + // Thunk from the patch to the target. + uint64_t dstSymAddr = (sr.rel->expr == R_PLT_PC) ? sr.rel->sym->getPltVA() + : sr.rel->sym->getVA(); + destIsARM = (dstSymAddr & 1) == 0; + } + psec = make(isec, sr.off, sr.instr, destIsARM); + if (destIsARM) { + // The patch will be in ARM state. Use an ARM relocation and account for + // the larger ARM PC-bias of 8 rather than Thumb's 4. + patchRelType = R_ARM_JUMP24; + patchRelAddend -= 4; + } + psec->relocations.push_back( + Relocation{sr.rel->expr, patchRelType, 0, patchRelAddend, sr.rel->sym}); + // Redirect the existing branch relocation to the patch. + sr.rel->expr = R_PC; + sr.rel->addend = -4; + sr.rel->sym = psec->patchSym; + } else { + // Case 2. We do not have a relocation to the patch. Add a relocation of the + // appropriate type to the patch at patcheeOffset. + + // The destination is ARM if we have a BLX. + psec = make(isec, sr.off, sr.instr, isBLX(sr.instr)); + RelType type; + if (isBcc(sr.instr)) + type = R_ARM_THM_JUMP19; + else if (isB(sr.instr)) + type = R_ARM_THM_JUMP24; + else + type = R_ARM_THM_CALL; + isec->relocations.push_back( + Relocation{R_PC, type, sr.off, -4, psec->patchSym}); + } + patches.push_back(psec); +} + +// Scan all the instructions in InputSectionDescription, for each instance of +// the erratum sequence create a Patch657417Section. We return the list of +// Patch657417Sections that need to be applied to the InputSectionDescription. +std::vector +ARMErr657417Patcher::patchInputSectionDescription( + InputSectionDescription &isd) { + std::vector patches; + for (InputSection *isec : isd.sections) { + // LLD doesn't use the erratum sequence in SyntheticSections. + if (isa(isec)) + continue; + // Use sectionMap to make sure we only scan Thumb code and not Arm or inline + // data. We have already sorted mapSyms in ascending order and removed + // consecutive mapping symbols of the same type. Our range of executable + // instructions to scan is therefore [thumbSym->value, nonThumbSym->value) + // or [thumbSym->value, section size). + std::vector &mapSyms = sectionMap[isec]; + + auto thumbSym = mapSyms.begin(); + while (thumbSym != mapSyms.end()) { + auto nonThumbSym = std::next(thumbSym); + uint64_t off = (*thumbSym)->value; + uint64_t limit = (nonThumbSym == mapSyms.end()) ? isec->data().size() + : (*nonThumbSym)->value; + + while (off < limit) { + ScanResult sr = scanCortexA8Errata657417(isec, off, limit); + if (sr.off) + implementPatch(sr, isec, patches); + } + if (nonThumbSym == mapSyms.end()) + break; + thumbSym = std::next(nonThumbSym); + } + } + return patches; +} + +bool ARMErr657417Patcher::createFixes() { + if (!initialized) + init(); + + bool addressesChanged = false; + for (OutputSection *os : outputSections) { + if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR)) + continue; + for (BaseCommand *bc : os->sectionCommands) + if (auto *isd = dyn_cast(bc)) { + std::vector patches = + patchInputSectionDescription(*isd); + if (!patches.empty()) { + insertPatches(*isd, patches); + addressesChanged = true; + } + } + } + return addressesChanged; +} + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/ARMErrataFix.h =================================================================== --- vendor/lld/dist/ELF/ARMErrataFix.h (nonexistent) +++ vendor/lld/dist/ELF/ARMErrataFix.h (revision 353950) @@ -0,0 +1,51 @@ +//===- ARMErrataFix.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_ARMA8ERRATAFIX_H +#define LLD_ELF_ARMA8ERRATAFIX_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include +#include + +namespace lld { +namespace elf { + +class Defined; +class InputSection; +struct InputSectionDescription; +class OutputSection; +class Patch657417Section; + +class ARMErr657417Patcher { +public: + // Return true if Patches have been added to the OutputSections. + bool createFixes(); + +private: + std::vector + patchInputSectionDescription(InputSectionDescription &isd); + + void insertPatches(InputSectionDescription &isd, + std::vector &patches); + + void init(); + + // A cache of the mapping symbols defined by the InputSection sorted in order + // of ascending value with redundant symbols removed. These describe + // the ranges of code and data in an executable InputSection. + llvm::DenseMap> sectionMap; + + bool initialized = false; +}; + +} // namespace elf +} // namespace lld + +#endif Index: vendor/lld/dist/ELF/Arch/AArch64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/AArch64.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/AArch64.cpp (revision 353950) @@ -1,590 +1,686 @@ //===- AArch64.cpp --------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + // Page(Expr) is the page address of the expression Expr, defined // as (Expr & ~0xFFF). (This applies even if the machine page size // supported by the platform has a different value.) -uint64_t elf::getAArch64Page(uint64_t expr) { +uint64_t getAArch64Page(uint64_t expr) { return expr & ~static_cast(0xFFF); } namespace { class AArch64 : public TargetInfo { public: AArch64(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; bool needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; bool usesOnlyLowPageBits(RelType type) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const override; void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace AArch64::AArch64() { copyRel = R_AARCH64_COPY; relativeRel = R_AARCH64_RELATIVE; iRelativeRel = R_AARCH64_IRELATIVE; gotRel = R_AARCH64_GLOB_DAT; noneRel = R_AARCH64_NONE; pltRel = R_AARCH64_JUMP_SLOT; symbolicRel = R_AARCH64_ABS64; tlsDescRel = R_AARCH64_TLSDESC; tlsGotRel = R_AARCH64_TLS_TPREL64; pltEntrySize = 16; pltHeaderSize = 32; defaultMaxPageSize = 65536; // Align to the 2 MiB page size (known as a superpage or huge page). // FreeBSD automatically promotes 2 MiB-aligned allocations. defaultImageBase = 0x200000; needsThunks = true; } RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_AARCH64_ABS16: + case R_AARCH64_ABS32: + case R_AARCH64_ABS64: + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_MOVW_SABS_G0: + case R_AARCH64_MOVW_SABS_G1: + case R_AARCH64_MOVW_SABS_G2: + case R_AARCH64_MOVW_UABS_G0: + case R_AARCH64_MOVW_UABS_G0_NC: + case R_AARCH64_MOVW_UABS_G1: + case R_AARCH64_MOVW_UABS_G1_NC: + case R_AARCH64_MOVW_UABS_G2: + case R_AARCH64_MOVW_UABS_G2_NC: + case R_AARCH64_MOVW_UABS_G3: + return R_ABS; case R_AARCH64_TLSDESC_ADR_PAGE21: return R_AARCH64_TLSDESC_PAGE; case R_AARCH64_TLSDESC_LD64_LO12: case R_AARCH64_TLSDESC_ADD_LO12: return R_TLSDESC; case R_AARCH64_TLSDESC_CALL: return R_TLSDESC_CALL; case R_AARCH64_TLSLE_ADD_TPREL_HI12: case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: + case R_AARCH64_TLSLE_MOVW_TPREL_G0: + case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: + case R_AARCH64_TLSLE_MOVW_TPREL_G1: + case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: + case R_AARCH64_TLSLE_MOVW_TPREL_G2: return R_TLS; case R_AARCH64_CALL26: case R_AARCH64_CONDBR19: case R_AARCH64_JUMP26: case R_AARCH64_TSTBR14: return R_PLT_PC; case R_AARCH64_PREL16: case R_AARCH64_PREL32: case R_AARCH64_PREL64: case R_AARCH64_ADR_PREL_LO21: case R_AARCH64_LD_PREL_LO19: + case R_AARCH64_MOVW_PREL_G0: + case R_AARCH64_MOVW_PREL_G0_NC: + case R_AARCH64_MOVW_PREL_G1: + case R_AARCH64_MOVW_PREL_G1_NC: + case R_AARCH64_MOVW_PREL_G2: + case R_AARCH64_MOVW_PREL_G2_NC: + case R_AARCH64_MOVW_PREL_G3: return R_PC; case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_ADR_PREL_PG_HI21_NC: return R_AARCH64_PAGE_PC; case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return R_AARCH64_GOT_PAGE_PC; case R_AARCH64_NONE: return R_NONE; default: - return R_ABS; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const { if (expr == R_RELAX_TLS_GD_TO_IE) { if (type == R_AARCH64_TLSDESC_ADR_PAGE21) return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; return R_RELAX_TLS_GD_TO_IE_ABS; } return expr; } bool AArch64::usesOnlyLowPageBits(RelType type) const { switch (type) { default: return false; case R_AARCH64_ADD_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_LD64_LO12: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return true; } } RelType AArch64::getDynRel(RelType type) const { if (type == R_AARCH64_ABS64) return type; return R_AARCH64_NONE; } void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { write64le(buf, in.plt->getVA()); } void AArch64::writePltHeader(uint8_t *buf) const { const uint8_t pltData[] = { 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) 0x20, 0x02, 0x1f, 0xd6, // br x17 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5 // nop }; memcpy(buf, pltData, sizeof(pltData)); uint64_t got = in.gotPlt->getVA(); uint64_t plt = in.plt->getVA(); relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, getAArch64Page(got + 16) - getAArch64Page(plt + 4)); relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); } void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t inst[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) 0x20, 0x02, 0x1f, 0xd6 // br x17 }; memcpy(buf, inst, sizeof(inst)); relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); } bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const { // ELF for the ARM 64-bit architecture, section Call and Jump relocations // only permits range extension thunks for R_AARCH64_CALL26 and // R_AARCH64_JUMP26 relocation types. if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) return false; uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); return !inBranchRange(type, branchAddr, dst); } uint32_t AArch64::getThunkSectionSpacing() const { // See comment in Arch/ARM.cpp for a more detailed explanation of // getThunkSectionSpacing(). For AArch64 the only branches we are permitted to // Thunk have a range of +/- 128 MiB return (128 * 1024 * 1024) - 0x30000; } bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) return true; // The AArch64 call and unconditional branch instructions have a range of // +/- 128 MiB. uint64_t range = 128 * 1024 * 1024; if (dst > src) { // Immediate of branch is signed. range -= 4; return dst - src <= range; } return src - dst <= range; } static void write32AArch64Addr(uint8_t *l, uint64_t imm) { uint32_t immLo = (imm & 0x3) << 29; uint32_t immHi = (imm & 0x1FFFFC) << 3; uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); write32le(l, (read32le(l) & ~mask) | immLo | immHi); } // Return the bits [Start, End] from Val shifted Start bits. // For instance, getBits(0xF0, 4, 8) returns 0xF. static uint64_t getBits(uint64_t val, int start, int end) { uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1; return (val >> start) & mask; } static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } // Update the immediate field in a AARCH64 ldr, str, and add instruction. static void or32AArch64Imm(uint8_t *l, uint64_t imm) { or32le(l, (imm & 0xFFF) << 10); } +// Update the immediate field in an AArch64 movk, movn or movz instruction +// for a signed relocation, and update the opcode of a movn or movz instruction +// to match the sign of the operand. +static void writeSMovWImm(uint8_t *loc, uint32_t imm) { + uint32_t inst = read32le(loc); + // Opcode field is bits 30, 29, with 10 = movz, 00 = movn and 11 = movk. + if (!(inst & (1 << 29))) { + // movn or movz. + if (imm & 0x10000) { + // Change opcode to movn, which takes an inverted operand. + imm ^= 0xFFFF; + inst &= ~(1 << 30); + } else { + // Change opcode to movz. + inst |= 1 << 30; + } + } + write32le(loc, inst | ((imm & 0xFFFF) << 5)); +} + void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: checkIntUInt(loc, val, 16, type); write16le(loc, val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: checkIntUInt(loc, val, 32, type); write32le(loc, val); break; case R_AARCH64_ABS64: case R_AARCH64_PREL64: write64le(loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: or32AArch64Imm(loc, val); break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: checkInt(loc, val, 33, type); LLVM_FALLTHROUGH; case R_AARCH64_ADR_PREL_PG_HI21_NC: write32AArch64Addr(loc, val >> 12); break; case R_AARCH64_ADR_PREL_LO21: checkInt(loc, val, 21, type); write32AArch64Addr(loc, val); break; case R_AARCH64_JUMP26: // Normally we would just write the bits of the immediate field, however // when patching instructions for the cpu errata fix -fix-cortex-a53-843419 // we want to replace a non-branch instruction with a branch immediate // instruction. By writing all the bits of the instruction including the // opcode and the immediate (0 001 | 01 imm26) we can do this // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of // the instruction we want to patch. write32le(loc, 0x14000000); LLVM_FALLTHROUGH; case R_AARCH64_CALL26: checkInt(loc, val, 28, type); or32le(loc, (val & 0x0FFFFFFC) >> 2); break; case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: checkAlignment(loc, val, 4, type); checkInt(loc, val, 21, type); or32le(loc, (val & 0x1FFFFC) << 3); break; case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: or32AArch64Imm(loc, getBits(val, 0, 11)); break; case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: checkAlignment(loc, val, 2, type); or32AArch64Imm(loc, getBits(val, 1, 11)); break; case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: checkAlignment(loc, val, 4, type); or32AArch64Imm(loc, getBits(val, 2, 11)); break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: checkAlignment(loc, val, 8, type); or32AArch64Imm(loc, getBits(val, 3, 11)); break; case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: checkAlignment(loc, val, 16, type); or32AArch64Imm(loc, getBits(val, 4, 11)); break; + case R_AARCH64_MOVW_UABS_G0: + checkUInt(loc, val, 16, type); + LLVM_FALLTHROUGH; case R_AARCH64_MOVW_UABS_G0_NC: or32le(loc, (val & 0xFFFF) << 5); break; + case R_AARCH64_MOVW_UABS_G1: + checkUInt(loc, val, 32, type); + LLVM_FALLTHROUGH; case R_AARCH64_MOVW_UABS_G1_NC: or32le(loc, (val & 0xFFFF0000) >> 11); break; + case R_AARCH64_MOVW_UABS_G2: + checkUInt(loc, val, 48, type); + LLVM_FALLTHROUGH; case R_AARCH64_MOVW_UABS_G2_NC: or32le(loc, (val & 0xFFFF00000000) >> 27); break; case R_AARCH64_MOVW_UABS_G3: or32le(loc, (val & 0xFFFF000000000000) >> 43); break; + case R_AARCH64_MOVW_PREL_G0: + case R_AARCH64_MOVW_SABS_G0: + case R_AARCH64_TLSLE_MOVW_TPREL_G0: + checkInt(loc, val, 17, type); + LLVM_FALLTHROUGH; + case R_AARCH64_MOVW_PREL_G0_NC: + case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: + writeSMovWImm(loc, val); + break; + case R_AARCH64_MOVW_PREL_G1: + case R_AARCH64_MOVW_SABS_G1: + case R_AARCH64_TLSLE_MOVW_TPREL_G1: + checkInt(loc, val, 33, type); + LLVM_FALLTHROUGH; + case R_AARCH64_MOVW_PREL_G1_NC: + case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: + writeSMovWImm(loc, val >> 16); + break; + case R_AARCH64_MOVW_PREL_G2: + case R_AARCH64_MOVW_SABS_G2: + case R_AARCH64_TLSLE_MOVW_TPREL_G2: + checkInt(loc, val, 49, type); + LLVM_FALLTHROUGH; + case R_AARCH64_MOVW_PREL_G2_NC: + writeSMovWImm(loc, val >> 32); + break; + case R_AARCH64_MOVW_PREL_G3: + writeSMovWImm(loc, val >> 48); + break; case R_AARCH64_TSTBR14: checkInt(loc, val, 16, type); or32le(loc, (val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: checkUInt(loc, val, 24, type); or32AArch64Imm(loc, val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case R_AARCH64_TLSDESC_ADD_LO12: or32AArch64Imm(loc, val); break; default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + llvm_unreachable("unknown relocation"); } } void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] // add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12] // .tlsdesccall [R_AARCH64_TLSDESC_CALL] // blr x1 // And it can optimized to: // movz x0, #0x0, lsl #16 // movk x0, #0x10 // nop // nop checkUInt(loc, val, 32, type); switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: write32le(loc, 0xd503201f); // nop return; case R_AARCH64_TLSDESC_ADR_PAGE21: write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz return; case R_AARCH64_TLSDESC_LD64_LO12: write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk return; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] // add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12] // .tlsdesccall [R_AARCH64_TLSDESC_CALL] // blr x1 // And it can optimized to: // adrp x0, :gottprel:v // ldr x0, [x0, :gottprel_lo12:v] // nop // nop switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: write32le(loc, 0xd503201f); // nop break; case R_AARCH64_TLSDESC_ADR_PAGE21: write32le(loc, 0x90000000); // adrp relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); break; case R_AARCH64_TLSDESC_LD64_LO12: write32le(loc, 0xf9400000); // ldr relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { checkUInt(loc, val, 32, type); if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { // Generate MOVZ. uint32_t regNo = read32le(loc) & 0x1f; write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5)); return; } if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { // Generate MOVK. uint32_t regNo = read32le(loc) & 0x1f; write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5)); return; } llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); } // AArch64 may use security features in variant PLT sequences. These are: // Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target // Indicator (BTI) introduced in armv8.5-a. The additional instructions used // in the variant Plt sequences are encoded in the Hint space so they can be // deployed on older architectures, which treat the instructions as a nop. // PAC and BTI can be combined leading to the following combinations: // writePltHeader // writePltHeaderBti (no PAC Header needed) // writePlt // writePltBti (BTI only) // writePltPac (PAC only) // writePltBtiPac (BTI and PAC) // // When PAC is enabled the dynamic loader encrypts the address that it places // in the .got.plt using the pacia1716 instruction which encrypts the value in // x17 using the modifier in x16. The static linker places autia1716 before the // indirect branch to x17 to authenticate the address in x17 with the modifier // in x16. This makes it more difficult for an attacker to modify the value in // the .got.plt. // // When BTI is enabled all indirect branches must land on a bti instruction. // The static linker must place a bti instruction at the start of any PLT entry // that may be the target of an indirect branch. As the PLT entries call the // lazy resolver indirectly this must have a bti instruction at start. In // general a bti instruction is not needed for a PLT entry as indirect calls // are resolved to the function address and not the PLT entry for the function. // There are a small number of cases where the PLT address can escape, such as // taking the address of a function or ifunc via a non got-generating // relocation, and a shared library refers to that symbol. // // We use the bti c variant of the instruction which permits indirect branches // (br) via x16/x17 and indirect function calls (blr) via any register. The ABI // guarantees that all indirect branches from code requiring BTI protection // will go via x16/x17 namespace { class AArch64BtiPac final : public AArch64 { public: AArch64BtiPac(); void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; private: bool btiHeader; // bti instruction needed in PLT Header bool btiEntry; // bti instruction needed in PLT Entry bool pacEntry; // autia1716 instruction needed in PLT Entry }; } // namespace AArch64BtiPac::AArch64BtiPac() { btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); // A BTI (Branch Target Indicator) Plt Entry is only required if the // address of the PLT entry can be taken by the program, which permits an // indirect jump to the PLT entry. This can happen when the address // of the PLT entry for a function is canonicalised due to the address of // the function in an executable being taken by a shared library. // FIXME: There is a potential optimization to omit the BTI if we detect // that the address of the PLT entry isn't taken. btiEntry = btiHeader && !config->shared; pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); if (btiEntry || pacEntry) pltEntrySize = 24; } void AArch64BtiPac::writePltHeader(uint8_t *buf) const { const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c const uint8_t pltData[] = { 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) 0x20, 0x02, 0x1f, 0xd6, // br x17 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5 // nop }; const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop uint64_t got = in.gotPlt->getVA(); uint64_t plt = in.plt->getVA(); if (btiHeader) { // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C // instruction. memcpy(buf, btiData, sizeof(btiData)); buf += sizeof(btiData); plt += sizeof(btiData); } memcpy(buf, pltData, sizeof(pltData)); relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, getAArch64Page(got + 16) - getAArch64Page(plt + 8)); relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); if (!btiHeader) // We didn't add the BTI c instruction so round out size with NOP. memcpy(buf + sizeof(pltData), nopData, sizeof(nopData)); } void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { // The PLT entry is of the form: // [btiData] addrInst (pacBr | stdBr) [nopData] const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c const uint8_t addrInst[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n])) }; const uint8_t pacBr[] = { 0x9f, 0x21, 0x03, 0xd5, // autia1716 0x20, 0x02, 0x1f, 0xd6 // br x17 }; const uint8_t stdBr[] = { 0x20, 0x02, 0x1f, 0xd6, // br x17 0x1f, 0x20, 0x03, 0xd5 // nop }; const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop if (btiEntry) { memcpy(buf, btiData, sizeof(btiData)); buf += sizeof(btiData); pltEntryAddr += sizeof(btiData); } memcpy(buf, addrInst, sizeof(addrInst)); relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); if (pacEntry) memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); else memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr)); if (!btiEntry) // We didn't add the BTI c instruction so round out size with NOP. memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); } static TargetInfo *getTargetInfo() { if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { static AArch64BtiPac t; return &t; } static AArch64 t; return &t; } -TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } +TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/AMDGPU.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/AMDGPU.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/AMDGPU.cpp (revision 353950) @@ -1,113 +1,117 @@ //===- AMDGPU.cpp ---------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class AMDGPU final : public TargetInfo { public: AMDGPU(); uint32_t calcEFlags() const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; }; } // namespace AMDGPU::AMDGPU() { relativeRel = R_AMDGPU_RELATIVE64; gotRel = R_AMDGPU_ABS64; noneRel = R_AMDGPU_NONE; symbolicRel = R_AMDGPU_ABS64; } static uint32_t getEFlags(InputFile *file) { return cast>(file)->getObj().getHeader()->e_flags; } uint32_t AMDGPU::calcEFlags() const { assert(!objectFiles.empty()); uint32_t ret = getEFlags(objectFiles[0]); // Verify that all input files have the same e_flags. for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { if (ret == getEFlags(f)) continue; error("incompatible e_flags: " + toString(f)); return 0; } return ret; } void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: write32le(loc, val); break; case R_AMDGPU_ABS64: case R_AMDGPU_REL64: write64le(loc, val); break; case R_AMDGPU_GOTPCREL32_HI: case R_AMDGPU_REL32_HI: write32le(loc, val >> 32); break; default: llvm_unreachable("unknown relocation"); } } RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_ABS64: return R_ABS; case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: case R_AMDGPU_REL32_HI: case R_AMDGPU_REL64: return R_PC; case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: case R_AMDGPU_GOTPCREL32_HI: return R_GOT_PC; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); return R_NONE; } } RelType AMDGPU::getDynRel(RelType type) const { if (type == R_AMDGPU_ABS64) return type; return R_AMDGPU_NONE; } -TargetInfo *elf::getAMDGPUTargetInfo() { +TargetInfo *getAMDGPUTargetInfo() { static AMDGPU target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/ARM.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/ARM.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/ARM.cpp (revision 353950) @@ -1,606 +1,610 @@ //===- ARM.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class ARM final : public TargetInfo { public: ARM(); uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; void addPltSymbols(InputSection &isec, uint64_t off) const override; void addPltHeaderSymbols(InputSection &isd) const override; bool needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace ARM::ARM() { copyRel = R_ARM_COPY; relativeRel = R_ARM_RELATIVE; iRelativeRel = R_ARM_IRELATIVE; gotRel = R_ARM_GLOB_DAT; noneRel = R_ARM_NONE; pltRel = R_ARM_JUMP_SLOT; symbolicRel = R_ARM_ABS32; tlsGotRel = R_ARM_TLS_TPOFF32; tlsModuleIndexRel = R_ARM_TLS_DTPMOD32; tlsOffsetRel = R_ARM_TLS_DTPOFF32; gotBaseSymInGotPlt = false; pltEntrySize = 16; pltHeaderSize = 32; trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; needsThunks = true; } uint32_t ARM::calcEFlags() const { // The ABIFloatType is used by loaders to detect the floating point calling // convention. uint32_t abiFloatType = 0; if (config->armVFPArgs == ARMVFPArgKind::Base || config->armVFPArgs == ARMVFPArgKind::Default) abiFloatType = EF_ARM_ABI_FLOAT_SOFT; else if (config->armVFPArgs == ARMVFPArgKind::VFP) abiFloatType = EF_ARM_ABI_FLOAT_HARD; // We don't currently use any features incompatible with EF_ARM_EABI_VER5, // but we don't have any firm guarantees of conformance. Linux AArch64 // kernels (as of 2016) require an EABI version to be set. return EF_ARM_EABI_VER5 | abiFloatType; } RelExpr ARM::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { case R_ARM_THM_JUMP11: return R_PC; case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_PREL31: case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: return R_PLT_PC; case R_ARM_GOTOFF32: // (S + A) - GOT_ORG return R_GOTREL; case R_ARM_GOT_BREL: // GOT(S) + A - GOT_ORG return R_GOT_OFF; case R_ARM_GOT_PREL: case R_ARM_TLS_IE32: // GOT(S) + A - P return R_GOT_PC; case R_ARM_SBREL32: return R_ARM_SBREL; case R_ARM_TARGET1: return config->target1Rel ? R_PC : R_ABS; case R_ARM_TARGET2: if (config->target2 == Target2Policy::Rel) return R_PC; if (config->target2 == Target2Policy::Abs) return R_ABS; return R_GOT_PC; case R_ARM_TLS_GD32: return R_TLSGD_PC; case R_ARM_TLS_LDM32: return R_TLSLD_PC; case R_ARM_BASE_PREL: // B(S) + A - P // FIXME: currently B(S) assumed to be .got, this may not hold for all // platforms. return R_GOTONLY_PC; case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_REL32: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: return R_PC; case R_ARM_NONE: return R_NONE; case R_ARM_TLS_LE32: return R_TLS; case R_ARM_V4BX: // V4BX is just a marker to indicate there's a "bx rN" instruction at the // given address. It can be used to implement a special linker mode which // rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and // not ARMv4 output, we can just ignore it. return R_HINT; default: return R_ABS; } } RelType ARM::getDynRel(RelType type) const { if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel)) return R_ARM_ABS32; return R_ARM_NONE; } void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const { write32le(buf, in.plt->getVA()); } void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const { // An ARM entry is the address of the ifunc resolver function. write32le(buf, s.getVA()); } // Long form PLT Header that does not have any restrictions on the displacement // of the .plt from the .plt.got. static void writePltHeaderLong(uint8_t *buf) { const uint8_t pltData[] = { 0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]! 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2 0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr 0x08, 0xf0, 0xbe, 0xe5, // ldr pc, [lr, #8] 0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4}; memcpy(buf, pltData, sizeof(pltData)); uint64_t gotPlt = in.gotPlt->getVA(); uint64_t l1 = in.plt->getVA() + 8; write32le(buf + 16, gotPlt - l1 - 8); } // The default PLT header requires the .plt.got to be within 128 Mb of the // .plt in the positive direction. void ARM::writePltHeader(uint8_t *buf) const { // Use a similar sequence to that in writePlt(), the difference is the calling // conventions mean we use lr instead of ip. The PLT entry is responsible for // saving lr on the stack, the dynamic loader is responsible for reloading // it. const uint32_t pltData[] = { 0xe52de004, // L1: str lr, [sp,#-4]! 0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4) 0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4) 0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4) }; uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4; if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. writePltHeaderLong(buf); return; } write32le(buf + 0, pltData[0]); write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff)); write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff)); write32le(buf + 12, pltData[3] | (offset & 0xfff)); memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary memcpy(buf + 20, trapInstr.data(), 4); memcpy(buf + 24, trapInstr.data(), 4); memcpy(buf + 28, trapInstr.data(), 4); } void ARM::addPltHeaderSymbols(InputSection &isec) const { addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec); addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec); } // Long form PLT entries that do not have any restrictions on the displacement // of the .plt from the .plt.got. static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) { const uint8_t pltData[] = { 0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc 0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8 }; memcpy(buf, pltData, sizeof(pltData)); uint64_t l1 = pltEntryAddr + 4; write32le(buf + 12, gotPltEntryAddr - l1 - 8); } // The default PLT entries require the .plt.got to be within 128 Mb of the // .plt in the positive direction. void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { // The PLT entry is similar to the example given in Appendix A of ELF for // the Arm Architecture. Instead of using the Group Relocations to find the // optimal rotation for the 8-bit immediate used in the add instructions we // hard code the most compact rotations for simplicity. This saves a load // instruction over the long plt sequences. const uint32_t pltData[] = { 0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8 0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8 0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8 }; uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8; if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff); return; } write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff)); write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff)); write32le(buf + 8, pltData[2] | (offset & 0xfff)); memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary } void ARM::addPltSymbols(InputSection &isec, uint64_t off) const { addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec); addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec); } bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const { // If S is an undefined weak symbol and does not have a PLT entry then it // will be resolved as a branch to the next instruction. if (s.isUndefWeak() && !s.isInPlt()) return false; // A state change from ARM to Thumb and vice versa must go through an // interworking thunk if the relocation type is not R_ARM_CALL or // R_ARM_THM_CALL. switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: // Source is ARM, all PLT entries are ARM so no interworking required. // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). if (expr == R_PC && ((s.getVA() & 1) == 1)) return true; LLVM_FALLTHROUGH; case R_ARM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); return !inBranchRange(type, branchAddr, dst); } case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: // Source is Thumb, all PLT entries are ARM so interworking is required. // Otherwise we need to interwork if Symbol has bit 0 clear (ARM). if (expr == R_PLT_PC || ((s.getVA() & 1) == 0)) return true; LLVM_FALLTHROUGH; case R_ARM_THM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); return !inBranchRange(type, branchAddr, dst); } } return false; } uint32_t ARM::getThunkSectionSpacing() const { // The placing of pre-created ThunkSections is controlled by the value // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to // place the ThunkSection such that all branches from the InputSections // prior to the ThunkSection can reach a Thunk placed at the end of the // ThunkSection. Graphically: // | up to thunkSectionSpacing .text input sections | // | ThunkSection | // | up to thunkSectionSpacing .text input sections | // | ThunkSection | // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This // is to match the most common expected case of a Thumb 2 encoded BL, BLX or // B.W: // ARM B, BL, BLX range +/- 32MiB // Thumb B.W, BL, BLX range +/- 16MiB // Thumb B.W range +/- 1MiB // If a branch cannot reach a pre-created ThunkSection a new one will be // created so we can handle the rare cases of a Thumb 2 conditional branch. // We intentionally use a lower size for thunkSectionSpacing than the maximum // branch range so the end of the ThunkSection is more likely to be within // range of the branch instruction that is furthest away. The value we shorten // thunkSectionSpacing by is set conservatively to allow us to create 16,384 // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to // one of the Thunks going out of range. // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except // ARMv6T2) the range is +/- 4MiB. return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000 : 0x400000 - 0x7500; } bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { uint64_t range; uint64_t instrSize; switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: case R_ARM_CALL: range = 0x2000000; instrSize = 4; break; case R_ARM_THM_JUMP19: range = 0x100000; instrSize = 2; break; case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; instrSize = 2; break; default: return true; } // PC at Src is 2 instructions ahead, immediate of branch is signed if (src > dst) range -= 2 * instrSize; else range += instrSize; if ((dst & 0x1) == 0) // Destination is ARM, if ARM caller then Src is already 4-byte aligned. // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure // destination will be 4 byte aligned. src &= ~0x3; else // Bit 0 == 1 denotes Thumb state, it is not part of the range dst &= ~0x1; uint64_t distance = (src > dst) ? src - dst : dst - src; return distance <= range; } void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: case R_ARM_REL32: case R_ARM_RELATIVE: case R_ARM_SBREL32: case R_ARM_TARGET1: case R_ARM_TARGET2: case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: case R_ARM_TLS_LDM32: case R_ARM_TLS_LDO32: case R_ARM_TLS_LE32: case R_ARM_TLS_TPOFF32: case R_ARM_TLS_DTPOFF32: write32le(loc, val); break; case R_ARM_PREL31: checkInt(loc, val, 31, type); write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); break; case R_ARM_CALL: // R_ARM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction if (val & 1) { // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' checkInt(loc, val, 26, type); write32le(loc, 0xfa000000 | // opcode ((val & 2) << 23) | // H ((val >> 2) & 0x00ffffff)); // imm24 break; } if ((read32le(loc) & 0xfe000000) == 0xfa000000) // BLX (always unconditional) instruction to an ARM Target, select an // unconditional BL. write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B LLVM_FALLTHROUGH; case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: checkInt(loc, val, 26, type); write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); break; case R_ARM_THM_JUMP11: checkInt(loc, val, 12, type); write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); break; case R_ARM_THM_JUMP19: // Encoding T3: Val = S:J2:J1:imm6:imm11:0 checkInt(loc, val, 21, type); write16le(loc, (read16le(loc) & 0xfbc0) | // opcode cond ((val >> 10) & 0x0400) | // S ((val >> 12) & 0x003f)); // imm6 write16le(loc + 2, 0x8000 | // opcode ((val >> 8) & 0x0800) | // J2 ((val >> 5) & 0x2000) | // J1 ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_THM_CALL: // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction if ((val & 1) == 0) { // Ensure BLX destination is 4-byte aligned. As BLX instruction may // only be two byte aligned. This must be done before overflow check val = alignTo(val, 4); } // Bit 12 is 0 for BLX, 1 for BL write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. checkInt(loc, val, 23, type); write16le(loc, 0xf000 | // opcode ((val >> 12) & 0x07ff)); // imm11 write16le(loc + 2, (read16le(loc + 2) & 0xd000) | // opcode 0x2800 | // J1 == J2 == 1 ((val >> 1) & 0x07ff)); // imm11 break; } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 checkInt(loc, val, 25, type); write16le(loc, 0xf000 | // opcode ((val >> 14) & 0x0400) | // S ((val >> 12) & 0x03ff)); // imm10 write16le(loc + 2, (read16le(loc + 2) & 0xd000) | // opcode (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1 (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2 ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVW_PREL_NC: write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) | (val & 0x0fff)); break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: write32le(loc, (read32le(loc) & ~0x000f0fff) | (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff)); break; case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVT_PREL: // Encoding T1: A = imm4:i:imm3:imm8 write16le(loc, 0xf2c0 | // opcode ((val >> 17) & 0x0400) | // i ((val >> 28) & 0x000f)); // imm4 write16le(loc + 2, (read16le(loc + 2) & 0x8f00) | // opcode ((val >> 12) & 0x7000) | // imm3 ((val >> 16) & 0x00ff)); // imm8 break; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVW_PREL_NC: // Encoding T3: A = imm4:i:imm3:imm8 write16le(loc, 0xf240 | // opcode ((val >> 1) & 0x0400) | // i ((val >> 12) & 0x000f)); // imm4 write16le(loc + 2, (read16le(loc + 2) & 0x8f00) | // opcode ((val << 4) & 0x7000) | // imm3 (val & 0x00ff)); // imm8 break; default: error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { default: return 0; case R_ARM_ABS32: case R_ARM_BASE_PREL: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: case R_ARM_REL32: case R_ARM_TARGET1: case R_ARM_TARGET2: case R_ARM_TLS_GD32: case R_ARM_TLS_LDM32: case R_ARM_TLS_LDO32: case R_ARM_TLS_IE32: case R_ARM_TLS_LE32: return SignExtend64<32>(read32le(buf)); case R_ARM_PREL31: return SignExtend64<31>(read32le(buf)); case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: return SignExtend64<26>(read32le(buf) << 2); case R_ARM_THM_JUMP11: return SignExtend64<12>(read16le(buf) << 1); case R_ARM_THM_JUMP19: { // Encoding T3: A = S:J2:J1:imm10:imm6:0 uint16_t hi = read16le(buf); uint16_t lo = read16le(buf + 2); return SignExtend64<20>(((hi & 0x0400) << 10) | // S ((lo & 0x0800) << 8) | // J2 ((lo & 0x2000) << 5) | // J1 ((hi & 0x003f) << 12) | // imm6 ((lo & 0x07ff) << 1)); // imm11:0 } case R_ARM_THM_CALL: if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. uint16_t hi = read16le(buf); uint16_t lo = read16le(buf + 2); return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11 ((lo & 0x7ff) << 1)); // imm11:0 break; } LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: { // Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0 // I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S) uint16_t hi = read16le(buf); uint16_t lo = read16le(buf + 2); return SignExtend64<24>(((hi & 0x0400) << 14) | // S (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1 (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2 ((hi & 0x003ff) << 12) | // imm0 ((lo & 0x007ff) << 1)); // imm11:0 } // ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and // MOVT is in the range -32768 <= A < 32768 case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: { uint64_t val = read32le(buf) & 0x000f0fff; return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff)); } case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: { // Encoding T3: A = imm4:i:imm3:imm8 uint16_t hi = read16le(buf); uint16_t lo = read16le(buf + 2); return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4 ((hi & 0x0400) << 1) | // i ((lo & 0x7000) >> 4) | // imm3 (lo & 0x00ff)); // imm8 } } } -TargetInfo *elf::getARMTargetInfo() { +TargetInfo *getARMTargetInfo() { static ARM target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/AVR.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/AVR.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/AVR.cpp (revision 353950) @@ -1,76 +1,80 @@ //===- AVR.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // AVR is a Harvard-architecture 8-bit micrcontroller designed for small // baremetal programs. All AVR-family processors have 32 8-bit registers. // The tiniest AVR has 32 byte RAM and 1 KiB program memory, and the largest // one supports up to 2^24 data address space and 2^22 code address space. // // Since it is a baremetal programming, there's usually no loader to load // ELF files on AVRs. You are expected to link your program against address // 0 and pull out a .text section from the result using objcopy, so that you // can write the linked code to on-chip flush memory. You can do that with // the following commands: // // ld.lld -Ttext=0 -o foo foo.o // objcopy -O binary --only-section=.text foo output.bin // // Note that the current AVR support is very preliminary so you can't // link any useful program yet, though. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class AVR final : public TargetInfo { public: AVR(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace AVR::AVR() { noneRel = R_AVR_NONE; } RelExpr AVR::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { return R_ABS; } void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_AVR_CALL: { uint16_t hi = val >> 17; uint16_t lo = val >> 1; write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1)); write16le(loc + 2, lo); break; } default: error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -TargetInfo *elf::getAVRTargetInfo() { +TargetInfo *getAVRTargetInfo() { static AVR target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/Hexagon.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/Hexagon.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/Hexagon.cpp (revision 353950) @@ -1,291 +1,332 @@ //===-- Hexagon.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class Hexagon final : public TargetInfo { public: Hexagon(); uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; }; } // namespace Hexagon::Hexagon() { pltRel = R_HEX_JMP_SLOT; relativeRel = R_HEX_RELATIVE; gotRel = R_HEX_GLOB_DAT; symbolicRel = R_HEX_32; // The zero'th GOT entry is reserved for the address of _DYNAMIC. The // next 3 are reserved for the dynamic loader. gotPltHeaderEntriesNum = 4; pltEntrySize = 16; pltHeaderSize = 32; // Hexagon Linux uses 64K pages by default. defaultMaxPageSize = 0x10000; noneRel = R_HEX_NONE; } uint32_t Hexagon::calcEFlags() const { assert(!objectFiles.empty()); // The architecture revision must always be equal to or greater than // greatest revision in the list of inputs. uint32_t ret = 0; for (InputFile *f : objectFiles) { uint32_t eflags = cast>(f)->getObj().getHeader()->e_flags; if (eflags > ret) ret = eflags; } return ret; } static uint32_t applyMask(uint32_t mask, uint32_t data) { uint32_t result = 0; size_t off = 0; for (size_t bit = 0; bit != 32; ++bit) { uint32_t valBit = (data >> off) & 1; uint32_t maskBit = (mask >> bit) & 1; if (maskBit) { result |= (valBit << bit); ++off; } } return result; } RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_HEX_NONE: + return R_NONE; + case R_HEX_6_X: + case R_HEX_8_X: + case R_HEX_9_X: + case R_HEX_10_X: + case R_HEX_11_X: + case R_HEX_12_X: + case R_HEX_16_X: + case R_HEX_32: + case R_HEX_32_6_X: + case R_HEX_HI16: + case R_HEX_LO16: + return R_ABS; case R_HEX_B9_PCREL: - case R_HEX_B9_PCREL_X: case R_HEX_B13_PCREL: case R_HEX_B15_PCREL: - case R_HEX_B15_PCREL_X: case R_HEX_6_PCREL_X: case R_HEX_32_PCREL: return R_PC; + case R_HEX_B9_PCREL_X: + case R_HEX_B15_PCREL_X: case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: case R_HEX_B22_PCREL_X: case R_HEX_B32_PCREL_X: return R_PLT_PC; + case R_HEX_GOTREL_11_X: + case R_HEX_GOTREL_16_X: + case R_HEX_GOTREL_32_6_X: + case R_HEX_GOTREL_HI16: + case R_HEX_GOTREL_LO16: + return R_GOTPLTREL; case R_HEX_GOT_11_X: case R_HEX_GOT_16_X: case R_HEX_GOT_32_6_X: - return R_HEXAGON_GOT; + return R_GOTPLT; default: - return R_ABS; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } static uint32_t findMaskR6(uint32_t insn) { // There are (arguably too) many relocation masks for the DSP's // R_HEX_6_X type. The table below is used to select the correct mask // for the given instruction. struct InstructionMask { uint32_t cmpMask; uint32_t relocMask; }; static const InstructionMask r6[] = { {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f}, {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80}, {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0}, {0x42000000, 0x000020f8}, {0x43000000, 0x000007e0}, {0x44000000, 0x000020f8}, {0x45000000, 0x000007e0}, {0x46000000, 0x000020f8}, {0x47000000, 0x000007e0}, {0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000}, {0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60}, {0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60}, {0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f}, {0xad000000, 0x0000003f}, {0xaf000000, 0x00030078}, {0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0}, {0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}}; // Duplex forms have a fixed mask and parse bits 15:14 are always // zero. Non-duplex insns will always have at least one bit set in the // parse field. if ((0xC000 & insn) == 0x0) return 0x03f00000; for (InstructionMask i : r6) if ((0xff000000 & insn) == i.cmpMask) return i.relocMask; error("unrecognized instruction for R_HEX_6 relocation: 0x" + utohexstr(insn)); return 0; } static uint32_t findMaskR8(uint32_t insn) { if ((0xff000000 & insn) == 0xde000000) return 0x00e020e8; if ((0xff000000 & insn) == 0x3c000000) return 0x0000207f; return 0x00001fe0; } static uint32_t findMaskR11(uint32_t insn) { if ((0xff000000 & insn) == 0xa1000000) return 0x060020ff; return 0x06003fe0; } static uint32_t findMaskR16(uint32_t insn) { if ((0xff000000 & insn) == 0x48000000) return 0x061f20ff; if ((0xff000000 & insn) == 0x49000000) return 0x061f3fe0; if ((0xff000000 & insn) == 0x78000000) return 0x00df3fe0; if ((0xff000000 & insn) == 0xb0000000) return 0x0fe03fe0; error("unrecognized instruction for R_HEX_16_X relocation: 0x" + utohexstr(insn)); return 0; } static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_HEX_NONE: break; case R_HEX_6_PCREL_X: case R_HEX_6_X: or32le(loc, applyMask(findMaskR6(read32le(loc)), val)); break; case R_HEX_8_X: or32le(loc, applyMask(findMaskR8(read32le(loc)), val)); break; case R_HEX_9_X: or32le(loc, applyMask(0x00003fe0, val & 0x3f)); break; case R_HEX_10_X: or32le(loc, applyMask(0x00203fe0, val & 0x3f)); break; case R_HEX_11_X: case R_HEX_GOT_11_X: + case R_HEX_GOTREL_11_X: or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f)); break; case R_HEX_12_X: or32le(loc, applyMask(0x000007e0, val)); break; case R_HEX_16_X: // These relocs only have 6 effective bits. case R_HEX_GOT_16_X: + case R_HEX_GOTREL_16_X: or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f)); break; case R_HEX_32: case R_HEX_32_PCREL: or32le(loc, val); break; case R_HEX_32_6_X: case R_HEX_GOT_32_6_X: + case R_HEX_GOTREL_32_6_X: or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_B9_PCREL: + checkInt(loc, val, 11, type); or32le(loc, applyMask(0x003000fe, val >> 2)); break; case R_HEX_B9_PCREL_X: or32le(loc, applyMask(0x003000fe, val & 0x3f)); break; case R_HEX_B13_PCREL: + checkInt(loc, val, 15, type); or32le(loc, applyMask(0x00202ffe, val >> 2)); break; case R_HEX_B15_PCREL: + checkInt(loc, val, 17, type); or32le(loc, applyMask(0x00df20fe, val >> 2)); break; case R_HEX_B15_PCREL_X: or32le(loc, applyMask(0x00df20fe, val & 0x3f)); break; case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: + checkInt(loc, val, 22, type); or32le(loc, applyMask(0x1ff3ffe, val >> 2)); break; case R_HEX_B22_PCREL_X: or32le(loc, applyMask(0x1ff3ffe, val & 0x3f)); break; case R_HEX_B32_PCREL_X: or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; + case R_HEX_GOTREL_HI16: case R_HEX_HI16: or32le(loc, applyMask(0x00c03fff, val >> 16)); break; + case R_HEX_GOTREL_LO16: case R_HEX_LO16: or32le(loc, applyMask(0x00c03fff, val)); break; default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); - break; + llvm_unreachable("unknown relocation"); } } void Hexagon::writePltHeader(uint8_t *buf) const { const uint8_t pltData[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment }; memcpy(buf, pltData, sizeof(pltData)); // Offset from PLT0 to the GOT. uint64_t off = in.gotPlt->getVA() - in.plt->getVA(); relocateOne(buf, R_HEX_B32_PCREL_X, off); relocateOne(buf + 4, R_HEX_6_PCREL_X, off); } void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t inst[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 }; memcpy(buf, inst, sizeof(inst)); relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); } -TargetInfo *elf::getHexagonTargetInfo() { +RelType Hexagon::getDynRel(RelType type) const { + if (type == R_HEX_32) + return type; + return R_HEX_NONE; +} + +TargetInfo *getHexagonTargetInfo() { static Hexagon target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/MSP430.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/MSP430.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/MSP430.cpp (revision 353950) @@ -1,93 +1,97 @@ //===- MSP430.cpp ---------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set // has only 27 core instructions orthogonally augmented with a variety // of addressing modes for source and destination operands. Entire address space // of MSP430 is 64KB (the extended MSP430X architecture is not considered here). // A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty // of peripherals and is generally optimized for a low power consumption. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class MSP430 final : public TargetInfo { public: MSP430(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace MSP430::MSP430() { // mov.b #0, r3 trapInstr = {0x43, 0x43, 0x43, 0x43}; } RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { case R_MSP430_10_PCREL: case R_MSP430_16_PCREL: case R_MSP430_16_PCREL_BYTE: case R_MSP430_2X_PCREL: case R_MSP430_RL_PCREL: case R_MSP430_SYM_DIFF: return R_PC; default: return R_ABS; } } void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_MSP430_8: checkIntUInt(loc, val, 8, type); *loc = val; break; case R_MSP430_16: case R_MSP430_16_PCREL: case R_MSP430_16_BYTE: case R_MSP430_16_PCREL_BYTE: checkIntUInt(loc, val, 16, type); write16le(loc, val); break; case R_MSP430_32: checkIntUInt(loc, val, 32, type); write32le(loc, val); break; case R_MSP430_10_PCREL: { int16_t offset = ((int16_t)val >> 1) - 1; checkInt(loc, offset, 10, type); write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF)); break; } default: error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -TargetInfo *elf::getMSP430TargetInfo() { +TargetInfo *getMSP430TargetInfo() { static MSP430 target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/Mips.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/Mips.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/Mips.cpp (revision 353950) @@ -1,741 +1,755 @@ //===- MIPS.cpp -----------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" -#include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; -using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { namespace { template class MIPS final : public TargetInfo { public: MIPS(); uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; RelType getDynRel(RelType type) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; bool needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; bool usesOnlyLowPageBits(RelType type) const override; }; } // namespace template MIPS::MIPS() { gotPltHeaderEntriesNum = 2; defaultMaxPageSize = 65536; gotBaseSymInGotPlt = false; pltEntrySize = 16; pltHeaderSize = 32; copyRel = R_MIPS_COPY; noneRel = R_MIPS_NONE; pltRel = R_MIPS_JUMP_SLOT; needsThunks = true; // Set `sigrie 1` as a trap instruction. write32(trapInstr.data(), 0x04170001); if (ELFT::Is64Bits) { relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; symbolicRel = R_MIPS_64; tlsGotRel = R_MIPS_TLS_TPREL64; tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; tlsOffsetRel = R_MIPS_TLS_DTPREL64; } else { relativeRel = R_MIPS_REL32; symbolicRel = R_MIPS_32; tlsGotRel = R_MIPS_TLS_TPREL32; tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; tlsOffsetRel = R_MIPS_TLS_DTPREL32; } } template uint32_t MIPS::calcEFlags() const { return calcMipsEFlags(); } template RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { // See comment in the calculateMipsRelChain. if (ELFT::Is64Bits || config->mipsN32Abi) type &= 0xff; switch (type) { case R_MIPS_JALR: + // If the target symbol is not preemptible and is not microMIPS, + // it might be possible to replace jalr/jr instruction by bal/b. + // It depends on the target symbol's offset. + if (!s.isPreemptible && !(s.getVA() & 0x1)) + return R_PC; + return R_NONE; case R_MICROMIPS_JALR: - return R_HINT; + return R_NONE; case R_MIPS_GPREL16: case R_MIPS_GPREL32: case R_MICROMIPS_GPREL16: case R_MICROMIPS_GPREL7_S2: return R_MIPS_GOTREL; case R_MIPS_26: case R_MICROMIPS_26_S1: return R_PLT; case R_MICROMIPS_PC26_S1: return R_PLT_PC; case R_MIPS_HI16: case R_MIPS_LO16: case R_MIPS_HIGHER: case R_MIPS_HIGHEST: case R_MICROMIPS_HI16: case R_MICROMIPS_LO16: // R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate // offset between start of function and 'gp' value which by default // equal to the start of .got section. In that case we consider these // relocations as relative. if (&s == ElfSym::mipsGpDisp) return R_MIPS_GOT_GP_PC; if (&s == ElfSym::mipsLocalGp) return R_MIPS_GOT_GP; LLVM_FALLTHROUGH; case R_MIPS_32: case R_MIPS_64: case R_MIPS_GOT_OFST: case R_MIPS_SUB: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_DTPREL64: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + return R_ABS; case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_TPREL64: - case R_MICROMIPS_TLS_DTPREL_HI16: - case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: - return R_ABS; + return R_TLS; case R_MIPS_PC32: case R_MIPS_PC16: case R_MIPS_PC19_S2: case R_MIPS_PC21_S2: case R_MIPS_PC26_S2: case R_MIPS_PCHI16: case R_MIPS_PCLO16: case R_MICROMIPS_PC7_S1: case R_MICROMIPS_PC10_S1: case R_MICROMIPS_PC16_S1: case R_MICROMIPS_PC18_S3: case R_MICROMIPS_PC19_S2: case R_MICROMIPS_PC23_S2: case R_MICROMIPS_PC21_S1: return R_PC; case R_MIPS_GOT16: case R_MICROMIPS_GOT16: if (s.isLocal()) return R_MIPS_GOT_LOCAL_PAGE; LLVM_FALLTHROUGH; case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_TLS_GOTTPREL: case R_MICROMIPS_CALL16: case R_MICROMIPS_TLS_GOTTPREL: return R_MIPS_GOT_OFF; case R_MIPS_CALL_HI16: case R_MIPS_CALL_LO16: case R_MIPS_GOT_HI16: case R_MIPS_GOT_LO16: case R_MICROMIPS_CALL_HI16: case R_MICROMIPS_CALL_LO16: case R_MICROMIPS_GOT_HI16: case R_MICROMIPS_GOT_LO16: return R_MIPS_GOT_OFF32; case R_MIPS_GOT_PAGE: return R_MIPS_GOT_LOCAL_PAGE; case R_MIPS_TLS_GD: case R_MICROMIPS_TLS_GD: return R_MIPS_TLSGD; case R_MIPS_TLS_LDM: case R_MICROMIPS_TLS_LDM: return R_MIPS_TLSLD; case R_MIPS_NONE: return R_NONE; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); return R_NONE; } } template RelType MIPS::getDynRel(RelType type) const { if (type == symbolicRel) return type; return R_MIPS_NONE; } template void MIPS::writeGotPlt(uint8_t *buf, const Symbol &) const { uint64_t va = in.plt->getVA(); if (isMicroMips()) va |= 1; - write32(buf, va); + write32(buf, va); } template static uint32_t readShuffle(const uint8_t *loc) { // The major opcode of a microMIPS instruction needs to appear // in the first 16-bit word (lowest address) for efficient hardware // decode so that it knows if the instruction is 16-bit or 32-bit // as early as possible. To do so, little-endian binaries keep 16-bit // words in a big-endian order. That is why we have to swap these // words to get a correct value. - uint32_t v = read32(loc); + uint32_t v = read32(loc); if (E == support::little) return (v << 16) | (v >> 16); return v; } -template static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, uint8_t shift) { - uint32_t instr = read32(loc); + uint32_t instr = read32(loc); uint32_t mask = 0xffffffff >> (32 - bitsSize); uint32_t data = (instr & ~mask) | ((v >> shift) & mask); - write32(loc, data); + write32(loc, data); } template static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, uint8_t shift) { // See comments in readShuffle for purpose of this code. uint16_t *words = (uint16_t *)loc; if (E == support::little) std::swap(words[0], words[1]); - writeValue(loc, v, bitsSize, shift); + writeValue(loc, v, bitsSize, shift); if (E == support::little) std::swap(words[0], words[1]); } template static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize, uint8_t shift) { - uint16_t instr = read16(loc); + uint16_t instr = read16(loc); uint16_t mask = 0xffff >> (16 - bitsSize); uint16_t data = (instr & ~mask) | ((v >> shift) & mask); - write16(loc, data); + write16(loc, data); } template void MIPS::writePltHeader(uint8_t *buf) const { - const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { uint64_t gotPlt = in.gotPlt->getVA(); uint64_t plt = in.plt->getVA(); // Overwrite trap instructions written by Writer::writeTrapInstr. memset(buf, 0, pltHeaderSize); - write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . - write16(buf + 4, 0xff23); // lw $25, 0($3) - write16(buf + 8, 0x0535); // subu16 $2, $2, $3 - write16(buf + 10, 0x2525); // srl16 $2, $2, 2 - write16(buf + 12, 0x3302); // addiu $24, $2, -2 - write16(buf + 14, 0xfffe); - write16(buf + 16, 0x0dff); // move $15, $31 + write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16(buf + 4, 0xff23); // lw $25, 0($3) + write16(buf + 8, 0x0535); // subu16 $2, $2, $3 + write16(buf + 10, 0x2525); // srl16 $2, $2, 2 + write16(buf + 12, 0x3302); // addiu $24, $2, -2 + write16(buf + 14, 0xfffe); + write16(buf + 16, 0x0dff); // move $15, $31 if (isMipsR6()) { - write16(buf + 18, 0x0f83); // move $28, $3 - write16(buf + 20, 0x472b); // jalrc $25 - write16(buf + 22, 0x0c00); // nop + write16(buf + 18, 0x0f83); // move $28, $3 + write16(buf + 20, 0x472b); // jalrc $25 + write16(buf + 22, 0x0c00); // nop relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); } else { - write16(buf + 18, 0x45f9); // jalrc $25 - write16(buf + 20, 0x0f83); // move $28, $3 - write16(buf + 22, 0x0c00); // nop + write16(buf + 18, 0x45f9); // jalrc $25 + write16(buf + 20, 0x0f83); // move $28, $3 + write16(buf + 22, 0x0c00); // nop relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); } return; } if (config->mipsN32Abi) { - write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) - write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32(buf + 12, 0x030ec023); // subu $24, $24, $14 - write32(buf + 16, 0x03e07825); // move $15, $31 - write32(buf + 20, 0x0018c082); // srl $24, $24, 2 + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 } else if (ELFT::Is64Bits) { - write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) - write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32(buf + 12, 0x030ec023); // subu $24, $24, $14 - write32(buf + 16, 0x03e07825); // move $15, $31 - write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3 + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3 } else { - write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) - write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) - write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) - write32(buf + 12, 0x031cc023); // subu $24, $24, $28 - write32(buf + 16, 0x03e07825); // move $15, $31 - write32(buf + 20, 0x0018c082); // srl $24, $24, 2 + write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 } uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809; - write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 - write32(buf + 28, 0x2718fffe); // subu $24, $24, 2 + write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 + write32(buf + 28, 0x2718fffe); // subu $24, $24, 2 uint64_t gotPlt = in.gotPlt->getVA(); - writeValue(buf, gotPlt + 0x8000, 16, 16); - writeValue(buf + 4, gotPlt, 16, 0); - writeValue(buf + 8, gotPlt, 16, 0); + writeValue(buf, gotPlt + 0x8000, 16, 16); + writeValue(buf + 4, gotPlt, 16, 0); + writeValue(buf + 8, gotPlt, 16, 0); } template void MIPS::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { - const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { // Overwrite trap instructions written by Writer::writeTrapInstr. memset(buf, 0, pltEntrySize); if (isMipsR6()) { - write16(buf, 0x7840); // addiupc $2, (GOTPLT) - . - write16(buf + 4, 0xff22); // lw $25, 0($2) - write16(buf + 8, 0x0f02); // move $24, $2 - write16(buf + 10, 0x4723); // jrc $25 / jr16 $25 + write16(buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x0f02); // move $24, $2 + write16(buf + 10, 0x4723); // jrc $25 / jr16 $25 relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); } else { - write16(buf, 0x7900); // addiupc $2, (GOTPLT) - . - write16(buf + 4, 0xff22); // lw $25, 0($2) - write16(buf + 8, 0x4599); // jrc $25 / jr16 $25 - write16(buf + 10, 0x0f02); // move $24, $2 + write16(buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16(buf + 10, 0x0f02); // move $24, $2 relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); } return; } uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000; uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009) : (config->zHazardplt ? 0x03200408 : 0x03200008); uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000; - write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) - write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) - write32(buf + 8, jrInst); // jr $25 / jr.hb $25 - write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) - writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16); - writeValue(buf + 4, gotPltEntryAddr, 16, 0); - writeValue(buf + 12, gotPltEntryAddr, 16, 0); + write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) + write32(buf + 8, jrInst); // jr $25 / jr.hb $25 + write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) + writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16); + writeValue(buf + 4, gotPltEntryAddr, 16, 0); + writeValue(buf + 12, gotPltEntryAddr, 16, 0); } template bool MIPS::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const { // Any MIPS PIC code function is invoked with its address in register $t9. // So if we have a branch instruction from non-PIC code to the PIC one // we cannot make the jump directly and need to create a small stubs // to save the target function address. // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 && type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1) return false; auto *f = dyn_cast_or_null>(file); if (!f) return false; // If current file has PIC code, LA25 stub is not required. if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC) return false; auto *d = dyn_cast(&s); // LA25 is required if target file has PIC code // or target symbol is a PIC symbol. return d && isMipsPIC(d); } template int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { const endianness e = ELFT::TargetEndianness; switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - return SignExtend64<32>(read32(buf)); + return SignExtend64<32>(read32(buf)); case R_MIPS_26: // FIXME (simon): If the relocation target symbol is not a PLT entry // we should use another expression for calculation: // ((A << 2) | (P & 0xf0000000)) >> 2 - return SignExtend64<28>(read32(buf) << 2); + return SignExtend64<28>(read32(buf) << 2); case R_MIPS_GOT16: case R_MIPS_HI16: case R_MIPS_PCHI16: - return SignExtend64<16>(read32(buf)) << 16; + return SignExtend64<16>(read32(buf)) << 16; case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: - return SignExtend64<16>(read32(buf)); + return SignExtend64<16>(read32(buf)); case R_MICROMIPS_GOT16: case R_MICROMIPS_HI16: return SignExtend64<16>(readShuffle(buf)) << 16; case R_MICROMIPS_GPREL16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: return SignExtend64<16>(readShuffle(buf)); case R_MICROMIPS_GPREL7_S2: return SignExtend64<9>(readShuffle(buf) << 2); case R_MIPS_PC16: - return SignExtend64<18>(read32(buf) << 2); + return SignExtend64<18>(read32(buf) << 2); case R_MIPS_PC19_S2: - return SignExtend64<21>(read32(buf) << 2); + return SignExtend64<21>(read32(buf) << 2); case R_MIPS_PC21_S2: - return SignExtend64<23>(read32(buf) << 2); + return SignExtend64<23>(read32(buf) << 2); case R_MIPS_PC26_S2: - return SignExtend64<28>(read32(buf) << 2); + return SignExtend64<28>(read32(buf) << 2); case R_MIPS_PC32: - return SignExtend64<32>(read32(buf)); + return SignExtend64<32>(read32(buf)); case R_MICROMIPS_26_S1: return SignExtend64<27>(readShuffle(buf) << 1); case R_MICROMIPS_PC7_S1: - return SignExtend64<8>(read16(buf) << 1); + return SignExtend64<8>(read16(buf) << 1); case R_MICROMIPS_PC10_S1: - return SignExtend64<11>(read16(buf) << 1); + return SignExtend64<11>(read16(buf) << 1); case R_MICROMIPS_PC16_S1: return SignExtend64<17>(readShuffle(buf) << 1); case R_MICROMIPS_PC18_S3: return SignExtend64<21>(readShuffle(buf) << 3); case R_MICROMIPS_PC19_S2: return SignExtend64<21>(readShuffle(buf) << 2); case R_MICROMIPS_PC21_S1: return SignExtend64<22>(readShuffle(buf) << 1); case R_MICROMIPS_PC23_S2: return SignExtend64<25>(readShuffle(buf) << 2); case R_MICROMIPS_PC26_S1: return SignExtend64<27>(readShuffle(buf) << 1); default: return 0; } } static std::pair calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) { // MIPS N64 ABI packs multiple relocations into the single relocation // record. In general, all up to three relocations can have arbitrary // types. In fact, Clang and GCC uses only a few combinations. For now, // we support two of them. That is allow to pass at least all LLVM // test suite cases. // / R_MIPS_SUB / R_MIPS_HI16 | R_MIPS_LO16 // / R_MIPS_64 / R_MIPS_NONE // The first relocation is a 'real' relocation which is calculated // using the corresponding symbol's value. The second and the third // relocations used to modify result of the first one: extend it to // 64-bit, extract high or low part etc. For details, see part 2.9 Relocation // at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf RelType type2 = (type >> 8) & 0xff; RelType type3 = (type >> 16) & 0xff; if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE) return std::make_pair(type, val); if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE) return std::make_pair(type2, val); if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16)) return std::make_pair(type3, -val); error(getErrorLocation(loc) + "unsupported relocations combination " + Twine(type)); return std::make_pair(type & 0xff, val); } static bool isBranchReloc(RelType type) { return type == R_MIPS_26 || type == R_MIPS_PC26_S2 || type == R_MIPS_PC21_S2 || type == R_MIPS_PC16; } static bool isMicroBranchReloc(RelType type) { return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 || type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1; } template static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) { // Here we need to detect jump/branch from regular MIPS code // to a microMIPS target and vice versa. In that cases jump // instructions need to be replaced by their "cross-mode" // equivalents. const endianness e = ELFT::TargetEndianness; bool isMicroTgt = val & 0x1; bool isCrossJump = (isMicroTgt && isBranchReloc(type)) || (!isMicroTgt && isMicroBranchReloc(type)); if (!isCrossJump) return val; switch (type) { case R_MIPS_26: { - uint32_t inst = read32(loc) >> 26; + uint32_t inst = read32(loc) >> 26; if (inst == 0x3 || inst == 0x1d) { // JAL or JALX - writeValue(loc, 0x1d << 26, 32, 0); + writeValue(loc, 0x1d << 26, 32, 0); return val; } break; } case R_MICROMIPS_26_S1: { uint32_t inst = readShuffle(loc) >> 26; if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32 val >>= 1; writeShuffleValue(loc, 0x3c << 26, 32, 0); return val; } break; } case R_MIPS_PC26_S2: case R_MIPS_PC21_S2: case R_MIPS_PC16: case R_MICROMIPS_PC16_S1: case R_MICROMIPS_PC10_S1: case R_MICROMIPS_PC7_S1: // FIXME (simon): Support valid branch relocations. break; default: llvm_unreachable("unexpected jump/branch relocation"); } error(getErrorLocation(loc) + "unsupported jump/branch instruction between ISA modes referenced by " + toString(type) + " relocation"); return val; } template void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { const endianness e = ELFT::TargetEndianness; if (ELFT::Is64Bits || config->mipsN32Abi) std::tie(type, val) = calculateMipsRelChain(loc, type, val); // Detect cross-mode jump/branch and fix instruction. val = fixupCrossModeJump(loc, type, val); // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 || type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 || type == R_MICROMIPS_TLS_DTPREL_HI16 || type == R_MICROMIPS_TLS_DTPREL_LO16) { val -= 0x8000; - } else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 || - type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 || - type == R_MICROMIPS_TLS_TPREL_HI16 || - type == R_MICROMIPS_TLS_TPREL_LO16) { - val -= 0x7000; } switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - write32(loc, val); + write32(loc, val); break; case R_MIPS_64: case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_TPREL64: - write64(loc, val); + write64(loc, val); break; case R_MIPS_26: - writeValue(loc, val, 26, 2); + writeValue(loc, val, 26, 2); break; case R_MIPS_GOT16: // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode // is updated addend (not a GOT index). In that case write high 16 bits // to store a correct addend value. if (config->relocatable) { - writeValue(loc, val + 0x8000, 16, 16); + writeValue(loc, val + 0x8000, 16, 16); } else { checkInt(loc, val, 16, type); - writeValue(loc, val, 16, 0); + writeValue(loc, val, 16, 0); } break; case R_MICROMIPS_GOT16: if (config->relocatable) { writeShuffleValue(loc, val + 0x8000, 16, 16); } else { checkInt(loc, val, 16, type); writeShuffleValue(loc, val, 16, 0); } break; case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: case R_MIPS_GPREL16: case R_MIPS_TLS_GD: case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_LDM: checkInt(loc, val, 16, type); LLVM_FALLTHROUGH; case R_MIPS_CALL_LO16: case R_MIPS_GOT_LO16: case R_MIPS_GOT_OFST: case R_MIPS_LO16: case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_LO16: - writeValue(loc, val, 16, 0); + writeValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: checkInt(loc, val, 16, type); writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_CALL16: case R_MICROMIPS_CALL_LO16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_GOTTPREL: case R_MICROMIPS_TLS_TPREL_LO16: writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL7_S2: checkInt(loc, val, 7, type); writeShuffleValue(loc, val, 7, 2); break; case R_MIPS_CALL_HI16: case R_MIPS_GOT_HI16: case R_MIPS_HI16: case R_MIPS_PCHI16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: - writeValue(loc, val + 0x8000, 16, 16); + writeValue(loc, val + 0x8000, 16, 16); break; case R_MICROMIPS_CALL_HI16: case R_MICROMIPS_GOT_HI16: case R_MICROMIPS_HI16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_TPREL_HI16: writeShuffleValue(loc, val + 0x8000, 16, 16); break; case R_MIPS_HIGHER: - writeValue(loc, val + 0x80008000, 16, 32); + writeValue(loc, val + 0x80008000, 16, 32); break; case R_MIPS_HIGHEST: - writeValue(loc, val + 0x800080008000, 16, 48); + writeValue(loc, val + 0x800080008000, 16, 48); break; case R_MIPS_JALR: + val -= 4; + // Replace jalr/jr instructions by bal/b if the target + // offset fits into the 18-bit range. + if (isInt<18>(val)) { + switch (read32(loc)) { + case 0x0320f809: // jalr $25 => bal sym + write32(loc, 0x04110000 | ((val >> 2) & 0xffff)); + break; + case 0x03200008: // jr $25 => b sym + write32(loc, 0x10000000 | ((val >> 2) & 0xffff)); + break; + } + } + break; case R_MICROMIPS_JALR: // Ignore this optimization relocation for now break; case R_MIPS_PC16: checkAlignment(loc, val, 4, type); checkInt(loc, val, 18, type); - writeValue(loc, val, 16, 2); + writeValue(loc, val, 16, 2); break; case R_MIPS_PC19_S2: checkAlignment(loc, val, 4, type); checkInt(loc, val, 21, type); - writeValue(loc, val, 19, 2); + writeValue(loc, val, 19, 2); break; case R_MIPS_PC21_S2: checkAlignment(loc, val, 4, type); checkInt(loc, val, 23, type); - writeValue(loc, val, 21, 2); + writeValue(loc, val, 21, 2); break; case R_MIPS_PC26_S2: checkAlignment(loc, val, 4, type); checkInt(loc, val, 28, type); - writeValue(loc, val, 26, 2); + writeValue(loc, val, 26, 2); break; case R_MIPS_PC32: - writeValue(loc, val, 32, 0); + writeValue(loc, val, 32, 0); break; case R_MICROMIPS_26_S1: case R_MICROMIPS_PC26_S1: checkInt(loc, val, 27, type); writeShuffleValue(loc, val, 26, 1); break; case R_MICROMIPS_PC7_S1: checkInt(loc, val, 8, type); writeMicroRelocation16(loc, val, 7, 1); break; case R_MICROMIPS_PC10_S1: checkInt(loc, val, 11, type); writeMicroRelocation16(loc, val, 10, 1); break; case R_MICROMIPS_PC16_S1: checkInt(loc, val, 17, type); writeShuffleValue(loc, val, 16, 1); break; case R_MICROMIPS_PC18_S3: checkInt(loc, val, 21, type); writeShuffleValue(loc, val, 18, 3); break; case R_MICROMIPS_PC19_S2: checkInt(loc, val, 21, type); writeShuffleValue(loc, val, 19, 2); break; case R_MICROMIPS_PC21_S1: checkInt(loc, val, 22, type); writeShuffleValue(loc, val, 21, 1); break; case R_MICROMIPS_PC23_S2: checkInt(loc, val, 25, type); writeShuffleValue(loc, val, 23, 2); break; default: llvm_unreachable("unknown relocation"); } } template bool MIPS::usesOnlyLowPageBits(RelType type) const { return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST || type == R_MICROMIPS_LO16; } // Return true if the symbol is a PIC function. -template bool elf::isMipsPIC(const Defined *sym) { +template bool isMipsPIC(const Defined *sym) { if (!sym->isFunc()) return false; if (sym->stOther & STO_MIPS_PIC) return true; if (!sym->section) return false; ObjFile *file = cast(sym->section)->template getFile(); if (!file) return false; return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; } -template TargetInfo *elf::getMipsTargetInfo() { +template TargetInfo *getMipsTargetInfo() { static MIPS target; return ⌖ } -template TargetInfo *elf::getMipsTargetInfo(); -template TargetInfo *elf::getMipsTargetInfo(); -template TargetInfo *elf::getMipsTargetInfo(); -template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *getMipsTargetInfo(); +template TargetInfo *getMipsTargetInfo(); +template TargetInfo *getMipsTargetInfo(); +template TargetInfo *getMipsTargetInfo(); -template bool elf::isMipsPIC(const Defined *); -template bool elf::isMipsPIC(const Defined *); -template bool elf::isMipsPIC(const Defined *); -template bool elf::isMipsPIC(const Defined *); +template bool isMipsPIC(const Defined *); +template bool isMipsPIC(const Defined *); +template bool isMipsPIC(const Defined *); +template bool isMipsPIC(const Defined *); + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/MipsArchTree.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/MipsArchTree.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/MipsArchTree.cpp (revision 353950) @@ -1,389 +1,397 @@ //===- MipsArchTree.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===---------------------------------------------------------------------===// // // This file contains a helper function for the Writer. // //===---------------------------------------------------------------------===// #include "InputFiles.h" #include "SymbolTable.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/MipsABIFlags.h" using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { namespace { struct ArchTreeEdge { uint32_t child; uint32_t parent; }; struct FileFlags { InputFile *file; uint32_t flags; }; } // namespace static StringRef getAbiName(uint32_t flags) { switch (flags) { case 0: return "n64"; case EF_MIPS_ABI2: return "n32"; case EF_MIPS_ABI_O32: return "o32"; case EF_MIPS_ABI_O64: return "o64"; case EF_MIPS_ABI_EABI32: return "eabi32"; case EF_MIPS_ABI_EABI64: return "eabi64"; default: return "unknown"; } } static StringRef getNanName(bool isNan2008) { return isNan2008 ? "2008" : "legacy"; } static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; } static void checkFlags(ArrayRef files) { assert(!files.empty() && "expected non-empty file list"); uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2); bool nan = files[0].flags & EF_MIPS_NAN2008; bool fp = files[0].flags & EF_MIPS_FP64; for (const FileFlags &f : files) { if (config->is64 && f.flags & EF_MIPS_MICROMIPS) error(toString(f.file) + ": microMIPS 64-bit is not supported"); uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2); if (abi != abi2) error(toString(f.file) + ": ABI '" + getAbiName(abi2) + "' is incompatible with target ABI '" + getAbiName(abi) + "'"); bool nan2 = f.flags & EF_MIPS_NAN2008; if (nan != nan2) error(toString(f.file) + ": -mnan=" + getNanName(nan2) + " is incompatible with target -mnan=" + getNanName(nan)); bool fp2 = f.flags & EF_MIPS_FP64; if (fp != fp2) error(toString(f.file) + ": -mfp" + getFpName(fp2) + " is incompatible with target -mfp" + getFpName(fp)); } } static uint32_t getMiscFlags(ArrayRef files) { uint32_t ret = 0; for (const FileFlags &f : files) ret |= f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER | EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE); return ret; } static uint32_t getPicFlags(ArrayRef files) { // Check PIC/non-PIC compatibility. bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); for (const FileFlags &f : files.slice(1)) { bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); if (isPic && !isPic2) warn(toString(f.file) + ": linking non-abicalls code with abicalls code " + toString(files[0].file)); if (!isPic && isPic2) warn(toString(f.file) + ": linking abicalls code with non-abicalls code " + toString(files[0].file)); } // Compute the result PIC/non-PIC flag. uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); for (const FileFlags &f : files.slice(1)) ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); // PIC code is inherently CPIC and may not set CPIC flag explicitly. if (ret & EF_MIPS_PIC) ret |= EF_MIPS_CPIC; return ret; } static ArchTreeEdge archTree[] = { // MIPS32R6 and MIPS64R6 are not compatible with other extensions // MIPS64R2 extensions. {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2}, {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2}, {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2}, {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2}, // MIPS64 extensions. {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64}, {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64}, {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, // MIPS V extensions. {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, // R5000 extensions. {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400}, // MIPS IV extensions. {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4}, {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4}, {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, // VR4100 extensions. {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, // MIPS III extensions. {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3}, // MIPS32 extensions. {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32}, // MIPS II extensions. {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2}, {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2}, // MIPS I extensions. {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1}, {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, }; -static bool isArchMatched(uint32_t New, uint32_t res) { - if (New == res) +static bool isArchMatched(uint32_t newFlags, uint32_t res) { + if (newFlags == res) return true; - if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) + if (newFlags == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) return true; - if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) + if (newFlags == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) return true; for (const auto &edge : archTree) { if (res == edge.child) { res = edge.parent; - if (res == New) + if (res == newFlags) return true; } } return false; } static StringRef getMachName(uint32_t flags) { switch (flags & EF_MIPS_MACH) { case EF_MIPS_MACH_NONE: return ""; case EF_MIPS_MACH_3900: return "r3900"; case EF_MIPS_MACH_4010: return "r4010"; case EF_MIPS_MACH_4100: return "r4100"; case EF_MIPS_MACH_4650: return "r4650"; case EF_MIPS_MACH_4120: return "r4120"; case EF_MIPS_MACH_4111: return "r4111"; case EF_MIPS_MACH_5400: return "vr5400"; case EF_MIPS_MACH_5900: return "vr5900"; case EF_MIPS_MACH_5500: return "vr5500"; case EF_MIPS_MACH_9000: return "rm9000"; case EF_MIPS_MACH_LS2E: return "loongson2e"; case EF_MIPS_MACH_LS2F: return "loongson2f"; case EF_MIPS_MACH_LS3A: return "loongson3a"; case EF_MIPS_MACH_OCTEON: return "octeon"; case EF_MIPS_MACH_OCTEON2: return "octeon2"; case EF_MIPS_MACH_OCTEON3: return "octeon3"; case EF_MIPS_MACH_SB1: return "sb1"; case EF_MIPS_MACH_XLR: return "xlr"; default: return "unknown machine"; } } static StringRef getArchName(uint32_t flags) { switch (flags & EF_MIPS_ARCH) { case EF_MIPS_ARCH_1: return "mips1"; case EF_MIPS_ARCH_2: return "mips2"; case EF_MIPS_ARCH_3: return "mips3"; case EF_MIPS_ARCH_4: return "mips4"; case EF_MIPS_ARCH_5: return "mips5"; case EF_MIPS_ARCH_32: return "mips32"; case EF_MIPS_ARCH_64: return "mips64"; case EF_MIPS_ARCH_32R2: return "mips32r2"; case EF_MIPS_ARCH_64R2: return "mips64r2"; case EF_MIPS_ARCH_32R6: return "mips32r6"; case EF_MIPS_ARCH_64R6: return "mips64r6"; default: return "unknown arch"; } } static std::string getFullArchName(uint32_t flags) { StringRef arch = getArchName(flags); StringRef mach = getMachName(flags); if (mach.empty()) return arch.str(); return (arch + " (" + mach + ")").str(); } // There are (arguably too) many MIPS ISAs out there. Their relationships // can be represented as a forest. If all input files have ISAs which // reachable by repeated proceeding from the single child to the parent, // these input files are compatible. In that case we need to return "highest" // ISA. If there are incompatible input files, we show an error. // For example, mips1 is a "parent" of mips2 and such files are compatible. // Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32 // are incompatible because nor mips3 is a parent for misp32, nor mips32 // is a parent for mips3. static uint32_t getArchFlags(ArrayRef files) { uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); for (const FileFlags &f : files.slice(1)) { - uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + uint32_t newFlags = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); // Check ISA compatibility. - if (isArchMatched(New, ret)) + if (isArchMatched(newFlags, ret)) continue; - if (!isArchMatched(ret, New)) { + if (!isArchMatched(ret, newFlags)) { error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + - getFullArchName(New)); + getFullArchName(newFlags)); return 0; } - ret = New; + ret = newFlags; } return ret; } -template uint32_t elf::calcMipsEFlags() { +template uint32_t calcMipsEFlags() { std::vector v; for (InputFile *f : objectFiles) v.push_back({f, cast>(f)->getObj().getHeader()->e_flags}); - if (v.empty()) - return 0; + if (v.empty()) { + // If we don't have any input files, we'll have to rely on the information + // we can derive from emulation information, since this at least gets us + // ABI. + if (config->emulation.empty() || config->is64) + return 0; + return config->mipsN32Abi ? EF_MIPS_ABI2 : EF_MIPS_ABI_O32; + } checkFlags(v); return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); } static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) { if (fpA == fpB) return 0; if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) return 1; if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A && fpA == Mips::Val_GNU_MIPS_ABI_FP_64) return 1; if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX) return -1; if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || fpA == Mips::Val_GNU_MIPS_ABI_FP_64 || fpA == Mips::Val_GNU_MIPS_ABI_FP_64A) return 1; return -1; } static StringRef getMipsFpAbiName(uint8_t fpAbi) { switch (fpAbi) { case Mips::Val_GNU_MIPS_ABI_FP_ANY: return "any"; case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: return "-mdouble-float"; case Mips::Val_GNU_MIPS_ABI_FP_SINGLE: return "-msingle-float"; case Mips::Val_GNU_MIPS_ABI_FP_SOFT: return "-msoft-float"; case Mips::Val_GNU_MIPS_ABI_FP_OLD_64: return "-mgp32 -mfp64 (old)"; case Mips::Val_GNU_MIPS_ABI_FP_XX: return "-mfpxx"; case Mips::Val_GNU_MIPS_ABI_FP_64: return "-mgp32 -mfp64"; case Mips::Val_GNU_MIPS_ABI_FP_64A: return "-mgp32 -mfp64 -mno-odd-spreg"; default: return "unknown"; } } -uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, - StringRef fileName) { +uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) { if (compareMipsFpAbi(newFlag, oldFlag) >= 0) return newFlag; if (compareMipsFpAbi(oldFlag, newFlag) < 0) error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) + "' is incompatible with target floating point ABI '" + getMipsFpAbiName(oldFlag) + "'"); return oldFlag; } template static bool isN32Abi(const InputFile *f) { if (auto *ef = dyn_cast(f)) return ef->template getObj().getHeader()->e_flags & EF_MIPS_ABI2; return false; } -bool elf::isMipsN32Abi(const InputFile *f) { +bool isMipsN32Abi(const InputFile *f) { switch (config->ekind) { case ELF32LEKind: return isN32Abi(f); case ELF32BEKind: return isN32Abi(f); case ELF64LEKind: return isN32Abi(f); case ELF64BEKind: return isN32Abi(f); default: llvm_unreachable("unknown Config->EKind"); } } -bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } +bool isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } -bool elf::isMipsR6() { +bool isMipsR6() { uint32_t arch = config->eflags & EF_MIPS_ARCH; return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; } -template uint32_t elf::calcMipsEFlags(); -template uint32_t elf::calcMipsEFlags(); -template uint32_t elf::calcMipsEFlags(); -template uint32_t elf::calcMipsEFlags(); +template uint32_t calcMipsEFlags(); +template uint32_t calcMipsEFlags(); +template uint32_t calcMipsEFlags(); +template uint32_t calcMipsEFlags(); + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/PPC.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/PPC.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/PPC.cpp (revision 353950) @@ -1,432 +1,445 @@ //===- PPC.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class PPC final : public TargetInfo { public: PPC(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; void writeGotHeader(uint8_t *buf) const override; void writePltHeader(uint8_t *buf) const override { llvm_unreachable("should call writePPC32GlinkSection() instead"); } void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override { llvm_unreachable("should call writePPC32GlinkSection() instead"); } void writeGotPlt(uint8_t *buf, const Symbol &s) const override; bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const override; int getTlsGdRelaxSkip(RelType type) const override; void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace static uint16_t lo(uint32_t v) { return v; } static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; } static uint32_t readFromHalf16(const uint8_t *loc) { return read32(config->isLE ? loc : loc - 2); } static void writeFromHalf16(uint8_t *loc, uint32_t insn) { write32(config->isLE ? loc : loc - 2, insn); } -void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { +void writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an // absolute address from a specific .plt slot (usually called .got.plt on // other targets) and jumps there. // // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load // time. The .glink section is not used. // b) With lazy binding, the .plt entry points to a `b PLTresolve` // instruction in .glink, filled in by PPC::writeGotPlt(). // Write N `b PLTresolve` first. for (size_t i = 0; i != numEntries; ++i) write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i)); buf += 4 * numEntries; // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve() // computes the PLT index (by computing the distance from the landing b to // itself) and calls _dl_runtime_resolve() (in glibc). uint32_t got = in.got->getVA(); uint32_t glink = in.plt->getVA(); // VA of .glink const uint8_t *end = buf + 64; if (config->isPic) { uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12; uint32_t gotBcl = got + 4 - (glink + afterBcl); write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha write32(buf + 4, 0x7c0802a6); // mflr r0 write32(buf + 8, 0x429f0005); // bcl 20,30,.+4 write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l write32(buf + 16, 0x7d8802a6); // mflr r12 write32(buf + 20, 0x7c0803a6); // mtlr r0 write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12 write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha if (ha(gotBcl) == ha(gotBcl + 4)) { write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12) write32(buf + 36, 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12) } else { write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12) write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12) } write32(buf + 40, 0x7c0903a6); // mtctr 0 write32(buf + 44, 0x7c0b5a14); // add r0,11,11 write32(buf + 48, 0x7d605a14); // add r11,0,11 write32(buf + 52, 0x4e800420); // bctr buf += 56; } else { write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha if (ha(got + 4) == ha(got + 8)) write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12) else write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12) write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l write32(buf + 16, 0x7c0903a6); // mtctr r0 write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11 if (ha(got + 4) == ha(got + 8)) write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12) else write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12) write32(buf + 28, 0x7d605a14); // add r11,r0,r11 write32(buf + 32, 0x4e800420); // bctr buf += 36; } // Pad with nop. They should not be executed. for (; buf < end; buf += 4) write32(buf, 0x60000000); } PPC::PPC() { gotRel = R_PPC_GLOB_DAT; noneRel = R_PPC_NONE; pltRel = R_PPC_JMP_SLOT; relativeRel = R_PPC_RELATIVE; iRelativeRel = R_PPC_IRELATIVE; symbolicRel = R_PPC_ADDR32; gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 3; gotPltHeaderEntriesNum = 0; pltHeaderSize = 64; // size of PLTresolve in .glink pltEntrySize = 4; needsThunks = true; tlsModuleIndexRel = R_PPC_DTPMOD32; tlsOffsetRel = R_PPC_DTPREL32; tlsGotRel = R_PPC_TPREL32; defaultMaxPageSize = 65536; defaultImageBase = 0x10000000; write32(trapInstr.data(), 0x7fe00008); } void PPC::writeGotHeader(uint8_t *buf) const { // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], // link_map in _GLOBAL_OFFSET_TABLE_[2]. write32(buf, mainPart->dynamic->getVA()); } void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { // Address of the symbol resolver stub in .glink . write32(buf, in.plt->getVA() + 4 * s.pltIndex); } bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const { if (type != R_PPC_REL24 && type != R_PPC_PLTREL24) return false; if (s.isInPlt()) return true; if (s.isUndefWeak()) return false; return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA())); } uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { uint64_t offset = dst - src; if (type == R_PPC_REL24 || type == R_PPC_PLTREL24) return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } RelExpr PPC::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_PPC_NONE: + return R_NONE; + case R_PPC_ADDR16_HA: + case R_PPC_ADDR16_HI: + case R_PPC_ADDR16_LO: + case R_PPC_ADDR32: + return R_ABS; case R_PPC_DTPREL16: case R_PPC_DTPREL16_HA: case R_PPC_DTPREL16_HI: case R_PPC_DTPREL16_LO: case R_PPC_DTPREL32: return R_DTPREL; case R_PPC_REL14: case R_PPC_REL32: case R_PPC_LOCAL24PC: case R_PPC_REL16_LO: case R_PPC_REL16_HI: case R_PPC_REL16_HA: return R_PC; case R_PPC_GOT16: return R_GOT_OFF; case R_PPC_REL24: return R_PLT_PC; case R_PPC_PLTREL24: return R_PPC32_PLTREL; case R_PPC_GOT_TLSGD16: return R_TLSGD_GOT; case R_PPC_GOT_TLSLD16: return R_TLSLD_GOT; case R_PPC_GOT_TPREL16: return R_GOT_OFF; case R_PPC_TLS: return R_TLSIE_HINT; case R_PPC_TLSGD: return R_TLSDESC_CALL; case R_PPC_TLSLD: return R_TLSLD_HINT; case R_PPC_TPREL16: case R_PPC_TPREL16_HA: case R_PPC_TPREL16_LO: case R_PPC_TPREL16_HI: return R_TLS; default: - return R_ABS; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } RelType PPC::getDynRel(RelType type) const { if (type == R_PPC_ADDR32) return type; return R_PPC_NONE; } static std::pair fromDTPREL(RelType type, uint64_t val) { uint64_t dtpBiasedVal = val - 0x8000; switch (type) { case R_PPC_DTPREL16: return {R_PPC64_ADDR16, dtpBiasedVal}; case R_PPC_DTPREL16_HA: return {R_PPC_ADDR16_HA, dtpBiasedVal}; case R_PPC_DTPREL16_HI: return {R_PPC_ADDR16_HI, dtpBiasedVal}; case R_PPC_DTPREL16_LO: return {R_PPC_ADDR16_LO, dtpBiasedVal}; case R_PPC_DTPREL32: return {R_PPC_ADDR32, dtpBiasedVal}; default: return {type, val}; } } void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { RelType newType; std::tie(newType, val) = fromDTPREL(type, val); switch (newType) { case R_PPC_ADDR16: checkIntUInt(loc, val, 16, type); write16(loc, val); break; case R_PPC_GOT16: case R_PPC_GOT_TLSGD16: case R_PPC_GOT_TLSLD16: case R_PPC_GOT_TPREL16: case R_PPC_TPREL16: checkInt(loc, val, 16, type); write16(loc, val); break; case R_PPC_ADDR16_HA: case R_PPC_DTPREL16_HA: case R_PPC_GOT_TLSGD16_HA: case R_PPC_GOT_TLSLD16_HA: case R_PPC_GOT_TPREL16_HA: case R_PPC_REL16_HA: case R_PPC_TPREL16_HA: write16(loc, ha(val)); break; case R_PPC_ADDR16_HI: case R_PPC_DTPREL16_HI: case R_PPC_GOT_TLSGD16_HI: case R_PPC_GOT_TLSLD16_HI: case R_PPC_GOT_TPREL16_HI: case R_PPC_REL16_HI: case R_PPC_TPREL16_HI: write16(loc, val >> 16); break; case R_PPC_ADDR16_LO: case R_PPC_DTPREL16_LO: case R_PPC_GOT_TLSGD16_LO: case R_PPC_GOT_TLSLD16_LO: case R_PPC_GOT_TPREL16_LO: case R_PPC_REL16_LO: case R_PPC_TPREL16_LO: write16(loc, val); break; case R_PPC_ADDR32: case R_PPC_REL32: write32(loc, val); break; case R_PPC_REL14: { uint32_t mask = 0x0000FFFC; checkInt(loc, val, 16, type); checkAlignment(loc, val, 4, type); write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC_REL24: case R_PPC_LOCAL24PC: case R_PPC_PLTREL24: { uint32_t mask = 0x03FFFFFC; checkInt(loc, val, 26, type); checkAlignment(loc, val, 4, type); write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + llvm_unreachable("unknown relocation"); } } RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const { if (expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; return expr; } int PPC::getTlsGdRelaxSkip(RelType type) const { // A __tls_get_addr call instruction is marked with 2 relocations: // // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation // R_PPC_REL24: __tls_get_addr // // After the relaxation we no longer call __tls_get_addr and should skip both // relocations to not create a false dependence on __tls_get_addr being // defined. if (type == R_PPC_TLSGD || type == R_PPC_TLSLD) return 2; return 1; } void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC_GOT_TLSGD16: { // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) uint32_t insn = readFromHalf16(loc); writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000)); relocateOne(loc, R_PPC_GOT_TPREL16, val); break; } case R_PPC_TLSGD: // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 write32(loc, 0x7c631214); break; default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); } } void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC_GOT_TLSGD16: // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha writeFromHalf16(loc, 0x3c620000 | ha(val)); break; case R_PPC_TLSGD: // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l write32(loc, 0x38630000 | lo(val)); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC_GOT_TLSLD16: // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 writeFromHalf16(loc, 0x3c620000); break; case R_PPC_TLSLD: // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel // = r3+x-0x7000, so add 4096 to r3. // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 write32(loc, 0x38631000); break; case R_PPC_DTPREL16: case R_PPC_DTPREL16_HA: case R_PPC_DTPREL16_HI: case R_PPC_DTPREL16_LO: relocateOne(loc, type, val); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); } } void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC_GOT_TPREL16: { // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha uint32_t rt = readFromHalf16(loc) & 0x03e00000; writeFromHalf16(loc, 0x3c020000 | rt | ha(val)); break; } case R_PPC_TLS: { uint32_t insn = read32(loc); if (insn >> 26 != 31) error("unrecognized instruction for IE to LE R_PPC_TLS"); // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1); if (dFormOp == 0) error("unrecognized instruction for IE to LE R_PPC_TLS"); write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val)); break; } default: llvm_unreachable("unsupported relocation for TLS IE to LE relaxation"); } } -TargetInfo *elf::getPPCTargetInfo() { +TargetInfo *getPPCTargetInfo() { static PPC target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/PPC64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/PPC64.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/PPC64.cpp (revision 353950) @@ -1,1077 +1,1103 @@ //===- PPC64.cpp ----------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + static uint64_t ppc64TocOffset = 0x8000; static uint64_t dynamicThreadPointerOffset = 0x8000; // The instruction encoding of bits 21-30 from the ISA for the Xform and Dform // instructions that can be used as part of the initial exec TLS sequence. enum XFormOpcd { LBZX = 87, LHZX = 279, LWZX = 23, LDX = 21, STBX = 215, STHX = 407, STWX = 151, STDX = 149, ADD = 266, }; enum DFormOpcd { LBZ = 34, LBZU = 35, LHZ = 40, LHZU = 41, LHAU = 43, LWZ = 32, LWZU = 33, LFSU = 49, LD = 58, LFDU = 51, STB = 38, STBU = 39, STH = 44, STHU = 45, STW = 36, STWU = 37, STFSU = 53, STFDU = 55, STD = 62, ADDI = 14 }; -uint64_t elf::getPPC64TocBase() { +uint64_t getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a // .got when we see a relocation that uses it, so for us the start is always // the .got. uint64_t tocVA = in.got->getVA(); // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 // thus permitting a full 64 Kbytes segment. Note that the glibc startup // code (crt1.o) assumes that you can get from the TOC base to the // start of the .toc section with only a single (signed) 16-bit relocation. return tocVA + ppc64TocOffset; } -unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { +unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { // The offset is encoded into the 3 most significant bits of the st_other // field, with some special values described in section 3.4.1 of the ABI: // 0 --> Zero offset between the GEP and LEP, and the function does NOT use // the TOC pointer (r2). r2 will hold the same value on returning from // the function as it did on entering the function. // 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a // caller-saved register for all callers. // 2-6 --> The binary logarithm of the offset eg: // 2 --> 2^2 = 4 bytes --> 1 instruction. // 6 --> 2^6 = 64 bytes --> 16 instructions. // 7 --> Reserved. uint8_t gepToLep = (stOther >> 5) & 7; if (gepToLep < 2) return 0; // The value encoded in the st_other bits is the // log-base-2(offset). if (gepToLep < 7) return 1 << gepToLep; error("reserved value of 7 in the 3 most-significant-bits of st_other"); return 0; } -bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { +bool isPPC64SmallCodeModelTocReloc(RelType type) { // The only small code model relocations that access the .toc section. return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; } // Find the R_PPC64_ADDR64 in .rela.toc with matching offset. template static std::pair getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { if (tocSec->numRelocations == 0) return {}; // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the // relocation index in most cases. // // In rare cases a TOC entry may store a constant that doesn't need an // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8 // points to a relocation with larger r_offset. Do a linear probe then. // Constants are extremely uncommon in .toc and the extra number of array // accesses can be seen as a small constant. ArrayRef relas = tocSec->template relas(); uint64_t index = std::min(offset / 8, relas.size() - 1); for (;;) { if (relas[index].r_offset == offset) { Symbol &sym = tocSec->getFile()->getRelocTargetSym(relas[index]); return {dyn_cast(&sym), getAddend(relas[index])}; } if (relas[index].r_offset < offset || index == 0) break; --index; } return {}; } // When accessing a symbol defined in another translation unit, compilers // reserve a .toc entry, allocate a local label and generate toc-indirect // instuctions: // // addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA // ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry // ld/lwa 3, 0(3) # load the value from the address // // .section .toc,"aw",@progbits // .LC0: .tc var[TC],var // // If var is defined, non-preemptable and addressable with a 32-bit signed // offset from the toc base, the address of var can be computed by adding an // offset to the toc base, saving a load. // // addis 3,2,var@toc@ha # this may be relaxed to a nop, // addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc // ld/lwa 3, 0(3) # load the value from the address // // Returns true if the relaxation is performed. -bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, - uint8_t *bufLoc) { +bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, + uint8_t *bufLoc) { assert(config->tocOptimize); if (rel.addend < 0) return false; // If the symbol is not the .toc section, this isn't a toc-indirection. Defined *defSym = dyn_cast(rel.sym); if (!defSym || !defSym->isSection() || defSym->section->name != ".toc") return false; Defined *d; int64_t addend; auto *tocISB = cast(defSym->section); std::tie(d, addend) = config->isLE ? getRelaTocSymAndAddend(tocISB, rel.addend) : getRelaTocSymAndAddend(tocISB, rel.addend); // Only non-preemptable defined symbols can be relaxed. if (!d || d->isPreemptible) return false; + // R_PPC64_ADDR64 should have created a canonical PLT for the non-preemptable + // ifunc and changed its type to STT_FUNC. + assert(!d->isGnuIFunc()); + // Two instructions can materialize a 32-bit signed offset from the toc base. uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); if (!isInt<32>(tocRelative)) return false; // Add PPC64TocOffset that will be subtracted by relocateOne(). target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset); return true; } namespace { class PPC64 final : public TargetInfo { public: PPC64(); int getTlsGdRelaxSkip(RelType type) const override; uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; void writeGotHeader(uint8_t *buf) const override; bool needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const override; void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const override; }; } // namespace // Relocation masks following the #lo(value), #hi(value), #ha(value), // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi // document. static uint16_t lo(uint64_t v) { return v; } static uint16_t hi(uint64_t v) { return v >> 16; } static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; } static uint16_t higher(uint64_t v) { return v >> 32; } static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; } static uint16_t highest(uint64_t v) { return v >> 48; } static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; } // Extracts the 'PO' field of an instruction encoding. static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); } static bool isDQFormInstruction(uint32_t encoding) { switch (getPrimaryOpCode(encoding)) { default: return false; case 56: // The only instruction with a primary opcode of 56 is `lq`. return true; case 61: // There are both DS and DQ instruction forms with this primary opcode. // Namely `lxv` and `stxv` are the DQ-forms that use it. // The DS 'XO' bits being set to 01 is restricted to DQ form. return (encoding & 3) == 0x1; } } static bool isInstructionUpdateForm(uint32_t encoding) { switch (getPrimaryOpCode(encoding)) { default: return false; case LBZU: case LHAU: case LHZU: case LWZU: case LFSU: case LFDU: case STBU: case STHU: case STWU: case STFSU: case STFDU: return true; // LWA has the same opcode as LD, and the DS bits is what differentiates // between LD/LDU/LWA case LD: case STD: return (encoding & 3) == 1; } } // There are a number of places when we either want to read or write an // instruction when handling a half16 relocation type. On big-endian the buffer // pointer is pointing into the middle of the word we want to extract, and on // little-endian it is pointing to the start of the word. These 2 helpers are to // simplify reading and writing in that context. static void writeFromHalf16(uint8_t *loc, uint32_t insn) { write32(config->isLE ? loc : loc - 2, insn); } static uint32_t readFromHalf16(const uint8_t *loc) { return read32(config->isLE ? loc : loc - 2); } PPC64::PPC64() { gotRel = R_PPC64_GLOB_DAT; noneRel = R_PPC64_NONE; pltRel = R_PPC64_JMP_SLOT; relativeRel = R_PPC64_RELATIVE; iRelativeRel = R_PPC64_IRELATIVE; symbolicRel = R_PPC64_ADDR64; pltEntrySize = 4; gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; gotPltHeaderEntriesNum = 2; pltHeaderSize = 60; needsThunks = true; tlsModuleIndexRel = R_PPC64_DTPMOD64; tlsOffsetRel = R_PPC64_DTPREL64; tlsGotRel = R_PPC64_TPREL64; needsMoreStackNonSplit = false; // We need 64K pages (at least under glibc/Linux, the loader won't // set different permissions on a finer granularity than that). defaultMaxPageSize = 65536; // The PPC64 ELF ABI v1 spec, says: // // It is normally desirable to put segments with different characteristics // in separate 256 Mbyte portions of the address space, to give the // operating system full paging flexibility in the 64-bit address space. // // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers // use 0x10000000 as the starting address. defaultImageBase = 0x10000000; write32(trapInstr.data(), 0x7fe00008); } int PPC64::getTlsGdRelaxSkip(RelType type) const { // A __tls_get_addr call instruction is marked with 2 relocations: // // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation // R_PPC64_REL24: __tls_get_addr // // After the relaxation we no longer call __tls_get_addr and should skip both // relocations to not create a false dependence on __tls_get_addr being // defined. if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD) return 2; return 1; } static uint32_t getEFlags(InputFile *file) { if (config->ekind == ELF64BEKind) return cast>(file)->getObj().getHeader()->e_flags; return cast>(file)->getObj().getHeader()->e_flags; } // This file implements v2 ABI. This function makes sure that all // object files have v2 or an unspecified version as an ABI version. uint32_t PPC64::calcEFlags() const { for (InputFile *f : objectFiles) { uint32_t flag = getEFlags(f); if (flag == 1) error(toString(f) + ": ABI version 1 is not supported"); else if (flag > 2) error(toString(f) + ": unrecognized e_flags: " + Twine(flag)); } return 2; } void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC64_TOC16_HA: // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop". relocateOne(loc, type, val); break; case R_PPC64_TOC16_LO_DS: { // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or // "addi reg, 2, var@toc". uint32_t insn = readFromHalf16(loc); if (getPrimaryOpCode(insn) != LD) error("expected a 'ld' for got-indirect to toc-relative relaxing"); writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000); relocateOne(loc, R_PPC64_TOC16_LO, val); break; } default: llvm_unreachable("unexpected relocation type"); } } void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x // R_PPC64_REL24 __tls_get_addr // nop None None // Relaxing to local exec entails converting: // addis r3, r2, x@got@tlsgd@ha into nop // addi r3, r3, x@got@tlsgd@l into addis r3, r13, x@tprel@ha // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, x@tprel@l switch (type) { case R_PPC64_GOT_TLSGD16_HA: writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 relocateOne(loc, R_PPC64_TPREL16_HA, val); break; case R_PPC64_TLSGD: write32(loc, 0x60000000); // nop write32(loc + 4, 0x38630000); // addi r3, r3 // Since we are relocating a half16 type relocation and Loc + 4 points to // the start of an instruction we need to advance the buffer by an extra // 2 bytes on BE. relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), R_PPC64_TPREL16_LO, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. // The local dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r3, r2, x@got@tlsld@ha R_PPC64_GOT_TLSLD16_HA x // addi r3, r3, x@got@tlsld@l R_PPC64_GOT_TLSLD16_LO x // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSLD x // R_PPC64_REL24 __tls_get_addr // nop None None // Relaxing to local exec entails converting: // addis r3, r2, x@got@tlsld@ha into nop // addi r3, r3, x@got@tlsld@l into addis r3, r13, 0 // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, 4096 switch (type) { case R_PPC64_GOT_TLSLD16_HA: writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSLD16_LO: writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0 break; case R_PPC64_TLSLD: write32(loc, 0x60000000); // nop write32(loc + 4, 0x38631000); // addi r3, r3, 4096 break; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_HA: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: relocateOne(loc, type, val); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); } } -unsigned elf::getPPCDFormOp(unsigned secondaryOp) { +unsigned getPPCDFormOp(unsigned secondaryOp) { switch (secondaryOp) { case LBZX: return LBZ; case LHZX: return LHZ; case LWZX: return LWZ; case LDX: return LD; case STBX: return STB; case STHX: return STH; case STWX: return STW; case STDX: return STD; case ADD: return ADDI; default: return 0; } } void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // The initial exec code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x // ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x // add r9, r9, x@tls R_PPC64_TLS x // Relaxing to local exec entails converting: // addis r9, r2, x@got@tprel@ha into nop // ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha // add r9, r9, x@tls into addi r9, r9, x@tprel@l // x@tls R_PPC64_TLS is a relocation which does not compute anything, // it is replaced with r13 (thread pointer). // The add instruction in the initial exec sequence has multiple variations // that need to be handled. If we are building an address it will use an add // instruction, if we are accessing memory it will use any of the X-form // indexed load or store instructions. unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; switch (type) { case R_PPC64_GOT_TPREL16_HA: write32(loc - offset, 0x60000000); // nop break; case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: { uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10 write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13 relocateOne(loc, R_PPC64_TPREL16_HA, val); break; } case R_PPC64_TLS: { uint32_t primaryOp = getPrimaryOpCode(read32(loc)); if (primaryOp != 31) error("unrecognized instruction for IE to LE R_PPC64_TLS"); uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 uint32_t dFormOp = getPPCDFormOp(secondaryOp); if (dFormOp == 0) error("unrecognized instruction for IE to LE R_PPC64_TLS"); write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); relocateOne(loc + offset, R_PPC64_TPREL16_LO, val); break; } default: llvm_unreachable("unknown relocation for IE to LE"); break; } } RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_PPC64_NONE: + return R_NONE; + case R_PPC64_ADDR16: + case R_PPC64_ADDR16_DS: + case R_PPC64_ADDR16_HA: + case R_PPC64_ADDR16_HI: + case R_PPC64_ADDR16_HIGHER: + case R_PPC64_ADDR16_HIGHERA: + case R_PPC64_ADDR16_HIGHEST: + case R_PPC64_ADDR16_HIGHESTA: + case R_PPC64_ADDR16_LO: + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR32: + case R_PPC64_ADDR64: + return R_ABS; case R_PPC64_GOT16: case R_PPC64_GOT16_DS: case R_PPC64_GOT16_HA: case R_PPC64_GOT16_HI: case R_PPC64_GOT16_LO: case R_PPC64_GOT16_LO_DS: return R_GOT_OFF; case R_PPC64_TOC16: case R_PPC64_TOC16_DS: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_LO: return R_GOTREL; case R_PPC64_TOC16_HA: case R_PPC64_TOC16_LO_DS: return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL; case R_PPC64_TOC: return R_PPC64_TOCBASE; case R_PPC64_REL14: case R_PPC64_REL24: return R_PPC64_CALL_PLT; case R_PPC64_REL16_LO: case R_PPC64_REL16_HA: + case R_PPC64_REL16_HI: case R_PPC64_REL32: case R_PPC64_REL64: return R_PC; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_HA: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_LO: return R_TLSGD_GOT; case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TLSLD16_LO: return R_TLSLD_GOT; case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_TPREL16_HI: return R_GOT_OFF; case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_GOT_DTPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_DS: case R_PPC64_GOT_DTPREL16_HI: return R_TLSLD_GOT_OFF; case R_PPC64_TPREL16: case R_PPC64_TPREL16_HA: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: case R_PPC64_TPREL16_DS: case R_PPC64_TPREL16_LO_DS: case R_PPC64_TPREL16_HIGHER: case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: return R_TLS; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_HA: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_HIGHER: case R_PPC64_DTPREL16_HIGHERA: case R_PPC64_DTPREL16_HIGHEST: case R_PPC64_DTPREL16_HIGHESTA: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: case R_PPC64_DTPREL64: return R_DTPREL; case R_PPC64_TLSGD: return R_TLSDESC_CALL; case R_PPC64_TLSLD: return R_TLSLD_HINT; case R_PPC64_TLS: return R_TLSIE_HINT; default: - return R_ABS; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } RelType PPC64::getDynRel(RelType type) const { if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC) return R_PPC64_ADDR64; return R_PPC64_NONE; } void PPC64::writeGotHeader(uint8_t *buf) const { write64(buf, getPPC64TocBase()); } void PPC64::writePltHeader(uint8_t *buf) const { // The generic resolver stub goes first. write32(buf + 0, 0x7c0802a6); // mflr r0 write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> write32(buf + 8, 0x7d6802a6); // mflr r11 write32(buf + 12, 0x7c0803a6); // mtlr r0 write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12 write32(buf + 20, 0x380cffcc); // subi r0,r12,52 write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2 write32(buf + 28, 0xe98b002c); // ld r12,44(r11) write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11 write32(buf + 36, 0xe98b0000); // ld r12,0(r11) write32(buf + 40, 0xe96b0008); // ld r11,8(r11) write32(buf + 44, 0x7d8903a6); // mtctr r12 write32(buf + 48, 0x4e800420); // bctr // The 'bcl' instruction will set the link register to the address of the // following instruction ('mflr r11'). Here we store the offset from that // instruction to the first entry in the GotPlt section. int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8); write64(buf + 52, gotPltOffset); } void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { int32_t offset = pltHeaderSize + index * pltEntrySize; // bl __glink_PLTresolve write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } static std::pair toAddr16Rel(RelType type, uint64_t val) { // Relocations relative to the toc-base need to be adjusted by the Toc offset. uint64_t tocBiasedVal = val - ppc64TocOffset; // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset. uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset; switch (type) { // TOC biased relocation. case R_PPC64_GOT16: case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSLD16: case R_PPC64_TOC16: return {R_PPC64_ADDR16, tocBiasedVal}; case R_PPC64_GOT16_DS: case R_PPC64_TOC16_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_DTPREL16_DS: return {R_PPC64_ADDR16_DS, tocBiasedVal}; case R_PPC64_GOT16_HA: case R_PPC64_GOT_TLSGD16_HA: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_TOC16_HA: return {R_PPC64_ADDR16_HA, tocBiasedVal}; case R_PPC64_GOT16_HI: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_TOC16_HI: return {R_PPC64_ADDR16_HI, tocBiasedVal}; case R_PPC64_GOT16_LO: case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_TOC16_LO: return {R_PPC64_ADDR16_LO, tocBiasedVal}; case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_LO_DS: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_LO_DS: return {R_PPC64_ADDR16_LO_DS, tocBiasedVal}; // Dynamic Thread pointer biased relocation types. case R_PPC64_DTPREL16: return {R_PPC64_ADDR16, dtpBiasedVal}; case R_PPC64_DTPREL16_DS: return {R_PPC64_ADDR16_DS, dtpBiasedVal}; case R_PPC64_DTPREL16_HA: return {R_PPC64_ADDR16_HA, dtpBiasedVal}; case R_PPC64_DTPREL16_HI: return {R_PPC64_ADDR16_HI, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHER: return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHERA: return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHEST: return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHESTA: return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal}; case R_PPC64_DTPREL16_LO: return {R_PPC64_ADDR16_LO, dtpBiasedVal}; case R_PPC64_DTPREL16_LO_DS: return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal}; case R_PPC64_DTPREL64: return {R_PPC64_ADDR64, dtpBiasedVal}; default: return {type, val}; } } static bool isTocOptType(RelType type) { switch (type) { case R_PPC64_GOT16_HA: case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_HA: case R_PPC64_TOC16_LO_DS: case R_PPC64_TOC16_LO: return true; default: return false; } } void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // We need to save the original relocation type to use in diagnostics, and // use the original type to determine if we should toc-optimize the // instructions being relocated. RelType originalType = type; bool shouldTocOptimize = isTocOptType(type); // For dynamic thread pointer relative, toc-relative, and got-indirect // relocations, proceed in terms of the corresponding ADDR16 relocation type. std::tie(type, val) = toAddr16Rel(type, val); switch (type) { case R_PPC64_ADDR14: { checkAlignment(loc, val, 4, type); // Preserve the AA/LK bits in the branch instruction uint8_t aalk = loc[3]; write16(loc + 2, (aalk & 3) | (val & 0xfffc)); break; } case R_PPC64_ADDR16: checkIntUInt(loc, val, 16, originalType); write16(loc, val); break; case R_PPC64_ADDR32: checkIntUInt(loc, val, 32, originalType); write32(loc, val); break; case R_PPC64_ADDR16_DS: case R_PPC64_TPREL16_DS: { checkInt(loc, val, 16, originalType); // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3; checkAlignment(loc, lo(val), mask + 1, originalType); write16(loc, (read16(loc) & mask) | lo(val)); } break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: case R_PPC64_TPREL16_HA: if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) writeFromHalf16(loc, 0x60000000); else write16(loc, ha(val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: case R_PPC64_TPREL16_HI: write16(loc, hi(val)); break; case R_PPC64_ADDR16_HIGHER: case R_PPC64_TPREL16_HIGHER: write16(loc, higher(val)); break; case R_PPC64_ADDR16_HIGHERA: case R_PPC64_TPREL16_HIGHERA: write16(loc, highera(val)); break; case R_PPC64_ADDR16_HIGHEST: case R_PPC64_TPREL16_HIGHEST: write16(loc, highest(val)); break; case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_TPREL16_HIGHESTA: write16(loc, highesta(val)); break; case R_PPC64_ADDR16_LO: case R_PPC64_REL16_LO: case R_PPC64_TPREL16_LO: // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the // toc-pointer register r2, as the base register. if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { uint32_t insn = readFromHalf16(loc); if (isInstructionUpdateForm(insn)) error(getErrorLocation(loc) + "can't toc-optimize an update instruction: 0x" + utohexstr(insn)); writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val)); } else { write16(loc, lo(val)); } break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_TPREL16_LO_DS: { // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. uint32_t insn = readFromHalf16(loc); uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3; checkAlignment(loc, lo(val), mask + 1, originalType); if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the toc // pointer register r2, as the base register. if (isInstructionUpdateForm(insn)) error(getErrorLocation(loc) + "Can't toc-optimize an update instruction: 0x" + Twine::utohexstr(insn)); insn &= 0xffe00000 | mask; writeFromHalf16(loc, insn | 0x00020000 | lo(val)); } else { write16(loc, (read16(loc) & mask) | lo(val)); } } break; case R_PPC64_TPREL16: checkInt(loc, val, 16, originalType); write16(loc, val); break; case R_PPC64_REL32: checkInt(loc, val, 32, type); write32(loc, val); break; case R_PPC64_ADDR64: case R_PPC64_REL64: case R_PPC64_TOC: write64(loc, val); break; case R_PPC64_REL14: { uint32_t mask = 0x0000FFFC; checkInt(loc, val, 16, type); checkAlignment(loc, val, 4, type); write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_REL24: { uint32_t mask = 0x03FFFFFC; checkInt(loc, val, 26, type); checkAlignment(loc, val, 4, type); write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_DTPREL64: write64(loc, val - dynamicThreadPointerOffset); break; default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + llvm_unreachable("unknown relocation"); } } bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s) const { if (type != R_PPC64_REL14 && type != R_PPC64_REL24) return false; // If a function is in the Plt it needs to be called with a call-stub. if (s.isInPlt()) return true; // If a symbol is a weak undefined and we are compiling an executable // it doesn't need a range-extending thunk since it can't be called. if (s.isUndefWeak() && !config->shared) return false; // If the offset exceeds the range of the branch type then it will need // a range-extending thunk. // See the comment in getRelocTargetVA() about R_PPC64_CALL. return !inBranchRange(type, branchAddr, s.getVA() + getPPC64GlobalEntryToLocalEntryOffset(s.stOther)); } uint32_t PPC64::getThunkSectionSpacing() const { // See comment in Arch/ARM.cpp for a more detailed explanation of // getThunkSectionSpacing(). For PPC64 we pick the constant here based on // R_PPC64_REL24, which is used by unconditional branch instructions. // 0x2000000 = (1 << 24-1) * 4 return 0x2000000; } bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { int64_t offset = dst - src; if (type == R_PPC64_REL14) return isInt<16>(offset); if (type == R_PPC64_REL24) return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const { if (expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; return expr; } // Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` uses 4 instructions. // Instruction Relocation Symbol // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x // R_PPC64_REL24 __tls_get_addr // nop None None // // Relaxing to initial-exec entails: // 1) Convert the addis/addi pair that builds the address of the tls_index // struct for 'x' to an addis/ld pair that loads an offset from a got-entry. // 2) Convert the call to __tls_get_addr to a nop. // 3) Convert the nop following the call to an add of the loaded offset to the // thread pointer. // Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is // used as the relaxation hint for both steps 2 and 3. void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC64_GOT_TLSGD16_HA: // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to // addis rT, r2, sym@got@tprel@ha. relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val); return; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: { // Relax from addi r3, rA, sym@got@tlsgd@l to // ld r3, sym@got@tprel@l(rA) uint32_t ra = (readFromHalf16(loc) & (0x1f << 16)); writeFromHalf16(loc, 0xe8600000 | ra); relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } case R_PPC64_TLSGD: write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 return; default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); } } // The prologue for a split-stack function is expected to look roughly // like this: // .Lglobal_entry_point: // # TOC pointer initalization. // ... // .Llocal_entry_point: // # load the __private_ss member of the threads tcbhead. // ld r0,-0x7000-64(r13) // # subtract the functions stack size from the stack pointer. // addis r12, r1, ha(-stack-frame size) // addi r12, r12, l(-stack-frame size) // # compare needed to actual and branch to allocate_more_stack if more // # space is needed, otherwise fallthrough to 'normal' function body. // cmpld cr7,r12,r0 // blt- cr7, .Lallocate_more_stack // // -) The allocate_more_stack block might be placed after the split-stack // prologue and the `blt-` replaced with a `bge+ .Lnormal_func_body` // instead. // -) If either the addis or addi is not needed due to the stack size being // smaller then 32K or a multiple of 64K they will be replaced with a nop, // but there will always be 2 instructions the linker can overwrite for the // adjusted stack size. // // The linkers job here is to increase the stack size used in the addis/addi // pair by split-stack-size-adjust. // addis r12, r1, ha(-stack-frame size - split-stack-adjust-size) // addi r12, r12, l(-stack-frame size - split-stack-adjust-size) bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const { // If the caller has a global entry point adjust the buffer past it. The start // of the split-stack prologue will be at the local entry point. loc += getPPC64GlobalEntryToLocalEntryOffset(stOther); // At the very least we expect to see a load of some split-stack data from the // tcb, and 2 instructions that calculate the ending stack address this // function will require. If there is not enough room for at least 3 // instructions it can't be a split-stack prologue. if (loc + 12 >= end) return false; // First instruction must be `ld r0, -0x7000-64(r13)` if (read32(loc) != 0xe80d8fc0) return false; int16_t hiImm = 0; int16_t loImm = 0; // First instruction can be either an addis if the frame size is larger then // 32K, or an addi if the size is less then 32K. int32_t firstInstr = read32(loc + 4); if (getPrimaryOpCode(firstInstr) == 15) { hiImm = firstInstr & 0xFFFF; } else if (getPrimaryOpCode(firstInstr) == 14) { loImm = firstInstr & 0xFFFF; } else { return false; } // Second instruction is either an addi or a nop. If the first instruction was // an addi then LoImm is set and the second instruction must be a nop. uint32_t secondInstr = read32(loc + 8); if (!loImm && getPrimaryOpCode(secondInstr) == 14) { loImm = secondInstr & 0xFFFF; } else if (secondInstr != 0x60000000) { return false; } // The register operands of the first instruction should be the stack-pointer // (r1) as the input (RA) and r12 as the output (RT). If the second // instruction is not a nop, then it should use r12 as both input and output. auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT, uint8_t expectedRA) { return ((instr & 0x3E00000) >> 21 == expectedRT) && ((instr & 0x1F0000) >> 16 == expectedRA); }; if (!checkRegOperands(firstInstr, 12, 1)) return false; if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12)) return false; int32_t stackFrameSize = (hiImm * 65536) + loImm; // Check that the adjusted size doesn't overflow what we can represent with 2 // instructions. if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) { error(getErrorLocation(loc) + "split-stack prologue adjustment overflows"); return false; } int32_t adjustedStackFrameSize = stackFrameSize - config->splitStackAdjustSize; loImm = adjustedStackFrameSize & 0xFFFF; hiImm = (adjustedStackFrameSize + 0x8000) >> 16; if (hiImm) { write32(loc + 4, 0x3D810000 | (uint16_t)hiImm); // If the low immediate is zero the second instruction will be a nop. secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000; write32(loc + 8, secondInstr); } else { // addi r12, r1, imm write32(loc + 4, (0x39810000) | (uint16_t)loImm); write32(loc + 8, 0x60000000); } return true; } -TargetInfo *elf::getPPC64TargetInfo() { +TargetInfo *getPPC64TargetInfo() { static PPC64 target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/RISCV.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/RISCV.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/RISCV.cpp (revision 353950) @@ -1,442 +1,446 @@ //===- RISCV.cpp ----------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "SyntheticSections.h" #include "Target.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class RISCV final : public TargetInfo { public: RISCV(); uint32_t calcEFlags() const override; void writeGotHeader(uint8_t *buf) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; RelType getDynRel(RelType type) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // end anonymous namespace const uint64_t dtpOffset = 0x800; enum Op { ADDI = 0x13, AUIPC = 0x17, JALR = 0x67, LD = 0x3003, LW = 0x2003, SRLI = 0x5013, SUB = 0x40000033, }; enum Reg { X_RA = 1, X_T0 = 5, X_T1 = 6, X_T2 = 7, X_T3 = 28, }; static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } static uint32_t lo12(uint32_t val) { return val & 4095; } static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) { return op | (rd << 7) | (rs1 << 15) | (imm << 20); } static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) { return op | (rd << 7) | (rs1 << 15) | (rs2 << 20); } static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) { return op | (rd << 7) | (imm << 12); } RISCV::RISCV() { copyRel = R_RISCV_COPY; noneRel = R_RISCV_NONE; pltRel = R_RISCV_JUMP_SLOT; relativeRel = R_RISCV_RELATIVE; if (config->is64) { symbolicRel = R_RISCV_64; tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64; tlsOffsetRel = R_RISCV_TLS_DTPREL64; tlsGotRel = R_RISCV_TLS_TPREL64; } else { symbolicRel = R_RISCV_32; tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32; tlsOffsetRel = R_RISCV_TLS_DTPREL32; tlsGotRel = R_RISCV_TLS_TPREL32; } gotRel = symbolicRel; // .got[0] = _DYNAMIC gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map gotPltHeaderEntriesNum = 2; pltEntrySize = 16; pltHeaderSize = 32; } static uint32_t getEFlags(InputFile *f) { if (config->is64) return cast>(f)->getObj().getHeader()->e_flags; return cast>(f)->getObj().getHeader()->e_flags; } uint32_t RISCV::calcEFlags() const { assert(!objectFiles.empty()); uint32_t target = getEFlags(objectFiles.front()); for (InputFile *f : objectFiles) { uint32_t eflags = getEFlags(f); if (eflags & EF_RISCV_RVC) target |= EF_RISCV_RVC; if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI)) error(toString(f) + ": cannot link object files with different floating-point ABI"); if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE)) error(toString(f) + ": cannot link object files with different EF_RISCV_RVE"); } return target; } void RISCV::writeGotHeader(uint8_t *buf) const { if (config->is64) write64le(buf, mainPart->dynamic->getVA()); else write32le(buf, mainPart->dynamic->getVA()); } void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const { if (config->is64) write64le(buf, in.plt->getVA()); else write32le(buf, in.plt->getVA()); } void RISCV::writePltHeader(uint8_t *buf) const { // 1: auipc t2, %pcrel_hi(.got.plt) // sub t1, t1, t3 // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0] // addi t0, t2, %pcrel_lo(1b) // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0] // l[wd] t0, Wordsize(t0); t0 = link_map // jr t3 uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); uint32_t load = config->is64 ? LD : LW; write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset))); write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3)); write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset))); write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12)); write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset))); write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2)); write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize)); write32le(buf + 28, itype(JALR, 0, X_T3, 0)); } void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { // 1: auipc t3, %pcrel_hi(f@.got.plt) // l[wd] t3, %pcrel_lo(1b)(t3) // jalr t1, t3 // nop uint32_t offset = gotPltEntryAddr - pltEntryAddr; write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset))); write32le(buf + 8, itype(JALR, X_T1, X_T3, 0)); write32le(buf + 12, itype(ADDI, 0, 0, 0)); } RelType RISCV::getDynRel(RelType type) const { return type == target->symbolicRel ? type : static_cast(R_RISCV_NONE); } RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { case R_RISCV_ADD8: case R_RISCV_ADD16: case R_RISCV_ADD32: case R_RISCV_ADD64: case R_RISCV_SET6: case R_RISCV_SET8: case R_RISCV_SET16: case R_RISCV_SET32: case R_RISCV_SUB6: case R_RISCV_SUB8: case R_RISCV_SUB16: case R_RISCV_SUB32: case R_RISCV_SUB64: return R_RISCV_ADD; case R_RISCV_JAL: case R_RISCV_BRANCH: case R_RISCV_PCREL_HI20: case R_RISCV_RVC_BRANCH: case R_RISCV_RVC_JUMP: case R_RISCV_32_PCREL: return R_PC; case R_RISCV_CALL: case R_RISCV_CALL_PLT: return R_PLT_PC; case R_RISCV_GOT_HI20: return R_GOT_PC; case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: return R_RISCV_PC_INDIRECT; case R_RISCV_TLS_GD_HI20: return R_TLSGD_PC; case R_RISCV_TLS_GOT_HI20: config->hasStaticTlsModel = true; return R_GOT_PC; case R_RISCV_TPREL_HI20: case R_RISCV_TPREL_LO12_I: case R_RISCV_TPREL_LO12_S: return R_TLS; case R_RISCV_RELAX: case R_RISCV_ALIGN: case R_RISCV_TPREL_ADD: return R_HINT; default: return R_ABS; } } // Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { return (v & ((1ULL << (begin + 1)) - 1)) >> end; } void RISCV::relocateOne(uint8_t *loc, const RelType type, const uint64_t val) const { const unsigned bits = config->wordsize * 8; switch (type) { case R_RISCV_32: write32le(loc, val); return; case R_RISCV_64: write64le(loc, val); return; case R_RISCV_RVC_BRANCH: { checkInt(loc, static_cast(val) >> 1, 8, type); checkAlignment(loc, val, 2, type); uint16_t insn = read16le(loc) & 0xE383; uint16_t imm8 = extractBits(val, 8, 8) << 12; uint16_t imm4_3 = extractBits(val, 4, 3) << 10; uint16_t imm7_6 = extractBits(val, 7, 6) << 5; uint16_t imm2_1 = extractBits(val, 2, 1) << 3; uint16_t imm5 = extractBits(val, 5, 5) << 2; insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5; write16le(loc, insn); return; } case R_RISCV_RVC_JUMP: { checkInt(loc, static_cast(val) >> 1, 11, type); checkAlignment(loc, val, 2, type); uint16_t insn = read16le(loc) & 0xE003; uint16_t imm11 = extractBits(val, 11, 11) << 12; uint16_t imm4 = extractBits(val, 4, 4) << 11; uint16_t imm9_8 = extractBits(val, 9, 8) << 9; uint16_t imm10 = extractBits(val, 10, 10) << 8; uint16_t imm6 = extractBits(val, 6, 6) << 7; uint16_t imm7 = extractBits(val, 7, 7) << 6; uint16_t imm3_1 = extractBits(val, 3, 1) << 3; uint16_t imm5 = extractBits(val, 5, 5) << 2; insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5; write16le(loc, insn); return; } case R_RISCV_RVC_LUI: { int64_t imm = SignExtend64(val + 0x800, bits) >> 12; checkInt(loc, imm, 6, type); if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` write16le(loc, (read16le(loc) & 0x0F83) | 0x4000); } else { uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12; uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2; write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12); } return; } case R_RISCV_JAL: { checkInt(loc, static_cast(val) >> 1, 20, type); checkAlignment(loc, val, 2, type); uint32_t insn = read32le(loc) & 0xFFF; uint32_t imm20 = extractBits(val, 20, 20) << 31; uint32_t imm10_1 = extractBits(val, 10, 1) << 21; uint32_t imm11 = extractBits(val, 11, 11) << 20; uint32_t imm19_12 = extractBits(val, 19, 12) << 12; insn |= imm20 | imm10_1 | imm11 | imm19_12; write32le(loc, insn); return; } case R_RISCV_BRANCH: { checkInt(loc, static_cast(val) >> 1, 12, type); checkAlignment(loc, val, 2, type); uint32_t insn = read32le(loc) & 0x1FFF07F; uint32_t imm12 = extractBits(val, 12, 12) << 31; uint32_t imm10_5 = extractBits(val, 10, 5) << 25; uint32_t imm4_1 = extractBits(val, 4, 1) << 8; uint32_t imm11 = extractBits(val, 11, 11) << 7; insn |= imm12 | imm10_5 | imm4_1 | imm11; write32le(loc, insn); return; } // auipc + jalr pair case R_RISCV_CALL: case R_RISCV_CALL_PLT: { int64_t hi = SignExtend64(val + 0x800, bits) >> 12; checkInt(loc, hi, 20, type); if (isInt<20>(hi)) { relocateOne(loc, R_RISCV_PCREL_HI20, val); relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val); } return; } case R_RISCV_GOT_HI20: case R_RISCV_PCREL_HI20: case R_RISCV_TLS_GD_HI20: case R_RISCV_TLS_GOT_HI20: case R_RISCV_TPREL_HI20: case R_RISCV_HI20: { uint64_t hi = val + 0x800; checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type); write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000)); return; } case R_RISCV_PCREL_LO12_I: case R_RISCV_TPREL_LO12_I: case R_RISCV_LO12_I: { uint64_t hi = (val + 0x800) >> 12; uint64_t lo = val - (hi << 12); write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20)); return; } case R_RISCV_PCREL_LO12_S: case R_RISCV_TPREL_LO12_S: case R_RISCV_LO12_S: { uint64_t hi = (val + 0x800) >> 12; uint64_t lo = val - (hi << 12); uint32_t imm11_5 = extractBits(lo, 11, 5) << 25; uint32_t imm4_0 = extractBits(lo, 4, 0) << 7; write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0); return; } case R_RISCV_ADD8: *loc += val; return; case R_RISCV_ADD16: write16le(loc, read16le(loc) + val); return; case R_RISCV_ADD32: write32le(loc, read32le(loc) + val); return; case R_RISCV_ADD64: write64le(loc, read64le(loc) + val); return; case R_RISCV_SUB6: *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f); return; case R_RISCV_SUB8: *loc -= val; return; case R_RISCV_SUB16: write16le(loc, read16le(loc) - val); return; case R_RISCV_SUB32: write32le(loc, read32le(loc) - val); return; case R_RISCV_SUB64: write64le(loc, read64le(loc) - val); return; case R_RISCV_SET6: *loc = (*loc & 0xc0) | (val & 0x3f); return; case R_RISCV_SET8: *loc = val; return; case R_RISCV_SET16: write16le(loc, val); return; case R_RISCV_SET32: case R_RISCV_32_PCREL: write32le(loc, val); return; case R_RISCV_TLS_DTPREL32: write32le(loc, val - dtpOffset); break; case R_RISCV_TLS_DTPREL64: write64le(loc, val - dtpOffset); break; case R_RISCV_ALIGN: case R_RISCV_RELAX: return; // Ignored (for now) case R_RISCV_NONE: return; // Do nothing // These are handled by the dynamic linker case R_RISCV_RELATIVE: case R_RISCV_COPY: case R_RISCV_JUMP_SLOT: // GP-relative relocations are only produced after relaxation, which // we don't support for now case R_RISCV_GPREL_I: case R_RISCV_GPREL_S: default: error(getErrorLocation(loc) + "unimplemented relocation: " + toString(type)); return; } } -TargetInfo *elf::getRISCVTargetInfo() { +TargetInfo *getRISCVTargetInfo() { static RISCV target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/SPARCV9.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/SPARCV9.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/SPARCV9.cpp (revision 353950) @@ -1,149 +1,153 @@ //===- SPARCV9.cpp --------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class SPARCV9 final : public TargetInfo { public: SPARCV9(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace SPARCV9::SPARCV9() { copyRel = R_SPARC_COPY; gotRel = R_SPARC_GLOB_DAT; noneRel = R_SPARC_NONE; pltRel = R_SPARC_JMP_SLOT; relativeRel = R_SPARC_RELATIVE; symbolicRel = R_SPARC_64; pltEntrySize = 32; pltHeaderSize = 4 * pltEntrySize; defaultCommonPageSize = 8192; defaultMaxPageSize = 0x100000; defaultImageBase = 0x100000; } RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { case R_SPARC_32: case R_SPARC_UA32: case R_SPARC_64: case R_SPARC_UA64: return R_ABS; case R_SPARC_PC10: case R_SPARC_PC22: case R_SPARC_DISP32: case R_SPARC_WDISP30: return R_PC; case R_SPARC_GOT10: return R_GOT_OFF; case R_SPARC_GOT22: return R_GOT_OFF; case R_SPARC_WPLT30: return R_PLT_PC; case R_SPARC_NONE: return R_NONE; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); return R_NONE; } } void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_SPARC_32: case R_SPARC_UA32: // V-word32 checkUInt(loc, val, 32, type); write32be(loc, val); break; case R_SPARC_DISP32: // V-disp32 checkInt(loc, val, 32, type); write32be(loc, val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 checkInt(loc, val, 32, type); write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); break; case R_SPARC_22: // V-imm22 checkUInt(loc, val, 22, type); write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; case R_SPARC_GOT22: case R_SPARC_PC22: // T-imm22 write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); break; case R_SPARC_WDISP19: // V-disp19 checkInt(loc, val, 21, type); write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); break; case R_SPARC_GOT10: case R_SPARC_PC10: // T-simm10 write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; case R_SPARC_64: case R_SPARC_UA64: // V-xword64 write64be(loc, val); break; default: llvm_unreachable("unknown relocation"); } } void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t pltData[] = { 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00 // nop }; memcpy(buf, pltData, sizeof(pltData)); uint64_t off = pltHeaderSize + pltEntrySize * index; relocateOne(buf, R_SPARC_22, off); relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); } -TargetInfo *elf::getSPARCV9TargetInfo() { +TargetInfo *getSPARCV9TargetInfo() { static SPARCV9 target; return ⌖ } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/X86.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/X86.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/X86.cpp (revision 353950) @@ -1,554 +1,558 @@ //===- X86.cpp ------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class X86 : public TargetInfo { public: X86(); int getTlsGdRelaxSkip(RelType type) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void writeGotPltHeader(uint8_t *buf) const override; RelType getDynRel(RelType type) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const override; void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace X86::X86() { copyRel = R_386_COPY; gotRel = R_386_GLOB_DAT; noneRel = R_386_NONE; pltRel = R_386_JUMP_SLOT; iRelativeRel = R_386_IRELATIVE; relativeRel = R_386_RELATIVE; symbolicRel = R_386_32; tlsGotRel = R_386_TLS_TPOFF; tlsModuleIndexRel = R_386_TLS_DTPMOD32; tlsOffsetRel = R_386_TLS_DTPOFF32; pltEntrySize = 16; pltHeaderSize = 16; trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the non-PAE large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. defaultImageBase = 0x400000; } int X86::getTlsGdRelaxSkip(RelType type) const { return 2; } RelExpr X86::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { // There are 4 different TLS variable models with varying degrees of // flexibility and performance. LocalExec and InitialExec models are fast but // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the // dynamic section to let runtime know about that. if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE || type == R_386_TLS_GOTIE) config->hasStaticTlsModel = true; switch (type) { case R_386_8: case R_386_16: case R_386_32: return R_ABS; case R_386_TLS_LDO_32: return R_DTPREL; case R_386_TLS_GD: return R_TLSGD_GOTPLT; case R_386_TLS_LDM: return R_TLSLD_GOTPLT; case R_386_PLT32: return R_PLT_PC; case R_386_PC8: case R_386_PC16: case R_386_PC32: return R_PC; case R_386_GOTPC: return R_GOTPLTONLY_PC; case R_386_TLS_IE: return R_GOT; case R_386_GOT32: case R_386_GOT32X: // These relocations are arguably mis-designed because their calculations // depend on the instructions they are applied to. This is bad because we // usually don't care about whether the target section contains valid // machine instructions or not. But this is part of the documented ABI, so // we had to implement as the standard requires. // // x86 does not support PC-relative data access. Therefore, in order to // access GOT contents, a GOT address needs to be known at link-time // (which means non-PIC) or compilers have to emit code to get a GOT // address at runtime (which means code is position-independent but // compilers need to emit extra code for each GOT access.) This decision // is made at compile-time. In the latter case, compilers emit code to // load an GOT address to a register, which is usually %ebx. // // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or // foo@GOT(%ebx). // // foo@GOT is not usable in PIC. If we are creating a PIC output and if we // find such relocation, we should report an error. foo@GOT is resolved to // an *absolute* address of foo's GOT entry, because both GOT address and // foo's offset are known. In other words, it's G + A. // // foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to // foo's GOT entry in the table, because GOT address is not known but foo's // offset in the table is known. It's G + A - GOT. // // It's unfortunate that compilers emit the same relocation for these // different use cases. In order to distinguish them, we have to read a // machine instruction. // // The following code implements it. We assume that Loc[0] is the first byte // of a displacement or an immediate field of a valid machine // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at // the byte, we can determine whether the instruction uses the operand as an // absolute address (R_GOT) or a register-relative address (R_GOTPLT). return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT; case R_386_TLS_GOTIE: return R_GOTPLT; case R_386_GOTOFF: return R_GOTPLTREL; case R_386_TLS_LE: return R_TLS; case R_386_TLS_LE_32: return R_NEG_TLS; case R_386_NONE: return R_NONE; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); return R_NONE; } } RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const { switch (expr) { default: return expr; case R_RELAX_TLS_GD_TO_IE: return R_RELAX_TLS_GD_TO_IE_GOTPLT; case R_RELAX_TLS_GD_TO_LE: return R_RELAX_TLS_GD_TO_LE_NEG; } } void X86::writeGotPltHeader(uint8_t *buf) const { write32le(buf, mainPart->dynamic->getVA()); } void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const { // Entries in .got.plt initially points back to the corresponding // PLT entries with a fixed offset to skip the first instruction. write32le(buf, s.getPltVA() + 6); } void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const { // An x86 entry is the address of the ifunc resolver function. write32le(buf, s.getVA()); } RelType X86::getDynRel(RelType type) const { if (type == R_386_TLS_LE) return R_386_TLS_TPOFF; if (type == R_386_TLS_LE_32) return R_386_TLS_TPOFF32; return type; } void X86::writePltHeader(uint8_t *buf) const { if (config->isPic) { const uint8_t v[] = { 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx) 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx) 0x90, 0x90, 0x90, 0x90 // nop }; memcpy(buf, v, sizeof(v)); return; } const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4) 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8) 0x90, 0x90, 0x90, 0x90, // nop }; memcpy(buf, pltData, sizeof(pltData)); uint32_t gotPlt = in.gotPlt->getVA(); write32le(buf + 2, gotPlt + 4); write32le(buf + 8, gotPlt + 8); } void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { if (config->isPic) { const uint8_t inst[] = { 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx) 0x68, 0, 0, 0, 0, // pushl $reloc_offset 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC }; memcpy(buf, inst, sizeof(inst)); write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA()); } else { const uint8_t inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT 0x68, 0, 0, 0, 0, // pushl $reloc_offset 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC }; memcpy(buf, inst, sizeof(inst)); write32le(buf + 2, gotPltEntryAddr); } write32le(buf + 7, relOff); write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { case R_386_8: case R_386_PC8: return SignExtend64<8>(*buf); case R_386_16: case R_386_PC16: return SignExtend64<16>(read16le(buf)); case R_386_32: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: case R_386_GOTPC: case R_386_PC32: case R_386_PLT32: case R_386_TLS_LDO_32: case R_386_TLS_LE: return SignExtend64<32>(read32le(buf)); default: return 0; } } void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are // being used for some 16-bit programs such as boot loaders, so // we want to support them. checkIntUInt(loc, val, 8, type); *loc = val; break; case R_386_PC8: checkInt(loc, val, 8, type); *loc = val; break; case R_386_16: checkIntUInt(loc, val, 16, type); write16le(loc, val); break; case R_386_PC16: // R_386_PC16 is normally used with 16 bit code. In that situation // the PC is 16 bits, just like the addend. This means that it can // point from any 16 bit address to any other if the possibility // of wrapping is included. // The only restriction we have to check then is that the destination // address fits in 16 bits. That is impossible to do here. The problem is // that we are passed the final value, which already had the // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. checkInt(loc, val, 17, type); write16le(loc, val); break; case R_386_32: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: case R_386_GOTPC: case R_386_PC32: case R_386_PLT32: case R_386_RELATIVE: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: case R_386_TLS_GD: case R_386_TLS_GOTIE: case R_386_TLS_IE: case R_386_TLS_LDM: case R_386_TLS_LDO_32: case R_386_TLS_LE: case R_386_TLS_LE_32: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: checkInt(loc, val, 32, type); write32le(loc, val); break; default: llvm_unreachable("unknown relocation"); } } void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0,%eax // subl $x@ntpoff,%eax const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax }; memcpy(loc - 3, inst, sizeof(inst)); write32le(loc + 5, val); } void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0, %eax // addl x@gotntpoff(%ebx), %eax const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax }; memcpy(loc - 3, inst, sizeof(inst)); write32le(loc + 5, val); } // In some conditions, relocations can be optimized to avoid using GOT. // This function does that for Initial Exec to Local Exec case. void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // Ulrich's document section 6.2 says that @gotntpoff can // be used with MOVL or ADDL instructions. // @indntpoff is similar to @gotntpoff, but for use in // position dependent code. uint8_t reg = (loc[-1] >> 3) & 7; if (type == R_386_TLS_IE) { if (loc[-1] == 0xa1) { // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" // This case is different from the generic case below because // this is a 5 byte instruction while below is 6 bytes. loc[-1] = 0xb8; } else if (loc[-2] == 0x8b) { // "movl foo@indntpoff,%reg" -> "movl $foo,%reg" loc[-2] = 0xc7; loc[-1] = 0xc0 | reg; } else { // "addl foo@indntpoff,%reg" -> "addl $foo,%reg" loc[-2] = 0x81; loc[-1] = 0xc0 | reg; } } else { assert(type == R_386_TLS_GOTIE); if (loc[-2] == 0x8b) { // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" loc[-2] = 0xc7; loc[-1] = 0xc0 | reg; } else { // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg" loc[-2] = 0x8d; loc[-1] = 0x80 | (reg << 3) | reg; } } write32le(loc, val); } void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { if (type == R_386_TLS_LDO_32) { write32le(loc, val); return; } // Convert // leal foo(%reg),%eax // call ___tls_get_addr // to // movl %gs:0,%eax // nop // leal 0(%esi,1),%esi const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax 0x90, // nop 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi }; memcpy(loc - 2, inst, sizeof(inst)); } namespace { class RetpolinePic : public X86 { public: RetpolinePic(); void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; }; class RetpolineNoPic : public X86 { public: RetpolineNoPic(); void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; }; } // namespace RetpolinePic::RetpolinePic() { pltHeaderSize = 48; pltEntrySize = 32; } void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const { write32le(buf, s.getPltVA() + 17); } void RetpolinePic::writePltHeader(uint8_t *buf) const { const uint8_t insn[] = { 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx) 0x50, // 6: pushl %eax 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next 0xf3, 0x90, // 12: loop: pause 0x0f, 0xae, 0xe8, // 14: lfence 0xeb, 0xf9, // 17: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp) 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp) 0x89, 0xc8, // 2b: mov %ecx, %eax 0x59, // 2d: pop %ecx 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; memcpy(buf, insn, sizeof(insn)); } void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t insn[] = { 0x50, // pushl %eax 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax 0xe8, 0, 0, 0, 0, // call plt+0x20 0xe9, 0, 0, 0, 0, // jmp plt+0x12 0x68, 0, 0, 0, 0, // pushl $reloc_offset 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; memcpy(buf, insn, sizeof(insn)); uint32_t ebx = in.gotPlt->getVA(); unsigned off = pltHeaderSize + pltEntrySize * index; write32le(buf + 3, gotPltEntryAddr - ebx); write32le(buf + 8, -off - 12 + 32); write32le(buf + 13, -off - 17 + 18); write32le(buf + 18, relOff); write32le(buf + 23, -off - 27); } RetpolineNoPic::RetpolineNoPic() { pltHeaderSize = 48; pltEntrySize = 32; } void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const { write32le(buf, s.getPltVA() + 16); } void RetpolineNoPic::writePltHeader(uint8_t *buf) const { const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4 0x50, // 6: pushl %eax 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax 0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next 0xf3, 0x90, // 11: loop: pause 0x0f, 0xae, 0xe8, // 13: lfence 0xeb, 0xf9, // 16: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3 0xcc, 0xcc, 0xcc, // 1f: int3; .align 16 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp) 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp) 0x89, 0xc8, // 2b: mov %ecx, %eax 0x59, // 2d: pop %ecx 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; memcpy(buf, insn, sizeof(insn)); uint32_t gotPlt = in.gotPlt->getVA(); write32le(buf + 2, gotPlt + 4); write32le(buf + 8, gotPlt + 8); } void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t insn[] = { 0x50, // 0: pushl %eax 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset 0xe9, 0, 0, 0, 0, // 15: jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; memcpy(buf, insn, sizeof(insn)); unsigned off = pltHeaderSize + pltEntrySize * index; write32le(buf + 2, gotPltEntryAddr); write32le(buf + 7, -off - 11 + 32); write32le(buf + 12, -off - 16 + 17); write32le(buf + 17, relOff); write32le(buf + 22, -off - 26); } -TargetInfo *elf::getX86TargetInfo() { +TargetInfo *getX86TargetInfo() { if (config->zRetpolineplt) { if (config->isPic) { static RetpolinePic t; return &t; } static RetpolineNoPic t; return &t; } static X86 t; return &t; } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Arch/X86_64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/X86_64.cpp (revision 353949) +++ vendor/lld/dist/ELF/Arch/X86_64.cpp (revision 353950) @@ -1,701 +1,705 @@ //===- X86_64.cpp ---------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { class X86_64 : public TargetInfo { public: X86_64(); int getTlsGdRelaxSkip(RelType type) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; void writeGotPltHeader(uint8_t *buf) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const override; void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const override; }; } // namespace X86_64::X86_64() { copyRel = R_X86_64_COPY; gotRel = R_X86_64_GLOB_DAT; noneRel = R_X86_64_NONE; pltRel = R_X86_64_JUMP_SLOT; relativeRel = R_X86_64_RELATIVE; iRelativeRel = R_X86_64_IRELATIVE; symbolicRel = R_X86_64_64; tlsDescRel = R_X86_64_TLSDESC; tlsGotRel = R_X86_64_TPOFF64; tlsModuleIndexRel = R_X86_64_DTPMOD64; tlsOffsetRel = R_X86_64_DTPOFF64; pltEntrySize = 16; pltHeaderSize = 16; trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. defaultImageBase = 0x200000; } int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { if (type == R_X86_64_GOTTPOFF) config->hasStaticTlsModel = true; switch (type) { case R_X86_64_8: case R_X86_64_16: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: return R_ABS; case R_X86_64_DTPOFF32: case R_X86_64_DTPOFF64: return R_DTPREL; case R_X86_64_TPOFF32: return R_TLS; case R_X86_64_TLSDESC_CALL: return R_TLSDESC_CALL; case R_X86_64_TLSLD: return R_TLSLD_PC; case R_X86_64_TLSGD: return R_TLSGD_PC; case R_X86_64_SIZE32: case R_X86_64_SIZE64: return R_SIZE; case R_X86_64_PLT32: return R_PLT_PC; case R_X86_64_PC8: case R_X86_64_PC16: case R_X86_64_PC32: case R_X86_64_PC64: return R_PC; case R_X86_64_GOT32: case R_X86_64_GOT64: return R_GOTPLT; case R_X86_64_GOTPC32_TLSDESC: return R_TLSDESC_PC; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTTPOFF: return R_GOT_PC; case R_X86_64_GOTOFF64: return R_GOTPLTREL; case R_X86_64_GOTPC32: case R_X86_64_GOTPC64: return R_GOTPLTONLY_PC; case R_X86_64_NONE: return R_NONE; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); return R_NONE; } } void X86_64::writeGotPltHeader(uint8_t *buf) const { // The first entry holds the value of _DYNAMIC. It is not clear why that is // required, but it is documented in the psabi and the glibc dynamic linker // seems to use it (note that this is relevant for linking ld.so, not any // other program). write64le(buf, mainPart->dynamic->getVA()); } void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { // See comments in X86::writeGotPlt. write64le(buf, s.getPltVA() + 6); } void X86_64::writePltHeader(uint8_t *buf) const { const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) 0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip) 0x0f, 0x1f, 0x40, 0x00, // nop }; memcpy(buf, pltData, sizeof(pltData)); uint64_t gotPlt = in.gotPlt->getVA(); uint64_t plt = in.plt->getVA(); write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8 write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16 } void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) 0x68, 0, 0, 0, 0, // pushq 0xe9, 0, 0, 0, 0, // jmpq plt[0] }; memcpy(buf, inst, sizeof(inst)); write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6); write32le(buf + 7, index); write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } RelType X86_64::getDynRel(RelType type) const { if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 || type == R_X86_64_SIZE64) return type; return R_X86_64_NONE; } void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { if (type == R_X86_64_TLSGD) { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi // .word 0x6666 // rex64 // call __tls_get_addr@plt // to the following two instructions. const uint8_t inst[] = { 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax }; memcpy(loc - 4, inst, sizeof(inst)); // The original code used a pc relative relocation and so we have to // compensate for the -4 in had in the addend. write32le(loc + 8, val + 4); } else { // Convert // lea x@tlsgd(%rip), %rax // call *(%rax) // to the following two instructions. assert(type == R_X86_64_GOTPC32_TLSDESC); if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " "in callq *x@tlsdesc(%rip), %rax"); return; } // movq $x@tpoff(%rip),%rax loc[-2] = 0xc7; loc[-1] = 0xc0; write32le(loc, val + 4); // xchg ax,ax loc[4] = 0x66; loc[5] = 0x90; } } void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { if (type == R_X86_64_TLSGD) { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi // .word 0x6666 // rex64 // call __tls_get_addr@plt // to the following two instructions. const uint8_t inst[] = { 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax }; memcpy(loc - 4, inst, sizeof(inst)); // Both code sequences are PC relatives, but since we are moving the // constant forward by 8 bytes we have to subtract the value by 8. write32le(loc + 8, val - 8); } else { // Convert // lea x@tlsgd(%rip), %rax // call *(%rax) // to the following two instructions. assert(type == R_X86_64_GOTPC32_TLSDESC); if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " "in callq *x@tlsdesc(%rip), %rax"); return; } // movq x@gottpoff(%rip),%rax loc[-2] = 0x8b; write32le(loc, val); // xchg ax,ax loc[4] = 0x66; loc[5] = 0x90; } } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { uint8_t *inst = loc - 3; uint8_t reg = loc[-1] >> 3; uint8_t *regSlot = loc - 1; // Note that ADD with RSP or R12 is converted to ADD instead of LEA // because LEA with these registers needs 4 bytes to encode and thus // wouldn't fit the space. if (memcmp(inst, "\x48\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp" memcpy(inst, "\x48\x81\xc4", 3); } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12" memcpy(inst, "\x49\x81\xc4", 3); } else if (memcmp(inst, "\x4c\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]" memcpy(inst, "\x4d\x8d", 2); *regSlot = 0x80 | (reg << 3) | reg; } else if (memcmp(inst, "\x48\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg" memcpy(inst, "\x48\x8d", 2); *regSlot = 0x80 | (reg << 3) | reg; } else if (memcmp(inst, "\x4c\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]" memcpy(inst, "\x49\xc7", 2); *regSlot = 0xc0 | reg; } else if (memcmp(inst, "\x48\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg" memcpy(inst, "\x48\xc7", 2); *regSlot = 0xc0 | reg; } else { error(getErrorLocation(loc - 3) + "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only"); } // The original code used a PC relative relocation. // Need to compensate for the -4 it had in the addend. write32le(loc, val + 4); } void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { if (type == R_X86_64_DTPOFF64) { write64le(loc, val); return; } if (type == R_X86_64_DTPOFF32) { write32le(loc, val); return; } const uint8_t inst[] = { 0x66, 0x66, // .word 0x6666 0x66, // .byte 0x66 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax }; if (loc[4] == 0xe8) { // Convert // leaq bar@tlsld(%rip), %rdi # 48 8d 3d // callq __tls_get_addr@PLT # e8 // leaq bar@dtpoff(%rax), %rcx // to // .word 0x6666 // .byte 0x66 // mov %fs:0,%rax // leaq bar@tpoff(%rax), %rcx memcpy(loc - 3, inst, sizeof(inst)); return; } if (loc[4] == 0xff && loc[5] == 0x15) { // Convert // leaq x@tlsld(%rip),%rdi # 48 8d 3d // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 // to // .long 0x66666666 // movq %fs:0,%rax // See "Table 11.9: LD -> LE Code Transition (LP64)" in // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf loc[-3] = 0x66; memcpy(loc - 2, inst, sizeof(inst)); return; } error(getErrorLocation(loc - 3) + "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); } void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_X86_64_8: checkIntUInt(loc, val, 8, type); *loc = val; break; case R_X86_64_PC8: checkInt(loc, val, 8, type); *loc = val; break; case R_X86_64_16: checkIntUInt(loc, val, 16, type); write16le(loc, val); break; case R_X86_64_PC16: checkInt(loc, val, 16, type); write16le(loc, val); break; case R_X86_64_32: checkUInt(loc, val, 32, type); write32le(loc, val); break; case R_X86_64_32S: case R_X86_64_TPOFF32: case R_X86_64_GOT32: case R_X86_64_GOTPC32: case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_PC32: case R_X86_64_GOTTPOFF: case R_X86_64_PLT32: case R_X86_64_TLSGD: case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: checkInt(loc, val, 32, type); write32le(loc, val); break; case R_X86_64_64: case R_X86_64_DTPOFF64: case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: case R_X86_64_GOTOFF64: case R_X86_64_GOTPC64: write64le(loc, val); break; default: llvm_unreachable("unknown relocation"); } } RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr relExpr) const { if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) return relExpr; const uint8_t op = data[-2]; const uint8_t modRm = data[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into // immediate operand. Otherwise, mov must be changed to lea. We support only // latter relaxation at this moment. if (op == 0x8b) return R_RELAX_GOT_PC; // Relax call and jmp. if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) return R_RELAX_GOT_PC; // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. // We also don't relax test/binop instructions without REX byte, // they are 32bit operations and not common to have. assert(type == R_X86_64_REX_GOTPCRELX); return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method // handles such relaxations. Instructions encoding information was taken from: // "Intel 64 and IA-32 Architectures Software Developer's Manual V2" // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, uint8_t modRm) { const uint8_t rex = loc[-3]; // Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg". if (op == 0x85) { // See "TEST-Logical Compare" (4-428 Vol. 2B), // TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension). // ModR/M byte has form XX YYY ZZZ, where // YYY is MODRM.reg(register 2), ZZZ is MODRM.rm(register 1). // XX has different meanings: // 00: The operand's memory address is in reg1. // 01: The operand's memory address is reg1 + a byte-sized displacement. // 10: The operand's memory address is reg1 + a word-sized displacement. // 11: The operand is reg1 itself. // If an instruction requires only one operand, the unused reg2 field // holds extra opcode bits rather than a register code // 0xC0 == 11 000 000 binary. // 0x38 == 00 111 000 binary. // We transfer reg2 to reg1 here as operand. // See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3). loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte. // Change opcode from TEST r/m64, r64 to TEST r/m64, imm32 // See "TEST-Logical Compare" (4-428 Vol. 2B). loc[-2] = 0xf7; // Move R bit to the B bit in REX byte. // REX byte is encoded as 0100WRXB, where // 0100 is 4bit fixed pattern. // REX.W When 1, a 64-bit operand size is used. Otherwise, when 0, the // default operand size is used (which is 32-bit for most but not all // instructions). // REX.R This 1-bit value is an extension to the MODRM.reg field. // REX.X This 1-bit value is an extension to the SIB.index field. // REX.B This 1-bit value is an extension to the MODRM.rm field or the // SIB.base field. // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; write32le(loc, val); return; } // If we are here then we need to relax the adc, add, and, cmp, or, sbb, sub // or xor operations. // Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg". // Logic is close to one for test instruction above, but we also // write opcode extension here, see below for details. loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte. // Primary opcode is 0x81, opcode extension is one of: // 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB, // 100b is AND, 101b is SUB, 110b is XOR, 111b is CMP. // This value was wrote to MODRM.reg in a line above. // See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15), // "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for // descriptions about each operation. loc[-2] = 0x81; loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; write32le(loc, val); } void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { const uint8_t op = loc[-2]; const uint8_t modRm = loc[-1]; // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". if (op == 0x8b) { loc[-2] = 0x8d; write32le(loc, val); return; } if (op != 0xff) { // We are relaxing a rip relative to an absolute, so compensate // for the old -4 addend. assert(!config->isPic); relaxGotNoPic(loc, val + 4, op, modRm); return; } // Convert call/jmp instructions. if (modRm == 0x15) { // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo". // Instead we convert to "addr32 call foo" where addr32 is an instruction // prefix. That makes result expression to be a single instruction. loc[-2] = 0x67; // addr32 prefix loc[-1] = 0xe8; // call write32le(loc, val); return; } // Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop". // jmp doesn't return, so it is fine to use nop here, it is just a stub. assert(modRm == 0x25); loc[-2] = 0xe9; // jmp loc[3] = 0x90; // nop write32le(loc - 1, val + 1); } // A split-stack prologue starts by checking the amount of stack remaining // in one of two ways: // A) Comparing of the stack pointer to a field in the tcb. // B) Or a load of a stack pointer offset with an lea to r10 or r11. bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const { if (!config->is64) { error("Target doesn't support split stacks."); return false; } if (loc + 8 >= end) return false; // Replace "cmp %fs:0x70,%rsp" and subsequent branch // with "stc, nopl 0x0(%rax,%rax,1)" if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) { memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); return true; } // Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could // be r10 or r11. The lea instruction feeds a subsequent compare which checks // if there is X available stack space. Making X larger effectively reserves // that much additional space. The stack grows downward so subtract the value. if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 || memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) { // The offset bytes are encoded four bytes after the start of the // instruction. write32le(loc + 4, read32le(loc + 4) - 0x4000); return true; } return false; } // These nonstandard PLT entries are to migtigate Spectre v2 security // vulnerability. In order to mitigate Spectre v2, we want to avoid indirect // branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT // entries, we use a CALL followed by MOV and RET to do the same thing as an // indirect jump. That instruction sequence is so-called "retpoline". // // We have two types of retpoline PLTs as a size optimization. If `-z now` // is specified, all dynamic symbols are resolved at load-time. Thus, when // that option is given, we can omit code for symbol lazy resolution. namespace { class Retpoline : public X86_64 { public: Retpoline(); void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; }; class RetpolineZNow : public X86_64 { public: RetpolineZNow(); void writeGotPlt(uint8_t *buf, const Symbol &s) const override {} void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const override; }; } // namespace Retpoline::Retpoline() { pltHeaderSize = 48; pltEntrySize = 32; } void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { write64le(buf, s.getPltVA() + 17); } void Retpoline::writePltHeader(uint8_t *buf) const { const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip) 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next 0xf3, 0x90, // 12: loop: pause 0x0f, 0xae, 0xe8, // 14: lfence 0xeb, 0xf9, // 17: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp) 0xc3, // 24: ret 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding }; memcpy(buf, insn, sizeof(insn)); uint64_t gotPlt = in.gotPlt->getVA(); uint64_t plt = in.plt->getVA(); write32le(buf + 2, gotPlt - plt - 6 + 8); write32le(buf + 9, gotPlt - plt - 13 + 16); } void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 0x68, 0, 0, 0, 0, // 11: pushq 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding }; memcpy(buf, insn, sizeof(insn)); uint64_t off = pltHeaderSize + pltEntrySize * index; write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); write32le(buf + 8, -off - 12 + 32); write32le(buf + 13, -off - 17 + 18); write32le(buf + 18, index); write32le(buf + 23, -off - 27); } RetpolineZNow::RetpolineZNow() { pltHeaderSize = 32; pltEntrySize = 16; } void RetpolineZNow::writePltHeader(uint8_t *buf) const { const uint8_t insn[] = { 0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next 0xf3, 0x90, // 5: loop: pause 0x0f, 0xae, 0xe8, // 7: lfence 0xeb, 0xf9, // a: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16 0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp) 0xc3, // 14: ret 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; memcpy(buf, insn, sizeof(insn)); } void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index, unsigned relOff) const { const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; memcpy(buf, insn, sizeof(insn)); write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12); } static TargetInfo *getTargetInfo() { if (config->zRetpolineplt) { if (config->zNow) { static RetpolineZNow t; return &t; } static Retpoline t; return &t; } static X86_64 t; return &t; } -TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } +TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/CMakeLists.txt =================================================================== --- vendor/lld/dist/ELF/CMakeLists.txt (revision 353949) +++ vendor/lld/dist/ELF/CMakeLists.txt (revision 353950) @@ -1,67 +1,68 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(ELFOptionsTableGen) if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() add_lld_library(lldELF AArch64ErrataFix.cpp Arch/AArch64.cpp Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp Arch/Hexagon.cpp Arch/Mips.cpp Arch/MipsArchTree.cpp Arch/MSP430.cpp Arch/PPC.cpp Arch/PPC64.cpp Arch/RISCV.cpp Arch/SPARCV9.cpp Arch/X86.cpp Arch/X86_64.cpp + ARMErrataFix.cpp CallGraphSort.cpp DWARF.cpp Driver.cpp DriverUtils.cpp EhFrame.cpp ICF.cpp InputFiles.cpp InputSection.cpp LTO.cpp LinkerScript.cpp MapFile.cpp MarkLive.cpp OutputSections.cpp Relocations.cpp ScriptLexer.cpp ScriptParser.cpp SymbolTable.cpp Symbols.cpp SyntheticSections.cpp Target.cpp Thunks.cpp Writer.cpp LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat BitWriter Core DebugInfoDWARF LTO MC Object Option Support LINK_LIBS lldCommon ${LLVM_PTHREAD_LIB} DEPENDS ELFOptionsTableGen ${tablegen_deps} ) Index: vendor/lld/dist/ELF/CallGraphSort.cpp =================================================================== --- vendor/lld/dist/ELF/CallGraphSort.cpp (revision 353949) +++ vendor/lld/dist/ELF/CallGraphSort.cpp (revision 353950) @@ -1,259 +1,273 @@ //===- CallGraphSort.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// Implementation of Call-Chain Clustering from: Optimizing Function Placement /// for Large-Scale Data-Center Applications /// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf /// /// The goal of this algorithm is to improve runtime performance of the final /// executable by arranging code sections such that page table and i-cache /// misses are minimized. /// /// Definitions: /// * Cluster /// * An ordered list of input sections which are layed out as a unit. At the /// beginning of the algorithm each input section has its own cluster and /// the weight of the cluster is the sum of the weight of all incomming /// edges. /// * Call-Chain Clustering (C³) Heuristic /// * Defines when and how clusters are combined. Pick the highest weighted /// input section then add it to its most likely predecessor if it wouldn't /// penalize it too much. /// * Density /// * The weight of the cluster divided by the size of the cluster. This is a /// proxy for the ammount of execution time spent per byte of the cluster. /// /// It does so given a call graph profile by the following: /// * Build a weighted call graph from the call graph profile /// * Sort input sections by weight /// * For each input section starting with the highest weight /// * Find its most likely predecessor cluster /// * Check if the combined cluster would be too large, or would have too low /// a density. /// * If not, then combine the clusters. /// * Sort non-empty clusters by density /// //===----------------------------------------------------------------------===// #include "CallGraphSort.h" #include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" +#include + using namespace llvm; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { + namespace { struct Edge { int from; uint64_t weight; }; struct Cluster { - Cluster(int sec, size_t s) : sections{sec}, size(s) {} + Cluster(int sec, size_t s) : next(sec), prev(sec), size(s) {} double getDensity() const { if (size == 0) return 0; return double(weight) / double(size); } - std::vector sections; + int next; + int prev; size_t size = 0; uint64_t weight = 0; uint64_t initialWeight = 0; Edge bestPred = {-1, 0}; }; class CallGraphSort { public: CallGraphSort(); DenseMap run(); private: std::vector clusters; std::vector sections; - - void groupClusters(); }; // Maximum ammount the combined cluster density can be worse than the original // cluster to consider merging. constexpr int MAX_DENSITY_DEGRADATION = 8; // Maximum cluster size in bytes. constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; } // end anonymous namespace using SectionPair = std::pair; // Take the edge list in Config->CallGraphProfile, resolve symbol names to // Symbols, and generate a graph between InputSections with the provided // weights. CallGraphSort::CallGraphSort() { MapVector &profile = config->callGraphProfile; DenseMap secToCluster; auto getOrCreateNode = [&](const InputSectionBase *isec) -> int { - auto res = secToCluster.insert(std::make_pair(isec, clusters.size())); + auto res = secToCluster.try_emplace(isec, clusters.size()); if (res.second) { sections.push_back(isec); clusters.emplace_back(clusters.size(), isec->getSize()); } return res.first->second; }; // Create the graph. for (std::pair &c : profile) { const auto *fromSB = cast(c.first.first->repl); const auto *toSB = cast(c.first.second->repl); uint64_t weight = c.second; // Ignore edges between input sections belonging to different output // sections. This is done because otherwise we would end up with clusters // containing input sections that can't actually be placed adjacently in the // output. This messes with the cluster size and density calculations. We // would also end up moving input sections in other output sections without // moving them closer to what calls them. if (fromSB->getOutputSection() != toSB->getOutputSection()) continue; int from = getOrCreateNode(fromSB); int to = getOrCreateNode(toSB); clusters[to].weight += weight; if (from == to) continue; // Remember the best edge. Cluster &toC = clusters[to]; if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) { toC.bestPred.from = from; toC.bestPred.weight = weight; } } for (Cluster &c : clusters) c.initialWeight = c.weight; } // It's bad to merge clusters which would degrade the density too much. static bool isNewDensityBad(Cluster &a, Cluster &b) { double newDensity = double(a.weight + b.weight) / double(a.size + b.size); return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION; } -static void mergeClusters(Cluster &into, Cluster &from) { - into.sections.insert(into.sections.end(), from.sections.begin(), - from.sections.end()); +// Find the leader of V's belonged cluster (represented as an equivalence +// class). We apply union-find path-halving technique (simple to implement) in +// the meantime as it decreases depths and the time complexity. +static int getLeader(std::vector &leaders, int v) { + while (leaders[v] != v) { + leaders[v] = leaders[leaders[v]]; + v = leaders[v]; + } + return v; +} + +static void mergeClusters(std::vector &cs, Cluster &into, int intoIdx, + Cluster &from, int fromIdx) { + int tail1 = into.prev, tail2 = from.prev; + into.prev = tail2; + cs[tail2].next = intoIdx; + from.prev = tail1; + cs[tail1].next = fromIdx; into.size += from.size; into.weight += from.weight; - from.sections.clear(); from.size = 0; from.weight = 0; } // Group InputSections into clusters using the Call-Chain Clustering heuristic // then sort the clusters by density. -void CallGraphSort::groupClusters() { - std::vector sortedSecs(clusters.size()); - std::vector secToCluster(clusters.size()); +DenseMap CallGraphSort::run() { + std::vector sorted(clusters.size()); + std::vector leaders(clusters.size()); - for (size_t i = 0; i < clusters.size(); ++i) { - sortedSecs[i] = i; - secToCluster[i] = &clusters[i]; - } - - llvm::stable_sort(sortedSecs, [&](int a, int b) { + std::iota(leaders.begin(), leaders.end(), 0); + std::iota(sorted.begin(), sorted.end(), 0); + llvm::stable_sort(sorted, [&](int a, int b) { return clusters[a].getDensity() > clusters[b].getDensity(); }); - for (int si : sortedSecs) { - // clusters[si] is the same as secToClusters[si] here because it has not - // been merged into another cluster yet. - Cluster &c = clusters[si]; + for (int l : sorted) { + // The cluster index is the same as the index of its leader here because + // clusters[L] has not been merged into another cluster yet. + Cluster &c = clusters[l]; // Don't consider merging if the edge is unlikely. if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) continue; - Cluster *predC = secToCluster[c.bestPred.from]; - if (predC == &c) + int predL = getLeader(leaders, c.bestPred.from); + if (l == predL) continue; + Cluster *predC = &clusters[predL]; if (c.size + predC->size > MAX_CLUSTER_SIZE) continue; if (isNewDensityBad(*predC, c)) continue; - // NOTE: Consider using a disjoint-set to track section -> cluster mapping - // if this is ever slow. - for (int si : c.sections) - secToCluster[si] = predC; - - mergeClusters(*predC, c); + leaders[l] = predL; + mergeClusters(clusters, *predC, predL, c, l); } - // Remove empty or dead nodes. Invalidates all cluster indices. - llvm::erase_if(clusters, [](const Cluster &c) { - return c.size == 0 || c.sections.empty(); + // Sort remaining non-empty clusters by density. + sorted.clear(); + for (int i = 0, e = (int)clusters.size(); i != e; ++i) + if (clusters[i].size > 0) + sorted.push_back(i); + llvm::stable_sort(sorted, [&](int a, int b) { + return clusters[a].getDensity() > clusters[b].getDensity(); }); - // Sort by density. - llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) { - return a.getDensity() > b.getDensity(); - }); -} - -DenseMap CallGraphSort::run() { - groupClusters(); - - // Generate order. DenseMap orderMap; - ssize_t curOrder = 1; + int curOrder = 1; + for (int leader : sorted) + for (int i = leader;;) { + orderMap[sections[i]] = curOrder++; + i = clusters[i].next; + if (i == leader) + break; + } - for (const Cluster &c : clusters) - for (int secIndex : c.sections) - orderMap[sections[secIndex]] = curOrder++; - if (!config->printSymbolOrder.empty()) { std::error_code ec; - raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None); + raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None); if (ec) { error("cannot open " + config->printSymbolOrder + ": " + ec.message()); return orderMap; } // Print the symbols ordered by C3, in the order of increasing curOrder // Instead of sorting all the orderMap, just repeat the loops above. - for (const Cluster &c : clusters) - for (int secIndex : c.sections) + for (int leader : sorted) + for (int i = leader;;) { // Search all the symbols in the file of the section // and find out a Defined symbol with name that is within the section. - for (Symbol *sym: sections[secIndex]->file->getSymbols()) + for (Symbol *sym : sections[i]->file->getSymbols()) if (!sym->isSection()) // Filter out section-type symbols here. if (auto *d = dyn_cast(sym)) - if (sections[secIndex] == d->section) + if (sections[i] == d->section) os << sym->getName() << "\n"; + i = clusters[i].next; + if (i == leader) + break; + } } return orderMap; } // Sort sections by the profile data provided by -callgraph-profile-file // // This first builds a call graph based on the profile data then merges sections // according to the C³ huristic. All clusters are then sorted by a density // metric to further improve locality. -DenseMap elf::computeCallGraphProfileOrder() { +DenseMap computeCallGraphProfileOrder() { return CallGraphSort().run(); } + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/Config.h =================================================================== --- vendor/lld/dist/ELF/Config.h (revision 353949) +++ vendor/lld/dist/ELF/Config.h (revision 353950) @@ -1,321 +1,329 @@ //===- Config.h -------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_ELF_CONFIG_H #define LLD_ELF_CONFIG_H #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" #include #include namespace lld { namespace elf { class InputFile; class InputSectionBase; enum ELFKind { ELFNoneKind, ELF32LEKind, ELF32BEKind, ELF64LEKind, ELF64BEKind }; // For --build-id. enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; // For --icf={none,safe,all}. enum class ICFLevel { None, Safe, All }; // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; // For --unresolved-symbols. enum class UnresolvedPolicy { ReportError, Warn, Ignore }; // For --orphan-handling. enum class OrphanHandlingPolicy { Place, Warn, Error }; // For --sort-section and linkerscript sorting rules. enum class SortSectionPolicy { Default, None, Alignment, Name, Priority }; // For --target2 enum class Target2Policy { Abs, Rel, GotRel }; // For tracking ARM Float Argument PCS enum class ARMVFPArgKind { Default, Base, VFP, ToolChain }; +// For -z noseparate-code, -z separate-code and -z separate-loadable-segments. +enum class SeparateSegmentKind { None, Code, Loadable }; + struct SymbolVersion { llvm::StringRef name; bool isExternCpp; bool hasWildcard; }; // This struct contains symbols version definition that // can be found in version script if it is used for link. struct VersionDefinition { llvm::StringRef name; - uint16_t id = 0; - std::vector globals; + uint16_t id; + std::vector patterns; }; // This struct contains the global configuration for the linker. // Most fields are direct mapping from the command line options // and such fields have the same name as the corresponding options. // Most fields are initialized by the driver. struct Configuration { uint8_t osabi = 0; uint32_t andFeatures = 0; llvm::CachePruningPolicy thinLTOCachePolicy; llvm::StringMap sectionStartMap; llvm::StringRef chroot; llvm::StringRef dynamicLinker; llvm::StringRef dwoDir; llvm::StringRef entry; llvm::StringRef emulation; llvm::StringRef fini; llvm::StringRef init; llvm::StringRef ltoAAPipeline; llvm::StringRef ltoCSProfileFile; llvm::StringRef ltoNewPmPasses; llvm::StringRef ltoObjPath; llvm::StringRef ltoSampleProfile; llvm::StringRef mapFile; llvm::StringRef outputFile; llvm::StringRef optRemarksFilename; llvm::StringRef optRemarksPasses; llvm::StringRef optRemarksFormat; llvm::StringRef progName; llvm::StringRef printSymbolOrder; llvm::StringRef soName; llvm::StringRef sysroot; llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; std::pair thinLTOObjectSuffixReplace; std::pair thinLTOPrefixReplace; std::string rpath; std::vector versionDefinitions; std::vector auxiliaryList; std::vector filterList; std::vector searchPaths; std::vector symbolOrderingFile; std::vector undefined; std::vector dynamicList; - std::vector versionScriptGlobals; - std::vector versionScriptLocals; std::vector buildIdVector; llvm::MapVector, uint64_t> callGraphProfile; bool allowMultipleDefinition; bool allowShlibUndefined; bool androidPackDynRelocs; bool armHasBlx = false; bool armHasMovtMovw = false; bool armJ1J2BranchEncoding = false; bool asNeeded = false; bool bsymbolic; bool bsymbolicFunctions; bool callGraphProfileSort; bool checkSections; bool compressDebugSections; bool cref; bool defineCommon; bool demangle = true; bool dependentLibraries; bool disableVerify; bool ehFrameHdr; bool emitLLVM; bool emitRelocs; bool enableNewDtags; bool executeOnly; bool exportDynamic; bool fixCortexA53Errata843419; + bool fixCortexA8; bool forceBTI; bool formatBinary = false; bool requireCET; bool gcSections; bool gdbIndex; bool gnuHash = false; bool gnuUnique; bool hasDynamicList = false; bool hasDynSymTab; bool ignoreDataAddressEquality; bool ignoreFunctionAddressEquality; bool ltoCSProfileGenerate; bool ltoDebugPassManager; bool ltoNewPassManager; bool mergeArmExidx; bool mipsN32Abi = false; bool nmagic; bool noinhibitExec; bool nostdlib; bool oFormatBinary; bool omagic; bool optRemarksWithHotness; bool pacPlt; bool picThunk; bool pie; bool printGcSections; bool printIcfSections; bool relocatable; bool relrPackDynRelocs; bool saveTemps; bool singleRoRx; bool shared; bool isStatic = false; bool sysvHash = false; bool target1Rel; bool trace; bool thinLTOEmitImportsFiles; bool thinLTOIndexOnly; bool tocOptimize; bool undefinedVersion; bool useAndroidRelrTags = false; bool warnBackrefs; bool warnCommon; bool warnIfuncTextrel; bool warnMissingEntry; bool warnSymbolOrdering; bool writeAddends; bool zCombreloc; bool zCopyreloc; bool zExecstack; bool zGlobal; bool zHazardplt; bool zIfuncNoplt; bool zInitfirst; bool zInterpose; bool zKeepTextSectionPrefix; bool zNodefaultlib; bool zNodelete; bool zNodlopen; bool zNow; bool zOrigin; bool zRelro; bool zRodynamic; bool zText; bool zRetpolineplt; bool zWxneeded; DiscardPolicy discard; ICFLevel icf; OrphanHandlingPolicy orphanHandling; SortSectionPolicy sortSection; StripPolicy strip; UnresolvedPolicy unresolvedSymbols; Target2Policy target2; ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; BuildIdKind buildId = BuildIdKind::None; + SeparateSegmentKind zSeparate; ELFKind ekind = ELFNoneKind; - uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; uint16_t emachine = llvm::ELF::EM_NONE; llvm::Optional imageBase; uint64_t commonPageSize; uint64_t maxPageSize; uint64_t mipsGotSize; uint64_t zStackSize; unsigned ltoPartitions; unsigned ltoo; unsigned optimize; unsigned thinLTOJobs; int32_t splitStackAdjustSize; // The following config options do not directly correspond to any // particualr command line options. // True if we need to pass through relocations in input files to the // output file. Usually false because we consume relocations. bool copyRelocs; // True if the target is ELF64. False if ELF32. bool is64; // True if the target is little-endian. False if big-endian. bool isLE; // endianness::little if isLE is true. endianness::big otherwise. llvm::support::endianness endianness; // True if the target is the little-endian MIPS64. // // The reason why we have this variable only for the MIPS is because // we use this often. Some ELF headers for MIPS64EL are in a // mixed-endian (which is horrible and I'd say that's a serious spec // bug), and we need to know whether we are reading MIPS ELF files or // not in various places. // // (Note that MIPS64EL is not a typo for MIPS64LE. This is the official // name whatever that means. A fun hypothesis is that "EL" is short for // little-endian written in the little-endian order, but I don't know // if that's true.) bool isMips64EL; // True if we need to set the DF_STATIC_TLS flag to an output file, // which works as a hint to the dynamic loader that the file contains // code compiled with the static TLS model. The thread-local variable // compiled with the static TLS model is faster but less flexible, and // it may not be loaded using dlopen(). // // We set this flag to true when we see a relocation for the static TLS // model. Once this becomes true, it will never become false. // // Since the flag is updated by multi-threaded code, we use std::atomic. // (Writing to a variable is not considered thread-safe even if the // variable is boolean and we always set the same value from all threads.) std::atomic hasStaticTlsModel{false}; // Holds set of ELF header flags for the target. uint32_t eflags = 0; // The ELF spec defines two types of relocation table entries, RELA and // REL. RELA is a triplet of (offset, info, addend) while REL is a // tuple of (offset, info). Addends for REL are implicit and read from // the location where the relocations are applied. So, REL is more // compact than RELA but requires a bit of more work to process. // // (From the linker writer's view, this distinction is not necessary. // If the ELF had chosen whichever and sticked with it, it would have // been easier to write code to process relocations, but it's too late // to change the spec.) // // Each ABI defines its relocation type. IsRela is true if target // uses RELA. As far as we know, all 64-bit ABIs are using RELA. A // few 32-bit ABIs are using RELA too. bool isRela; // True if we are creating position-independent code. bool isPic; // 4 for ELF32, 8 for ELF64. int wordsize; }; // The only instance of Configuration struct. extern Configuration *config; + +// The first two elements of versionDefinitions represent VER_NDX_LOCAL and +// VER_NDX_GLOBAL. This helper returns other elements. +static inline ArrayRef namedVersionDefs() { + return llvm::makeArrayRef(config->versionDefinitions).slice(2); +} static inline void errorOrWarn(const Twine &msg) { if (!config->noinhibitExec) error(msg); else warn(msg); } } // namespace elf } // namespace lld #endif Index: vendor/lld/dist/ELF/DWARF.cpp =================================================================== --- vendor/lld/dist/ELF/DWARF.cpp (revision 353949) +++ vendor/lld/dist/ELF/DWARF.cpp (revision 353950) @@ -1,129 +1,133 @@ //===- DWARF.cpp ----------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // The -gdb-index option instructs the linker to emit a .gdb_index section. // The section contains information to make gdb startup faster. // The format of the section is described at // https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html. // //===----------------------------------------------------------------------===// #include "DWARF.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/Memory.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/ELFObjectFile.h" using namespace llvm; using namespace llvm::object; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { for (InputSectionBase *sec : obj->getSections()) { if (!sec) continue; if (LLDDWARFSection *m = StringSwitch(sec->name) .Case(".debug_addr", &addrSection) - .Case(".debug_gnu_pubnames", &gnuPubNamesSection) - .Case(".debug_gnu_pubtypes", &gnuPubTypesSection) + .Case(".debug_gnu_pubnames", &gnuPubnamesSection) + .Case(".debug_gnu_pubtypes", &gnuPubtypesSection) .Case(".debug_info", &infoSection) - .Case(".debug_ranges", &rangeSection) - .Case(".debug_rnglists", &rngListsSection) + .Case(".debug_ranges", &rangesSection) + .Case(".debug_rnglists", &rnglistsSection) + .Case(".debug_str_offsets", &strOffsetsSection) .Case(".debug_line", &lineSection) .Default(nullptr)) { m->Data = toStringRef(sec->data()); m->sec = sec; continue; } if (sec->name == ".debug_abbrev") abbrevSection = toStringRef(sec->data()); else if (sec->name == ".debug_str") strSection = toStringRef(sec->data()); else if (sec->name == ".debug_line_str") - lineStringSection = toStringRef(sec->data()); + lineStrSection = toStringRef(sec->data()); } } namespace { template struct LLDRelocationResolver { // In the ELF ABIs, S sepresents the value of the symbol in the relocation // entry. For Rela, the addend is stored as part of the relocation entry. static uint64_t resolve(object::RelocationRef ref, uint64_t s, uint64_t /* A */) { return s + ref.getRawDataRefImpl().p; } }; template struct LLDRelocationResolver> { // For Rel, the addend A is supplied by the caller. static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s, uint64_t a) { return s + a; } }; } // namespace // Find if there is a relocation at Pos in Sec. The code is a bit // more complicated than usual because we need to pass a section index // to llvm since it has no idea about InputSection. template template Optional LLDDwarfObj::findAux(const InputSectionBase &sec, uint64_t pos, ArrayRef rels) const { auto it = partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; }); if (it == rels.end() || it->r_offset != pos) return None; const RelTy &rel = *it; const ObjFile *file = sec.getFile(); uint32_t symIndex = rel.getSymbol(config->isMips64EL); const typename ELFT::Sym &sym = file->template getELFSyms()[symIndex]; uint32_t secIndex = file->getSectionIndex(sym); // An undefined symbol may be a symbol defined in a discarded section. We // shall still resolve it. This is important for --gdb-index: the end address // offset of an entry in .debug_ranges is relocated. If it is not resolved, // its zero value will terminate the decoding of .debug_ranges prematurely. Symbol &s = file->getRelocTargetSym(rel); uint64_t val = 0; if (auto *dr = dyn_cast(&s)) { val = dr->value; // FIXME: We should be consistent about always adding the file // offset or not. if (dr->section->flags & ELF::SHF_ALLOC) val += cast(dr->section)->getOffsetInFile(); } DataRefImpl d; d.p = getAddend(rel); return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), val, Optional(), 0, LLDRelocationResolver::resolve}; } template Optional LLDDwarfObj::find(const llvm::DWARFSection &s, uint64_t pos) const { auto &sec = static_cast(s); if (sec.sec->areRelocsRela) return findAux(*sec.sec, pos, sec.sec->template relas()); return findAux(*sec.sec, pos, sec.sec->template rels()); } -template class elf::LLDDwarfObj; -template class elf::LLDDwarfObj; -template class elf::LLDDwarfObj; -template class elf::LLDDwarfObj; +template class LLDDwarfObj; +template class LLDDwarfObj; +template class LLDDwarfObj; +template class LLDDwarfObj; + +} // namespace elf +} // namespace lld Index: vendor/lld/dist/ELF/DWARF.h =================================================================== --- vendor/lld/dist/ELF/DWARF.h (revision 353949) +++ vendor/lld/dist/ELF/DWARF.h (revision 353950) @@ -1,92 +1,97 @@ //===- DWARF.h -----------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-------------------------------------------------------------------===// #ifndef LLD_ELF_DWARF_H #define LLD_ELF_DWARF_H #include "InputFiles.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ELF.h" namespace lld { namespace elf { class InputSection; struct LLDDWARFSection final : public llvm::DWARFSection { InputSectionBase *sec = nullptr; }; template class LLDDwarfObj final : public llvm::DWARFObject { public: explicit LLDDwarfObj(ObjFile *obj); void forEachInfoSections( llvm::function_ref f) const override { f(infoSection); } - const llvm::DWARFSection &getRangeSection() const override { - return rangeSection; + const llvm::DWARFSection &getRangesSection() const override { + return rangesSection; } const llvm::DWARFSection &getRnglistsSection() const override { - return rngListsSection; + return rnglistsSection; } + const llvm::DWARFSection &getStrOffsetsSection() const override { + return strOffsetsSection; + } + const llvm::DWARFSection &getLineSection() const override { return lineSection; } const llvm::DWARFSection &getAddrSection() const override { return addrSection; } - const llvm::DWARFSection &getGnuPubNamesSection() const override { - return gnuPubNamesSection; + const llvm::DWARFSection &getGnuPubnamesSection() const override { + return gnuPubnamesSection; } - const llvm::DWARFSection &getGnuPubTypesSection() const override { - return gnuPubTypesSection; + const llvm::DWARFSection &getGnuPubtypesSection() const override { + return gnuPubtypesSection; } StringRef getFileName() const override { return ""; } StringRef getAbbrevSection() const override { return abbrevSection; } - StringRef getStringSection() const override { return strSection; } - StringRef getLineStringSection() const override { return lineStringSection; } + StringRef getStrSection() const override { return strSection; } + StringRef getLineStrSection() const override { return lineStrSection; } bool isLittleEndian() const override { return ELFT::TargetEndianness == llvm::support::little; } llvm::Optional find(const llvm::DWARFSection &sec, uint64_t pos) const override; private: template llvm::Optional findAux(const InputSectionBase &sec, uint64_t pos, ArrayRef rels) const; - LLDDWARFSection gnuPubNamesSection; - LLDDWARFSection gnuPubTypesSection; + LLDDWARFSection gnuPubnamesSection; + LLDDWARFSection gnuPubtypesSection; LLDDWARFSection infoSection; - LLDDWARFSection rangeSection; - LLDDWARFSection rngListsSection; + LLDDWARFSection rangesSection; + LLDDWARFSection rnglistsSection; + LLDDWARFSection strOffsetsSection; LLDDWARFSection lineSection; LLDDWARFSection addrSection; StringRef abbrevSection; StringRef strSection; - StringRef lineStringSection; + StringRef lineStrSection; }; } // namespace elf } // namespace lld #endif Index: vendor/lld/dist/ELF/Driver.cpp =================================================================== --- vendor/lld/dist/ELF/Driver.cpp (revision 353949) +++ vendor/lld/dist/ELF/Driver.cpp (revision 353950) @@ -1,1911 +1,1977 @@ //===- Driver.cpp ---------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // The driver drives the entire linking process. It is responsible for // parsing command line options and doing whatever it is instructed to do. // // One notable thing in the LLD's driver when compared to other linkers is // that the LLD's driver is agnostic on the host operating system. // Other linkers usually have implicit default values (such as a dynamic // linker path or library paths) for each host OS. // // I don't think implicit default values are useful because they are // usually explicitly specified by the compiler driver. They can even // be harmful when you are doing cross-linking. Therefore, in LLD, we // simply trust the compiler driver to pass all required options and // don't try to make effort on our side. // //===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" #include "ICF.h" #include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" #include "MarkLive.h" #include "OutputSections.h" #include "ScriptParser.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Writer.h" #include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "lld/Common/Threads.h" #include "lld/Common/Version.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/LTO/LTO.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::support; -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { -Configuration *elf::config; -LinkerDriver *elf::driver; +Configuration *config; +LinkerDriver *driver; static void setConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef args, bool canExitEarly, - raw_ostream &error) { +bool link(ArrayRef args, bool canExitEarly, raw_ostream &error) { errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; errorHandler().errorOS = &error; errorHandler().exitEarly = canExitEarly; - errorHandler().colorDiagnostics = error.has_colors(); + enableColors(error.has_colors()); inputSections.clear(); outputSections.clear(); binaryFiles.clear(); bitcodeFiles.clear(); objectFiles.clear(); sharedFiles.clear(); config = make(); driver = make(); script = make(); symtab = make(); tar = nullptr; memset(&in, 0, sizeof(in)); partitions = {Partition()}; SharedFile::vernauxNum = 0; config->progName = args[0]; driver->main(args); // Exit immediately if we don't need to return to the caller. // This saves time because the overhead of calling destructors // for all globally-allocated objects is not negligible. if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); return !errorCount(); } // Parses a linker -m option. static std::tuple parseEmulation(StringRef emul) { uint8_t osabi = 0; StringRef s = emul; if (s.endswith("_fbsd")) { s = s.drop_back(5); osabi = ELFOSABI_FREEBSD; } std::pair ret = StringSwitch>(s) .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec", {ELF64LEKind, EM_AARCH64}) .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) .Case("elf64lppc", {ELF64LEKind, EM_PPC64}) .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) .Case("elf_i386", {ELF32LEKind, EM_386}) .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Default({ELFNoneKind, EM_NONE}); if (ret.first == ELFNoneKind) error("unknown emulation: " + emul); return std::make_tuple(ret.first, ret.second, osabi); } // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. std::vector> static getArchiveMembers( MemoryBufferRef mb) { std::unique_ptr file = CHECK(Archive::create(mb), mb.getBufferIdentifier() + ": failed to parse archive"); std::vector> v; Error err = Error::success(); bool addToTar = file->isThin() && tar; for (const ErrorOr &cOrErr : file->children(err)) { Archive::Child c = CHECK(cOrErr, mb.getBufferIdentifier() + ": could not get the child of the archive"); MemoryBufferRef mbref = CHECK(c.getMemoryBufferRef(), mb.getBufferIdentifier() + ": could not get the buffer for a child of the archive"); if (addToTar) tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer()); v.push_back(std::make_pair(mbref, c.getChildOffset())); } if (err) fatal(mb.getBufferIdentifier() + ": Archive::children failed: " + toString(std::move(err))); // Take ownership of memory buffers created for members of thin archives. for (std::unique_ptr &mb : file->takeThinBuffers()) make>(std::move(mb)); return v; } // Opens a file and create a file object. Path has to be resolved already. void LinkerDriver::addFile(StringRef path, bool withLOption) { using namespace sys::fs; Optional buffer = readFile(path); if (!buffer.hasValue()) return; MemoryBufferRef mbref = *buffer; if (config->formatBinary) { files.push_back(make(mbref)); return; } switch (identify_magic(mbref.getBuffer())) { case file_magic::unknown: readLinkerScript(mbref); return; case file_magic::archive: { // Handle -whole-archive. if (inWholeArchive) { for (const auto &p : getArchiveMembers(mbref)) files.push_back(createObjectFile(p.first, path, p.second)); return; } std::unique_ptr file = CHECK(Archive::create(mbref), path + ": failed to parse archive"); // If an archive file has no symbol table, it is likely that a user // is attempting LTO and using a default ar command that doesn't // understand the LLVM bitcode file. It is a pretty common error, so // we'll handle it as if it had a symbol table. if (!file->isEmpty() && !file->hasSymbolTable()) { // Check if all members are bitcode files. If not, ignore, which is the // default action without the LTO hack described above. for (const std::pair &p : getArchiveMembers(mbref)) if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) { error(path + ": archive has no index; run ranlib to add one"); return; } for (const std::pair &p : getArchiveMembers(mbref)) files.push_back(make(p.first, path, p.second)); return; } // Handle the regular case. files.push_back(make(std::move(file))); return; } case file_magic::elf_shared_object: if (config->isStatic || config->relocatable) { error("attempted static link of dynamic object " + path); return; } // DSOs usually have DT_SONAME tags in their ELF headers, and the // sonames are used to identify DSOs. But if they are missing, // they are identified by filenames. We don't know whether the new // file has a DT_SONAME or not because we haven't parsed it yet. // Here, we set the default soname for the file because we might // need it later. // // If a file was specified by -lfoo, the directory part is not // significant, as a user did not specify it. This behavior is // compatible with GNU. files.push_back( make(mbref, withLOption ? path::filename(path) : path)); return; case file_magic::bitcode: case file_magic::elf_relocatable: if (inLib) files.push_back(make(mbref, "", 0)); else files.push_back(createObjectFile(mbref)); break; default: error(path + ": unknown file type"); } } // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef name) { if (Optional path = searchLibrary(name)) addFile(*path, /*withLOption=*/true); else error("unable to find library -l" + name); } // This function is called on startup. We need this for LTO since // LTO calls LLVM functions to compile bitcode files to native code. // Technically this can be delayed until we read bitcode files, but // we don't bother to do lazily because the initialization is fast. static void initLLVM() { InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); } // Some command line options or some combinations of them are not allowed. // This function checks for such errors. static void checkOptions() { // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. if (config->emachine == EM_MIPS && config->gnuHash) error("the .gnu.hash section is not compatible with the MIPS target"); if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); + if (config->fixCortexA8 && config->emachine != EM_ARM) + error("--fix-cortex-a8 is only supported on ARM targets"); + if (config->tocOptimize && config->emachine != EM_PPC64) error("--toc-optimize is only supported on the PowerPC64 target"); if (config->pie && config->shared) error("-shared and -pie may not be used together"); if (!config->shared && !config->filterList.empty()) error("-F may not be used without -shared"); if (!config->shared && !config->auxiliaryList.empty()) error("-f may not be used without -shared"); if (!config->relocatable && !config->defineCommon) error("-no-define-common not supported in non relocatable output"); + if (config->strip == StripPolicy::All && config->emitRelocs) + error("--strip-all and --emit-relocs may not be used together"); + if (config->zText && config->zIfuncNoplt) error("-z text and -z ifunc-noplt may not be used together"); if (config->relocatable) { if (config->shared) error("-r and -shared may not be used together"); if (config->gcSections) error("-r and --gc-sections may not be used together"); if (config->gdbIndex) error("-r and --gdb-index may not be used together"); if (config->icf != ICFLevel::None) error("-r and --icf may not be used together"); if (config->pie) error("-r and -pie may not be used together"); + if (config->exportDynamic) + error("-r and --export-dynamic may not be used together"); } if (config->executeOnly) { if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); if (config->singleRoRx && !script->hasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } if (config->zRetpolineplt && config->requireCET) error("--require-cet may not be used with -z retpolineplt"); if (config->emachine != EM_AARCH64) { if (config->pacPlt) error("--pac-plt only supported on AArch64"); if (config->forceBTI) error("--force-bti only supported on AArch64"); } } static const char *getReproduceOption(opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_reproduce)) return arg->getValue(); return getenv("LLD_REPRODUCE"); } static bool hasZOption(opt::InputArgList &args, StringRef key) { for (auto *arg : args.filtered(OPT_z)) if (key == arg->getValue()) return true; return false; } static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2, bool Default) { for (auto *arg : args.filtered_reverse(OPT_z)) { if (k1 == arg->getValue()) return true; if (k2 == arg->getValue()) return false; } return Default; } +static SeparateSegmentKind getZSeparate(opt::InputArgList &args) { + for (auto *arg : args.filtered_reverse(OPT_z)) { + StringRef v = arg->getValue(); + if (v == "noseparate-code") + return SeparateSegmentKind::None; + if (v == "separate-code") + return SeparateSegmentKind::Code; + if (v == "separate-loadable-segments") + return SeparateSegmentKind::Loadable; + } + return SeparateSegmentKind::None; +} + static bool isKnownZFlag(StringRef s) { return s == "combreloc" || s == "copyreloc" || s == "defs" || s == "execstack" || s == "global" || s == "hazardplt" || s == "ifunc-noplt" || s == "initfirst" || s == "interpose" || s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || + s == "separate-code" || s == "separate-loadable-segments" || s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" || s == "noexecstack" || - s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" || - s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || - s == "rodynamic" || s == "text" || s == "wxneeded" || - s.startswith("common-page-size") || s.startswith("max-page-size=") || + s == "nokeep-text-section-prefix" || s == "norelro" || + s == "noseparate-code" || s == "notext" || s == "now" || + s == "origin" || s == "relro" || s == "retpolineplt" || + s == "rodynamic" || s == "text" || s == "undefs" || s == "wxneeded" || + s.startswith("common-page-size=") || s.startswith("max-page-size=") || s.startswith("stack-size="); } // Report an error for an unknown -z option. static void checkZOptions(opt::InputArgList &args) { for (auto *arg : args.filtered(OPT_z)) if (!isKnownZFlag(arg->getValue())) error("unknown -z value: " + StringRef(arg->getValue())); } void LinkerDriver::main(ArrayRef argsArr) { ELFOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); // Interpret this flag early because error() depends on them. errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); checkZOptions(args); // Handle -help if (args.hasArg(OPT_help)) { printHelp(); return; } // Handle -v or -version. // // A note about "compatible with GNU linkers" message: this is a hack for // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and // still the newest version in March 2017) or earlier to recognize LLD as // a GNU compatible linker. As long as an output for the -v option // contains "GNU" or "with BFD", they recognize us as GNU-compatible. // // This is somewhat ugly hack, but in reality, we had no choice other // than doing this. Considering the very long release cycle of Libtool, // it is not easy to improve it to recognize LLD as a GNU compatible // linker in a timely manner. Even if we can make it, there are still a // lot of "configure" scripts out there that are generated by old version // of Libtool. We cannot convince every software developer to migrate to // the latest version and re-generate scripts. So we have this hack. if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) message(getLLDVersion() + " (compatible with GNU linkers)"); if (const char *path = getReproduceOption(args)) { // Note that --reproduce is a debug option so you can ignore it // if you are trying to understand the whole picture of the code. Expected> errOrWriter = TarWriter::create(path, path::stem(path)); if (errOrWriter) { tar = std::move(*errOrWriter); tar->append("response.txt", createResponseFile(args)); tar->append("version.txt", getLLDVersion() + "\n"); } else { error("--reproduce: " + toString(errOrWriter.takeError())); } } readConfigs(args); // The behavior of -v or --version is a bit strange, but this is // needed for compatibility with GNU linkers. if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT)) return; if (args.hasArg(OPT_version)) return; initLLVM(); createFiles(args); if (errorCount()) return; inferMachineType(); setConfigs(args); checkOptions(); if (errorCount()) return; // The Target instance handles target-specific stuff, such as applying // relocations or writing a PLT section. It also contains target-dependent // values such as a default image base address. target = getTarget(); switch (config->ekind) { case ELF32LEKind: link(args); return; case ELF32BEKind: link(args); return; case ELF64LEKind: link(args); return; case ELF64BEKind: link(args); return; default: llvm_unreachable("unknown Config->EKind"); } } static std::string getRpath(opt::InputArgList &args) { std::vector v = args::getStrings(args, OPT_rpath); return llvm::join(v.begin(), v.end(), ":"); } // Determines what we should do if there are remaining unresolved // symbols after the name resolution. static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError : UnresolvedPolicy::Warn; // Process the last of -unresolved-symbols, -no-undefined or -z defs. for (auto *arg : llvm::reverse(args)) { switch (arg->getOption().getID()) { case OPT_unresolved_symbols: { StringRef s = arg->getValue(); if (s == "ignore-all" || s == "ignore-in-object-files") return UnresolvedPolicy::Ignore; if (s == "ignore-in-shared-libs" || s == "report-all") return errorOrWarn; error("unknown --unresolved-symbols value: " + s); continue; } case OPT_no_undefined: return errorOrWarn; case OPT_z: if (StringRef(arg->getValue()) == "defs") return errorOrWarn; + if (StringRef(arg->getValue()) == "undefs") + return UnresolvedPolicy::Ignore; continue; } } // -shared implies -unresolved-symbols=ignore-all because missing // symbols are likely to be resolved at runtime using other DSOs. if (config->shared) return UnresolvedPolicy::Ignore; return errorOrWarn; } static Target2Policy getTarget2(opt::InputArgList &args) { StringRef s = args.getLastArgValue(OPT_target2, "got-rel"); if (s == "rel") return Target2Policy::Rel; if (s == "abs") return Target2Policy::Abs; if (s == "got-rel") return Target2Policy::GotRel; error("unknown --target2 option: " + s); return Target2Policy::GotRel; } static bool isOutputFormatBinary(opt::InputArgList &args) { StringRef s = args.getLastArgValue(OPT_oformat, "elf"); if (s == "binary") return true; if (!s.startswith("elf")) error("unknown --oformat value: " + s); return false; } static DiscardPolicy getDiscard(opt::InputArgList &args) { if (args.hasArg(OPT_relocatable)) return DiscardPolicy::None; auto *arg = args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); if (!arg) return DiscardPolicy::Default; if (arg->getOption().getID() == OPT_discard_all) return DiscardPolicy::All; if (arg->getOption().getID() == OPT_discard_locals) return DiscardPolicy::Locals; return DiscardPolicy::None; } static StringRef getDynamicLinker(opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker) return ""; return arg->getValue(); } static ICFLevel getICF(opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); if (!arg || arg->getOption().getID() == OPT_icf_none) return ICFLevel::None; if (arg->getOption().getID() == OPT_icf_safe) return ICFLevel::Safe; return ICFLevel::All; } static StripPolicy getStrip(opt::InputArgList &args) { if (args.hasArg(OPT_relocatable)) return StripPolicy::None; auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug); if (!arg) return StripPolicy::None; if (arg->getOption().getID() == OPT_strip_all) return StripPolicy::All; return StripPolicy::Debug; } static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args, const opt::Arg &arg) { uint64_t va = 0; if (s.startswith("0x")) s = s.drop_front(2); if (!to_integer(s, va, 16)) error("invalid argument: " + arg.getAsString(args)); return va; } static StringMap getSectionStartMap(opt::InputArgList &args) { StringMap ret; for (auto *arg : args.filtered(OPT_section_start)) { StringRef name; StringRef addr; std::tie(name, addr) = StringRef(arg->getValue()).split('='); ret[name] = parseSectionAddress(addr, args, *arg); } if (auto *arg = args.getLastArg(OPT_Ttext)) ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg); if (auto *arg = args.getLastArg(OPT_Tdata)) ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg); if (auto *arg = args.getLastArg(OPT_Tbss)) ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg); return ret; } static SortSectionPolicy getSortSection(opt::InputArgList &args) { StringRef s = args.getLastArgValue(OPT_sort_section); if (s == "alignment") return SortSectionPolicy::Alignment; if (s == "name") return SortSectionPolicy::Name; if (!s.empty()) error("unknown --sort-section rule: " + s); return SortSectionPolicy::Default; } static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) { StringRef s = args.getLastArgValue(OPT_orphan_handling, "place"); if (s == "warn") return OrphanHandlingPolicy::Warn; if (s == "error") return OrphanHandlingPolicy::Error; if (s != "place") error("unknown --orphan-handling mode: " + s); return OrphanHandlingPolicy::Place; } // Parse --build-id or --build-id=