From: robert Date: Sat, 11 Nov 2023 18:21:06 +0000 (+0000) Subject: import of lld from LLVM-16.0.6 X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=dfe94b169149f14cc1aee2cf6dad58a8d9a1860c;p=openbsd import of lld from LLVM-16.0.6 --- diff --git a/gnu/llvm/lld/CMakeLists.txt b/gnu/llvm/lld/CMakeLists.txt index 2e99564f4e3..3d6225646fe 100644 --- a/gnu/llvm/lld/CMakeLists.txt +++ b/gnu/llvm/lld/CMakeLists.txt @@ -1,56 +1,49 @@ -# Check if lld is built as a standalone project. +cmake_minimum_required(VERSION 3.13.4) + +if(NOT DEFINED LLVM_COMMON_CMAKE_UTILS) + set(LLVM_COMMON_CMAKE_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) +endif() +include(${LLVM_COMMON_CMAKE_UTILS}/Modules/CMakePolicy.cmake + NO_POLICY_SCOPE) + +# If we are not building as a part of LLVM, build LLD as an +# standalone project, using LLVM as an external library: if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) project(lld) - cmake_minimum_required(VERSION 3.13.4) - - 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}") + if ("${CMAKE_VERSION}" VERSION_LESS "3.20.0") + message(WARNING + "Your CMake version is ${CMAKE_VERSION}. Starting with LLVM 17.0.0, the " + "minimum version of CMake required to build LLVM will become 3.20.0, and " + "using an older CMake will become an error. Please upgrade your CMake to " + "at least 3.20.0 now to avoid issues in the future!") endif() +endif() - string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}") +# Must go below project(..) +include(GNUInstallDirs) - 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) +if(LLD_BUILT_STANDALONE) + set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to") + set(CMAKE_CXX_STANDARD_REQUIRED YES) + set(CMAKE_CXX_EXTENSIONS NO) - 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") + set(CMAKE_INCLUDE_CURRENT_DIR ON) - file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR) - file(TO_CMAKE_PATH ${LLVM_CMAKE_PATH} LLVM_CMAKE_PATH) + find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_DIR}") + list(APPEND CMAKE_MODULE_PATH "${LLVM_DIR}") - if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") - message(FATAL_ERROR "LLVMConfig.cmake not found") - endif() - include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake") + # Turn into CACHE PATHs for overwriting + set(LLVM_INCLUDE_DIRS ${LLVM_INCLUDE_DIRS} CACHE PATH "Path to llvm/include and any other header dirs needed") + set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}" CACHE PATH "Path to LLVM build tree") + set(LLVM_MAIN_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../llvm" CACHE PATH "Path to LLVM source tree") - list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") + find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_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}) + # They are used as destination of target generators. 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) + set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) include(AddLLVM) include(TableGen) @@ -58,6 +51,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) include(GetErrcMessages) include(CheckAtomic) + set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") + + include_directories(${LLVM_INCLUDE_DIRS}) + link_directories(${LLVM_LIBRARY_DIRS}) + if(LLVM_INCLUDE_TESTS) find_package(Python3 ${LLVM_MINIMUM_PYTHON_VERSION} REQUIRED COMPONENTS Interpreter) @@ -77,11 +75,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(LLVM_UTILS_PROVIDED ON) set(LLD_TEST_DEPS FileCheck not) endif() - set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest) + set(UNITTEST_DIR ${LLVM_THIRD_PARTY_DIR}/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) + add_subdirectory(${UNITTEST_DIR} third-party/unittest) endif() else() # Seek installed Lit. @@ -114,7 +112,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) if(LLVM_HAVE_LIBXAR) set(XAR_LIB xar) endif() -endif() +endif() # standalone + +set(LLD_TOOLS_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}" CACHE PATH + "Path for binary subdirectory (defaults to '${CMAKE_INSTALL_BINDIR}')") +mark_as_advanced(LLD_TOOLS_INSTALL_DIR) set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include ) @@ -151,7 +153,11 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) "`CMakeFiles'. Please delete them.") endif() -list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules") +# Add path for custom modules. +list(INSERT CMAKE_MODULE_PATH 0 + "${LLD_SOURCE_DIR}/cmake/modules" + "${LLVM_COMMON_CMAKE_UTILS}/Modules" + ) include(AddLLD) @@ -188,19 +194,17 @@ include_directories(BEFORE if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) install(DIRECTORY include/ - DESTINATION include + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h" ) 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) diff --git a/gnu/llvm/lld/COFF/CMakeLists.txt b/gnu/llvm/lld/COFF/CMakeLists.txt index bbcd337b210..acbd2e5fe2a 100644 --- a/gnu/llvm/lld/COFF/CMakeLists.txt +++ b/gnu/llvm/lld/COFF/CMakeLists.txt @@ -5,6 +5,7 @@ add_public_tablegen_target(COFFOptionsTableGen) add_lld_library(lldCOFF CallGraphSort.cpp Chunks.cpp + COFFLinkerContext.cpp DebugTypes.cpp DLL.cpp Driver.cpp @@ -37,11 +38,14 @@ add_lld_library(lldCOFF Option Passes Support + TargetParser + WindowsDriver WindowsManifest LINK_LIBS lldCommon ${LLVM_PTHREAD_LIB} + ${LLVM_ATOMIC_LIB} DEPENDS COFFOptionsTableGen diff --git a/gnu/llvm/lld/COFF/COFFLinkerContext.cpp b/gnu/llvm/lld/COFF/COFFLinkerContext.cpp new file mode 100644 index 00000000000..2aba87d7160 --- /dev/null +++ b/gnu/llvm/lld/COFF/COFFLinkerContext.cpp @@ -0,0 +1,44 @@ +//===- COFFContext.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 +// +//===----------------------------------------------------------------------===// +// +// Description +// +//===----------------------------------------------------------------------===// + +#include "COFFLinkerContext.h" +#include "Symbols.h" +#include "lld/Common/Memory.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" +#include "llvm/Demangle/Demangle.h" + +namespace lld::coff { +COFFLinkerContext::COFFLinkerContext() + : driver(*this), symtab(*this), + ltoTextSection(llvm::COFF::IMAGE_SCN_MEM_EXECUTE), + ltoDataSection(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA), + ltoTextSectionChunk(<oTextSection.section), + ltoDataSectionChunk(<oDataSection.section), + rootTimer("Total Linking Time"), + inputFileTimer("Input File Reading", rootTimer), + ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer), + icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer), + outputCommitTimer("Commit Output File", rootTimer), + totalMapTimer("MAP Emission (Cumulative)", rootTimer), + symbolGatherTimer("Gather Symbols", totalMapTimer), + symbolStringsTimer("Build Symbol Strings", totalMapTimer), + writeTimer("Write to File", totalMapTimer), + totalPdbLinkTimer("PDB Emission (Cumulative)", rootTimer), + addObjectsTimer("Add Objects", totalPdbLinkTimer), + typeMergingTimer("Type Merging", addObjectsTimer), + loadGHashTimer("Global Type Hashing", addObjectsTimer), + mergeGHashTimer("GHash Type Merging", addObjectsTimer), + symbolMergingTimer("Symbol Merging", addObjectsTimer), + publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer), + tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer), + diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {} +} // namespace lld::coff diff --git a/gnu/llvm/lld/COFF/COFFLinkerContext.h b/gnu/llvm/lld/COFF/COFFLinkerContext.h new file mode 100644 index 00000000000..059d4aeddc6 --- /dev/null +++ b/gnu/llvm/lld/COFF/COFFLinkerContext.h @@ -0,0 +1,95 @@ +//===- COFFLinkerContext.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_COFFLINKERCONTEXT_H +#define LLD_COFF_COFFLINKERCONTEXT_H + +#include "Chunks.h" +#include "Config.h" +#include "DebugTypes.h" +#include "Driver.h" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Writer.h" +#include "lld/Common/CommonLinkerContext.h" +#include "lld/Common/Timer.h" + +namespace lld::coff { + +class COFFLinkerContext : public CommonLinkerContext { +public: + COFFLinkerContext(); + COFFLinkerContext(const COFFLinkerContext &) = delete; + COFFLinkerContext &operator=(const COFFLinkerContext &) = delete; + ~COFFLinkerContext() = default; + + LinkerDriver driver; + SymbolTable symtab; + COFFOptTable optTable; + + std::vector objFileInstances; + std::map pdbInputFileInstances; + std::vector importFileInstances; + std::vector bitcodeFileInstances; + + MergeChunk *mergeChunkInstances[Log2MaxSectionAlignment + 1] = {}; + + /// All sources of type information in the program. + std::vector tpiSourceList; + + void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); } + + std::map typeServerSourceMappings; + std::map precompSourceMappings; + + /// List of all output sections. After output sections are finalized, this + /// can be indexed by getOutputSection. + std::vector outputSections; + + OutputSection *getOutputSection(const Chunk *c) const { + return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1]; + } + + // Fake sections for parsing bitcode files. + FakeSection ltoTextSection; + FakeSection ltoDataSection; + FakeSectionChunk ltoTextSectionChunk; + FakeSectionChunk ltoDataSectionChunk; + + // All timers used in the COFF linker. + Timer rootTimer; + Timer inputFileTimer; + Timer ltoTimer; + Timer gcTimer; + Timer icfTimer; + + // Writer timers. + Timer codeLayoutTimer; + Timer outputCommitTimer; + Timer totalMapTimer; + Timer symbolGatherTimer; + Timer symbolStringsTimer; + Timer writeTimer; + + // PDB timers. + Timer totalPdbLinkTimer; + Timer addObjectsTimer; + Timer typeMergingTimer; + Timer loadGHashTimer; + Timer mergeGHashTimer; + Timer symbolMergingTimer; + Timer publicsLayoutTimer; + Timer tpiStreamLayoutTimer; + Timer diskCommitTimer; + + Configuration config; +}; + +} // namespace lld::coff + +#endif diff --git a/gnu/llvm/lld/COFF/CallGraphSort.cpp b/gnu/llvm/lld/COFF/CallGraphSort.cpp index d3e5312ce7f..e83996b035f 100644 --- a/gnu/llvm/lld/COFF/CallGraphSort.cpp +++ b/gnu/llvm/lld/COFF/CallGraphSort.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "CallGraphSort.h" +#include "COFFLinkerContext.h" #include "InputFiles.h" #include "SymbolTable.h" #include "Symbols.h" @@ -48,13 +49,15 @@ struct Cluster { class CallGraphSort { public: - CallGraphSort(); + CallGraphSort(const COFFLinkerContext &ctx); DenseMap run(); private: std::vector clusters; std::vector sections; + + const COFFLinkerContext &ctx; }; // Maximum amount the combined cluster density can be worse than the original @@ -70,8 +73,8 @@ 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; +CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) : ctx(ctx) { + const MapVector &profile = ctx.config.callGraphProfile; DenseMap secToCluster; auto getOrCreateNode = [&](const SectionChunk *isec) -> int { @@ -84,7 +87,7 @@ CallGraphSort::CallGraphSort() { }; // Create the graph. - for (std::pair &c : profile) { + for (const std::pair &c : profile) { const auto *fromSec = cast(c.first.first->repl); const auto *toSec = cast(c.first.second->repl); uint64_t weight = c.second; @@ -95,7 +98,7 @@ CallGraphSort::CallGraphSort() { // 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 (fromSec->getOutputSection() != toSec->getOutputSection()) + if (ctx.getOutputSection(fromSec) != ctx.getOutputSection(toSec)) continue; int from = getOrCreateNode(fromSec); @@ -204,11 +207,11 @@ DenseMap CallGraphSort::run() { break; } } - if (!config->printSymbolOrder.empty()) { + if (!ctx.config.printSymbolOrder.empty()) { std::error_code ec; - raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None); + raw_fd_ostream os(ctx.config.printSymbolOrder, ec, sys::fs::OF_None); if (ec) { - error("cannot open " + config->printSymbolOrder + ": " + ec.message()); + error("cannot open " + ctx.config.printSymbolOrder + ": " + ec.message()); return orderMap; } // Print the symbols ordered by C3, in the order of increasing curOrder @@ -240,6 +243,7 @@ DenseMap CallGraphSort::run() { // This first builds a call graph based on the profile data then merges sections // according to the C³ heuristic. All clusters are then sorted by a density // metric to further improve locality. -DenseMap coff::computeCallGraphProfileOrder() { - return CallGraphSort().run(); +DenseMap +coff::computeCallGraphProfileOrder(const COFFLinkerContext &ctx) { + return CallGraphSort(ctx).run(); } diff --git a/gnu/llvm/lld/COFF/CallGraphSort.h b/gnu/llvm/lld/COFF/CallGraphSort.h index e4f37213744..60f2941d37a 100644 --- a/gnu/llvm/lld/COFF/CallGraphSort.h +++ b/gnu/llvm/lld/COFF/CallGraphSort.h @@ -11,12 +11,12 @@ #include "llvm/ADT/DenseMap.h" -namespace lld { -namespace coff { +namespace lld::coff { class SectionChunk; +class COFFLinkerContext; -llvm::DenseMap computeCallGraphProfileOrder(); -} // namespace coff -} // namespace lld +llvm::DenseMap +computeCallGraphProfileOrder(const COFFLinkerContext &ctx); +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/Chunks.cpp b/gnu/llvm/lld/COFF/Chunks.cpp index 36d5f371326..65de868a5ff 100644 --- a/gnu/llvm/lld/COFF/Chunks.cpp +++ b/gnu/llvm/lld/COFF/Chunks.cpp @@ -7,11 +7,12 @@ //===----------------------------------------------------------------------===// #include "Chunks.h" +#include "COFFLinkerContext.h" #include "InputFiles.h" +#include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" -#include "SymbolTable.h" -#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" @@ -19,6 +20,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include +#include using namespace llvm; using namespace llvm::object; @@ -26,8 +28,7 @@ using namespace llvm::support::endian; using namespace llvm::COFF; using llvm::support::ulittle32_t; -namespace lld { -namespace coff { +namespace lld::coff { SectionChunk::SectionChunk(ObjFile *f, const coff_section *h) : Chunk(SectionKind), file(f), header(h), repl(this) { @@ -52,8 +53,8 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h) // enabled, treat non-comdat sections as roots. Generally optimized object // files will be built with -ffunction-sections or /Gy, so most things worth // stripping will be in a comdat. - if (config) - live = !config->doGC || !isCOMDAT(); + if (file) + live = !file->ctx.config.doGC || !isCOMDAT(); else live = true; } @@ -93,21 +94,31 @@ static void applySecRel(const SectionChunk *sec, uint8_t *off, add32(off, secRel); } -static void applySecIdx(uint8_t *off, OutputSection *os) { +static void applySecIdx(uint8_t *off, OutputSection *os, + unsigned numOutputSections) { + // numOutputSections is the largest valid section index. Make sure that + // it fits in 16 bits. + assert(numOutputSections <= 0xffff && "size of outputSections is too big"); + // Absolute symbol doesn't have section index, but section index relocation // against absolute symbol should be resolved to one plus the last output // section index. This is required for compatibility with MSVC. if (os) add16(off, os->sectionIndex); else - add16(off, DefinedAbsolute::numOutputSections + 1); + add16(off, numOutputSections + 1); } void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { switch (type) { - case IMAGE_REL_AMD64_ADDR32: add32(off, s + config->imageBase); break; - case IMAGE_REL_AMD64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_AMD64_ADDR32: + add32(off, s + imageBase); + break; + case IMAGE_REL_AMD64_ADDR64: + add64(off, s + imageBase); + break; case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break; case IMAGE_REL_AMD64_REL32: add32(off, s - p - 4); break; case IMAGE_REL_AMD64_REL32_1: add32(off, s - p - 5); break; @@ -115,7 +126,9 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, case IMAGE_REL_AMD64_REL32_3: add32(off, s - p - 7); break; case IMAGE_REL_AMD64_REL32_4: add32(off, s - p - 8); break; case IMAGE_REL_AMD64_REL32_5: add32(off, s - p - 9); break; - case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_AMD64_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break; default: error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + @@ -124,13 +137,18 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, } void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { switch (type) { case IMAGE_REL_I386_ABSOLUTE: break; - case IMAGE_REL_I386_DIR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_I386_DIR32: + add32(off, s + imageBase); + break; case IMAGE_REL_I386_DIR32NB: add32(off, s); break; case IMAGE_REL_I386_REL32: add32(off, s - p - 4); break; - case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_I386_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break; default: error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + @@ -187,19 +205,26 @@ void applyBranch24T(uint8_t *off, int32_t v) { } void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { // Pointer to thumb code must have the LSB set. uint64_t sx = s; if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE)) sx |= 1; switch (type) { - case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_ADDR32: + add32(off, sx + imageBase); + break; case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break; - case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_MOV32T: + applyMOV32T(off, sx + imageBase); + break; case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break; case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break; case IMAGE_REL_ARM_BLX23T: applyBranch24T(off, sx - p - 4); break; - case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break; case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break; default: @@ -213,7 +238,8 @@ void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, // the page offset from the current instruction to the target. void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift) { uint32_t orig = read32le(off); - uint64_t imm = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC); + int64_t imm = + SignExtend64<21>(((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC)); s += imm; imm = (s >> shift) - (p >> shift); uint32_t immLo = (imm & 0x3) << 29; @@ -296,7 +322,8 @@ static void applyArm64Branch14(uint8_t *off, int64_t v) { } void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { switch (type) { case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break; case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break; @@ -305,14 +332,20 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(off, s - p); break; case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(off, s - p); break; case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(off, s - p); break; - case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_ADDR32: + add32(off, s + imageBase); + break; case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break; - case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_ADDR64: + add64(off, s + imageBase); + break; case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break; case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, off, os, s); break; case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break; case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, off, os, s); break; - case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM64_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break; default: error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + @@ -322,12 +355,13 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk, Defined *sym, - const coff_relocation &rel) { + const coff_relocation &rel, + bool isMinGW) { // Don't report these errors when the relocation comes from a debug info // section or in mingw mode. MinGW mode object files (built by GCC) can // have leftover sections with relocations against discarded comdat // sections. Such sections are left as is, with relocations untouched. - if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw) + if (fromChunk->isCodeView() || fromChunk->isDWARF() || isMinGW) return; // Get the name of the symbol. If it's null, it was discarded early, so we @@ -385,7 +419,7 @@ void SectionChunk::applyRelocation(uint8_t *off, // section is needed to compute SECREL and SECTION relocations used in debug // info. Chunk *c = sym ? sym->getChunk() : nullptr; - OutputSection *os = c ? c->getOutputSection() : nullptr; + OutputSection *os = c ? file->ctx.getOutputSection(c) : nullptr; // Skip the relocation if it refers to a discarded section, and diagnose it // as an error if appropriate. If a symbol was discarded early, it may be @@ -393,7 +427,7 @@ void SectionChunk::applyRelocation(uint8_t *off, // it was an absolute or synthetic symbol. if (!sym || (!os && !isa(sym) && !isa(sym))) { - maybeReportRelocationToDiscarded(this, sym, rel); + maybeReportRelocationToDiscarded(this, sym, rel, file->ctx.config.mingw); return; } @@ -401,18 +435,19 @@ void SectionChunk::applyRelocation(uint8_t *off, // Compute the RVA of the relocation for relative relocations. uint64_t p = rva + rel.VirtualAddress; - switch (config->machine) { + uint64_t imageBase = file->ctx.config.imageBase; + switch (file->ctx.config.machine) { case AMD64: - applyRelX64(off, rel.Type, os, s, p); + applyRelX64(off, rel.Type, os, s, p, imageBase); break; case I386: - applyRelX86(off, rel.Type, os, s, p); + applyRelX86(off, rel.Type, os, s, p, imageBase); break; case ARMNT: - applyRelARM(off, rel.Type, os, s, p); + applyRelARM(off, rel.Type, os, s, p, imageBase); break; case ARM64: - applyRelARM64(off, rel.Type, os, s, p); + applyRelARM64(off, rel.Type, os, s, p, imageBase); break; default: llvm_unreachable("unknown machine type"); @@ -428,7 +463,7 @@ void SectionChunk::sortRelocations() { return; warn("some relocations in " + file->getName() + " are not sorted"); MutableArrayRef newRelocs( - bAlloc.Allocate(relocsSize), relocsSize); + bAlloc().Allocate(relocsSize), relocsSize); memcpy(newRelocs.data(), relocsData, relocsSize * sizeof(coff_relocation)); llvm::sort(newRelocs, cmpByVa); setRelocs(newRelocs); @@ -477,8 +512,9 @@ void SectionChunk::addAssociative(SectionChunk *child) { child->assocChildren = next; } -static uint8_t getBaserelType(const coff_relocation &rel) { - switch (config->machine) { +static uint8_t getBaserelType(const coff_relocation &rel, + llvm::COFF::MachineTypes machine) { + switch (machine) { case AMD64: if (rel.Type == IMAGE_REL_AMD64_ADDR64) return IMAGE_REL_BASED_DIR64; @@ -510,7 +546,7 @@ static uint8_t getBaserelType(const coff_relocation &rel) { // Only called when base relocation is enabled. void SectionChunk::getBaserels(std::vector *res) { for (const coff_relocation &rel : getRelocs()) { - uint8_t ty = getBaserelType(rel); + uint8_t ty = getBaserelType(rel, file->ctx.config.machine); if (ty == IMAGE_REL_BASED_ABSOLUTE) continue; Symbol *target = file->getSymbol(rel.SymbolTableIndex); @@ -527,7 +563,8 @@ void SectionChunk::getBaserels(std::vector *res) { // another DLL) This returns the size the relocation is supposed to update, // in bits, or 0 if the relocation cannot be handled as a runtime pseudo // relocation. -static int getRuntimePseudoRelocSize(uint16_t type) { +static int getRuntimePseudoRelocSize(uint16_t type, + llvm::COFF::MachineTypes machine) { // Relocations that either contain an absolute address, or a plain // relative offset, since the runtime pseudo reloc implementation // adds 8/16/32/64 bit values to a memory address. @@ -553,7 +590,7 @@ static int getRuntimePseudoRelocSize(uint16_t type) { // the image, or temporarily changed at runtime with VirtualProtect. // Since this only operates on direct address values, it doesn't work for // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations. - switch (config->machine) { + switch (machine) { case AMD64: switch (type) { case IMAGE_REL_AMD64_ADDR64: @@ -610,7 +647,8 @@ void SectionChunk::getRuntimePseudoRelocs( dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); if (!target || !target->isRuntimePseudoReloc) continue; - int sizeInBits = getRuntimePseudoRelocSize(rel.Type); + int sizeInBits = + getRuntimePseudoRelocSize(rel.Type, file->ctx.config.machine); if (sizeInBits == 0) { error("unable to automatically import from " + target->getName() + " with relocation type " + @@ -633,7 +671,7 @@ void SectionChunk::printDiscardedMessage() const { // Removed by dead-stripping. If it's removed by ICF, ICF already // printed out the name, so don't repeat that here. if (sym && this == repl) - message("Discarded " + sym->getName()); + log("Discarded " + sym->getName()); } StringRef SectionChunk::getDebugName() const { @@ -716,7 +754,8 @@ void StringChunk::writeTo(uint8_t *buf) const { buf[str.size()] = '\0'; } -ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) { +ImportThunkChunkX64::ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) { // Intel Optimization Manual says that all branch targets // should be 16-byte aligned. MSVC linker does this too. setAlignment(16); @@ -729,14 +768,13 @@ void ImportThunkChunkX64::writeTo(uint8_t *buf) const { } void ImportThunkChunkX86::getBaserels(std::vector *res) { - res->emplace_back(getRVA() + 2); + res->emplace_back(getRVA() + 2, ctx.config.machine); } void ImportThunkChunkX86::writeTo(uint8_t *buf) const { memcpy(buf, importThunkX86, sizeof(importThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. - write32le(buf + 2, - impSymbol->getRVA() + config->imageBase); + write32le(buf + 2, impSymbol->getRVA() + ctx.config.imageBase); } void ImportThunkChunkARM::getBaserels(std::vector *res) { @@ -746,7 +784,7 @@ void ImportThunkChunkARM::getBaserels(std::vector *res) { void ImportThunkChunkARM::writeTo(uint8_t *buf) const { memcpy(buf, importThunkARM, sizeof(importThunkARM)); // Fix mov.w and mov.t operands. - applyMOV32T(buf, impSymbol->getRVA() + config->imageBase); + applyMOV32T(buf, impSymbol->getRVA() + ctx.config.imageBase); } void ImportThunkChunkARM64::writeTo(uint8_t *buf) const { @@ -764,12 +802,13 @@ const uint8_t armThunk[] = { }; size_t RangeExtensionThunkARM::getSize() const { - assert(config->machine == ARMNT); + assert(ctx.config.machine == ARMNT); + (void)&ctx; return sizeof(armThunk); } void RangeExtensionThunkARM::writeTo(uint8_t *buf) const { - assert(config->machine == ARMNT); + assert(ctx.config.machine == ARMNT); uint64_t offset = target->getRVA() - rva - 12; memcpy(buf, armThunk, sizeof(armThunk)); applyMOV32T(buf, uint32_t(offset)); @@ -784,28 +823,34 @@ const uint8_t arm64Thunk[] = { }; size_t RangeExtensionThunkARM64::getSize() const { - assert(config->machine == ARM64); + assert(ctx.config.machine == ARM64); + (void)&ctx; return sizeof(arm64Thunk); } void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const { - assert(config->machine == ARM64); + assert(ctx.config.machine == ARM64); memcpy(buf, arm64Thunk, sizeof(arm64Thunk)); applyArm64Addr(buf + 0, target->getRVA(), rva, 12); applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0); } +LocalImportChunk::LocalImportChunk(COFFLinkerContext &c, Defined *s) + : sym(s), ctx(c) { + setAlignment(ctx.config.wordsize); +} + void LocalImportChunk::getBaserels(std::vector *res) { - res->emplace_back(getRVA()); + res->emplace_back(getRVA(), ctx.config.machine); } -size_t LocalImportChunk::getSize() const { return config->wordsize; } +size_t LocalImportChunk::getSize() const { return ctx.config.wordsize; } void LocalImportChunk::writeTo(uint8_t *buf) const { - if (config->is64()) { - write64le(buf, sym->getRVA() + config->imageBase); + if (ctx.config.is64()) { + write64le(buf, sym->getRVA() + ctx.config.imageBase); } else { - write32le(buf, sym->getRVA() + config->imageBase); + write32le(buf, sym->getRVA() + ctx.config.imageBase); } } @@ -814,7 +859,7 @@ void RVATableChunk::writeTo(uint8_t *buf) const { size_t cnt = 0; for (const ChunkAndOffset &co : syms) begin[cnt++] = co.inputChunk->getRVA() + co.offset; - std::sort(begin, begin + cnt); + llvm::sort(begin, begin + cnt); assert(std::unique(begin, begin + cnt) == begin + cnt && "RVA tables should be de-duplicated"); } @@ -825,7 +870,7 @@ void RVAFlagTableChunk::writeTo(uint8_t *buf) const { uint8_t flag; }; auto flags = - makeMutableArrayRef(reinterpret_cast(buf), syms.size()); + MutableArrayRef(reinterpret_cast(buf), syms.size()); for (auto t : zip(syms, flags)) { const auto &sym = std::get<0>(t); auto &flag = std::get<1>(t); @@ -925,8 +970,8 @@ void BaserelChunk::writeTo(uint8_t *buf) const { memcpy(buf, data.data(), data.size()); } -uint8_t Baserel::getDefaultType() { - switch (config->machine) { +uint8_t Baserel::getDefaultType(llvm::COFF::MachineTypes machine) { + switch (machine) { case AMD64: case ARM64: return IMAGE_REL_BASED_DIR64; @@ -938,18 +983,16 @@ uint8_t Baserel::getDefaultType() { } } -MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {}; - MergeChunk::MergeChunk(uint32_t alignment) - : builder(StringTableBuilder::RAW, alignment) { + : builder(StringTableBuilder::RAW, llvm::Align(alignment)) { setAlignment(alignment); } -void MergeChunk::addSection(SectionChunk *c) { +void MergeChunk::addSection(COFFLinkerContext &ctx, SectionChunk *c) { assert(isPowerOf2_32(c->getAlignment())); uint8_t p2Align = llvm::Log2_32(c->getAlignment()); - assert(p2Align < array_lengthof(instances)); - auto *&mc = instances[p2Align]; + assert(p2Align < std::size(ctx.mergeChunkInstances)); + auto *&mc = ctx.mergeChunkInstances[p2Align]; if (!mc) mc = make(c->getAlignment()); mc->sections.push_back(c); @@ -986,15 +1029,14 @@ void MergeChunk::writeTo(uint8_t *buf) const { } // MinGW specific. -size_t AbsolutePointerChunk::getSize() const { return config->wordsize; } +size_t AbsolutePointerChunk::getSize() const { return ctx.config.wordsize; } void AbsolutePointerChunk::writeTo(uint8_t *buf) const { - if (config->is64()) { + if (ctx.config.is64()) { write64le(buf, value); } else { write32le(buf, value); } } -} // namespace coff -} // namespace lld +} // namespace lld::coff diff --git a/gnu/llvm/lld/COFF/Chunks.h b/gnu/llvm/lld/COFF/Chunks.h index bdd3faa179a..9d9e73870cc 100644 --- a/gnu/llvm/lld/COFF/Chunks.h +++ b/gnu/llvm/lld/COFF/Chunks.h @@ -21,8 +21,7 @@ #include #include -namespace lld { -namespace coff { +namespace lld::coff { using llvm::COFF::ImportDirectoryTableEntry; using llvm::object::COFFSymbolRef; @@ -101,7 +100,6 @@ public: // chunk has a back pointer to an output section. void setOutputSectionIdx(uint16_t o) { osidx = o; } uint16_t getOutputSectionIdx() const { return osidx; } - OutputSection *getOutputSection() const; // Windows-specific. // Collect all locations that contain absolute addresses for base relocations. @@ -176,6 +174,23 @@ protected: NonSectionChunk(Kind k = OtherKind) : Chunk(k) {} }; +// MinGW specific; information about one individual location in the image +// that needs to be fixed up at runtime after loading. This represents +// one individual element in the PseudoRelocTableChunk table. +class RuntimePseudoReloc { +public: + RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset, + int flags) + : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {} + + Defined *sym; + SectionChunk *target; + uint32_t targetOffset; + // The Flags field contains the size of the relocation, in bits. No other + // flags are currently defined. + int flags; +}; + // A chunk corresponding a section of an input file. class SectionChunk final : public Chunk { // Identical COMDAT Folding feature accesses section internal data. @@ -223,13 +238,13 @@ public: bool isCOMDAT() const; void applyRelocation(uint8_t *off, const coff_relocation &rel) const; void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void getRuntimePseudoRelocs(std::vector &res); @@ -261,7 +276,7 @@ public: } ArrayRef getRelocs() const { - return llvm::makeArrayRef(relocsData, relocsSize); + return llvm::ArrayRef(relocsData, relocsSize); } // Reloc setter used by ARM range extension thunk insertion. @@ -415,7 +430,7 @@ inline StringRef Chunk::getDebugName() const { class MergeChunk : public NonSectionChunk { public: MergeChunk(uint32_t alignment); - static void addSection(SectionChunk *c); + static void addSection(COFFLinkerContext &ctx, SectionChunk *c); void finalizeContents(); void assignSubsectionRVAs(); @@ -424,7 +439,6 @@ public: size_t getSize() const override; void writeTo(uint8_t *buf) const override; - static MergeChunk *instances[Log2MaxSectionAlignment + 1]; std::vector sections; private: @@ -476,24 +490,26 @@ static const uint8_t importThunkARM64[] = { // contents will be a JMP instruction to some __imp_ symbol. class ImportThunkChunk : public NonSectionChunk { public: - ImportThunkChunk(Defined *s) - : NonSectionChunk(ImportThunkKind), impSymbol(s) {} + ImportThunkChunk(COFFLinkerContext &ctx, Defined *s) + : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {} static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; } protected: Defined *impSymbol; + COFFLinkerContext &ctx; }; class ImportThunkChunkX64 : public ImportThunkChunk { public: - explicit ImportThunkChunkX64(Defined *s); + explicit ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s); size_t getSize() const override { return sizeof(importThunkX86); } void writeTo(uint8_t *buf) const override; }; class ImportThunkChunkX86 : public ImportThunkChunk { public: - explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {} + explicit ImportThunkChunkX86(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) {} size_t getSize() const override { return sizeof(importThunkX86); } void getBaserels(std::vector *res) override; void writeTo(uint8_t *buf) const override; @@ -501,7 +517,8 @@ public: class ImportThunkChunkARM : public ImportThunkChunk { public: - explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) { + explicit ImportThunkChunkARM(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) { setAlignment(2); } size_t getSize() const override { return sizeof(importThunkARM); } @@ -511,7 +528,8 @@ public: class ImportThunkChunkARM64 : public ImportThunkChunk { public: - explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) { + explicit ImportThunkChunkARM64(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) { setAlignment(4); } size_t getSize() const override { return sizeof(importThunkARM64); } @@ -520,35 +538,46 @@ public: class RangeExtensionThunkARM : public NonSectionChunk { public: - explicit RangeExtensionThunkARM(Defined *t) : target(t) { setAlignment(2); } + explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t) + : target(t), ctx(ctx) { + setAlignment(2); + } size_t getSize() const override; void writeTo(uint8_t *buf) const override; Defined *target; + +private: + COFFLinkerContext &ctx; }; class RangeExtensionThunkARM64 : public NonSectionChunk { public: - explicit RangeExtensionThunkARM64(Defined *t) : target(t) { setAlignment(4); } + explicit RangeExtensionThunkARM64(COFFLinkerContext &ctx, Defined *t) + : target(t), ctx(ctx) { + setAlignment(4); + } size_t getSize() const override; void writeTo(uint8_t *buf) const override; Defined *target; + +private: + COFFLinkerContext &ctx; }; // Windows-specific. // See comments for DefinedLocalImport class. class LocalImportChunk : public NonSectionChunk { public: - explicit LocalImportChunk(Defined *s) : sym(s) { - setAlignment(config->wordsize); - } + explicit LocalImportChunk(COFFLinkerContext &ctx, Defined *s); size_t getSize() const override; void getBaserels(std::vector *res) override; void writeTo(uint8_t *buf) const override; private: Defined *sym; + COFFLinkerContext &ctx; }; // Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and @@ -615,8 +644,9 @@ private: class Baserel { public: Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {} - explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {} - uint8_t getDefaultType(); + explicit Baserel(uint32_t v, llvm::COFF::MachineTypes machine) + : Baserel(v, getDefaultType(machine)) {} + uint8_t getDefaultType(llvm::COFF::MachineTypes machine); uint32_t rva; uint8_t type; @@ -652,27 +682,11 @@ private: std::vector relocs; }; -// MinGW specific; information about one individual location in the image -// that needs to be fixed up at runtime after loading. This represents -// one individual element in the PseudoRelocTableChunk table. -class RuntimePseudoReloc { -public: - RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset, - int flags) - : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {} - - Defined *sym; - SectionChunk *target; - uint32_t targetOffset; - // The Flags field contains the size of the relocation, in bits. No other - // flags are currently defined. - int flags; -}; - // MinGW specific. A Chunk that contains one pointer-sized absolute value. class AbsolutePointerChunk : public NonSectionChunk { public: - AbsolutePointerChunk(uint64_t value) : value(value) { + AbsolutePointerChunk(COFFLinkerContext &ctx, uint64_t value) + : value(value), ctx(ctx) { setAlignment(getSize()); } size_t getSize() const override; @@ -680,6 +694,7 @@ public: private: uint64_t value; + COFFLinkerContext &ctx; }; // Return true if this file has the hotpatch flag set to true in the S_COMPILE3 @@ -700,8 +715,28 @@ void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift); void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit); void applyArm64Branch26(uint8_t *off, int64_t v); -} // namespace coff -} // namespace lld +// Convenience class for initializing a coff_section with specific flags. +class FakeSection { +public: + FakeSection(int c) { section.Characteristics = c; } + + coff_section section; +}; + +// Convenience class for initializing a SectionChunk with specific flags. +class FakeSectionChunk { +public: + FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) { + // Comdats from LTO files can't be fully treated as regular comdats + // at this point; we don't know what size or contents they are going to + // have, so we can't do proper checking of such aspects of them. + chunk.selection = llvm::COFF::IMAGE_COMDAT_SELECT_ANY; + } + + SectionChunk chunk; +}; + +} // namespace lld::coff namespace llvm { template <> diff --git a/gnu/llvm/lld/COFF/Config.h b/gnu/llvm/lld/COFF/Config.h index df883b779ee..4711573a5b7 100644 --- a/gnu/llvm/lld/COFF/Config.h +++ b/gnu/llvm/lld/COFF/Config.h @@ -10,17 +10,19 @@ #define LLD_COFF_CONFIG_H #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include #include #include -namespace lld { -namespace coff { +namespace lld::coff { using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::WindowsSubsystem; @@ -42,6 +44,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386; struct Export { StringRef name; // N in /export:N or /export:E=N StringRef extName; // E in /export:E=N + StringRef aliasTarget; // GNU specific: N in "alias == N" Symbol *sym = nullptr; uint16_t ordinal = 0; bool noname = false; @@ -62,6 +65,7 @@ struct Export { bool operator==(const Export &e) { return (name == e.name && extName == e.extName && + aliasTarget == e.aliasTarget && ordinal == e.ordinal && noname == e.noname && data == e.data && isPrivate == e.isPrivate); } @@ -91,8 +95,8 @@ enum class ICFLevel { // Global configuration. struct Configuration { - enum ManifestKind { SideBySide, Embed, No }; - bool is64() { return machine == AMD64 || machine == ARM64; } + enum ManifestKind { Default, SideBySide, Embed, No }; + bool is64() const { return machine == AMD64 || machine == ARM64; } llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN; size_t wordsize; @@ -120,9 +124,11 @@ struct Configuration { bool showTiming = false; bool showSummary = false; unsigned debugTypes = static_cast(DebugType::None); + llvm::SmallVector mllvmOpts; std::vector natvisFiles; llvm::StringMap namedStreams; llvm::SmallString<128> pdbAltPath; + int pdbPageSize = 4096; llvm::SmallString<128> pdbPath; llvm::SmallString<128> pdbSourcePath; std::vector argv; @@ -136,6 +142,7 @@ struct Configuration { // True if we are creating a DLL. bool dll = false; StringRef implib; + bool noimplib = false; std::vector exports; bool hadExplicitExports; std::set delayLoads; @@ -166,8 +173,6 @@ struct Configuration { // Used for /opt:lldltocachepolicy=policy llvm::CachePruningPolicy ltoCachePolicy; - // Used for /opt:[no]ltonewpassmanager - bool ltoNewPassManager = false; // Used for /opt:[no]ltodebugpassmanager bool ltoDebugPassManager = false; @@ -178,9 +183,9 @@ struct Configuration { std::map section; // Options for manifest files. - ManifestKind manifest = No; + ManifestKind manifest = Default; int manifestID = 1; - StringRef manifestDependency; + llvm::SetVector manifestDependencies; bool manifestUAC = true; std::vector manifestInput; StringRef manifestLevel = "'asInvoker'"; @@ -205,6 +210,9 @@ struct Configuration { // Used for /map. std::string mapFile; + // Used for /mapinfo. + bool mapInfo = false; + // Used for /thinlto-index-only: llvm::StringRef thinLTOIndexOnlyArg; @@ -223,6 +231,9 @@ struct Configuration { // Used for /lto-cs-profile-path llvm::StringRef ltoCSProfileFile; + // Used for /lto-pgo-warn-mismatch: + bool ltoPGOWarnMismatch = true; + // Used for /call-graph-ordering-file: llvm::MapVector, uint64_t> @@ -232,6 +243,9 @@ struct Configuration { // Used for /print-symbol-order: StringRef printSymbolOrder; + // Used for /vfsoverlay: + std::unique_ptr vfs; + uint64_t align = 4096; uint64_t imageBase = -1; uint64_t fileAlign = 512; @@ -275,11 +289,9 @@ struct Configuration { bool autoImport = false; bool pseudoRelocs = false; bool stdcallFixup = false; + bool writeCheckSum = false; }; -extern Configuration *config; - -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/DLL.cpp b/gnu/llvm/lld/COFF/DLL.cpp index b9e12ef4b34..417b7041fbf 100644 --- a/gnu/llvm/lld/COFF/DLL.cpp +++ b/gnu/llvm/lld/COFF/DLL.cpp @@ -18,8 +18,10 @@ //===----------------------------------------------------------------------===// #include "DLL.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "SymbolTable.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" @@ -29,8 +31,7 @@ using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::COFF; -namespace lld { -namespace coff { +namespace lld::coff { namespace { // Import table @@ -60,19 +61,23 @@ private: // A chunk for the import descriptor table. class LookupChunk : public NonSectionChunk { public: - explicit LookupChunk(Chunk *c) : hintName(c) { - setAlignment(config->wordsize); + explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c) + : hintName(c), ctx(ctx) { + setAlignment(ctx.config.wordsize); } - size_t getSize() const override { return config->wordsize; } + size_t getSize() const override { return ctx.config.wordsize; } void writeTo(uint8_t *buf) const override { - if (config->is64()) + if (ctx.config.is64()) write64le(buf, hintName->getRVA()); else write32le(buf, hintName->getRVA()); } Chunk *hintName; + +private: + COFFLinkerContext &ctx; }; // A chunk for the import descriptor table. @@ -80,15 +85,16 @@ public: // 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); + explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v) + : ordinal(v), ctx(c) { + setAlignment(ctx.config.wordsize); } - size_t getSize() const override { return config->wordsize; } + size_t getSize() const override { return ctx.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()) { + if (ctx.config.is64()) { write64le(buf, (1ULL << 63) | ordinal); } else { write32le(buf, (1ULL << 31) | ordinal); @@ -96,6 +102,9 @@ public: } uint16_t ordinal; + +private: + COFFLinkerContext &ctx; }; // A chunk for the import descriptor table. @@ -134,14 +143,15 @@ private: }; static std::vector> -binImports(const std::vector &imports) { +binImports(COFFLinkerContext &ctx, + const std::vector &imports) { // Group DLL-imported symbols by DLL name because that's how // 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]; + auto less = [&ctx](const std::string &a, const std::string &b) { + return ctx.config.dllOrder[a] < ctx.config.dllOrder[b]; }; - std::map, - bool(*)(const std::string &, const std::string &)> m(less); + std::map, decltype(less)> m( + less); for (DefinedImportData *sym : imports) m[sym->getDLLName().lower()].push_back(sym); @@ -149,10 +159,9 @@ binImports(const std::vector &imports) { 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(); - }); + llvm::sort(syms, [](DefinedImportData *a, DefinedImportData *b) { + return a->getName() < b->getName(); + }); v.push_back(std::move(syms)); } return v; @@ -220,6 +229,19 @@ static const uint8_t tailMergeX64[] = { 0xFF, 0xE0, // jmp rax }; +static const uint8_t tailMergeUnwindInfoX64[] = { + 0x01, // Version=1, Flags=UNW_FLAG_NHANDLER + 0x0a, // Size of prolog + 0x05, // Count of unwind codes + 0x00, // No frame register + 0x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48) + 0x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8) + 0x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8) + 0x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8) + 0x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8) + 0x00, 0x00 // Padding to align on 32-bits +}; + static const uint8_t thunkX86[] = { 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ @@ -323,49 +345,93 @@ public: Defined *helper = nullptr; }; +class TailMergePDataChunkX64 : public NonSectionChunk { +public: + TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) { + // See + // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function + setAlignment(4); + } + + size_t getSize() const override { return 3 * sizeof(uint32_t); } + + void writeTo(uint8_t *buf) const override { + write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA + write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA + write32le(buf + 8, unwind->getRVA()); // UnwindInfo RVA + } + + Chunk *tm = nullptr; + Chunk *unwind = nullptr; +}; + +class TailMergeUnwindInfoX64 : public NonSectionChunk { +public: + TailMergeUnwindInfoX64() { + // See + // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info + setAlignment(4); + } + + size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64)); + } +}; + class ThunkChunkX86 : public NonSectionChunk { public: - ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm) + : imp(i), tailMerge(tm), ctx(ctx) {} 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 + 1, imp->getRVA() + ctx.config.imageBase); write32le(buf + 6, tailMerge->getRVA() - rva - 10); } void getBaserels(std::vector *res) override { - res->emplace_back(rva + 1); + res->emplace_back(rva + 1, ctx.config.machine); } Defined *imp = nullptr; Chunk *tailMerge = nullptr; + +private: + const COFFLinkerContext &ctx; }; class TailMergeChunkX86 : public NonSectionChunk { public: - TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {} + TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h) + : desc(d), helper(h), ctx(ctx) {} 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 + 4, desc->getRVA() + ctx.config.imageBase); write32le(buf + 9, helper->getRVA() - rva - 13); } void getBaserels(std::vector *res) override { - res->emplace_back(rva + 4); + res->emplace_back(rva + 4, ctx.config.machine); } Chunk *desc = nullptr; Defined *helper = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ThunkChunkARM : public NonSectionChunk { public: - ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) { + ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm) + : imp(i), tailMerge(tm), ctx(ctx) { setAlignment(2); } @@ -373,7 +439,7 @@ public: void writeTo(uint8_t *buf) const override { memcpy(buf, thunkARM, sizeof(thunkARM)); - applyMOV32T(buf + 0, imp->getRVA() + config->imageBase); + applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase); applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12); } @@ -383,11 +449,15 @@ public: Defined *imp = nullptr; Chunk *tailMerge = nullptr; + +private: + const COFFLinkerContext &ctx; }; class TailMergeChunkARM : public NonSectionChunk { public: - TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) { + TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h) + : desc(d), helper(h), ctx(ctx) { setAlignment(2); } @@ -395,7 +465,7 @@ public: void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeARM, sizeof(tailMergeARM)); - applyMOV32T(buf + 14, desc->getRVA() + config->imageBase); + applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase); applyBranch24T(buf + 22, helper->getRVA() - rva - 26); } @@ -405,6 +475,9 @@ public: Chunk *desc = nullptr; Defined *helper = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ThunkChunkARM64 : public NonSectionChunk { @@ -448,28 +521,32 @@ public: // A chunk for the import descriptor table. class DelayAddressChunk : public NonSectionChunk { public: - explicit DelayAddressChunk(Chunk *c) : thunk(c) { - setAlignment(config->wordsize); + explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c) + : thunk(c), ctx(ctx) { + setAlignment(ctx.config.wordsize); } - size_t getSize() const override { return config->wordsize; } + size_t getSize() const override { return ctx.config.wordsize; } void writeTo(uint8_t *buf) const override { - if (config->is64()) { - write64le(buf, thunk->getRVA() + config->imageBase); + if (ctx.config.is64()) { + write64le(buf, thunk->getRVA() + ctx.config.imageBase); } else { uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (config->machine == ARMNT) + if (ctx.config.machine == ARMNT) bit = 1; - write32le(buf, (thunk->getRVA() + config->imageBase) | bit); + write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit); } } void getBaserels(std::vector *res) override { - res->emplace_back(rva); + res->emplace_back(rva, ctx.config.machine); } Chunk *thunk; + +private: + const COFFLinkerContext &ctx; }; // Export table @@ -491,8 +568,8 @@ public: auto *e = (export_directory_table_entry *)(buf); e->NameRVA = dllName->getRVA(); - e->OrdinalBase = 0; - e->AddressTableEntries = maxOrdinal + 1; + e->OrdinalBase = 1; + e->AddressTableEntries = maxOrdinal; e->NumberOfNamePointers = nameTabSize; e->ExportAddressTableRVA = addressTab->getRVA(); e->NamePointerRVA = nameTab->getRVA(); @@ -509,17 +586,20 @@ public: class AddressTableChunk : public NonSectionChunk { public: - explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {} + explicit AddressTableChunk(COFFLinkerContext &ctx, size_t maxOrdinal) + : size(maxOrdinal), ctx(ctx) {} 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; + for (const Export &e : ctx.config.exports) { + assert(e.ordinal != 0 && "Export symbol has invalid ordinal"); + // OrdinalBase is 1, so subtract 1 to get the index. + uint8_t *p = buf + (e.ordinal - 1) * 4; uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (config->machine == ARMNT && !e.data) + if (ctx.config.machine == ARMNT && !e.data) bit = 1; if (e.forwardChunk) { write32le(p, e.forwardChunk->getRVA() | bit); @@ -533,6 +613,7 @@ public: private: size_t size; + const COFFLinkerContext &ctx; }; class NamePointersChunk : public NonSectionChunk { @@ -553,26 +634,30 @@ private: class ExportOrdinalChunk : public NonSectionChunk { public: - explicit ExportOrdinalChunk(size_t i) : size(i) {} + explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t i) + : size(i), ctx(ctx) {} size_t getSize() const override { return size * 2; } void writeTo(uint8_t *buf) const override { - for (Export &e : config->exports) { + for (const Export &e : ctx.config.exports) { if (e.noname) continue; - write16le(buf, e.ordinal); + assert(e.ordinal != 0 && "Export symbol has invalid ordinal"); + // This table stores unbiased indices, so subtract 1 (OrdinalBase). + write16le(buf, e.ordinal - 1); buf += 2; } } private: size_t size; + const COFFLinkerContext &ctx; }; } // anonymous namespace -void IdataContents::create() { - std::vector> v = binImports(imports); +void IdataContents::create(COFFLinkerContext &ctx) { + std::vector> v = binImports(ctx, imports); // Create .idata contents for each DLL. for (std::vector &syms : v) { @@ -584,18 +669,18 @@ void IdataContents::create() { for (DefinedImportData *s : syms) { uint16_t ord = s->getOrdinal(); if (s->getExternalName().empty()) { - lookups.push_back(make(ord)); - addresses.push_back(make(ord)); + lookups.push_back(make(ctx, ord)); + addresses.push_back(make(ctx, ord)); continue; } auto *c = make(s->getExternalName(), ord); - lookups.push_back(make(c)); - addresses.push_back(make(c)); + lookups.push_back(make(ctx, c)); + addresses.push_back(make(ctx, c)); hints.push_back(c); } // Terminate with null values. - lookups.push_back(make(config->wordsize)); - addresses.push_back(make(config->wordsize)); + lookups.push_back(make(ctx.config.wordsize)); + addresses.push_back(make(ctx.config.wordsize)); for (int i = 0, e = syms.size(); i < e; ++i) syms[i]->setLocation(addresses[base + i]); @@ -633,7 +718,9 @@ uint64_t DelayLoadContents::getDirSize() { void DelayLoadContents::create(Defined *h) { helper = h; - std::vector> v = binImports(imports); + std::vector> v = binImports(ctx, imports); + + Chunk *unwind = newTailMergeUnwindInfoChunk(); // Create .didat contents for each DLL. for (std::vector &syms : v) { @@ -643,30 +730,33 @@ void DelayLoadContents::create(Defined *h) { size_t base = addresses.size(); Chunk *tm = newTailMergeChunk(dir); + Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr; for (DefinedImportData *s : syms) { Chunk *t = newThunkChunk(s, tm); - auto *a = make(t); + auto *a = make(ctx, t); addresses.push_back(a); thunks.push_back(t); StringRef extName = s->getExternalName(); if (extName.empty()) { - names.push_back(make(s->getOrdinal())); + names.push_back(make(ctx, s->getOrdinal())); } else { auto *c = make(extName, 0); - names.push_back(make(c)); + names.push_back(make(ctx, c)); hintNames.push_back(c); - // Add a syntentic symbol for this load thunk, using the "__imp_load" + // Add a synthetic symbol for this load thunk, using the "__imp___load" // prefix, in case this thunk needs to be added to the list of valid // call targets for Control Flow Guard. - StringRef symName = saver.save("__imp_load_" + extName); + StringRef symName = saver().save("__imp___load_" + extName); s->loadThunkSym = - cast(symtab->addSynthetic(symName, t)); + cast(ctx.symtab.addSynthetic(symName, t)); } } thunks.push_back(tm); + if (pdataChunk) + pdata.push_back(pdataChunk); StringRef tmName = - saver.save("__tailMerge_" + syms[0]->getDLLName().lower()); - symtab->addSynthetic(tmName, tm); + saver().save("__tailMerge_" + syms[0]->getDLLName().lower()); + ctx.symtab.addSynthetic(tmName, tm); // Terminate with null values. addresses.push_back(make(8)); names.push_back(make(8)); @@ -683,18 +773,21 @@ void DelayLoadContents::create(Defined *h) { dir->nameTab = names[base]; dirs.push_back(dir); } + + if (unwind) + unwindinfo.push_back(unwind); // Add null terminator. dirs.push_back(make(sizeof(delay_import_directory_table_entry))); } Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { - switch (config->machine) { + switch (ctx.config.machine) { case AMD64: return make(dir, helper); case I386: - return make(dir, helper); + return make(ctx, dir, helper); case ARMNT: - return make(dir, helper); + return make(ctx, dir, helper); case ARM64: return make(dir, helper); default: @@ -702,15 +795,34 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { } } +Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() { + switch (ctx.config.machine) { + case AMD64: + return make(); + // FIXME: Add support for other architectures. + default: + return nullptr; // Just don't generate unwind info. + } +} +Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) { + switch (ctx.config.machine) { + case AMD64: + return make(tm, unwind); + // FIXME: Add support for other architectures. + default: + return nullptr; // Just don't generate unwind info. + } +} + Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, Chunk *tailMerge) { - switch (config->machine) { + switch (ctx.config.machine) { case AMD64: return make(s, tailMerge); case I386: - return make(s, tailMerge); + return make(ctx, s, tailMerge); case ARMNT: - return make(s, tailMerge); + return make(ctx, s, tailMerge); case ARM64: return make(s, tailMerge); default: @@ -718,20 +830,20 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, } } -EdataContents::EdataContents() { +EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) { uint16_t maxOrdinal = 0; - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) maxOrdinal = std::max(maxOrdinal, e.ordinal); - auto *dllName = make(sys::path::filename(config->outputFile)); - auto *addressTab = make(maxOrdinal); + auto *dllName = make(sys::path::filename(ctx.config.outputFile)); + auto *addressTab = make(ctx, maxOrdinal); std::vector names; - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) if (!e.noname) names.push_back(make(e.exportName)); std::vector forwards; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (e.forwardTo.empty()) continue; e.forwardChunk = make(e.forwardTo); @@ -739,7 +851,7 @@ EdataContents::EdataContents() { } auto *nameTab = make(names); - auto *ordinalTab = make(names.size()); + auto *ordinalTab = make(ctx, names.size()); auto *dir = make(maxOrdinal, names.size(), dllName, addressTab, nameTab, ordinalTab); chunks.push_back(dir); @@ -751,5 +863,4 @@ EdataContents::EdataContents() { chunks.insert(chunks.end(), forwards.begin(), forwards.end()); } -} // namespace coff -} // namespace lld +} // namespace lld::coff diff --git a/gnu/llvm/lld/COFF/DLL.h b/gnu/llvm/lld/COFF/DLL.h index ce0ee01c4a3..7cf71f59d7c 100644 --- a/gnu/llvm/lld/COFF/DLL.h +++ b/gnu/llvm/lld/COFF/DLL.h @@ -12,8 +12,7 @@ #include "Chunks.h" #include "Symbols.h" -namespace lld { -namespace coff { +namespace lld::coff { // Windows-specific. // IdataContents creates all chunks for the DLL import table. @@ -24,7 +23,7 @@ public: void add(DefinedImportData *sym) { imports.push_back(sym); } bool empty() { return imports.empty(); } - void create(); + void create(COFFLinkerContext &ctx); std::vector imports; std::vector dirs; @@ -38,12 +37,15 @@ public: // DelayLoadContents creates all chunks for the delay-load DLL import table. class DelayLoadContents { public: + DelayLoadContents(COFFLinkerContext &ctx) : ctx(ctx) {} void add(DefinedImportData *sym) { imports.push_back(sym); } bool empty() { return imports.empty(); } void create(Defined *helper); std::vector getChunks(); std::vector getDataChunks(); ArrayRef getCodeChunks() { return thunks; } + ArrayRef getCodePData() { return pdata; } + ArrayRef getCodeUnwindInfo() { return unwindinfo; } uint64_t getDirRVA() { return dirs[0]->getRVA(); } uint64_t getDirSize(); @@ -51,6 +53,8 @@ public: private: Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge); Chunk *newTailMergeChunk(Chunk *dir); + Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind); + Chunk *newTailMergeUnwindInfoChunk(); Defined *helper; std::vector imports; @@ -60,23 +64,28 @@ private: std::vector names; std::vector hintNames; std::vector thunks; + std::vector pdata; + std::vector unwindinfo; std::vector dllNames; + + COFFLinkerContext &ctx; }; // Windows-specific. // EdataContents creates all chunks for the DLL export table. class EdataContents { public: - EdataContents(); + EdataContents(COFFLinkerContext &ctx); std::vector chunks; uint64_t getRVA() { return chunks[0]->getRVA(); } uint64_t getSize() { return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA(); } + + COFFLinkerContext &ctx; }; -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/DebugTypes.cpp b/gnu/llvm/lld/COFF/DebugTypes.cpp index 97be5bc79ac..7bbce84b2d5 100644 --- a/gnu/llvm/lld/COFF/DebugTypes.cpp +++ b/gnu/llvm/lld/COFF/DebugTypes.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "DebugTypes.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Driver.h" #include "InputFiles.h" @@ -14,7 +15,6 @@ #include "TypeMerger.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" -#include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" @@ -46,18 +46,22 @@ class TypeServerIpiSource; // before any dependent OBJ. class TypeServerSource : public TpiSource { public: - explicit TypeServerSource(PDBInputFile *f) - : TpiSource(PDB, nullptr), pdbInputFile(f) { - if (f->loadErr && *f->loadErr) + explicit TypeServerSource(COFFLinkerContext &ctx, PDBInputFile *f) + : TpiSource(ctx, PDB, nullptr), pdbInputFile(f) { + if (f->loadErrorStr) return; pdb::PDBFile &file = f->session->getPDBFile(); auto expectedInfo = file.getPDBInfoStream(); if (!expectedInfo) return; Guid = expectedInfo->getGuid(); - auto it = mappings.emplace(Guid, this); - assert(it.second); - (void)it; + auto it = ctx.typeServerSourceMappings.emplace(Guid, this); + if (!it.second) { + // If we hit here we have collision on Guid's in two PDB files. + // This can happen if the PDB Guid is invalid or if we are really + // unlucky. This should fall back on stright file-system lookup. + it.first->second = nullptr; + } } Error mergeDebugT(TypeMerger *m) override; @@ -74,8 +78,6 @@ public: // The PDB signature GUID. codeview::GUID Guid; - - static std::map mappings; }; // Companion to TypeServerSource. Stores the index map for the IPI stream in the @@ -83,7 +85,8 @@ public: // invariant of one type index space per source. class TypeServerIpiSource : public TpiSource { public: - explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {} + explicit TypeServerIpiSource(COFFLinkerContext &ctx) + : TpiSource(ctx, PDBIpi, nullptr) {} friend class TypeServerSource; @@ -101,8 +104,8 @@ class UseTypeServerSource : public TpiSource { Expected getTypeServerSource(); public: - UseTypeServerSource(ObjFile *f, TypeServer2Record ts) - : TpiSource(UsingPDB, f), typeServerDependency(ts) {} + UseTypeServerSource(COFFLinkerContext &ctx, ObjFile *f, TypeServer2Record ts) + : TpiSource(ctx, UsingPDB, f), typeServerDependency(ts) {} Error mergeDebugT(TypeMerger *m) override; @@ -121,29 +124,32 @@ public: // such files, clang does not. class PrecompSource : public TpiSource { public: - PrecompSource(ObjFile *f) : TpiSource(PCH, f) { - if (!f->pchSignature || !*f->pchSignature) - fatal(toString(f) + - " claims to be a PCH object, but does not have a valid signature"); - auto it = mappings.emplace(*f->pchSignature, this); - if (!it.second) - fatal("a PCH object with the same signature has already been provided (" + - toString(it.first->second->file) + " and " + toString(file) + ")"); + PrecompSource(COFFLinkerContext &ctx, ObjFile *f) : TpiSource(ctx, PCH, f) { + // If the S_OBJNAME record contains the PCH signature, we'll register this + // source file right away. + registerMapping(); } + Error mergeDebugT(TypeMerger *m) override; + void loadGHashes() override; bool isDependency() const override { return true; } - static std::map mappings; +private: + void registerMapping(); + + // Whether this precomp OBJ was recorded in the precompSourceMappings map. + // Only happens if the file->pchSignature is valid. + bool registered = false; }; // 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(ObjFile *f, PrecompRecord precomp) - : TpiSource(UsingPCH, f), precompDependency(precomp) {} + UsePrecompSource(COFFLinkerContext &ctx, ObjFile *f, PrecompRecord precomp) + : TpiSource(ctx, UsingPCH, f), precompDependency(precomp) {} Error mergeDebugT(TypeMerger *m) override; @@ -153,6 +159,10 @@ public: private: Error mergeInPrecompHeaderObj(); + PrecompSource *findObjByName(StringRef fileNameOnly); + PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr); + Expected findPrecompMap(ObjFile *file, PrecompRecord &pr); + public: // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. @@ -160,13 +170,9 @@ public: }; } // namespace -std::vector TpiSource::instances; -ArrayRef TpiSource::dependencySources; -ArrayRef TpiSource::objectSources; - -TpiSource::TpiSource(TpiKind k, ObjFile *f) - : kind(k), tpiSrcIdx(instances.size()), file(f) { - instances.push_back(this); +TpiSource::TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f) + : ctx(ctx), kind(k), tpiSrcIdx(ctx.tpiSourceList.size()), file(f) { + ctx.addTpiSource(this); } // Vtable key method. @@ -175,52 +181,35 @@ TpiSource::~TpiSource() { consumeError(std::move(typeMergingError)); } -void TpiSource::sortDependencies() { - // Order dependencies first, but preserve the existing order. - std::vector deps; - std::vector objs; - for (TpiSource *s : instances) - (s->isDependency() ? deps : objs).push_back(s); - uint32_t numDeps = deps.size(); - uint32_t numObjs = objs.size(); - instances = std::move(deps); - instances.insert(instances.end(), objs.begin(), objs.end()); - for (uint32_t i = 0, e = instances.size(); i < e; ++i) - instances[i]->tpiSrcIdx = i; - dependencySources = makeArrayRef(instances.data(), numDeps); - objectSources = makeArrayRef(instances.data() + numDeps, numObjs); -} - -TpiSource *lld::coff::makeTpiSource(ObjFile *file) { - return make(TpiSource::Regular, file); +TpiSource *lld::coff::makeTpiSource(COFFLinkerContext &ctx, ObjFile *file) { + return make(ctx, TpiSource::Regular, file); } -TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) { +TpiSource *lld::coff::makeTypeServerSource(COFFLinkerContext &ctx, + PDBInputFile *pdbInputFile) { // Type server sources come in pairs: the TPI stream, and the IPI stream. - auto *tpiSource = make(pdbInputFile); + auto *tpiSource = make(ctx, pdbInputFile); if (pdbInputFile->session->getPDBFile().hasPDBIpiStream()) - tpiSource->ipiSrc = make(); + tpiSource->ipiSrc = make(ctx); return tpiSource; } -TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file, +TpiSource *lld::coff::makeUseTypeServerSource(COFFLinkerContext &ctx, + ObjFile *file, TypeServer2Record ts) { - return make(file, ts); + return make(ctx, file, ts); } -TpiSource *lld::coff::makePrecompSource(ObjFile *file) { - return make(file); +TpiSource *lld::coff::makePrecompSource(COFFLinkerContext &ctx, ObjFile *file) { + return make(ctx, file); } -TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file, +TpiSource *lld::coff::makeUsePrecompSource(COFFLinkerContext &ctx, + ObjFile *file, PrecompRecord precomp) { - return make(file, precomp); + return make(ctx, file, precomp); } -std::map TypeServerSource::mappings; - -std::map PrecompSource::mappings; - bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const { if (ti.isSimple()) return true; @@ -246,7 +235,7 @@ void TpiSource::remapRecord(MutableArrayRef rec, reinterpret_cast(contents.data() + ref.Offset), ref.Count); for (TypeIndex &ti : indices) { if (!remapTypeIndex(ti, ref.Kind)) { - if (config->verbose) { + if (ctx.config.verbose) { uint16_t kind = reinterpret_cast(rec.data())->RecordKind; StringRef fname = file ? file->getName() : ""; @@ -291,18 +280,18 @@ static bool canUseDebugH(ArrayRef debugH) { 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) && + header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::BLAKE3) && (debugH.size() % 8 == 0); } -static Optional> getDebugH(ObjFile *file) { +static std::optional> getDebugH(ObjFile *file) { SectionChunk *sec = SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); if (!sec) - return llvm::None; + return std::nullopt; ArrayRef contents = sec->getContents(); if (!canUseDebugH(contents)) - return None; + return std::nullopt; return contents; } @@ -316,7 +305,7 @@ getHashesFromDebugH(ArrayRef debugH) { // Merge .debug$T for a generic object file. Error TpiSource::mergeDebugT(TypeMerger *m) { - assert(!config->debugGHashes && + assert(!ctx.config.debugGHashes && "use remapTpiWithGHashes when ghash is enabled"); CVTypeArray types; @@ -326,16 +315,21 @@ Error TpiSource::mergeDebugT(TypeMerger *m) { // When dealing with PCH.OBJ, some indices were already merged. unsigned nbHeadIndices = indexMapStorage.size(); - if (auto err = mergeTypeAndIdRecords( - m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature)) + std::optional pchInfo; + if (auto err = mergeTypeAndIdRecords(m->idTable, m->typeTable, + indexMapStorage, types, pchInfo)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); + if (pchInfo) { + file->pchSignature = pchInfo->PCHSignature; + endPrecompIdx = pchInfo->EndPrecompIndex; + } // In an object, there is only one mapping for both types and items. tpiMap = indexMapStorage; ipiMap = indexMapStorage; - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = indexMapStorage.size() - nbHeadIndices; nbTypeRecordsBytes = reader.getLength(); // Count how many times we saw each type record in our input. This @@ -345,7 +339,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) { m->tpiCounts.resize(m->getTypeTable().size()); m->ipiCounts.resize(m->getIDTable().size()); uint32_t srcIdx = nbHeadIndices; - for (CVType &ty : types) { + for (const CVType &ty : types) { TypeIndex dstIdx = tpiMap[srcIdx++]; // Type merging may fail, so a complex source type may become the simple // NotTranslated type, which cannot be used as an array index. @@ -362,7 +356,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) { // Merge types from a type server PDB. Error TypeServerSource::mergeDebugT(TypeMerger *m) { - assert(!config->debugGHashes && + assert(!ctx.config.debugGHashes && "use remapTpiWithGHashes when ghash is enabled"); pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); @@ -391,7 +385,7 @@ Error TypeServerSource::mergeDebugT(TypeMerger *m) { ipiMap = ipiSrc->indexMapStorage; } - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = tpiMap.size() + ipiMap.size(); nbTypeRecordsBytes = expectedTpi->typeArray().getUnderlyingStream().getLength() + @@ -418,19 +412,22 @@ Expected UseTypeServerSource::getTypeServerSource() { const codeview::GUID &tsId = typeServerDependency.getGuid(); StringRef tsPath = typeServerDependency.getName(); - TypeServerSource *tsSrc; - auto it = TypeServerSource::mappings.find(tsId); - if (it != TypeServerSource::mappings.end()) { - tsSrc = it->second; - } else { + TypeServerSource *tsSrc = nullptr; + auto it = ctx.typeServerSourceMappings.find(tsId); + if (it != ctx.typeServerSourceMappings.end()) { + tsSrc = (TypeServerSource *)it->second; + } + if (tsSrc == nullptr) { // The file failed to load, lookup by name - PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file); + PDBInputFile *pdb = PDBInputFile::findFromRecordPath(ctx, tsPath, file); if (!pdb) return createFileError(tsPath, errorCodeToError(std::error_code( ENOENT, std::generic_category()))); // If an error occurred during loading, throw it now - if (pdb->loadErr && *pdb->loadErr) - return createFileError(tsPath, std::move(*pdb->loadErr)); + if (pdb->loadErrorStr) + return createFileError( + tsPath, make_error(*pdb->loadErrorStr, + llvm::inconvertibleErrorCode())); tsSrc = (TypeServerSource *)pdb->debugTypesObj; @@ -471,36 +468,37 @@ static bool equalsPath(StringRef path1, StringRef path2) { } // Find by name an OBJ provided on the command line -static PrecompSource *findObjByName(StringRef fileNameOnly) { +PrecompSource *UsePrecompSource::findObjByName(StringRef fileNameOnly) { SmallString<128> currentPath; - for (auto kv : PrecompSource::mappings) { + for (auto kv : ctx.precompSourceMappings) { StringRef currentFileName = sys::path::filename(kv.second->file->getName(), sys::path::Style::windows); // Compare based solely on the file name (link.exe behavior) if (equalsPath(currentFileName, fileNameOnly)) - return kv.second; + return (PrecompSource *)kv.second; } return nullptr; } -static PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr) { +PrecompSource *UsePrecompSource::findPrecompSource(ObjFile *file, + PrecompRecord &pr) { // 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> prFileName = sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows); - auto it = PrecompSource::mappings.find(pr.getSignature()); - if (it != PrecompSource::mappings.end()) { - return it->second; + auto it = ctx.precompSourceMappings.find(pr.getSignature()); + if (it != ctx.precompSourceMappings.end()) { + return (PrecompSource *)it->second; } // Lookup by name return findObjByName(prFileName); } -static Expected findPrecompMap(ObjFile *file, - PrecompRecord &pr) { +Expected UsePrecompSource::findPrecompMap(ObjFile *file, + PrecompRecord &pr) { PrecompSource *precomp = findPrecompSource(file, pr); if (!precomp) @@ -508,16 +506,15 @@ static Expected findPrecompMap(ObjFile *file, pr.getPrecompFilePath(), make_error(pdb::pdb_error_code::no_matching_pch)); - if (pr.getSignature() != file->pchSignature) + // Don't rely on the PCH signature to validate the concordance between the PCH + // and the OBJ that uses it. However we do validate here that the + // LF_ENDPRECOMP record index lines up with the number of type records + // LF_PRECOMP is expecting. + if (precomp->endPrecompIdx != pr.getTypesCount()) return createFileError( toString(file), make_error(pdb::pdb_error_code::no_matching_pch)); - if (pr.getSignature() != *precomp->file->pchSignature) - return createFileError( - toString(precomp->file), - make_error(pdb::pdb_error_code::no_matching_pch)); - return precomp; } @@ -555,20 +552,28 @@ Error UsePrecompSource::mergeDebugT(TypeMerger *m) { return TpiSource::mergeDebugT(m); } -uint32_t TpiSource::countTypeServerPDBs() { - return TypeServerSource::mappings.size(); -} +Error PrecompSource::mergeDebugT(TypeMerger *m) { + // In some cases, the S_OBJNAME record doesn't contain the PCH signature. + // The signature comes later with the LF_ENDPRECOMP record, so we first need + // to merge in all the .PCH.OBJ file type records, before registering below. + if (Error e = TpiSource::mergeDebugT(m)) + return e; + + registerMapping(); -uint32_t TpiSource::countPrecompObjs() { - return PrecompSource::mappings.size(); + return Error::success(); } -void TpiSource::clear() { - // Clean up any owned ghash allocations. - clearGHashes(); - TpiSource::instances.clear(); - TypeServerSource::mappings.clear(); - PrecompSource::mappings.clear(); +void PrecompSource::registerMapping() { + if (registered) + return; + if (file->pchSignature && *file->pchSignature) { + auto it = ctx.precompSourceMappings.emplace(*file->pchSignature, this); + if (!it.second) + fatal("a PCH object with the same signature has already been provided (" + + toString(it.first->second->file) + " and " + toString(file) + ")"); + registered = true; + } } //===----------------------------------------------------------------------===// @@ -576,7 +581,7 @@ void TpiSource::clear() { //===----------------------------------------------------------------------===// void TpiSource::loadGHashes() { - if (Optional> debugH = getDebugH(file)) { + if (std::optional> debugH = getDebugH(file)) { ghashes = getHashesFromDebugH(*debugH); ownedGHashes = false; } else { @@ -598,7 +603,7 @@ void TpiSource::assignGHashesFromVector( return; GloballyHashedType *hashes = new GloballyHashedType[hashVec.size()]; memcpy(hashes, hashVec.data(), hashVec.size() * sizeof(GloballyHashedType)); - ghashes = makeArrayRef(hashes, hashVec.size()); + ghashes = ArrayRef(hashes, hashVec.size()); ownedGHashes = true; } @@ -637,7 +642,7 @@ void TpiSource::mergeTypeRecord(TypeIndex curIndex, CVType ty) { size_t offset = merged.recs.size(); size_t newSize = alignTo(ty.length(), 4); merged.recs.resize(offset + newSize); - auto newRec = makeMutableArrayRef(&merged.recs[offset], newSize); + auto newRec = MutableArrayRef(&merged.recs[offset], newSize); memcpy(newRec.data(), ty.data().data(), newSize); // Fix up the record prefix and padding bytes if it required resizing. @@ -677,7 +682,7 @@ void TpiSource::mergeUniqueTypeRecords(ArrayRef typeRecords, TypeIndex beginIndex) { // Re-sort the list of unique types by index. if (kind == PDB) - assert(std::is_sorted(uniqueTypes.begin(), uniqueTypes.end())); + assert(llvm::is_sorted(uniqueTypes)); else llvm::sort(uniqueTypes); @@ -722,14 +727,14 @@ void TpiSource::mergeUniqueTypeRecords(ArrayRef typeRecords, } void TpiSource::remapTpiWithGHashes(GHashState *g) { - assert(config->debugGHashes && "ghashes must be enabled"); + assert(ctx.config.debugGHashes && "ghashes must be enabled"); fillMapFromGHashes(g); tpiMap = indexMapStorage; ipiMap = indexMapStorage; mergeUniqueTypeRecords(file->debugTypes); // TODO: Free all unneeded ghash resources now that we have a full index map. - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = ghashes.size(); nbTypeRecordsBytes = file->debugTypes.size(); } @@ -782,7 +787,7 @@ static ArrayRef typeArrayToBytes(const CVTypeArray &types) { // Merge types from a type server PDB. void TypeServerSource::remapTpiWithGHashes(GHashState *g) { - assert(config->debugGHashes && "ghashes must be enabled"); + assert(ctx.config.debugGHashes && "ghashes must be enabled"); // IPI merging depends on TPI, so do TPI first, then do IPI. No need to // propagate errors, those should've been handled during ghash loading. @@ -800,13 +805,13 @@ void TypeServerSource::remapTpiWithGHashes(GHashState *g) { ipiSrc->ipiMap = ipiMap; ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray())); - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = ipiSrc->ghashes.size(); nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength(); } } - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords += ghashes.size(); nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength(); } @@ -838,8 +843,14 @@ void PrecompSource::loadGHashes() { // Remember the index of the LF_ENDPRECOMP record so it can be excluded from // the PDB. There must be an entry in the list of ghashes so that the type // indexes of the following records in the /Yc PCH object line up. - if (ty.kind() == LF_ENDPRECOMP) - endPrecompGHashIdx = ghashIdx; + if (ty.kind() == LF_ENDPRECOMP) { + EndPrecompRecord endPrecomp; + cantFail(TypeDeserializer::deserializeAs( + const_cast(ty), endPrecomp)); + file->pchSignature = endPrecomp.getSignature(); + registerMapping(); + endPrecompIdx = ghashIdx; + } hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec)); isItemIndex.push_back(isIdRecord(ty.kind())); @@ -849,14 +860,17 @@ void PrecompSource::loadGHashes() { } void UsePrecompSource::loadGHashes() { - PrecompSource *pchSrc = findPrecompSource(file, precompDependency); - if (!pchSrc) + auto e = findPrecompMap(file, precompDependency); + if (!e) { + warn(toString(e.takeError())); return; + } + + PrecompSource *pchSrc = *e; - // To compute ghashes of a /Yu object file, we need to build on the the - // ghashes of the /Yc PCH object. After we are done hashing, discard the - // ghashes from the PCH source so we don't unnecessarily try to deduplicate - // them. + // To compute ghashes of a /Yu object file, we need to build on the ghashes of + // the /Yc PCH object. After we are done hashing, discard the ghashes from the + // PCH source so we don't unnecessarily try to deduplicate them. std::vector hashVec = pchSrc->ghashes.take_front(precompDependency.getTypesCount()); forEachTypeChecked(file->debugTypes, [&](const CVType &ty) { @@ -884,7 +898,7 @@ void UsePrecompSource::remapTpiWithGHashes(GHashState *g) { mergeUniqueTypeRecords(file->debugTypes, TypeIndex(precompDependency.getStartTypeIndex() + precompDependency.getTypesCount())); - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = ghashes.size(); nbTypeRecordsBytes = file->debugTypes.size(); } @@ -926,12 +940,17 @@ struct GHashTable { /// Insert the cell with the given ghash into the table. Return the insertion /// position in the table. It is safe for the caller to store the insertion /// position because the table cannot be resized. - uint32_t insert(GloballyHashedType ghash, GHashCell newCell); + uint32_t insert(COFFLinkerContext &ctx, GloballyHashedType ghash, + GHashCell newCell); }; /// A ghash table cell for deduplicating types from TpiSources. class GHashCell { - uint64_t data = 0; + // Force "data" to be 64-bit aligned; otherwise, some versions of clang + // will generate calls to libatomic when using some versions of libstdc++ + // on 32-bit targets. (Also, in theory, there could be a target where + // new[] doesn't always return an 8-byte-aligned allocation.) + alignas(sizeof(uint64_t)) uint64_t data = 0; public: GHashCell() = default; @@ -965,8 +984,8 @@ public: bool isItem() const { return data & (1ULL << 63U); } /// Get the ghash key for this cell. - GloballyHashedType getGHash() const { - return TpiSource::instances[getTpiSrcIdx()]->ghashes[getGHashIdx()]; + GloballyHashedType getGHash(const COFFLinkerContext &ctx) const { + return ctx.tpiSourceList[getTpiSrcIdx()]->ghashes[getGHashIdx()]; } /// The priority function for the cell. The data is stored such that lower @@ -978,15 +997,13 @@ public: }; } // namespace -namespace lld { -namespace coff { +namespace lld::coff { /// This type is just a wrapper around GHashTable with external linkage so it /// can be used from a header. struct GHashState { GHashTable table; }; -} // namespace coff -} // namespace lld +} // namespace lld::coff GHashTable::~GHashTable() { delete[] table; } @@ -996,7 +1013,8 @@ void GHashTable::init(uint32_t newTableSize) { tableSize = newTableSize; } -uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) { +uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash, + GHashCell newCell) { assert(!newCell.isEmpty() && "cannot insert empty cell value"); // FIXME: The low bytes of SHA1 have low entropy for short records, which @@ -1015,7 +1033,7 @@ uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) { // - cell has non-matching key: hash collision, probe next cell auto *cellPtr = reinterpret_cast *>(&table[idx]); GHashCell oldCell(cellPtr->load()); - while (oldCell.isEmpty() || oldCell.getGHash() == ghash) { + while (oldCell.isEmpty() || oldCell.getGHash(ctx) == ghash) { // Check if there is an existing ghash entry with a higher priority // (earlier ordering). If so, this is a duplicate, we are done. if (!oldCell.isEmpty() && oldCell < newCell) @@ -1040,22 +1058,22 @@ uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) { llvm_unreachable("left infloop"); } -TypeMerger::TypeMerger(llvm::BumpPtrAllocator &alloc) - : typeTable(alloc), idTable(alloc) {} +TypeMerger::TypeMerger(COFFLinkerContext &c, llvm::BumpPtrAllocator &alloc) + : typeTable(alloc), idTable(alloc), ctx(c) {} TypeMerger::~TypeMerger() = default; void TypeMerger::mergeTypesWithGHash() { // Load ghashes. Do type servers and PCH objects first. { - ScopedTimer t1(loadGHashTimer); - parallelForEach(TpiSource::dependencySources, + ScopedTimer t1(ctx.loadGHashTimer); + parallelForEach(dependencySources, [&](TpiSource *source) { source->loadGHashes(); }); - parallelForEach(TpiSource::objectSources, + parallelForEach(objectSources, [&](TpiSource *source) { source->loadGHashes(); }); } - ScopedTimer t2(mergeGHashTimer); + ScopedTimer t2(ctx.mergeGHashTimer); GHashState ghashState; // Estimate the size of hash table needed to deduplicate ghashes. This *must* @@ -1066,7 +1084,7 @@ void TypeMerger::mergeTypesWithGHash() { // small compared to total memory usage, at eight bytes per input type record, // and most input type records are larger than eight bytes. size_t tableSize = 0; - for (TpiSource *source : TpiSource::instances) + for (TpiSource *source : ctx.tpiSourceList) tableSize += source->ghashes.size(); // Cap the table size so that we can use 32-bit cell indices. Type indices are @@ -1080,8 +1098,8 @@ void TypeMerger::mergeTypesWithGHash() { // position. Because the table does not rehash, the position will not change // under insertion. After insertion is done, the value of the cell can be read // to retrieve the final PDB type index. - parallelForEachN(0, TpiSource::instances.size(), [&](size_t tpiSrcIdx) { - TpiSource *source = TpiSource::instances[tpiSrcIdx]; + parallelFor(0, ctx.tpiSourceList.size(), [&](size_t tpiSrcIdx) { + TpiSource *source = ctx.tpiSourceList[tpiSrcIdx]; source->indexMapStorage.resize(source->ghashes.size()); for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) { if (source->shouldOmitFromPdb(i)) { @@ -1091,7 +1109,7 @@ void TypeMerger::mergeTypesWithGHash() { GloballyHashedType ghash = source->ghashes[i]; bool isItem = source->isItemIndex.test(i); uint32_t cellIdx = - ghashState.table.insert(ghash, GHashCell(isItem, tpiSrcIdx, i)); + ghashState.table.insert(ctx, ghash, GHashCell(isItem, tpiSrcIdx, i)); // Store the ghash cell index as a type index in indexMapStorage. Later // we will replace it with the PDB type index. @@ -1109,8 +1127,7 @@ void TypeMerger::mergeTypesWithGHash() { // - source 0, type 1... // - source 1, type 0... std::vector entries; - for (const GHashCell &cell : - makeArrayRef(ghashState.table.table, tableSize)) { + for (const GHashCell &cell : ArrayRef(ghashState.table.table, tableSize)) { if (!cell.isEmpty()) entries.push_back(cell); } @@ -1120,8 +1137,7 @@ void TypeMerger::mergeTypesWithGHash() { entries.size(), tableSize)); // Find out how many type and item indices there are. - auto mid = - std::lower_bound(entries.begin(), entries.end(), GHashCell(true, 0, 0)); + auto mid = llvm::lower_bound(entries, GHashCell(true, 0, 0)); assert((mid == entries.end() || mid->isItem()) && (mid == entries.begin() || !std::prev(mid)->isItem()) && "midpoint is not midpoint"); @@ -1137,7 +1153,7 @@ void TypeMerger::mergeTypesWithGHash() { for (uint32_t i = 0, e = entries.size(); i < e; ++i) { auto &cell = entries[i]; uint32_t tpiSrcIdx = cell.getTpiSrcIdx(); - TpiSource *source = TpiSource::instances[tpiSrcIdx]; + TpiSource *source = ctx.tpiSourceList[tpiSrcIdx]; source->uniqueTypes.push_back(cell.getGHashIdx()); // Update the ghash table to store the destination PDB type index in the @@ -1150,21 +1166,36 @@ void TypeMerger::mergeTypesWithGHash() { } // In parallel, remap all types. - for_each(TpiSource::dependencySources, [&](TpiSource *source) { + for (TpiSource *source : dependencySources) source->remapTpiWithGHashes(&ghashState); - }); - parallelForEach(TpiSource::objectSources, [&](TpiSource *source) { + parallelForEach(objectSources, [&](TpiSource *source) { source->remapTpiWithGHashes(&ghashState); }); // Build a global map of from function ID to function type. - for (TpiSource *source : TpiSource::instances) { + for (TpiSource *source : ctx.tpiSourceList) { for (auto idToType : source->funcIdToType) funcIdToType.insert(idToType); source->funcIdToType.clear(); } - TpiSource::clearGHashes(); + clearGHashes(); +} + +void TypeMerger::sortDependencies() { + // Order dependencies first, but preserve the existing order. + std::vector deps; + std::vector objs; + for (TpiSource *s : ctx.tpiSourceList) + (s->isDependency() ? deps : objs).push_back(s); + uint32_t numDeps = deps.size(); + uint32_t numObjs = objs.size(); + ctx.tpiSourceList = std::move(deps); + ctx.tpiSourceList.insert(ctx.tpiSourceList.end(), objs.begin(), objs.end()); + for (uint32_t i = 0, e = ctx.tpiSourceList.size(); i < e; ++i) + ctx.tpiSourceList[i]->tpiSrcIdx = i; + dependencySources = ArrayRef(ctx.tpiSourceList.data(), numDeps); + objectSources = ArrayRef(ctx.tpiSourceList.data() + numDeps, numObjs); } /// Given the index into the ghash table for a particular type, return the type @@ -1175,6 +1206,17 @@ static TypeIndex loadPdbTypeIndexFromCell(GHashState *g, return TypeIndex::fromArrayIndex(cell.getGHashIdx()); } +/// Free heap allocated ghashes. +void TypeMerger::clearGHashes() { + for (TpiSource *src : ctx.tpiSourceList) { + if (src->ownedGHashes) + delete[] src->ghashes.data(); + src->ghashes = {}; + src->isItemIndex.clear(); + src->uniqueTypes.clear(); + } +} + // Fill in a TPI or IPI index map using ghashes. For each source type, use its // ghash to lookup its final type index in the PDB, and store that in the map. void TpiSource::fillMapFromGHashes(GHashState *g) { @@ -1187,13 +1229,3 @@ void TpiSource::fillMapFromGHashes(GHashState *g) { loadPdbTypeIndexFromCell(g, fakeCellIndex.toArrayIndex()); } } - -void TpiSource::clearGHashes() { - for (TpiSource *src : TpiSource::instances) { - if (src->ownedGHashes) - delete[] src->ghashes.data(); - src->ghashes = {}; - src->isItemIndex.clear(); - src->uniqueTypes.clear(); - } -} diff --git a/gnu/llvm/lld/COFF/DebugTypes.h b/gnu/llvm/lld/COFF/DebugTypes.h index faad30b141e..a7d477695af 100644 --- a/gnu/llvm/lld/COFF/DebugTypes.h +++ b/gnu/llvm/lld/COFF/DebugTypes.h @@ -17,18 +17,15 @@ #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" -namespace llvm { -namespace codeview { +namespace llvm::codeview { struct GloballyHashedType; -} // namespace codeview -namespace pdb { +} +namespace llvm::pdb { class NativeSession; class TpiStream; } -} // namespace llvm -namespace lld { -namespace coff { +namespace lld::coff { using llvm::codeview::GloballyHashedType; using llvm::codeview::TypeIndex; @@ -37,12 +34,13 @@ class ObjFile; class PDBInputFile; class TypeMerger; struct GHashState; +class COFFLinkerContext; class TpiSource { public: enum TpiKind : uint8_t { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB }; - TpiSource(TpiKind k, ObjFile *f); + TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f); virtual ~TpiSource(); /// Produce a mapping from the type and item indices used in the object @@ -93,6 +91,8 @@ protected: // Walk over file->debugTypes and fill in the isItemIndex bit vector. void fillIsItemIndexFromDebugT(); + COFFLinkerContext &ctx; + public: bool remapTypesInSymbolRecord(MutableArrayRef rec); @@ -106,41 +106,18 @@ public: /// it is unique. This prevents a record from being added to the input ghash /// table. bool shouldOmitFromPdb(uint32_t ghashIdx) { - return ghashIdx == endPrecompGHashIdx; + return ghashIdx == endPrecompIdx; } - /// All sources of type information in the program. - static std::vector instances; - - /// Dependency type sources, such as type servers or PCH object files. These - /// must be processed before objects that rely on them. Set by - /// TpiSources::sortDependencies. - static ArrayRef dependencySources; - - /// Object file sources. These must be processed after dependencySources. - static ArrayRef objectSources; - - /// Sorts the dependencies and reassigns TpiSource indices. - static void sortDependencies(); - - static uint32_t countTypeServerPDBs(); - static uint32_t countPrecompObjs(); - - /// Free heap allocated ghashes. - static void clearGHashes(); - - /// Clear global data structures for TpiSources. - static void clear(); - const TpiKind kind; bool ownedGHashes = true; uint32_t tpiSrcIdx = 0; -protected: - /// The ghash index (zero based, not 0x1000-based) of the LF_ENDPRECOMP record - /// in this object, if one exists. This is the all ones value otherwise. It is - /// recorded here so that it can be omitted from the final ghash table. - uint32_t endPrecompGHashIdx = ~0U; + /// The index (zero based, not 0x1000-based) of the LF_ENDPRECOMP record in + /// this object, if one exists. This is the all ones value otherwise. It is + /// recorded here for validation, and so that it can be omitted from the final + /// ghash table. + uint32_t endPrecompIdx = ~0U; public: ObjFile *file; @@ -186,15 +163,15 @@ public: uint64_t nbTypeRecordsBytes = 0; }; -TpiSource *makeTpiSource(ObjFile *file); -TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile); -TpiSource *makeUseTypeServerSource(ObjFile *file, +TpiSource *makeTpiSource(COFFLinkerContext &ctx, ObjFile *f); +TpiSource *makeTypeServerSource(COFFLinkerContext &ctx, + PDBInputFile *pdbInputFile); +TpiSource *makeUseTypeServerSource(COFFLinkerContext &ctx, ObjFile *file, llvm::codeview::TypeServer2Record ts); -TpiSource *makePrecompSource(ObjFile *file); -TpiSource *makeUsePrecompSource(ObjFile *file, +TpiSource *makePrecompSource(COFFLinkerContext &ctx, ObjFile *file); +TpiSource *makeUsePrecompSource(COFFLinkerContext &ctx, ObjFile *file, llvm::codeview::PrecompRecord ts); -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/Driver.cpp b/gnu/llvm/lld/COFF/Driver.cpp index 9ba0db31507..d6b71eaf7f2 100644 --- a/gnu/llvm/lld/COFF/Driver.cpp +++ b/gnu/llvm/lld/COFF/Driver.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Driver.h" +#include "COFFLinkerContext.h" #include "Config.h" #include "DebugTypes.h" #include "ICF.h" @@ -17,14 +18,14 @@ #include "Symbols.h" #include "Writer.h" #include "lld/Common/Args.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Driver.h" -#include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" -#include "lld/Common/Memory.h" #include "lld/Common/Timer.h" #include "lld/Common/Version.h" -#include "llvm/ADT/Optional.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" @@ -45,62 +46,34 @@ #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" #include #include #include +#include using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; using namespace llvm::sys; -namespace lld { -namespace coff { +namespace lld::coff { -static Timer inputFileTimer("Input File Reading", Timer::root()); +bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new COFFLinkerContext; -Configuration *config; -LinkerDriver *driver; + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now" + " (use /errorlimit:0 to see all errors)"; -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; + ctx->driver.linkerMain(args); - errorHandler().cleanupCallback = []() { - TpiSource::clear(); - freeArena(); - ObjFile::instances.clear(); - PDBInputFile::instances.clear(); - ImportFile::instances.clear(); - BitcodeFile::instances.clear(); - memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); - OutputSection::clear(); - }; - - errorHandler().logName = args::getFilenameWithoutExe(args[0]); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now" - " (use /errorlimit:0 to see all errors)"; - errorHandler().exitEarly = canExitEarly; - stderrOS.enable_colors(stderrOS.has_colors()); - - config = make(); - symtab = make(); - driver = make(); - - driver->linkerMain(args); - - // Call exit() if we can to avoid calling destructors. - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - bool ret = errorCount() == 0; - if (!canExitEarly) - errorHandler().reset(); - return ret; + return errorCount() == 0; } // Parse options of the form "old;new". @@ -119,11 +92,11 @@ static std::pair getOldNewOptions(opt::InputArgList &args, // Drop directory components and replace extension with // ".exe", ".dll" or ".sys". -static std::string getOutputPath(StringRef path) { +static std::string getOutputPath(StringRef path, bool isDll, bool isDriver) { StringRef ext = ".exe"; - if (config->dll) + if (isDll) ext = ".dll"; - else if (config->driver) + else if (isDriver) ext = ".sys"; return (sys::path::stem(path) + ext).str(); @@ -167,15 +140,30 @@ static std::future createFutureForFile(std::string path) { } // 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); +StringRef LinkerDriver::mangle(StringRef sym) { + assert(ctx.config.machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (ctx.config.machine == I386) + return saver().save("_" + sym); return sym; } -static bool findUnderscoreMangle(StringRef sym) { - Symbol *s = symtab->findMangle(mangle(sym)); +llvm::Triple::ArchType LinkerDriver::getArch() { + switch (ctx.config.machine) { + case I386: + return llvm::Triple::ArchType::x86; + case AMD64: + return llvm::Triple::ArchType::x86_64; + case ARMNT: + return llvm::Triple::ArchType::arm; + case ARM64: + return llvm::Triple::ArchType::aarch64; + default: + return llvm::Triple::ArchType::UnknownArch; + } +} + +bool LinkerDriver::findUnderscoreMangle(StringRef sym) { + Symbol *s = ctx.symtab.findMangle(mangle(sym)); return s && !isa(s); } @@ -183,9 +171,9 @@ 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()); + if (ctx.driver.tar) + ctx.driver.tar->append(relativeToRoot(mbref.getBufferIdentifier()), + mbref.getBuffer()); return mbref; } @@ -213,30 +201,24 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, addArchiveBuffer(m, "", filename, memberIndex++); return; } - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); break; case file_magic::bitcode: - if (lazy) - symtab->addFile(make(mbref)); - else - symtab->addFile(make(mbref, "", 0)); + ctx.symtab.addFile(make(ctx, mbref, "", 0, lazy)); break; case file_magic::coff_object: case file_magic::coff_import_library: - if (lazy) - symtab->addFile(make(mbref)); - else - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref, lazy)); break; case file_magic::pdb: - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, 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 (config->mingw) { - symtab->addFile(make(mbref)); + if (ctx.config.mingw) { + ctx.symtab.addFile(make(ctx, mbref)); break; } if (filename.endswith_insensitive(".dll")) { @@ -244,7 +226,7 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, "import library?"); break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; default: error(mbref.getBufferIdentifier() + ": unknown file type"); break; @@ -266,12 +248,12 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { // the option `/nodefaultlib` than a reference to a file in the root // directory. std::string nearest; - if (optTable.findNearest(pathStr, nearest) > 1) + if (ctx.optTable.findNearest(pathStr, nearest) > 1) error(msg); else error(msg + "; did you mean '" + nearest + "'"); } else - driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); + ctx.driver.addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); } @@ -280,24 +262,29 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, uint64_t offsetInArchive) { file_magic magic = identify_magic(mb.getBuffer()); if (magic == file_magic::coff_import_library) { - InputFile *imp = make(mb); + InputFile *imp = make(ctx, mb); imp->parentName = parentName; - symtab->addFile(imp); + ctx.symtab.addFile(imp); return; } InputFile *obj; if (magic == file_magic::coff_object) { - obj = make(mb); + obj = make(ctx, mb); } else if (magic == file_magic::bitcode) { - obj = make(mb, parentName, offsetInArchive); + obj = + make(ctx, mb, parentName, offsetInArchive, /*lazy=*/false); + } else if (magic == file_magic::coff_cl_gl_object) { + error(mb.getBufferIdentifier() + + ": is not a native COFF file. Recompile without /GL?"); + return; } else { error("unknown file type: " + mb.getBufferIdentifier()); return; } obj->parentName = parentName; - symtab->addFile(obj); + ctx.symtab.addFile(obj); log("Loaded " + toString(obj) + " for " + symName); } @@ -307,8 +294,8 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, auto reportBufferError = [=](Error &&e, StringRef childName) { fatal("could not get the buffer for the member defining symbol " + - toCOFFString(sym) + ": " + parentName + "(" + childName + "): " + - toString(std::move(e))); + toCOFFString(ctx, sym) + ": " + parentName + "(" + childName + + "): " + toString(std::move(e))); }; if (!c.getParent()->isThin()) { @@ -318,16 +305,16 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, reportBufferError(mbOrErr.takeError(), check(c.getFullName())); MemoryBufferRef mb = mbOrErr.get(); enqueueTask([=]() { - driver->addArchiveBuffer(mb, toCOFFString(sym), parentName, - offsetInArchive); + ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName, + offsetInArchive); }); return; } - std::string childName = CHECK( - c.getFullName(), - "could not get the filename for the member defining symbol " + - toCOFFString(sym)); + std::string childName = + CHECK(c.getFullName(), + "could not get the filename for the member defining symbol " + + toCOFFString(ctx, sym)); auto future = std::make_shared>( createFutureForFile(childName)); enqueueTask([=]() { @@ -336,14 +323,15 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, reportBufferError(errorCodeToError(mbOrErr.second), childName); // 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); + ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), + toCOFFString(ctx, sym), "", + /*OffsetInArchive=*/0); }); } -static bool isDecorated(StringRef sym) { +bool LinkerDriver::isDecorated(StringRef sym) { return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") || - (!config->mingw && sym.contains('@')); + (!ctx.config.mingw && sym.contains('@')); } // Parses .drectve section contents and returns a list of files @@ -355,7 +343,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { log("Directives: " + toString(file) + ": " + s); - ArgParser parser; + ArgParser parser(ctx); // .drectve is always tokenized using Windows shell rules. // /EXPORT: option can appear too many times, processing in fastpath. ParsedDirectives directives = parser.parseDirectives(s); @@ -369,20 +357,29 @@ void LinkerDriver::parseDirectives(InputFile *file) { continue; Export exp = parseExport(e); - if (config->machine == I386 && config->mingw) { + if (ctx.config.machine == I386 && ctx.config.mingw) { if (!isDecorated(exp.name)) - exp.name = saver.save("_" + exp.name); + exp.name = saver().save("_" + exp.name); if (!exp.extName.empty() && !isDecorated(exp.extName)) - exp.extName = saver.save("_" + exp.extName); + exp.extName = saver().save("_" + exp.extName); } exp.directives = true; - config->exports.push_back(exp); + ctx.config.exports.push_back(exp); } // Handle /include: in bulk. for (StringRef inc : directives.includes) addUndefined(inc); + // Handle /exclude-symbols: in bulk. + for (StringRef e : directives.excludes) { + SmallVector vec; + e.split(vec, ','); + for (StringRef sym : vec) + excludedSymbols.insert(mangle(sym)); + } + + // https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160 for (auto *arg : directives.args) { switch (arg->getOption().getID()) { case OPT_aligncomm: @@ -392,11 +389,11 @@ void LinkerDriver::parseDirectives(InputFile *file) { parseAlternateName(arg->getValue()); break; case OPT_defaultlib: - if (Optional path = findLib(arg->getValue())) + if (std::optional path = findLib(arg->getValue())) enqueuePath(*path, false, false); break; case OPT_entry: - config->entry = addUndefined(mangle(arg->getValue())); + ctx.config.entry = addUndefined(mangle(arg->getValue())); break; case OPT_failifmismatch: checkFailIfMismatch(arg->getValue(), file); @@ -404,27 +401,33 @@ void LinkerDriver::parseDirectives(InputFile *file) { case OPT_incl: addUndefined(arg->getValue()); break; + case OPT_manifestdependency: + ctx.config.manifestDependencies.insert(arg->getValue()); + break; case OPT_merge: parseMerge(arg->getValue()); break; case OPT_nodefaultlib: - config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); + ctx.config.noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); + break; + case OPT_release: + ctx.config.writeCheckSum = true; break; case OPT_section: parseSection(arg->getValue()); break; case OPT_stack: - parseNumbers(arg->getValue(), &config->stackReserve, - &config->stackCommit); + parseNumbers(arg->getValue(), &ctx.config.stackReserve, + &ctx.config.stackCommit); break; case OPT_subsystem: { bool gotVersion = false; - parseSubsystem(arg->getValue(), &config->subsystem, - &config->majorSubsystemVersion, - &config->minorSubsystemVersion, &gotVersion); + parseSubsystem(arg->getValue(), &ctx.config.subsystem, + &ctx.config.majorSubsystemVersion, + &ctx.config.minorSubsystemVersion, &gotVersion); if (gotVersion) { - config->majorOSVersion = config->majorSubsystemVersion; - config->minorOSVersion = config->minorSubsystemVersion; + ctx.config.majorOSVersion = ctx.config.majorSubsystemVersion; + ctx.config.minorOSVersion = ctx.config.minorSubsystemVersion; } break; } @@ -433,9 +436,12 @@ void LinkerDriver::parseDirectives(InputFile *file) { case OPT_editandcontinue: case OPT_guardsym: case OPT_throwingnew: + case OPT_inferasanlibs: + case OPT_inferasanlibs_no: break; default: - error(arg->getSpelling() + " is not allowed in .drectve"); + error(arg->getSpelling() + " is not allowed in .drectve (" + + toString(file) + ")"); } } } @@ -443,44 +449,53 @@ void LinkerDriver::parseDirectives(InputFile *file) { // 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) { + auto getFilename = [this](StringRef filename) -> StringRef { + if (ctx.config.vfs) + if (auto statOrErr = ctx.config.vfs->status(filename)) + return saver().save(statOrErr->getName()); + return filename; + }; + bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); if (hasPathSep) - return filename; + return getFilename(filename); bool hasExt = filename.contains('.'); for (StringRef dir : searchPaths) { SmallString<128> path = dir; sys::path::append(path, filename); + path = SmallString<128>{getFilename(path.str())}; if (sys::fs::exists(path.str())) - return saver.save(path.str()); + return saver().save(path.str()); if (!hasExt) { path.append(".obj"); + path = SmallString<128>{getFilename(path.str())}; if (sys::fs::exists(path.str())) - return saver.save(path.str()); + return saver().save(path.str()); } } return filename; } -static Optional getUniqueID(StringRef path) { +static std::optional getUniqueID(StringRef path) { sys::fs::UniqueID ret; if (sys::fs::getUniqueID(path, ret)) - return None; + return std::nullopt; return ret; } // Resolves a file path. This never returns the same path -// (in that case, it returns None). -Optional LinkerDriver::findFile(StringRef filename) { +// (in that case, it returns std::nullopt). +std::optional LinkerDriver::findFile(StringRef filename) { StringRef path = doFindFile(filename); - if (Optional id = getUniqueID(path)) { + if (std::optional id = getUniqueID(path)) { bool seen = !visitedFiles.insert(*id).second; if (seen) - return None; + return std::nullopt; } if (path.endswith_insensitive(".lib")) - visitedLibs.insert(std::string(sys::path::filename(path))); + visitedLibs.insert(std::string(sys::path::filename(path).lower())); return path; } @@ -492,7 +507,7 @@ StringRef LinkerDriver::doFindLibMinGW(StringRef filename) { SmallString<128> s = filename; sys::path::replace_extension(s, ".a"); - StringRef libName = saver.save("lib" + s.str()); + StringRef libName = saver().save("lib" + s.str()); return doFindFile(libName); } @@ -501,40 +516,135 @@ 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"); + 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) + if (ctx.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; +// it returns std::nullopt). +std::optional LinkerDriver::findLib(StringRef filename) { + if (ctx.config.noDefaultLibAll) + return std::nullopt; if (!visitedLibs.insert(filename.lower()).second) - return None; + return std::nullopt; StringRef path = doFindLib(filename); - if (config->noDefaultLibs.count(path.lower())) - return None; + if (ctx.config.noDefaultLibs.count(path.lower())) + return std::nullopt; - if (Optional id = getUniqueID(path)) + if (std::optional id = getUniqueID(path)) if (!visitedFiles.insert(*id).second) - return None; + return std::nullopt; return path; } +void LinkerDriver::detectWinSysRoot(const opt::InputArgList &Args) { + IntrusiveRefCntPtr VFS = vfs::getRealFileSystem(); + + // Check the command line first, that's the user explicitly telling us what to + // use. Check the environment next, in case we're being invoked from a VS + // command prompt. Failing that, just try to find the newest Visual Studio + // version we can and use its default VC toolchain. + std::optional VCToolsDir, VCToolsVersion, WinSysRoot; + if (auto *A = Args.getLastArg(OPT_vctoolsdir)) + VCToolsDir = A->getValue(); + if (auto *A = Args.getLastArg(OPT_vctoolsversion)) + VCToolsVersion = A->getValue(); + if (auto *A = Args.getLastArg(OPT_winsysroot)) + WinSysRoot = A->getValue(); + if (!findVCToolChainViaCommandLine(*VFS, VCToolsDir, VCToolsVersion, + WinSysRoot, vcToolChainPath, vsLayout) && + (Args.hasArg(OPT_lldignoreenv) || + !findVCToolChainViaEnvironment(*VFS, vcToolChainPath, vsLayout)) && + !findVCToolChainViaSetupConfig(*VFS, vcToolChainPath, vsLayout) && + !findVCToolChainViaRegistry(vcToolChainPath, vsLayout)) + return; + + // If the VC environment hasn't been configured (perhaps because the user did + // not run vcvarsall), try to build a consistent link environment. If the + // environment variable is set however, assume the user knows what they're + // doing. If the user passes /vctoolsdir or /winsdkdir, trust that over env + // vars. + if (const auto *A = Args.getLastArg(OPT_diasdkdir, OPT_winsysroot)) { + diaPath = A->getValue(); + if (A->getOption().getID() == OPT_winsysroot) + path::append(diaPath, "DIA SDK"); + } + useWinSysRootLibPath = Args.hasArg(OPT_lldignoreenv) || + !Process::GetEnv("LIB") || + Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot); + if (Args.hasArg(OPT_lldignoreenv) || !Process::GetEnv("LIB") || + Args.getLastArg(OPT_winsdkdir, OPT_winsysroot)) { + std::optional WinSdkDir, WinSdkVersion; + if (auto *A = Args.getLastArg(OPT_winsdkdir)) + WinSdkDir = A->getValue(); + if (auto *A = Args.getLastArg(OPT_winsdkversion)) + WinSdkVersion = A->getValue(); + + if (useUniversalCRT(vsLayout, vcToolChainPath, getArch(), *VFS)) { + std::string UniversalCRTSdkPath; + std::string UCRTVersion; + if (getUniversalCRTSdkDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot, + UniversalCRTSdkPath, UCRTVersion)) { + universalCRTLibPath = UniversalCRTSdkPath; + path::append(universalCRTLibPath, "Lib", UCRTVersion, "ucrt"); + } + } + + std::string sdkPath; + std::string windowsSDKIncludeVersion; + std::string windowsSDKLibVersion; + if (getWindowsSDKDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot, sdkPath, + sdkMajor, windowsSDKIncludeVersion, + windowsSDKLibVersion)) { + windowsSdkLibPath = sdkPath; + path::append(windowsSdkLibPath, "Lib"); + if (sdkMajor >= 8) + path::append(windowsSdkLibPath, windowsSDKLibVersion, "um"); + } + } +} + +void LinkerDriver::addWinSysRootLibSearchPaths() { + if (!diaPath.empty()) { + // The DIA SDK always uses the legacy vc arch, even in new MSVC versions. + path::append(diaPath, "lib", archToLegacyVCArch(getArch())); + searchPaths.push_back(saver().save(diaPath.str())); + } + if (useWinSysRootLibPath) { + searchPaths.push_back(saver().save(getSubDirectoryPath( + SubDirectoryType::Lib, vsLayout, vcToolChainPath, getArch()))); + searchPaths.push_back(saver().save( + getSubDirectoryPath(SubDirectoryType::Lib, vsLayout, vcToolChainPath, + getArch(), "atlmfc"))); + } + if (!universalCRTLibPath.empty()) { + StringRef ArchName = archToWindowsSDKArch(getArch()); + if (!ArchName.empty()) { + path::append(universalCRTLibPath, ArchName); + searchPaths.push_back(saver().save(universalCRTLibPath.str())); + } + } + if (!windowsSdkLibPath.empty()) { + std::string path; + if (appendArchToWindowsSDKLibPath(sdkMajor, windowsSdkLibPath, getArch(), + path)) + searchPaths.push_back(saver().save(path)); + } +} + // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { - Optional envOpt = Process::GetEnv("LIB"); - if (!envOpt.hasValue()) + std::optional envOpt = Process::GetEnv("LIB"); + if (!envOpt) return; - StringRef env = saver.save(*envOpt); + StringRef env = saver().save(*envOpt); while (!env.empty()) { StringRef path; std::tie(path, env) = env.split(';'); @@ -543,10 +653,10 @@ void LinkerDriver::addLibSearchPaths() { } Symbol *LinkerDriver::addUndefined(StringRef name) { - Symbol *b = symtab->addUndefined(name); + Symbol *b = ctx.symtab.addUndefined(name); if (!b->isGCRoot) { b->isGCRoot = true; - config->gcroot.push_back(b); + ctx.config.gcroot.push_back(b); } return b; } @@ -558,14 +668,14 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) { return ""; // Otherwise, see if a similar, mangled symbol exists in the symbol table. - Symbol *mangled = symtab->findMangle(unmangled->getName()); + Symbol *mangled = ctx.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()); + unmangled->weakAlias = ctx.symtab.addUndefined(mangled->getName()); return mangled->getName(); } @@ -575,15 +685,15 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) { // 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 && + assert(ctx.config.subsystem != IMAGE_SUBSYSTEM_UNKNOWN && "must handle /subsystem before calling this"); - if (config->mingw) - return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI + if (ctx.config.mingw) + return mangle(ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? "WinMainCRTStartup" : "mainCRTStartup"); - if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { + if (ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { if (findUnderscoreMangle("wWinMain")) { if (!findUnderscoreMangle("WinMain")) return mangle("wWinMainCRTStartup"); @@ -600,9 +710,9 @@ StringRef LinkerDriver::findDefaultEntry() { } WindowsSubsystem LinkerDriver::inferSubsystem() { - if (config->dll) + if (ctx.config.dll) return IMAGE_SUBSYSTEM_WINDOWS_GUI; - if (config->mingw) + if (ctx.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 @@ -624,10 +734,10 @@ WindowsSubsystem LinkerDriver::inferSubsystem() { return IMAGE_SUBSYSTEM_UNKNOWN; } -static uint64_t getDefaultImageBase() { - if (config->is64()) - return config->dll ? 0x180000000 : 0x140000000; - return config->dll ? 0x10000000 : 0x400000; +uint64_t LinkerDriver::getDefaultImageBase() { + if (ctx.config.is64()) + return ctx.config.dll ? 0x180000000 : 0x140000000; + return ctx.config.dll ? 0x10000000 : 0x400000; } static std::string rewritePath(StringRef s) { @@ -651,15 +761,11 @@ static std::string createResponseFile(const opt::InputArgList &args, 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: + case OPT_winsysroot: break; case OPT_call_graph_ordering_file: case OPT_deffile: + case OPT_manifestinput: case OPT_natvis: os << arg->getSpelling() << quote(rewritePath(arg->getValue())) << '\n'; break; @@ -677,6 +783,7 @@ static std::string createResponseFile(const opt::InputArgList &args, break; } case OPT_implib: + case OPT_manifestfile: case OPT_pdb: case OPT_pdbstripped: case OPT_out: @@ -771,8 +878,9 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) { return debugTypes; } -static std::string getMapFile(const opt::InputArgList &args, - opt::OptSpecifier os, opt::OptSpecifier osFile) { +std::string LinkerDriver::getMapFile(const opt::InputArgList &args, + opt::OptSpecifier os, + opt::OptSpecifier osFile) { auto *arg = args.getLastArg(os, osFile); if (!arg) return ""; @@ -780,14 +888,14 @@ static std::string getMapFile(const opt::InputArgList &args, return arg->getValue(); assert(arg->getOption().getID() == os.getID()); - StringRef outFile = config->outputFile; + StringRef outFile = ctx.config.outputFile; return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); } -static std::string getImplibPath() { - if (!config->implib.empty()) - return std::string(config->implib); - SmallString<128> out = StringRef(config->outputFile); +std::string LinkerDriver::getImplibPath() { + if (!ctx.config.implib.empty()) + return std::string(ctx.config.implib); + SmallString<128> out = StringRef(ctx.config.outputFile); sys::path::replace_extension(out, ".lib"); return std::string(out.str()); } @@ -799,30 +907,31 @@ static std::string getImplibPath() { // LINK | {value} | {value}.{.dll/.exe} | {output name} // LIB | {value} | {value}.dll | {output name}.dll // -static std::string getImportName(bool asLib) { +std::string LinkerDriver::getImportName(bool asLib) { SmallString<128> out; - if (config->importName.empty()) { - out.assign(sys::path::filename(config->outputFile)); + if (ctx.config.importName.empty()) { + out.assign(sys::path::filename(ctx.config.outputFile)); if (asLib) sys::path::replace_extension(out, ".dll"); } else { - out.assign(config->importName); + out.assign(ctx.config.importName); if (!sys::path::has_extension(out)) sys::path::replace_extension(out, - (config->dll || asLib) ? ".dll" : ".exe"); + (ctx.config.dll || asLib) ? ".dll" : ".exe"); } return std::string(out.str()); } -static void createImportLibrary(bool asLib) { +void LinkerDriver::createImportLibrary(bool asLib) { std::vector exports; - for (Export &e1 : config->exports) { + for (Export &e1 : ctx.config.exports) { COFFShortExport e2; e2.Name = std::string(e1.name); e2.SymbolName = std::string(e1.symbolName); e2.ExtName = std::string(e1.extName); + e2.AliasTarget = std::string(e1.aliasTarget); e2.Ordinal = e1.ordinal; e2.Noname = e1.noname; e2.Data = e1.data; @@ -831,16 +940,12 @@ static void createImportLibrary(bool asLib) { 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)); + if (!ctx.config.incremental) { + checkError(writeImportLibrary(libName, path, exports, ctx.config.machine, + ctx.config.mingw)); return; } @@ -849,8 +954,8 @@ static void createImportLibrary(bool asLib) { ErrorOr> oldBuf = MemoryBuffer::getFile( path, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (!oldBuf) { - handleError(writeImportLibrary(libName, path, exports, config->machine, - config->mingw)); + checkError(writeImportLibrary(libName, path, exports, ctx.config.machine, + ctx.config.mingw)); return; } @@ -860,9 +965,9 @@ static void createImportLibrary(bool asLib) { 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)); + if (Error e = writeImportLibrary(libName, tmpName, exports, + ctx.config.machine, ctx.config.mingw)) { + checkError(std::move(e)); return; } @@ -870,45 +975,45 @@ static void createImportLibrary(bool asLib) { tmpName, /*IsText=*/false, /*RequiresNullTerminator=*/false)); if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { oldBuf->reset(); - handleError(errorCodeToError(sys::fs::rename(tmpName, path))); + checkError(errorCodeToError(sys::fs::rename(tmpName, path))); } else { sys::fs::remove(tmpName); } } -static void parseModuleDefs(StringRef path) { +void LinkerDriver::parseModuleDefs(StringRef path) { std::unique_ptr mb = CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, /*RequiresNullTerminator=*/false, /*IsVolatile=*/true), "could not open " + path); COFFModuleDefinition m = check(parseCOFFModuleDefinition( - mb->getMemBufferRef(), config->machine, config->mingw)); + mb->getMemBufferRef(), ctx.config.machine, ctx.config.mingw)); // Include in /reproduce: output if applicable. - driver->takeBuffer(std::move(mb)); + ctx.driver.takeBuffer(std::move(mb)); - if (config->outputFile.empty()) - config->outputFile = std::string(saver.save(m.OutputFile)); - config->importName = std::string(saver.save(m.ImportName)); + if (ctx.config.outputFile.empty()) + ctx.config.outputFile = std::string(saver().save(m.OutputFile)); + ctx.config.importName = std::string(saver().save(m.ImportName)); if (m.ImageBase) - config->imageBase = m.ImageBase; + ctx.config.imageBase = m.ImageBase; if (m.StackReserve) - config->stackReserve = m.StackReserve; + ctx.config.stackReserve = m.StackReserve; if (m.StackCommit) - config->stackCommit = m.StackCommit; + ctx.config.stackCommit = m.StackCommit; if (m.HeapReserve) - config->heapReserve = m.HeapReserve; + ctx.config.heapReserve = m.HeapReserve; if (m.HeapCommit) - config->heapCommit = m.HeapCommit; + ctx.config.heapCommit = m.HeapCommit; if (m.MajorImageVersion) - config->majorImageVersion = m.MajorImageVersion; + ctx.config.majorImageVersion = m.MajorImageVersion; if (m.MinorImageVersion) - config->minorImageVersion = m.MinorImageVersion; + ctx.config.minorImageVersion = m.MinorImageVersion; if (m.MajorOSVersion) - config->majorOSVersion = m.MajorOSVersion; + ctx.config.majorOSVersion = m.MajorOSVersion; if (m.MinorOSVersion) - config->minorOSVersion = m.MinorOSVersion; + ctx.config.minorOSVersion = m.MinorOSVersion; for (COFFShortExport e1 : m.Exports) { Export e2; @@ -918,19 +1023,20 @@ static void parseModuleDefs(StringRef path) { // DLL instead. This is supported by both MS and GNU linkers. if (!e1.ExtName.empty() && 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); + e2.name = saver().save(e1.ExtName); + e2.forwardTo = saver().save(e1.Name); + ctx.config.exports.push_back(e2); continue; } - e2.name = saver.save(e1.Name); - e2.extName = saver.save(e1.ExtName); + e2.name = saver().save(e1.Name); + e2.extName = saver().save(e1.ExtName); + e2.aliasTarget = saver().save(e1.AliasTarget); 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); + ctx.config.exports.push_back(e2); } } @@ -939,7 +1045,7 @@ void LinkerDriver::enqueueTask(std::function task) { } bool LinkerDriver::run() { - ScopedTimer t(inputFileTimer); + ScopedTimer t(ctx.inputFileTimer); bool didWork = !taskQueue.empty(); while (!taskQueue.empty()) { @@ -952,7 +1058,7 @@ bool LinkerDriver::run() { // 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) { +void LinkerDriver::parseOrderFile(StringRef arg) { // For some reason, the MSVC linker requires a filename to be // preceded by "@". if (!arg.startswith("@")) { @@ -962,7 +1068,7 @@ static void parseOrderFile(StringRef arg) { // Get a list of all comdat sections for error checking. DenseSet set; - for (Chunk *c : symtab->getChunks()) + for (Chunk *c : ctx.symtab.getChunks()) if (auto *sec = dyn_cast(c)) if (sec->sym) set.insert(sec->sym->getName()); @@ -981,22 +1087,22 @@ static void parseOrderFile(StringRef arg) { // end of an output section. for (StringRef arg : args::getLines(mb->getMemBufferRef())) { std::string s(arg); - if (config->machine == I386 && !isDecorated(s)) + if (ctx.config.machine == I386 && !isDecorated(s)) s = "_" + s; if (set.count(s) == 0) { - if (config->warnMissingOrderSymbol) + if (ctx.config.warnMissingOrderSymbol) warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]"); } else - config->order[s] = INT_MIN + config->order.size(); + ctx.config.order[s] = INT_MIN + ctx.config.order.size(); } // Include in /reproduce: output if applicable. - driver->takeBuffer(std::move(mb)); + ctx.driver.takeBuffer(std::move(mb)); } -static void parseCallGraphFile(StringRef path) { +void LinkerDriver::parseCallGraphFile(StringRef path) { std::unique_ptr mb = CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, /*RequiresNullTerminator=*/false, @@ -1005,7 +1111,7 @@ static void parseCallGraphFile(StringRef path) { // Build a map from symbol name to section. DenseMap map; - for (ObjFile *file : ObjFile::instances) + for (ObjFile *file : ctx.objFileInstances) for (Symbol *sym : file->getSymbols()) if (sym) map[sym->getName()] = sym; @@ -1013,7 +1119,7 @@ static void parseCallGraphFile(StringRef path) { auto findSection = [&](StringRef name) -> SectionChunk * { Symbol *sym = map.lookup(name); if (!sym) { - if (config->warnMissingOrderSymbol) + if (ctx.config.warnMissingOrderSymbol) warn(path + ": no such symbol: " + name); return nullptr; } @@ -1035,15 +1141,15 @@ static void parseCallGraphFile(StringRef path) { if (SectionChunk *from = findSection(fields[0])) if (SectionChunk *to = findSection(fields[1])) - config->callGraphProfile[{from, to}] += count; + ctx.config.callGraphProfile[{from, to}] += count; } // Include in /reproduce: output if applicable. - driver->takeBuffer(std::move(mb)); + ctx.driver.takeBuffer(std::move(mb)); } -static void readCallGraphsFromObjectFiles() { - for (ObjFile *obj : ObjFile::instances) { +static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) { + for (ObjFile *obj : ctx.objFileInstances) { if (obj->callgraphSec) { ArrayRef contents; cantFail( @@ -1065,7 +1171,7 @@ static void readCallGraphsFromObjectFiles() { auto *from = dyn_cast_or_null(fromSym->getChunk()); auto *to = dyn_cast_or_null(toSym->getChunk()); if (from && to) - config->callGraphProfile[{from, to}] += count; + ctx.config.callGraphProfile[{from, to}] += count; } } } @@ -1077,15 +1183,15 @@ static void markAddrsig(Symbol *s) { c->keepUnique = true; } -static void findKeepUniqueSections() { +static void findKeepUniqueSections(COFFLinkerContext &ctx) { // Exported symbols could be address-significant in other executables or DSOs, // so we conservatively mark them as address-significant. - for (Export &r : config->exports) + for (Export &r : ctx.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) { + for (ObjFile *obj : ctx.objFileInstances) { ArrayRef syms = obj->getSymbols(); if (obj->addrsigSec) { ArrayRef contents; @@ -1118,12 +1224,12 @@ static void findKeepUniqueSections() { // binary). // lld only supports %_PDB% and %_EXT% and warns on references to all other env // vars. -static void parsePDBAltPath(StringRef altPath) { +void LinkerDriver::parsePDBAltPath() { SmallString<128> buf; StringRef pdbBasename = - sys::path::filename(config->pdbPath, sys::path::Style::windows); + sys::path::filename(ctx.config.pdbPath, sys::path::Style::windows); StringRef binaryExtension = - sys::path::extension(config->outputFile, sys::path::Style::windows); + sys::path::extension(ctx.config.outputFile, sys::path::Style::windows); if (!binaryExtension.empty()) binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. @@ -1134,19 +1240,22 @@ static void parsePDBAltPath(StringRef altPath) { // v v v // a...%...%... size_t cursor = 0; - while (cursor < altPath.size()) { + while (cursor < ctx.config.pdbAltPath.size()) { size_t firstMark, secondMark; - if ((firstMark = altPath.find('%', cursor)) == StringRef::npos || - (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) { + if ((firstMark = ctx.config.pdbAltPath.find('%', cursor)) == + StringRef::npos || + (secondMark = ctx.config.pdbAltPath.find('%', firstMark + 1)) == + StringRef::npos) { // Didn't find another full fragment, treat rest of string as literal. - buf.append(altPath.substr(cursor)); + buf.append(ctx.config.pdbAltPath.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); + buf.append(ctx.config.pdbAltPath.substr(cursor, firstMark - cursor)); + StringRef var = + ctx.config.pdbAltPath.substr(firstMark, secondMark - firstMark + 1); if (var.equals_insensitive("%_pdb%")) buf.append(pdbBasename); else if (var.equals_insensitive("%_ext%")) @@ -1160,7 +1269,7 @@ static void parsePDBAltPath(StringRef altPath) { cursor = secondMark + 1; } - config->pdbAltPath = buf; + ctx.config.pdbAltPath = buf; } /// Convert resource files and potentially merge input resource object @@ -1169,12 +1278,12 @@ static void parsePDBAltPath(StringRef altPath) { void LinkerDriver::convertResources() { std::vector resourceObjFiles; - for (ObjFile *f : ObjFile::instances) { + for (ObjFile *f : ctx.objFileInstances) { if (f->isResourceObjFile()) resourceObjFiles.push_back(f); } - if (!config->mingw && + if (!ctx.config.mingw && (resourceObjFiles.size() > 1 || (resourceObjFiles.size() == 1 && !resources.empty()))) { error((!resources.empty() ? "internal .obj file created from .res files" @@ -1191,8 +1300,9 @@ void LinkerDriver::convertResources() { f->includeResourceChunks(); return; } - ObjFile *f = make(convertResToCOFF(resources, resourceObjFiles)); - symtab->addFile(f); + ObjFile *f = + make(ctx, convertResToCOFF(resources, resourceObjFiles)); + ctx.symtab.addFile(f); f->includeResourceChunks(); } @@ -1204,29 +1314,36 @@ void LinkerDriver::convertResources() { // than MinGW in the case that nothing is explicitly exported. void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!args.hasArg(OPT_export_all_symbols)) { - if (!config->dll) + if (!ctx.config.dll) return; - if (!config->exports.empty()) + if (!ctx.config.exports.empty()) return; if (args.hasArg(OPT_exclude_all_symbols)) return; } - AutoExporter exporter; + AutoExporter exporter(ctx, excludedSymbols); for (auto *arg : args.filtered(OPT_wholearchive_file)) - if (Optional path = doFindFile(arg->getValue())) + if (std::optional path = doFindFile(arg->getValue())) exporter.addWholeArchive(*path); - symtab->forEachSymbol([&](Symbol *s) { + for (auto *arg : args.filtered(OPT_exclude_symbols)) { + SmallVector vec; + StringRef(arg->getValue()).split(vec, ','); + for (StringRef sym : vec) + exporter.addExcludedSymbol(mangle(sym)); + } + + ctx.symtab.forEachSymbol([&](Symbol *s) { auto *def = dyn_cast(s); if (!exporter.shouldExport(def)) return; if (!def->isGCRoot) { def->isGCRoot = true; - config->gcroot.push_back(def); + ctx.config.gcroot.push_back(def); } Export e; @@ -1236,7 +1353,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) e.data = true; s->isUsedInRegularObj = true; - config->exports.push_back(e); + ctx.config.exports.push_back(e); }); } @@ -1247,7 +1364,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { // /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) { +std::optional getReproduceFile(const opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_reproduce)) return std::string(arg->getValue()); @@ -1262,11 +1379,34 @@ Optional getReproduceFile(const opt::InputArgList &args) { if (auto *path = getenv("LLD_REPRODUCE")) return std::string(path); - return None; + return std::nullopt; +} + +static std::unique_ptr +getVFS(const opt::InputArgList &args) { + using namespace llvm::vfs; + + const opt::Arg *arg = args.getLastArg(OPT_vfsoverlay); + if (!arg) + return nullptr; + + auto bufOrErr = llvm::MemoryBuffer::getFile(arg->getValue()); + if (!bufOrErr) { + checkError(errorCodeToError(bufOrErr.getError())); + return nullptr; + } + + if (auto ret = vfs::getVFSFromYAML(std::move(*bufOrErr), /*DiagHandler*/ nullptr, + arg->getValue())) + return ret; + + error("Invalid vfs overlay"); + return nullptr; } void LinkerDriver::linkerMain(ArrayRef argsArr) { - ScopedTimer rootTimer(Timer::root()); + ScopedTimer rootTimer(ctx.rootTimer); + Configuration *config = &ctx.config; // Needed for LTO. InitializeAllTargetInfos(); @@ -1286,14 +1426,16 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } // Parse command line options. - ArgParser parser; + ArgParser parser(ctx); 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)) + for (const auto *arg : args.filtered(OPT_mllvm)) { v.push_back(arg->getValue()); + config->mllvmOpts.emplace_back(arg->getValue()); + } cl::ResetAllOptionOccurrences(); cl::ParseCommandLineOptions(v.size(), v.data()); @@ -1306,6 +1448,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { errorHandler().errorLimit = n; } + config->vfs = getVFS(args); + // Handle /help if (args.hasArg(OPT_help)) { printHelp(argsArr[0]); @@ -1340,9 +1484,12 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle /lldmingw early, since it can potentially affect how other // options are handled. config->mingw = args.hasArg(OPT_lldmingw); + if (config->mingw) + ctx.e.errorLimitExceededMsg = "too many errors emitted, stopping now" + " (use --error-limit=0 to see all errors)"; // Handle /linkrepro and /reproduce. - if (Optional path = getReproduceFile(args)) { + if (std::optional path = getReproduceFile(args)) { Expected> errOrWriter = TarWriter::create(*path, sys::path::stem(*path)); @@ -1365,7 +1512,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { searchPaths.push_back(""); for (auto *arg : args.filtered(OPT_libpath)) searchPaths.push_back(arg->getValue()); - if (!args.hasArg(OPT_lldignoreenv)) + detectWinSysRoot(args); + if (!args.hasArg(OPT_lldignoreenv) && !args.hasArg(OPT_winsysroot)) addLibSearchPaths(); // Handle /ignore @@ -1415,7 +1563,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } // Handle /demangle - config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); + config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no, true); // Handle /debugtype config->debugTypes = parseDebugTypes(args); @@ -1439,6 +1587,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { config->pdbPath = arg->getValue(); if (auto *arg = args.getLastArg(OPT_pdbaltpath)) config->pdbAltPath = arg->getValue(); + if (auto *arg = args.getLastArg(OPT_pdbpagesize)) + parsePDBPageSize(arg->getValue()); if (args.hasArg(OPT_natvis)) config->natvisFiles = args.getAllArgValues(OPT_natvis); if (args.hasArg(OPT_pdbstream)) { @@ -1503,6 +1653,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { config->machine = getMachineType(arg->getValue()); if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) fatal(Twine("unknown /machine argument: ") + arg->getValue()); + addWinSysRootLibSearchPaths(); } // Handle /nodefaultlib: @@ -1585,13 +1736,14 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (auto *arg = args.getLastArg(OPT_implib)) config->implib = arg->getValue(); + config->noimplib = args.hasArg(OPT_noimplib); + // Handle /opt. bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); - Optional icfLevel = None; + std::optional icfLevel; if (args.hasArg(OPT_profile)) icfLevel = ICFLevel::None; unsigned tailMerge = 1; - bool ltoNewPM = LLVM_ENABLE_NEW_PASS_MANAGER; bool ltoDebugPM = false; for (auto *arg : args.filtered(OPT_opt)) { std::string str = StringRef(arg->getValue()).lower(); @@ -1613,9 +1765,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } else if (s == "nolldtailmerge") { tailMerge = 0; } else if (s == "ltonewpassmanager") { - ltoNewPM = true; - } else if (s == "noltonewpassmanager") { - ltoNewPM = false; + /* We always use the new PM. */ } else if (s == "ltodebugpassmanager") { ltoDebugPM = true; } else if (s == "noltodebugpassmanager") { @@ -1642,10 +1792,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (!icfLevel) icfLevel = doGC ? ICFLevel::All : ICFLevel::None; config->doGC = doGC; - config->doICF = icfLevel.getValue(); + config->doICF = *icfLevel; config->tailMerge = (tailMerge == 1 && config->doICF != ICFLevel::None) || tailMerge == 2; - config->ltoNewPassManager = ltoNewPM; config->ltoDebugPassManager = ltoDebugPM; // Handle /lldsavetemps @@ -1705,12 +1854,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { 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 /manifestdependency. + for (auto *arg : args.filtered(OPT_manifestdependency)) + config->manifestDependencies.insert(arg->getValue()); // Handle /manifest and /manifest: if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) { @@ -1750,6 +1896,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); // Handle miscellaneous boolean flags. + config->ltoPGOWarnMismatch = args.hasFlag(OPT_lto_pgo_warn_mismatch, + OPT_lto_pgo_warn_mismatch_no, true); config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); config->allowIsolation = args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); @@ -1778,20 +1926,14 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, config->mingw); config->warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup); + if (args.hasFlag(OPT_inferasanlibs, OPT_inferasanlibs_no, false)) + warn("ignoring '/inferasanlibs', this flag is not supported"); + // Don't warn about long section names, such as .debug_info, for mingw or // when -debug:dwarf is requested. if (config->mingw || config->debugDwarf) config->warnLongSectionNames = false; - config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file); - config->mapFile = getMapFile(args, OPT_map, OPT_map_file); - - if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) { - warn("/lldmap and /map have the same output file '" + config->mapFile + - "'.\n>>> ignoring /lldmap"); - config->lldmapFile.clear(); - } - if (config->incremental && args.hasArg(OPT_profile)) { warn("ignoring '/incremental' due to '/profile' specification"); config->incremental = false; @@ -1819,8 +1961,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { std::set wholeArchives; for (auto *arg : args.filtered(OPT_wholearchive_file)) - if (Optional path = doFindFile(arg->getValue())) - if (Optional id = getUniqueID(*path)) + if (std::optional path = doFindFile(arg->getValue())) + if (std::optional id = getUniqueID(*path)) wholeArchives.insert(*id); // A predicate returning true if a given path is an argument for @@ -1830,7 +1972,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { auto isWholeArchive = [&](StringRef path) -> bool { if (args.hasArg(OPT_wholearchive_flag)) return true; - if (Optional id = getUniqueID(path)) + if (std::optional id = getUniqueID(path)) return wholeArchives.count(*id); return false; }; @@ -1852,11 +1994,11 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { inLib = true; break; case OPT_wholearchive_file: - if (Optional path = findFile(arg->getValue())) + if (std::optional path = findFile(arg->getValue())) enqueuePath(*path, true, inLib); break; case OPT_INPUT: - if (Optional path = findFile(arg->getValue())) + if (std::optional path = findFile(arg->getValue())) enqueuePath(*path, isWholeArchive(*path), inLib); break; default: @@ -1865,19 +2007,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } } - // 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, false); - - // Windows specific -- Create a resource file containing a manifest file. - if (config->manifest == Configuration::Embed) - addBuffer(createManifestRes(), false, false); - // Read all input files given via the command line. run(); - if (errorCount()) return; @@ -1886,9 +2017,23 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); config->machine = AMD64; + addWinSysRootLibSearchPaths(); } config->wordsize = config->is64() ? 8 : 4; + // Process files specified as /defaultlib. These must be processed after + // addWinSysRootLibSearchPaths(), which is why they are in a separate loop. + for (auto *arg : args.filtered(OPT_defaultlib)) + if (std::optional path = findLib(arg->getValue())) + enqueuePath(*path, false, false); + run(); + if (errorCount()) + return; + + // Handle /RELEASE + if (args.hasArg(OPT_release)) + config->writeCheckSum = true; + // Handle /safeseh, x86 only, on by default, except for mingw. if (config->machine == I386) { config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw); @@ -1897,12 +2042,13 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle /functionpadmin for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) - parseFunctionPadMin(arg, config->machine); + parseFunctionPadMin(arg); - if (tar) + if (tar) { tar->append("response.txt", createResponseFile(args, filePaths, ArrayRef(searchPaths).slice(1))); + } // Handle /largeaddressaware config->largeAddressAware = args.hasFlag( @@ -1923,9 +2069,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { Export e = parseExport(arg->getValue()); if (config->machine == I386) { if (!isDecorated(e.name)) - e.name = saver.save("_" + e.name); + e.name = saver().save("_" + e.name); if (!e.extName.empty() && !isDecorated(e.extName)) - e.extName = saver.save("_" + e.extName); + e.extName = saver().save("_" + e.extName); } config->exports.push_back(e); } @@ -1939,7 +2085,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle generation of import library from a def file. if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) { fixupExports(); - createImportLibrary(/*asLib=*/true); + if (!config->noimplib) + createImportLibrary(/*asLib=*/true); return; } @@ -1987,7 +2134,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Set default image name if neither /out or /def set it. if (config->outputFile.empty()) { config->outputFile = getOutputPath( - (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue()); + (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue(), + config->dll, config->driver); } // Fail early if an output file is not writable. @@ -1996,6 +2144,25 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { return; } + config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file); + config->mapFile = getMapFile(args, OPT_map, OPT_map_file); + + if (config->mapFile != "" && args.hasArg(OPT_map_info)) { + for (auto *arg : args.filtered(OPT_map_info)) { + std::string s = StringRef(arg->getValue()).lower(); + if (s == "exports") + config->mapInfo = true; + else + error("unknown option: /mapinfo:" + s); + } + } + + if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) { + warn("/lldmap and /map have the same output file '" + config->mapFile + + "'.\n>>> ignoring /lldmap"); + config->lldmapFile.clear(); + } + if (shouldCreatePDB) { // Put the PDB next to the image if no /pdb flag was passed. if (config->pdbPath.empty()) { @@ -2014,8 +2181,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { 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); + // Don't do this earlier, so that ctx.OutputFile is ready. + parsePDBAltPath(); } } @@ -2023,32 +2190,32 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (config->imageBase == uint64_t(-1)) config->imageBase = getDefaultImageBase(); - symtab->addSynthetic(mangle("__ImageBase"), nullptr); + ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr); if (config->machine == I386) { - symtab->addAbsolute("___safe_se_handler_table", 0); - symtab->addAbsolute("___safe_se_handler_count", 0); + ctx.symtab.addAbsolute("___safe_se_handler_table", 0); + ctx.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); + ctx.symtab.addAbsolute(mangle("__guard_fids_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_fids_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_flags"), 0); + ctx.symtab.addAbsolute(mangle("__guard_iat_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_iat_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_longjmp_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_longjmp_table"), 0); // Needed for MSVC 2017 15.5 CRT. - symtab->addAbsolute(mangle("__enclave_config"), 0); + ctx.symtab.addAbsolute(mangle("__enclave_config"), 0); // Needed for MSVC 2019 16.8 CRT. - symtab->addAbsolute(mangle("__guard_eh_cont_count"), 0); - symtab->addAbsolute(mangle("__guard_eh_cont_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0); if (config->pseudoRelocs) { - symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); - symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); + ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); + ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); } if (config->mingw) { - symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); - symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); + ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0); + ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0); } // This code may add new undefined symbols to the link, which may enqueue more @@ -2074,12 +2241,12 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { for (auto pair : config->alternateNames) { StringRef from = pair.first; StringRef to = pair.second; - Symbol *sym = symtab->find(from); + Symbol *sym = ctx.symtab.find(from); if (!sym) continue; if (auto *u = dyn_cast(sym)) if (!u->weakAlias) - u->weakAlias = symtab->addUndefined(to); + u->weakAlias = ctx.symtab.addUndefined(to); } // If any inputs are bitcode files, the LTO code generator may create @@ -2087,25 +2254,24 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // 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()) + if (!ctx.bitcodeFileInstances.empty()) for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) - symtab->addLibcall(s); + ctx.symtab.addLibcall(s); // Windows specific -- if __load_config_used can be resolved, resolve it. - if (symtab->findUnderscore("_load_config_used")) + if (ctx.symtab.findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); - } while (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()))) - addUndefined(arg->getValue()); - while (run()); - } + if (args.hasArg(OPT_include_optional)) { + // Handle /includeoptional + for (auto *arg : args.filtered(OPT_include_optional)) + if (isa_and_nonnull(ctx.symtab.find(arg->getValue()))) + addUndefined(arg->getValue()); + } + } while (run()); // Create wrapped symbols for -wrap option. - std::vector wrapped = addWrappedSymbols(args); + std::vector wrapped = addWrappedSymbols(ctx, args); // Load more object files that might be needed for wrapped symbols. if (!wrapped.empty()) while (run()); @@ -2131,7 +2297,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // If it ends up pulling in more object files from static libraries, // (and maybe doing more stdcall fixups along the way), this would need // to loop these two calls. - symtab->loadMinGWSymbols(); + ctx.symtab.loadMinGWSymbols(); run(); } @@ -2139,8 +2305,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // 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 (!ctx.bitcodeFileInstances.empty() && !config->forceUnresolved) + ctx.symtab.reportUnresolvable(); if (errorCount()) return; @@ -2154,7 +2320,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // 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(); + ctx.symtab.compileBitcodeFiles(); // If -thinlto-index-only is given, we should create only "index // files" and not object files. Index file creation is already done @@ -2168,10 +2334,10 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Apply symbol renames for -wrap. if (!wrapped.empty()) - wrapSymbols(wrapped); + wrapSymbols(ctx, wrapped); // Resolve remaining undefined symbols and warn about imported locals. - symtab->resolveRemainingUndefines(); + ctx.symtab.resolveRemainingUndefines(); if (errorCount()) return; @@ -2182,12 +2348,12 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // 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(); + for (auto i = ctx.objFileInstances.begin(), e = ctx.objFileInstances.end(); i != e; i++) { ObjFile *file = *i; if (isCrtend(file->getName())) { - ObjFile::instances.erase(i); - ObjFile::instances.push_back(file); + ctx.objFileInstances.erase(i); + ctx.objFileInstances.push_back(file); break; } } @@ -2198,21 +2364,21 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // -implib option is given explicitly, for compatibility with GNU ld. if (!config->exports.empty() || config->dll) { fixupExports(); - if (!config->mingw || !config->implib.empty()) + if (!config->noimplib && (!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()); + writeDefFile(arg->getValue(), config->exports); // 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); + Symbol *sym = ctx.symtab.find(name); if (!sym) { warn("/aligncomm symbol " + name + " not found"); continue; @@ -2228,8 +2394,14 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { c->setAlignment(std::max(c->getAlignment(), alignment)); } - // Windows specific -- Create a side-by-side manifest file. - if (config->manifest == Configuration::SideBySide) + // Windows specific -- Create an embedded or side-by-side manifest. + // /manifestdependency: enables /manifest unless an explicit /manifest:no is + // also passed. + if (config->manifest == Configuration::Embed) + addBuffer(createManifestRes(), false, false); + else if (config->manifest == Configuration::SideBySide || + (config->manifest == Configuration::Default && + !config->manifestDependencies.empty())) createSideBySideManifest(); // Handle /order. We want to do this at this moment because we @@ -2247,7 +2419,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) { parseCallGraphFile(arg->getValue()); } - readCallGraphsFromObjectFiles(); + readCallGraphsFromObjectFiles(ctx); } // Handle /print-symbol-order. @@ -2263,8 +2435,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // For now, just manually try to retain the known possible personality // functions. This doesn't bring in more object files, but only marks // functions that already have been included to be retained. - for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0"}) { - Defined *d = dyn_cast_or_null(symtab->findUnderscore(n)); + for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0", + "rust_eh_personality"}) { + Defined *d = dyn_cast_or_null(ctx.symtab.findUnderscore(n)); if (d && !d->isGCRoot) { d->isGCRoot = true; config->gcroot.push_back(d); @@ -2272,7 +2445,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } } - markLive(symtab->getChunks()); + markLive(ctx); } // Needs to happen after the last call to addFile(). @@ -2280,18 +2453,17 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Identify identical COMDAT sections to merge them. if (config->doICF != ICFLevel::None) { - findKeepUniqueSections(); - doICF(symtab->getChunks(), config->doICF); + findKeepUniqueSections(ctx); + doICF(ctx); } // Write the result. - writeResult(); + writeResult(ctx); // Stop early so we can print the results. rootTimer.stop(); if (config->showTiming) - Timer::root().print(); + ctx.rootTimer.print(); } -} // namespace coff -} // namespace lld +} // namespace lld::coff diff --git a/gnu/llvm/lld/COFF/Driver.h b/gnu/llvm/lld/COFF/Driver.h index 5729bed6952..8ec3bb46420 100644 --- a/gnu/llvm/lld/COFF/Driver.h +++ b/gnu/llvm/lld/COFF/Driver.h @@ -13,7 +13,6 @@ #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" @@ -22,29 +21,23 @@ #include "llvm/Option/ArgList.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" +#include "llvm/WindowsDriver/MSVCPaths.h" #include +#include #include #include -namespace lld { -namespace coff { - -class LinkerDriver; -extern LinkerDriver *driver; +namespace lld::coff { using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; -using llvm::Optional; +using std::optional; -class COFFOptTable : public llvm::opt::OptTable { +class COFFOptTable : public llvm::opt::GenericOptTable { public: COFFOptTable(); }; -// Constructing the option table is expensive. Use a global table to avoid doing -// it more than once. -extern COFFOptTable optTable; - // The result of parsing the .drective section. The /export: and /include: // options are handled separately because they reference symbols, and the number // of symbols can be quite large. The LLVM Option library will perform at least @@ -53,11 +46,14 @@ extern COFFOptTable optTable; struct ParsedDirectives { std::vector exports; std::vector includes; + std::vector excludes; llvm::opt::InputArgList args; }; class ArgParser { public: + ArgParser(COFFLinkerContext &ctx); + // Parses command line options. llvm::opt::InputArgList parse(llvm::ArrayRef args); @@ -74,12 +70,20 @@ private: void addLINK(SmallVector &argv); std::vector tokenize(StringRef s); + + COFFLinkerContext &ctx; }; class LinkerDriver { public: + LinkerDriver(COFFLinkerContext &ctx) : ctx(ctx) {} + void linkerMain(llvm::ArrayRef args); + // Adds various search paths based on the sysroot. Must only be called once + // config->machine has been set. + void addWinSysRootLibSearchPaths(); + // Used by the resolver to parse .drectve section contents. void parseDirectives(InputFile *file); @@ -97,12 +101,53 @@ public: private: // Searches a file from search paths. - Optional findFile(StringRef filename); - Optional findLib(StringRef filename); + std::optional findFile(StringRef filename); + std::optional findLib(StringRef filename); StringRef doFindFile(StringRef filename); StringRef doFindLib(StringRef filename); StringRef doFindLibMinGW(StringRef filename); + bool findUnderscoreMangle(StringRef sym); + + // Determines the location of the sysroot based on `args`, environment, etc. + void detectWinSysRoot(const llvm::opt::InputArgList &args); + + // Symbol names are mangled by prepending "_" on x86. + StringRef mangle(StringRef sym); + + llvm::Triple::ArchType getArch(); + + uint64_t getDefaultImageBase(); + + bool isDecorated(StringRef sym); + + std::string getMapFile(const llvm::opt::InputArgList &args, + llvm::opt::OptSpecifier os, + llvm::opt::OptSpecifier osFile); + + std::string getImplibPath(); + + // The import name is calculated as follows: + // + // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY + // -----+----------------+---------------------+------------------ + // LINK | {value} | {value}.{.dll/.exe} | {output name} + // LIB | {value} | {value}.dll | {output name}.dll + // + std::string getImportName(bool asLib); + + void createImportLibrary(bool asLib); + + void parseModuleDefs(StringRef path); + + // Parse an /order file. If an option is given, the linker places COMDAT + // sections int he same order as their names appear in the given file. + void parseOrderFile(StringRef arg); + + void parseCallGraphFile(StringRef path); + + void parsePDBAltPath(); + // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); @@ -147,61 +192,80 @@ private: std::vector filePaths; std::vector resources; - llvm::StringSet<> directivesExports; -}; + llvm::DenseSet directivesExports; + llvm::DenseSet excludedSymbols; + + COFFLinkerContext &ctx; -// Functions below this line are defined in DriverUtils.cpp. + llvm::ToolsetLayout vsLayout = llvm::ToolsetLayout::OlderVS; + std::string vcToolChainPath; + llvm::SmallString<128> diaPath; + bool useWinSysRootLibPath = false; + llvm::SmallString<128> universalCRTLibPath; + int sdkMajor = 0; + llvm::SmallString<128> windowsSdkLibPath; -void printHelp(const char *argv0); + // Functions below this line are defined in DriverUtils.cpp. -// Parses a string in the form of "[,]". -void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); + void printHelp(const char *argv0); -void parseGuard(StringRef arg); + // Parses a string in the form of "[,]". + void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); -// Parses a string in the form of "[.]". -// Minor's default value is 0. -void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); + void parseGuard(StringRef arg); -// Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, - uint32_t *minor, bool *gotVersion = nullptr); + // Parses a string in the form of "[.]". + // Minor's default value is 0. + void parseVersion(StringRef arg, 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 parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor, bool *gotVersion = nullptr); -// Parses a string in the form of "[:]" -void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + void parseAlternateName(StringRef); + void parseMerge(StringRef); + void parsePDBPageSize(StringRef); + void parseSection(StringRef); + void parseAligncomm(StringRef); -// Parses a string in the form of "EMBED[,=]|NO". -void parseManifest(StringRef arg); + // Parses a string in the form of "[:]" + void parseFunctionPadMin(llvm::opt::Arg *a); -// Parses a string in the form of "level=|uiAccess=" -void parseManifestUAC(StringRef arg); + // Parses a string in the form of "EMBED[,=]|NO". + void parseManifest(StringRef arg); -// Parses a string in the form of "cd|net[,(cd|net)]*" -void parseSwaprun(StringRef arg); + // Parses a string in the form of "level=|uiAccess=" + void parseManifestUAC(StringRef arg); -// Create a resource file containing a manifest XML. -std::unique_ptr createManifestRes(); -void createSideBySideManifest(); + // Parses a string in the form of "cd|net[,(cd|net)]*" + void parseSwaprun(StringRef arg); -// Used for dllexported symbols. -Export parseExport(StringRef arg); -void fixupExports(); -void assignExportOrdinals(); + // Create a resource file containing a manifest XML. + std::unique_ptr createManifestRes(); + void createSideBySideManifest(); + std::string createDefaultXml(); + std::string createManifestXmlWithInternalMt(StringRef defaultXml); + std::string createManifestXmlWithExternalMt(StringRef defaultXml); + std::string createManifestXml(); -// 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); + std::unique_ptr + createMemoryBufferForManifestRes(size_t manifestRes); -// Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef mbs, - ArrayRef objs); + // 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, + ArrayRef objs); +}; // Create enum with OPT_xxx values for each option in Options.td enum { @@ -211,7 +275,6 @@ enum { #undef OPTION }; -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/DriverUtils.cpp b/gnu/llvm/lld/COFF/DriverUtils.cpp index b5abe8b1196..d70f53aca5b 100644 --- a/gnu/llvm/lld/COFF/DriverUtils.cpp +++ b/gnu/llvm/lld/COFF/DriverUtils.cpp @@ -12,12 +12,12 @@ // //===----------------------------------------------------------------------===// -#include "Config.h" +#include "COFFLinkerContext.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/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" @@ -34,6 +34,7 @@ #include "llvm/WindowsManifest/WindowsManifestMerger.h" #include #include +#include using namespace llvm::COFF; using namespace llvm; @@ -48,17 +49,17 @@ 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)); } + 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); + StringRef exe = saver().save(*exeOrErr); args.insert(args.begin(), exe); if (sys::ExecuteAndWait(args[0], args) != 0) @@ -74,9 +75,8 @@ private: } // 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(','); +void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { + auto [s1, s2] = arg.split(','); if (s1.getAsInteger(0, *addr)) fatal("invalid number: " + s1); if (size && !s2.empty() && s2.getAsInteger(0, *size)) @@ -85,9 +85,9 @@ void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { // 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('.'); +void LinkerDriver::parseVersion(StringRef arg, uint32_t *major, + uint32_t *minor) { + auto [s1, s2] = arg.split('.'); if (s1.getAsInteger(10, *major)) fatal("invalid number: " + s1); *minor = 0; @@ -95,32 +95,30 @@ void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { fatal("invalid number: " + s2); } -void parseGuard(StringRef fullArg) { +void LinkerDriver::parseGuard(StringRef fullArg) { SmallVector splitArgs; fullArg.split(splitArgs, ","); for (StringRef arg : splitArgs) { if (arg.equals_insensitive("no")) - config->guardCF = GuardCFLevel::Off; + ctx.config.guardCF = GuardCFLevel::Off; else if (arg.equals_insensitive("nolongjmp")) - config->guardCF &= ~GuardCFLevel::LongJmp; + ctx.config.guardCF &= ~GuardCFLevel::LongJmp; else if (arg.equals_insensitive("noehcont")) - config->guardCF &= ~GuardCFLevel::EHCont; - else if (arg.equals_insensitive("cf")) - config->guardCF = GuardCFLevel::CF; - else if (arg.equals_insensitive("longjmp")) - config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; + ctx.config.guardCF &= ~GuardCFLevel::EHCont; + else if (arg.equals_insensitive("cf") || arg.equals_insensitive("longjmp")) + ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; else if (arg.equals_insensitive("ehcont")) - config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; + ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; 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, bool *gotVersion) { - StringRef sysStr, ver; - std::tie(sysStr, ver) = arg.split(','); +void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys, + uint32_t *major, uint32_t *minor, + bool *gotVersion) { + auto [sysStr, ver] = arg.split(','); std::string sysStrLower = sysStr.lower(); *sys = StringSwitch(sysStrLower) .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) @@ -144,29 +142,27 @@ void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, // 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('='); +void LinkerDriver::parseAlternateName(StringRef s) { + auto [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) + auto it = ctx.config.alternateNames.find(from); + if (it != ctx.config.alternateNames.end() && it->second != to) fatal("/alternatename: conflicts: " + s); - config->alternateNames.insert(it, std::make_pair(from, to)); + ctx.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('='); +void LinkerDriver::parseMerge(StringRef s) { + auto [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)); + auto pair = ctx.config.merge.insert(std::make_pair(from, to)); bool inserted = pair.second; if (!inserted) { StringRef existing = pair.first->second; @@ -175,6 +171,20 @@ void parseMerge(StringRef s) { } } +void LinkerDriver::parsePDBPageSize(StringRef s) { + int v; + if (s.getAsInteger(0, v)) { + error("/pdbpagesize: invalid argument: " + s); + return; + } + if (v != 4096 && v != 8192 && v != 16384 && v != 32768) { + error("/pdbpagesize: invalid argument: " + s); + return; + } + + ctx.config.pdbPageSize = v; +} + static uint32_t parseSectionAttributes(StringRef s) { uint32_t ret = 0; for (char c : s.lower()) { @@ -208,18 +218,16 @@ static uint32_t parseSectionAttributes(StringRef s) { } // Parses /section option argument. -void parseSection(StringRef s) { - StringRef name, attrs; - std::tie(name, attrs) = s.split(','); +void LinkerDriver::parseSection(StringRef s) { + auto [name, attrs] = s.split(','); if (name.empty() || attrs.empty()) fatal("/section: invalid argument: " + s); - config->section[name] = parseSectionAttributes(attrs); + ctx.config.section[name] = parseSectionAttributes(attrs); } // Parses /aligncomm option argument. -void parseAligncomm(StringRef s) { - StringRef name, align; - std::tie(name, align) = s.split(','); +void LinkerDriver::parseAligncomm(StringRef s) { + auto [name, align] = s.split(','); if (name.empty() || align.empty()) { error("/aligncomm: invalid argument: " + s); return; @@ -229,56 +237,57 @@ void parseAligncomm(StringRef s) { error("/aligncomm: invalid argument: " + s); return; } - config->alignComm[std::string(name)] = - std::max(config->alignComm[std::string(name)], 1 << v); + ctx.config.alignComm[std::string(name)] = + std::max(ctx.config.alignComm[std::string(name)], 1 << v); } // Parses /functionpadmin option argument. -void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { +void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) { StringRef arg = a->getNumValues() ? a->getValue() : ""; if (!arg.empty()) { // Optional padding in bytes is given. - if (arg.getAsInteger(0, config->functionPadMin)) + if (arg.getAsInteger(0, ctx.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; + if (ctx.config.machine == I386) { + ctx.config.functionPadMin = 5; + } else if (ctx.config.machine == AMD64) { + ctx.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) { +// Results are directly written to +// Config. +void LinkerDriver::parseManifest(StringRef arg) { if (arg.equals_insensitive("no")) { - config->manifest = Configuration::No; + ctx.config.manifest = Configuration::No; return; } if (!arg.startswith_insensitive("embed")) fatal("invalid option " + arg); - config->manifest = Configuration::Embed; + ctx.config.manifest = Configuration::Embed; arg = arg.substr(strlen("embed")); if (arg.empty()) return; if (!arg.startswith_insensitive(",id=")) fatal("invalid option " + arg); arg = arg.substr(strlen(",id=")); - if (arg.getAsInteger(0, config->manifestID)) + if (arg.getAsInteger(0, ctx.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) { +void LinkerDriver::parseManifestUAC(StringRef arg) { if (arg.equals_insensitive("no")) { - config->manifestUAC = false; + ctx.config.manifestUAC = false; return; } for (;;) { @@ -287,12 +296,12 @@ void parseManifestUAC(StringRef arg) { return; if (arg.startswith_insensitive("level=")) { arg = arg.substr(strlen("level=")); - std::tie(config->manifestLevel, arg) = arg.split(" "); + std::tie(ctx.config.manifestLevel, arg) = arg.split(" "); continue; } if (arg.startswith_insensitive("uiaccess=")) { arg = arg.substr(strlen("uiaccess=")); - std::tie(config->manifestUIAccess, arg) = arg.split(" "); + std::tie(ctx.config.manifestUIAccess, arg) = arg.split(" "); continue; } fatal("invalid option " + arg); @@ -301,14 +310,13 @@ void parseManifestUAC(StringRef arg) { // Parses a string in the form of "cd|net[,(cd|net)]*" // Results are directly written to Config. -void parseSwaprun(StringRef arg) { +void LinkerDriver::parseSwaprun(StringRef arg) { do { - StringRef swaprun, newArg; - std::tie(swaprun, newArg) = arg.split(','); + auto [swaprun, newArg] = arg.split(','); if (swaprun.equals_insensitive("cd")) - config->swaprunCD = true; + ctx.config.swaprunCD = true; else if (swaprun.equals_insensitive("net")) - config->swaprunNet = true; + ctx.config.swaprunNet = true; else if (swaprun.empty()) error("/swaprun: missing argument"); else @@ -366,7 +374,7 @@ public: }; } -static std::string createDefaultXml() { +std::string LinkerDriver::createDefaultXml() { std::string ret; raw_string_ostream os(ret); @@ -375,20 +383,20 @@ static std::string createDefaultXml() { os << "\n" << "\n"; - if (config->manifestUAC) { + if (ctx.config.manifestUAC) { os << " \n" << " \n" << " \n" - << " \n" + << " \n" << " \n" << " \n" << " \n"; } - if (!config->manifestDependency.empty()) { + for (auto manifestDependency : ctx.config.manifestDependencies) { os << " \n" << " \n" - << " manifestDependency << " />\n" + << " \n" << " \n" << " \n"; } @@ -396,7 +404,8 @@ static std::string createDefaultXml() { return os.str(); } -static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { +std::string +LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) { std::unique_ptr defaultXmlCopy = MemoryBuffer::getMemBufferCopy(defaultXml); @@ -405,10 +414,11 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { fatal("internal manifest tool failed on default xml: " + toString(std::move(e))); - for (StringRef filename : config->manifestInput) { + for (StringRef filename : ctx.config.manifestInput) { std::unique_ptr manifest = check(MemoryBuffer::getFile(filename)); - if (auto e = merger.merge(*manifest.get())) + // Call takeBuffer to include in /reproduce: output if applicable. + if (auto e = merger.merge(takeBuffer(std::move(manifest)))) fatal("internal manifest tool failed on file " + filename + ": " + toString(std::move(e))); } @@ -416,7 +426,8 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { return std::string(merger.getMergedManifest().get()->getBuffer()); } -static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { +std::string +LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) { // Create the default manifest file as a temporary file. TemporaryFile Default("defaultxml", "manifest"); std::error_code ec; @@ -433,9 +444,14 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { Executor e("mt.exe"); e.add("/manifest"); e.add(Default.path); - for (StringRef filename : config->manifestInput) { + for (StringRef filename : ctx.config.manifestInput) { e.add("/manifest"); e.add(filename); + + // Manually add the file to the /reproduce: tar if needed. + if (tar) + if (auto mbOrErr = MemoryBuffer::getFile(filename)) + takeBuffer(std::move(*mbOrErr)); } e.add("/nologo"); e.add("/out:" + StringRef(user.path)); @@ -447,9 +463,9 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { ->getBuffer()); } -static std::string createManifestXml() { +std::string LinkerDriver::createManifestXml() { std::string defaultXml = createDefaultXml(); - if (config->manifestInput.empty()) + if (ctx.config.manifestInput.empty()) return defaultXml; if (windows_manifest::isAvailable()) @@ -458,14 +474,14 @@ static std::string createManifestXml() { return createManifestXmlWithExternalMt(defaultXml); } -static std::unique_ptr -createMemoryBufferForManifestRes(size_t manifestSize) { +std::unique_ptr +LinkerDriver::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 + + return WritableMemoryBuffer::getNewMemBuffer(resSize, ctx.config.outputFile + ".manifest.res"); } @@ -476,7 +492,8 @@ static void writeResFileHeader(char *&buf) { buf += object::WIN_RES_NULL_ENTRY_SIZE; } -static void writeResEntryHeader(char *&buf, size_t manifestSize) { +static void writeResEntryHeader(char *&buf, size_t manifestSize, + int manifestID) { // Write the prefix. auto *prefix = reinterpret_cast(buf); prefix->DataSize = manifestSize; @@ -488,7 +505,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize) { // Write the Type/Name IDs. auto *iDs = reinterpret_cast(buf); iDs->setType(RT_MANIFEST); - iDs->setName(config->manifestID); + iDs->setName(manifestID); buf += sizeof(object::WinResIDs); // Write the suffix. @@ -502,7 +519,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize) { } // Create a resource file containing a manifest XML. -std::unique_ptr createManifestRes() { +std::unique_ptr LinkerDriver::createManifestRes() { std::string manifest = createManifestXml(); std::unique_ptr res = @@ -510,17 +527,17 @@ std::unique_ptr createManifestRes() { char *buf = res->getBufferStart(); writeResFileHeader(buf); - writeResEntryHeader(buf, manifest.size()); + writeResEntryHeader(buf, manifest.size(), ctx.config.manifestID); // Copy the manifest data into the .res file. std::copy(manifest.begin(), manifest.end(), buf); return std::move(res); } -void createSideBySideManifest() { - std::string path = std::string(config->manifestFile); +void LinkerDriver::createSideBySideManifest() { + std::string path = std::string(ctx.config.manifestFile); if (path == "") - path = config->outputFile + ".manifest"; + path = ctx.config.outputFile + ".manifest"; std::error_code ec; raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF); if (ec) @@ -532,7 +549,7 @@ void createSideBySideManifest() { // "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "=.". // Used for parsing /export arguments. -Export parseExport(StringRef arg) { +Export LinkerDriver::parseExport(StringRef arg) { Export e; StringRef rest; std::tie(e.name, rest) = arg.split(","); @@ -540,8 +557,7 @@ Export parseExport(StringRef arg) { goto err; if (e.name.contains('=')) { - StringRef x, y; - std::tie(x, y) = e.name.split("="); + auto [x, y] = e.name.split("="); // If "=.". if (y.contains(".")) { @@ -595,14 +611,14 @@ err: fatal("invalid /export: " + arg); } -static StringRef undecorate(StringRef sym) { - if (config->machine != I386) +static StringRef undecorate(COFFLinkerContext &ctx, StringRef sym) { + if (ctx.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) + if (sym.startswith("_") && sym.contains('@') && !ctx.config.mingw) return sym; return sym.startswith("_") ? sym.substr(1) : sym; } @@ -616,39 +632,39 @@ static StringRef killAt(StringRef sym, bool prefix) { sym = sym.substr(0, sym.find('@', 1)); if (!sym.startswith("@")) { if (prefix && !sym.startswith("_")) - return saver.save("_" + sym); + 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); + sym = saver().save("_" + sym); return sym; } // Performs error checking on all /export arguments. // It also sets ordinals. -void fixupExports() { +void LinkerDriver::fixupExports() { // Symbol ordinals must be unique. std::set ords; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (e.ordinal == 0) continue; if (!ords.insert(e.ordinal).second) fatal("duplicate export ordinal: " + e.name); } - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (!e.forwardTo.empty()) { - e.exportName = undecorate(e.name); + e.exportName = undecorate(ctx, e.name); } else { - e.exportName = undecorate(e.extName.empty() ? e.name : e.extName); + e.exportName = undecorate(ctx, e.extName.empty() ? e.name : e.extName); } } - if (config->killAt && config->machine == I386) { - for (Export &e : config->exports) { + if (ctx.config.killAt && ctx.config.machine == I386) { + for (Export &e : ctx.config.exports) { e.name = killAt(e.name, true); e.exportName = killAt(e.exportName, false); e.extName = killAt(e.extName, true); @@ -657,9 +673,9 @@ void fixupExports() { } // Uniquefy by name. - DenseMap map(config->exports.size()); + DenseMap map(ctx.config.exports.size()); std::vector v; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { auto pair = map.insert(std::make_pair(e.exportName, &e)); bool inserted = pair.second; if (inserted) { @@ -671,36 +687,34 @@ void fixupExports() { continue; warn("duplicate /export option: " + e.name); } - config->exports = std::move(v); + ctx.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; - }); + llvm::sort(ctx.config.exports, [](const Export &a, const Export &b) { + return a.exportName < b.exportName; + }); } -void assignExportOrdinals() { +void LinkerDriver::assignExportOrdinals() { // Assign unique ordinals if default (= 0). uint32_t max = 0; - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) max = std::max(max, (uint32_t)e.ordinal); - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) if (e.ordinal == 0) e.ordinal = ++max; if (max > std::numeric_limits::max()) - fatal("too many exported symbols (max " + + fatal("too many exported symbols (got " + Twine(max) + ", max " + Twine(std::numeric_limits::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('='); +void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) { + auto [k, v] = arg.split('='); if (k.empty() || v.empty()) fatal("/failifmismatch: invalid argument: " + arg); - std::pair existing = config->mustMatch[k]; + std::pair existing = ctx.config.mustMatch[k]; if (!existing.first.empty() && v != existing.first) { std::string sourceStr = source ? toString(source) : "cmd-line"; std::string existingStr = @@ -709,14 +723,14 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) { existingStr + " has value " + existing.first + "\n>>> " + sourceStr + " has value " + v); } - config->mustMatch[k] = {v, source}; + ctx.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, - ArrayRef objs) { - object::WindowsResourceParser parser(/* MinGW */ config->mingw); +MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef mbs, + ArrayRef objs) { + object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw); std::vector duplicates; for (MemoryBufferRef mb : mbs) { @@ -741,18 +755,18 @@ MemoryBufferRef convertResToCOFF(ArrayRef mbs, fatal(toString(std::move(ec))); } - if (config->mingw) + if (ctx.config.mingw) parser.cleanUpManifests(duplicates); for (const auto &dupeDiag : duplicates) - if (config->forceMultipleRes) + if (ctx.config.forceMultipleRes) warn(dupeDiag); else error(dupeDiag); Expected> e = - llvm::object::writeWindowsResourceCOFF(config->machine, parser, - config->timestamp); + llvm::object::writeWindowsResourceCOFF(ctx.config.machine, parser, + ctx.config.timestamp); if (!e) fatal("failed to write .res to COFF: " + toString(e.takeError())); @@ -764,12 +778,15 @@ MemoryBufferRef convertResToCOFF(ArrayRef mbs, // Create OptTable // Create prefix string literals used in Options.td -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#define PREFIX(NAME, VALUE) \ + static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \ + static constexpr llvm::ArrayRef NAME( \ + NAME##_init, std::size(NAME##_init) - 1); #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td -static const llvm::opt::OptTable::Info infoTable[] = { +static constexpr 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}, @@ -777,9 +794,7 @@ static const llvm::opt::OptTable::Info infoTable[] = { #undef OPTION }; -COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} - -COFFOptTable optTable; +COFFOptTable::COFFOptTable() : GenericOptTable(infoTable, true) {} // Set color diagnostics according to --color-diagnostics={auto,always,never} // or --no-color-diagnostics flags. @@ -816,6 +831,8 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { return cl::TokenizeWindowsCommandLine; } +ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {} + // Parses a given list of options. opt::InputArgList ArgParser::parse(ArrayRef argv) { // Make InputArgList from string vectors. @@ -826,7 +843,8 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { // options so we parse here before and ignore all the options but // --rsp-quoting and /lldignoreenv. // (This means --rsp-quoting can't be added through %LINK%.) - opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount); + opt::InputArgList args = + ctx.optTable.ParseArgs(argv, missingIndex, missingCount); // Expand response files (arguments in the form of @) and insert // flags from %LINK% and %_LINK_%, and then parse the argument again. @@ -834,9 +852,9 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { argv.data() + argv.size()); if (!args.hasArg(OPT_lldignoreenv)) addLINK(expandedArgv); - cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); - args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), - missingIndex, missingCount); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv); + args = ctx.optTable.ParseArgs(ArrayRef(expandedArgv).drop_front(), + missingIndex, missingCount); // Print the real command line if response files are expanded. if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { @@ -847,8 +865,13 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { } // Save the command line after response file expansion so we can write it to - // the PDB if necessary. - config->argv = {expandedArgv.begin(), expandedArgv.end()}; + // the PDB if necessary. Mimic MSVC, which skips input files. + ctx.config.argv = {argv[0]}; + for (opt::Arg *arg : args) { + if (arg->getOption().getKind() != opt::Option::InputClass) { + ctx.config.argv.push_back(args.getArgString(arg->getIndex())); + } + } // Handle /WX early since it converts missing argument warnings to errors. errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); @@ -860,7 +883,7 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) { std::string nearest; - if (optTable.findNearest(arg->getAsString(args), nearest) > 1) + if (ctx.optTable.findNearest(arg->getAsString(args), nearest) > 1) warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); else warn("ignoring unknown argument '" + arg->getAsString(args) + @@ -881,7 +904,7 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) { // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for // potentially every symbol in the object, so they must be handled quickly. SmallVector tokens; - cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens); + cl::TokenizeWindowsCommandLineNoCopy(s, saver(), tokens); for (StringRef tok : tokens) { if (tok.startswith_insensitive("/export:") || tok.startswith_insensitive("-export:")) @@ -889,12 +912,15 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) { else if (tok.startswith_insensitive("/include:") || tok.startswith_insensitive("-include:")) result.includes.push_back(tok.substr(strlen("/include:"))); + else if (tok.startswith_insensitive("/exclude-symbols:") || + tok.startswith_insensitive("-exclude-symbols:")) + result.excludes.push_back(tok.substr(strlen("/exclude-symbols:"))); else { // Copy substrings that are not valid C strings. The tokenizer may have // already copied quoted arguments for us, so those do not need to be // copied again. bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0'; - rest.push_back(HasNul ? tok.data() : saver.save(tok).data()); + rest.push_back(HasNul ? tok.data() : saver().save(tok).data()); } } @@ -902,7 +928,7 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) { unsigned missingIndex; unsigned missingCount; - result.args = optTable.ParseArgs(rest, missingIndex, missingCount); + result.args = ctx.optTable.ParseArgs(rest, missingIndex, missingCount); if (missingCount) fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument"); @@ -916,11 +942,11 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) { // So you can pass extra arguments using them. void ArgParser::addLINK(SmallVector &argv) { // Concatenate LINK env and command line arguments, and then parse them. - if (Optional s = Process::GetEnv("LINK")) { + if (std::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_")) { + if (std::optional s = Process::GetEnv("_LINK_")) { std::vector v = tokenize(*s); argv.insert(std::next(argv.begin()), v.begin(), v.end()); } @@ -928,14 +954,14 @@ void ArgParser::addLINK(SmallVector &argv) { std::vector ArgParser::tokenize(StringRef s) { SmallVector tokens; - cl::TokenizeWindowsCommandLine(s, saver, tokens); + cl::TokenizeWindowsCommandLine(s, saver(), tokens); return std::vector(tokens.begin(), tokens.end()); } -void printHelp(const char *argv0) { - optTable.printHelp(lld::outs(), - (std::string(argv0) + " [options] file...").c_str(), - "LLVM Linker", false); +void LinkerDriver::printHelp(const char *argv0) { + ctx.optTable.printHelp(lld::outs(), + (std::string(argv0) + " [options] file...").c_str(), + "LLVM Linker", false); } } // namespace coff diff --git a/gnu/llvm/lld/COFF/ICF.cpp b/gnu/llvm/lld/COFF/ICF.cpp index 73264696729..7ece725313d 100644 --- a/gnu/llvm/lld/COFF/ICF.cpp +++ b/gnu/llvm/lld/COFF/ICF.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "ICF.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" @@ -33,15 +34,12 @@ using namespace llvm; -namespace lld { -namespace coff { - -static Timer icfTimer("ICF", Timer::root()); +namespace lld::coff { class ICF { public: - ICF(ICFLevel icfLevel) : icfLevel(icfLevel){}; - void run(ArrayRef v); + ICF(COFFLinkerContext &c) : ctx(c){}; + void run(); private: void segregate(size_t begin, size_t end, bool constant); @@ -63,7 +61,8 @@ private: std::vector chunks; int cnt = 0; std::atomic repeat = {false}; - ICFLevel icfLevel = ICFLevel::All; + + COFFLinkerContext &ctx; }; // Returns true if section S is subject of ICF. @@ -84,7 +83,7 @@ bool ICF::isEligible(SectionChunk *c) { return false; // Under regular (not safe) ICF, all code sections are eligible. - if ((icfLevel == ICFLevel::All) && + if ((ctx.config.doICF == ICFLevel::All) && c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; @@ -232,10 +231,10 @@ void ICF::forEachClass(std::function fn) { size_t boundaries[numShards + 1]; boundaries[0] = 0; boundaries[numShards] = chunks.size(); - parallelForEachN(1, numShards, [&](size_t i) { + parallelFor(1, numShards, [&](size_t i) { boundaries[i] = findBoundary((i - 1) * step, chunks.size()); }); - parallelForEachN(1, numShards + 1, [&](size_t i) { + parallelFor(1, numShards + 1, [&](size_t i) { if (boundaries[i - 1] < boundaries[i]) { forEachClassRange(boundaries[i - 1], boundaries[i], fn); } @@ -246,12 +245,12 @@ void ICF::forEachClass(std::function fn) { // 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); +void ICF::run() { + ScopedTimer t(ctx.icfTimer); // Collect only mergeable sections and group by hash value. uint32_t nextId = 1; - for (Chunk *c : vec) { + for (Chunk *c : ctx.symtab.getChunks()) { if (auto *sc = dyn_cast(c)) { if (isEligible(sc)) chunks.push_back(sc); @@ -262,7 +261,7 @@ void ICF::run(ArrayRef vec) { // Make sure that ICF doesn't merge sections that are being handled by string // tail merging. - for (MergeChunk *mc : MergeChunk::instances) + for (MergeChunk *mc : ctx.mergeChunkInstances) if (mc) for (SectionChunk *sc : mc->sections) sc->eqClass[0] = nextId++; @@ -317,9 +316,6 @@ void ICF::run(ArrayRef vec) { } // Entry point to ICF. -void doICF(ArrayRef chunks, ICFLevel icfLevel) { - ICF(icfLevel).run(chunks); -} +void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); } -} // namespace coff -} // namespace lld +} // namespace lld::coff diff --git a/gnu/llvm/lld/COFF/ICF.h b/gnu/llvm/lld/COFF/ICF.h index f8cc8071f9e..f9ed8edc396 100644 --- a/gnu/llvm/lld/COFF/ICF.h +++ b/gnu/llvm/lld/COFF/ICF.h @@ -13,14 +13,12 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" -namespace lld { -namespace coff { +namespace lld::coff { -class Chunk; +class COFFLinkerContext; -void doICF(ArrayRef chunks, ICFLevel); +void doICF(COFFLinkerContext &ctx); -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/InputFiles.cpp b/gnu/llvm/lld/COFF/InputFiles.cpp index f32353ca4f9..f79aa3f1af0 100644 --- a/gnu/llvm/lld/COFF/InputFiles.cpp +++ b/gnu/llvm/lld/COFF/InputFiles.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" @@ -14,8 +15,6 @@ #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/DWARF.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" @@ -38,6 +37,7 @@ #include "llvm/Support/Path.h" #include "llvm/Target/TargetOptions.h" #include +#include #include #include @@ -69,15 +69,10 @@ std::string lld::toString(const coff::InputFile *file) { .str(); } -std::vector ObjFile::instances; -std::map PDBInputFile::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, +static void checkAndSetWeakAlias(COFFLinkerContext &ctx, InputFile *f, Symbol *source, Symbol *target) { if (auto *u = dyn_cast(source)) { if (u->weakAlias && u->weakAlias != target) { @@ -86,9 +81,9 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, // 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) + if (ctx.config.mingw) return; - symtab->reportDuplicate(source, f); + ctx.symtab.reportDuplicate(source, f); } u->weakAlias = target; } @@ -98,7 +93,8 @@ static bool ignoredSymbolName(StringRef name) { return name == "@feat.00" || name == "@comp.id"; } -ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} +ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, ArchiveKind, m) {} void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. @@ -106,20 +102,20 @@ void ArchiveFile::parse() { // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &sym : file->symbols()) - symtab->addLazyArchive(this, sym); + ctx.symtab.addLazyArchive(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. void ArchiveFile::addMember(const Archive::Symbol &sym) { const Archive::Child &c = CHECK(sym.getMember(), - "could not get the member for symbol " + toCOFFString(sym)); + "could not get the member for symbol " + toCOFFString(ctx, 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()); + ctx.driver.enqueueArchiveMember(c, sym, getName()); } std::vector lld::coff::getArchiveMembers(Archive *file) { @@ -138,31 +134,7 @@ std::vector lld::coff::getArchiveMembers(Archive *file) { 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; - } - +void ObjFile::parseLazy() { // Native object file. std::unique_ptr coffObjPtr = CHECK(createBinary(mb), this); COFFObjectFile *coffObj = cast(coffObjPtr.get()); @@ -175,7 +147,7 @@ void LazyObjFile::parse() { StringRef name = check(coffObj->getSymbolName(coffSym)); if (coffSym.isAbsolute() && ignoredSymbolName(name)) continue; - symtab->addLazyObject(this, name); + ctx.symtab.addLazyObject(this, name); i += coffSym.getNumberOfAuxSymbols(); } } @@ -265,7 +237,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, // and then write it to a separate .pdb file. // Ignore DWARF debug info unless /debug is given. - if (!config->debug && name.startswith(".debug_")) + if (!ctx.config.debug && name.startswith(".debug_")) return nullptr; if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) @@ -288,12 +260,12 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, guardEHContChunks.push_back(c); else if (name == ".sxdata") sxDataChunks.push_back(c); - else if (config->tailMerge && sec->NumberOfRelocations == 0 && + else if (ctx.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); + MergeChunk::addSection(ctx, c); else if (name == ".rsrc" || name.startswith(".rsrc$")) resourceChunks.push_back(c); else @@ -387,16 +359,16 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) { if (sym.isExternal()) { StringRef name = check(coffObj->getSymbolName(sym)); if (sc) - return symtab->addRegular(this, name, sym.getGeneric(), sc, - sym.getValue()); + return ctx.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.")) + if (ctx.config.mingw && name.startswith(".weak.")) return nullptr; - return symtab->addUndefined(name, this, false); + return ctx.symtab.addUndefined(name, this, false); } if (sc) return make(this, /*Name*/ "", /*IsCOMDAT*/ false, @@ -425,15 +397,15 @@ void ObjFile::initializeSymbols() { symbols[i] = createUndefined(coffSym); uint32_t tagIndex = coffSym.getAux()->TagIndex; weakAliases.emplace_back(symbols[i], tagIndex); - } else if (Optional optSym = + } else if (std::optional optSym = createDefined(coffSym, comdatDefs, prevailingComdat)) { symbols[i] = *optSym; - if (config->mingw && prevailingComdat) + if (ctx.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: + // createDefined() returns std::nullopt 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 @@ -449,7 +421,7 @@ void ObjFile::initializeSymbols() { if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) readAssociativeDefinition(sym, def); - else if (config->mingw) + else if (ctx.config.mingw) maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); } if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { @@ -464,7 +436,7 @@ void ObjFile::initializeSymbols() { for (auto &kv : weakAliases) { Symbol *sym = kv.first; uint32_t idx = kv.second; - checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); + checkAndSetWeakAlias(ctx, this, sym, symbols[idx]); } // Free the memory used by sparseChunks now that symbol loading is finished. @@ -473,7 +445,7 @@ void ObjFile::initializeSymbols() { Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { StringRef name = check(coffObj->getSymbolName(sym)); - return symtab->addUndefined(name, this, sym.isWeakExternal()); + return ctx.symtab.addUndefined(name, this, sym.isWeakExternal()); } static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj, @@ -524,10 +496,10 @@ void ObjFile::handleComdatSelection( // Clang on the other hand picks "any". To be able to link two object files // with a __declspec(selectany) declaration, one compiled with gcc and the // other with clang, we merge them as proper "same size as" - if (config->mingw && ((selection == IMAGE_COMDAT_SELECT_ANY && - leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) || - (selection == IMAGE_COMDAT_SELECT_SAME_SIZE && - leaderSelection == IMAGE_COMDAT_SELECT_ANY))) { + if (ctx.config.mingw && ((selection == IMAGE_COMDAT_SELECT_ANY && + leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) || + (selection == IMAGE_COMDAT_SELECT_SAME_SIZE && + leaderSelection == IMAGE_COMDAT_SELECT_ANY))) { leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE; } @@ -539,17 +511,17 @@ void ObjFile::handleComdatSelection( // seems better though. // (This behavior matches ModuleLinker::getComdatResult().) if (selection != leaderSelection) { - log(("conflicting comdat type for " + toString(*leader) + ": " + + log(("conflicting comdat type for " + toString(ctx, *leader) + ": " + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + " and " + Twine((int)selection) + " in " + toString(this)) .str()); - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); return; } switch (selection) { case IMAGE_COMDAT_SELECT_NODUPLICATES: - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); break; case IMAGE_COMDAT_SELECT_ANY: @@ -558,15 +530,15 @@ void ObjFile::handleComdatSelection( case IMAGE_COMDAT_SELECT_SAME_SIZE: if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) { - if (!config->mingw) { - symtab->reportDuplicate(leader, this); + if (!ctx.config.mingw) { + ctx.symtab.reportDuplicate(leader, this); } else { const coff_aux_section_definition *leaderDef = nullptr; if (leaderChunk->file) leaderDef = findSectionDef(leaderChunk->file->getCOFFObj(), leaderChunk->getSectionNumber()); if (!leaderDef || leaderDef->Length != def->Length) - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); } } break; @@ -577,7 +549,7 @@ void ObjFile::handleComdatSelection( // if the two comdat sections have e.g. different alignment. // Match that. if (leaderChunk->getContents() != newChunk.getContents()) - symtab->reportDuplicate(leader, this, &newChunk, sym.getValue()); + ctx.symtab.reportDuplicate(leader, this, &newChunk, sym.getValue()); break; } @@ -610,7 +582,7 @@ void ObjFile::handleComdatSelection( } } -Optional ObjFile::createDefined( +std::optional ObjFile::createDefined( COFFSymbolRef sym, std::vector &comdatDefs, bool &prevailing) { @@ -620,8 +592,8 @@ Optional ObjFile::createDefined( if (sym.isCommon()) { auto *c = make(sym); chunks.push_back(c); - return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(), - c); + return ctx.symtab.addCommon(this, getName(), sym.getValue(), + sym.getGeneric(), c); } if (sym.isAbsolute()) { @@ -634,8 +606,8 @@ Optional ObjFile::createDefined( return nullptr; if (sym.isExternal()) - return symtab->addAbsolute(name, sym); - return make(name, sym); + return ctx.symtab.addAbsolute(name, sym); + return make(ctx, name, sym); } int32_t sectionNumber = sym.getSectionNumber(); @@ -657,8 +629,8 @@ Optional ObjFile::createDefined( // 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. + // it sets comdatDefs and returns std::nullopt, 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]) { @@ -667,7 +639,7 @@ Optional ObjFile::createDefined( if (sym.isExternal()) { std::tie(leader, prevailing) = - symtab->addComdat(this, getName(), sym.getGeneric()); + ctx.symtab.addComdat(this, getName(), sym.getGeneric()); } else { leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, /*IsExternal*/ false, sym.getGeneric()); @@ -705,7 +677,7 @@ Optional ObjFile::createDefined( if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) comdatDefs[sectionNumber] = def; } - return None; + return std::nullopt; } return createRegular(sym); @@ -763,7 +735,8 @@ void ObjFile::initializeFlags() { if (sym->kind() == SymbolKind::S_OBJNAME) { auto objName = cantFail(SymbolDeserializer::deserializeAs( sym.get())); - pchSignature = objName.Signature; + if (objName.Signature) + pchSignature = objName.Signature; } offset += sym->length(); } @@ -778,7 +751,7 @@ void ObjFile::initializeFlags() { // 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) + if (!ctx.config.debug) return; bool isPCH = false; @@ -789,12 +762,11 @@ void ObjFile::initializeDependencies() { else data = getDebugSection(".debug$T"); - // Don't make a TpiSource for objects with no debug info. If the object has // symbols but no types, make a plain, empty TpiSource anyway, because it // simplifies adding the symbols later. if (data.empty()) { if (!debugChunks.empty()) - debugTypesObj = makeTpiSource(this); + debugTypesObj = makeTpiSource(ctx, this); return; } @@ -812,7 +784,7 @@ void ObjFile::initializeDependencies() { // This object file is a PCH file that others will depend on. if (isPCH) { - debugTypesObj = makePrecompSource(this); + debugTypesObj = makePrecompSource(ctx, this); return; } @@ -820,8 +792,8 @@ void ObjFile::initializeDependencies() { if (firstType->kind() == LF_TYPESERVER2) { TypeServer2Record ts = cantFail( TypeDeserializer::deserializeAs(firstType->data())); - debugTypesObj = makeUseTypeServerSource(this, ts); - PDBInputFile::enqueue(ts.getName(), this); + debugTypesObj = makeUseTypeServerSource(ctx, this, ts); + enqueuePdbFile(ts.getName(), this); return; } @@ -830,14 +802,18 @@ void ObjFile::initializeDependencies() { if (firstType->kind() == LF_PRECOMP) { PrecompRecord precomp = cantFail( TypeDeserializer::deserializeAs(firstType->data())); - debugTypesObj = makeUsePrecompSource(this, precomp); + // We're better off trusting the LF_PRECOMP signature. In some cases the + // S_OBJNAME record doesn't contain a valid PCH signature. + if (precomp.Signature) + pchSignature = precomp.Signature; + debugTypesObj = makeUsePrecompSource(ctx, this, precomp); // Drop the LF_PRECOMP record from the input stream. debugTypes = debugTypes.drop_front(firstType->RecordData.size()); return; } // This is a plain old object file. - debugTypesObj = makeTpiSource(this); + debugTypesObj = makeTpiSource(ctx, this); } // Make a PDB path assuming the PDB is in the same folder as the OBJ @@ -855,7 +831,7 @@ static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) { // 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. +// pdbInputFileInstances map, at least on Windows. static std::string normalizePdbPath(StringRef path) { #if defined(_WIN32) return path.lower(); @@ -865,8 +841,8 @@ static std::string normalizePdbPath(StringRef path) { } // If existing, return the actual PDB path on disk. -static Optional findPdbPath(StringRef pdbPath, - ObjFile *dependentFile) { +static std::optional findPdbPath(StringRef pdbPath, + 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 @@ -876,43 +852,37 @@ static Optional findPdbPath(StringRef pdbPath, std::string ret = getPdbBaseName(dependentFile, pdbPath); if (llvm::sys::fs::exists(ret)) return normalizePdbPath(ret); - return None; + return std::nullopt; } -PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {} +PDBInputFile::PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, PDBKind, m) {} PDBInputFile::~PDBInputFile() = default; -PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path, +PDBInputFile *PDBInputFile::findFromRecordPath(const COFFLinkerContext &ctx, + StringRef path, ObjFile *fromFile) { auto p = findPdbPath(path.str(), fromFile); if (!p) return nullptr; - auto it = PDBInputFile::instances.find(*p); - if (it != PDBInputFile::instances.end()) + auto it = ctx.pdbInputFileInstances.find(*p); + if (it != ctx.pdbInputFileInstances.end()) return it->second; return nullptr; } -void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) { - auto p = findPdbPath(path.str(), fromFile); - if (!p) - return; - auto it = PDBInputFile::instances.emplace(*p, nullptr); - if (!it.second) - return; // already scheduled for load - driver->enqueuePDB(*p); -} - void PDBInputFile::parse() { - PDBInputFile::instances[mb.getBufferIdentifier().str()] = this; + ctx.pdbInputFileInstances[mb.getBufferIdentifier().str()] = this; std::unique_ptr thisSession; - loadErr.emplace(pdb::NativeSession::createFromPdb( - MemoryBuffer::getMemBuffer(mb, false), thisSession)); - if (*loadErr) + Error E = pdb::NativeSession::createFromPdb( + MemoryBuffer::getMemBuffer(mb, false), thisSession); + if (E) { + loadErrorStr.emplace(toString(std::move(E))); return; // fail silently at this point - the error will be handled later, // when merging the debug type stream + } session.reset(static_cast(thisSession.release())); @@ -920,43 +890,57 @@ void PDBInputFile::parse() { auto expectedInfo = pdbFile.getPDBInfoStream(); // All PDB Files should have an Info stream. if (!expectedInfo) { - loadErr.emplace(expectedInfo.takeError()); + loadErrorStr.emplace(toString(expectedInfo.takeError())); return; } - debugTypesObj = makeTypeServerSource(this); + debugTypesObj = makeTypeServerSource(ctx, 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> +std::optional> ObjFile::getVariableLocation(StringRef var) { if (!dwarf) { dwarf = make(DWARFContext::create(*getCOFFObj())); if (!dwarf) - return None; + return std::nullopt; } - if (config->machine == I386) + if (ctx.config.machine == I386) var.consume_front("_"); - Optional> ret = dwarf->getVariableLoc(var); + std::optional> ret = + dwarf->getVariableLoc(var); if (!ret) - return None; - return std::make_pair(saver.save(ret->first), ret->second); + return std::nullopt; + 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) { +std::optional ObjFile::getDILineInfo(uint32_t offset, + uint32_t sectionIndex) { if (!dwarf) { dwarf = make(DWARFContext::create(*getCOFFObj())); if (!dwarf) - return None; + return std::nullopt; } return dwarf->getDILineInfo(offset, sectionIndex); } +void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) { + auto p = findPdbPath(path.str(), fromFile); + if (!p) + return; + auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr); + if (!it.second) + return; // already scheduled for load + ctx.driver.enqueuePDB(*p); +} + +ImportFile::ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, ImportKind, m), live(!ctx.config.doGC), thunkLive(live) {} + void ImportFile::parse() { const char *buf = mb.getBufferStart(); const auto *hdr = reinterpret_cast(buf); @@ -966,8 +950,8 @@ void ImportFile::parse() { 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); + 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 = std::string(StringRef(nameStart)); StringRef extName; @@ -990,34 +974,32 @@ void ImportFile::parse() { this->hdr = hdr; externalName = extName; - impSym = symtab->addImportData(impName, this); + impSym = ctx.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)); + static_cast(ctx.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( + thunkSym = ctx.symtab.addImportThunk( name, cast_or_null(impSym), hdr->Machine); } -BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : BitcodeFile(mb, archiveName, offsetInArchive, {}) {} - -BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive, - std::vector &&symbols) - : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) { +BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb, + StringRef archiveName, uint64_t offsetInArchive, + bool lazy) + : InputFile(ctx, BitcodeKind, mb, lazy) { std::string path = mb.getBufferIdentifier().str(); - if (config->thinLTOIndexOnly) - path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + if (ctx.config.thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier(), + ctx.config.thinLTOObjectSuffixReplace.first, + ctx.config.thinLTOObjectSuffixReplace.second); // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two archives define two members with the same name, this @@ -1025,90 +1007,72 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, // 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.empty() ? path - : archiveName + sys::path::filename(path) + - utostr(offsetInArchive))); + MemoryBufferRef mbref(mb.getBuffer(), + saver().save(archiveName.empty() + ? path + : archiveName + + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); } BitcodeFile::~BitcodeFile() = default; -namespace { -// Convenience class for initializing a coff_section with specific flags. -class FakeSection { -public: - FakeSection(int c) { section.Characteristics = c; } - - coff_section section; -}; - -// Convenience class for initializing a SectionChunk with specific flags. -class FakeSectionChunk { -public: - FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) { - // Comdats from LTO files can't be fully treated as regular comdats - // at this point; we don't know what size or contents they are going to - // have, so we can't do proper checking of such aspects of them. - chunk.selection = IMAGE_COMDAT_SELECT_ANY; - } - - SectionChunk chunk; -}; - -FakeSection ltoTextSection(IMAGE_SCN_MEM_EXECUTE); -FakeSection ltoDataSection(IMAGE_SCN_CNT_INITIALIZED_DATA); -FakeSectionChunk ltoTextSectionChunk(<oTextSection.section); -FakeSectionChunk ltoDataSectionChunk(<oDataSection.section); -} // namespace - void BitcodeFile::parse() { + llvm::StringSaver &saver = lld::saver(); + std::vector> comdat(obj->getComdatTable().size()); for (size_t i = 0; i != obj->getComdatTable().size(); ++i) // FIXME: Check nodeduplicate comdat[i] = - symtab->addComdat(this, saver.save(obj->getComdatTable()[i].first)); + ctx.symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first)); for (const lto::InputFile::Symbol &objSym : obj->symbols()) { StringRef symName = saver.save(objSym.getName()); int comdatIndex = objSym.getComdatIndex(); Symbol *sym; SectionChunk *fakeSC = nullptr; if (objSym.isExecutable()) - fakeSC = <oTextSectionChunk.chunk; + fakeSC = &ctx.ltoTextSectionChunk.chunk; else - fakeSC = <oDataSectionChunk.chunk; + fakeSC = &ctx.ltoDataSectionChunk.chunk; if (objSym.isUndefined()) { - sym = symtab->addUndefined(symName, this, false); + sym = ctx.symtab.addUndefined(symName, this, false); } else if (objSym.isCommon()) { - sym = symtab->addCommon(this, symName, objSym.getCommonSize()); + sym = ctx.symtab.addCommon(this, symName, objSym.getCommonSize()); } else if (objSym.isWeak() && objSym.isIndirect()) { // Weak external. - sym = symtab->addUndefined(symName, this, true); + sym = ctx.symtab.addUndefined(symName, this, true); std::string fallback = std::string(objSym.getCOFFWeakExternalFallback()); - Symbol *alias = symtab->addUndefined(saver.save(fallback)); - checkAndSetWeakAlias(symtab, this, sym, alias); + Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback)); + checkAndSetWeakAlias(ctx, this, sym, alias); } else if (comdatIndex != -1) { if (symName == obj->getComdatTable()[comdatIndex].first) { sym = comdat[comdatIndex].first; if (cast(sym)->data == nullptr) cast(sym)->data = &fakeSC->repl; } else if (comdat[comdatIndex].second) { - sym = symtab->addRegular(this, symName, nullptr, fakeSC); + sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC); } else { - sym = symtab->addUndefined(symName, this, false); + sym = ctx.symtab.addUndefined(symName, this, false); } } else { - sym = symtab->addRegular(this, symName, nullptr, fakeSC); + sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC, 0, + objSym.isWeak()); } symbols.push_back(sym); if (objSym.isUsed()) - config->gcroot.push_back(sym); + ctx.config.gcroot.push_back(sym); } directives = obj->getCOFFLinkerOpts(); } +void BitcodeFile::parseLazy() { + for (const lto::InputFile::Symbol &sym : obj->symbols()) + if (!sym.isUndefined()) + ctx.symtab.addLazyObject(this, sym.getName()); +} + MachineTypes BitcodeFile::getMachineType() { switch (Triple(obj->getTargetTriple()).getArch()) { case Triple::x86_64: @@ -1124,10 +1088,8 @@ MachineTypes BitcodeFile::getMachineType() { } } -std::string lld::coff::replaceThinLTOSuffix(StringRef path) { - StringRef suffix = config->thinLTOObjectSuffixReplace.first; - StringRef repl = config->thinLTOObjectSuffixReplace.second; - +std::string lld::coff::replaceThinLTOSuffix(StringRef path, StringRef suffix, + StringRef repl) { if (path.consume_back(suffix)) return (path + repl).str(); return std::string(path); @@ -1180,14 +1142,14 @@ void DLLFile::parse() { s->nameType = ImportNameType::IMPORT_NAME; if (coffObj->getMachine() == I386) { - s->symbolName = symbolName = saver.save("_" + symbolName); + s->symbolName = symbolName = saver().save("_" + symbolName); s->nameType = ImportNameType::IMPORT_NAME_NOPREFIX; } - StringRef impName = saver.save("__imp_" + symbolName); - symtab->addLazyDLLSymbol(this, s, impName); + StringRef impName = saver().save("__imp_" + symbolName); + ctx.symtab.addLazyDLLSymbol(this, s, impName); if (code) - symtab->addLazyDLLSymbol(this, s, symbolName); + ctx.symtab.addLazyDLLSymbol(this, s, symbolName); } } @@ -1203,7 +1165,7 @@ void DLLFile::makeImport(DLLFile::Symbol *s) { size_t impSize = s->dllName.size() + s->symbolName.size() + 2; // +2 for NULs size_t size = sizeof(coff_import_header) + impSize; - char *buf = bAlloc.Allocate(size); + char *buf = bAlloc().Allocate(size); memset(buf, 0, size); char *p = buf; auto *imp = reinterpret_cast(p); @@ -1219,6 +1181,6 @@ void DLLFile::makeImport(DLLFile::Symbol *s) { p += s->symbolName.size() + 1; memcpy(p, s->dllName.data(), s->dllName.size()); MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName); - ImportFile *impFile = make(mbref); - symtab->addFile(impFile); + ImportFile *impFile = make(ctx, mbref); + ctx.symtab.addFile(impFile); } diff --git a/gnu/llvm/lld/COFF/InputFiles.h b/gnu/llvm/lld/COFF/InputFiles.h index 47b5c588c5a..3acd74fb52f 100644 --- a/gnu/llvm/lld/COFF/InputFiles.h +++ b/gnu/llvm/lld/COFF/InputFiles.h @@ -38,6 +38,7 @@ namespace lld { class DWARFCache; namespace coff { +class COFFLinkerContext; std::vector getArchiveMembers(llvm::object::Archive *file); @@ -91,19 +92,26 @@ public: // Returns .drectve section contents if exist. StringRef getDirectives() { return directives; } + COFFLinkerContext &ctx; + protected: - InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {} + InputFile(COFFLinkerContext &c, Kind k, MemoryBufferRef m, bool lazy = false) + : mb(m), ctx(c), fileKind(k), lazy(lazy) {} StringRef directives; private: const Kind fileKind; + +public: + // True if this is a lazy ObjFile or BitcodeFile. + bool lazy = false; }; // .lib or .a file. class ArchiveFile : public InputFile { public: - explicit ArchiveFile(MemoryBufferRef m); + explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m); static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } void parse() override; @@ -117,30 +125,14 @@ private: 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)) {} + explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy = false) + : InputFile(ctx, ObjectKind, m, lazy) {} static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; + void parseLazy(); MachineTypes getMachineType() override; ArrayRef getChunks() { return chunks; } ArrayRef getDebugChunks() { return debugChunks; } @@ -175,8 +167,6 @@ public: 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. @@ -202,7 +192,7 @@ public: // 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; + std::optional pchSignature; // Whether this file was compiled with /hotpatch. bool hotPatchable = false; @@ -216,11 +206,11 @@ public: // The .debug$P or .debug$T section data if present. Empty otherwise. ArrayRef debugTypes; - llvm::Optional> + std::optional> getVariableLocation(StringRef var); - llvm::Optional getDILineInfo(uint32_t offset, - uint32_t sectionIndex); + std::optional getDILineInfo(uint32_t offset, + uint32_t sectionIndex); private: const coff_section* getSection(uint32_t i); @@ -228,6 +218,8 @@ private: return getSection(sym.getSectionNumber()); } + void enqueuePdbFile(StringRef path, ObjFile *fromFile); + void initializeChunks(); void initializeSymbols(); void initializeFlags(); @@ -266,7 +258,7 @@ private: bool &prevailing, DefinedRegular *leader, const llvm::object::coff_aux_section_definition *def); - llvm::Optional + std::optional createDefined(COFFSymbolRef sym, std::vector &comdatDefs, @@ -318,19 +310,16 @@ private: // stream. class PDBInputFile : public InputFile { public: - explicit PDBInputFile(MemoryBufferRef m); + explicit PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m); ~PDBInputFile(); static bool classof(const InputFile *f) { return f->kind() == PDBKind; } void parse() override; - static void enqueue(StringRef path, ObjFile *fromFile); - - static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile); - - static std::map instances; + static PDBInputFile *findFromRecordPath(const COFFLinkerContext &ctx, + StringRef path, ObjFile *fromFile); // Record possible errors while opening the PDB file - llvm::Optional loadErr; + std::optional loadErrorStr; // This is the actual interface to the PDB (if it was opened successfully) std::unique_ptr session; @@ -344,12 +333,10 @@ public: // for details about the format. class ImportFile : public InputFile { public: - explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {} + explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m); static bool classof(const InputFile *f) { return f->kind() == ImportKind; } - static std::vector instances; - Symbol *impSym = nullptr; Symbol *thunkSym = nullptr; std::string dllName; @@ -370,23 +357,21 @@ public: // 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; + bool live; + bool thunkLive; }; // Used for LTO. class BitcodeFile : public InputFile { public: - BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); - explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName, - uint64_t offsetInArchive, - std::vector &&symbols); + explicit BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb, + StringRef archiveName, uint64_t offsetInArchive, + bool lazy); ~BitcodeFile(); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; - static std::vector instances; + void parseLazy(); std::unique_ptr obj; private: @@ -398,7 +383,8 @@ private: // .dll file. MinGW only. class DLLFile : public InputFile { public: - explicit DLLFile(MemoryBufferRef m) : InputFile(DLLKind, m) {} + explicit DLLFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, DLLKind, m) {} static bool classof(const InputFile *f) { return f->kind() == DLLKind; } void parse() override; MachineTypes getMachineType() override; @@ -421,7 +407,8 @@ inline bool isBitcode(MemoryBufferRef mb) { return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; } -std::string replaceThinLTOSuffix(StringRef path); +std::string replaceThinLTOSuffix(StringRef path, StringRef suffix, + StringRef repl); } // namespace coff std::string toString(const coff::InputFile *file); diff --git a/gnu/llvm/lld/COFF/LLDMapFile.cpp b/gnu/llvm/lld/COFF/LLDMapFile.cpp index 79df33a3535..c14480aaf82 100644 --- a/gnu/llvm/lld/COFF/LLDMapFile.cpp +++ b/gnu/llvm/lld/COFF/LLDMapFile.cpp @@ -19,6 +19,7 @@ //===----------------------------------------------------------------------===// #include "LLDMapFile.h" +#include "COFFLinkerContext.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" @@ -44,9 +45,9 @@ static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, } // Returns a list of all symbols that we want to print out. -static std::vector getSymbols() { +static std::vector getSymbols(const COFFLinkerContext &ctx) { std::vector v; - for (ObjFile *file : ObjFile::instances) + for (ObjFile *file : ctx.objFileInstances) for (Symbol *b : file->getSymbols()) if (auto *sym = dyn_cast_or_null(b)) if (sym && !sym->getCOFFSymbol().isSectionDefinition()) @@ -72,12 +73,13 @@ static SymbolMapTy getSectionSyms(ArrayRef syms) { // Construct a map from symbols to their stringified representations. static DenseMap -getSymbolStrings(ArrayRef syms) { +getSymbolStrings(const COFFLinkerContext &ctx, + ArrayRef syms) { std::vector str(syms.size()); - parallelForEachN((size_t)0, syms.size(), [&](size_t i) { + parallelFor((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]); + os << indent16 << toString(ctx, *syms[i]); }); DenseMap ret; @@ -86,25 +88,25 @@ getSymbolStrings(ArrayRef syms) { return ret; } -void lld::coff::writeLLDMapFile(ArrayRef outputSections) { - if (config->lldmapFile.empty()) +void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) { + if (ctx.config.lldmapFile.empty()) return; std::error_code ec; - raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None); + raw_fd_ostream os(ctx.config.lldmapFile, ec, sys::fs::OF_None); if (ec) - fatal("cannot open " + config->lldmapFile + ": " + ec.message()); + fatal("cannot open " + ctx.config.lldmapFile + ": " + ec.message()); // Collect symbol info that we want to print out. - std::vector syms = getSymbols(); + std::vector syms = getSymbols(ctx); SymbolMapTy sectionSyms = getSectionSyms(syms); - DenseMap symStr = getSymbolStrings(syms); + DenseMap symStr = getSymbolStrings(ctx, syms); // Print out the header line. os << "Address Size Align Out In Symbol\n"; // Print out file contents. - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); os << sec->name << '\n'; diff --git a/gnu/llvm/lld/COFF/LLDMapFile.h b/gnu/llvm/lld/COFF/LLDMapFile.h index b731293a862..7cbc21fca5f 100644 --- a/gnu/llvm/lld/COFF/LLDMapFile.h +++ b/gnu/llvm/lld/COFF/LLDMapFile.h @@ -9,13 +9,9 @@ #ifndef LLD_COFF_LLDMAPFILE_H #define LLD_COFF_LLDMAPFILE_H -#include "llvm/ADT/ArrayRef.h" - -namespace lld { -namespace coff { -class OutputSection; -void writeLLDMapFile(llvm::ArrayRef outputSections); -} +namespace lld::coff { +class COFFLinkerContext; +void writeLLDMapFile(const COFFLinkerContext &ctx); } #endif diff --git a/gnu/llvm/lld/COFF/LTO.cpp b/gnu/llvm/lld/COFF/LTO.cpp index d117abf86f7..b5643b88597 100644 --- a/gnu/llvm/lld/COFF/LTO.cpp +++ b/gnu/llvm/lld/COFF/LTO.cpp @@ -7,11 +7,12 @@ //===----------------------------------------------------------------------===// #include "LTO.h" +#include "COFFLinkerContext.h" #include "Config.h" #include "InputFiles.h" #include "Symbols.h" #include "lld/Common/Args.h" -#include "lld/Common/ErrorHandler.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" @@ -20,10 +21,10 @@ #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/Caching.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -53,16 +54,18 @@ static std::unique_ptr openFile(StringRef file) { return ret; } -static std::string getThinLTOOutputFile(StringRef path) { +std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) { return lto::getThinLTOOutputFile( - std::string(path), std::string(config->thinLTOPrefixReplace.first), - std::string(config->thinLTOPrefixReplace.second)); + std::string(path), std::string(ctx.config.thinLTOPrefixReplace.first), + std::string(ctx.config.thinLTOPrefixReplace.second)); } -static lto::Config createConfig() { +lto::Config BitcodeCompiler::createConfig() { lto::Config c; c.Options = initTargetOptionsFromCodeGenFlags(); c.Options.EmitAddrsig = true; + for (StringRef C : ctx.config.mllvmOpts) + c.MllvmArgs.emplace_back(C.str()); // 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. @@ -72,48 +75,52 @@ static lto::Config createConfig() { // 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) + if (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386) c.RelocModel = Reloc::Static; else c.RelocModel = Reloc::PIC_; +#ifndef NDEBUG + c.DisableVerify = false; +#else c.DisableVerify = true; +#endif c.DiagHandler = diagnosticHandler; - c.OptLevel = config->ltoo; + c.OptLevel = ctx.config.ltoo; c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); - c.CGOptLevel = args::getCGOptLevel(config->ltoo); - c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); - c.UseNewPM = config->ltoNewPassManager; - c.DebugPassManager = config->ltoDebugPassManager; - c.CSIRProfile = std::string(config->ltoCSProfileFile); - c.RunCSIRInstr = config->ltoCSProfileGenerate; - - if (config->saveTemps) - checkError(c.addSaveTemps(std::string(config->outputFile) + ".", + c.CGOptLevel = args::getCGOptLevel(ctx.config.ltoo); + c.AlwaysEmitRegularLTOObj = !ctx.config.ltoObjPath.empty(); + c.DebugPassManager = ctx.config.ltoDebugPassManager; + c.CSIRProfile = std::string(ctx.config.ltoCSProfileFile); + c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate; + c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch; + + if (ctx.config.saveTemps) + checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".", /*UseInputModulePath*/ true)); return c; } -BitcodeCompiler::BitcodeCompiler() { +BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) { // Initialize indexFile. - if (!config->thinLTOIndexOnlyArg.empty()) - indexFile = openFile(config->thinLTOIndexOnlyArg); + if (!ctx.config.thinLTOIndexOnlyArg.empty()) + indexFile = openFile(ctx.config.thinLTOIndexOnlyArg); // Initialize ltoObj. lto::ThinBackend backend; - if (config->thinLTOIndexOnly) { + if (ctx.config.thinLTOIndexOnly) { auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; backend = lto::createWriteIndexesThinBackend( - std::string(config->thinLTOPrefixReplace.first), - std::string(config->thinLTOPrefixReplace.second), - config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); + std::string(ctx.config.thinLTOPrefixReplace.first), + std::string(ctx.config.thinLTOPrefixReplace.second), + ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); } else { backend = lto::createInProcessThinBackend( - llvm::heavyweight_hardware_concurrency(config->thinLTOJobs)); + llvm::heavyweight_hardware_concurrency(ctx.config.thinLTOJobs)); } ltoObj = std::make_unique(createConfig(), backend, - config->ltoPartitions); + ctx.config.ltoPartitions); } BitcodeCompiler::~BitcodeCompiler() = default; @@ -126,7 +133,7 @@ void BitcodeCompiler::add(BitcodeFile &f) { std::vector symBodies = f.getSymbols(); std::vector resols(symBodies.size()); - if (config->thinLTOIndexOnly) + if (ctx.config.thinLTOIndexOnly) thinIndices.insert(obj.getName()); // Provide a resolution to the LTO API for each symbol. @@ -159,21 +166,25 @@ std::vector BitcodeCompiler::compile() { unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); + file_names.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); - })); + FileCache cache; + if (!ctx.config.ltoCache.empty()) + cache = check(localCache("ThinLTO", "Thin", ctx.config.ltoCache, + [&](size_t task, const Twine &moduleName, + std::unique_ptr mb) { + files[task] = std::move(mb); + file_names[task] = moduleName.str(); + })); checkError(ltoObj->run( - [&](size_t task) { - return std::make_unique( - std::make_unique(buf[task])); + [&](size_t task, const Twine &moduleName) { + buf[task].first = moduleName.str(); + return std::make_unique( + std::make_unique(buf[task].second)); }, cache)); @@ -181,49 +192,61 @@ std::vector BitcodeCompiler::compile() { for (StringRef s : thinIndices) { std::string path = getThinLTOOutputFile(s); openFile(path + ".thinlto.bc"); - if (config->thinLTOEmitImportsFiles) + if (ctx.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 (ctx.config.thinLTOIndexOnly) { + if (!ctx.config.ltoObjPath.empty()) + saveBuffer(buf[0].second, ctx.config.ltoObjPath); if (indexFile) indexFile->close(); return {}; } - if (!config->ltoCache.empty()) - pruneCache(config->ltoCache, config->ltoCachePolicy); + if (!ctx.config.ltoCache.empty()) + pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files); std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) { - // Assign unique names to LTO objects. This ensures they have unique names - // in the PDB if one is produced. The names should look like: - // - foo.exe.lto.obj - // - foo.exe.lto.1.obj - // - ... - StringRef ltoObjName = - saver.save(Twine(config->outputFile) + ".lto" + - (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); - + StringRef bitcodeFilePath; // Get the native object contents either from the cache or from memory. Do // not use the cached MemoryBuffer directly, or the PDB will not be // deterministic. StringRef objBuf; - if (files[i]) + if (files[i]) { objBuf = files[i]->getBuffer(); - else - objBuf = buf[i]; + bitcodeFilePath = file_names[i]; + } else { + objBuf = buf[i].second; + bitcodeFilePath = buf[i].first; + } if (objBuf.empty()) continue; - if (config->saveTemps) - saveBuffer(buf[i], ltoObjName); - ret.push_back(make(MemoryBufferRef(objBuf, ltoObjName))); + // If the input bitcode file is path/to/a.obj, then the corresponding lto + // object file name will look something like: path/to/main.exe.lto.a.obj. + StringRef ltoObjName; + if (bitcodeFilePath == "ld-temp.o") { + ltoObjName = + saver().save(Twine(ctx.config.outputFile) + ".lto" + + (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); + } else { + StringRef directory = sys::path::parent_path(bitcodeFilePath); + StringRef baseName = sys::path::filename(bitcodeFilePath); + StringRef outputFileBaseName = sys::path::filename(ctx.config.outputFile); + SmallString<64> path; + sys::path::append(path, directory, + outputFileBaseName + ".lto." + baseName); + sys::path::remove_dots(path, true); + ltoObjName = saver().save(path.str()); + } + if (ctx.config.saveTemps) + saveBuffer(buf[i].second, ltoObjName); + ret.push_back(make(ctx, MemoryBufferRef(objBuf, ltoObjName))); } return ret; diff --git a/gnu/llvm/lld/COFF/LTO.h b/gnu/llvm/lld/COFF/LTO.h index a2b321df790..6826251b5ff 100644 --- a/gnu/llvm/lld/COFF/LTO.h +++ b/gnu/llvm/lld/COFF/LTO.h @@ -27,21 +27,20 @@ #include #include -namespace llvm { -namespace lto { +namespace llvm::lto { +struct Config; class LTO; } -} -namespace lld { -namespace coff { +namespace lld::coff { class BitcodeFile; class InputFile; +class COFFLinkerContext; class BitcodeCompiler { public: - BitcodeCompiler(); + BitcodeCompiler(COFFLinkerContext &ctx); ~BitcodeCompiler(); void add(BitcodeFile &f); @@ -49,12 +48,17 @@ public: private: std::unique_ptr ltoObj; - std::vector> buf; + std::vector>> buf; std::vector> files; + std::vector file_names; std::unique_ptr indexFile; llvm::DenseSet thinIndices; + + std::string getThinLTOOutputFile(StringRef path); + llvm::lto::Config createConfig(); + + COFFLinkerContext &ctx; }; } -} #endif diff --git a/gnu/llvm/lld/COFF/MapFile.cpp b/gnu/llvm/lld/COFF/MapFile.cpp index 41e169ef56e..f7a4ef96129 100644 --- a/gnu/llvm/lld/COFF/MapFile.cpp +++ b/gnu/llvm/lld/COFF/MapFile.cpp @@ -28,6 +28,7 @@ //===----------------------------------------------------------------------===// #include "MapFile.h" +#include "COFFLinkerContext.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" @@ -42,11 +43,6 @@ using namespace llvm::object; using namespace lld; using namespace lld::coff; -static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root()); -static Timer symbolGatherTimer("Gather symbols", totalMapTimer); -static Timer symbolStringsTimer("Build symbol strings", totalMapTimer); -static Timer writeTimer("Write to file", totalMapTimer); - // Print out the first two columns of a line. static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) { os << format(" %04x:%08llx", sec, addr); @@ -67,7 +63,8 @@ static void writeFormattedTimestamp(raw_ostream &os, time_t tds) { time->tm_sec, time->tm_year + 1900); } -static void sortUniqueSymbols(std::vector &syms) { +static void sortUniqueSymbols(std::vector &syms, + uint64_t imageBase) { // Build helper vector using SortEntry = std::pair; std::vector v; @@ -84,11 +81,11 @@ static void sortUniqueSymbols(std::vector &syms) { v.erase(end, v.end()); // Sort by RVA then original order - parallelSort(v, [](const SortEntry &a, const SortEntry &b) { - // Add config->imageBase to avoid comparing "negative" RVAs. + parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) { + // Add config.imageBase to avoid comparing "negative" RVAs. // This can happen with symbols of Absolute kind - uint64_t rvaa = config->imageBase + a.first->getRVA(); - uint64_t rvab = config->imageBase + b.first->getRVA(); + uint64_t rvaa = imageBase + a.first->getRVA(); + uint64_t rvab = imageBase + b.first->getRVA(); return rvaa < rvab || (rvaa == rvab && a.second < b.second); }); @@ -98,10 +95,11 @@ static void sortUniqueSymbols(std::vector &syms) { } // Returns the lists of all symbols that we want to print out. -static void getSymbols(std::vector &syms, +static void getSymbols(const COFFLinkerContext &ctx, + std::vector &syms, std::vector &staticSyms) { - for (ObjFile *file : ObjFile::instances) + for (ObjFile *file : ctx.objFileInstances) for (Symbol *b : file->getSymbols()) { if (!b || !b->isLive()) continue; @@ -119,7 +117,7 @@ static void getSymbols(std::vector &syms, } } - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -136,15 +134,15 @@ static void getSymbols(std::vector &syms, syms.push_back(impSym); } - sortUniqueSymbols(syms); - sortUniqueSymbols(staticSyms); + sortUniqueSymbols(syms, ctx.config.imageBase); + sortUniqueSymbols(staticSyms, ctx.config.imageBase); } // Construct a map from symbols to their stringified representations. static DenseMap -getSymbolStrings(ArrayRef syms) { +getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef syms) { std::vector str(syms.size()); - parallelForEachN((size_t)0, syms.size(), [&](size_t i) { + parallelFor((size_t)0, syms.size(), [&](size_t i) { raw_string_ostream os(str[i]); Defined *sym = syms[i]; @@ -161,7 +159,7 @@ getSymbolStrings(ArrayRef syms) { fileDescr = ""; } else if (Chunk *chunk = sym->getChunk()) { address = sym->getRVA(); - if (OutputSection *sec = chunk->getOutputSection()) + if (OutputSection *sec = ctx.getOutputSection(chunk)) address -= sec->header.VirtualAddress; sectionIdx = chunk->getOutputSectionIdx(); @@ -187,7 +185,7 @@ getSymbolStrings(ArrayRef syms) { os << " "; os << left_justify(sym->getName(), 26); os << " "; - os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16); + os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16); if (!fileDescr.empty()) { os << " "; // FIXME : Handle "f" and "i" flags sometimes generated // by link.exe in those spaces @@ -201,54 +199,56 @@ getSymbolStrings(ArrayRef syms) { return ret; } -void lld::coff::writeMapFile(ArrayRef outputSections) { - if (config->mapFile.empty()) +void lld::coff::writeMapFile(COFFLinkerContext &ctx) { + if (ctx.config.mapFile.empty()) return; std::error_code ec; - raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); + raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None); if (ec) - fatal("cannot open " + config->mapFile + ": " + ec.message()); + fatal("cannot open " + ctx.config.mapFile + ": " + ec.message()); - ScopedTimer t1(totalMapTimer); + ScopedTimer t1(ctx.totalMapTimer); // Collect symbol info that we want to print out. - ScopedTimer t2(symbolGatherTimer); + ScopedTimer t2(ctx.symbolGatherTimer); std::vector syms; std::vector staticSyms; - getSymbols(syms, staticSyms); + getSymbols(ctx, syms, staticSyms); t2.stop(); - ScopedTimer t3(symbolStringsTimer); - DenseMap symStr = getSymbolStrings(syms); - DenseMap staticSymStr = getSymbolStrings(staticSyms); + ScopedTimer t3(ctx.symbolStringsTimer); + DenseMap symStr = getSymbolStrings(ctx, syms); + DenseMap staticSymStr = + getSymbolStrings(ctx, staticSyms); t3.stop(); - ScopedTimer t4(writeTimer); - SmallString<128> AppName = sys::path::filename(config->outputFile); + ScopedTimer t4(ctx.writeTimer); + SmallString<128> AppName = sys::path::filename(ctx.config.outputFile); sys::path::replace_extension(AppName, ""); // Print out the file header os << " " << AppName << "\n"; os << "\n"; - os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " ("; - if (config->repro) { + os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8) + << " ("; + if (ctx.config.repro) { os << "Repro mode"; } else { - writeFormattedTimestamp(os, config->timestamp); + writeFormattedTimestamp(os, ctx.config.timestamp); } os << ")\n"; os << "\n"; os << " Preferred load address is " - << format_hex_no_prefix(config->imageBase, 16) << "\n"; + << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n"; os << "\n"; // Print out section table. os << " Start Length Name Class\n"; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { // Merge display of chunks with same sectionName std::vector> ChunkRanges; for (Chunk *c : sec->chunks) { @@ -297,13 +297,13 @@ void lld::coff::writeMapFile(ArrayRef outputSections) { uint16_t entrySecIndex = 0; uint64_t entryAddress = 0; - if (!config->noEntry) { - Defined *entry = dyn_cast_or_null(config->entry); + if (!ctx.config.noEntry) { + Defined *entry = dyn_cast_or_null(ctx.config.entry); if (entry) { Chunk *chunk = entry->getChunk(); entrySecIndex = chunk->getOutputSectionIdx(); entryAddress = - entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress; + entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress; } } os << " entry point at "; @@ -317,6 +317,19 @@ void lld::coff::writeMapFile(ArrayRef outputSections) { for (Defined *sym : staticSyms) os << staticSymStr[sym] << '\n'; + // Print out the exported functions + if (ctx.config.mapInfo) { + os << "\n"; + os << " Exports\n"; + os << "\n"; + os << " ordinal name\n\n"; + for (Export &e : ctx.config.exports) { + os << format(" %7d", e.ordinal) << " " << e.name << "\n"; + if (!e.extName.empty() && e.extName != e.name) + os << " exported name: " << e.extName << "\n"; + } + } + t4.stop(); t1.stop(); } diff --git a/gnu/llvm/lld/COFF/MapFile.h b/gnu/llvm/lld/COFF/MapFile.h index 2bf01bd0728..de1e9901742 100644 --- a/gnu/llvm/lld/COFF/MapFile.h +++ b/gnu/llvm/lld/COFF/MapFile.h @@ -9,13 +9,9 @@ #ifndef LLD_COFF_MAPFILE_H #define LLD_COFF_MAPFILE_H -#include "llvm/ADT/ArrayRef.h" - -namespace lld { -namespace coff { -class OutputSection; -void writeMapFile(llvm::ArrayRef outputSections); -} +namespace lld::coff { +class COFFLinkerContext; +void writeMapFile(COFFLinkerContext &ctx); } #endif diff --git a/gnu/llvm/lld/COFF/MarkLive.cpp b/gnu/llvm/lld/COFF/MarkLive.cpp index 0afa615a193..ad8c340f184 100644 --- a/gnu/llvm/lld/COFF/MarkLive.cpp +++ b/gnu/llvm/lld/COFF/MarkLive.cpp @@ -6,22 +6,20 @@ // //===----------------------------------------------------------------------===// +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/Timer.h" #include "llvm/ADT/STLExtras.h" #include -namespace lld { -namespace coff { - -static Timer gctimer("GC", Timer::root()); +namespace lld::coff { // Set live bit on for each reachable chunk. Unmarked (unreachable) // COMDAT chunks will be ignored by Writer, so they will be excluded // from the final output. -void markLive(ArrayRef chunks) { - ScopedTimer t(gctimer); +void markLive(COFFLinkerContext &ctx) { + ScopedTimer t(ctx.gcTimer); // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark @@ -31,7 +29,7 @@ void markLive(ArrayRef chunks) { // COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not // traverse DWARF sections. They are live, but they should not keep other // sections alive. - for (Chunk *c : chunks) + for (Chunk *c : ctx.symtab.getChunks()) if (auto *sc = dyn_cast(c)) if (sc->live && !sc->isDWARF()) worklist.push_back(sc); @@ -53,7 +51,7 @@ void markLive(ArrayRef chunks) { }; // Add GC root chunks. - for (Symbol *b : config->gcroot) + for (Symbol *b : ctx.config.gcroot) addSym(b); while (!worklist.empty()) { @@ -70,6 +68,4 @@ void markLive(ArrayRef chunks) { enqueue(&c); } } - -} } diff --git a/gnu/llvm/lld/COFF/MarkLive.h b/gnu/llvm/lld/COFF/MarkLive.h index e4e4c31c7c7..8382223d41a 100644 --- a/gnu/llvm/lld/COFF/MarkLive.h +++ b/gnu/llvm/lld/COFF/MarkLive.h @@ -10,16 +10,13 @@ #define LLD_COFF_MARKLIVE_H #include "lld/Common/LLVM.h" -#include "llvm/ADT/ArrayRef.h" -namespace lld { -namespace coff { +namespace lld::coff { -class Chunk; +class COFFLinkerContext; -void markLive(ArrayRef chunks); +void markLive(COFFLinkerContext &ctx); -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif // LLD_COFF_MARKLIVE_H diff --git a/gnu/llvm/lld/COFF/MinGW.cpp b/gnu/llvm/lld/COFF/MinGW.cpp index 7c1891e67d4..71aa596ea6a 100644 --- a/gnu/llvm/lld/COFF/MinGW.cpp +++ b/gnu/llvm/lld/COFF/MinGW.cpp @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// #include "MinGW.h" +#include "COFFLinkerContext.h" #include "Driver.h" #include "InputFiles.h" #include "SymbolTable.h" -#include "lld/Common/ErrorHandler.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Object/COFF.h" @@ -23,7 +23,10 @@ using namespace llvm::COFF; using namespace lld; using namespace lld::coff; -AutoExporter::AutoExporter() { +AutoExporter::AutoExporter( + COFFLinkerContext &ctx, + const llvm::DenseSet &manualExcludeSymbols) + : manualExcludeSymbols(manualExcludeSymbols), ctx(ctx) { excludeLibs = { "libgcc", "libgcc_s", @@ -46,6 +49,9 @@ AutoExporter::AutoExporter() { "libclang_rt.profile-x86_64", "libc++", "libc++abi", + "libFortran_main", + "libFortranRuntime", + "libFortranDecimal", "libunwind", "libmsvcrt", "libucrtbase", @@ -78,7 +84,7 @@ AutoExporter::AutoExporter() { "_NULL_THUNK_DATA", }; - if (config->machine == I386) { + if (ctx.config.machine == I386) { excludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", "__pei386_runtime_relocator", @@ -122,6 +128,10 @@ void AutoExporter::addWholeArchive(StringRef path) { excludeLibs.erase(libName); } +void AutoExporter::addExcludedSymbol(StringRef symbol) { + excludeSymbols.insert(symbol); +} + bool AutoExporter::shouldExport(Defined *sym) const { if (!sym || !sym->getChunk()) return false; @@ -130,7 +140,7 @@ bool AutoExporter::shouldExport(Defined *sym) const { // disallow import symbols. if (!isa(sym) && !isa(sym)) return false; - if (excludeSymbols.count(sym->getName())) + if (excludeSymbols.count(sym->getName()) || manualExcludeSymbols.count(sym->getName())) return false; for (StringRef prefix : excludeSymbolPrefixes.keys()) @@ -141,7 +151,7 @@ bool AutoExporter::shouldExport(Defined *sym) const { return false; // If a corresponding __imp_ symbol exists and is defined, don't export it. - if (symtab->find(("__imp_" + sym->getName()).str())) + if (ctx.symtab.find(("__imp_" + sym->getName()).str())) return false; // Check that file is non-null before dereferencing it, symbols not @@ -160,14 +170,15 @@ bool AutoExporter::shouldExport(Defined *sym) const { return !excludeObjects.count(fileName); } -void lld::coff::writeDefFile(StringRef name) { +void lld::coff::writeDefFile(StringRef name, + const std::vector &exports) { std::error_code ec; 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) { + for (const Export &e : exports) { os << " " << e.exportName << " " << "@" << e.ordinal; if (auto *def = dyn_cast_or_null(e.sym)) { @@ -179,11 +190,11 @@ void lld::coff::writeDefFile(StringRef name) { } } -static StringRef mangle(Twine sym) { - assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); - if (config->machine == I386) - return saver.save("_" + sym); - return saver.save(sym); +static StringRef mangle(Twine sym, MachineTypes machine) { + assert(machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (machine == I386) + return saver().save("_" + sym); + return saver().save(sym); } // Handles -wrap option. @@ -192,7 +203,7 @@ static StringRef mangle(Twine sym) { // like they are not being used at all, so we explicitly set some flags so // that LTO won't eliminate them. std::vector -lld::coff::addWrappedSymbols(opt::InputArgList &args) { +lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) { std::vector v; DenseSet seen; @@ -201,18 +212,20 @@ lld::coff::addWrappedSymbols(opt::InputArgList &args) { if (!seen.insert(name).second) continue; - Symbol *sym = symtab->findUnderscore(name); + Symbol *sym = ctx.symtab.findUnderscore(name); if (!sym) continue; - Symbol *real = symtab->addUndefined(mangle("__real_" + name)); - Symbol *wrap = symtab->addUndefined(mangle("__wrap_" + name)); + Symbol *real = + ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine)); + Symbol *wrap = + ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine)); v.push_back({sym, real, wrap}); // These symbols may seem undefined initially, but don't bail out - // at symtab->reportUnresolvable() due to them, but let wrapSymbols + // at symtab.reportUnresolvable() due to them, but let wrapSymbols // below sort things out before checking finally with - // symtab->resolveRemainingUndefines(). + // symtab.resolveRemainingUndefines(). sym->deferUndefined = true; real->deferUndefined = true; // We want to tell LTO not to inline symbols to be overwritten @@ -233,28 +246,29 @@ lld::coff::addWrappedSymbols(opt::InputArgList &args) { // When this function is executed, only InputFiles and symbol table // contain pointers to symbol objects. We visit them to replace pointers, // so that wrapped symbols are swapped as instructed by the command line. -void lld::coff::wrapSymbols(ArrayRef wrapped) { +void lld::coff::wrapSymbols(COFFLinkerContext &ctx, + ArrayRef wrapped) { DenseMap map; for (const WrappedSymbol &w : wrapped) { map[w.sym] = w.wrap; map[w.real] = w.sym; if (Defined *d = dyn_cast(w.wrap)) { - Symbol *imp = symtab->find(("__imp_" + w.sym->getName()).str()); + Symbol *imp = ctx.symtab.find(("__imp_" + w.sym->getName()).str()); // Create a new defined local import for the wrap symbol. If // no imp prefixed symbol existed, there's no need for it. // (We can't easily distinguish whether any object file actually // referenced it or not, though.) if (imp) { DefinedLocalImport *wrapimp = make( - saver.save("__imp_" + w.wrap->getName()), d); - symtab->localImportChunks.push_back(wrapimp->getChunk()); + ctx, saver().save("__imp_" + w.wrap->getName()), d); + ctx.symtab.localImportChunks.push_back(wrapimp->getChunk()); map[imp] = wrapimp; } } } // Update pointers in input files. - parallelForEach(ObjFile::instances, [&](ObjFile *file) { + parallelForEach(ctx.objFileInstances, [&](ObjFile *file) { MutableArrayRef syms = file->getMutableSymbols(); for (size_t i = 0, e = syms.size(); i != e; ++i) if (Symbol *s = map.lookup(syms[i])) diff --git a/gnu/llvm/lld/COFF/MinGW.h b/gnu/llvm/lld/COFF/MinGW.h index 2f2bd119c33..aa5e53278ca 100644 --- a/gnu/llvm/lld/COFF/MinGW.h +++ b/gnu/llvm/lld/COFF/MinGW.h @@ -13,20 +13,23 @@ #include "Symbols.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Option/ArgList.h" #include -namespace lld { -namespace coff { +namespace lld::coff { +class COFFLinkerContext; // Logic for deciding what symbols to export, when exporting all // symbols for MinGW. class AutoExporter { public: - AutoExporter(); + AutoExporter(COFFLinkerContext &ctx, + const llvm::DenseSet &manualExcludeSymbols); void addWholeArchive(StringRef path); + void addExcludedSymbol(StringRef symbol); llvm::StringSet<> excludeSymbols; llvm::StringSet<> excludeSymbolPrefixes; @@ -34,10 +37,15 @@ public: llvm::StringSet<> excludeLibs; llvm::StringSet<> excludeObjects; + const llvm::DenseSet &manualExcludeSymbols; + bool shouldExport(Defined *sym) const; + +private: + COFFLinkerContext &ctx; }; -void writeDefFile(StringRef name); +void writeDefFile(StringRef name, const std::vector &exports); // The -wrap option is a feature to rename symbols so that you can write // wrappers for existing functions. If you pass `-wrap:foo`, all @@ -53,11 +61,11 @@ struct WrappedSymbol { Symbol *wrap; }; -std::vector addWrappedSymbols(llvm::opt::InputArgList &args); +std::vector addWrappedSymbols(COFFLinkerContext &ctx, + llvm::opt::InputArgList &args); -void wrapSymbols(ArrayRef wrapped); +void wrapSymbols(COFFLinkerContext &ctx, ArrayRef wrapped); -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/Options.td b/gnu/llvm/lld/COFF/Options.td index 2ce145520ea..2a89509be5f 100644 --- a/gnu/llvm/lld/COFF/Options.td +++ b/gnu/llvm/lld/COFF/Options.td @@ -41,9 +41,12 @@ def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, MetaVarName<"[auto,always,never]">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; +def diasdkdir : P<"diasdkdir", "Set the location of the DIA SDK">; 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 exclude_symbols : P<"exclude-symbols", "Exclude symbols from automatic export">, + MetaVarName<"">; 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", "">; @@ -55,6 +58,8 @@ 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 noimplib : F<"noimplib">, + HelpText<"Don't output an import lib">; def lib : F<"lib">, HelpText<"Act like lib.exe; must be first argument if present">; def libpath : P<"libpath", "Additional library search path">; @@ -78,8 +83,9 @@ 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 pdb : P<"pdb", "PDB file path">; -def pdbstripped : P<"pdbstripped", "Stripped PDB file path">; def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; +def pdbpagesize : P<"pdbpagesize", "PDB page size">; +def pdbstripped : P<"pdbstripped", "Stripped PDB file path">; def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">, MetaVarName<"=">, HelpText<"Embed the contents of in the PDB as named stream ">; @@ -88,9 +94,16 @@ 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 vctoolsdir : P<"vctoolsdir", "Set the location of the VC tools">; +def vctoolsversion : P<"vctoolsversion", + "Specify which VC tools version to use">; def version : P<"version", "Specify a version number in the PE header">; def wholearchive_file : P<"wholearchive", "Include all object files from this library">; +def winsdkdir : P<"winsdkdir", "Set the location of the Windows SDK">; +def winsdkversion : P<"winsdkversion", "Specify which SDK version to use">; +def winsysroot : P<"winsysroot", + "Adds several subdirectories to the library search paths">; def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, Alias; @@ -148,6 +161,8 @@ def swaprun_net : F<"swaprun:net">, Alias, AliasArgs<["net"]>, def verbose : F<"verbose">; def wholearchive_flag : F<"wholearchive">, HelpText<"Include all object files from all libraries">; +def release : F<"release">, + HelpText<"Set the Checksum in the header of an PE file">; def force : F<"force">, HelpText<"Allow undefined and multiply defined symbols">; @@ -157,7 +172,8 @@ 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 WX : B<"WX", "Treat warnings as errors", + "Don't treat warnings as errors (default)">; defm allowbind : B<"allowbind", "Enable DLL binding (default)", "Disable DLL binding">; @@ -178,6 +194,9 @@ defm highentropyva : B<"highentropyva", defm incremental : B<"incremental", "Keep original import library if contents are unchanged", "Overwrite import library even if contents are unchanged">; +defm inferasanlibs : B<"inferasanlibs", + "Unused, generates a warning", + "No effect (default)">; defm integritycheck : B<"integritycheck", "Set FORCE_INTEGRITY bit in PE header", "No effect (default)">; @@ -244,13 +263,17 @@ def lto_cs_profile_generate: F<"lto-cs-profile-generate">, HelpText<"Perform context sensitive PGO instrumentation">; def lto_cs_profile_file : P<"lto-cs-profile-file", "Context sensitive profile file path">; +defm lto_pgo_warn_mismatch: B< + "lto-pgo-warn-mismatch", + "turn on warnings about profile cfg mismatch (default)>", + "turn off warnings about profile cfg mismatch">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Display the version number and exit">; def threads : P<"threads", "Number of threads. '1' disables multi-threading. By " "default all available hardware threads are used">; def call_graph_ordering_file: P< - "call-graph-ordering-file", + "call-graph-ordering-file", "Layout sections to optimize the given callgraph">; defm call_graph_profile_sort: B< "call-graph-profile-sort", @@ -262,11 +285,14 @@ def print_symbol_order: P< "/call-graph-profile-sort into the specified file">; def wrap : P_priv<"wrap">; +def vfsoverlay : P<"vfsoverlay", "Path to a vfsoverlay yaml file to optionally look for /defaultlib's in">; + // Flags for debugging def lldmap : F<"lldmap">; -def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">; +def lldmap_file : P_priv<"lldmap">; def map : F<"map">; -def map_file : Joined<["/", "-", "/?", "-?"], "map:">; +def map_file : P_priv<"map">; +def map_info : P<"mapinfo", "Include the specified information in a map file">; def show_timing : F<"time">; def summary : F<"summary">; @@ -274,19 +300,26 @@ 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 ltcg : F<"ltcg">; +def assemblydebug : F<"assemblydebug">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; def editandcontinue : F<"editandcontinue">; def fastfail : F<"fastfail">; +def kernel : F<"kernel">; +def pdbcompress : F<"pdbcompress">; +def emitpogophaseinfo : F<"emitpogophaseinfo">; -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">; +def delay : P_priv<"delay">; +def errorreport : P_priv<"errorreport">; +def idlout : P_priv<"idlout">; +def ilk : P_priv<"ilk">; +def ltcg_opt : P_priv<"ltcg">; +def assemblydebug_opt : P_priv<"assemblydebug">; +def ltcgout : P_priv<"ltcgout">; +def maxilksize : P_priv<"maxilksize">; +def tlbid : P_priv<"tlbid">; +def tlbout : P_priv<"tlbout">; +def verbose_all : P_priv<"verbose">; +def guardsym : P_priv<"guardsym">; diff --git a/gnu/llvm/lld/COFF/PDB.cpp b/gnu/llvm/lld/COFF/PDB.cpp index e355857dd93..745afab12e8 100644 --- a/gnu/llvm/lld/COFF/PDB.cpp +++ b/gnu/llvm/lld/COFF/PDB.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "PDB.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" @@ -15,9 +16,10 @@ #include "Symbols.h" #include "TypeMerger.h" #include "Writer.h" -#include "lld/Common/ErrorHandler.h" #include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" @@ -55,6 +57,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include +#include using namespace llvm; using namespace llvm::codeview; @@ -64,18 +67,6 @@ using namespace lld::coff; using llvm::object::coff_section; using llvm::pdb::StringTableFixup; -static ExitOnError exitOnErr; - -static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); -static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer); -Timer lld::coff::loadGHashTimer("Global Type Hashing", addObjectsTimer); -Timer lld::coff::mergeGHashTimer("GHash Type Merging", addObjectsTimer); -static Timer typeMergingTimer("Type Merging", addObjectsTimer); -static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer); -static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer); -static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer); -static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer); - namespace { class DebugSHandler; @@ -83,8 +74,8 @@ class PDBLinker { friend DebugSHandler; public: - PDBLinker(SymbolTable *symtab) - : symtab(symtab), builder(bAlloc), tMerger(bAlloc) { + PDBLinker(COFFLinkerContext &ctx) + : builder(bAlloc()), tMerger(ctx, bAlloc()), ctx(ctx) { // 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. @@ -107,7 +98,7 @@ public: void addPublicsToPDB(); /// Link info for each import file in the symbol table into the PDB. - void addImportFilesToPDB(ArrayRef outputSections); + void addImportFilesToPDB(); void createModuleDBI(ObjFile *file); @@ -127,7 +118,7 @@ public: std::vector &stringTableFixups, BinaryStreamRef symData); - // Write all module symbols from all all live debug symbol subsections of the + // Write all module symbols from all live debug symbol subsections of the // given object file into the given stream writer. Error writeAllModuleSymbolRecords(ObjFile *file, BinaryStreamWriter &writer); @@ -144,8 +135,7 @@ public: std::vector &storage); /// Add the section map and section contributions to the PDB. - void addSections(ArrayRef outputSections, - ArrayRef sectionTable); + void addSections(ArrayRef sectionTable); /// Write the PDB to disk and store the Guid generated for it in *Guid. void commit(codeview::GUID *guid); @@ -154,12 +144,18 @@ public: void printStats(); private: - SymbolTable *symtab; + void pdbMakeAbsolute(SmallVectorImpl &fileName); + void translateIdSymbols(MutableArrayRef &recordData, + TpiSource *source); + void addCommonLinkerModuleSymbols(StringRef path, + pdb::DbiModuleDescriptorBuilder &mod); pdb::PDBFileBuilder builder; TypeMerger tMerger; + COFFLinkerContext &ctx; + /// PDBs use a single global string table for filenames in the file checksum /// table. DebugStringTableSubsection pdbStrTab; @@ -248,7 +244,7 @@ public: // 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) { +void PDBLinker::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 @@ -263,9 +259,10 @@ static void pdbMakeAbsolute(SmallVectorImpl &fileName) { // 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()) { + if (ctx.config.pdbSourcePath.empty()) { sys::path::native(fileName); sys::fs::make_absolute(fileName); + sys::path::remove_dots(fileName, true); return; } @@ -273,7 +270,7 @@ static void pdbMakeAbsolute(SmallVectorImpl &fileName) { // 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; + SmallString<128> absoluteFileName = ctx.config.pdbSourcePath; sys::path::Style guessedStyle = absoluteFileName.startswith("/") ? sys::path::Style::posix : sys::path::Style::windows; @@ -298,18 +295,19 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, }); } -static void addGHashTypeInfo(pdb::PDBFileBuilder &builder) { +static void addGHashTypeInfo(COFFLinkerContext &ctx, + pdb::PDBFileBuilder &builder) { // Start the TPI or IPI stream header. builder.getTpiBuilder().setVersionHeader(pdb::PdbTpiV80); builder.getIpiBuilder().setVersionHeader(pdb::PdbTpiV80); - for_each(TpiSource::instances, [&](TpiSource *source) { + for (TpiSource *source : ctx.tpiSourceList) { builder.getTpiBuilder().addTypeRecords(source->mergedTpi.recs, source->mergedTpi.recSizes, source->mergedTpi.recHashes); builder.getIpiBuilder().addTypeRecords(source->mergedIpi.recs, source->mergedIpi.recSizes, source->mergedIpi.recHashes); - }); + } } static void @@ -343,8 +341,8 @@ static SymbolKind symbolKind(ArrayRef recordData) { } /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 -static void translateIdSymbols(MutableArrayRef &recordData, - TypeMerger &tMerger, TpiSource *source) { +void PDBLinker::translateIdSymbols(MutableArrayRef &recordData, + TpiSource *source) { RecordPrefix *prefix = reinterpret_cast(recordData.data()); SymbolKind kind = symbolKind(recordData); @@ -375,7 +373,7 @@ static void translateIdSymbols(MutableArrayRef &recordData, // in both cases we just need the second type index. if (!ti->isSimple() && !ti->isNoneType()) { TypeIndex newType = TypeIndex(SimpleTypeKind::NotTranslated); - if (config->debugGHashes) { + if (ctx.config.debugGHashes) { auto idToType = tMerger.funcIdToType.find(*ti); if (idToType != tMerger.funcIdToType.end()) newType = idToType->second; @@ -447,7 +445,6 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, unsigned symbolScopeDepth) { switch (sym.kind()) { case SymbolKind::S_GDATA32: - case SymbolKind::S_CONSTANT: case SymbolKind::S_GTHREAD32: // 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 @@ -456,8 +453,9 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, 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. + // S_UDT and S_CONSTANT records go in the module stream if it is not a global record. case SymbolKind::S_UDT: + case SymbolKind::S_CONSTANT: return symbolScopeDepth > 0; // S_GDATA32 does not go in the module stream, but S_LDATA32 does. case SymbolKind::S_LDATA32: @@ -470,7 +468,6 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, static bool symbolGoesInGlobalsStream(const CVSymbol &sym, unsigned symbolScopeDepth) { switch (sym.kind()) { - case SymbolKind::S_CONSTANT: case SymbolKind::S_GDATA32: case SymbolKind::S_GTHREAD32: case SymbolKind::S_GPROC32: @@ -487,6 +484,7 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym, case SymbolKind::S_UDT: case SymbolKind::S_LDATA32: case SymbolKind::S_LTHREAD32: + case SymbolKind::S_CONSTANT: return symbolScopeDepth == 0; default: return false; @@ -496,7 +494,7 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym, static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, unsigned symOffset, std::vector &symStorage) { - CVSymbol sym(makeArrayRef(symStorage)); + CVSymbol sym{ArrayRef(symStorage)}; switch (sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_UDT: @@ -508,9 +506,9 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, case SymbolKind::S_LPROCREF: { // sym is a temporary object, so we have to copy and reallocate the record // to stabilize it. - uint8_t *mem = bAlloc.Allocate(sym.length()); + uint8_t *mem = bAlloc().Allocate(sym.length()); memcpy(mem, sym.data().data(), sym.length()); - builder.addGlobalSymbol(CVSymbol(makeArrayRef(mem, sym.length()))); + builder.addGlobalSymbol(CVSymbol(ArrayRef(mem, sym.length()))); break; } case SymbolKind::S_GPROC32: @@ -580,7 +578,7 @@ void PDBLinker::writeSymbolRecord(SectionChunk *debugChunk, // An object file may have S_xxx_ID symbols, but these get converted to // "real" symbols in a PDB. - translateIdSymbols(recordBytes, tMerger, source); + translateIdSymbols(recordBytes, source); } void PDBLinker::analyzeSymbolSubsection( @@ -647,6 +645,7 @@ void PDBLinker::analyzeSymbolSubsection( Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file, BinaryStreamWriter &writer) { + ExitOnError exitOnErr; std::vector storage; SmallVector scopes; @@ -718,8 +717,9 @@ Error PDBLinker::commitSymbolsForObject(void *ctx, void *obj, static_cast(obj), writer); } -static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { - OutputSection *os = c ? c->getOutputSection() : nullptr; +static pdb::SectionContrib createSectionContrib(COFFLinkerContext &ctx, + const Chunk *c, uint32_t modi) { + OutputSection *os = c ? ctx.getOutputSection(c) : nullptr; pdb::SectionContrib sc; memset(&sc, 0, sizeof(sc)); sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex; @@ -762,6 +762,7 @@ void DebugSHandler::handleDebugS(SectionChunk *debugChunk) { contents = SectionChunk::consumeDebugMagic(contents, ".debug$S"); DebugSubsectionArray subsections; BinaryStreamReader reader(contents, support::little); + ExitOnError exitOnErr; exitOnErr(reader.readArray(subsections, contents.size())); debugChunk->sortRelocations(); @@ -816,6 +817,10 @@ void DebugSHandler::handleDebugS(SectionChunk *debugChunk) { // Unclear what this is for. break; + case DebugSubsectionKind::XfgHashType: + case DebugSubsectionKind::XfgHashVirtual: + break; + default: warn("ignoring unknown debug$S subsection kind 0x" + utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file)); @@ -867,6 +872,7 @@ Error UnrelocatedDebugSubsection::commit(BinaryStreamWriter &writer) const { TpiSource *source = debugChunk->file->debugTypesObj; DebugInlineeLinesSubsectionRef inlineeLines; BinaryStreamReader storageReader(relocatedBytes, support::little); + ExitOnError exitOnErr; exitOnErr(inlineeLines.initialize(storageReader)); for (const InlineeSourceLine &line : inlineeLines) { TypeIndex &inlinee = *const_cast(&line.Header->Inlinee); @@ -935,6 +941,8 @@ void DebugSHandler::finish() { return; } + ExitOnError exitOnErr; + // Handle FPO data. Each subsection begins with a single image base // relocation, which is then added to the RvaStart of each frame data record // when it is added to the PDB. The string table indices for the FPO program @@ -981,10 +989,10 @@ void DebugSHandler::finish() { // size as the original. Otherwise, the file references in the line and // inlinee line tables will be incorrect. auto newChecksums = std::make_unique(linker.pdbStrTab); - for (FileChecksumEntry &fc : checksums) { + for (const FileChecksumEntry &fc : checksums) { SmallString<128> filename = exitOnErr(cvStrTab.getString(fc.FileNameOffset)); - pdbMakeAbsolute(filename); + linker.pdbMakeAbsolute(filename); exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); } @@ -995,8 +1003,8 @@ void DebugSHandler::finish() { file.moduleDBI->addDebugSubsection(std::move(newChecksums)); } -static void warnUnusable(InputFile *f, Error e) { - if (!config->warnDebugInfoUnusable) { +static void warnUnusable(InputFile *f, Error e, bool shouldWarn) { + if (!shouldWarn) { consumeError(std::move(e)); return; } @@ -1009,11 +1017,11 @@ static void warnUnusable(InputFile *f, Error e) { // Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef relocateDebugChunk(SectionChunk &debugChunk) { - uint8_t *buffer = bAlloc.Allocate(debugChunk.getSize()); + uint8_t *buffer = bAlloc().Allocate(debugChunk.getSize()); assert(debugChunk.getOutputSectionIdx() == 0 && "debug sections should not be in output sections"); debugChunk.writeTo(buffer); - return makeArrayRef(buffer, debugChunk.getSize()); + return ArrayRef(buffer, debugChunk.getSize()); } void PDBLinker::addDebugSymbols(TpiSource *source) { @@ -1022,7 +1030,8 @@ void PDBLinker::addDebugSymbols(TpiSource *source) { if (!source->file) return; - ScopedTimer t(symbolMergingTimer); + ScopedTimer t(ctx.symbolMergingTimer); + ExitOnError exitOnErr; pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); DebugSHandler dsh(*this, *source->file, source); // Now do all live .debug$S and .debug$F sections. @@ -1064,6 +1073,7 @@ void PDBLinker::addDebugSymbols(TpiSource *source) { void PDBLinker::createModuleDBI(ObjFile *file) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); SmallString<128> objName; + ExitOnError exitOnErr; bool inArchive = !file->parentName.empty(); objName = inArchive ? file->parentName : file->getName(); @@ -1081,7 +1091,7 @@ void PDBLinker::createModuleDBI(ObjFile *file) { auto *secChunk = dyn_cast(c); if (!secChunk || !secChunk->live) continue; - pdb::SectionContrib sc = createSectionContrib(secChunk, modi); + pdb::SectionContrib sc = createSectionContrib(ctx, secChunk, modi); file->moduleDBI->setFirstSectionContrib(sc); break; } @@ -1093,11 +1103,12 @@ void PDBLinker::addDebug(TpiSource *source) { // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. If we are using ghashes, types have // already been merged. - if (!config->debugGHashes) { - ScopedTimer t(typeMergingTimer); + if (!ctx.config.debugGHashes) { + ScopedTimer t(ctx.typeMergingTimer); if (Error e = source->mergeDebugT(&tMerger)) { // If type merging failed, ignore the symbols. - warnUnusable(source->file, std::move(e)); + warnUnusable(source->file, std::move(e), + ctx.config.warnDebugInfoUnusable); return; } } @@ -1105,14 +1116,15 @@ void PDBLinker::addDebug(TpiSource *source) { // If type merging failed, ignore the symbols. Error typeError = std::move(source->typeMergingError); if (typeError) { - warnUnusable(source->file, std::move(typeError)); + warnUnusable(source->file, std::move(typeError), + ctx.config.warnDebugInfoUnusable); return; } addDebugSymbols(source); } -static pdb::BulkPublic createPublic(Defined *def) { +static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) { pdb::BulkPublic pub; pub.Name = def->getName().data(); pub.NameLen = def->getName().size(); @@ -1126,7 +1138,7 @@ static pdb::BulkPublic createPublic(Defined *def) { } pub.setFlags(flags); - OutputSection *os = def->getChunk()->getOutputSection(); + OutputSection *os = ctx.getOutputSection(def->getChunk()); assert(os && "all publics should be in final image"); pub.Offset = def->getRVA() - os->getRVA(); pub.Segment = os->sectionIndex; @@ -1136,52 +1148,54 @@ static pdb::BulkPublic createPublic(Defined *def) { // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { - ScopedTimer t1(addObjectsTimer); + ScopedTimer t1(ctx.addObjectsTimer); // Create module descriptors - for_each(ObjFile::instances, [&](ObjFile *obj) { createModuleDBI(obj); }); + for (ObjFile *obj : ctx.objFileInstances) + createModuleDBI(obj); // Reorder dependency type sources to come first. - TpiSource::sortDependencies(); + tMerger.sortDependencies(); // Merge type information from input files using global type hashing. - if (config->debugGHashes) + if (ctx.config.debugGHashes) tMerger.mergeTypesWithGHash(); // Merge dependencies and then regular objects. - for_each(TpiSource::dependencySources, - [&](TpiSource *source) { addDebug(source); }); - for_each(TpiSource::objectSources, - [&](TpiSource *source) { addDebug(source); }); + for (TpiSource *source : tMerger.dependencySources) + addDebug(source); + for (TpiSource *source : tMerger.objectSources) + addDebug(source); builder.getStringTableBuilder().setStrings(pdbStrTab); t1.stop(); // Construct TPI and IPI stream contents. - ScopedTimer t2(tpiStreamLayoutTimer); + ScopedTimer t2(ctx.tpiStreamLayoutTimer); + // Collect all the merged types. - if (config->debugGHashes) { - addGHashTypeInfo(builder); + if (ctx.config.debugGHashes) { + addGHashTypeInfo(ctx, builder); } else { addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); } t2.stop(); - if (config->showSummary) { - for_each(TpiSource::instances, [&](TpiSource *source) { + if (ctx.config.showSummary) { + for (TpiSource *source : ctx.tpiSourceList) { nbTypeRecords += source->nbTypeRecords; nbTypeRecordsBytes += source->nbTypeRecordsBytes; - }); + } } } void PDBLinker::addPublicsToPDB() { - ScopedTimer t3(publicsLayoutTimer); + ScopedTimer t3(ctx.publicsLayoutTimer); // Compute the public symbols. auto &gsiBuilder = builder.getGsiBuilder(); std::vector publics; - symtab->forEachSymbol([&publics](Symbol *s) { + ctx.symtab.forEachSymbol([&publics, this](Symbol *s) { // Only emit external, defined, live symbols that have a chunk. Static, // non-external symbols do not appear in the symbol table. auto *def = dyn_cast(s); @@ -1195,14 +1209,14 @@ void PDBLinker::addPublicsToPDB() { StringRef name = def->getName(); if (name.data()[0] == '_' && name.data()[1] == '_') { // Drop the '_' prefix for x86. - if (config->machine == I386) + if (ctx.config.machine == I386) name = name.drop_front(1); if (name.startswith("__profd_") || name.startswith("__profc_") || name.startswith("__covrec_")) { return; } } - publics.push_back(createPublic(def)); + publics.push_back(createPublic(ctx, def)); } }); @@ -1213,7 +1227,7 @@ void PDBLinker::addPublicsToPDB() { } void PDBLinker::printStats() { - if (!config->showSummary) + if (!ctx.config.showSummary) return; SmallString<256> buffer; @@ -1226,10 +1240,10 @@ void PDBLinker::printStats() { stream << format_decimal(v, 15) << " " << s << '\n'; }; - print(ObjFile::instances.size(), + print(ctx.objFileInstances.size(), "Input OBJ files (expanded from all cmd-line inputs)"); - print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies"); - print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies"); + print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies"); + print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies"); print(nbTypeRecords, "Input type records"); print(nbTypeRecordsBytes, "Input type records bytes"); print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records"); @@ -1281,11 +1295,11 @@ void PDBLinker::printStats() { << "Run llvm-pdbutil to print details about a particular record:\n"; stream << formatv("llvm-pdbutil dump -{0}s -{0}-index {1:X} {2}\n", (name == "TPI" ? "type" : "id"), - tsis.back().typeIndex.getIndex(), config->pdbPath); + tsis.back().typeIndex.getIndex(), ctx.config.pdbPath); } }; - if (!config->debugGHashes) { + if (!ctx.config.debugGHashes) { // FIXME: Reimplement for ghash. printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable()); printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable()); @@ -1295,7 +1309,7 @@ void PDBLinker::printStats() { } void PDBLinker::addNatvisFiles() { - for (StringRef file : config->natvisFiles) { + for (StringRef file : ctx.config.natvisFiles) { ErrorOr> dataOrErr = MemoryBuffer::getFile(file); if (!dataOrErr) { @@ -1305,16 +1319,17 @@ void PDBLinker::addNatvisFiles() { std::unique_ptr data = std::move(*dataOrErr); // Can't use takeBuffer() here since addInjectedSource() takes ownership. - if (driver->tar) - driver->tar->append(relativeToRoot(data->getBufferIdentifier()), - data->getBuffer()); + if (ctx.driver.tar) + ctx.driver.tar->append(relativeToRoot(data->getBufferIdentifier()), + data->getBuffer()); builder.addInjectedSource(file, std::move(data)); } } void PDBLinker::addNamedStreams() { - for (const auto &streamFile : config->namedStreams) { + ExitOnError exitOnErr; + for (const auto &streamFile : ctx.config.namedStreams) { const StringRef stream = streamFile.getKey(), file = streamFile.getValue(); ErrorOr> dataOrErr = MemoryBuffer::getFile(file); @@ -1324,7 +1339,7 @@ void PDBLinker::addNamedStreams() { } std::unique_ptr data = std::move(*dataOrErr); exitOnErr(builder.addNamedStream(stream, data->getBuffer())); - driver->takeBuffer(std::move(data)); + ctx.driver.takeBuffer(std::move(data)); } } @@ -1354,8 +1369,8 @@ static std::string quote(ArrayRef args) { for (StringRef a : args) { if (!r.empty()) r.push_back(' '); - bool hasWS = a.find(' ') != StringRef::npos; - bool hasQ = a.find('"') != StringRef::npos; + bool hasWS = a.contains(' '); + bool hasQ = a.contains('"'); if (hasWS || hasQ) r.push_back('"'); if (hasQ) { @@ -1371,8 +1386,8 @@ static std::string quote(ArrayRef args) { return r; } -static void fillLinkerVerRecord(Compile3Sym &cs) { - cs.Machine = toCodeViewMachine(config->machine); +static void fillLinkerVerRecord(Compile3Sym &cs, MachineTypes machine) { + cs.Machine = toCodeViewMachine(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 @@ -1397,33 +1412,34 @@ static void fillLinkerVerRecord(Compile3Sym &cs) { cs.setLanguage(SourceLanguage::Link); } -static void addCommonLinkerModuleSymbols(StringRef path, - pdb::DbiModuleDescriptorBuilder &mod) { +void PDBLinker::addCommonLinkerModuleSymbols( + StringRef path, pdb::DbiModuleDescriptorBuilder &mod) { ObjNameSym ons(SymbolRecordKind::ObjNameSym); EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); - fillLinkerVerRecord(cs); + fillLinkerVerRecord(cs, ctx.config.machine); ons.Name = "* Linker *"; ons.Signature = 0; - ArrayRef args = makeArrayRef(config->argv).drop_front(); + ArrayRef args = ArrayRef(ctx.config.argv).drop_front(); std::string argStr = quote(args); ebs.Fields.push_back("cwd"); SmallString<64> cwd; - if (config->pdbSourcePath.empty()) + if (ctx.config.pdbSourcePath.empty()) sys::fs::current_path(cwd); else - cwd = config->pdbSourcePath; + cwd = ctx.config.pdbSourcePath; ebs.Fields.push_back(cwd); ebs.Fields.push_back("exe"); - SmallString<64> exe = config->argv[0]; + SmallString<64> exe = ctx.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); + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, bAlloc, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( @@ -1455,11 +1471,11 @@ static void addLinkerModuleCoffGroup(PartialSection *sec, cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - cgs, bAlloc, CodeViewContainer::Pdb)); + cgs, bAlloc(), CodeViewContainer::Pdb)); } static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, - OutputSection &os) { + OutputSection &os, bool isMinGW) { SectionSym sym(SymbolRecordKind::SectionSym); sym.Alignment = 12; // 2^12 = 4KB sym.Characteristics = os.header.Characteristics; @@ -1468,11 +1484,11 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, sym.Rva = os.getRVA(); sym.SectionNumber = os.sectionIndex; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - sym, bAlloc, CodeViewContainer::Pdb)); + sym, bAlloc(), 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) + if (isMinGW) return; // Output COFF groups for individual chunks of this section. @@ -1482,13 +1498,14 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, } // Add all import files as modules to the PDB. -void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { - if (ImportFile::instances.empty()) +void PDBLinker::addImportFilesToPDB() { + if (ctx.importFileInstances.empty()) return; + ExitOnError exitOnErr; std::map dllToModuleDbi; - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -1512,7 +1529,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { exitOnErr(dbiBuilder.addModuleInfo(file->dllName)); firstMod.setObjFileName(libPath); pdb::SectionContrib sc = - createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex); firstMod.setFirstSectionContrib(sc); // The second module is where the import stream goes. @@ -1522,7 +1539,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { DefinedImportThunk *thunk = cast(file->thunkSym); Chunk *thunkChunk = thunk->getChunk(); - OutputSection *thunkOS = thunkChunk->getOutputSection(); + OutputSection *thunkOS = ctx.getOutputSection(thunkChunk); ObjNameSym ons(SymbolRecordKind::ObjNameSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); @@ -1532,7 +1549,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { ons.Name = file->dllName; ons.Signature = 0; - fillLinkerVerRecord(cs); + fillLinkerVerRecord(cs, ctx.config.machine); ts.Name = thunk->getName(); ts.Parent = 0; @@ -1543,6 +1560,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { ts.Segment = thunkOS->sectionIndex; ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, bAlloc, CodeViewContainer::Pdb)); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( @@ -1564,28 +1582,27 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { mod->addSymbol(newSym); pdb::SectionContrib sc = - createSectionContrib(thunk->getChunk(), mod->getModuleIndex()); + createSectionContrib(ctx, thunk->getChunk(), mod->getModuleIndex()); mod->setFirstSectionContrib(sc); } } // Creates a PDB file. -void lld::coff::createPDB(SymbolTable *symtab, - ArrayRef outputSections, +void lld::coff::createPDB(COFFLinkerContext &ctx, ArrayRef sectionTable, llvm::codeview::DebugInfo *buildId) { - ScopedTimer t1(totalPdbLinkTimer); - PDBLinker pdb(symtab); + ScopedTimer t1(ctx.totalPdbLinkTimer); + PDBLinker pdb(ctx); pdb.initialize(buildId); pdb.addObjectsToPDB(); - pdb.addImportFilesToPDB(outputSections); - pdb.addSections(outputSections, sectionTable); + pdb.addImportFilesToPDB(); + pdb.addSections(sectionTable); pdb.addNatvisFiles(); pdb.addNamedStreams(); pdb.addPublicsToPDB(); - ScopedTimer t2(diskCommitTimer); + ScopedTimer t2(ctx.diskCommitTimer); codeview::GUID guid; pdb.commit(&guid); memcpy(&buildId->PDB70.Signature, &guid, 16); @@ -1596,7 +1613,8 @@ void lld::coff::createPDB(SymbolTable *symtab, } void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { - exitOnErr(builder.initialize(4096)); // 4096 is blocksize + ExitOnError exitOnErr; + exitOnErr(builder.initialize(ctx.config.pdbPageSize)); buildId->Signature.CVSignature = OMF::Signature::PDB70; // Signature is set to a hash of the PDB contents when the PDB is done. @@ -1617,7 +1635,7 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); dbiBuilder.setAge(buildId->PDB70.Age); dbiBuilder.setVersionHeader(pdb::PdbDbiV70); - dbiBuilder.setMachineType(config->machine); + dbiBuilder.setMachineType(ctx.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 @@ -1625,11 +1643,11 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { dbiBuilder.setBuildNumber(14, 11); } -void PDBLinker::addSections(ArrayRef outputSections, - ArrayRef sectionTable) { +void PDBLinker::addSections(ArrayRef sectionTable) { + ExitOnError exitOnErr; // It's not entirely clear what this is, but the * Linker * module uses it. pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); - nativePath = config->pdbPath; + nativePath = ctx.config.pdbPath; pdbMakeAbsolute(nativePath); uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); @@ -1637,11 +1655,11 @@ void PDBLinker::addSections(ArrayRef outputSections, addCommonLinkerModuleSymbols(nativePath, linkerModule); // Add section contributions. They must be ordered by ascending RVA. - for (OutputSection *os : outputSections) { - addLinkerModuleSectionSymbol(linkerModule, *os); + for (OutputSection *os : ctx.outputSections) { + addLinkerModuleSectionSymbol(linkerModule, *os, ctx.config.mingw); for (Chunk *c : os->chunks) { pdb::SectionContrib sc = - createSectionContrib(c, linkerModule.getModuleIndex()); + createSectionContrib(ctx, c, linkerModule.getModuleIndex()); builder.getDbiBuilder().addSectionContrib(sc); } } @@ -1650,7 +1668,7 @@ void PDBLinker::addSections(ArrayRef outputSections, // 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); + createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex); linkerModule.setFirstSectionContrib(sc); // Add Section Map stream. @@ -1668,14 +1686,14 @@ void PDBLinker::commit(codeview::GUID *guid) { // Print an error and continue if PDB writing fails. This is done mainly so // the user can see the output of /time and /summary, which is very helpful // when trying to figure out why a PDB file is too large. - if (Error e = builder.commit(config->pdbPath, guid)) { + if (Error e = builder.commit(ctx.config.pdbPath, guid)) { checkError(std::move(e)); - error("failed to write PDB file " + Twine(config->pdbPath)); + error("failed to write PDB file " + Twine(ctx.config.pdbPath)); } } -static uint32_t getSecrelReloc() { - switch (config->machine) { +static uint32_t getSecrelReloc(llvm::COFF::MachineTypes machine) { + switch (machine) { case AMD64: return COFF::IMAGE_REL_AMD64_SECREL; case I386: @@ -1700,7 +1718,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr, DebugLinesSubsectionRef &lines, uint32_t &offsetInLinetable) { ExitOnError exitOnErr; - uint32_t secrelReloc = getSecrelReloc(); + const uint32_t secrelReloc = getSecrelReloc(c->file->ctx.config.machine); for (SectionChunk *dbgC : c->file->getDebugChunks()) { if (dbgC->getSectionName() != ".debug$S") @@ -1774,9 +1792,9 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr, } // Use CodeView line tables to resolve a file and line number for the given -// offset into the given chunk and return them, or None if a line table was -// not found. -Optional> +// offset into the given chunk and return them, or std::nullopt if a line table +// was not found. +std::optional> lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) { ExitOnError exitOnErr; @@ -1786,11 +1804,11 @@ lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) { uint32_t offsetInLinetable; if (!findLineTable(c, addr, cvStrTab, checksums, lines, offsetInLinetable)) - return None; + return std::nullopt; - Optional nameIndex; - Optional lineNumber; - for (LineColumnEntry &entry : lines) { + std::optional nameIndex; + std::optional lineNumber; + for (const LineColumnEntry &entry : lines) { for (const LineNumberEntry &ln : entry.LineNumbers) { LineInfo li(ln.Flags); if (ln.Offset > offsetInLinetable) { @@ -1807,7 +1825,7 @@ lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) { } } if (!nameIndex) - return None; + return std::nullopt; StringRef filename = exitOnErr(getFileName(cvStrTab, checksums, *nameIndex)); return std::make_pair(filename, *lineNumber); } diff --git a/gnu/llvm/lld/COFF/PDB.h b/gnu/llvm/lld/COFF/PDB.h index 53506d40bae..991805cc95b 100644 --- a/gnu/llvm/lld/COFF/PDB.h +++ b/gnu/llvm/lld/COFF/PDB.h @@ -10,34 +10,26 @@ #define LLD_COFF_PDB_H #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include -namespace llvm { -namespace codeview { +namespace llvm::codeview { union DebugInfo; } -} namespace lld { class Timer; namespace coff { -class OutputSection; class SectionChunk; -class SymbolTable; +class COFFLinkerContext; -void createPDB(SymbolTable *symtab, - llvm::ArrayRef outputSections, - llvm::ArrayRef sectionTable, +void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef sectionTable, llvm::codeview::DebugInfo *buildId); -llvm::Optional> +std::optional> getFileLineCodeView(const SectionChunk *c, uint32_t addr); -extern Timer loadGHashTimer; -extern Timer mergeGHashTimer; - } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/SymbolTable.cpp b/gnu/llvm/lld/COFF/SymbolTable.cpp index 536f3435072..2ca7b82cac4 100644 --- a/gnu/llvm/lld/COFF/SymbolTable.cpp +++ b/gnu/llvm/lld/COFF/SymbolTable.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SymbolTable.h" +#include "COFFLinkerContext.h" #include "Config.h" #include "Driver.h" #include "LTO.h" @@ -15,7 +16,7 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Timer.h" -#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/DebugInfo/DIContext.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/WindowsMachineFlag.h" @@ -25,8 +26,7 @@ using namespace llvm; -namespace lld { -namespace coff { +namespace lld::coff { StringRef ltrim1(StringRef s, const char *chars) { if (!s.empty() && strchr(chars, s[0])) @@ -34,36 +34,39 @@ StringRef ltrim1(StringRef s, const char *chars) { return s; } -static Timer ltoTimer("LTO", Timer::root()); - -SymbolTable *symtab; - void SymbolTable::addFile(InputFile *file) { log("Reading " + toString(file)); - file->parse(); + if (file->lazy) { + if (auto *f = dyn_cast(file)) + f->parseLazy(); + else + cast(file)->parseLazy(); + } else { + file->parse(); + if (auto *f = dyn_cast(file)) { + ctx.objFileInstances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + ctx.bitcodeFileInstances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + ctx.importFileInstances.push_back(f); + } + } MachineTypes mt = file->getMachineType(); - if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { - config->machine = mt; - } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { + if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) { + ctx.config.machine = mt; + ctx.driver.addWinSysRootLibSearchPaths(); + } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && ctx.config.machine != mt) { error(toString(file) + ": machine type " + machineToStr(mt) + - " conflicts with " + machineToStr(config->machine)); + " conflicts with " + machineToStr(ctx.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); + ctx.driver.parseDirectives(file); } -static void errorOrWarn(const Twine &s) { - if (config->forceUnresolved) +static void errorOrWarn(const Twine &s, bool forceUnresolved) { + if (forceUnresolved) warn(s); else error(s); @@ -78,9 +81,11 @@ static void forceLazy(Symbol *s) { l->file->addMember(l->sym); break; } - case Symbol::Kind::LazyObjectKind: - cast(s)->file->fetch(); + case Symbol::Kind::LazyObjectKind: { + InputFile *file = cast(s)->file; + file->ctx.symtab.addFile(file); break; + } case Symbol::Kind::LazyDLLSymbolKind: { auto *l = cast(s); l->file->makeImport(l->sym); @@ -120,25 +125,25 @@ static std::vector getSymbolLocations(BitcodeFile *file) { return {res}; } -static Optional> +static std::optional> getFileLineDwarf(const SectionChunk *c, uint32_t addr) { - Optional optionalLineInfo = + std::optional optionalLineInfo = c->file->getDILineInfo(addr, c->getSectionNumber() - 1); if (!optionalLineInfo) - return None; + return std::nullopt; const DILineInfo &lineInfo = *optionalLineInfo; if (lineInfo.FileName == DILineInfo::BadString) - return None; - return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line); + return std::nullopt; + return std::make_pair(saver().save(lineInfo.FileName), lineInfo.Line); } -static Optional> +static std::optional> getFileLine(const SectionChunk *c, uint32_t addr) { // MinGW can optionally use codeview, even if the default is dwarf. - Optional> fileLine = + std::optional> fileLine = getFileLineCodeView(c, addr); // If codeview didn't yield any result, check dwarf in MinGW mode. - if (!fileLine && config->mingw) + if (!fileLine && c->file->ctx.config.mingw) fileLine = getFileLineDwarf(c, addr); return fileLine; } @@ -169,7 +174,7 @@ getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) { if (locations.size() >= maxStrings) continue; - Optional> fileLine = + std::optional> fileLine = getFileLine(sc, r.VirtualAddress); Symbol *sym = getSymbol(sc, r.VirtualAddress); if (fileLine) @@ -196,7 +201,7 @@ getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) { << "\n>>> "; os << toString(file); if (loc.sym) - os << ":(" << toString(*loc.sym) << ')'; + os << ":(" << toString(file->ctx, *loc.sym) << ')'; } return std::make_pair(symbolLocations, numLocations); } @@ -231,17 +236,16 @@ struct UndefinedDiag { std::vector files; }; -static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { +static void reportUndefinedSymbol(const COFFLinkerContext &ctx, + const UndefinedDiag &undefDiag) { std::string out; llvm::raw_string_ostream os(out); - os << "undefined symbol: " << toString(*undefDiag.sym); + os << "undefined symbol: " << toString(ctx, *undefDiag.sym); const size_t maxUndefReferences = 3; size_t numDisplayedRefs = 0, numRefs = 0; for (const UndefinedDiag::File &ref : undefDiag.files) { - std::vector symbolLocations; - size_t totalLocations = 0; - std::tie(symbolLocations, totalLocations) = getSymbolLocations( + auto [symbolLocations, totalLocations] = getSymbolLocations( ref.file, ref.symIndex, maxUndefReferences - numDisplayedRefs); numRefs += totalLocations; @@ -252,7 +256,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { } if (numDisplayedRefs < numRefs) os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times"; - errorOrWarn(os.str()); + errorOrWarn(os.str(), ctx.config.forceUnresolved); } void SymbolTable::loadMinGWSymbols() { @@ -266,9 +270,9 @@ void SymbolTable::loadMinGWSymbols() { StringRef name = undef->getName(); - if (config->machine == I386 && config->stdcallFixup) { + if (ctx.config.machine == I386 && ctx.config.stdcallFixup) { // Check if we can resolve an undefined decorated symbol by finding - // the indended target as an undecorated symbol (only with a leading + // the intended target as an undecorated symbol (only with a leading // underscore). StringRef origName = name; StringRef baseName = name; @@ -287,7 +291,7 @@ void SymbolTable::loadMinGWSymbols() { } // If it's lazy or already defined, hook it up as weak alias. if (l->isLazy() || isa(l)) { - if (config->warnStdcallFixup) + if (ctx.config.warnStdcallFixup) warn("Resolving " + origName + " by linking to " + newName); else log("Resolving " + origName + " by linking to " + newName); @@ -297,7 +301,7 @@ void SymbolTable::loadMinGWSymbols() { } } - if (config->autoImport) { + if (ctx.config.autoImport) { if (name.startswith("__imp_")) continue; // If we have an undefined symbol, but we have a lazy symbol we could @@ -355,7 +359,7 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { // 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) { + if (refptr && refptr->getChunk()->getSize() == ctx.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()); @@ -372,23 +376,21 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { /// 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) { - +static void reportProblemSymbols( + const COFFLinkerContext &ctx, const SmallPtrSetImpl &undefs, + const DenseMap *localImports, bool needBitcodeFiles) { // 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) { + for (Symbol *b : ctx.config.gcroot) { if (undefs.count(b)) - errorOrWarn(": undefined symbol: " + toString(*b)); + errorOrWarn(": undefined symbol: " + toString(ctx, *b), + ctx.config.forceUnresolved); if (localImports) if (Symbol *imp = localImports->lookup(b)) - warn(": locally defined symbol imported: " + toString(*imp) + + warn(": locally defined symbol imported: " + toString(ctx, *imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } @@ -413,20 +415,20 @@ reportProblemSymbols(const SmallPtrSetImpl &undefs, if (localImports) if (Symbol *imp = localImports->lookup(sym)) warn(toString(file) + - ": locally defined symbol imported: " + toString(*imp) + + ": locally defined symbol imported: " + toString(ctx, *imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } }; - for (ObjFile *file : objFiles) + for (ObjFile *file : ctx.objFileInstances) processFile(file, file->getSymbols()); - if (bitcodeFiles) - for (BitcodeFile *file : *bitcodeFiles) + if (needBitcodeFiles) + for (BitcodeFile *file : ctx.bitcodeFileInstances) processFile(file, file->getSymbols()); for (const UndefinedDiag &undefDiag : undefDiags) - reportUndefinedSymbol(undefDiag); + reportUndefinedSymbol(ctx, undefDiag); } void SymbolTable::reportUnresolvable() { @@ -446,14 +448,13 @@ void SymbolTable::reportUnresolvable() { } if (name.contains("_PchSym_")) continue; - if (config->autoImport && impSymbol(name)) + if (ctx.config.autoImport && impSymbol(name)) continue; undefs.insert(sym); } - reportProblemSymbols(undefs, - /* localImports */ nullptr, ObjFile::instances, - &BitcodeFile::instances); + reportProblemSymbols(ctx, undefs, + /* localImports */ nullptr, true); } void SymbolTable::resolveRemainingUndefines() { @@ -492,7 +493,7 @@ void SymbolTable::resolveRemainingUndefines() { Symbol *imp = find(name.substr(strlen("__imp_"))); if (imp && isa(imp)) { auto *d = cast(imp); - replaceSymbol(sym, name, d); + replaceSymbol(sym, ctx, name, d); localImportChunks.push_back(cast(sym)->getChunk()); localImports[sym] = d; continue; @@ -504,19 +505,19 @@ void SymbolTable::resolveRemainingUndefines() { if (name.contains("_PchSym_")) continue; - if (config->autoImport && handleMinGWAutomaticImport(sym, name)) + if (ctx.config.autoImport && 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); + if (ctx.config.forceUnresolved) + replaceSymbol(sym, ctx, name, 0); undefs.insert(sym); } reportProblemSymbols( - undefs, config->warnLocallyDefinedImported ? &localImports : nullptr, - ObjFile::instances, /* bitcode files no longer needed */ nullptr); + ctx, undefs, + ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, false); } std::pair SymbolTable::insert(StringRef name) { @@ -541,9 +542,7 @@ std::pair SymbolTable::insert(StringRef name, InputFile *file) { Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, bool isWeakAlias) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name, f); + auto [s, wasInserted] = insert(name, f); if (wasInserted || (s->isLazy() && isWeakAlias)) { replaceSymbol(s, name); return s; @@ -555,9 +554,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { StringRef name = sym.getName(); - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name); + auto [s, wasInserted] = insert(name); if (wasInserted) { replaceSymbol(s, f, sym); return; @@ -569,10 +566,9 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { f->addMember(sym); } -void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n, f); +void SymbolTable::addLazyObject(InputFile *f, StringRef n) { + assert(f->lazy); + auto [s, wasInserted] = insert(n, f); if (wasInserted) { replaceSymbol(s, f, n); return; @@ -581,14 +577,13 @@ void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { if (!u || u->weakAlias || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; - f->fetch(); + f->lazy = false; + addFile(f); } void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, StringRef n) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n); + auto [s, wasInserted] = insert(n); if (wasInserted) { replaceSymbol(s, f, sym, n); return; @@ -611,7 +606,7 @@ static std::string getSourceLocationBitcode(BitcodeFile *file) { static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc, uint32_t offset, StringRef name) { - Optional> fileLine; + std::optional> fileLine; if (sc) fileLine = getFileLine(sc, offset); if (!fileLine) @@ -649,7 +644,7 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile, uint32_t newSectionOffset) { std::string msg; llvm::raw_string_ostream os(msg); - os << "duplicate symbol: " << toString(*existing); + os << "duplicate symbol: " << toString(ctx, *existing); DefinedRegular *d = dyn_cast(existing); if (d && isa(d->getFile())) { @@ -661,19 +656,17 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile, os << getSourceLocation(newFile, newSc, newSectionOffset, existing->getName()); - if (config->forceMultiple) + if (ctx.config.forceMultiple) warn(os.str()); else error(os.str()); } Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n, nullptr); + auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) - replaceSymbol(s, n, sym); + replaceSymbol(s, ctx, n, sym); else if (auto *da = dyn_cast(s)) { if (da->getVA() != sym.getValue()) reportDuplicate(s, nullptr); @@ -683,12 +676,10 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { } Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n, nullptr); + auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) - replaceSymbol(s, n, va); + replaceSymbol(s, ctx, n, va); else if (auto *da = dyn_cast(s)) { if (da->getVA() != va) reportDuplicate(s, nullptr); @@ -698,9 +689,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { } Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n, nullptr); + auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, c); @@ -711,14 +700,12 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, 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)) + uint32_t sectionOffset, bool isWeak) { + auto [s, wasInserted] = insert(n, f); + if (wasInserted || !isa(s) || s->isWeak) replaceSymbol(s, f, n, /*IsCOMDAT*/ false, - /*IsExternal*/ true, sym, c); - else + /*IsExternal*/ true, sym, c, isWeak); + else if (!isWeak) reportDuplicate(s, f, c, sectionOffset); return s; } @@ -726,9 +713,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, std::pair SymbolTable::addComdat(InputFile *f, StringRef n, const coff_symbol_generic *sym) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n, f); + auto [s, wasInserted] = insert(n, f); if (wasInserted || !isa(s)) { replaceSymbol(s, f, n, /*IsCOMDAT*/ true, /*IsExternal*/ true, sym, nullptr); @@ -742,9 +727,7 @@ SymbolTable::addComdat(InputFile *f, StringRef n, 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); + auto [s, wasInserted] = insert(n, f); if (wasInserted || !isa(s)) replaceSymbol(s, f, n, size, sym, c); else if (auto *dc = dyn_cast(s)) @@ -754,9 +737,7 @@ Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, } Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(n, nullptr); + auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, n, f); @@ -769,12 +750,10 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, uint16_t machine) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name, nullptr); + auto [s, wasInserted] = insert(name, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) { - replaceSymbol(s, name, id, machine); + replaceSymbol(s, ctx, name, id, machine); return s; } @@ -797,21 +776,21 @@ void SymbolTable::addLibcall(StringRef name) { } } -std::vector SymbolTable::getChunks() { +std::vector SymbolTable::getChunks() const { std::vector res; - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { ArrayRef v = file->getChunks(); res.insert(res.end(), v.begin(), v.end()); } return res; } -Symbol *SymbolTable::find(StringRef name) { +Symbol *SymbolTable::find(StringRef name) const { return symMap.lookup(CachedHashStringRef(name)); } -Symbol *SymbolTable::findUnderscore(StringRef name) { - if (config->machine == I386) +Symbol *SymbolTable::findUnderscore(StringRef name) const { + if (ctx.config.machine == I386) return find(("_" + name).str()); return find(name); } @@ -832,9 +811,17 @@ std::vector SymbolTable::getSymsWithPrefix(StringRef prefix) { } Symbol *SymbolTable::findMangle(StringRef name) { - if (Symbol *sym = find(name)) - if (!isa(sym)) + if (Symbol *sym = find(name)) { + if (auto *u = dyn_cast(sym)) { + // We're specifically looking for weak aliases that ultimately resolve to + // defined symbols, hence the call to getWeakAlias() instead of just using + // the weakAlias member variable. This matches link.exe's behavior. + if (Symbol *weakAlias = u->getWeakAlias()) + return weakAlias; + } else { 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 @@ -850,7 +837,7 @@ Symbol *SymbolTable::findMangle(StringRef name) { }; // For non-x86, just look for C++ functions. - if (config->machine != I386) + if (ctx.config.machine != I386) return findByPrefix("?" + name + "@@Y"); if (!name.startswith("_")) @@ -872,20 +859,19 @@ Symbol *SymbolTable::addUndefined(StringRef name) { return addUndefined(name, nullptr, false); } -void SymbolTable::addCombinedLTOObjects() { - if (BitcodeFile::instances.empty()) +void SymbolTable::compileBitcodeFiles() { + if (ctx.bitcodeFileInstances.empty()) return; - ScopedTimer t(ltoTimer); - lto.reset(new BitcodeCompiler); - for (BitcodeFile *f : BitcodeFile::instances) + ScopedTimer t(ctx.ltoTimer); + lto.reset(new BitcodeCompiler(ctx)); + for (BitcodeFile *f : ctx.bitcodeFileInstances) lto->add(*f); for (InputFile *newObj : lto->compile()) { ObjFile *obj = cast(newObj); obj->parse(); - ObjFile::instances.push_back(obj); + ctx.objFileInstances.push_back(obj); } } -} // namespace coff -} // namespace lld +} // namespace lld::coff diff --git a/gnu/llvm/lld/COFF/SymbolTable.h b/gnu/llvm/lld/COFF/SymbolTable.h index e88002c8831..33ed65c4580 100644 --- a/gnu/llvm/lld/COFF/SymbolTable.h +++ b/gnu/llvm/lld/COFF/SymbolTable.h @@ -20,11 +20,11 @@ namespace llvm { struct LTOCodeGenerator; } -namespace lld { -namespace coff { +namespace lld::coff { class Chunk; class CommonChunk; +class COFFLinkerContext; class Defined; class DefinedAbsolute; class DefinedRegular; @@ -47,6 +47,8 @@ class Symbol; // There is one add* function per symbol type. class SymbolTable { public: + SymbolTable(COFFLinkerContext &c) : ctx(c) {} + void addFile(InputFile *file); // Emit errors for symbols that cannot be resolved. @@ -63,11 +65,11 @@ public: bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); // Returns a list of chunks of selected symbols. - std::vector getChunks(); + std::vector getChunks() const; // Returns a symbol for a given name. Returns a nullptr if not found. - Symbol *find(StringRef name); - Symbol *findUnderscore(StringRef name); + Symbol *find(StringRef name) const; + Symbol *findUnderscore(StringRef name) const; // Occasionally we have to resolve an undefined symbol to its // mangled symbol. This function tries to find a mangled name @@ -78,7 +80,7 @@ public: // 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(); + void compileBitcodeFiles(); // Creates an Undefined symbol for a given name. Symbol *addUndefined(StringRef name); @@ -88,12 +90,13 @@ public: Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); - void addLazyObject(LazyObjFile *f, StringRef n); + void addLazyObject(InputFile *f, StringRef n); void addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, 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, uint32_t sectionOffset = 0); + SectionChunk *c = nullptr, uint32_t sectionOffset = 0, + bool isWeak = false); std::pair addComdat(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr); @@ -131,15 +134,14 @@ private: llvm::DenseMap symMap; std::unique_ptr lto; -}; -extern SymbolTable *symtab; + COFFLinkerContext &ctx; +}; std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); StringRef ltrim1(StringRef s, const char *chars); -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/Symbols.cpp b/gnu/llvm/lld/COFF/Symbols.cpp index 8a6a9b27d45..c042386e010 100644 --- a/gnu/llvm/lld/COFF/Symbols.cpp +++ b/gnu/llvm/lld/COFF/Symbols.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Symbols.h" +#include "COFFLinkerContext.h" #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" @@ -27,27 +28,29 @@ static_assert(sizeof(SymbolUnion) <= 48, "symbols should be optimized for memory usage"); // Returns a symbol name for an error message. -static std::string maybeDemangleSymbol(StringRef symName) { - if (config->demangle) { +static std::string maybeDemangleSymbol(const COFFLinkerContext &ctx, + StringRef symName) { + if (ctx.config.demangle) { std::string prefix; StringRef prefixless = symName; if (prefixless.consume_front("__imp_")) prefix = "__declspec(dllimport) "; StringRef demangleInput = prefixless; - if (config->machine == I386) + if (ctx.config.machine == I386) demangleInput.consume_front("_"); - std::string demangled = demangle(std::string(demangleInput)); + std::string demangled = demangle(demangleInput.str()); if (demangled != demangleInput) - return prefix + demangle(std::string(demangleInput)); + return prefix + demangle(demangleInput.str()); return (prefix + prefixless).str(); } return std::string(symName); } -std::string toString(coff::Symbol &b) { - return maybeDemangleSymbol(b.getName()); +std::string toString(const COFFLinkerContext &ctx, coff::Symbol &b) { + return maybeDemangleSymbol(ctx, b.getName()); } -std::string toCOFFString(const Archive::Symbol &b) { - return maybeDemangleSymbol(b.getName()); +std::string toCOFFString(const COFFLinkerContext &ctx, + const Archive::Symbol &b) { + return maybeDemangleSymbol(ctx, b.getName()); } namespace coff { @@ -102,23 +105,24 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() { return COFFSymbolRef(reinterpret_cast(sym)); } -uint16_t DefinedAbsolute::numOutputSections; +uint64_t DefinedAbsolute::getRVA() { return va - ctx.config.imageBase; } -static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { +static Chunk *makeImportThunk(COFFLinkerContext &ctx, DefinedImportData *s, + uint16_t machine) { if (machine == AMD64) - return make(s); + return make(ctx, s); if (machine == I386) - return make(s); + return make(ctx, s); if (machine == ARM64) - return make(s); + return make(ctx, s); assert(machine == ARMNT); - return make(s); + return make(ctx, s); } -DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s, - uint16_t machine) +DefinedImportThunk::DefinedImportThunk(COFFLinkerContext &ctx, StringRef name, + DefinedImportData *s, uint16_t machine) : Defined(DefinedImportThunkKind, name), wrappedSym(s), - data(makeImportThunk(s, machine)) {} + data(makeImportThunk(ctx, s, machine)) {} Defined *Undefined::getWeakAlias() { // A weak alias may be a weak alias to another symbol, so check recursively. @@ -130,11 +134,11 @@ Defined *Undefined::getWeakAlias() { MemoryBufferRef LazyArchive::getMemberBuffer() { Archive::Child c = - CHECK(sym.getMember(), - "could not get the member for symbol " + toCOFFString(sym)); + CHECK(sym.getMember(), "could not get the member for symbol " + + toCOFFString(file->ctx, sym)); return CHECK(c.getMemoryBufferRef(), - "could not get the buffer for the member defining symbol " + - toCOFFString(sym)); + "could not get the buffer for the member defining symbol " + + toCOFFString(file->ctx, sym)); } } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/Symbols.h b/gnu/llvm/lld/COFF/Symbols.h index bb911171b1f..750269fd0bb 100644 --- a/gnu/llvm/lld/COFF/Symbols.h +++ b/gnu/llvm/lld/COFF/Symbols.h @@ -22,13 +22,6 @@ 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; @@ -37,6 +30,7 @@ using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; class ArchiveFile; +class COFFLinkerContext; class InputFile; class ObjFile; class SymbolTable; @@ -106,7 +100,11 @@ protected: : symbolKind(k), isExternal(true), isCOMDAT(false), writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false), isRuntimePseudoReloc(false), deferUndefined(false), canInline(true), - nameSize(n.size()), nameData(n.empty() ? nullptr : n.data()) {} + isWeak(false), nameSize(n.size()), + nameData(n.empty() ? nullptr : n.data()) { + assert((!n.empty() || k <= LastDefinedCOFFKind) && + "If the name is empty, the Symbol must be a DefinedCOFF."); + } const unsigned symbolKind : 8; unsigned isExternal : 1; @@ -142,6 +140,11 @@ public: // doesn't know the final contents of the symbol. unsigned canInline : 1; + // True if the symbol is weak. This is only tracked for bitcode/LTO symbols. + // This information isn't written to the output; rather, it's used for + // managing weak symbol overrides. + unsigned isWeak : 1; + protected: // Symbol name length. Assume symbol lengths fit in a 32-bit integer. uint32_t nameSize; @@ -197,10 +200,11 @@ public: DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT, bool isExternal = false, const coff_symbol_generic *s = nullptr, - SectionChunk *c = nullptr) + SectionChunk *c = nullptr, bool isWeak = false) : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) { this->isExternal = isExternal; this->isCOMDAT = isCOMDAT; + this->isWeak = isWeak; } static bool classof(const Symbol *s) { @@ -240,29 +244,25 @@ private: // Absolute symbols. class DefinedAbsolute : public Defined { public: - DefinedAbsolute(StringRef n, COFFSymbolRef s) - : Defined(DefinedAbsoluteKind, n), va(s.getValue()) { + DefinedAbsolute(const COFFLinkerContext &c, StringRef n, COFFSymbolRef s) + : Defined(DefinedAbsoluteKind, n), va(s.getValue()), ctx(c) { isExternal = s.isExternal(); } - DefinedAbsolute(StringRef n, uint64_t v) - : Defined(DefinedAbsoluteKind, n), va(v) {} + DefinedAbsolute(const COFFLinkerContext &c, StringRef n, uint64_t v) + : Defined(DefinedAbsoluteKind, n), va(v), ctx(c) {} static bool classof(const Symbol *s) { return s->kind() == DefinedAbsoluteKind; } - uint64_t getRVA() { return va - config->imageBase; } + uint64_t getRVA(); void setVA(uint64_t v) { va = v; } uint64_t getVA() const { return va; } - // 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; + const COFFLinkerContext &ctx; }; // This symbol is used for linker-synthesized symbols like __ImageBase and @@ -305,10 +305,9 @@ public: class LazyObject : public Symbol { public: - LazyObject(LazyObjFile *f, StringRef n) - : Symbol(LazyObjectKind, n), file(f) {} + LazyObject(InputFile *f, StringRef n) : Symbol(LazyObjectKind, n), file(f) {} static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } - LazyObjFile *file; + InputFile *file; }; // MinGW only. @@ -384,7 +383,8 @@ public: // a regular name. A function pointer is given as a DefinedImportData. class DefinedImportThunk : public Defined { public: - DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); + DefinedImportThunk(COFFLinkerContext &ctx, StringRef name, + DefinedImportData *s, uint16_t machine); static bool classof(const Symbol *s) { return s->kind() == DefinedImportThunkKind; @@ -406,8 +406,9 @@ private: // This is here just for compatibility with MSVC. class DefinedLocalImport : public Defined { public: - DefinedLocalImport(StringRef n, Defined *s) - : Defined(DefinedLocalImportKind, n), data(make(s)) {} + DefinedLocalImport(COFFLinkerContext &ctx, StringRef n, Defined *s) + : Defined(DefinedLocalImportKind, n), + data(make(ctx, s)) {} static bool classof(const Symbol *s) { return s->kind() == DefinedLocalImportKind; @@ -502,6 +503,10 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) { } } // namespace coff +std::string toString(const coff::COFFLinkerContext &ctx, coff::Symbol &b); +std::string toCOFFString(const coff::COFFLinkerContext &ctx, + const llvm::object::Archive::Symbol &b); + } // namespace lld #endif diff --git a/gnu/llvm/lld/COFF/TypeMerger.h b/gnu/llvm/lld/COFF/TypeMerger.h index 72fd5fc72b0..b4e3d6e7d9f 100644 --- a/gnu/llvm/lld/COFF/TypeMerger.h +++ b/gnu/llvm/lld/COFF/TypeMerger.h @@ -10,13 +10,14 @@ #define LLD_COFF_TYPEMERGER_H #include "Config.h" +#include "DebugTypes.h" +#include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeHashing.h" #include "llvm/Support/Allocator.h" #include -namespace lld { -namespace coff { +namespace lld::coff { using llvm::codeview::GloballyHashedType; using llvm::codeview::TypeIndex; @@ -25,19 +26,19 @@ struct GHashState; class TypeMerger { public: - TypeMerger(llvm::BumpPtrAllocator &alloc); + TypeMerger(COFFLinkerContext &ctx, llvm::BumpPtrAllocator &alloc); ~TypeMerger(); /// Get the type table or the global type table if /DEBUG:GHASH is enabled. inline llvm::codeview::TypeCollection &getTypeTable() { - assert(!config->debugGHashes); + assert(!ctx.config.debugGHashes); return typeTable; } /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. inline llvm::codeview::TypeCollection &getIDTable() { - assert(!config->debugGHashes); + assert(!ctx.config.debugGHashes); return idTable; } @@ -59,9 +60,24 @@ public: // keyed by type index. SmallVector tpiCounts; SmallVector ipiCounts; + + /// Dependency type sources, such as type servers or PCH object files. These + /// must be processed before objects that rely on them. Set by + /// sortDependencies. + ArrayRef dependencySources; + + /// Object file sources. These must be processed after dependencySources. + ArrayRef objectSources; + + /// Sorts the dependencies and reassigns TpiSource indices. + void sortDependencies(); + +private: + void clearGHashes(); + + COFFLinkerContext &ctx; }; -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/COFF/Writer.cpp b/gnu/llvm/lld/COFF/Writer.cpp index 37cbe2bb96a..a8fc38c5fa3 100644 --- a/gnu/llvm/lld/COFF/Writer.cpp +++ b/gnu/llvm/lld/COFF/Writer.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Writer.h" +#include "COFFLinkerContext.h" #include "CallGraphSort.h" #include "Config.h" #include "DLL.h" @@ -22,7 +23,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" -#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/COFF.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" @@ -80,23 +81,14 @@ 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]; -} - -void OutputSection::clear() { outputSections.clear(); } - namespace { class DebugDirectoryChunk : public NonSectionChunk { public: - DebugDirectoryChunk(const std::vector> &r, + DebugDirectoryChunk(const COFFLinkerContext &c, + const std::vector> &r, bool writeRepro) - : records(r), writeRepro(writeRepro) {} + : records(r), writeRepro(writeRepro), ctx(c) {} size_t getSize() const override { return (records.size() + int(writeRepro)) * sizeof(debug_directory); @@ -107,7 +99,7 @@ public: for (const std::pair& record : records) { Chunk *c = record.second; - OutputSection *os = c->getOutputSection(); + const OutputSection *os = ctx.getOutputSection(c); uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); fillEntry(d, record.first, c->getSize(), c->getRVA(), offs); ++d; @@ -146,12 +138,15 @@ private: mutable std::vector timeDateStamps; const std::vector> &records; bool writeRepro; + const COFFLinkerContext &ctx; }; class CVDebugRecordChunk : public NonSectionChunk { public: + CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {} + size_t getSize() const override { - return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1; + return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1; } void writeTo(uint8_t *b) const override { @@ -161,12 +156,15 @@ public: // 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'; + if (!ctx.config.pdbAltPath.empty()) + memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size()); + p[ctx.config.pdbAltPath.size()] = '\0'; } mutable codeview::DebugInfo *buildId = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ExtendedDllCharacteristicsChunk : public NonSectionChunk { @@ -190,7 +188,7 @@ public: bool operator<(const PartialSectionKey &other) const { int c = name.compare(other.name); - if (c == 1) + if (c > 0) return false; if (c == 0) return characteristics < other.characteristics; @@ -201,7 +199,8 @@ public: // The writer writes a SymbolTable result to a file. class Writer { public: - Writer() : buffer(errorHandler().outputBuffer) {} + Writer(COFFLinkerContext &c) + : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {} void run(); private: @@ -214,6 +213,12 @@ private: void mergeSections(); void removeUnusedSections(); void assignAddresses(); + bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin); + std::pair getThunk(DenseMap &lastThunks, + Defined *target, uint64_t p, + uint16_t type, int margin); + bool createThunks(OutputSection *os, int margin); + bool verifyRanges(const std::vector chunks); void finalizeAddresses(); void removeEmptySections(); void assignOutputSectionIndices(); @@ -223,6 +228,7 @@ private: void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); + void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols); void createGuardCFTables(); void markSymbolsForRVATable(ObjFile *file, ArrayRef symIdxChunks, @@ -235,17 +241,19 @@ private: void setSectionPermissions(); void writeSections(); void writeBuildId(); + void writePEChecksum(); void sortSections(); void sortExceptionTable(); void sortCRTSectionChunks(std::vector &chunks); void addSyntheticIdata(); + void sortBySectionOrder(std::vector &chunks); void fixPartialSectionChars(StringRef name, uint32_t chars); bool fixGnuImportChunks(); void fixTlsAlignment(); PartialSection *createPartialSection(StringRef name, uint32_t outChars); PartialSection *findPartialSection(StringRef name, uint32_t outChars); - llvm::Optional createSymbol(Defined *d); + std::optional createSymbol(Defined *d); size_t addEntryToStringTable(StringRef str); OutputSection *findSection(StringRef name); @@ -254,6 +262,9 @@ private: uint32_t getSizeOfInitializedData(); + void checkLoadConfig(); + template void checkLoadConfigGuardData(const T *loadConfig); + std::unique_ptr &buffer; std::map partialSections; std::vector strtab; @@ -304,13 +315,12 @@ private: // files, so we need to keep track of them separately. Chunk *firstPdata = nullptr; Chunk *lastPdata; + + COFFLinkerContext &ctx; }; } // anonymous namespace -static Timer codeLayoutTimer("Code Layout", Timer::root()); -static Timer diskCommitTimer("Commit Output File", Timer::root()); - -void lld::coff::writeResult() { Writer().run(); } +void lld::coff::writeResult(COFFLinkerContext &ctx) { Writer(ctx).run(); } void OutputSection::addChunk(Chunk *c) { chunks.push_back(c); @@ -334,14 +344,14 @@ void OutputSection::merge(OutputSection *other) { } // Write the section header to a given buffer. -void OutputSection::writeHeaderTo(uint8_t *buf) { +void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) { 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); + encodeSectionName(hdr->Name, stringTableOff); } else { - assert(!config->debug || name.size() <= COFF::NameSize || + assert(!isDebug || name.size() <= COFF::NameSize || (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); strncpy(hdr->Name, name.data(), std::min(name.size(), (size_t)COFF::NameSize)); @@ -354,8 +364,8 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) { // 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) { +bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { + if (ctx.config.machine == ARMNT) { int64_t diff = AbsoluteDifference(s, p + 4) + margin; switch (relType) { case IMAGE_REL_ARM_BRANCH20T: @@ -366,7 +376,7 @@ static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { default: return true; } - } else if (config->machine == ARM64) { + } else if (ctx.config.machine == ARM64) { int64_t diff = AbsoluteDifference(s, p) + margin; switch (relType) { case IMAGE_REL_ARM64_BRANCH26: @@ -385,24 +395,24 @@ static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { // 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) { +std::pair +Writer::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) { + switch (ctx.config.machine) { case ARMNT: - c = make(target); + c = make(ctx, target); break; case ARM64: - c = make(target); + c = make(ctx, target); break; default: llvm_unreachable("Unexpected architecture"); } - Defined *d = make("", c); + Defined *d = make("range_extension_thunk", c); lastThunk = d; return {d, true}; } @@ -418,7 +428,7 @@ getThunk(DenseMap &lastThunks, Defined *target, uint64_t p, // 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 Writer::createThunks(OutputSection *os, int margin) { bool addressesChanged = false; DenseMap lastThunks; DenseMap, uint32_t> thunkSymtabIndices; @@ -458,11 +468,8 @@ static bool createThunks(OutputSection *os, int margin) { 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 the target isn't in range, hook it up to an existing or new thunk. + auto [thunk, wasNew] = getThunk(lastThunks, sym, p, rel.Type, margin); if (wasNew) { Chunk *thunkChunk = thunk->getChunk(); thunkChunk->setRVA( @@ -491,11 +498,11 @@ static bool createThunks(OutputSection *os, int margin) { ArrayRef curRelocs = sc->getRelocs(); MutableArrayRef newRelocs; if (originalRelocs.data() == curRelocs.data()) { - newRelocs = makeMutableArrayRef( - bAlloc.Allocate(originalRelocs.size()), + newRelocs = MutableArrayRef( + bAlloc().Allocate(originalRelocs.size()), originalRelocs.size()); } else { - newRelocs = makeMutableArrayRef( + newRelocs = MutableArrayRef( const_cast(curRelocs.data()), curRelocs.size()); } @@ -517,7 +524,7 @@ static bool createThunks(OutputSection *os, int margin) { } // Verify that all relocations are in range, with no extra margin requirements. -static bool verifyRanges(const std::vector chunks) { +bool Writer::verifyRanges(const std::vector chunks) { for (Chunk *c : chunks) { SectionChunk *sc = dyn_cast_or_null(c); if (!sc) @@ -545,11 +552,11 @@ static bool verifyRanges(const std::vector chunks) { // Assign addresses and add thunks if necessary. void Writer::finalizeAddresses() { assignAddresses(); - if (config->machine != ARMNT && config->machine != ARM64) + if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64) return; size_t origNumChunks = 0; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { sec->origChunks = sec->chunks; origNumChunks += sec->chunks.size(); } @@ -561,7 +568,7 @@ void Writer::finalizeAddresses() { // adding them turned out ok. bool rangesOk = true; size_t numChunks = 0; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (!verifyRanges(sec->chunks)) { rangesOk = false; break; @@ -582,7 +589,7 @@ void Writer::finalizeAddresses() { // 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) + for (OutputSection *sec : ctx.outputSections) sec->chunks = sec->origChunks; margin *= 2; } @@ -590,7 +597,7 @@ void Writer::finalizeAddresses() { // 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) + for (OutputSection *sec : ctx.outputSections) addressesChanged |= createThunks(sec, margin); // If the verification above thought we needed thunks, we should have // added some. @@ -605,9 +612,46 @@ void Writer::finalizeAddresses() { } } +void Writer::writePEChecksum() { + if (!ctx.config.writeCheckSum) { + return; + } + + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#checksum + uint32_t *buf = (uint32_t *)buffer->getBufferStart(); + uint32_t size = (uint32_t)(buffer->getBufferSize()); + + coff_file_header *coffHeader = + (coff_file_header *)((uint8_t *)buf + dosStubSize + sizeof(PEMagic)); + pe32_header *peHeader = + (pe32_header *)((uint8_t *)coffHeader + sizeof(coff_file_header)); + + uint64_t sum = 0; + uint32_t count = size; + ulittle16_t *addr = (ulittle16_t *)buf; + + // The PE checksum algorithm, implemented as suggested in RFC1071 + while (count > 1) { + sum += *addr++; + count -= 2; + } + + // Add left-over byte, if any + if (count > 0) + sum += *(unsigned char *)addr; + + // Fold 32-bit sum to 16 bits + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + + sum += size; + peHeader->CheckSum = sum; +} + // The main function of the writer. void Writer::run() { - ScopedTimer t1(codeLayoutTimer); + ScopedTimer t1(ctx.codeLayoutTimer); createImportTables(); createSections(); @@ -627,13 +671,14 @@ void Writer::run() { fatal("image size (" + Twine(fileSize) + ") " + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); - openFile(config->outputFile); - if (config->is64()) { + openFile(ctx.config.outputFile); + if (ctx.config.is64()) { writeHeader(); } else { writeHeader(); } writeSections(); + checkLoadConfig(); sortExceptionTable(); // Fix up the alignment in the TLS Directory's characteristic field, @@ -643,21 +688,24 @@ void Writer::run() { t1.stop(); - if (!config->pdbPath.empty() && config->debug) { + if (!ctx.config.pdbPath.empty() && ctx.config.debug) { assert(buildId); - createPDB(symtab, outputSections, sectionTable, buildId->buildId); + createPDB(ctx, sectionTable, buildId->buildId); } writeBuildId(); - writeLLDMapFile(outputSections); - writeMapFile(outputSections); + writeLLDMapFile(ctx); + writeMapFile(ctx); + + writePEChecksum(); if (errorCount()) return; - ScopedTimer t2(diskCommitTimer); + ScopedTimer t2(ctx.outputCommitTimer); if (auto e = buffer->commit()) - fatal("failed to write the output file: " + toString(std::move(e))); + fatal("failed to write output '" + buffer->getPath() + + "': " + toString(std::move(e))); } static StringRef getOutputSectionName(StringRef name) { @@ -669,11 +717,11 @@ static StringRef getOutputSectionName(StringRef name) { } // For /order. -static void sortBySectionOrder(std::vector &chunks) { - auto getPriority = [](const Chunk *c) { +void Writer::sortBySectionOrder(std::vector &chunks) { + auto getPriority = [&ctx = ctx](const Chunk *c) { if (auto *sec = dyn_cast(c)) if (sec->sym) - return config->order.lookup(sec->sym->getName()); + return ctx.config.order.lookup(sec->sym->getName()); return 0; }; @@ -719,7 +767,7 @@ bool Writer::fixGnuImportChunks() { bool hasIdata = false; // Sort all .idata$* chunks, grouping chunks from the same library, - // with alphabetical ordering of the object fils within a library. + // with alphabetical ordering of the object files within a library. for (auto it : partialSections) { PartialSection *pSec = it.second; if (!pSec->name.startswith(".idata")) @@ -752,7 +800,7 @@ bool Writer::fixGnuImportChunks() { // terminator in .idata$2. void Writer::addSyntheticIdata() { uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - idata.create(); + idata.create(ctx); // Add the .idata content in the right section groups, to allow // chunks from other linked in object files to be grouped together. @@ -795,7 +843,8 @@ void Writer::locateImportTables() { // 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) { +static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name, + bool isMinGW) { // 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 @@ -805,7 +854,7 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { // 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) + if (!isMinGW) return false; if (!sc || !sc->isCOMDAT()) return false; @@ -815,14 +864,15 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { } void Writer::sortSections() { - if (!config->callGraphProfile.empty()) { - DenseMap order = computeCallGraphProfileOrder(); + if (!ctx.config.callGraphProfile.empty()) { + DenseMap order = + computeCallGraphProfileOrder(ctx); for (auto it : order) { if (DefinedRegular *sym = it.first->sym) - config->order[sym->getName()] = it.second; + ctx.config.order[sym->getName()] = it.second; } } - if (!config->order.empty()) + if (!ctx.config.order.empty()) for (auto it : partialSections) sortBySectionOrder(it.second->chunks); } @@ -843,7 +893,7 @@ void Writer::createSections() { OutputSection *&sec = sections[{name, outChars}]; if (!sec) { sec = make(name, outChars); - outputSections.push_back(sec); + ctx.outputSections.push_back(sec); } return sec; }; @@ -864,15 +914,15 @@ void Writer::createSections() { dtorsSec = createSection(".dtors", data | r | w); // Then bin chunks by name and output characteristics. - for (Chunk *c : symtab->getChunks()) { + for (Chunk *c : ctx.symtab.getChunks()) { auto *sc = dyn_cast(c); if (sc && !sc->live) { - if (config->verbose) + if (ctx.config.verbose) sc->printDiscardedMessage(); continue; } StringRef name = c->getSectionName(); - if (shouldStripSectionSuffix(sc, name)) + if (shouldStripSectionSuffix(sc, name, ctx.config.mingw)) name = name.split('$').first; if (name.startswith(".tls")) @@ -932,8 +982,14 @@ void Writer::createSections() { // 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) + if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) { + // Move discardable sections named .debug_ to the end, after other + // discardable sections. Stripping only removes the sections named + // .debug_* - thus try to avoid leaving holes after stripping. + if (s->name.startswith(".debug_")) + return 3; 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). @@ -941,14 +997,16 @@ void Writer::createSections() { return 1; return 0; }; - llvm::stable_sort(outputSections, + llvm::stable_sort(ctx.outputSections, [&](const OutputSection *s, const OutputSection *t) { return sectionOrder(s) < sectionOrder(t); }); } void Writer::createMiscChunks() { - for (MergeChunk *p : MergeChunk::instances) { + Configuration *config = &ctx.config; + + for (MergeChunk *p : ctx.mergeChunkInstances) { if (p) { p->finalizeContents(); rdataSec->addChunk(p); @@ -956,15 +1014,16 @@ void Writer::createMiscChunks() { } // Create thunks for locally-dllimported symbols. - if (!symtab->localImportChunks.empty()) { - for (Chunk *c : symtab->localImportChunks) + if (!ctx.symtab.localImportChunks.empty()) { + for (Chunk *c : ctx.symtab.localImportChunks) rdataSec->addChunk(c); } // Create Debug Information Chunks OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; if (config->debug || config->repro || config->cetCompat) { - debugDirectory = make(debugRecords, config->repro); + debugDirectory = + make(ctx, debugRecords, config->repro); debugDirectory->setAlignment(4); debugInfoSec->addChunk(debugDirectory); } @@ -974,7 +1033,7 @@ void Writer::createMiscChunks() { // 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(); + buildId = make(ctx); debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId}); } @@ -1013,21 +1072,21 @@ 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) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; std::string dll = StringRef(file->dllName).lower(); - if (config->dllOrder.count(dll) == 0) - config->dllOrder[dll] = config->dllOrder.size(); + if (ctx.config.dllOrder.count(dll) == 0) + ctx.config.dllOrder[dll] = ctx.config.dllOrder.size(); if (file->impSym && !isa(file->impSym)) - fatal(toString(*file->impSym) + " was replaced"); + fatal(toString(ctx, *file->impSym) + " was replaced"); DefinedImportData *impSym = cast_or_null(file->impSym); - if (config->delayLoads.count(StringRef(file->dllName).lower())) { + if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) { if (!file->thunkSym) fatal("cannot delay-load " + toString(file) + - " due to import of data: " + toString(*impSym)); + " due to import of data: " + toString(ctx, *impSym)); delayIdata.add(impSym); } else { idata.add(impSym); @@ -1036,10 +1095,10 @@ void Writer::createImportTables() { } void Writer::appendImportThunks() { - if (ImportFile::instances.empty()) + if (ctx.importFileInstances.empty()) return; - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -1047,14 +1106,14 @@ void Writer::appendImportThunks() { continue; if (!isa(file->thunkSym)) - fatal(toString(*file->thunkSym) + " was replaced"); + fatal(toString(ctx, *file->thunkSym) + " was replaced"); DefinedImportThunk *thunk = cast(file->thunkSym); if (file->thunkLive) textSec->addChunk(thunk->getChunk()); } if (!delayIdata.empty()) { - Defined *helper = cast(config->delayLoadHelper); + Defined *helper = cast(ctx.config.delayLoadHelper); delayIdata.create(helper); for (Chunk *c : delayIdata.getChunks()) didatSec->addChunk(c); @@ -1062,6 +1121,10 @@ void Writer::appendImportThunks() { dataSec->addChunk(c); for (Chunk *c : delayIdata.getCodeChunks()) textSec->addChunk(c); + for (Chunk *c : delayIdata.getCodePData()) + pdataSec->addChunk(c); + for (Chunk *c : delayIdata.getCodeUnwindInfo()) + rdataSec->addChunk(c); } } @@ -1069,9 +1132,9 @@ void Writer::createExportTable() { 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) + if (ctx.config.hadExplicitExports) warn("literal .edata sections override exports"); - } else if (!config->exports.empty()) { + } else if (!ctx.config.exports.empty()) { for (Chunk *c : edata.chunks) edataSec->addChunk(c); } @@ -1080,9 +1143,9 @@ void Writer::createExportTable() { edataEnd = edataSec->chunks.back(); } // Warn on exported deleting destructor. - for (auto e : config->exports) + for (auto e : ctx.config.exports) if (e.sym && e.sym->getName().startswith("??_G")) - warn("export of deleting dtor: " + toString(*e.sym)); + warn("export of deleting dtor: " + toString(ctx, *e.sym)); } void Writer::removeUnusedSections() { @@ -1095,25 +1158,21 @@ void Writer::removeUnusedSections() { // 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()); + llvm::erase_if(ctx.outputSections, isUnused); } // 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()); + llvm::erase_if(ctx.outputSections, isEmpty); } void Writer::assignOutputSectionIndices() { // Assign final output section indices, and assign each chunk to its output // section. uint32_t idx = 1; - for (OutputSection *os : outputSections) { + for (OutputSection *os : ctx.outputSections) { os->sectionIndex = idx; for (Chunk *c : os->chunks) c->setOutputSectionIdx(idx); @@ -1122,7 +1181,7 @@ void Writer::assignOutputSectionIndices() { // Merge chunks are containers of chunks, so assign those an output section // too. - for (MergeChunk *mc : MergeChunk::instances) + for (MergeChunk *mc : ctx.mergeChunkInstances) if (mc) for (SectionChunk *sc : mc->sections) if (sc && sc->live) @@ -1137,25 +1196,29 @@ size_t Writer::addEntryToStringTable(StringRef str) { return offsetOfEntry; } -Optional Writer::createSymbol(Defined *def) { +std::optional Writer::createSymbol(Defined *def) { coff_symbol16 sym; switch (def->kind()) { - case Symbol::DefinedAbsoluteKind: - sym.Value = def->getRVA(); + case Symbol::DefinedAbsoluteKind: { + auto *da = dyn_cast(def); + // Note: COFF symbol can only store 32-bit values, so 64-bit absolute + // values will be truncated. + sym.Value = da->getVA(); 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. + // We also try to write DefinedSynthetic as a normal symbol. Some of these + // symbols do point to an actual chunk, like __safe_se_handler_table. Others + // like __ImageBase are outside of sections and thus cannot be represented. Chunk *c = def->getChunk(); if (!c) - return None; - OutputSection *os = c->getOutputSection(); + return std::nullopt; + OutputSection *os = ctx.getOutputSection(c); if (!os) - return None; + return std::nullopt; sym.Value = def->getRVA() - os->getRVA(); sym.SectionNumber = os->sectionIndex; @@ -1168,7 +1231,7 @@ Optional Writer::createSymbol(Defined *def) { // instead. Avoid emitting them to the symbol table, as they can confuse // debuggers. if (def->isRuntimePseudoReloc) - return None; + return std::nullopt; StringRef name = def->getName(); if (name.size() > COFF::NameSize) { @@ -1183,6 +1246,10 @@ Optional Writer::createSymbol(Defined *def) { COFFSymbolRef ref = d->getCOFFSymbol(); sym.Type = ref.getType(); sym.StorageClass = ref.getStorageClass(); + } else if (def->kind() == Symbol::DefinedImportThunkKind) { + sym.Type = (IMAGE_SYM_DTYPE_FUNCTION << SCT_COMPLEX_TYPE_SHIFT) | + IMAGE_SYM_TYPE_NULL; + sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } else { sym.Type = IMAGE_SYM_TYPE_NULL; sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; @@ -1200,12 +1267,12 @@ void Writer::createSymbolAndStringTable() { // 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) { + for (OutputSection *sec : ctx.outputSections) { if (sec->name.size() <= COFF::NameSize) continue; if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - if (config->warnLongSectionNames) { + if (ctx.config.warnLongSectionNames) { warn("section name " + sec->name + " is longer than 8 characters and will use a non-standard string " "table"); @@ -1213,16 +1280,31 @@ void Writer::createSymbolAndStringTable() { sec->setStringTableOff(addEntryToStringTable(sec->name)); } - if (config->debugDwarf || config->debugSymtab) { - for (ObjFile *file : ObjFile::instances) { + if (ctx.config.debugDwarf || ctx.config.debugSymtab) { + for (ObjFile *file : ctx.objFileInstances) { 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)) + if (auto *dc = dyn_cast_or_null(d)) { + COFFSymbolRef symRef = dc->getCOFFSymbol(); + if (symRef.isSectionDefinition() || + symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL) + continue; + } + + if (std::optional sym = createSymbol(d)) outputSymtab.push_back(*sym); + + if (auto *dthunk = dyn_cast(d)) { + if (!dthunk->wrappedSym->writtenToSymtab) { + dthunk->wrappedSym->writtenToSymtab = true; + if (std::optional sym = + createSymbol(dthunk->wrappedSym)) + outputSymtab.push_back(*sym); + } + } } } } @@ -1235,7 +1317,7 @@ void Writer::createSymbolAndStringTable() { pointerToSymbolTable = fileOff; fileOff += outputSymtab.size() * sizeof(coff_symbol16); fileOff += 4 + strtab.size(); - fileSize = alignTo(fileOff, config->fileAlign); + fileSize = alignTo(fileOff, ctx.config.fileAlign); } void Writer::mergeSections() { @@ -1244,16 +1326,16 @@ void Writer::mergeSections() { lastPdata = pdataSec->chunks.back(); } - for (auto &p : config->merge) { + for (auto &p : ctx.config.merge) { StringRef toName = p.second; if (p.first == toName) continue; StringSet<> names; - while (1) { + while (true) { if (!names.insert(toName).second) fatal("/merge: cycle found for section '" + p.first + "'"); - auto i = config->merge.find(toName); - if (i == config->merge.end()) + auto i = ctx.config.merge.find(toName); + if (i == ctx.config.merge.end()) break; toName = i->second; } @@ -1272,9 +1354,11 @@ void Writer::mergeSections() { // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { + Configuration *config = &ctx.config; + sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + sizeof(data_directory) * numberOfDataDirectory + - sizeof(coff_section) * outputSections.size(); + sizeof(coff_section) * ctx.outputSections.size(); sizeOfHeaders += config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); @@ -1283,7 +1367,7 @@ void Writer::assignAddresses() { // The first page is kept unmapped. uint64_t rva = alignTo(sizeOfHeaders, config->align); - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (sec == relocSec) addBaserels(); uint64_t rawSize = 0, virtualSize = 0; @@ -1318,7 +1402,7 @@ void Writer::assignAddresses() { sizeOfImage = alignTo(rva, config->align); // Assign addresses to sections in MergeChunks. - for (MergeChunk *mc : MergeChunk::instances) + for (MergeChunk *mc : ctx.mergeChunkInstances) if (mc) mc->assignSubsectionRVAs(); } @@ -1329,6 +1413,7 @@ template void Writer::writeHeader() { // 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. + Configuration *config = &ctx.config; uint8_t *buf = buffer->getBufferStart(); auto *dos = reinterpret_cast(buf); buf += sizeof(dos_header); @@ -1353,7 +1438,7 @@ template void Writer::writeHeader() { auto *coff = reinterpret_cast(buf); buf += sizeof(*coff); coff->Machine = config->machine; - coff->NumberOfSections = outputSections.size(); + coff->NumberOfSections = ctx.outputSections.size(); coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; if (config->largeAddressAware) coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; @@ -1466,7 +1551,7 @@ template void Writer::writeHeader() { dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); } - if (Symbol *sym = symtab->findUnderscore("_tls_used")) { + if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) { if (Defined *b = dyn_cast(sym)) { dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); dir[TLS_TABLE].Size = config->is64() @@ -1478,7 +1563,7 @@ template void Writer::writeHeader() { dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); } - if (Symbol *sym = symtab->findUnderscore("_load_config_used")) { + if (Symbol *sym = ctx.symtab.findUnderscore("_load_config_used")) { if (auto *b = dyn_cast(sym)) { SectionChunk *sc = b->getChunk(); assert(b->getRVA() >= sc->getRVA()); @@ -1502,12 +1587,12 @@ template void Writer::writeHeader() { } // Write section table - for (OutputSection *sec : outputSections) { - sec->writeHeaderTo(buf); + for (OutputSection *sec : ctx.outputSections) { + sec->writeHeaderTo(buf, config->debug); buf += sizeof(coff_section); } sectionTable = ArrayRef( - buf - outputSections.size() * sizeof(coff_section), buf); + buf - ctx.outputSections.size() * sizeof(coff_section), buf); if (outputSymtab.empty() && strtab.empty()) return; @@ -1535,7 +1620,7 @@ void Writer::openFile(StringRef path) { void Writer::createSEHTable() { SymbolRVASet handlers; - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { if (!file->hasSafeSEH()) error("/safeseh: " + file->getName() + " is not compatible with SEH"); markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); @@ -1544,7 +1629,7 @@ void Writer::createSEHTable() { // 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"); + handlers.empty() || !ctx.symtab.findUnderscore("_load_config_used"); maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", "__safe_se_handler_count"); @@ -1612,8 +1697,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, // 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) { +void Writer::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. @@ -1622,7 +1707,8 @@ static void markSymbolsWithRelocations(ObjFile *file, continue; for (const coff_relocation &reloc : sc->getRelocs()) { - if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32) + if (ctx.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; @@ -1637,12 +1723,14 @@ static void markSymbolsWithRelocations(ObjFile *file, // address-taken functions. It is sorted and uniqued, just like the safe SEH // table. void Writer::createGuardCFTables() { + Configuration *config = &ctx.config; + SymbolRVASet addressTakenSyms; SymbolRVASet giatsRVASet; std::vector giatsSymbols; SymbolRVASet longJmpTargets; SymbolRVASet ehContTargets; - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { // If the object was compiled with /guard:cf, the address taken symbols // are in .gfids$y sections, the longjmp targets are in .gljmp$y sections, // and ehcont targets are in .gehcont$y sections. If the object was not @@ -1702,13 +1790,13 @@ void Writer::createGuardCFTables() { // 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); + uint32_t guardFlags = uint32_t(GuardFlags::CF_INSTRUMENTED) | + uint32_t(GuardFlags::CF_FUNCTION_TABLE_PRESENT); if (config->guardCF & GuardCFLevel::LongJmp) - guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + guardFlags |= uint32_t(GuardFlags::CF_LONGJUMP_TABLE_PRESENT); if (config->guardCF & GuardCFLevel::EHCont) - guardFlags |= uint32_t(coff_guard_flags::HasEHContTable); - Symbol *flagSym = symtab->findUnderscore("__guard_flags"); + guardFlags |= uint32_t(GuardFlags::EH_CONTINUATION_TABLE_PRESENT); + Symbol *flagSym = ctx.symtab.findUnderscore("__guard_flags"); cast(flagSym)->setVA(guardFlags); } @@ -1780,8 +1868,8 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, tableChunk = make(std::move(tableSymbols)); rdataSec->addChunk(tableChunk); - Symbol *t = symtab->findUnderscore(tableSym); - Symbol *c = symtab->findUnderscore(countSym); + Symbol *t = ctx.symtab.findUnderscore(tableSym); + Symbol *c = ctx.symtab.findUnderscore(countSym); replaceSymbol(t, t->getName(), tableChunk); cast(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4)); } @@ -1793,14 +1881,14 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, void Writer::createRuntimePseudoRelocs() { std::vector rels; - for (Chunk *c : symtab->getChunks()) { + for (Chunk *c : ctx.symtab.getChunks()) { auto *sc = dyn_cast(c); if (!sc || !sc->live) continue; sc->getRuntimePseudoRelocs(rels); } - if (!config->pseudoRelocs) { + if (!ctx.config.pseudoRelocs) { // Not writing any pseudo relocs; if some were needed, error out and // indicate what required them. for (const RuntimePseudoReloc &rpr : rels) @@ -1816,8 +1904,9 @@ void Writer::createRuntimePseudoRelocs() { EmptyChunk *endOfList = make(); rdataSec->addChunk(endOfList); - Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); - Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); + Symbol *headSym = ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); + Symbol *endSym = + ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); replaceSymbol(headSym, headSym->getName(), table); replaceSymbol(endSym, endSym->getName(), endOfList); } @@ -1828,17 +1917,17 @@ void Writer::createRuntimePseudoRelocs() { // 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); + AbsolutePointerChunk *ctorListHead = make(ctx, -1); + AbsolutePointerChunk *ctorListEnd = make(ctx, 0); + AbsolutePointerChunk *dtorListHead = make(ctx, -1); + AbsolutePointerChunk *dtorListEnd = make(ctx, 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__"); + Symbol *ctorListSym = ctx.symtab.findUnderscore("__CTOR_LIST__"); + Symbol *dtorListSym = ctx.symtab.findUnderscore("__DTOR_LIST__"); replaceSymbol(ctorListSym, ctorListSym->getName(), ctorListHead); replaceSymbol(dtorListSym, dtorListSym->getName(), @@ -1848,10 +1937,10 @@ void Writer::insertCtorDtorSymbols() { // Handles /section options to allow users to overwrite // section attributes. void Writer::setSectionPermissions() { - for (auto &p : config->section) { + for (auto &p : ctx.config.section) { StringRef name = p.first; uint32_t perm = p.second; - for (OutputSection *sec : outputSections) + for (OutputSection *sec : ctx.outputSections) if (sec->name == name) sec->setPermissions(perm); } @@ -1859,12 +1948,8 @@ void Writer::setSectionPermissions() { // 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) { + for (OutputSection *sec : ctx.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 @@ -1884,6 +1969,8 @@ void Writer::writeBuildId() { // 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. + Configuration *config = &ctx.config; + if (config->debug) { assert(buildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. @@ -1934,13 +2021,13 @@ void Writer::sortExceptionTable() { return; // We assume .pdata contains function table entries only. auto bufAddr = [&](Chunk *c) { - OutputSection *os = c->getOutputSection(); + OutputSection *os = ctx.getOutputSection(c); 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) { + if (ctx.config.machine == AMD64) { struct Entry { ulittle32_t begin, end, unwind; }; if ((end - begin) % sizeof(Entry) != 0) { fatal("unexpected .pdata size: " + Twine(end - begin) + @@ -1951,7 +2038,7 @@ void Writer::sortExceptionTable() { [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } - if (config->machine == ARMNT || config->machine == ARM64) { + if (ctx.config.machine == ARMNT || ctx.config.machine == ARM64) { struct Entry { ulittle32_t begin, unwind; }; if ((end - begin) % sizeof(Entry) != 0) { fatal("unexpected .pdata size: " + Twine(end - begin) + @@ -1992,7 +2079,7 @@ void Writer::sortCRTSectionChunks(std::vector &chunks) { }; llvm::stable_sort(chunks, sectionChunkOrder); - if (config->verbose) { + if (ctx.config.verbose) { for (auto &c : chunks) { auto sc = dyn_cast(c); log(" " + sc->file->mb.getBufferIdentifier().str() + @@ -2002,7 +2089,7 @@ void Writer::sortCRTSectionChunks(std::vector &chunks) { } OutputSection *Writer::findSection(StringRef name) { - for (OutputSection *sec : outputSections) + for (OutputSection *sec : ctx.outputSections) if (sec->name == name) return sec; return nullptr; @@ -2010,7 +2097,7 @@ OutputSection *Writer::findSection(StringRef name) { uint32_t Writer::getSizeOfInitializedData() { uint32_t res = 0; - for (OutputSection *s : outputSections) + for (OutputSection *s : ctx.outputSections) if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) res += s->getRawSize(); return res; @@ -2018,11 +2105,11 @@ uint32_t Writer::getSizeOfInitializedData() { // Add base relocations to .reloc section. void Writer::addBaserels() { - if (!config->relocatable) + if (!ctx.config.relocatable) return; relocSec->chunks.clear(); std::vector v; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. @@ -2071,24 +2158,24 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { void Writer::fixTlsAlignment() { Defined *tlsSym = - dyn_cast_or_null(symtab->findUnderscore("_tls_used")); + dyn_cast_or_null(ctx.symtab.findUnderscore("_tls_used")); if (!tlsSym) return; - OutputSection *sec = tlsSym->getChunk()->getOutputSection(); + OutputSection *sec = ctx.getOutputSection(tlsSym->getChunk()); assert(sec && tlsSym->getRVA() >= sec->getRVA() && "no output section for _tls_used"); uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff(); uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA(); - uint64_t directorySize = config->is64() + uint64_t directorySize = ctx.config.is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); if (tlsOffset + directorySize > sec->getRawSize()) fatal("_tls_used sym is malformed"); - if (config->is64()) { + if (ctx.config.is64()) { object::coff_tls_directory64 *tlsDir = reinterpret_cast(&secBuf[tlsOffset]); tlsDir->setAlignment(tlsAlignment); @@ -2098,3 +2185,86 @@ void Writer::fixTlsAlignment() { tlsDir->setAlignment(tlsAlignment); } } + +void Writer::checkLoadConfig() { + Symbol *sym = ctx.symtab.findUnderscore("_load_config_used"); + auto *b = cast_if_present(sym); + if (!b) { + if (ctx.config.guardCF != GuardCFLevel::Off) + warn("Control Flow Guard is enabled but '_load_config_used' is missing"); + return; + } + + OutputSection *sec = ctx.getOutputSection(b->getChunk()); + uint8_t *buf = buffer->getBufferStart(); + uint8_t *secBuf = buf + sec->getFileOff(); + uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA()); + uint32_t expectedAlign = ctx.config.is64() ? 8 : 4; + if (b->getChunk()->getAlignment() < expectedAlign) + warn("'_load_config_used' is misaligned (expected alignment to be " + + Twine(expectedAlign) + " bytes, got " + + Twine(b->getChunk()->getAlignment()) + " instead)"); + else if (!isAligned(Align(expectedAlign), b->getRVA())) + warn("'_load_config_used' is misaligned (RVA is 0x" + + Twine::utohexstr(b->getRVA()) + " not aligned to " + + Twine(expectedAlign) + " bytes)"); + + if (ctx.config.is64()) + checkLoadConfigGuardData( + reinterpret_cast(symBuf)); + else + checkLoadConfigGuardData( + reinterpret_cast(symBuf)); +} + +template +void Writer::checkLoadConfigGuardData(const T *loadConfig) { + size_t loadConfigSize = loadConfig->Size; + +#define RETURN_IF_NOT_CONTAINS(field) \ + if (loadConfigSize < offsetof(T, field) + sizeof(T::field)) { \ + warn("'_load_config_used' structure too small to include " #field); \ + return; \ + } + +#define IF_CONTAINS(field) \ + if (loadConfigSize >= offsetof(T, field) + sizeof(T::field)) + +#define CHECK_VA(field, sym) \ + if (auto *s = dyn_cast(ctx.symtab.findUnderscore(sym))) \ + if (loadConfig->field != ctx.config.imageBase + s->getRVA()) \ + warn(#field " not set correctly in '_load_config_used'"); + +#define CHECK_ABSOLUTE(field, sym) \ + if (auto *s = dyn_cast(ctx.symtab.findUnderscore(sym))) \ + if (loadConfig->field != s->getVA()) \ + warn(#field " not set correctly in '_load_config_used'"); + + if (ctx.config.guardCF == GuardCFLevel::Off) + return; + RETURN_IF_NOT_CONTAINS(GuardFlags) + CHECK_VA(GuardCFFunctionTable, "__guard_fids_table") + CHECK_ABSOLUTE(GuardCFFunctionCount, "__guard_fids_count") + CHECK_ABSOLUTE(GuardFlags, "__guard_flags") + IF_CONTAINS(GuardAddressTakenIatEntryCount) { + CHECK_VA(GuardAddressTakenIatEntryTable, "__guard_iat_table") + CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count") + } + + if (!(ctx.config.guardCF & GuardCFLevel::LongJmp)) + return; + RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount) + CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table") + CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count") + + if (!(ctx.config.guardCF & GuardCFLevel::EHCont)) + return; + RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount) + CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table") + CHECK_ABSOLUTE(GuardEHContinuationCount, "__guard_eh_cont_count") + +#undef RETURN_IF_NOT_CONTAINS +#undef IF_CONTAINS +#undef CHECK_VA +#undef CHECK_ABSOLUTE +} diff --git a/gnu/llvm/lld/COFF/Writer.h b/gnu/llvm/lld/COFF/Writer.h index 2bb26da7d42..4a74aa7ada5 100644 --- a/gnu/llvm/lld/COFF/Writer.h +++ b/gnu/llvm/lld/COFF/Writer.h @@ -16,11 +16,11 @@ #include #include -namespace lld { -namespace coff { +namespace lld::coff { static const int pageSize = 4096; +class COFFLinkerContext; -void writeResult(); +void writeResult(COFFLinkerContext &ctx); class PartialSection { public: @@ -45,14 +45,11 @@ public: void insertChunkAtStart(Chunk *c); void merge(OutputSection *other); void setPermissions(uint32_t c); - uint64_t getRVA() { return header.VirtualAddress; } - uint64_t getFileOff() { return header.PointerToRawData; } - void writeHeaderTo(uint8_t *buf); + uint64_t getRVA() const { return header.VirtualAddress; } + uint64_t getFileOff() const { return header.PointerToRawData; } + void writeHeaderTo(uint8_t *buf, bool isDebug); void addContributingPartialSection(PartialSection *sec); - // Clear the output sections static container. - static void clear(); - // Returns the size of this section in an executable memory image. // This may be smaller than the raw size (the raw size is multiple // of disk sector size, so there may be padding at end), or may be @@ -82,7 +79,6 @@ private: uint32_t stringTableOff = 0; }; -} // namespace coff -} // namespace lld +} // namespace lld::coff #endif diff --git a/gnu/llvm/lld/Common/Args.cpp b/gnu/llvm/lld/Common/Args.cpp index fe7300dd597..388c15b3db3 100644 --- a/gnu/llvm/lld/Common/Args.cpp +++ b/gnu/llvm/lld/Common/Args.cpp @@ -54,8 +54,9 @@ int64_t lld::args::getHex(opt::InputArgList &args, unsigned key, return ::getInteger(args, key, Default, 16); } -std::vector lld::args::getStrings(opt::InputArgList &args, int id) { - std::vector v; +SmallVector lld::args::getStrings(opt::InputArgList &args, + int id) { + SmallVector v; for (auto *arg : args.filtered(id)) v.push_back(arg->getValue()); return v; diff --git a/gnu/llvm/lld/Common/CMakeLists.txt b/gnu/llvm/lld/Common/CMakeLists.txt index 0437d5afb8e..71df89c28ae 100644 --- a/gnu/llvm/lld/Common/CMakeLists.txt +++ b/gnu/llvm/lld/Common/CMakeLists.txt @@ -1,14 +1,8 @@ -set(LLD_SYSTEM_LIBS ${LLVM_PTHREAD_LIB}) - -if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) - list(APPEND LLD_SYSTEM_LIBS atomic) -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") +set(generate_vcs_version_script "${LLVM_CMAKE_DIR}/GenerateVersionFromVCS.cmake") if(lld_vc AND LLVM_APPEND_VC_REV) set(lld_source_dir ${LLD_SOURCE_DIR}) @@ -28,6 +22,7 @@ set_source_files_properties("${version_inc}" add_lld_library(lldCommon Args.cpp + CommonLinkerContext.cpp DWARF.cpp ErrorHandler.cpp Filesystem.cpp @@ -51,9 +46,11 @@ add_lld_library(lldCommon Option Support Target + TargetParser LINK_LIBS - ${LLD_SYSTEM_LIBS} + ${LLVM_PTHREAD_LIB} + ${LLVM_ATOMIC_LIB} DEPENDS intrinsics_gen diff --git a/gnu/llvm/lld/Common/CommonLinkerContext.cpp b/gnu/llvm/lld/Common/CommonLinkerContext.cpp new file mode 100644 index 00000000000..12f56bc10ec --- /dev/null +++ b/gnu/llvm/lld/Common/CommonLinkerContext.cpp @@ -0,0 +1,51 @@ +//===- CommonLinkerContext.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/CommonLinkerContext.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" + +#include "llvm/CodeGen/CommandFlags.h" + +using namespace llvm; +using namespace lld; + +// Reference to the current LLD instance. This is a temporary situation, until +// we pass this context everywhere by reference, or we make it a thread_local, +// as in https://reviews.llvm.org/D108850?id=370678 where each thread can be +// associated with a LLD instance. Only then will LLD be free of global +// state. +static CommonLinkerContext *lctx; + +CommonLinkerContext::CommonLinkerContext() { + lctx = this; + // Fire off the static initializations in CGF's constructor. + codegen::RegisterCodeGenFlags CGF; +} + +CommonLinkerContext::~CommonLinkerContext() { + assert(lctx); + // Explicitly call the destructors since we created the objects with placement + // new in SpecificAlloc::create(). + for (auto &it : instances) + it.second->~SpecificAllocBase(); + lctx = nullptr; +} + +CommonLinkerContext &lld::commonContext() { + assert(lctx); + return *lctx; +} + +bool lld::hasContext() { return lctx != nullptr; } + +void CommonLinkerContext::destroy() { + if (lctx == nullptr) + return; + delete lctx; +} diff --git a/gnu/llvm/lld/Common/DWARF.cpp b/gnu/llvm/lld/Common/DWARF.cpp index 077adbcaf85..2cd8ca4575d 100644 --- a/gnu/llvm/lld/Common/DWARF.cpp +++ b/gnu/llvm/lld/Common/DWARF.cpp @@ -69,27 +69,27 @@ DWARFCache::DWARFCache(std::unique_ptr d) // Returns the pair of file name and line number describing location of data // object (variable, array, etc) definition. -Optional> +std::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; + return std::nullopt; // 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::nullopt; 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) { +std::optional DWARFCache::getDILineInfo(uint64_t offset, + uint64_t sectionIndex) { DILineInfo info; for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) { if (lt->getFileLineInfoForAddress( @@ -97,7 +97,7 @@ Optional DWARFCache::getDILineInfo(uint64_t offset, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info)) return info; } - return None; + return std::nullopt; } } // namespace lld diff --git a/gnu/llvm/lld/Common/ErrorHandler.cpp b/gnu/llvm/lld/Common/ErrorHandler.cpp index 269a0f62ec6..0a8fc8f054f 100644 --- a/gnu/llvm/lld/Common/ErrorHandler.cpp +++ b/gnu/llvm/lld/Common/ErrorHandler.cpp @@ -10,6 +10,7 @@ #include "llvm/Support/Parallel.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -18,51 +19,81 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" -#include #include using namespace llvm; using namespace lld; -// The functions defined in this file can be called from multiple threads, -// but lld::outs() or lld::errs() are not thread-safe. We protect them using a -// mutex. -static std::mutex mu; - -// We want to separate multi-line messages with a newline. `sep` is "\n" -// if the last messages was multi-line. Otherwise "". -static StringRef sep; - static StringRef getSeparator(const Twine &msg) { if (StringRef(msg.str()).contains('\n')) return "\n"; return ""; } -raw_ostream *lld::stdoutOS; -raw_ostream *lld::stderrOS; +ErrorHandler::~ErrorHandler() { + if (cleanupCallback) + cleanupCallback(); +} + +void ErrorHandler::initialize(llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, + bool disableOutput) { + this->stdoutOS = &stdoutOS; + this->stderrOS = &stderrOS; + stderrOS.enable_colors(stderrOS.has_colors()); + this->exitEarly = exitEarly; + this->disableOutput = disableOutput; +} + +void ErrorHandler::flushStreams() { + std::lock_guard lock(mu); + outs().flush(); + errs().flush(); +} + +ErrorHandler &lld::errorHandler() { return context().e; } -ErrorHandler &lld::errorHandler() { - static ErrorHandler handler; - return handler; +void lld::error(const Twine &msg) { errorHandler().error(msg); } +void lld::error(const Twine &msg, ErrorTag tag, ArrayRef args) { + errorHandler().error(msg, tag, args); +} +void lld::fatal(const Twine &msg) { errorHandler().fatal(msg); } +void lld::log(const Twine &msg) { errorHandler().log(msg); } +void lld::message(const Twine &msg, llvm::raw_ostream &s) { + errorHandler().message(msg, s); } +void lld::warn(const Twine &msg) { errorHandler().warn(msg); } +uint64_t lld::errorCount() { return errorHandler().errorCount; } raw_ostream &lld::outs() { - if (errorHandler().disableOutput) + ErrorHandler &e = errorHandler(); + return e.outs(); +} + +raw_ostream &lld::errs() { + ErrorHandler &e = errorHandler(); + return e.errs(); +} + +raw_ostream &ErrorHandler::outs() { + if (disableOutput) return llvm::nulls(); return stdoutOS ? *stdoutOS : llvm::outs(); } -raw_ostream &lld::errs() { - if (errorHandler().disableOutput) +raw_ostream &ErrorHandler::errs() { + if (disableOutput) return llvm::nulls(); return stderrOS ? *stderrOS : llvm::errs(); } void lld::exitLld(int val) { - // Delete any temporary file, while keeping the memory mapping open. - if (errorHandler().outputBuffer) - errorHandler().outputBuffer->discard(); + if (hasContext()) { + ErrorHandler &e = errorHandler(); + // Delete any temporary file, while keeping the memory mapping open. + if (e.outputBuffer) + e.outputBuffer->discard(); + } // Re-throw a possible signal or exception once/if it was catched by // safeLldMain(). @@ -75,11 +106,9 @@ void lld::exitLld(int val) { if (!CrashRecoveryContext::GetCurrent()) llvm_shutdown(); - { - std::lock_guard lock(mu); - lld::outs().flush(); - lld::errs().flush(); - } + if (hasContext()) + lld::errorHandler().flushStreams(); + // When running inside safeLldMain(), restore the control flow back to the // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup, // since we want to avoid further crashes on shutdown. @@ -90,6 +119,13 @@ void lld::diagnosticHandler(const DiagnosticInfo &di) { SmallString<128> s; raw_svector_ostream os(s); DiagnosticPrinterRawOStream dp(os); + + // For an inline asm diagnostic, prepend the module name to get something like + // "$module :1:5: ". + if (auto *dism = dyn_cast(&di)) + if (dism->isInlineAsmDiag()) + os << dism->getModuleName() << ' '; + di.print(dp); switch (di.getSeverity()) { case DS_Error: @@ -168,19 +204,36 @@ std::string ErrorHandler::getLocation(const Twine &msg) { return std::string(logName); } +void ErrorHandler::reportDiagnostic(StringRef location, Colors c, + StringRef diagKind, const Twine &msg) { + SmallString<256> buf; + raw_svector_ostream os(buf); + os << sep << location << ": "; + if (!diagKind.empty()) { + if (lld::errs().colors_enabled()) { + os.enable_colors(true); + os << c << diagKind << ": " << Colors::RESET; + } else { + os << diagKind << ": "; + } + } + os << msg << '\n'; + lld::errs() << buf; +} + void ErrorHandler::log(const Twine &msg) { if (!verbose || disableOutput) return; std::lock_guard lock(mu); - lld::errs() << logName << ": " << msg << "\n"; + reportDiagnostic(logName, Colors::RESET, "", msg); } -void ErrorHandler::message(const Twine &msg) { +void ErrorHandler::message(const Twine &msg, llvm::raw_ostream &s) { if (disableOutput) return; std::lock_guard lock(mu); - lld::outs() << msg << "\n"; - lld::outs().flush(); + s << msg << "\n"; + s.flush(); } void ErrorHandler::warn(const Twine &msg) { @@ -189,9 +242,11 @@ void ErrorHandler::warn(const Twine &msg) { return; } + if (suppressWarnings) + return; + std::lock_guard lock(mu); - lld::errs() << sep << getLocation(msg) << ": " << Colors::MAGENTA - << "warning: " << Colors::RESET << msg << "\n"; + reportDiagnostic(getLocation(msg), Colors::MAGENTA, "warning", msg); sep = getSeparator(msg); } @@ -217,12 +272,9 @@ void ErrorHandler::error(const Twine &msg) { std::lock_guard lock(mu); if (errorLimit == 0 || errorCount < errorLimit) { - lld::errs() << sep << getLocation(msg) << ": " << Colors::RED - << "error: " << Colors::RESET << msg << "\n"; + reportDiagnostic(getLocation(msg), Colors::RED, "error", msg); } else if (errorCount == errorLimit) { - lld::errs() << sep << getLocation(msg) << ": " << Colors::RED - << "error: " << Colors::RESET << errorLimitExceededMsg - << "\n"; + reportDiagnostic(logName, Colors::RED, "error", errorLimitExceededMsg); exit = exitEarly; } diff --git a/gnu/llvm/lld/Common/Memory.cpp b/gnu/llvm/lld/Common/Memory.cpp index c53e1d3e6cf..7c90ff1d799 100644 --- a/gnu/llvm/lld/Common/Memory.cpp +++ b/gnu/llvm/lld/Common/Memory.cpp @@ -7,16 +7,19 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" using namespace llvm; using namespace lld; -BumpPtrAllocator lld::bAlloc; -StringSaver lld::saver{bAlloc}; -std::vector lld::SpecificAllocBase::instances; - -void lld::freeArena() { - for (SpecificAllocBase *alloc : SpecificAllocBase::instances) - alloc->reset(); - bAlloc.Reset(); +SpecificAllocBase * +lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)) { + auto &instances = context().instances; + auto &instance = instances[tag]; + if (instance == nullptr) { + void *storage = context().bAlloc.Allocate(size, align); + instance = creator(storage); + } + return instance; } diff --git a/gnu/llvm/lld/Common/Strings.cpp b/gnu/llvm/lld/Common/Strings.cpp index 7bf336490da..31397b75b29 100644 --- a/gnu/llvm/lld/Common/Strings.cpp +++ b/gnu/llvm/lld/Common/Strings.cpp @@ -9,7 +9,6 @@ #include "lld/Common/Strings.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" -#include "llvm/Demangle/Demangle.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/GlobPattern.h" #include @@ -19,18 +18,6 @@ using namespace llvm; using namespace lld; -// Returns the demangled C++ symbol name for name. -std::string lld::demangleItanium(StringRef name) { - // demangleItanium() can be called for all symbols. Only demangle C++ symbols, - // to avoid getting unexpected result for a C symbol that happens to match a - // mangled type name such as "Pi" (which would demangle to "int*"). - if (!name.startswith("_Z") && !name.startswith("__Z") && - !name.startswith("___Z") && !name.startswith("____Z")) - return std::string(name); - - return demangle(std::string(name)); -} - SingleStringMatcher::SingleStringMatcher(StringRef Pattern) { if (Pattern.size() > 2 && Pattern.startswith("\"") && Pattern.endswith("\"")) { @@ -59,8 +46,8 @@ bool StringMatcher::match(StringRef s) const { } // Converts a hex string (e.g. "deadbeef") to a vector. -std::vector lld::parseHex(StringRef s) { - std::vector hex; +SmallVector lld::parseHex(StringRef s) { + SmallVector hex; while (!s.empty()) { StringRef b = s.substr(0, 2); s = s.substr(2); diff --git a/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp b/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp index d39477ed89a..0efe679842c 100644 --- a/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp +++ b/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp @@ -7,21 +7,20 @@ //===----------------------------------------------------------------------===// #include "lld/Common/TargetOptionsCommandFlags.h" - +#include "llvm/ADT/Triple.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/Target/TargetOptions.h" - -static llvm::codegen::RegisterCodeGenFlags CGF; +#include llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { return llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); } -llvm::Optional lld::getRelocModelFromCMModel() { +std::optional lld::getRelocModelFromCMModel() { return llvm::codegen::getExplicitRelocModel(); } -llvm::Optional lld::getCodeModelFromCMModel() { +std::optional lld::getCodeModelFromCMModel() { return llvm::codegen::getExplicitCodeModel(); } diff --git a/gnu/llvm/lld/Common/Timer.cpp b/gnu/llvm/lld/Common/Timer.cpp index 16c518e4bf8..29838c9720b 100644 --- a/gnu/llvm/lld/Common/Timer.cpp +++ b/gnu/llvm/lld/Common/Timer.cpp @@ -9,6 +9,7 @@ #include "lld/Common/Timer.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Format.h" +#include using namespace lld; using namespace llvm; @@ -26,18 +27,14 @@ void ScopedTimer::stop() { ScopedTimer::~ScopedTimer() { stop(); } -Timer::Timer(llvm::StringRef name) : name(std::string(name)) {} -Timer::Timer(llvm::StringRef name, Timer &parent) : name(std::string(name)) { +Timer::Timer(llvm::StringRef name) : total(0), name(std::string(name)) {} +Timer::Timer(llvm::StringRef name, Timer &parent) + : total(0), name(std::string(name)) { parent.children.push_back(this); } -Timer &Timer::root() { - static Timer rootTimer("Total Link Time"); - return rootTimer; -} - void Timer::print() { - double totalDuration = static_cast(root().millis()); + double totalDuration = static_cast(millis()); // We want to print the grand total under all the intermediate phases, so we // print all children first, then print the total under that. @@ -47,7 +44,7 @@ void Timer::print() { message(std::string(50, '-')); - root().print(0, root().millis(), false); + print(0, millis(), false); } double Timer::millis() const { diff --git a/gnu/llvm/lld/Common/Version.cpp b/gnu/llvm/lld/Common/Version.cpp index f3768091cd0..ec6eda6a674 100644 --- a/gnu/llvm/lld/Common/Version.cpp +++ b/gnu/llvm/lld/Common/Version.cpp @@ -15,18 +15,14 @@ #include "VCSVersion.inc" // Returns a version string, e.g.: -// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef) +// LLD 14.0.0 (https://github.com/llvm/llvm-project.git +// 2d9759c7902c5cbc9a7e3ab623321d5578d51687) std::string lld::getLLDVersion() { #ifdef LLD_VENDOR #define LLD_VENDOR_DISPLAY LLD_VENDOR " " #else #define LLD_VENDOR_DISPLAY #endif -#if defined(LLD_REPOSITORY) && defined(LLD_REVISION) - return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY - " " LLD_REVISION ")"; -#else return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING; -#endif #undef LLD_VENDOR_DISPLAY } diff --git a/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp b/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp index b9fd4cdbad6..6ec7e338cd4 100644 --- a/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp +++ b/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp @@ -26,17 +26,16 @@ //===----------------------------------------------------------------------===// #include "AArch64ErrataFix.h" -#include "Config.h" +#include "InputFiles.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/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" -#include "llvm/Support/raw_ostream.h" #include using namespace llvm; @@ -56,7 +55,7 @@ static bool isADRP(uint32_t instr) { return (instr & 0x9f000000) == 0x90000000; } -// Load and store bit patterns from ARMv8-A ARM ARM. +// Load and store bit patterns from ARMv8-A. // Instructions appear in order of appearance starting from table in // C4.1.3 Loads and Stores. @@ -351,7 +350,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, } uint64_t patchOff = 0; - const uint8_t *buf = isec->data().begin(); + const uint8_t *buf = isec->content().begin(); const ulittle32_t *instBuf = reinterpret_cast(buf + off); uint32_t instr1 = *instBuf++; uint32_t instr2 = *instBuf++; @@ -370,7 +369,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, return patchOff; } -class elf::Patch843419Section : public SyntheticSection { +class elf::Patch843419Section final : public SyntheticSection { public: Patch843419Section(InputSection *p, uint64_t off); @@ -398,9 +397,9 @@ Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) 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); + saver().save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, + 0, getSize(), *this); + addSyntheticLocal(saver().save("$x"), STT_NOTYPE, 0, 0, *this); } uint64_t Patch843419Section::getLDSTAddr() const { @@ -410,10 +409,10 @@ uint64_t Patch843419Section::getLDSTAddr() const { void Patch843419Section::writeTo(uint8_t *buf) { // Copy the instruction that we will be replacing with a branch in the // patchee Section. - write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); + write32le(buf, read32le(patchee->content().begin() + patcheeOffset)); // Apply any relocation transferred from the original patchee section. - relocateAlloc(buf, buf + getSize()); + target->relocateAlloc(*this, buf); // Return address is the next instruction after the one we have just copied. uint64_t s = getLDSTAddr() + 4; @@ -440,9 +439,8 @@ void AArch64Err843419Patcher::init() { }; // Collect mapping symbols for every executable InputSection. - for (InputFile *file : objectFiles) { - auto *f = cast>(file); - for (Symbol *b : f->getLocalSymbols()) { + for (ELFFileBase *file : ctx.objectFiles) { + for (Symbol *b : file->getLocalSymbols()) { auto *def = dyn_cast(b); if (!def) continue; @@ -513,7 +511,7 @@ void AArch64Err843419Patcher::insertPatches( // 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; + SmallVector tmp; tmp.reserve(isd.sections.size() + patches.size()); auto mergeCmp = [](const InputSection *a, const InputSection *b) { if (a->outSecOff != b->outSecOff) @@ -546,10 +544,10 @@ static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, // 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) { + auto relIt = llvm::find_if(isec->relocs(), [=](const Relocation &r) { return r.offset == patcheeOffset; }); - if (relIt != isec->relocations.end() && + if (relIt != isec->relocs().end() && (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE)) return; @@ -563,12 +561,11 @@ static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, 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}); + if (relIt != isec->relocs().end()) { + ps->addReloc({relIt->expr, relIt->type, 0, relIt->addend, relIt->sym}); *relIt = makeRelToPatch(patcheeOffset, ps->patchSym); } else - isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym)); + isec->addReloc(makeRelToPatch(patcheeOffset, ps->patchSym)); } // Scan all the instructions in InputSectionDescription, for each instance of @@ -593,8 +590,8 @@ AArch64Err843419Patcher::patchInputSectionDescription( 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; + uint64_t limit = (dataSym == mapSyms.end()) ? isec->content().size() + : (*dataSym)->value; while (off < limit) { uint64_t startAddr = isec->getVA(off); @@ -630,8 +627,8 @@ bool AArch64Err843419Patcher::createFixes() { 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)) { + for (SectionCommand *cmd : os->commands) + if (auto *isd = dyn_cast(cmd)) { std::vector patches = patchInputSectionDescription(*isd); if (!patches.empty()) { diff --git a/gnu/llvm/lld/ELF/AArch64ErrataFix.h b/gnu/llvm/lld/ELF/AArch64ErrataFix.h index dfe57b95dd9..fa34beb5d7a 100644 --- a/gnu/llvm/lld/ELF/AArch64ErrataFix.h +++ b/gnu/llvm/lld/ELF/AArch64ErrataFix.h @@ -10,16 +10,14 @@ #define LLD_ELF_AARCH64ERRATAFIX_H #include "lld/Common/LLVM.h" -#include +#include "llvm/ADT/DenseMap.h" #include -namespace lld { -namespace elf { +namespace lld::elf { class Defined; class InputSection; class InputSectionDescription; -class OutputSection; class Patch843419Section; class AArch64Err843419Patcher { @@ -39,12 +37,11 @@ private: // 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. - std::map> sectionMap; + llvm::DenseMap> sectionMap; bool initialized = false; }; -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/ARMErrataFix.cpp b/gnu/llvm/lld/ELF/ARMErrataFix.cpp index 77623780ffa..9fbff869397 100644 --- a/gnu/llvm/lld/ELF/ARMErrataFix.cpp +++ b/gnu/llvm/lld/ELF/ARMErrataFix.cpp @@ -14,18 +14,16 @@ //===----------------------------------------------------------------------===// #include "ARMErrataFix.h" - -#include "Config.h" +#include "InputFiles.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/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" -#include "llvm/Support/raw_ostream.h" #include using namespace llvm; @@ -70,7 +68,7 @@ using namespace lld::elf; // 00001002 2 - bytes padding // 00001004 __CortexA8657417_00000FFE: B.w func -class elf::Patch657417Section : public SyntheticSection { +class elf::Patch657417Section final : public SyntheticSection { public: Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM); @@ -142,9 +140,9 @@ Patch657417Section::Patch657417Section(InputSection *p, uint64_t off, patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) { parent = p->getParent(); patchSym = addSyntheticLocal( - saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, + saver().save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, isARM ? 0 : 1, getSize(), *this); - addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); + addSyntheticLocal(saver().save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); } uint64_t Patch657417Section::getBranchAddr() const { @@ -183,8 +181,8 @@ void Patch657417Section::writeTo(uint8_t *buf) { else write32le(buf, 0x9000f000); // If we have a relocation then apply it. - if (!relocations.empty()) { - relocateAlloc(buf, buf + getSize()); + if (!relocs().empty()) { + target->relocateAlloc(*this, buf); return; } @@ -209,7 +207,7 @@ 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; + uint64_t destAddr; // 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. @@ -268,7 +266,7 @@ static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off, } ScanResult scanRes = {0, 0, nullptr}; - const uint8_t *buf = isec->data().begin(); + const uint8_t *buf = isec->content().begin(); // ARMv7-A Thumb 32-bit instructions are encoded 2 consecutive // little-endian halfwords. const ulittle16_t *instBuf = reinterpret_cast(buf + off); @@ -283,12 +281,12 @@ static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off, // 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) { + auto relIt = llvm::find_if(isec->relocs(), [=](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()) + if (relIt != isec->relocs().end()) scanRes.rel = &(*relIt); if (branchDestInFirstRegion(isec, branchOff, instr2, scanRes.rel)) { if (patchInRange(isec, branchOff, instr2)) { @@ -329,9 +327,8 @@ void ARMErr657417Patcher::init() { }; // Collect mapping symbols for every executable InputSection. - for (InputFile *file : objectFiles) { - auto *f = cast>(file); - for (Symbol *s : f->getLocalSymbols()) { + for (ELFFileBase *file : ctx.objectFiles) { + for (Symbol *s : file->getLocalSymbols()) { auto *def = dyn_cast(s); if (!def) continue; @@ -396,7 +393,7 @@ void ARMErr657417Patcher::insertPatches( // 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; + SmallVector tmp; tmp.reserve(isd.sections.size() + patches.size()); auto mergeCmp = [](const InputSection *a, const InputSection *b) { if (a->outSecOff != b->outSecOff) @@ -454,7 +451,7 @@ static void implementPatch(ScanResult sr, InputSection *isec, patchRelType = R_ARM_JUMP24; patchRelAddend -= 4; } - psec->relocations.push_back( + psec->addReloc( Relocation{sr.rel->expr, patchRelType, 0, patchRelAddend, sr.rel->sym}); // Redirect the existing branch relocation to the patch. sr.rel->expr = R_PC; @@ -473,8 +470,7 @@ static void implementPatch(ScanResult sr, InputSection *isec, type = R_ARM_THM_JUMP24; else type = R_ARM_THM_CALL; - isec->relocations.push_back( - Relocation{R_PC, type, sr.off, -4, psec->patchSym}); + isec->addReloc(Relocation{R_PC, type, sr.off, -4, psec->patchSym}); } patches.push_back(psec); } @@ -501,8 +497,8 @@ ARMErr657417Patcher::patchInputSectionDescription( 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; + uint64_t limit = nonThumbSym == mapSyms.end() ? isec->content().size() + : (*nonThumbSym)->value; while (off < limit) { ScanResult sr = scanCortexA8Errata657417(isec, off, limit); @@ -525,8 +521,8 @@ bool ARMErr657417Patcher::createFixes() { 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)) { + for (SectionCommand *cmd : os->commands) + if (auto *isd = dyn_cast(cmd)) { std::vector patches = patchInputSectionDescription(*isd); if (!patches.empty()) { diff --git a/gnu/llvm/lld/ELF/ARMErrataFix.h b/gnu/llvm/lld/ELF/ARMErrataFix.h index a93609b35ba..2a7e6aaeff1 100644 --- a/gnu/llvm/lld/ELF/ARMErrataFix.h +++ b/gnu/llvm/lld/ELF/ARMErrataFix.h @@ -11,16 +11,13 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/DenseMap.h" -#include #include -namespace lld { -namespace elf { +namespace lld::elf { class Defined; class InputSection; class InputSectionDescription; -class OutputSection; class Patch657417Section; class ARMErr657417Patcher { @@ -45,7 +42,6 @@ private: bool initialized = false; }; -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp b/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp index 466ad81922d..e5605497e27 100644 --- a/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp +++ b/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp @@ -10,7 +10,7 @@ #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Object/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -40,7 +40,6 @@ public: AMDGPU::AMDGPU() { relativeRel = R_AMDGPU_RELATIVE64; gotRel = R_AMDGPU_ABS64; - noneRel = R_AMDGPU_NONE; symbolicRel = R_AMDGPU_ABS64; } @@ -49,10 +48,10 @@ static uint32_t getEFlags(InputFile *file) { } uint32_t AMDGPU::calcEFlagsV3() const { - uint32_t ret = getEFlags(objectFiles[0]); + uint32_t ret = getEFlags(ctx.objectFiles[0]); // Verify that all input files have the same e_flags. - for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) { if (ret == getEFlags(f)) continue; error("incompatible e_flags: " + toString(f)); @@ -62,14 +61,15 @@ uint32_t AMDGPU::calcEFlagsV3() const { } uint32_t AMDGPU::calcEFlagsV4() const { - uint32_t retMach = getEFlags(objectFiles[0]) & EF_AMDGPU_MACH; - uint32_t retXnack = getEFlags(objectFiles[0]) & EF_AMDGPU_FEATURE_XNACK_V4; + uint32_t retMach = getEFlags(ctx.objectFiles[0]) & EF_AMDGPU_MACH; + uint32_t retXnack = + getEFlags(ctx.objectFiles[0]) & EF_AMDGPU_FEATURE_XNACK_V4; uint32_t retSramEcc = - getEFlags(objectFiles[0]) & EF_AMDGPU_FEATURE_SRAMECC_V4; + getEFlags(ctx.objectFiles[0]) & EF_AMDGPU_FEATURE_SRAMECC_V4; // Verify that all input files have compatible e_flags (same mach, all // features in the same category are either ANY, ANY and ON, or ANY and OFF). - for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) { if (retMach != (getEFlags(f) & EF_AMDGPU_MACH)) { error("incompatible mach: " + toString(f)); return 0; @@ -106,15 +106,19 @@ uint32_t AMDGPU::calcEFlagsV4() const { } uint32_t AMDGPU::calcEFlags() const { - assert(!objectFiles.empty()); + if (ctx.objectFiles.empty()) + return 0; - uint8_t abiVersion = cast>(objectFiles[0])->getObj() - .getHeader().e_ident[EI_ABIVERSION]; + uint8_t abiVersion = cast>(ctx.objectFiles[0]) + ->getObj() + .getHeader() + .e_ident[EI_ABIVERSION]; switch (abiVersion) { case ELFABIVERSION_AMDGPU_HSA_V2: case ELFABIVERSION_AMDGPU_HSA_V3: return calcEFlagsV3(); case ELFABIVERSION_AMDGPU_HSA_V4: + case ELFABIVERSION_AMDGPU_HSA_V5: return calcEFlagsV4(); default: error("unknown abi version: " + Twine(abiVersion)); diff --git a/gnu/llvm/lld/ELF/Arch/ARM.cpp b/gnu/llvm/lld/ELF/Arch/ARM.cpp index d909a3234c1..24d78ebf582 100644 --- a/gnu/llvm/lld/ELF/Arch/ARM.cpp +++ b/gnu/llvm/lld/ELF/Arch/ARM.cpp @@ -6,13 +6,11 @@ // //===----------------------------------------------------------------------===// -#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/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -52,13 +50,11 @@ ARM::ARM() { 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; pltHeaderSize = 32; pltEntrySize = 16; ipltEntrySize = 16; @@ -86,6 +82,13 @@ uint32_t ARM::calcEFlags() const { RelExpr ARM::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_ARM_ABS32: + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + return R_ABS; + case R_ARM_THM_JUMP8: case R_ARM_THM_JUMP11: return R_PC; case R_ARM_CALL: @@ -135,7 +138,16 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s, case R_ARM_THM_MOVT_PREL: return R_PC; case R_ARM_ALU_PC_G0: + case R_ARM_ALU_PC_G0_NC: + case R_ARM_ALU_PC_G1: + case R_ARM_ALU_PC_G1_NC: + case R_ARM_ALU_PC_G2: case R_ARM_LDR_PC_G0: + case R_ARM_LDR_PC_G1: + case R_ARM_LDR_PC_G2: + case R_ARM_LDRS_PC_G0: + case R_ARM_LDRS_PC_G1: + case R_ARM_LDRS_PC_G2: case R_ARM_THM_ALU_PREL_11_0: case R_ARM_THM_PC8: case R_ARM_THM_PC12: @@ -158,7 +170,9 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s, // not ARMv4 output, we can just ignore it. return R_NONE; default: - return R_ABS; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } @@ -178,7 +192,7 @@ void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const { } // Long form PLT Header that does not have any restrictions on the displacement -// of the .plt from the .plt.got. +// of the .plt from the .got.plt. static void writePltHeaderLong(uint8_t *buf) { const uint8_t pltData[] = { 0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]! @@ -195,7 +209,7 @@ static void writePltHeaderLong(uint8_t *buf) { write32le(buf + 16, gotPlt - l1 - 8); } -// The default PLT header requires the .plt.got to be within 128 Mb of the +// The default PLT header requires the .got.plt 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 @@ -231,21 +245,21 @@ void ARM::addPltHeaderSymbols(InputSection &isec) const { } // Long form PLT entries that do not have any restrictions on the displacement -// of the .plt from the .plt.got. +// of the .plt from the .got.plt. static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr) { 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 + 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.got.plt) - 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 +// The default PLT entries require the .got.plt to be within 128 Mb of the // .plt in the positive direction. void ARM::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { @@ -255,9 +269,9 @@ void ARM::writePlt(uint8_t *buf, const Symbol &sym, // 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 + 0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.got.plt) - L1 - 8 + 0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.got.plt) - L1 - 8 + 0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.got.plt) - L1 - 8 }; uint64_t offset = sym.getGotPltVA() - pltEntryAddr - 8; @@ -280,9 +294,11 @@ void ARM::addPltSymbols(InputSection &isec, uint64_t off) const { bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s, int64_t a) 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()) + // 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 it is hidden, its + // binding has been converted to local, so we just check isUndefined() here. A + // undefined non-weak symbol will have been errored. + if (s.isUndefined() && !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 @@ -295,10 +311,11 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, // Otherwise we need to interwork if STT_FUNC Symbol has bit 0 set (Thumb). if (s.isFunc() && expr == R_PC && (s.getVA() & 1)) return true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_ARM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); - return !inBranchRange(type, branchAddr, dst + a); + return !inBranchRange(type, branchAddr, dst + a) || + (!config->armHasBlx && (s.getVA() & 1)); } case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: @@ -306,10 +323,11 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, // Otherwise we need to interwork if STT_FUNC Symbol has bit 0 clear (ARM). if (expr == R_PLT_PC || (s.isFunc() && (s.getVA() & 1) == 0)) return true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_ARM_THM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); - return !inBranchRange(type, branchAddr, dst + a); + return !inBranchRange(type, branchAddr, dst + a) || + (!config->armHasBlx && (s.getVA() & 1) == 0);; } } return false; @@ -382,73 +400,105 @@ bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { // or Thumb. static void stateChangeWarning(uint8_t *loc, RelType relt, const Symbol &s) { assert(!s.isFunc()); + const ErrorPlace place = getErrorPlace(loc); + std::string hint; + if (!place.srcLoc.empty()) + hint = "; " + place.srcLoc; if (s.isSection()) { // Section symbols must be defined and in a section. Users cannot change // the type. Use the section name as getName() returns an empty string. - warn(getErrorLocation(loc) + "branch and link relocation: " + - toString(relt) + " to STT_SECTION symbol " + - cast(s).section->name + " ; interworking not performed"); + warn(place.loc + "branch and link relocation: " + toString(relt) + + " to STT_SECTION symbol " + cast(s).section->name + + " ; interworking not performed" + hint); } else { // Warn with hint on how to alter the symbol type. warn(getErrorLocation(loc) + "branch and link relocation: " + toString(relt) + " to non STT_FUNC symbol: " + s.getName() + " interworking not performed; consider using directive '.type " + s.getName() + - ", %function' to give symbol type STT_FUNC if" - " interworking between ARM and Thumb is required"); + ", %function' to give symbol type STT_FUNC if interworking between " + "ARM and Thumb is required" + + hint); } } -// Utility functions taken from ARMAddressingModes.h, only changes are LLD -// coding style. - // Rotate a 32-bit unsigned value right by a specified amt of bits. static uint32_t rotr32(uint32_t val, uint32_t amt) { assert(amt < 32 && "Invalid rotate amount"); return (val >> amt) | (val << ((32 - amt) & 31)); } -// Rotate a 32-bit unsigned value left by a specified amt of bits. -static uint32_t rotl32(uint32_t val, uint32_t amt) { - assert(amt < 32 && "Invalid rotate amount"); - return (val << amt) | (val >> ((32 - amt) & 31)); +static std::pair getRemAndLZForGroup(unsigned group, + uint32_t val) { + uint32_t rem, lz; + do { + lz = llvm::countLeadingZeros(val) & ~1; + rem = val; + if (lz == 32) // implies rem == 0 + break; + val &= 0xffffff >> lz; + } while (group--); + return {rem, lz}; } -// Try to encode a 32-bit unsigned immediate imm with an immediate shifter -// operand, this form is an 8-bit immediate rotated right by an even number of -// bits. We compute the rotate amount to use. If this immediate value cannot be -// handled with a single shifter-op, determine a good rotate amount that will -// take a maximal chunk of bits out of the immediate. -static uint32_t getSOImmValRotate(uint32_t imm) { - // 8-bit (or less) immediates are trivially shifter_operands with a rotate - // of zero. - if ((imm & ~255U) == 0) - return 0; - - // Use CTZ to compute the rotate amount. - unsigned tz = llvm::countTrailingZeros(imm); - - // Rotate amount must be even. Something like 0x200 must be rotated 8 bits, - // not 9. - unsigned rotAmt = tz & ~1; - - // If we can handle this spread, return it. - if ((rotr32(imm, rotAmt) & ~255U) == 0) - return (32 - rotAmt) & 31; // HW rotates right, not left. +static void encodeAluGroup(uint8_t *loc, const Relocation &rel, uint64_t val, + int group, bool check) { + // ADD/SUB (immediate) add = bit23, sub = bit22 + // immediate field carries is a 12-bit modified immediate, made up of a 4-bit + // even rotate right and an 8-bit immediate. + uint32_t opcode = 0x00800000; + if (val >> 63) { + opcode = 0x00400000; + val = -val; + } + uint32_t imm, lz; + std::tie(imm, lz) = getRemAndLZForGroup(group, val); + uint32_t rot = 0; + if (lz < 24) { + imm = rotr32(imm, 24 - lz); + rot = (lz + 8) << 7; + } + if (check && imm > 0xff) + error(getErrorLocation(loc) + "unencodeable immediate " + Twine(val).str() + + " for relocation " + toString(rel.type)); + write32le(loc, (read32le(loc) & 0xff3ff000) | opcode | rot | (imm & 0xff)); +} - // For values like 0xF000000F, we should ignore the low 6 bits, then - // retry the hunt. - if (imm & 63U) { - unsigned tz2 = countTrailingZeros(imm & ~63U); - unsigned rotAmt2 = tz2 & ~1; - if ((rotr32(imm, rotAmt2) & ~255U) == 0) - return (32 - rotAmt2) & 31; // HW rotates right, not left. +static void encodeLdrGroup(uint8_t *loc, const Relocation &rel, uint64_t val, + int group) { + // R_ARM_LDR_PC_Gn is S + A - P, we have ((S + A) | T) - P, if S is a + // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear + // bottom bit to recover S + A - P. + if (rel.sym->isFunc()) + val &= ~0x1; + // LDR (literal) u = bit23 + uint32_t opcode = 0x00800000; + if (val >> 63) { + opcode = 0x0; + val = -val; } + uint32_t imm = getRemAndLZForGroup(group, val).first; + checkUInt(loc, imm, 12, rel); + write32le(loc, (read32le(loc) & 0xff7ff000) | opcode | imm); +} - // Otherwise, we have no way to cover this span of bits with a single - // shifter_op immediate. Return a chunk of bits that will be useful to - // handle. - return (32 - rotAmt) & 31; // HW rotates right, not left. +static void encodeLdrsGroup(uint8_t *loc, const Relocation &rel, uint64_t val, + int group) { + // R_ARM_LDRS_PC_Gn is S + A - P, we have ((S + A) | T) - P, if S is a + // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear + // bottom bit to recover S + A - P. + if (rel.sym->isFunc()) + val &= ~0x1; + // LDRD/LDRH/LDRSB/LDRSH (literal) u = bit23 + uint32_t opcode = 0x00800000; + if (val >> 63) { + opcode = 0x0; + val = -val; + } + uint32_t imm = getRemAndLZForGroup(group, val).first; + checkUInt(loc, imm, 8, rel); + write32le(loc, (read32le(loc) & 0xff7ff0f0) | opcode | ((imm & 0xf0) << 4) | + (imm & 0xf)); } void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { @@ -502,14 +552,20 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B } - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: checkInt(loc, val, 26, rel); write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); break; + case R_ARM_THM_JUMP8: + // We do a 9 bit check because val is right-shifted by 1 bit. + checkInt(loc, val, 9, rel); + write16le(loc, (read32le(loc) & 0xff00) | ((val >> 1) & 0x00ff)); + break; case R_ARM_THM_JUMP11: + // We do a 12 bit check because val is right-shifted by 1 bit. checkInt(loc, val, 12, rel); write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); break; @@ -563,7 +619,7 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } } // Fall through as rest of encoding is the same as B.W - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 checkInt(loc, val, 25, rel); @@ -615,45 +671,39 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { ((val << 4) & 0x7000) | // imm3 (val & 0x00ff)); // imm8 break; - case R_ARM_ALU_PC_G0: { - // ADR (literal) add = bit23, sub = bit22 - // literal is a 12-bit modified immediate, made up of a 4-bit even rotate - // right and an 8-bit immediate. The code-sequence here is derived from - // ARMAddressingModes.h in llvm/Target/ARM/MCTargetDesc. In our case we - // want to give an error if we cannot encode the constant. - uint32_t opcode = 0x00800000; - if (val >> 63) { - opcode = 0x00400000; - val = ~val + 1; - } - if ((val & ~255U) != 0) { - uint32_t rotAmt = getSOImmValRotate(val); - // Error if we cannot encode this with a single shift - if (rotr32(~255U, rotAmt) & val) - error(getErrorLocation(loc) + "unencodeable immediate " + - Twine(val).str() + " for relocation " + toString(rel.type)); - val = rotl32(val, rotAmt) | ((rotAmt >> 1) << 8); - } - write32le(loc, (read32le(loc) & 0xff0ff000) | opcode | val); + case R_ARM_ALU_PC_G0: + encodeAluGroup(loc, rel, val, 0, true); break; - } - case R_ARM_LDR_PC_G0: { - // R_ARM_LDR_PC_G0 is S + A - P, we have ((S + A) | T) - P, if S is a - // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear - // bottom bit to recover S + A - P. - if (rel.sym->isFunc()) - val &= ~0x1; - // LDR (literal) u = bit23 - int64_t imm = val; - uint32_t u = 0x00800000; - if (imm < 0) { - imm = -imm; - u = 0; - } - checkUInt(loc, imm, 12, rel); - write32le(loc, (read32le(loc) & 0xff7ff000) | u | imm); + case R_ARM_ALU_PC_G0_NC: + encodeAluGroup(loc, rel, val, 0, false); + break; + case R_ARM_ALU_PC_G1: + encodeAluGroup(loc, rel, val, 1, true); + break; + case R_ARM_ALU_PC_G1_NC: + encodeAluGroup(loc, rel, val, 1, false); + break; + case R_ARM_ALU_PC_G2: + encodeAluGroup(loc, rel, val, 2, true); + break; + case R_ARM_LDR_PC_G0: + encodeLdrGroup(loc, rel, val, 0); + break; + case R_ARM_LDR_PC_G1: + encodeLdrGroup(loc, rel, val, 1); + break; + case R_ARM_LDR_PC_G2: + encodeLdrGroup(loc, rel, val, 2); + break; + case R_ARM_LDRS_PC_G0: + encodeLdrsGroup(loc, rel, val, 0); + break; + case R_ARM_LDRS_PC_G1: + encodeLdrsGroup(loc, rel, val, 1); + break; + case R_ARM_LDRS_PC_G2: + encodeLdrsGroup(loc, rel, val, 2); break; - } case R_ARM_THM_ALU_PREL_11_0: { // ADR encoding T2 (sub), T3 (add) i:imm3:imm8 int64_t imm = val; @@ -699,8 +749,7 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { break; } default: - error(getErrorLocation(loc) + "unrecognized relocation " + - toString(rel.type)); + llvm_unreachable("unknown relocation"); } } @@ -738,6 +787,8 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_ARM_PC24: case R_ARM_PLT32: return SignExtend64<26>(read32le(buf) << 2); + case R_ARM_THM_JUMP8: + return SignExtend64<9>(read16le(buf) << 1); case R_ARM_THM_JUMP11: return SignExtend64<12>(read16le(buf) << 1); case R_ARM_THM_JUMP19: { @@ -760,7 +811,7 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { ((lo & 0x7ff) << 1)); // imm11:0 break; } - LLVM_FALLTHROUGH; + [[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) @@ -797,7 +848,11 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { ((lo & 0x7000) >> 4) | // imm3 (lo & 0x00ff)); // imm8 } - case R_ARM_ALU_PC_G0: { + case R_ARM_ALU_PC_G0: + case R_ARM_ALU_PC_G0_NC: + case R_ARM_ALU_PC_G1: + case R_ARM_ALU_PC_G1_NC: + case R_ARM_ALU_PC_G2: { // 12-bit immediate is a modified immediate made up of a 4-bit even // right rotation and 8-bit constant. After the rotation the value // is zero-extended. When bit 23 is set the instruction is an add, when @@ -806,13 +861,25 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { uint32_t val = rotr32(instr & 0xff, ((instr & 0xf00) >> 8) * 2); return (instr & 0x00400000) ? -val : val; } - case R_ARM_LDR_PC_G0: { + case R_ARM_LDR_PC_G0: + case R_ARM_LDR_PC_G1: + case R_ARM_LDR_PC_G2: { // ADR (literal) add = bit23, sub = bit22 // LDR (literal) u = bit23 unsigned imm12 bool u = read32le(buf) & 0x00800000; uint32_t imm12 = read32le(buf) & 0xfff; return u ? imm12 : -imm12; } + case R_ARM_LDRS_PC_G0: + case R_ARM_LDRS_PC_G1: + case R_ARM_LDRS_PC_G2: { + // LDRD/LDRH/LDRSB/LDRSH (literal) u = bit23 unsigned imm8 + uint32_t opcode = read32le(buf); + bool u = opcode & 0x00800000; + uint32_t imm4l = opcode & 0xf; + uint32_t imm4h = (opcode & 0xf00) >> 4; + return u ? (imm4h | imm4l) : -(imm4h | imm4l); + } case R_ARM_THM_ALU_PREL_11_0: { // Thumb2 ADR, which is an alias for a sub or add instruction with an // unsigned immediate. @@ -838,6 +905,7 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { return u ? imm12 : -imm12; } case R_ARM_NONE: + case R_ARM_V4BX: case R_ARM_JUMP_SLOT: // These relocations are defined as not having an implicit addend. return 0; diff --git a/gnu/llvm/lld/ELF/Arch/AVR.cpp b/gnu/llvm/lld/ELF/Arch/AVR.cpp index d0d24722570..f0bb8efc4c5 100644 --- a/gnu/llvm/lld/ELF/Arch/AVR.cpp +++ b/gnu/llvm/lld/ELF/Arch/AVR.cpp @@ -29,7 +29,7 @@ #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Object/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -42,7 +42,6 @@ using namespace lld::elf; namespace { class AVR final : public TargetInfo { public: - AVR(); uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; @@ -51,8 +50,6 @@ public: }; } // namespace -AVR::AVR() { noneRel = R_AVR_NONE; } - RelExpr AVR::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { @@ -77,6 +74,7 @@ RelExpr AVR::getRelExpr(RelType type, const Symbol &s, case R_AVR_HI8_LDI_PM_NEG: case R_AVR_HH8_LDI_PM: case R_AVR_HH8_LDI_PM_NEG: + case R_AVR_LDS_STS_16: case R_AVR_PORT5: case R_AVR_PORT6: case R_AVR_CALL: @@ -173,6 +171,14 @@ void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { writeLDI(loc, (-val >> 17) & 0xff); break; + case R_AVR_LDS_STS_16: { + checkUInt(loc, val, 7, rel); + const uint16_t hi = val >> 4; + const uint16_t lo = val & 0xf; + write16le(loc, (read16le(loc) & 0xf8f0) | ((hi << 8) | lo)); + break; + } + case R_AVR_PORT5: checkUInt(loc, val, 5, rel); write16le(loc, (read16le(loc) & 0xff07) | (val << 3)); @@ -229,12 +235,12 @@ static uint32_t getEFlags(InputFile *file) { } uint32_t AVR::calcEFlags() const { - assert(!objectFiles.empty()); + assert(!ctx.objectFiles.empty()); - uint32_t flags = getEFlags(objectFiles[0]); + uint32_t flags = getEFlags(ctx.objectFiles[0]); bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED; - for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) { uint32_t objFlags = getEFlags(f); if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK)) error(toString(f) + diff --git a/gnu/llvm/lld/ELF/Arch/Hexagon.cpp b/gnu/llvm/lld/ELF/Arch/Hexagon.cpp index 02d872d58ca..bc26653697c 100644 --- a/gnu/llvm/lld/ELF/Arch/Hexagon.cpp +++ b/gnu/llvm/lld/ELF/Arch/Hexagon.cpp @@ -12,7 +12,6 @@ #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; @@ -44,6 +43,7 @@ Hexagon::Hexagon() { gotRel = R_HEX_GLOB_DAT; symbolicRel = R_HEX_32; + gotBaseSymInGotPlt = true; // The zero'th GOT entry is reserved for the address of _DYNAMIC. The // next 3 are reserved for the dynamic loader. gotPltHeaderEntriesNum = 4; @@ -53,19 +53,18 @@ Hexagon::Hexagon() { // Hexagon Linux uses 64K pages by default. defaultMaxPageSize = 0x10000; - noneRel = R_HEX_NONE; tlsGotRel = R_HEX_TPREL_32; tlsModuleIndexRel = R_HEX_DTPMOD_32; tlsOffsetRel = R_HEX_DTPREL_32; } uint32_t Hexagon::calcEFlags() const { - assert(!objectFiles.empty()); + assert(!ctx.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) { + for (InputFile *f : ctx.objectFiles) { uint32_t eflags = cast>(f)->getObj().getHeader().e_flags; if (eflags > ret) ret = eflags; @@ -146,7 +145,6 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, case R_HEX_IE_GOT_32_6_X: case R_HEX_IE_GOT_HI16: case R_HEX_IE_GOT_LO16: - config->hasStaticTlsModel = true; return R_GOTPLT; case R_HEX_TPREL_11_X: case R_HEX_TPREL_16: @@ -162,6 +160,28 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, } } +// 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}}; + static bool isDuplex(uint32_t insn) { // 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 @@ -170,29 +190,6 @@ static bool isDuplex(uint32_t insn) { } 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}}; - if (isDuplex(insn)) return 0x03f00000; @@ -200,7 +197,7 @@ static uint32_t findMaskR6(uint32_t insn) { if ((0xff000000 & insn) == i.cmpMask) return i.relocMask; - error("unrecognized instruction for R_HEX_6 relocation: 0x" + + error("unrecognized instruction for 6_X relocation: 0x" + utohexstr(insn)); return 0; } @@ -232,7 +229,11 @@ static uint32_t findMaskR16(uint32_t insn) { if (isDuplex(insn)) return 0x03f00000; - error("unrecognized instruction for R_HEX_16_X relocation: 0x" + + for (InstructionMask i : r6) + if ((0xff000000 & insn) == i.cmpMask) + return i.relocMask; + + error("unrecognized instruction for 16_X type: 0x" + utohexstr(insn)); return 0; } diff --git a/gnu/llvm/lld/ELF/Arch/MSP430.cpp b/gnu/llvm/lld/ELF/Arch/MSP430.cpp index 4af90b40a34..378b2878d44 100644 --- a/gnu/llvm/lld/ELF/Arch/MSP430.cpp +++ b/gnu/llvm/lld/ELF/Arch/MSP430.cpp @@ -15,11 +15,10 @@ // //===----------------------------------------------------------------------===// -#include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Object/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; diff --git a/gnu/llvm/lld/ELF/Arch/Mips.cpp b/gnu/llvm/lld/ELF/Arch/Mips.cpp index a233a01d5bb..d5a335c6593 100644 --- a/gnu/llvm/lld/ELF/Arch/Mips.cpp +++ b/gnu/llvm/lld/ELF/Arch/Mips.cpp @@ -11,9 +11,8 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "Thunks.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Object/ELF.h" +#include "llvm/BinaryFormat/ELF.h" using namespace llvm; using namespace llvm::object; @@ -46,11 +45,9 @@ public: 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; @@ -128,18 +125,19 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, return R_MIPS_GOT_GP_PC; if (&s == ElfSym::mipsLocalGp) return R_MIPS_GOT_GP; - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_MIPS_32: case R_MIPS_64: case R_MIPS_GOT_OFST: case R_MIPS_SUB: + return R_ABS; 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; + return R_DTPREL; case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: case R_MIPS_TLS_TPREL32: @@ -166,7 +164,7 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, case R_MICROMIPS_GOT16: if (s.isLocal()) return R_MIPS_GOT_LOCAL_PAGE; - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_TLS_GOTTPREL: @@ -629,7 +627,7 @@ void MIPS::relocate(uint8_t *loc, const Relocation &rel, case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_LDM: checkInt(loc, val, 16, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_MIPS_CALL_LO16: case R_MIPS_GOT_LO16: case R_MIPS_GOT_OFST: diff --git a/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp b/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp index 77c05a818a5..44661731a3e 100644 --- a/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp +++ b/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp @@ -16,7 +16,6 @@ #include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/ELF.h" -#include "llvm/Object/ELF.h" #include "llvm/Support/MipsABIFlags.h" using namespace llvm; @@ -296,7 +295,7 @@ static uint32_t getArchFlags(ArrayRef files) { template uint32_t elf::calcMipsEFlags() { std::vector v; - for (InputFile *f : objectFiles) + for (InputFile *f : ctx.objectFiles) v.push_back({f, cast>(f)->getObj().getHeader().e_flags}); if (v.empty()) { // If we don't have any input files, we'll have to rely on the information diff --git a/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp b/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp index 9e18ae4753b..4ae742c6c4e 100644 --- a/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp +++ b/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -#include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -35,7 +34,6 @@ public: 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; diff --git a/gnu/llvm/lld/ELF/Arch/X86.cpp b/gnu/llvm/lld/ELF/Arch/X86.cpp index df769f0a1c8..8d4f258e2cf 100644 --- a/gnu/llvm/lld/ELF/Arch/X86.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "InputFiles.h" +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -38,28 +38,22 @@ public: uint64_t val) const override; RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; - void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) 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; + tlsDescRel = R_386_TLS_DESC; tlsGotRel = R_386_TLS_TPOFF; tlsModuleIndexRel = R_386_TLS_DTPMOD32; tlsOffsetRel = R_386_TLS_DTPOFF32; + gotBaseSymInGotPlt = true; pltHeaderSize = 16; pltEntrySize = 16; ipltEntrySize = 16; @@ -71,19 +65,12 @@ X86::X86() { } int X86::getTlsGdRelaxSkip(RelType type) const { - return 2; + // TLSDESC relocations are processed separately. See relaxTlsGdToLe below. + return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 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: @@ -143,6 +130,10 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s, // 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_GOTDESC: + return R_TLSDESC_GOTPLT; + case R_386_TLS_DESC_CALL: + return R_TLSDESC_CALL; case R_386_TLS_GOTIE: return R_GOTPLT; case R_386_GOTOFF: @@ -167,7 +158,8 @@ RelExpr X86::adjustTlsExpr(RelType type, RelExpr expr) const { 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; + return type == R_386_TLS_GD ? R_RELAX_TLS_GD_TO_LE_NEG + : R_RELAX_TLS_GD_TO_LE; } } @@ -218,7 +210,7 @@ void X86::writePltHeader(uint8_t *buf) const { void X86::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { - unsigned relOff = in.relaPlt->entsize * sym.pltIndex; + unsigned relOff = in.relaPlt->entsize * sym.getPltIdx(); if (config->isPic) { const uint8_t inst[] = { 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx) @@ -259,6 +251,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_386_PC32: case R_386_PLT32: case R_386_RELATIVE: + case R_386_TLS_GOTDESC: + case R_386_TLS_DESC_CALL: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: case R_386_TLS_LDO_32: @@ -273,6 +267,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: return SignExtend64<32>(read32le(buf)); + case R_386_TLS_DESC: + return SignExtend64<32>(read32le(buf + 4)); case R_386_NONE: case R_386_JUMP_SLOT: // These relocations are defined as not having an implicit addend. @@ -323,6 +319,8 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_386_PC32: case R_386_PLT32: case R_386_RELATIVE: + case R_386_TLS_GOTDESC: + case R_386_TLS_DESC_CALL: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: case R_386_TLS_GD: @@ -337,45 +335,85 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { checkInt(loc, val, 32, rel); write32le(loc, val); break; + case R_386_TLS_DESC: + // The addend is stored in the second 32-bit word. + write32le(loc + 4, val); + break; default: llvm_unreachable("unknown relocation"); } } -void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, 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); +static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) { + if (rel.type == R_386_TLS_GD) { + // Convert (loc[-2] == 0x04) + // leal x@tlsgd(, %ebx, 1), %eax + // call ___tls_get_addr@plt + // or + // leal x@tlsgd(%reg), %eax + // call *___tls_get_addr@got(%reg) + // to + const uint8_t inst[] = { + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax + 0x81, 0xe8, 0, 0, 0, 0, // subl x@ntpoff(%ebx), %eax + }; + uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2; + memcpy(w, inst, sizeof(inst)); + write32le(w + 8, val); + } else if (rel.type == R_386_TLS_GOTDESC) { + // Convert leal x@tlsdesc(%ebx), %eax to leal x@ntpoff, %eax. + // + // Note: call *x@tlsdesc(%eax) may not immediately follow this instruction. + if (memcmp(loc - 2, "\x8d\x83", 2)) { + error(getErrorLocation(loc - 2) + + "R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax"); + return; + } + loc[-1] = 0x05; + write32le(loc, val); + } else { + // Convert call *x@tlsdesc(%eax) to xchg ax, ax. + assert(rel.type == R_386_TLS_DESC_CALL); + loc[0] = 0x66; + loc[1] = 0x90; + } } -void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, 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); +static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) { + if (rel.type == R_386_TLS_GD) { + // Convert (loc[-2] == 0x04) + // leal x@tlsgd(, %ebx, 1), %eax + // call ___tls_get_addr@plt + // or + // leal x@tlsgd(%reg), %eax + // call *___tls_get_addr@got(%reg) + const uint8_t inst[] = { + 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax + 0x03, 0x83, 0, 0, 0, 0, // addl x@gottpoff(%ebx), %eax + }; + uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2; + memcpy(w, inst, sizeof(inst)); + write32le(w + 8, val); + } else if (rel.type == R_386_TLS_GOTDESC) { + // Convert leal x@tlsdesc(%ebx), %eax to movl x@gotntpoff(%ebx), %eax. + if (memcmp(loc - 2, "\x8d\x83", 2)) { + error(getErrorLocation(loc - 2) + + "R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax"); + return; + } + loc[-2] = 0x8b; + write32le(loc, val); + } else { + // Convert call *x@tlsdesc(%eax) to xchg ax, ax. + assert(rel.type == R_386_TLS_DESC_CALL); + loc[0] = 0x66; + loc[1] = 0x90; + } } // 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, const Relocation &rel, - uint64_t val) const { +static void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) { // 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 @@ -412,28 +450,68 @@ void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, write32le(loc, val); } -void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { +static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) { if (rel.type == R_386_TLS_LDO_32) { write32le(loc, val); return; } + if (loc[4] == 0xe8) { + // Convert + // leal x(%reg),%eax + // call ___tls_get_addr@plt + // to + 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)); + return; + } + // Convert - // leal foo(%reg),%eax - // call ___tls_get_addr + // leal x(%reg),%eax + // call *___tls_get_addr@got(%reg) // 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 + 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, // leal (%esi),%esi }; memcpy(loc - 2, inst, sizeof(inst)); } +void X86::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + for (const Relocation &rel : sec.relocs()) { + uint8_t *loc = buf + rel.offset; + const uint64_t val = SignExtend64( + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr), + 32); + switch (rel.expr) { + case R_RELAX_TLS_GD_TO_IE_GOTPLT: + relaxTlsGdToIe(loc, rel, val); + continue; + case R_RELAX_TLS_GD_TO_LE: + case R_RELAX_TLS_GD_TO_LE_NEG: + relaxTlsGdToLe(loc, rel, val); + continue; + case R_RELAX_TLS_LD_TO_LE: + relaxTlsLdToLe(loc, rel, val); + break; + case R_RELAX_TLS_IE_TO_LE: + relaxTlsIeToLe(loc, rel, val); + continue; + default: + relocate(loc, rel, val); + break; + } + } +} + // If Intel Indirect Branch Tracking is enabled, we have to emit special PLT // entries containing endbr32 instructions. A PLT entry will be split into two // parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt). @@ -454,7 +532,7 @@ IntelIBT::IntelIBT() { pltHeaderSize = 0; } void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const { uint64_t va = - in.ibtPlt->getVA() + IBTPltHeaderSize + s.pltIndex * pltEntrySize; + in.ibtPlt->getVA() + IBTPltHeaderSize + s.getPltIdx() * pltEntrySize; write32le(buf, va); } @@ -552,7 +630,7 @@ void RetpolinePic::writePltHeader(uint8_t *buf) const { void RetpolinePic::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { - unsigned relOff = in.relaPlt->entsize * sym.pltIndex; + unsigned relOff = in.relaPlt->entsize * sym.getPltIdx(); const uint8_t insn[] = { 0x50, // pushl %eax 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax @@ -611,7 +689,7 @@ void RetpolineNoPic::writePltHeader(uint8_t *buf) const { void RetpolineNoPic::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { - unsigned relOff = in.relaPlt->entsize * sym.pltIndex; + unsigned relOff = in.relaPlt->entsize * sym.getPltIdx(); const uint8_t insn[] = { 0x50, // 0: pushl %eax 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax diff --git a/gnu/llvm/lld/ELF/CMakeLists.txt b/gnu/llvm/lld/ELF/CMakeLists.txt index f85d0fb9f55..8e6a746d219 100644 --- a/gnu/llvm/lld/ELF/CMakeLists.txt +++ b/gnu/llvm/lld/ELF/CMakeLists.txt @@ -2,6 +2,22 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(ELFOptionsTableGen) +if(LLVM_ENABLE_ZLIB) + set(imported_libs ZLIB::ZLIB) +endif() + +if(LLVM_ENABLE_ZSTD) + if(TARGET zstd::libzstd_shared AND NOT LLVM_USE_STATIC_ZSTD) + set(zstd_target zstd::libzstd_shared) + else() + set(zstd_target zstd::libzstd_static) + endif() +endif() + +if(LLVM_ENABLE_ZSTD) + list(APPEND imported_libs ${zstd_target}) +endif() + add_lld_library(lldELF AArch64ErrataFix.cpp Arch/AArch64.cpp @@ -55,9 +71,11 @@ add_lld_library(lldELF Option Passes Support + TargetParser LINK_LIBS lldCommon + ${imported_libs} ${LLVM_PTHREAD_LIB} DEPENDS diff --git a/gnu/llvm/lld/ELF/CallGraphSort.cpp b/gnu/llvm/lld/ELF/CallGraphSort.cpp index 15da4d2414a..ff72731b1f3 100644 --- a/gnu/llvm/lld/ELF/CallGraphSort.cpp +++ b/gnu/llvm/lld/ELF/CallGraphSort.cpp @@ -41,9 +41,10 @@ //===----------------------------------------------------------------------===// #include "CallGraphSort.h" -#include "OutputSections.h" -#include "SymbolTable.h" +#include "InputFiles.h" +#include "InputSection.h" #include "Symbols.h" +#include "llvm/Support/FileSystem.h" #include @@ -114,8 +115,8 @@ CallGraphSort::CallGraphSort() { // Create the graph. for (std::pair &c : profile) { - const auto *fromSB = cast(c.first.first->repl); - const auto *toSB = cast(c.first.second->repl); + const auto *fromSB = cast(c.first.first); + const auto *toSB = cast(c.first.second); uint64_t weight = c.second; // Ignore edges between input sections belonging to different output @@ -155,7 +156,7 @@ static bool isNewDensityBad(Cluster &a, Cluster &b) { // 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) { +static int getLeader(int *leaders, int v) { while (leaders[v] != v) { leaders[v] = leaders[leaders[v]]; v = leaders[v]; @@ -180,9 +181,9 @@ static void mergeClusters(std::vector &cs, Cluster &into, int intoIdx, // then sort the clusters by density. DenseMap CallGraphSort::run() { std::vector sorted(clusters.size()); - std::vector leaders(clusters.size()); + std::unique_ptr leaders(new int[clusters.size()]); - std::iota(leaders.begin(), leaders.end(), 0); + std::iota(leaders.get(), leaders.get() + clusters.size(), 0); std::iota(sorted.begin(), sorted.end(), 0); llvm::stable_sort(sorted, [&](int a, int b) { return clusters[a].getDensity() > clusters[b].getDensity(); @@ -197,7 +198,7 @@ DenseMap CallGraphSort::run() { if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) continue; - int predL = getLeader(leaders, c.bestPred.from); + int predL = getLeader(leaders.get(), c.bestPred.from); if (l == predL) continue; @@ -259,7 +260,7 @@ DenseMap CallGraphSort::run() { return orderMap; } -// Sort sections by the profile data provided by -callgraph-profile-file +// 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³ heuristic. All clusters are then sorted by a density diff --git a/gnu/llvm/lld/ELF/CallGraphSort.h b/gnu/llvm/lld/ELF/CallGraphSort.h index 5a092278a41..4997cb102c3 100644 --- a/gnu/llvm/lld/ELF/CallGraphSort.h +++ b/gnu/llvm/lld/ELF/CallGraphSort.h @@ -11,12 +11,10 @@ #include "llvm/ADT/DenseMap.h" -namespace lld { -namespace elf { +namespace lld::elf { class InputSectionBase; llvm::DenseMap computeCallGraphProfileOrder(); -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/DWARF.cpp b/gnu/llvm/lld/ELF/DWARF.cpp index 707a6ebd169..a56454c53a6 100644 --- a/gnu/llvm/lld/ELF/DWARF.cpp +++ b/gnu/llvm/lld/ELF/DWARF.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// The -gdb-index option instructs the linker to emit a .gdb_index section. +// 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. @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// #include "DWARF.h" +#include "InputSection.h" #include "Symbols.h" -#include "Target.h" #include "lld/Common/Memory.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/ELFObjectFile.h" @@ -27,11 +27,9 @@ using namespace lld::elf; template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { // Get the ELF sections to retrieve sh_flags. See the SHF_GROUP comment below. - ArrayRef objSections = - CHECK(obj->getObj().sections(), obj); + ArrayRef objSections = obj->template getELFShdrs(); assert(objSections.size() == obj->getSections().size()); - for (auto it : llvm::enumerate(obj->getSections())) { - InputSectionBase *sec = it.value(); + for (auto [i, sec] : llvm::enumerate(obj->getSections())) { if (!sec) continue; @@ -46,19 +44,19 @@ template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { .Case(".debug_str_offsets", &strOffsetsSection) .Case(".debug_line", &lineSection) .Default(nullptr)) { - m->Data = toStringRef(sec->data()); + m->Data = toStringRef(sec->contentMaybeDecompress()); m->sec = sec; continue; } if (sec->name == ".debug_abbrev") - abbrevSection = toStringRef(sec->data()); + abbrevSection = toStringRef(sec->contentMaybeDecompress()); else if (sec->name == ".debug_str") - strSection = toStringRef(sec->data()); + strSection = toStringRef(sec->contentMaybeDecompress()); else if (sec->name == ".debug_line_str") - lineStrSection = toStringRef(sec->data()); + lineStrSection = toStringRef(sec->contentMaybeDecompress()); else if (sec->name == ".debug_info" && - !(objSections[it.index()].sh_flags & ELF::SHF_GROUP)) { + !(objSections[i].sh_flags & ELF::SHF_GROUP)) { // In DWARF v5, -fdebug-types-section places type units in .debug_info // sections in COMDAT groups. They are not compile units and thus should // be ignored for .gdb_index/diagnostics purposes. @@ -68,7 +66,7 @@ template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { // need to perform a lightweight parsing. We drop the SHF_GROUP flag when // the InputSection was created, so we need to retrieve sh_flags from the // associated ELF section header. - infoSection.Data = toStringRef(sec->data()); + infoSection.Data = toStringRef(sec->contentMaybeDecompress()); infoSection.sec = sec; } } @@ -103,13 +101,13 @@ template struct LLDRelocationResolver> { // to llvm since it has no idea about InputSection. template template -Optional +std::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; + return std::nullopt; const RelTy &rel = *it; const ObjFile *file = sec.getFile(); @@ -129,17 +127,18 @@ LLDDwarfObj::findAux(const InputSectionBase &sec, uint64_t pos, DataRefImpl d; d.p = getAddend(rel); return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), - val, Optional(), + val, std::optional(), 0, LLDRelocationResolver::resolve}; } template -Optional LLDDwarfObj::find(const llvm::DWARFSection &s, - uint64_t pos) const { +std::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()); + const RelsOrRelas rels = sec.sec->template relsOrRelas(); + if (rels.areRelocsRel()) + return findAux(*sec.sec, pos, rels.rels); + return findAux(*sec.sec, pos, rels.relas); } template class elf::LLDDwarfObj; diff --git a/gnu/llvm/lld/ELF/DWARF.h b/gnu/llvm/lld/ELF/DWARF.h index 900c63de26f..9a7993903d8 100644 --- a/gnu/llvm/lld/ELF/DWARF.h +++ b/gnu/llvm/lld/ELF/DWARF.h @@ -14,8 +14,7 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ELF.h" -namespace lld { -namespace elf { +namespace lld::elf { class InputSection; @@ -77,14 +76,14 @@ public: return ELFT::TargetEndianness == llvm::support::little; } - llvm::Optional find(const llvm::DWARFSection &sec, - uint64_t pos) const override; + std::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; + std::optional findAux(const InputSectionBase &sec, + uint64_t pos, + ArrayRef rels) const; LLDDWARFSection gnuPubnamesSection; LLDDWARFSection gnuPubtypesSection; @@ -100,7 +99,6 @@ private: StringRef lineStrSection; }; -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/Driver.h b/gnu/llvm/lld/ELF/Driver.h index 96d040041c5..16cb52229e7 100644 --- a/gnu/llvm/lld/ELF/Driver.h +++ b/gnu/llvm/lld/ELF/Driver.h @@ -9,47 +9,14 @@ #ifndef LLD_ELF_DRIVER_H #define LLD_ELF_DRIVER_H -#include "LTO.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/Option/ArgList.h" -#include "llvm/Support/raw_ostream.h" - -namespace lld { -namespace elf { - -extern class LinkerDriver *driver; - -class LinkerDriver { -public: - void linkerMain(ArrayRef args); - void addFile(StringRef path, bool withLOption); - void addLibrary(StringRef name); - -private: - void createFiles(llvm::opt::InputArgList &args); - void inferMachineType(); - template void link(llvm::opt::InputArgList &args); - template void compileBitcodeFiles(); - - // True if we are in --whole-archive and --no-whole-archive. - bool inWholeArchive = false; - - // True if we are in --start-lib and --end-lib. - bool inLib = false; - - // For LTO. - std::unique_ptr lto; - - std::vector files; -}; +#include +namespace lld::elf { // Parses command line options. -class ELFOptTable : public llvm::opt::OptTable { +class ELFOptTable : public llvm::opt::GenericOptTable { public: ELFOptTable(); llvm::opt::InputArgList parse(ArrayRef argv); @@ -66,12 +33,11 @@ enum { void printHelp(); std::string createResponseFile(const llvm::opt::InputArgList &args); -llvm::Optional findFromSearchPaths(StringRef path); -llvm::Optional searchScript(StringRef path); -llvm::Optional searchLibraryBaseName(StringRef path); -llvm::Optional searchLibrary(StringRef path); +std::optional findFromSearchPaths(StringRef path); +std::optional searchScript(StringRef path); +std::optional searchLibraryBaseName(StringRef path); +std::optional searchLibrary(StringRef path); -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/EhFrame.cpp b/gnu/llvm/lld/ELF/EhFrame.cpp index 578c640f021..db58c549007 100644 --- a/gnu/llvm/lld/ELF/EhFrame.cpp +++ b/gnu/llvm/lld/ELF/EhFrame.cpp @@ -36,14 +36,13 @@ namespace { class EhReader { public: EhReader(InputSectionBase *s, ArrayRef d) : isec(s), d(d) {} - size_t readEhRecordSize(); uint8_t getFdeEncoding(); bool hasLSDA(); private: template void failOn(const P *loc, const Twine &msg) { fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " + - isec->getObjMsg((const uint8_t *)loc - isec->data().data())); + isec->getObjMsg((const uint8_t *)loc - isec->content().data())); } uint8_t readByte(); @@ -58,28 +57,6 @@ private: }; } -size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) { - return EhReader(s, s->data().slice(off)).readEhRecordSize(); -} - -// .eh_frame section is a sequence of records. Each record starts with -// a 4 byte length field. This function reads the length. -size_t EhReader::readEhRecordSize() { - if (d.size() < 4) - failOn(d.data(), "CIE/FDE too small"); - - // First 4 bytes of CIE/FDE is the size of the record. - // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead, - // but we do not support that format yet. - uint64_t v = read32(d.data()); - if (v == UINT32_MAX) - failOn(d.data(), "CIE/FDE too large"); - uint64_t size = v + 4; - if (size > d.size()) - failOn(d.data(), "CIE/FDE ends past the end of the section"); - return size; -} - // Read a byte and advance D by one byte. uint8_t EhReader::readByte() { if (d.empty()) @@ -194,7 +171,7 @@ uint8_t EhReader::getFdeEncoding() { readByte(); else if (c == 'P') skipAugP(); - else if (c != 'B' && c != 'S') + else if (c != 'B' && c != 'S' && c != 'G') failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); } return DW_EH_PE_absptr; @@ -211,7 +188,7 @@ bool EhReader::hasLSDA() { skipAugP(); else if (c == 'R') readByte(); - else if (c != 'B' && c != 'S') + else if (c != 'B' && c != 'S' && c != 'G') failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); } return false; diff --git a/gnu/llvm/lld/ELF/EhFrame.h b/gnu/llvm/lld/ELF/EhFrame.h index e44832317b3..95264166e36 100644 --- a/gnu/llvm/lld/ELF/EhFrame.h +++ b/gnu/llvm/lld/ELF/EhFrame.h @@ -11,15 +11,11 @@ #include "lld/Common/LLVM.h" -namespace lld { -namespace elf { -class InputSectionBase; +namespace lld::elf { struct EhSectionPiece; -size_t readEhRecordSize(InputSectionBase *s, size_t off); uint8_t getFdeEncoding(EhSectionPiece *p); bool hasLSDA(const EhSectionPiece &p); -} // namespace elf -} // namespace lld +} #endif diff --git a/gnu/llvm/lld/ELF/ICF.cpp b/gnu/llvm/lld/ELF/ICF.cpp index 5cf944d39c8..c2b0ce9b12b 100644 --- a/gnu/llvm/lld/ELF/ICF.cpp +++ b/gnu/llvm/lld/ELF/ICF.cpp @@ -74,14 +74,12 @@ #include "ICF.h" #include "Config.h" -#include "EhFrame.h" +#include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" -#include "Writer.h" -#include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Parallel.h" @@ -122,7 +120,7 @@ private: void forEachClass(llvm::function_ref fn); - std::vector sections; + SmallVector sections; // We repeat the main loop while `Repeat` is true. std::atomic repeat; @@ -239,6 +237,8 @@ template template bool ICF::constantEq(const InputSection *secA, ArrayRef ra, const InputSection *secB, ArrayRef rb) { + if (ra.size() != rb.size()) + return false; for (size_t i = 0; i < ra.size(); ++i) { if (ra[i].r_offset != rb[i].r_offset || ra[i].getType(config->isMips64EL) != rb[i].getType(config->isMips64EL)) @@ -312,8 +312,8 @@ bool ICF::constantEq(const InputSection *secA, ArrayRef ra, // except relocation targets. template bool ICF::equalsConstant(const InputSection *a, const InputSection *b) { - if (a->numRelocations != b->numRelocations || a->flags != b->flags || - a->getSize() != b->getSize() || a->data() != b->data()) + if (a->flags != b->flags || a->getSize() != b->getSize() || + a->content() != b->content()) return false; // If two sections have different output sections, we cannot merge them. @@ -321,10 +321,11 @@ bool ICF::equalsConstant(const InputSection *a, const InputSection *b) { if (a->getParent() != b->getParent()) return false; - if (a->areRelocsRela) - return constantEq(a, a->template relas(), b, - b->template relas()); - return constantEq(a, a->template rels(), b, b->template rels()); + const RelsOrRelas ra = a->template relsOrRelas(); + const RelsOrRelas rb = b->template relsOrRelas(); + return ra.areRelocsRel() || rb.areRelocsRel() + ? constantEq(a, ra.rels, b, rb.rels) + : constantEq(a, ra.relas, b, rb.relas); } // Compare two lists of relocations. Returns true if all pairs of @@ -369,10 +370,11 @@ bool ICF::variableEq(const InputSection *secA, ArrayRef ra, // Compare "moving" part of two InputSections, namely relocation targets. template bool ICF::equalsVariable(const InputSection *a, const InputSection *b) { - if (a->areRelocsRela) - return variableEq(a, a->template relas(), b, - b->template relas()); - return variableEq(a, a->template rels(), b, b->template rels()); + const RelsOrRelas ra = a->template relsOrRelas(); + const RelsOrRelas rb = b->template relsOrRelas(); + return ra.areRelocsRel() || rb.areRelocsRel() + ? variableEq(a, ra.rels, b, rb.rels) + : variableEq(a, ra.relas, b, rb.relas); } template size_t ICF::findBoundary(size_t begin, size_t end) { @@ -422,11 +424,11 @@ void ICF::forEachClass(llvm::function_ref fn) { boundaries[0] = 0; boundaries[numShards] = sections.size(); - parallelForEachN(1, numShards, [&](size_t i) { + parallelFor(1, numShards, [&](size_t i) { boundaries[i] = findBoundary((i - 1) * step, sections.size()); }); - parallelForEachN(1, numShards + 1, [&](size_t i) { + parallelFor(1, numShards + 1, [&](size_t i) { if (boundaries[i - 1] < boundaries[i]) forEachClassRange(boundaries[i - 1], boundaries[i], fn); }); @@ -459,8 +461,9 @@ template void ICF::run() { // Compute isPreemptible early. We may add more symbols later, so this loop // cannot be merged with the later computeIsPreemptible() pass which is used // by scanRelocations(). - for (Symbol *sym : symtab->symbols()) - sym->isPreemptible = computeIsPreemptible(*sym); + if (config->hasDynSymTab) + for (Symbol *sym : symtab.getSymbols()) + sym->isPreemptible = computeIsPreemptible(*sym); // Two text sections may have identical content and relocations but different // LSDA, e.g. the two functions may have catch blocks of different types. If a @@ -476,9 +479,9 @@ template void ICF::run() { [&](InputSection &s) { s.eqClass[0] = s.eqClass[1] = ++uniqueId; }); // Collect sections to merge. - for (InputSectionBase *sec : inputSections) { - auto *s = cast(sec); - if (s->eqClass[0] == 0) { + for (InputSectionBase *sec : ctx.inputSections) { + auto *s = dyn_cast(sec); + if (s && s->eqClass[0] == 0) { if (isEligible(s)) sections.push_back(s); else @@ -491,7 +494,7 @@ template void ICF::run() { // Initially, we use hash values to partition sections. parallelForEach(sections, [&](InputSection *s) { // Set MSB to 1 to avoid collisions with unique IDs. - s->eqClass[0] = xxHash64(s->data()) | (1U << 31); + s->eqClass[0] = xxHash64(s->content()) | (1U << 31); }); // Perform 2 rounds of relocation hash propagation. 2 is an empirical value to @@ -499,10 +502,11 @@ template void ICF::run() { // a large time complexity will have less work to do. for (unsigned cnt = 0; cnt != 2; ++cnt) { parallelForEach(sections, [&](InputSection *s) { - if (s->areRelocsRela) - combineRelocHashes(cnt, s, s->template relas()); + const RelsOrRelas rels = s->template relsOrRelas(); + if (rels.areRelocsRel()) + combineRelocHashes(cnt, s, rels.rels); else - combineRelocHashes(cnt, s, s->template rels()); + combineRelocHashes(cnt, s, rels.relas); }); } @@ -547,12 +551,28 @@ template void ICF::run() { } }); + // Change Defined symbol's section field to the canonical one. + auto fold = [](Symbol *sym) { + if (auto *d = dyn_cast(sym)) + if (auto *sec = dyn_cast_or_null(d->section)) + if (sec->repl != d->section) { + d->section = sec->repl; + d->folded = true; + } + }; + for (Symbol *sym : symtab.getSymbols()) + fold(sym); + parallelForEach(ctx.objectFiles, [&](ELFFileBase *file) { + for (Symbol *sym : file->getLocalSymbols()) + fold(sym); + }); + // InputSectionDescription::sections is populated by processSectionCommands(). // ICF may fold some input sections assigned to output sections. Remove them. - for (BaseCommand *base : script->sectionCommands) - if (auto *sec = dyn_cast(base)) - for (BaseCommand *sub_base : sec->sectionCommands) - if (auto *isd = dyn_cast(sub_base)) + for (SectionCommand *cmd : script->sectionCommands) + if (auto *osd = dyn_cast(cmd)) + for (SectionCommand *subCmd : osd->osec.commands) + if (auto *isd = dyn_cast(subCmd)) llvm::erase_if(isd->sections, [](InputSection *isec) { return !isec->isLive(); }); } diff --git a/gnu/llvm/lld/ELF/ICF.h b/gnu/llvm/lld/ELF/ICF.h index ed828fc4a94..3246cc33f43 100644 --- a/gnu/llvm/lld/ELF/ICF.h +++ b/gnu/llvm/lld/ELF/ICF.h @@ -9,12 +9,10 @@ #ifndef LLD_ELF_ICF_H #define LLD_ELF_ICF_H -namespace lld { -namespace elf { +namespace lld::elf { template void doIcf(); -} // namespace elf -} // namespace lld +} #endif diff --git a/gnu/llvm/lld/ELF/InputSection.h b/gnu/llvm/lld/ELF/InputSection.h index c914d0b4215..356ccda2d74 100644 --- a/gnu/llvm/lld/ELF/InputSection.h +++ b/gnu/llvm/lld/ELF/InputSection.h @@ -9,29 +9,36 @@ #ifndef LLD_ELF_INPUT_SECTION_H #define LLD_ELF_INPUT_SECTION_H -#include "Config.h" #include "Relocations.h" -#include "Thunks.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/LLVM.h" +#include "lld/Common/Memory.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/Compiler.h" namespace lld { namespace elf { +class InputFile; class Symbol; -struct SectionPiece; class Defined; struct Partition; class SyntheticSection; -class MergeSyntheticSection; template class ObjFile; class OutputSection; -extern std::vector partitions; +LLVM_LIBRARY_VISIBILITY extern std::vector partitions; + +// Returned by InputSectionBase::relsOrRelas. At least one member is empty. +template struct RelsOrRelas { + ArrayRef rels; + ArrayRef relas; + bool areRelocsRel() const { return rels.size(); } +}; // This is the base class of all sections that lld handles. Some are sections in // input files, some are sections in the produced output file and some exist @@ -39,19 +46,10 @@ extern std::vector partitions; // sections. class SectionBase { public: - enum Kind { Regular, EHFrame, Merge, Synthetic, Output }; + enum Kind { Regular, Synthetic, EHFrame, Merge, Output }; Kind kind() const { return (Kind)sectionKind; } - StringRef name; - - // This pointer points to the "real" instance of this instance. - // Usually Repl == this. However, if ICF merges two sections, - // Repl pointer of one section points to another section. So, - // if you need to get a pointer to this instance, do not use - // this but instead this->Repl. - SectionBase *repl; - uint8_t sectionKind : 3; // The next two bit fields are only used by InputSectionBase, but we @@ -62,17 +60,19 @@ public: // Set for sections that should not be folded by ICF. uint8_t keepUnique : 1; + uint8_t partition = 1; + uint32_t type; + StringRef name; + // The 1-indexed partition that this section is assigned to by the garbage // collector, or 0 if this section is dead. Normally there is only one // partition, so this will either be 0 or 1. - uint8_t partition; elf::Partition &getPartition() const; // These corresponds to the fields in Elf_Shdr. - uint32_t alignment; uint64_t flags; - uint64_t entsize; - uint32_t type; + uint32_t addralign; + uint32_t entsize; uint32_t link; uint32_t info; @@ -92,14 +92,16 @@ public: void markDead() { partition = 0; } protected: - SectionBase(Kind sectionKind, StringRef name, uint64_t flags, - uint64_t entsize, uint64_t alignment, uint32_t type, - uint32_t info, uint32_t link) - : name(name), repl(this), sectionKind(sectionKind), bss(false), - keepUnique(false), partition(0), alignment(alignment), flags(flags), - entsize(entsize), type(type), link(link), info(info) {} + constexpr SectionBase(Kind sectionKind, StringRef name, uint64_t flags, + uint32_t entsize, uint32_t addralign, uint32_t type, + uint32_t info, uint32_t link) + : sectionKind(sectionKind), bss(false), keepUnique(false), type(type), + name(name), flags(flags), addralign(addralign), entsize(entsize), + link(link), info(info) {} }; +struct RISCVRelaxAux; + // This corresponds to a section of an input file. class InputSectionBase : public SectionBase { public: @@ -109,80 +111,74 @@ public: InputSectionBase(InputFile *file, uint64_t flags, uint32_t type, uint64_t entsize, uint32_t link, uint32_t info, - uint32_t alignment, ArrayRef data, StringRef name, + uint32_t addralign, ArrayRef data, StringRef name, Kind sectionKind); static bool classof(const SectionBase *s) { return s->kind() != Output; } - // Relocations that refer to this section. - unsigned numRelocations : 31; - unsigned areRelocsRela : 1; - const void *firstRelocation = nullptr; - // The file which contains this section. Its dynamic type is always // ObjFile, but in order to avoid ELFT, we use InputFile as // its static type. InputFile *file; + // Input sections are part of an output section. Special sections + // like .eh_frame and merge sections are first combined into a + // synthetic section that is then added to an output section. In all + // cases this points one level up. + SectionBase *parent = nullptr; + + // Section index of the relocation section if exists. + uint32_t relSecIdx = 0; + template ObjFile *getFile() const { return cast_or_null>(file); } - // If basic block sections are enabled, many code sections could end up with - // one or two jump instructions at the end that could be relaxed to a smaller - // instruction. The members below help trimming the trailing jump instruction - // and shrinking a section. - unsigned bytesDropped = 0; + // Used by --optimize-bb-jumps and RISC-V linker relaxation temporarily to + // indicate the number of bytes which is not counted in the size. This should + // be reset to zero after uses. + uint16_t bytesDropped = 0; + + mutable bool compressed = false; // Whether the section needs to be padded with a NOP filler due to // deleteFallThruJmpInsn. bool nopFiller = false; - void drop_back(uint64_t num) { bytesDropped += num; } + void drop_back(unsigned num) { + assert(bytesDropped + num < 256); + bytesDropped += num; + } void push_back(uint64_t num) { assert(bytesDropped >= num); bytesDropped -= num; } + mutable const uint8_t *content_; + uint64_t size; + void trim() { if (bytesDropped) { - rawData = rawData.drop_back(bytesDropped); + size -= bytesDropped; bytesDropped = 0; } } - ArrayRef data() const { - if (uncompressedSize >= 0) - uncompress(); - return rawData; + ArrayRef content() const { + return ArrayRef(content_, size); + } + ArrayRef contentMaybeDecompress() const { + if (compressed) + decompress(); + return content(); } - - uint64_t getOffsetInFile() const; - - // Input sections are part of an output section. Special sections - // like .eh_frame and merge sections are first combined into a - // synthetic section that is then added to an output section. In all - // cases this points one level up. - SectionBase *parent = nullptr; // The next member in the section group if this section is in a group. This is // used by --gc-sections. InputSectionBase *nextInSectionGroup = nullptr; - template ArrayRef rels() const { - assert(!areRelocsRela); - return llvm::makeArrayRef( - static_cast(firstRelocation), - numRelocations); - } - - template ArrayRef relas() const { - assert(areRelocsRela); - return llvm::makeArrayRef( - static_cast(firstRelocation), - numRelocations); - } + template RelsOrRelas relsOrRelas() const; // InputSections that are dependent on us (reverse dependency for GC) llvm::TinyPtrVector dependentSections; @@ -194,11 +190,10 @@ public: // Get the function symbol that encloses this offset from within the // section. - template Defined *getEnclosingFunction(uint64_t offset); // Returns a source location string. Used to construct an error message. - template std::string getLocation(uint64_t offset); + std::string getLocation(uint64_t offset); std::string getSrcMsg(const Symbol &sym, uint64_t offset); std::string getObjMsg(uint64_t offset); @@ -206,7 +201,6 @@ public: // relocations, assuming that Buf points to this section's copy in // the mmap'ed output buffer. template void relocate(uint8_t *buf, uint8_t *bufEnd); - void relocateAlloc(uint8_t *buf, uint8_t *bufEnd); static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, uint64_t P, const Symbol &Sym, RelExpr Expr); @@ -216,11 +210,24 @@ public: // This vector contains such "cooked" relocations. SmallVector relocations; - // These are modifiers to jump instructions that are necessary when basic - // block sections are enabled. Basic block sections creates opportunities to - // relax jump instructions at basic block boundaries after reordering the - // basic blocks. - SmallVector jumpInstrMods; + void addReloc(const Relocation &r) { relocations.push_back(r); } + MutableArrayRef relocs() { return relocations; } + ArrayRef relocs() const { return relocations; } + + union { + // These are modifiers to jump instructions that are necessary when basic + // block sections are enabled. Basic block sections creates opportunities + // to relax jump instructions at basic block boundaries after reordering the + // basic blocks. + JumpInstrMod *jumpInstrMod = nullptr; + + // Auxiliary information for RISC-V linker relaxation. RISC-V does not use + // jumpInstrMod. + RISCVRelaxAux *relaxAux; + + // The compressed content size when `compressed` is true. + size_t compressedSize; + }; // A function compiled with -fsplit-stack calling a function // compiled without -fsplit-stack needs its prologue adjusted. Find @@ -232,23 +239,15 @@ public: template llvm::ArrayRef getDataAs() const { - size_t s = data().size(); + size_t s = content().size(); assert(s % sizeof(T) == 0); - return llvm::makeArrayRef((const T *)data().data(), s / sizeof(T)); + return llvm::ArrayRef((const T *)content().data(), s / sizeof(T)); } protected: template void parseCompressedHeader(); - void uncompress() const; - - mutable ArrayRef rawData; - - // This field stores the uncompressed size of the compressed data in rawData, - // or -1 if rawData is not compressed (either because the section wasn't - // compressed in the first place, or because we ended up uncompressing it). - // Since the feature is not used often, this is usually -1. - mutable int64_t uncompressedSize = -1; + void decompress() const; }; // SectionPiece represents a piece of splittable section contents. @@ -256,8 +255,9 @@ protected: // have to be as compact as possible, which is why we don't store the size (can // be found by looking at the next one). struct SectionPiece { + SectionPiece() = default; SectionPiece(size_t off, uint32_t hash, bool live) - : inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {} + : inputOff(off), live(live), hash(hash >> 1) {} uint32_t inputOff; uint32_t live : 1; @@ -285,7 +285,7 @@ public: // Splittable sections are handled as a sequence of data // rather than a single large blob of data. - std::vector pieces; + SmallVector pieces; // Returns I'th piece's data. This function is very hot when // string merging is enabled, so we want to inline. @@ -293,20 +293,22 @@ public: llvm::CachedHashStringRef getData(size_t i) const { size_t begin = pieces[i].inputOff; size_t end = - (pieces.size() - 1 == i) ? data().size() : pieces[i + 1].inputOff; - return {toStringRef(data().slice(begin, end - begin)), pieces[i].hash}; + (pieces.size() - 1 == i) ? content().size() : pieces[i + 1].inputOff; + return {toStringRef(content().slice(begin, end - begin)), pieces[i].hash}; } // Returns the SectionPiece at a given input section offset. - SectionPiece *getSectionPiece(uint64_t offset); - const SectionPiece *getSectionPiece(uint64_t offset) const { + SectionPiece &getSectionPiece(uint64_t offset); + const SectionPiece &getSectionPiece(uint64_t offset) const { return const_cast(this)->getSectionPiece(offset); } - SyntheticSection *getParent() const; + SyntheticSection *getParent() const { + return cast_or_null(parent); + } private: - void splitStrings(ArrayRef a, size_t size); + void splitStrings(StringRef s, size_t size); void splitNonStrings(ArrayRef a, size_t size); }; @@ -316,7 +318,7 @@ struct EhSectionPiece { : inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {} ArrayRef data() const { - return {sec->data().data() + this->inputOff, size}; + return {sec->content().data() + this->inputOff, size}; } size_t inputOff; @@ -338,9 +340,10 @@ public: // Splittable sections are handled as a sequence of data // rather than a single large blob of data. - std::vector pieces; + SmallVector cies, fdes; SyntheticSection *getParent() const; + uint64_t getParentOffset(uint64_t offset) const; }; // This is a section that is added directly to an output section @@ -349,19 +352,24 @@ public: // .eh_frame. It also includes the synthetic sections themselves. class InputSection : public InputSectionBase { public: - InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t alignment, + InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t addralign, ArrayRef data, StringRef name, Kind k = Regular); template InputSection(ObjFile &f, const typename ELFT::Shdr &header, StringRef name); + static bool classof(const SectionBase *s) { + return s->kind() == SectionBase::Regular || + s->kind() == SectionBase::Synthetic; + } + // Write this section to a mmap'ed file, assuming Buf is pointing to // beginning of the output section. template void writeTo(uint8_t *buf); - uint64_t getOffset(uint64_t offset) const { return outSecOff + offset; } - - OutputSection *getParent() const; + OutputSection *getParent() const { + return reinterpret_cast(parent); + } // This variable has two usages. Initially, it represents an index in the // OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER @@ -369,13 +377,15 @@ public: // the beginning of the output section this section was assigned to. uint64_t outSecOff = 0; - static bool classof(const SectionBase *s); - InputSectionBase *getRelocatedSection() const; template void relocateNonAlloc(uint8_t *buf, llvm::ArrayRef rels); + // Points to the canonical section. If ICF folds two sections, repl pointer of + // one section points to the other. + InputSection *repl = this; + // Used by ICF. uint32_t eqClass[2] = {0, 0}; @@ -391,20 +401,34 @@ private: template void copyShtGroup(uint8_t *buf); }; -#ifdef _WIN32 -static_assert(sizeof(InputSection) <= 192, "InputSection is too big"); -#else -static_assert(sizeof(InputSection) <= 184, "InputSection is too big"); -#endif +static_assert(sizeof(InputSection) <= 152, "InputSection is too big"); + +class SyntheticSection : public InputSection { +public: + SyntheticSection(uint64_t flags, uint32_t type, uint32_t addralign, + StringRef name) + : InputSection(nullptr, flags, type, addralign, {}, name, + InputSectionBase::Synthetic) {} + + virtual ~SyntheticSection() = default; + virtual size_t getSize() const = 0; + virtual bool updateAllocSize() { return false; } + // If the section has the SHF_ALLOC flag and the size may be changed if + // thunks are added, update the section size. + virtual bool isNeeded() const { return true; } + virtual void finalizeContents() {} + virtual void writeTo(uint8_t *buf) = 0; + + static bool classof(const SectionBase *sec) { + return sec->kind() == InputSectionBase::Synthetic; + } +}; inline bool isDebugSection(const InputSectionBase &sec) { return (sec.flags & llvm::ELF::SHF_ALLOC) == 0 && - (sec.name.startswith(".debug") || sec.name.startswith(".zdebug")); + sec.name.startswith(".debug"); } -// The list of all input sections. -extern std::vector inputSections; - // The set of TOC entries (.toc + addend) for which we should not apply // toc-indirect to toc-relative relaxation. const Symbol * refers to the // STT_SECTION symbol associated to the .toc input section. diff --git a/gnu/llvm/lld/ELF/LTO.cpp b/gnu/llvm/lld/ELF/LTO.cpp index e8710e3bdb4..b80f1f48f76 100644 --- a/gnu/llvm/lld/ELF/LTO.cpp +++ b/gnu/llvm/lld/ELF/LTO.cpp @@ -9,24 +9,20 @@ #include "LTO.h" #include "Config.h" #include "InputFiles.h" -#include "LinkerScript.h" #include "SymbolTable.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/BinaryFormat/ELF.h" -#include "llvm/Bitcode/BitcodeReader.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/Caching.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -81,8 +77,9 @@ static lto::Config createConfig() { // LLD supports the new relocations and address-significance tables. c.Options = initTargetOptionsFromCodeGenFlags(); - c.Options.RelaxELFRelocations = true; c.Options.EmitAddrsig = true; + for (StringRef C : config->mllvmOpts) + c.MllvmArgs.emplace_back(C.str()); // Always emit a section per function/datum with LTO. c.Options.FunctionSections = true; @@ -112,14 +109,13 @@ static lto::Config createConfig() { } } - c.Options.PseudoProbeForProfiling = config->ltoPseudoProbeForProfiling; c.Options.UniqueBasicBlockSectionNames = config->ltoUniqueBasicBlockSectionNames; if (auto relocModel = getRelocModelFromCMModel()) c.RelocModel = *relocModel; else if (config->relocatable) - c.RelocModel = None; + c.RelocModel = std::nullopt; else if (config->isPic) c.RelocModel = Reloc::PIC_; else @@ -147,8 +143,12 @@ static lto::Config createConfig() { c.RemarksHotnessThreshold = config->optRemarksHotnessThreshold; c.RemarksFormat = std::string(config->optRemarksFormat); + // Set up output file to emit statistics. + c.StatsFile = std::string(config->optStatsFilename); + c.SampleProfile = std::string(config->ltoSampleProfile); - c.UseNewPM = config->ltoNewPassManager; + for (StringRef pluginFn : config->passPlugins) + c.PassPlugins.push_back(std::string(pluginFn)); c.DebugPassManager = config->ltoDebugPassManager; c.DwoDir = std::string(config->dwoDir); @@ -163,6 +163,9 @@ static lto::Config createConfig() { c.CSIRProfile = std::string(config->ltoCSProfileFile); c.RunCSIRInstr = config->ltoCSProfileGenerate; + c.PGOWarnMismatch = config->ltoPGOWarnMismatch; + + c.OpaquePointers = config->opaquePointers; if (config->emitLLVM) { c.PostInternalizeModuleHook = [](size_t task, const Module &m) { @@ -173,12 +176,15 @@ static lto::Config createConfig() { }; } - if (config->ltoEmitAsm) + if (config->ltoEmitAsm) { c.CGFileType = CGFT_AssemblyFile; + c.Options.MCOptions.AsmVerbose = true; + } - if (config->saveTemps) + if (!config->saveTempsArgs.empty()) checkError(c.addSaveTemps(config->outputFile.str() + ".", - /*UseInputModulePath*/ true)); + /*UseInputModulePath*/ true, + config->saveTempsArgs)); return c; } @@ -189,22 +195,28 @@ BitcodeCompiler::BitcodeCompiler() { // Initialize ltoObj. lto::ThinBackend backend; + auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); }; if (config->thinLTOIndexOnly) { - auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); }; backend = lto::createWriteIndexesThinBackend( std::string(config->thinLTOPrefixReplace.first), std::string(config->thinLTOPrefixReplace.second), config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite); } else { backend = lto::createInProcessThinBackend( - llvm::heavyweight_hardware_concurrency(config->thinLTOJobs)); + llvm::heavyweight_hardware_concurrency(config->thinLTOJobs), + onIndexWrite, config->thinLTOEmitIndexFiles, + config->thinLTOEmitImportsFiles); } ltoObj = std::make_unique(createConfig(), backend, config->ltoPartitions); // Initialize usedStartStop. - for (Symbol *sym : symtab->symbols()) { + if (ctx.bitcodeFiles.empty()) + return; + for (Symbol *sym : symtab.getSymbols()) { + if (sym->isPlaceholder()) + continue; StringRef s = sym->getName(); for (StringRef prefix : {"__start_", "__stop_"}) if (s.startswith(prefix)) @@ -218,7 +230,7 @@ void BitcodeCompiler::add(BitcodeFile &f) { lto::InputFile &obj = *f.obj; bool isExec = !config->shared && !config->relocatable; - if (config->thinLTOIndexOnly) + if (config->thinLTOEmitIndexFiles) thinIndices.insert(obj.getName()); ArrayRef syms = f.getSymbols(); @@ -243,18 +255,21 @@ void BitcodeCompiler::add(BitcodeFile &f) { // for doing final link. // 2) Symbols that are used in regular objects. // 3) C named sections if we have corresponding __start_/__stop_ symbol. - // 4) Symbols that are defined in bitcode files and used for dynamic linking. + // 4) Symbols that are defined in bitcode files and used for dynamic + // linking. + // 5) Symbols that will be referenced after linker wrapping is performed. r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj || + sym->referencedAfterWrap || (r.Prevailing && sym->includeInDynsym()) || usedStartStop.count(objSym.getSectionName()); // Identify symbols exported dynamically, and that therefore could be // referenced by a shared library not visible to the linker. - r.ExportDynamic = sym->computeBinding() != STB_LOCAL && - (sym->isExportDynamic(sym->kind(), sym->visibility) || - sym->exportDynamic || sym->inDynamicList); + r.ExportDynamic = + sym->computeBinding() != STB_LOCAL && + (config->exportDynamic || sym->exportDynamic || sym->inDynamicList); const auto *dr = dyn_cast(sym); r.FinalDefinitionInLinkageUnit = - (isExec || sym->visibility != STV_DEFAULT) && dr && + (isExec || sym->visibility() != STV_DEFAULT) && dr && // Skip absolute symbols from ELF objects, otherwise PC-rel relocations // will be generated by for them, triggering linker errors. // Symbol section is always null for bitcode symbols, hence the check @@ -263,13 +278,13 @@ void BitcodeCompiler::add(BitcodeFile &f) { !(dr->section == nullptr && (!sym->file || sym->file->isElf())); if (r.Prevailing) - sym->replace(Undefined{nullptr, sym->getName(), STB_GLOBAL, STV_DEFAULT, - sym->type}); + Undefined(nullptr, StringRef(), STB_GLOBAL, STV_DEFAULT, sym->type) + .overwrite(*sym); // We tell LTO to not apply interprocedural optimization for wrapped // (with --wrap) symbols because otherwise LTO would inline them while // their values are still not final. - r.LinkerRedefined = !sym->canInline; + r.LinkerRedefined = sym->scriptDefined; } checkError(ltoObj->add(std::move(f.obj), resols)); } @@ -278,17 +293,24 @@ void BitcodeCompiler::add(BitcodeFile &f) { // This is needed because this is what GNU gold plugin does and we have a // distributed build system that depends on that behavior. static void thinLTOCreateEmptyIndexFiles() { - for (LazyObjFile *f : lazyObjFiles) { - if (f->fetched || !isBitcode(f->mb)) + DenseSet linkedBitCodeFiles; + for (BitcodeFile *f : ctx.bitcodeFiles) + linkedBitCodeFiles.insert(f->getName()); + + for (BitcodeFile *f : ctx.lazyBitcodeFiles) { + if (!f->lazy) + continue; + if (linkedBitCodeFiles.contains(f->getName())) continue; - std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName())); + std::string path = + replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName())); std::unique_ptr os = openFile(path + ".thinlto.bc"); if (!os) continue; ModuleSummaryIndex m(/*HaveGVs*/ false); m.setSkipModuleByDistributedBackend(); - WriteIndexToFile(m, *os); + writeIndexToFile(m, *os); if (config->thinLTOEmitImportsFiles) openFile(path + ".imports"); } @@ -304,18 +326,18 @@ std::vector BitcodeCompiler::compile() { // The --thinlto-cache-dir 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; + FileCache cache; if (!config->thinLTOCacheDir.empty()) - cache = check( - lto::localCache(config->thinLTOCacheDir, - [&](size_t task, std::unique_ptr mb) { - files[task] = std::move(mb); - })); + cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir, + [&](size_t task, const Twine &moduleName, + std::unique_ptr mb) { + files[task] = std::move(mb); + })); - if (!bitcodeFiles.empty()) + if (!ctx.bitcodeFiles.empty()) checkError(ltoObj->run( - [&](size_t task) { - return std::make_unique( + [&](size_t task, const Twine &moduleName) { + return std::make_unique( std::make_unique(buf[task])); }, cache)); @@ -330,9 +352,10 @@ std::vector BitcodeCompiler::compile() { } } - if (config->thinLTOIndexOnly) { + if (config->thinLTOEmitIndexFiles) thinLTOCreateEmptyIndexFiles(); + if (config->thinLTOIndexOnly) { if (!config->ltoObjPath.empty()) saveBuffer(buf[0], config->ltoObjPath); @@ -345,7 +368,7 @@ std::vector BitcodeCompiler::compile() { } if (!config->thinLTOCacheDir.empty()) - pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); + pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files); if (!config->ltoObjPath.empty()) { saveBuffer(buf[0], config->ltoObjPath); @@ -353,7 +376,7 @@ std::vector BitcodeCompiler::compile() { saveBuffer(buf[i], config->ltoObjPath + Twine(i)); } - if (config->saveTemps) { + if (config->saveTempsArgs.contains("prelink")) { if (!buf[0].empty()) saveBuffer(buf[0], config->outputFile + ".lto.o"); for (unsigned i = 1; i != maxTasks; ++i) @@ -370,10 +393,10 @@ std::vector BitcodeCompiler::compile() { std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) if (!buf[i].empty()) - ret.push_back(createObjectFile(MemoryBufferRef(buf[i], "lto.tmp"))); + ret.push_back(createObjFile(MemoryBufferRef(buf[i], "lto.tmp"))); for (std::unique_ptr &file : files) if (file) - ret.push_back(createObjectFile(*file)); + ret.push_back(createObjFile(*file)); return ret; } diff --git a/gnu/llvm/lld/ELF/LTO.h b/gnu/llvm/lld/ELF/LTO.h index 4cb42d84d91..7ab654101f2 100644 --- a/gnu/llvm/lld/ELF/LTO.h +++ b/gnu/llvm/lld/ELF/LTO.h @@ -27,18 +27,14 @@ #include #include -namespace llvm { -namespace lto { +namespace llvm::lto { class LTO; } -} // namespace llvm -namespace lld { -namespace elf { +namespace lld::elf { class BitcodeFile; class InputFile; -class LazyObjFile; class BitcodeCompiler { public: @@ -56,7 +52,6 @@ private: std::unique_ptr indexFile; llvm::DenseSet thinIndices; }; -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/LinkerScript.h b/gnu/llvm/lld/ELF/LinkerScript.h index d2487ae0f9d..4e566853de5 100644 --- a/gnu/llvm/lld/ELF/LinkerScript.h +++ b/gnu/llvm/lld/ELF/LinkerScript.h @@ -15,18 +15,15 @@ #include "lld/Common/Strings.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Compiler.h" #include #include #include #include -#include -namespace lld { -namespace elf { +namespace lld::elf { class Defined; class InputFile; @@ -34,14 +31,14 @@ class InputSection; class InputSectionBase; class OutputSection; class SectionBase; -class Symbol; class ThunkSection; +struct OutputDesc; // This represents an r-value in the linker script. struct ExprValue { ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val, const Twine &loc) - : sec(sec), forceAbsolute(forceAbsolute), val(val), loc(loc.str()) {} + : sec(sec), val(val), forceAbsolute(forceAbsolute), loc(loc.str()) {} ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {} @@ -53,10 +50,6 @@ struct ExprValue { // If a value is relative to a section, it has a non-null Sec. SectionBase *sec; - // True if this expression is enclosed in ABSOLUTE(). - // This flag affects the return value of getValue(). - bool forceAbsolute; - uint64_t val; uint64_t alignment = 1; @@ -64,6 +57,10 @@ struct ExprValue { // resets type to STT_NOTYPE. uint8_t type = llvm::ELF::STT_NOTYPE; + // True if this expression is enclosed in ABSOLUTE(). + // This flag affects the return value of getValue(). + bool forceAbsolute; + // Original source location. Used for error messages. std::string loc; }; @@ -82,17 +79,18 @@ enum SectionsCommandKind { ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr) }; -struct BaseCommand { - BaseCommand(int k) : kind(k) {} +struct SectionCommand { + SectionCommand(int k) : kind(k) {} int kind; }; // This represents ". = " or " = ". -struct SymbolAssignment : BaseCommand { +struct SymbolAssignment : SectionCommand { SymbolAssignment(StringRef name, Expr e, std::string loc) - : BaseCommand(AssignmentKind), name(name), expression(e), location(loc) {} + : SectionCommand(AssignmentKind), name(name), expression(e), + location(loc) {} - static bool classof(const BaseCommand *c) { + static bool classof(const SectionCommand *c) { return c->kind == AssignmentKind; } @@ -132,16 +130,32 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite }; // MEMORY command. struct MemoryRegion { MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags, - uint32_t negFlags) + uint32_t invFlags, uint32_t negFlags, uint32_t negInvFlags) : name(std::string(name)), origin(origin), length(length), flags(flags), - negFlags(negFlags) {} + invFlags(invFlags), negFlags(negFlags), negInvFlags(negInvFlags) {} std::string name; Expr origin; Expr length; + // A section can be assigned to the region if any of these ELF section flags + // are set... uint32_t flags; + // ... or any of these flags are not set. + // For example, the memory region attribute "r" maps to SHF_WRITE. + uint32_t invFlags; + // A section cannot be assigned to the region if any of these ELF section + // flags are set... uint32_t negFlags; + // ... or any of these flags are not set. + // For example, the memory region attribute "!r" maps to SHF_WRITE. + uint32_t negInvFlags; uint64_t curPos = 0; + + bool compatibleWith(uint32_t secFlags) const { + if ((secFlags & negFlags) || (~secFlags & negInvFlags)) + return false; + return (secFlags & flags) || (~secFlags & invFlags); + } }; // This struct represents one section match pattern in SECTIONS() command. @@ -151,7 +165,7 @@ class SectionPattern { StringMatcher excludedFilePat; // Cache of the most recent input argument and result of excludesFile(). - mutable llvm::Optional> excludesFileCache; + mutable std::optional> excludesFileCache; public: SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2) @@ -166,19 +180,19 @@ public: SortSectionPolicy sortInner; }; -class InputSectionDescription : public BaseCommand { +class InputSectionDescription : public SectionCommand { SingleStringMatcher filePat; // Cache of the most recent input argument and result of matchesFile(). - mutable llvm::Optional> matchesFileCache; + mutable std::optional> matchesFileCache; public: InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0, uint64_t withoutFlags = 0) - : BaseCommand(InputSectionKind), filePat(filePattern), + : SectionCommand(InputSectionKind), filePat(filePattern), withFlags(withFlags), withoutFlags(withoutFlags) {} - static bool classof(const BaseCommand *c) { + static bool classof(const SectionCommand *c) { return c->kind == InputSectionKind; } @@ -186,20 +200,20 @@ public: // Input sections that matches at least one of SectionPatterns // will be associated with this InputSectionDescription. - std::vector sectionPatterns; + SmallVector sectionPatterns; // Includes InputSections and MergeInputSections. Used temporarily during // assignment of input sections to output sections. - std::vector sectionBases; + SmallVector sectionBases; // Used after the finalizeInputSections() pass. MergeInputSections have been // merged into MergeSyntheticSections. - std::vector sections; + SmallVector sections; // Temporary record of synthetic ThunkSection instances and the pass that // they were created in. This is used to insert newly created ThunkSections // into Sections at the end of a createThunks() pass. - std::vector> thunkSections; + SmallVector, 0> thunkSections; // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command. uint64_t withFlags; @@ -207,12 +221,12 @@ public: }; // Represents BYTE(), SHORT(), LONG(), or QUAD(). -struct ByteCommand : BaseCommand { +struct ByteCommand : SectionCommand { ByteCommand(Expr e, unsigned size, std::string commandString) - : BaseCommand(ByteKind), commandString(commandString), expression(e), + : SectionCommand(ByteKind), commandString(commandString), expression(e), size(size) {} - static bool classof(const BaseCommand *c) { return c->kind == ByteKind; } + static bool classof(const SectionCommand *c) { return c->kind == ByteKind; } // Keeps string representing the command. Used for -Map" is perhaps better. std::string commandString; @@ -227,7 +241,7 @@ struct ByteCommand : BaseCommand { }; struct InsertCommand { - std::vector names; + SmallVector names; bool isAfter; StringRef where; }; @@ -237,7 +251,7 @@ struct PhdrsCommand { unsigned type = llvm::ELF::PT_NULL; bool hasFilehdr = false; bool hasPhdrs = false; - llvm::Optional flags; + std::optional flags; Expr lmaExpr = nullptr; }; @@ -254,7 +268,7 @@ class LinkerScript final { uint64_t tbssAddr = 0; }; - llvm::DenseMap nameToOutputSection; + llvm::DenseMap nameToOutputSection; void addSymbol(SymbolAssignment *cmd); void assignSymbol(SymbolAssignment *cmd, bool inSec); @@ -262,97 +276,95 @@ class LinkerScript final { void expandOutputSection(uint64_t size); void expandMemoryRegions(uint64_t size); - std::vector + SmallVector computeInputSections(const InputSectionDescription *, ArrayRef); - std::vector createInputSectionList(OutputSection &cmd); + SmallVector createInputSectionList(OutputSection &cmd); void discardSynthetic(OutputSection &); - std::vector getPhdrIndices(OutputSection *sec); - - MemoryRegion *findMemoryRegion(OutputSection *sec); + SmallVector getPhdrIndices(OutputSection *sec); - void switchTo(OutputSection *sec); - uint64_t advance(uint64_t size, unsigned align); - void output(InputSection *sec); + std::pair + findMemoryRegion(OutputSection *sec, MemoryRegion *hint); void assignOffsets(OutputSection *sec); - // Ctx captures the local AddressState and makes it accessible + // This captures the local AddressState and makes it accessible // deliberately. This is needed as there are some cases where we cannot just // thread the current state through to a lambda function created by the // script parser. // This should remain a plain pointer as its lifetime is smaller than // LinkerScript. - AddressState *ctx = nullptr; + AddressState *state = nullptr; OutputSection *aether; uint64_t dot; public: - OutputSection *createOutputSection(StringRef name, StringRef location); - OutputSection *getOrCreateOutputSection(StringRef name); + OutputDesc *createOutputSection(StringRef name, StringRef location); + OutputDesc *getOrCreateOutputSection(StringRef name); bool hasPhdrsCommands() { return !phdrsCommands.empty(); } uint64_t getDot() { return dot; } - void discard(InputSectionBase *s); + void discard(InputSectionBase &s); ExprValue getSymbolValue(StringRef name, const Twine &loc); void addOrphanSections(); void diagnoseOrphanHandling() const; - void adjustSectionsBeforeSorting(); + void adjustOutputSections(); void adjustSectionsAfterSorting(); - std::vector createPhdrs(); + SmallVector createPhdrs(); bool needsInterpSection(); bool shouldKeep(InputSectionBase *s); const Defined *assignAddresses(); - void allocateHeaders(std::vector &phdrs); + void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); void declareSymbols(); + bool isDiscarded(const OutputSection *sec) const; + // Used to handle INSERT AFTER statements. void processInsertCommands(); // SECTIONS command list. - std::vector sectionCommands; + SmallVector sectionCommands; // PHDRS command list. - std::vector phdrsCommands; + SmallVector phdrsCommands; bool hasSectionsCommand = false; bool errorOnMissingSection = false; // List of section patterns specified with KEEP commands. They will // be kept even if they are unused and --gc-sections is specified. - std::vector keptSections; + SmallVector keptSections; // A map from memory region name to a memory region descriptor. llvm::MapVector memoryRegions; // A list of symbols referenced by the script. - std::vector referencedSymbols; + SmallVector referencedSymbols; // Used to implement INSERT [AFTER|BEFORE]. Contains output sections that need // to be reordered. - std::vector insertCommands; + SmallVector insertCommands; // OutputSections specified by OVERWRITE_SECTIONS. - std::vector overwriteSections; + SmallVector overwriteSections; // Sections that will be warned/errored by --orphan-handling. - std::vector orphanSections; + SmallVector orphanSections; }; -extern LinkerScript *script; +LLVM_LIBRARY_VISIBILITY extern std::unique_ptr script; -} // end namespace elf -} // end namespace lld +} // end namespace lld::elf #endif // LLD_ELF_LINKER_SCRIPT_H diff --git a/gnu/llvm/lld/ELF/MapFile.cpp b/gnu/llvm/lld/ELF/MapFile.cpp index 239c6c39484..e28656cd38d 100644 --- a/gnu/llvm/lld/ELF/MapFile.cpp +++ b/gnu/llvm/lld/ELF/MapFile.cpp @@ -22,12 +22,12 @@ #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" -#include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" -#include "lld/Common/Strings.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" @@ -37,7 +37,8 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf; -using SymbolMapTy = DenseMap>; +using SymbolMapTy = DenseMap, 0>>; static constexpr char indent8[] = " "; // 8 spaces static constexpr char indent16[] = " "; // 16 spaces @@ -54,11 +55,11 @@ static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma, // Returns a list of all symbols that we want to print out. static std::vector getSymbols() { std::vector v; - for (InputFile *file : objectFiles) + for (ELFFileBase *file : ctx.objectFiles) for (Symbol *b : file->getSymbols()) if (auto *dr = dyn_cast(b)) if (!dr->isSection() && dr->section && dr->section->isLive() && - (dr->file == file || dr->needsPltAddr || dr->section->bss)) + (dr->file == file || dr->hasFlag(NEEDS_COPY) || dr->section->bss)) v.push_back(dr); return v; } @@ -67,15 +68,21 @@ static std::vector getSymbols() { static SymbolMapTy getSectionSyms(ArrayRef syms) { SymbolMapTy ret; for (Defined *dr : syms) - ret[dr->section].push_back(dr); + ret[dr->section].emplace_back(dr, dr->getVA()); // Sort symbols by address. We want to print out symbols in the // order in the output file rather than the order they appeared // in the input files. - for (auto &it : ret) - llvm::stable_sort(it.second, [](Defined *a, Defined *b) { - return a->getVA() < b->getVA(); + SmallPtrSet set; + for (auto &it : ret) { + // Deduplicate symbols which need a canonical PLT entry/copy relocation. + set.clear(); + llvm::erase_if(it.second, [&](std::pair a) { + return !set.insert(a.first).second; }); + + llvm::stable_sort(it.second, llvm::less_second()); + } return ret; } @@ -84,9 +91,9 @@ static SymbolMapTy getSectionSyms(ArrayRef syms) { // we do that in batch using parallel-for. static DenseMap getSymbolStrings(ArrayRef syms) { - std::vector str(syms.size()); - parallelForEachN(0, syms.size(), [&](size_t i) { - raw_string_ostream os(str[i]); + auto strs = std::make_unique(syms.size()); + parallelFor(0, syms.size(), [&](size_t i) { + raw_string_ostream os(strs[i]); OutputSection *osec = syms[i]->getOutputSection(); uint64_t vma = syms[i]->getVA(); uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0; @@ -96,7 +103,7 @@ getSymbolStrings(ArrayRef syms) { DenseMap ret; for (size_t i = 0, e = syms.size(); i < e; ++i) - ret[syms[i]] = std::move(str[i]); + ret[syms[i]] = std::move(strs[i]); return ret; } @@ -139,20 +146,7 @@ static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) { } } -void elf::writeMapFile() { - if (config->mapFile.empty()) - return; - - llvm::TimeTraceScope timeScope("Write map file"); - - // Open a map file for writing. - std::error_code ec; - raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); - if (ec) { - error("cannot open " + config->mapFile + ": " + ec.message()); - return; - } - +static void writeMapFile(raw_fd_ostream &os) { // Collect symbol info that we want to print out. std::vector syms = getSymbols(); SymbolMapTy sectionSyms = getSectionSyms(syms); @@ -164,61 +158,58 @@ void elf::writeMapFile() { << " Size Align Out In Symbol\n"; OutputSection* osec = nullptr; - for (BaseCommand *base : script->sectionCommands) { - if (auto *cmd = dyn_cast(base)) { - if (cmd->provide && !cmd->sym) + for (SectionCommand *cmd : script->sectionCommands) { + if (auto *assign = dyn_cast(cmd)) { + if (assign->provide && !assign->sym) continue; - uint64_t lma = osec ? osec->getLMA() + cmd->addr - osec->getVA(0) : 0; - writeHeader(os, cmd->addr, lma, cmd->size, 1); - os << cmd->commandString << '\n'; + uint64_t lma = osec ? osec->getLMA() + assign->addr - osec->getVA(0) : 0; + writeHeader(os, assign->addr, lma, assign->size, 1); + os << assign->commandString << '\n'; continue; } - osec = cast(base); - writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->alignment); + osec = &cast(cmd)->osec; + writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->addralign); os << osec->name << '\n'; // Dump symbols for each input section. - for (BaseCommand *base : osec->sectionCommands) { - if (auto *isd = dyn_cast(base)) { + for (SectionCommand *subCmd : osec->commands) { + if (auto *isd = dyn_cast(subCmd)) { for (InputSection *isec : isd->sections) { if (auto *ehSec = dyn_cast(isec)) { printEhFrame(os, ehSec); continue; } - writeHeader(os, isec->getVA(0), osec->getLMA() + isec->getOffset(0), - isec->getSize(), isec->alignment); + writeHeader(os, isec->getVA(), osec->getLMA() + isec->outSecOff, + isec->getSize(), isec->addralign); os << indent8 << toString(isec) << '\n'; - for (Symbol *sym : sectionSyms[isec]) + for (Symbol *sym : llvm::make_first_range(sectionSyms[isec])) os << symStr[sym] << '\n'; } continue; } - if (auto *cmd = dyn_cast(base)) { - writeHeader(os, osec->addr + cmd->offset, osec->getLMA() + cmd->offset, - cmd->size, 1); - os << indent8 << cmd->commandString << '\n'; + if (auto *data = dyn_cast(subCmd)) { + writeHeader(os, osec->addr + data->offset, + osec->getLMA() + data->offset, data->size, 1); + os << indent8 << data->commandString << '\n'; continue; } - if (auto *cmd = dyn_cast(base)) { - if (cmd->provide && !cmd->sym) + if (auto *assign = dyn_cast(subCmd)) { + if (assign->provide && !assign->sym) continue; - writeHeader(os, cmd->addr, osec->getLMA() + cmd->addr - osec->getVA(0), - cmd->size, 1); - os << indent8 << cmd->commandString << '\n'; + writeHeader(os, assign->addr, + osec->getLMA() + assign->addr - osec->getVA(0), + assign->size, 1); + os << indent8 << assign->commandString << '\n'; continue; } } } } -static void print(StringRef a, StringRef b) { - lld::outs() << left_justify(a, 49) << " " << b << "\n"; -} - // Output a cross reference table to stdout. This is for --cref. // // For each global symbol, we print out a file that defines the symbol @@ -230,13 +221,10 @@ static void print(StringRef a, StringRef b) { // // In this case, strlen is defined by libc.so.6 and used by other two // files. -void elf::writeCrossReferenceTable() { - if (!config->cref) - return; - +static void writeCref(raw_fd_ostream &os) { // Collect symbols and files. MapVector> map; - for (InputFile *file : objectFiles) { + for (ELFFileBase *file : ctx.objectFiles) { for (Symbol *sym : file->getSymbols()) { if (isa(sym)) map[sym].insert(file); @@ -246,8 +234,12 @@ void elf::writeCrossReferenceTable() { } } - // Print out a header. - lld::outs() << "Cross Reference Table\n\n"; + auto print = [&](StringRef a, StringRef b) { + os << left_justify(a, 49) << ' ' << b << '\n'; + }; + + // Print a blank line and a header. The format matches GNU ld. + os << "\nCross Reference Table\n\n"; print("Symbol", "File"); // Print out a table. @@ -262,20 +254,23 @@ void elf::writeCrossReferenceTable() { } } -void elf::writeArchiveStats() { - if (config->printArchiveStats.empty()) +void elf::writeMapAndCref() { + if (config->mapFile.empty() && !config->cref) return; + llvm::TimeTraceScope timeScope("Write map file"); + + // Open a map file for writing. std::error_code ec; - raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None); + StringRef mapFile = config->mapFile.empty() ? "-" : config->mapFile; + raw_fd_ostream os(mapFile, ec, sys::fs::OF_None); if (ec) { - error("--print-archive-stats=: cannot open " + config->printArchiveStats + - ": " + ec.message()); + error("cannot open " + mapFile + ": " + ec.message()); return; } - os << "members\tfetched\tarchive\n"; - for (const ArchiveFile *f : archiveFiles) - os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t' - << f->getName() << '\n'; + if (!config->mapFile.empty()) + writeMapFile(os); + if (config->cref) + writeCref(os); } diff --git a/gnu/llvm/lld/ELF/MapFile.h b/gnu/llvm/lld/ELF/MapFile.h index c4da18f8ad7..b271f627df5 100644 --- a/gnu/llvm/lld/ELF/MapFile.h +++ b/gnu/llvm/lld/ELF/MapFile.h @@ -9,12 +9,8 @@ #ifndef LLD_ELF_MAPFILE_H #define LLD_ELF_MAPFILE_H -namespace lld { -namespace elf { -void writeMapFile(); -void writeCrossReferenceTable(); -void writeArchiveStats(); -} // namespace elf -} // namespace lld +namespace lld::elf { +void writeMapAndCref(); +} #endif diff --git a/gnu/llvm/lld/ELF/MarkLive.cpp b/gnu/llvm/lld/ELF/MarkLive.cpp index e828429b421..f6665e339d1 100644 --- a/gnu/llvm/lld/ELF/MarkLive.cpp +++ b/gnu/llvm/lld/ELF/MarkLive.cpp @@ -20,19 +20,18 @@ //===----------------------------------------------------------------------===// #include "MarkLive.h" +#include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" -#include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELF.h" #include "llvm/Support/TimeProfiler.h" -#include #include using namespace llvm; @@ -68,15 +67,15 @@ private: SmallVector queue; // There are normally few input sections whose names are valid C - // identifiers, so we just store a std::vector instead of a multimap. - DenseMap> cNamedSections; + // identifiers, so we just store a SmallVector instead of a multimap. + DenseMap> cNamedSections; }; } // namespace template static uint64_t getAddend(InputSectionBase &sec, const typename ELFT::Rel &rel) { - return target->getImplicitAddend(sec.data().begin() + rel.r_offset, + return target->getImplicitAddend(sec.content().begin() + rel.r_offset, rel.getType(config->isMips64EL)); } @@ -120,7 +119,7 @@ void MarkLive::resolveReloc(InputSectionBase &sec, RelTy &rel, if (auto *ss = dyn_cast(&sym)) if (!ss->isWeak()) - ss->getFile().isNeeded = true; + cast(ss->file)->isNeeded = true; for (InputSectionBase *sec : cNamedSections.lookup(sym.getName())) enqueue(sec, 0); @@ -144,20 +143,14 @@ template template void MarkLive::scanEhFrameSection(EhInputSection &eh, ArrayRef rels) { - for (size_t i = 0, end = eh.pieces.size(); i < end; ++i) { - EhSectionPiece &piece = eh.pieces[i]; - size_t firstRelI = piece.firstRelocation; + for (const EhSectionPiece &cie : eh.cies) + if (cie.firstRelocation != unsigned(-1)) + resolveReloc(eh, rels[cie.firstRelocation], false); + for (const EhSectionPiece &fde : eh.fdes) { + size_t firstRelI = fde.firstRelocation; if (firstRelI == (unsigned)-1) continue; - - if (read32(piece.data().data() + 4) == 0) { - // This is a CIE, we only need to worry about the first relocation. It is - // known to point to the personality function. - resolveReloc(eh, rels[firstRelI], false); - continue; - } - - uint64_t pieceEnd = piece.inputOff + piece.size; + uint64_t pieceEnd = fde.inputOff + fde.size; for (size_t j = firstRelI, end2 = rels.size(); j < end2 && rels[j].r_offset < pieceEnd; ++j) resolveReloc(eh, rels[j], true); @@ -177,27 +170,22 @@ static bool isReserved(InputSectionBase *sec) { // SHT_NOTE sections in a group are subject to garbage collection. return !sec->nextInSectionGroup; default: + // Support SHT_PROGBITS .init_array (https://golang.org/issue/50295) and + // .init_array.N (https://github.com/rust-lang/rust/issues/92181) for a + // while. StringRef s = sec->name; - return s.startswith(".ctors") || s.startswith(".dtors") || - s.startswith(".init") || s.startswith(".fini") || - s.startswith(".jcr"); + return s == ".init" || s == ".fini" || s.startswith(".init_array") || + s == ".jcr" || s.startswith(".ctors") || s.startswith(".dtors"); } } template void MarkLive::enqueue(InputSectionBase *sec, uint64_t offset) { - // Skip over discarded sections. This in theory shouldn't happen, because - // the ELF spec doesn't allow a relocation to point to a deduplicated - // COMDAT section directly. Unfortunately this happens in practice (e.g. - // .eh_frame) so we need to add a check. - if (sec == &InputSection::discarded) - return; - // Usually, a whole section is marked as live or dead, but in mergeable // (splittable) sections, each piece of data has independent liveness bit. // So we explicitly tell it which offset is in use. if (auto *ms = dyn_cast(sec)) - ms->getSectionPiece(offset)->live = true; + ms->getSectionPiece(offset).live = true; // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition @@ -225,7 +213,7 @@ template void MarkLive::run() { // Preserve externally-visible symbols if the symbols defined by this // file can interrupt other ELF file's symbols at runtime. - for (Symbol *sym : symtab->symbols()) + for (Symbol *sym : symtab.getSymbols()) if (sym->includeInDynsym() && sym->partition == partition) markSymbol(sym); @@ -235,32 +223,26 @@ template void MarkLive::run() { return; } - markSymbol(symtab->find(config->entry)); - markSymbol(symtab->find(config->init)); - markSymbol(symtab->find(config->fini)); + markSymbol(symtab.find(config->entry)); + markSymbol(symtab.find(config->init)); + markSymbol(symtab.find(config->fini)); for (StringRef s : config->undefined) - markSymbol(symtab->find(s)); + markSymbol(symtab.find(s)); for (StringRef s : script->referencedSymbols) - markSymbol(symtab->find(s)); - - // Preserve special sections and those which are specified in linker - // script KEEP command. - for (InputSectionBase *sec : inputSections) { - // Mark .eh_frame sections as live because there are usually no relocations - // that point to .eh_frames. Otherwise, the garbage collector would drop - // all of them. We also want to preserve personality routines and LSDA - // referenced by .eh_frame sections, so we scan them for that here. - if (auto *eh = dyn_cast(sec)) { - eh->markLive(); - if (!eh->numRelocations) - continue; - - if (eh->areRelocsRela) - scanEhFrameSection(*eh, eh->template relas()); - else - scanEhFrameSection(*eh, eh->template rels()); - } - + markSymbol(symtab.find(s)); + + // Mark .eh_frame sections as live because there are usually no relocations + // that point to .eh_frames. Otherwise, the garbage collector would drop + // all of them. We also want to preserve personality routines and LSDA + // referenced by .eh_frame sections, so we scan them for that here. + for (EhInputSection *eh : ctx.ehInputSections) { + const RelsOrRelas rels = eh->template relsOrRelas(); + if (rels.areRelocsRel()) + scanEhFrameSection(*eh, rels.rels); + else if (rels.relas.size()) + scanEhFrameSection(*eh, rels.relas); + } + for (InputSectionBase *sec : ctx.inputSections) { if (sec->flags & SHF_GNU_RETAIN) { enqueue(sec, 0); continue; @@ -268,6 +250,39 @@ template void MarkLive::run() { if (sec->flags & SHF_LINK_ORDER) continue; + // Usually, non-SHF_ALLOC sections are not removed even if they are + // unreachable through relocations because reachability is not a good signal + // whether they are garbage or not (e.g. there is usually no section + // referring to a .comment section, but we want to keep it.) When a + // non-SHF_ALLOC section is retained, we also retain sections dependent on + // it. + // + // Note on SHF_LINK_ORDER: Such sections contain metadata and they + // have a reverse dependency on the InputSection they are linked with. + // We are able to garbage collect them. + // + // Note on SHF_REL{,A}: Such sections reach here only when -r + // or --emit-reloc were given. And they are subject of garbage + // collection because, if we remove a text section, we also + // remove its relocation section. + // + // Note on nextInSectionGroup: The ELF spec says that group sections are + // included or omitted as a unit. We take the interpretation that: + // + // - Group members (nextInSectionGroup != nullptr) are subject to garbage + // collection. + // - Groups members are retained or discarded as a unit. + if (!(sec->flags & SHF_ALLOC)) { + bool isRel = sec->type == SHT_REL || sec->type == SHT_RELA; + if (!isRel && !sec->nextInSectionGroup) { + sec->markLive(); + for (InputSection *isec : sec->dependentSections) + isec->markLive(); + } + } + + // Preserve special sections and those which are specified in linker + // script KEEP command. if (isReserved(sec) || script->shouldKeep(sec)) { enqueue(sec, 0); } else if ((!config->zStartStopGC || sec->name.startswith("__libc_")) && @@ -275,8 +290,8 @@ template void MarkLive::run() { // As a workaround for glibc libc.a before 2.34 // (https://sourceware.org/PR27492), retain __libc_atexit and similar // sections regardless of zStartStopGC. - cNamedSections[saver.save("__start_" + sec->name)].push_back(sec); - cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec); + cNamedSections[saver().save("__start_" + sec->name)].push_back(sec); + cNamedSections[saver().save("__stop_" + sec->name)].push_back(sec); } } @@ -288,13 +303,11 @@ template void MarkLive::mark() { while (!queue.empty()) { InputSectionBase &sec = *queue.pop_back_val(); - if (sec.areRelocsRela) { - for (const typename ELFT::Rela &rel : sec.template relas()) - resolveReloc(sec, rel, false); - } else { - for (const typename ELFT::Rel &rel : sec.template rels()) - resolveReloc(sec, rel, false); - } + const RelsOrRelas rels = sec.template relsOrRelas(); + for (const typename ELFT::Rel &rel : rels.rels) + resolveReloc(sec, rel, false); + for (const typename ELFT::Rela &rel : rels.relas) + resolveReloc(sec, rel, false); for (InputSectionBase *isec : sec.dependentSections) enqueue(isec, 0); @@ -315,18 +328,18 @@ template void MarkLive::mark() { // to from __start_/__stop_ symbols because there will only be one set of // symbols for the whole program. template void MarkLive::moveToMain() { - for (InputFile *file : objectFiles) + for (ELFFileBase *file : ctx.objectFiles) for (Symbol *s : file->getSymbols()) if (auto *d = dyn_cast(s)) if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section && d->section->isLive()) markSymbol(s); - for (InputSectionBase *sec : inputSections) { + for (InputSectionBase *sec : ctx.inputSections) { if (!sec->isLive() || !isValidCIdentifier(sec->name)) continue; - if (symtab->find(("__start_" + sec->name).str()) || - symtab->find(("__stop_" + sec->name).str())) + if (symtab.find(("__start_" + sec->name).str()) || + symtab.find(("__stop_" + sec->name).str())) enqueue(sec, 0); } @@ -338,58 +351,18 @@ template void MarkLive::moveToMain() { // so that they are emitted to the output file. template void elf::markLive() { llvm::TimeTraceScope timeScope("markLive"); - // If -gc-sections is not given, no sections are removed. + // If --gc-sections is not given, retain all input sections. if (!config->gcSections) { - for (InputSectionBase *sec : inputSections) - sec->markLive(); - // If a DSO defines a symbol referenced in a regular object, it is needed. - for (Symbol *sym : symtab->symbols()) + for (Symbol *sym : symtab.getSymbols()) if (auto *s = dyn_cast(sym)) if (s->isUsedInRegularObj && !s->isWeak()) - s->getFile().isNeeded = true; + cast(s->file)->isNeeded = true; return; } - // Otherwise, do mark-sweep GC. - // - // The -gc-sections option works only for SHF_ALLOC sections (sections that - // are memory-mapped at runtime). So we can unconditionally make non-SHF_ALLOC - // sections alive except SHF_LINK_ORDER, SHT_REL/SHT_RELA sections, and - // sections in a group. - // - // Usually, non-SHF_ALLOC sections are not removed even if they are - // unreachable through relocations because reachability is not a good signal - // whether they are garbage or not (e.g. there is usually no section referring - // to a .comment section, but we want to keep it.) When a non-SHF_ALLOC - // section is retained, we also retain sections dependent on it. - // - // Note on SHF_LINK_ORDER: Such sections contain metadata and they - // have a reverse dependency on the InputSection they are linked with. - // We are able to garbage collect them. - // - // Note on SHF_REL{,A}: Such sections reach here only when -r - // or -emit-reloc were given. And they are subject of garbage - // collection because, if we remove a text section, we also - // remove its relocation section. - // - // Note on nextInSectionGroup: The ELF spec says that group sections are - // included or omitted as a unit. We take the interpretation that: - // - // - Group members (nextInSectionGroup != nullptr) are subject to garbage - // collection. - // - Groups members are retained or discarded as a unit. - for (InputSectionBase *sec : inputSections) { - bool isAlloc = (sec->flags & SHF_ALLOC); - bool isLinkOrder = (sec->flags & SHF_LINK_ORDER); - bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA); - - if (!isAlloc && !isLinkOrder && !isRel && !sec->nextInSectionGroup) { - sec->markLive(); - for (InputSection *isec : sec->dependentSections) - isec->markLive(); - } - } + for (InputSectionBase *sec : ctx.inputSections) + sec->markDead(); // Follow the graph to mark all live sections. for (unsigned curPart = 1; curPart <= partitions.size(); ++curPart) @@ -403,7 +376,7 @@ template void elf::markLive() { // Report garbage-collected sections. if (config->printGcSections) - for (InputSectionBase *sec : inputSections) + for (InputSectionBase *sec : ctx.inputSections) if (!sec->isLive()) message("removing unused section " + toString(sec)); } diff --git a/gnu/llvm/lld/ELF/MarkLive.h b/gnu/llvm/lld/ELF/MarkLive.h index 63b5b266914..ef62fdf964e 100644 --- a/gnu/llvm/lld/ELF/MarkLive.h +++ b/gnu/llvm/lld/ELF/MarkLive.h @@ -9,12 +9,10 @@ #ifndef LLD_ELF_MARKLIVE_H #define LLD_ELF_MARKLIVE_H -namespace lld { -namespace elf { +namespace lld::elf { template void markLive(); -} // namespace elf -} // namespace lld +} #endif // LLD_ELF_MARKLIVE_H diff --git a/gnu/llvm/lld/ELF/OutputSections.cpp b/gnu/llvm/lld/ELF/OutputSections.cpp index 088d1cdc65e..dc40f07ca6a 100644 --- a/gnu/llvm/lld/ELF/OutputSections.cpp +++ b/gnu/llvm/lld/ELF/OutputSections.cpp @@ -8,21 +8,25 @@ #include "OutputSections.h" #include "Config.h" +#include "InputFiles.h" #include "LinkerScript.h" -#include "SymbolTable.h" +#include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "lld/Common/Arrays.h" #include "lld/Common/Memory.h" -#include "lld/Common/Strings.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Config/llvm-config.h" // LLVM_ENABLE_ZLIB #include "llvm/Support/Compression.h" -#include "llvm/Support/MD5.h" -#include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" -#include "llvm/Support/SHA1.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TimeProfiler.h" -#include -#include +#if LLVM_ENABLE_ZLIB +#include +#endif +#if LLVM_ENABLE_ZSTD +#include +#endif using namespace llvm; using namespace llvm::dwarf; @@ -33,7 +37,6 @@ using namespace lld; using namespace lld::elf; uint8_t *Out::bufferStart; -uint8_t Out::first; PhdrEntry *Out::tlsPhdr; OutputSection *Out::elfHeader; OutputSection *Out::programHeaders; @@ -41,7 +44,7 @@ OutputSection *Out::preinitArray; OutputSection *Out::initArray; OutputSection *Out::finiArray; -std::vector elf::outputSections; +SmallVector elf::outputSections; uint32_t OutputSection::getPhdrFlags() const { uint32_t ret = 0; @@ -57,7 +60,7 @@ uint32_t OutputSection::getPhdrFlags() const { template void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) { shdr->sh_entsize = entsize; - shdr->sh_addralign = alignment; + shdr->sh_addralign = addralign; shdr->sh_type = type; shdr->sh_offset = offset; shdr->sh_flags = flags; @@ -69,8 +72,7 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) { } OutputSection::OutputSection(StringRef name, uint32_t type, uint64_t flags) - : BaseCommand(OutputSectionKind), - SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type, + : SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type, /*Info*/ 0, /*Link*/ 0) {} // We allow sections of types listed below to merged into a @@ -100,10 +102,9 @@ static bool canMergeToProgbits(unsigned type) { void OutputSection::recordSection(InputSectionBase *isec) { partition = isec->partition; isec->parent = this; - if (sectionCommands.empty() || - !isa(sectionCommands.back())) - sectionCommands.push_back(make("")); - auto *isd = cast(sectionCommands.back()); + if (commands.empty() || !isa(commands.back())) + commands.push_back(make("")); + auto *isd = cast(commands.back()); isd->sectionBases.push_back(isec); } @@ -111,32 +112,40 @@ void OutputSection::recordSection(InputSectionBase *isec) { // isec. Also check whether the InputSection flags and type are consistent with // other InputSections. void OutputSection::commitSection(InputSection *isec) { + if (LLVM_UNLIKELY(type != isec->type)) { + if (hasInputSections || typeIsSet) { + if (typeIsSet || !canMergeToProgbits(type) || + !canMergeToProgbits(isec->type)) { + // Changing the type of a (NOLOAD) section is fishy, but some projects + // (e.g. https://github.com/ClangBuiltLinux/linux/issues/1597) + // traditionally rely on the behavior. Issue a warning to not break + // them. Other types get an error. + auto diagnose = type == SHT_NOBITS ? warn : errorOrWarn; + diagnose("section type mismatch for " + isec->name + "\n>>> " + + toString(isec) + ": " + + getELFSectionTypeName(config->emachine, isec->type) + + "\n>>> output section " + name + ": " + + getELFSectionTypeName(config->emachine, type)); + } + if (!typeIsSet) + type = SHT_PROGBITS; + } else { + type = isec->type; + } + } if (!hasInputSections) { // If IS is the first section to be added to this section, // initialize type, entsize and flags from isec. hasInputSections = true; - type = isec->type; entsize = isec->entsize; flags = isec->flags; } else { // Otherwise, check if new type or flags are compatible with existing ones. if ((flags ^ isec->flags) & SHF_TLS) - error("incompatible section flags for " + name + "\n>>> " + toString(isec) + - ": 0x" + utohexstr(isec->flags) + "\n>>> output section " + name + - ": 0x" + utohexstr(flags)); - - if (type != isec->type) { - if (!canMergeToProgbits(type) || !canMergeToProgbits(isec->type)) - error("section type mismatch for " + isec->name + "\n>>> " + - toString(isec) + ": " + - getELFSectionTypeName(config->emachine, isec->type) + - "\n>>> output section " + name + ": " + - getELFSectionTypeName(config->emachine, type)); - type = SHT_PROGBITS; - } + error("incompatible section flags for " + name + "\n>>> " + + toString(isec) + ": 0x" + utohexstr(isec->flags) + + "\n>>> output section " + name + ": 0x" + utohexstr(flags)); } - if (noload) - type = SHT_NOBITS; isec->parent = this; uint64_t andMask = @@ -148,7 +157,7 @@ void OutputSection::commitSection(InputSection *isec) { if (nonAlloc) flags &= ~(uint64_t)SHF_ALLOC; - alignment = std::max(alignment, isec->alignment); + addralign = std::max(addralign, isec->addralign); // If this section contains a table of fixed-size entries, sh_entsize // holds the element size. If it contains elements of different size we @@ -157,6 +166,15 @@ void OutputSection::commitSection(InputSection *isec) { entsize = 0; } +static MergeSyntheticSection *createMergeSynthetic(StringRef name, + uint32_t type, + uint64_t flags, + uint32_t addralign) { + if ((flags & SHF_STRINGS) && config->optimize >= 2) + return make(name, type, flags, addralign); + return make(name, type, flags, addralign); +} + // This function scans over the InputSectionBase list sectionBases to create // InputSectionDescription::sections. // @@ -166,15 +184,15 @@ void OutputSection::commitSection(InputSection *isec) { // to compute an output offset for each piece of each input section. void OutputSection::finalizeInputSections() { std::vector mergeSections; - for (BaseCommand *base : sectionCommands) { - auto *cmd = dyn_cast(base); - if (!cmd) + for (SectionCommand *cmd : commands) { + auto *isd = dyn_cast(cmd); + if (!isd) continue; - cmd->sections.reserve(cmd->sectionBases.size()); - for (InputSectionBase *s : cmd->sectionBases) { + isd->sections.reserve(isd->sectionBases.size()); + for (InputSectionBase *s : isd->sectionBases) { MergeInputSection *ms = dyn_cast(s); if (!ms) { - cmd->sections.push_back(cast(s)); + isd->sections.push_back(cast(s)); continue; } @@ -195,25 +213,25 @@ void OutputSection::finalizeInputSections() { // // SHF_STRINGS section with different alignments should not be merged. return sec->flags == ms->flags && sec->entsize == ms->entsize && - (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS)); + (sec->addralign == ms->addralign || !(sec->flags & SHF_STRINGS)); }); if (i == mergeSections.end()) { MergeSyntheticSection *syn = - createMergeSynthetic(name, ms->type, ms->flags, ms->alignment); + createMergeSynthetic(name, ms->type, ms->flags, ms->addralign); mergeSections.push_back(syn); i = std::prev(mergeSections.end()); syn->entsize = ms->entsize; - cmd->sections.push_back(syn); + isd->sections.push_back(syn); } (*i)->addSection(ms); } // sectionBases should not be used from this point onwards. Clear it to // catch misuses. - cmd->sectionBases.clear(); + isd->sectionBases.clear(); // Some input sections may be removed from the list after ICF. - for (InputSection *s : cmd->sections) + for (InputSection *s : isd->sections) commitSection(s); } for (auto *ms : mergeSections) @@ -237,13 +255,9 @@ uint64_t elf::getHeaderSize() { return Out::elfHeader->size + Out::programHeaders->size; } -bool OutputSection::classof(const BaseCommand *c) { - return c->kind == OutputSectionKind; -} - void OutputSection::sort(llvm::function_ref order) { assert(isLive()); - for (BaseCommand *b : sectionCommands) + for (SectionCommand *b : commands) if (auto *isd = dyn_cast(b)) sortByOrder(isd->sections, order); } @@ -277,38 +291,140 @@ static void fill(uint8_t *buf, size_t size, memcpy(buf + i, filler.data(), size - i); } +#if LLVM_ENABLE_ZLIB +static SmallVector deflateShard(ArrayRef in, int level, + int flush) { + // 15 and 8 are default. windowBits=-15 is negative to generate raw deflate + // data with no zlib header or trailer. + z_stream s = {}; + deflateInit2(&s, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); + s.next_in = const_cast(in.data()); + s.avail_in = in.size(); + + // Allocate a buffer of half of the input size, and grow it by 1.5x if + // insufficient. + SmallVector out; + size_t pos = 0; + out.resize_for_overwrite(std::max(in.size() / 2, 64)); + do { + if (pos == out.size()) + out.resize_for_overwrite(out.size() * 3 / 2); + s.next_out = out.data() + pos; + s.avail_out = out.size() - pos; + (void)deflate(&s, flush); + pos = s.next_out - out.data(); + } while (s.avail_out == 0); + assert(s.avail_in == 0); + + out.truncate(pos); + deflateEnd(&s); + return out; +} +#endif + // Compress section contents if this section contains debug info. template void OutputSection::maybeCompress() { using Elf_Chdr = typename ELFT::Chdr; + (void)sizeof(Elf_Chdr); // Compress only DWARF debug sections. - if (!config->compressDebugSections || (flags & SHF_ALLOC) || - !name.startswith(".debug_")) + if (config->compressDebugSections == DebugCompressionType::None || + (flags & SHF_ALLOC) || !name.startswith(".debug_") || size == 0) return; llvm::TimeTraceScope timeScope("Compress debug sections"); + compressed.uncompressedSize = size; + auto buf = std::make_unique(size); + // Write uncompressed data to a temporary zero-initialized buffer. + { + parallel::TaskGroup tg; + writeTo(buf.get(), tg); + } - // Create a section header. - zDebugHeader.resize(sizeof(Elf_Chdr)); - auto *hdr = reinterpret_cast(zDebugHeader.data()); - hdr->ch_type = ELFCOMPRESS_ZLIB; - hdr->ch_size = size; - hdr->ch_addralign = alignment; - - // Write section contents to a temporary buffer and compress it. - std::vector buf(size); - writeTo(buf.data()); - // We chose 1 as the default compression level because it is the fastest. If - // -O2 is given, we use level 6 to compress debug info more by ~15%. We found - // that level 7 to 9 doesn't make much difference (~1% more compression) while - // they take significant amount of time (~2x), so level 6 seems enough. - if (Error e = zlib::compress(toStringRef(buf), compressedData, - config->optimize >= 2 ? 6 : 1)) - fatal("compress failed: " + llvm::toString(std::move(e))); - - // Update section headers. - size = sizeof(Elf_Chdr) + compressedData.size(); +#if LLVM_ENABLE_ZSTD + // Use ZSTD's streaming compression API which permits parallel workers working + // on the stream. See http://facebook.github.io/zstd/zstd_manual.html + // "Streaming compression - HowTo". + if (config->compressDebugSections == DebugCompressionType::Zstd) { + // Allocate a buffer of half of the input size, and grow it by 1.5x if + // insufficient. + compressed.shards = std::make_unique[]>(1); + SmallVector &out = compressed.shards[0]; + out.resize_for_overwrite(std::max(size / 2, 32)); + size_t pos = 0; + + ZSTD_CCtx *cctx = ZSTD_createCCtx(); + // Ignore error if zstd was not built with ZSTD_MULTITHREAD. + (void)ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, + parallel::strategy.compute_thread_count()); + ZSTD_outBuffer zob = {out.data(), out.size(), 0}; + ZSTD_EndDirective directive = ZSTD_e_continue; + const size_t blockSize = ZSTD_CStreamInSize(); + do { + const size_t n = std::min(static_cast(size - pos), blockSize); + if (n == size - pos) + directive = ZSTD_e_end; + ZSTD_inBuffer zib = {buf.get() + pos, n, 0}; + size_t bytesRemaining = 0; + while (zib.pos != zib.size || + (directive == ZSTD_e_end && bytesRemaining != 0)) { + if (zob.pos == zob.size) { + out.resize_for_overwrite(out.size() * 3 / 2); + zob.dst = out.data(); + zob.size = out.size(); + } + bytesRemaining = ZSTD_compressStream2(cctx, &zob, &zib, directive); + assert(!ZSTD_isError(bytesRemaining)); + } + pos += n; + } while (directive != ZSTD_e_end); + out.resize(zob.pos); + ZSTD_freeCCtx(cctx); + + size = sizeof(Elf_Chdr) + out.size(); + flags |= SHF_COMPRESSED; + return; + } +#endif + +#if LLVM_ENABLE_ZLIB + // We chose 1 (Z_BEST_SPEED) as the default compression level because it is + // the fastest. If -O2 is given, we use level 6 to compress debug info more by + // ~15%. We found that level 7 to 9 doesn't make much difference (~1% more + // compression) while they take significant amount of time (~2x), so level 6 + // seems enough. + const int level = config->optimize >= 2 ? 6 : Z_BEST_SPEED; + + // Split input into 1-MiB shards. + constexpr size_t shardSize = 1 << 20; + auto shardsIn = split(ArrayRef(buf.get(), size), shardSize); + const size_t numShards = shardsIn.size(); + + // Compress shards and compute Alder-32 checksums. Use Z_SYNC_FLUSH for all + // shards but the last to flush the output to a byte boundary to be + // concatenated with the next shard. + auto shardsOut = std::make_unique[]>(numShards); + auto shardsAdler = std::make_unique(numShards); + parallelFor(0, numShards, [&](size_t i) { + shardsOut[i] = deflateShard(shardsIn[i], level, + i != numShards - 1 ? Z_SYNC_FLUSH : Z_FINISH); + shardsAdler[i] = adler32(1, shardsIn[i].data(), shardsIn[i].size()); + }); + + // Update section size and combine Alder-32 checksums. + uint32_t checksum = 1; // Initial Adler-32 value + size = sizeof(Elf_Chdr) + 2; // Elf_Chdir and zlib header + for (size_t i = 0; i != numShards; ++i) { + size += shardsOut[i].size(); + checksum = adler32_combine(checksum, shardsAdler[i], shardsIn[i].size()); + } + size += 4; // checksum + + compressed.shards = std::move(shardsOut); + compressed.numShards = numShards; + compressed.checksum = checksum; flags |= SHF_COMPRESSED; +#endif } static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) { @@ -324,62 +440,117 @@ static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) { llvm_unreachable("unsupported Size argument"); } -template void OutputSection::writeTo(uint8_t *buf) { +template +void OutputSection::writeTo(uint8_t *buf, parallel::TaskGroup &tg) { + llvm::TimeTraceScope timeScope("Write sections", name); if (type == SHT_NOBITS) return; - // If -compress-debug-section is specified and if this is a debug section, + // If --compress-debug-section is specified and if this is a debug section, // we've already compressed section contents. If that's the case, // just write it down. - if (!compressedData.empty()) { - memcpy(buf, zDebugHeader.data(), zDebugHeader.size()); - memcpy(buf + zDebugHeader.size(), compressedData.data(), - compressedData.size()); + if (compressed.shards) { + auto *chdr = reinterpret_cast(buf); + chdr->ch_size = compressed.uncompressedSize; + chdr->ch_addralign = addralign; + buf += sizeof(*chdr); + if (config->compressDebugSections == DebugCompressionType::Zstd) { + chdr->ch_type = ELFCOMPRESS_ZSTD; + memcpy(buf, compressed.shards[0].data(), compressed.shards[0].size()); + return; + } + chdr->ch_type = ELFCOMPRESS_ZLIB; + + // Compute shard offsets. + auto offsets = std::make_unique(compressed.numShards); + offsets[0] = 2; // zlib header + for (size_t i = 1; i != compressed.numShards; ++i) + offsets[i] = offsets[i - 1] + compressed.shards[i - 1].size(); + + buf[0] = 0x78; // CMF + buf[1] = 0x01; // FLG: best speed + parallelFor(0, compressed.numShards, [&](size_t i) { + memcpy(buf + offsets[i], compressed.shards[i].data(), + compressed.shards[i].size()); + }); + + write32be(buf + (size - sizeof(*chdr) - 4), compressed.checksum); return; } // Write leading padding. - std::vector sections = getInputSections(this); + ArrayRef sections = getInputSections(*this, storage); std::array filler = getFiller(); bool nonZeroFiller = read32(filler.data()) != 0; if (nonZeroFiller) fill(buf, sections.empty() ? size : sections[0]->outSecOff, filler); - parallelForEachN(0, sections.size(), [&](size_t i) { - InputSection *isec = sections[i]; - isec->writeTo(buf); - - // Fill gaps between sections. - if (nonZeroFiller) { - uint8_t *start = buf + isec->outSecOff + isec->getSize(); - uint8_t *end; - if (i + 1 == sections.size()) - end = buf + size; + auto fn = [=](size_t begin, size_t end) { + size_t numSections = sections.size(); + for (size_t i = begin; i != end; ++i) { + InputSection *isec = sections[i]; + if (auto *s = dyn_cast(isec)) + s->writeTo(buf + isec->outSecOff); else - end = buf + sections[i + 1]->outSecOff; - if (isec->nopFiller) { - assert(target->nopInstrs); - nopInstrFill(start, end - start); - } else - fill(start, end - start, filler); + isec->writeTo(buf + isec->outSecOff); + + // Fill gaps between sections. + if (nonZeroFiller) { + uint8_t *start = buf + isec->outSecOff + isec->getSize(); + uint8_t *end; + if (i + 1 == numSections) + end = buf + size; + else + end = buf + sections[i + 1]->outSecOff; + if (isec->nopFiller) { + assert(target->nopInstrs); + nopInstrFill(start, end - start); + } else + fill(start, end - start, filler); + } } - }); - - // Linker scripts may have BYTE()-family commands with which you - // can write arbitrary bytes to the output. Process them if any. - for (BaseCommand *base : sectionCommands) - if (auto *data = dyn_cast(base)) + }; + + // If there is any BYTE()-family command (rare), write the section content + // first then process BYTE to overwrite the filler content. The write is + // serial due to the limitation of llvm/Support/Parallel.h. + bool written = false; + size_t numSections = sections.size(); + for (SectionCommand *cmd : commands) + if (auto *data = dyn_cast(cmd)) { + if (!std::exchange(written, true)) + fn(0, numSections); writeInt(buf + data->offset, data->expression().getValue(), data->size); -} + } + if (written || !numSections) + return; -static void finalizeShtGroup(OutputSection *os, - InputSection *section) { - assert(config->relocatable); + // There is no data command. Write content asynchronously to overlap the write + // time with other output sections. Note, if a linker script specifies + // overlapping output sections (needs --noinhibit-exec or --no-check-sections + // to supress the error), the output may be non-deterministic. + const size_t taskSizeLimit = 4 << 20; + for (size_t begin = 0, i = 0, taskSize = 0;;) { + taskSize += sections[i]->getSize(); + bool done = ++i == numSections; + if (done || taskSize >= taskSizeLimit) { + tg.execute([=] { fn(begin, i); }); + if (done) + break; + begin = i; + taskSize = 0; + } + } +} +static void finalizeShtGroup(OutputSection *os, InputSection *section) { // sh_link field for SHT_GROUP sections should contain the section index of // the symbol table. os->link = in.symTab->getParent()->sectionIndex; + if (!section) + return; + // sh_info then contain index of an entry in symbol table section which // provides signature of the section group. ArrayRef symbols = section->file->getSymbols(); @@ -387,7 +558,7 @@ static void finalizeShtGroup(OutputSection *os, // Some group members may be combined or discarded, so we need to compute the // new size. The content will be rewritten in InputSection::copyShtGroup. - std::unordered_set seen; + DenseSet seen; ArrayRef sections = section->file->getSections(); for (const uint32_t &idx : section->getDataAs().slice(1)) if (OutputSection *osec = sections[read32(&idx)]->getOutputSection()) @@ -437,18 +608,15 @@ void OutputSection::finalize() { // crtbegin files. // // Gcc uses any of crtbegin[|S|T].o. -// Clang uses Gcc's plus clang_rt.crtbegin[|S|T][-|].o. - -static bool isCrtbegin(StringRef s) { - static std::regex re(R"((clang_rt\.)?crtbegin[ST]?(-.*)?\.o)"); - s = sys::path::filename(s); - return std::regex_match(s.begin(), s.end(), re); -} +// Clang uses Gcc's plus clang_rt.crtbegin[-|].o. -static bool isCrtend(StringRef s) { - static std::regex re(R"((clang_rt\.)?crtend[ST]?(-.*)?\.o)"); +static bool isCrt(StringRef s, StringRef beginEnd) { s = sys::path::filename(s); - return std::regex_match(s.begin(), s.end(), re); + if (!s.consume_back(".o")) + return false; + if (s.consume_front("clang_rt.")) + return s.consume_front(beginEnd); + return s.consume_front(beginEnd) && s.size() <= 1; } // .ctors and .dtors are sorted by this order: @@ -470,12 +638,12 @@ static bool isCrtend(StringRef s) { // are too many real-world use cases of .ctors, so we had no choice to // support that with this rather ad-hoc semantics. static bool compCtors(const InputSection *a, const InputSection *b) { - bool beginA = isCrtbegin(a->file->getName()); - bool beginB = isCrtbegin(b->file->getName()); + bool beginA = isCrt(a->file->getName(), "crtbegin"); + bool beginB = isCrt(b->file->getName(), "crtbegin"); if (beginA != beginB) return beginA; - bool endA = isCrtend(a->file->getName()); - bool endB = isCrtend(b->file->getName()); + bool endA = isCrt(a->file->getName(), "crtend"); + bool endB = isCrt(b->file->getName(), "crtend"); if (endA != endB) return endB; return getPriority(a->name) > getPriority(b->name); @@ -485,8 +653,8 @@ static bool compCtors(const InputSection *a, const InputSection *b) { // Unfortunately, the rules are different from the one for .{init,fini}_array. // Read the comment above. void OutputSection::sortCtorsDtors() { - assert(sectionCommands.size() == 1); - auto *isd = cast(sectionCommands[0]); + assert(commands.size() == 1); + auto *isd = cast(commands[0]); llvm::stable_sort(isd->sections, compCtors); } @@ -505,19 +673,31 @@ int elf::getPriority(StringRef s) { } InputSection *elf::getFirstInputSection(const OutputSection *os) { - for (BaseCommand *base : os->sectionCommands) - if (auto *isd = dyn_cast(base)) + for (SectionCommand *cmd : os->commands) + if (auto *isd = dyn_cast(cmd)) if (!isd->sections.empty()) return isd->sections[0]; return nullptr; } -std::vector elf::getInputSections(const OutputSection *os) { - std::vector ret; - for (BaseCommand *base : os->sectionCommands) - if (auto *isd = dyn_cast(base)) - ret.insert(ret.end(), isd->sections.begin(), isd->sections.end()); - return ret; +ArrayRef +elf::getInputSections(const OutputSection &os, + SmallVector &storage) { + ArrayRef ret; + storage.clear(); + for (SectionCommand *cmd : os.commands) { + auto *isd = dyn_cast(cmd); + if (!isd) + continue; + if (ret.empty()) { + ret = isd->sections; + } else { + if (storage.empty()) + storage.assign(ret.begin(), ret.end()); + storage.insert(storage.end(), isd->sections.begin(), isd->sections.end()); + } + } + return storage.empty() ? ret : ArrayRef(storage); } // Sorts input sections by section name suffixes, so that .foo.N comes @@ -542,8 +722,9 @@ std::array OutputSection::getFiller() { void OutputSection::checkDynRelAddends(const uint8_t *bufStart) { assert(config->writeAddends && config->checkDynamicRelocs); assert(type == SHT_REL || type == SHT_RELA); - std::vector sections = getInputSections(this); - parallelForEachN(0, sections.size(), [&](size_t i) { + SmallVector storage; + ArrayRef sections = getInputSections(*this, storage); + parallelFor(0, sections.size(), [&](size_t i) { // When linking with -r or --emit-relocs we might also call this function // for input .rel[a]. sections which we simply pass through to the // output. We skip over those and only look at the synthetic relocation @@ -552,7 +733,7 @@ void OutputSection::checkDynRelAddends(const uint8_t *bufStart) { if (!sec) return; for (const DynamicReloc &rel : sec->relocs) { - int64_t addend = rel.computeAddend(); + int64_t addend = rel.addend; const OutputSection *relOsec = rel.inputSec->getOutputSection(); assert(relOsec != nullptr && "missing output section for relocation"); const uint8_t *relocTarget = @@ -579,10 +760,14 @@ template void OutputSection::writeHeaderTo(ELF32BE::Shdr *Shdr); template void OutputSection::writeHeaderTo(ELF64LE::Shdr *Shdr); template void OutputSection::writeHeaderTo(ELF64BE::Shdr *Shdr); -template void OutputSection::writeTo(uint8_t *Buf); -template void OutputSection::writeTo(uint8_t *Buf); -template void OutputSection::writeTo(uint8_t *Buf); -template void OutputSection::writeTo(uint8_t *Buf); +template void OutputSection::writeTo(uint8_t *, + llvm::parallel::TaskGroup &); +template void OutputSection::writeTo(uint8_t *, + llvm::parallel::TaskGroup &); +template void OutputSection::writeTo(uint8_t *, + llvm::parallel::TaskGroup &); +template void OutputSection::writeTo(uint8_t *, + llvm::parallel::TaskGroup &); template void OutputSection::maybeCompress(); template void OutputSection::maybeCompress(); diff --git a/gnu/llvm/lld/ELF/OutputSections.h b/gnu/llvm/lld/ELF/OutputSections.h index a0f80661438..c7931471a6e 100644 --- a/gnu/llvm/lld/ELF/OutputSections.h +++ b/gnu/llvm/lld/ELF/OutputSections.h @@ -9,27 +9,30 @@ #ifndef LLD_ELF_OUTPUT_SECTIONS_H #define LLD_ELF_OUTPUT_SECTIONS_H -#include "Config.h" #include "InputSection.h" #include "LinkerScript.h" -#include "Relocations.h" #include "lld/Common/LLVM.h" -#include "llvm/MC/StringTableBuilder.h" -#include "llvm/Object/ELF.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Parallel.h" + #include -namespace lld { -namespace elf { +namespace lld::elf { struct PhdrEntry; -class InputSection; -class InputSectionBase; + +struct CompressedData { + std::unique_ptr[]> shards; + uint32_t numShards = 0; + uint32_t checksum = 0; + uint64_t uncompressedSize; +}; // This represents a section in an output file. // It is composed of multiple InputSections. // The writer creates multiple OutputSections and assign them unique, // non-overlapping file offsets and VAs. -class OutputSection final : public BaseCommand, public SectionBase { +class OutputSection final : public SectionBase { public: OutputSection(StringRef name, uint32_t type, uint64_t flags); @@ -37,8 +40,6 @@ public: return s->kind() == SectionBase::Output; } - static bool classof(const BaseCommand *c); - uint64_t getLMA() const { return ptLoad ? addr + ptLoad->lmaOffset : addr; } template void writeHeaderTo(typename ELFT::Shdr *sHdr); @@ -82,15 +83,15 @@ public: Expr alignExpr; Expr lmaExpr; Expr subalignExpr; - std::vector sectionCommands; - std::vector phdrs; - llvm::Optional> filler; + SmallVector commands; + SmallVector phdrs; + std::optional> filler; ConstraintKind constraint = ConstraintKind::NoConstraint; std::string location; std::string memoryRegionName; std::string lmaRegionName; bool nonAlloc = false; - bool noload = false; + bool typeIsSet = false; bool expressionsUseSymbols = false; bool usedInExpression = false; bool inOverlay = false; @@ -100,8 +101,13 @@ public: // that wasn't needed). This is needed for orphan placement. bool hasInputSections = false; + // The output section description is specified between DATA_SEGMENT_ALIGN and + // DATA_RELRO_END. + bool relro = false; + void finalize(); - template void writeTo(uint8_t *buf); + template + void writeTo(uint8_t *buf, llvm::parallel::TaskGroup &tg); // Check that the addends for dynamic relocations were written correctly. void checkDynRelAddends(const uint8_t *bufStart); template void maybeCompress(); @@ -111,24 +117,36 @@ public: void sortCtorsDtors(); private: + SmallVector storage; + // Used for implementation of --compress-debug-sections option. - std::vector zDebugHeader; - llvm::SmallVector compressedData; + CompressedData compressed; std::array getFiller(); }; +struct OutputDesc final : SectionCommand { + OutputSection osec; + OutputDesc(StringRef name, uint32_t type, uint64_t flags) + : SectionCommand(OutputSectionKind), osec(name, type, flags) {} + + static bool classof(const SectionCommand *c) { + return c->kind == OutputSectionKind; + } +}; + int getPriority(StringRef s); InputSection *getFirstInputSection(const OutputSection *os); -std::vector getInputSections(const OutputSection *os); +llvm::ArrayRef +getInputSections(const OutputSection &os, + SmallVector &storage); // All output sections that are handled by the linker specially are // globally accessible. Writer initializes them, so don't use them // until Writer is initialized. struct Out { static uint8_t *bufferStart; - static uint8_t first; static PhdrEntry *tlsPhdr; static OutputSection *elfHeader; static OutputSection *programHeaders; @@ -139,8 +157,8 @@ struct Out { uint64_t getHeaderSize(); -extern std::vector outputSections; -} // namespace elf -} // namespace lld +LLVM_LIBRARY_VISIBILITY extern llvm::SmallVector + outputSections; +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/Relocations.h b/gnu/llvm/lld/ELF/Relocations.h index a702aac183a..29e3edeca6b 100644 --- a/gnu/llvm/lld/ELF/Relocations.h +++ b/gnu/llvm/lld/ELF/Relocations.h @@ -11,11 +11,10 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/DenseMap.h" -#include +#include "llvm/ADT/STLExtras.h" #include -namespace lld { -namespace elf { +namespace lld::elf { class Symbol; class InputSection; class InputSectionBase; @@ -45,6 +44,8 @@ enum RelExpr { R_PC, R_PLT, R_PLT_PC, + R_PLT_GOTPLT, + R_RELAX_HINT, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, R_RELAX_TLS_GD_TO_IE, @@ -62,6 +63,7 @@ enum RelExpr { R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC, + R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, @@ -115,17 +117,17 @@ struct Relocation { // jump instruction opcodes at basic block boundaries and are particularly // useful when basic block sections are enabled. struct JumpInstrMod { - JumpModType original; uint64_t offset; + JumpModType original; unsigned size; }; // This function writes undefined symbol diagnostics to an internal buffer. // Call reportUndefinedSymbols() after calling scanRelocations() to emit // the diagnostics. -template void scanRelocations(InputSectionBase &); - -template void reportUndefinedSymbols(); +template void scanRelocations(); +void reportUndefinedSymbols(); +void postScanRelocations(); void hexagonTLSSymbolUpdate(ArrayRef outputSections); bool hexagonNeedsTLSSymbol(ArrayRef outputSections); @@ -137,12 +139,7 @@ class InputSectionDescription; class ThunkCreator { public: // Return true if Thunks have been added to OutputSections - bool createThunks(ArrayRef outputSections); - - // The number of completed passes of createThunks this permits us - // to do one time initialization on Pass 0 and put a limit on the - // number of times it can be called to prevent infinite loops. - uint32_t pass = 0; + bool createThunks(uint32_t pass, ArrayRef outputSections); private: void mergeThunks(ArrayRef outputSections); @@ -184,6 +181,11 @@ private: // so we need to make sure that there is only one of them. // The Mips LA25 Thunk is an example of an inline ThunkSection. llvm::DenseMap thunkedSections; + + // The number of completed passes of createThunks this permits us + // to do one time initialization on Pass 0 and put a limit on the + // number of times it can be called to prevent infinite loops. + uint32_t pass = 0; }; // Return a int64_t to make sure we get the sign extension out of the way as @@ -209,7 +211,6 @@ ArrayRef sortRels(ArrayRef rels, SmallVector &storage) { } return rels; } -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/ScriptLexer.cpp b/gnu/llvm/lld/ELF/ScriptLexer.cpp index 236a188324c..e0a8ed7121a 100644 --- a/gnu/llvm/lld/ELF/ScriptLexer.cpp +++ b/gnu/llvm/lld/ELF/ScriptLexer.cpp @@ -34,6 +34,8 @@ #include "ScriptLexer.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include using namespace llvm; using namespace lld; @@ -132,10 +134,14 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) { continue; } - // ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>". - // "|", "||", "&" and "&&" are different operators. - if (s.startswith("<<") || s.startswith("<=") || s.startswith(">>") || - s.startswith(">=") || s.startswith("||") || s.startswith("&&")) { + // Some operators form separate tokens. + if (s.startswith("<<=") || s.startswith(">>=")) { + vec.push_back(s.substr(0, 3)); + s = s.substr(3); + continue; + } + if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) || + (s[0] == s[1] && strchr("<>&|", s[0])))) { vec.push_back(s.substr(0, 2)); s = s.substr(2); continue; @@ -190,7 +196,7 @@ bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; } // Split a given string as an expression. // This function returns "3", "*" and "5" for "3*5" for example. static std::vector tokenizeExpr(StringRef s) { - StringRef ops = "+-*/:!~=<>"; // List of operators + StringRef ops = "!~*/+-<>?:="; // List of operators // Quoted strings are literal strings, so we don't want to split it. if (s.startswith("\"")) diff --git a/gnu/llvm/lld/ELF/ScriptLexer.h b/gnu/llvm/lld/ELF/ScriptLexer.h index 405fc735cbe..17c0b529fa1 100644 --- a/gnu/llvm/lld/ELF/ScriptLexer.h +++ b/gnu/llvm/lld/ELF/ScriptLexer.h @@ -11,12 +11,10 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/MemoryBuffer.h" -#include +#include "llvm/Support/MemoryBufferRef.h" #include -namespace lld { -namespace elf { +namespace lld::elf { class ScriptLexer { public: @@ -53,7 +51,6 @@ private: size_t getColumnNumber(); }; -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/ScriptParser.h b/gnu/llvm/lld/ELF/ScriptParser.h index eed1958647f..34b27d2b478 100644 --- a/gnu/llvm/lld/ELF/ScriptParser.h +++ b/gnu/llvm/lld/ELF/ScriptParser.h @@ -10,10 +10,9 @@ #define LLD_ELF_SCRIPT_PARSER_H #include "lld/Common/LLVM.h" -#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/MemoryBufferRef.h" -namespace lld { -namespace elf { +namespace lld::elf { // Parses a linker script. Calling this function updates // lld::elf::config and lld::elf::script. @@ -29,7 +28,6 @@ void readDefsym(StringRef name, MemoryBufferRef mb); bool hasWildcard(StringRef s); -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/SymbolTable.h b/gnu/llvm/lld/ELF/SymbolTable.h index 54c4b1169ed..5255e8bfad6 100644 --- a/gnu/llvm/lld/ELF/SymbolTable.h +++ b/gnu/llvm/lld/ELF/SymbolTable.h @@ -9,15 +9,15 @@ #ifndef LLD_ELF_SYMBOL_TABLE_H #define LLD_ELF_SYMBOL_TABLE_H -#include "InputFiles.h" #include "Symbols.h" -#include "lld/Common/Strings.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" -namespace lld { -namespace elf { +namespace lld::elf { + +class InputFile; +class SharedFile; // SymbolTable is a bucket of all known symbols, including defined, // undefined, or lazy symbols (the last one is symbols in archive @@ -32,22 +32,19 @@ namespace elf { // add*() functions, which are called by input files as they are parsed. There // is one add* function per symbol type. class SymbolTable { - struct FilterOutPlaceholder { - bool operator()(Symbol *S) const { return !S->isPlaceholder(); } - }; - using iterator = llvm::filter_iterator::const_iterator, - FilterOutPlaceholder>; - public: - llvm::iterator_range symbols() const { - return llvm::make_filter_range(symVector, FilterOutPlaceholder()); - } + ArrayRef getSymbols() const { return symVector; } void wrap(Symbol *sym, Symbol *real, Symbol *wrap); Symbol *insert(StringRef name); - Symbol *addSymbol(const Symbol &newSym); + template Symbol *addSymbol(const T &newSym) { + Symbol *sym = insert(newSym.getName()); + sym->resolve(newSym); + return sym; + } + Symbol *addAndCheckDuplicate(const Defined &newSym); void scanVersionScript(); @@ -56,7 +53,7 @@ public: void handleDynamicList(); // Set of .so files to not link the same shared object file more than once. - llvm::DenseMap soNames; + llvm::DenseMap soNames; // Comdat groups define "link once" sections. If two comdat groups have the // same name, only one of them is linked, and the other is ignored. This map @@ -64,11 +61,11 @@ public: llvm::DenseMap comdatGroups; private: - std::vector findByVersion(SymbolVersion ver); - std::vector findAllByVersion(SymbolVersion ver, - bool includeNonDefault); + SmallVector findByVersion(SymbolVersion ver); + SmallVector findAllByVersion(SymbolVersion ver, + bool includeNonDefault); - llvm::StringMap> &getDemangledSyms(); + llvm::StringMap> &getDemangledSyms(); bool assignExactVersion(SymbolVersion ver, uint16_t versionId, StringRef versionName, bool includeNonDefault); void assignWildcardVersion(SymbolVersion ver, uint16_t versionId, @@ -82,18 +79,17 @@ private: // FIXME: Experiment with passing in a custom hashing or sorting the symbols // once symbol resolution is finished. llvm::DenseMap symMap; - std::vector symVector; + SmallVector symVector; // A map from demangled symbol names to their symbol objects. // This mapping is 1:N because two symbols with different versions // can have the same name. We use this map to handle "extern C++ {}" // directive in version scripts. - llvm::Optional>> demangledSyms; + std::optional>> demangledSyms; }; -extern SymbolTable *symtab; +LLVM_LIBRARY_VISIBILITY extern SymbolTable symtab; -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/Target.cpp b/gnu/llvm/lld/ELF/Target.cpp index d3e54f7387d..3873c7a25e4 100644 --- a/gnu/llvm/lld/ELF/Target.cpp +++ b/gnu/llvm/lld/ELF/Target.cpp @@ -91,42 +91,33 @@ TargetInfo *elf::getTarget() { llvm_unreachable("unknown target machine"); } -template static ErrorPlace getErrPlace(const uint8_t *loc) { +ErrorPlace elf::getErrorPlace(const uint8_t *loc) { assert(loc != nullptr); - for (InputSectionBase *d : inputSections) { - auto *isec = cast(d); - if (!isec->getParent() || (isec->type & SHT_NOBITS)) + for (InputSectionBase *d : ctx.inputSections) { + auto *isec = dyn_cast(d); + if (!isec || !isec->getParent() || (isec->type & SHT_NOBITS)) continue; const uint8_t *isecLoc = Out::bufferStart ? (Out::bufferStart + isec->getParent()->offset + isec->outSecOff) - : isec->data().data(); + : isec->contentMaybeDecompress().data(); if (isecLoc == nullptr) { assert(isa(isec) && "No data but not synthetic?"); continue; } - if (isecLoc <= loc && loc < isecLoc + isec->getSize()) - return {isec, isec->template getLocation(loc - isecLoc) + ": "}; + if (isecLoc <= loc && loc < isecLoc + isec->getSize()) { + std::string objLoc = isec->getLocation(loc - isecLoc); + // Return object file location and source file location. + // TODO: Refactor getSrcMsg not to take a variable. + Undefined dummy(nullptr, "", STB_LOCAL, 0, 0); + return {isec, objLoc + ": ", + isec->file ? isec->getSrcMsg(dummy, loc - isecLoc) : ""}; + } } return {}; } -ErrorPlace elf::getErrorPlace(const uint8_t *loc) { - switch (config->ekind) { - case ELF32LEKind: - return getErrPlace(loc); - case ELF32BEKind: - return getErrPlace(loc); - case ELF64LEKind: - return getErrPlace(loc); - case ELF64BEKind: - return getErrPlace(loc); - default: - llvm_unreachable("unknown ELF type"); - } -} - TargetInfo::~TargetInfo() {} int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const { @@ -161,33 +152,24 @@ RelExpr TargetInfo::adjustGotPcExpr(RelType type, int64_t addend, return R_GOT_PC; } -void TargetInfo::relaxGot(uint8_t *loc, const Relocation &rel, - uint64_t val) const { - llvm_unreachable("Should not have claimed to be relaxable"); -} - -void TargetInfo::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { - llvm_unreachable("Should not have claimed to be relaxable"); -} - -void TargetInfo::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { - llvm_unreachable("Should not have claimed to be relaxable"); -} - -void TargetInfo::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { - llvm_unreachable("Should not have claimed to be relaxable"); -} - -void TargetInfo::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { - llvm_unreachable("Should not have claimed to be relaxable"); +void TargetInfo::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + const unsigned bits = config->is64 ? 64 : 32; + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + for (const Relocation &rel : sec.relocs()) { + uint8_t *loc = buf + rel.offset; + const uint64_t val = SignExtend64( + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr), + bits); + if (rel.expr != R_RELAX_HINT) + relocate(loc, rel, val); + } } uint64_t TargetInfo::getImageBase() const { - // Use -image-base if set. Fall back to the target default if not. + // Use --image-base if set. Fall back to the target default if not. if (config->imageBase) return *config->imageBase; return config->isPic ? 0 : defaultImageBase; diff --git a/gnu/llvm/lld/ELF/Target.h b/gnu/llvm/lld/ELF/Target.h index 1fe3217c6d1..e6a78169058 100644 --- a/gnu/llvm/lld/ELF/Target.h +++ b/gnu/llvm/lld/ELF/Target.h @@ -9,9 +9,11 @@ #ifndef LLD_ELF_TARGET_H #define LLD_ELF_TARGET_H +#include "Config.h" #include "InputSection.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/MathExtras.h" #include @@ -87,6 +89,10 @@ public: void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const { relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val); } + virtual void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const; + + // Do a linker relaxation pass and return true if we changed something. + virtual bool relaxOnce(int pass) const { return false; } virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} @@ -109,11 +115,11 @@ public: uint64_t getImageBase() const; // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got. - bool gotBaseSymInGotPlt = true; + bool gotBaseSymInGotPlt = false; + static constexpr RelType noneRel = 0; RelType copyRel; RelType gotRel; - RelType noneRel; RelType pltRel; RelType relativeRel; RelType iRelativeRel; @@ -142,7 +148,7 @@ public: // Stores the NOP instructions of different sizes for the target and is used // to pad sections that are relaxed. - llvm::Optional>> nopInstrs; + std::optional>> nopInstrs; // If a target needs to rewrite calls to __morestack to instead call // __morestack_non_split when a split-stack enabled caller calls a @@ -152,16 +158,6 @@ public: virtual RelExpr adjustTlsExpr(RelType type, RelExpr expr) const; virtual RelExpr adjustGotPcExpr(RelType type, int64_t addend, const uint8_t *loc) const; - virtual void relaxGot(uint8_t *loc, const Relocation &rel, - uint64_t val) const; - virtual void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const; - virtual void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const; - virtual void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const; - virtual void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const; protected: // On FreeBSD x86_64 the first page cannot be mmaped. @@ -188,6 +184,7 @@ template TargetInfo *getMipsTargetInfo(); struct ErrorPlace { InputSectionBase *isec; std::string loc; + std::string srcLoc; }; // Returns input section and corresponding source string for the given location. @@ -199,7 +196,6 @@ static inline std::string getErrorLocation(const uint8_t *loc) { void writePPC32GlinkSection(uint8_t *buf, size_t numEntries); -bool tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc); unsigned getPPCDFormOp(unsigned secondaryOp); // In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first @@ -211,10 +207,6 @@ unsigned getPPCDFormOp(unsigned secondaryOp); // to the local entry-point. unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther); -// Returns true if a relocation is a small code model relocation that accesses -// the .toc section. -bool isPPC64SmallCodeModelTocReloc(RelType type); - // Write a prefixed instruction, which is a 4-byte prefix followed by a 4-byte // instruction (regardless of endianness). Therefore, the prefix is always in // lower memory than the instruction. @@ -223,8 +215,10 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn); void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); +void riscvFinalizeRelax(int passes); +void mergeRISCVAttributesSections(); -extern const TargetInfo *target; +LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target; TargetInfo *getTarget(); template bool isMipsPIC(const Defined *sym); @@ -291,4 +285,25 @@ inline void write64(void *p, uint64_t v) { } // namespace elf } // namespace lld +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#endif +#define invokeELFT(f, ...) \ + switch (config->ekind) { \ + case ELF32LEKind: \ + f(__VA_ARGS__); \ + break; \ + case ELF32BEKind: \ + f(__VA_ARGS__); \ + break; \ + case ELF64LEKind: \ + f(__VA_ARGS__); \ + break; \ + case ELF64BEKind: \ + f(__VA_ARGS__); \ + break; \ + default: \ + llvm_unreachable("unknown config->ekind"); \ + } + #endif diff --git a/gnu/llvm/lld/ELF/Thunks.cpp b/gnu/llvm/lld/ELF/Thunks.cpp index dbc476ffeeb..5964196a1ba 100644 --- a/gnu/llvm/lld/ELF/Thunks.cpp +++ b/gnu/llvm/lld/ELF/Thunks.cpp @@ -22,16 +22,15 @@ #include "Thunks.h" #include "Config.h" +#include "InputFiles.h" #include "InputSection.h" #include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include @@ -162,37 +161,96 @@ public: void addSymbols(ThunkSection &isec) override; }; -// Implementations of Thunks for older Arm architectures that do not support -// the movt/movw instructions. These thunks require at least Architecture v5 -// as used on processors such as the Arm926ej-s. There are no Thumb entry -// points as there is no Thumb branch instruction on these architecture that -// can result in a thunk -class ARMV5ABSLongThunk final : public ARMThunk { +// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted +class ThumbV6MABSLongThunk final : public ThumbThunk { +public: + ThumbV6MABSLongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} + + uint32_t sizeLong() override { return 12; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + +class ThumbV6MPILongThunk final : public ThumbThunk { +public: + ThumbV6MPILongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} + + uint32_t sizeLong() override { return 16; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + +// Architectures v4, v5 and v6 do not support the movt/movw instructions. v5 and +// v6 support BLX to which BL instructions can be rewritten inline. There are no +// Thumb entrypoints for v5 and v6 as there is no Thumb branch instruction on +// these architecture that can result in a thunk. + +// LDR on v5 and v6 can switch processor state, so for v5 and v6, +// ARMV5LongLdrPcThunk can be used for both Arm->Arm and Arm->Thumb calls. v4 +// can also use this thunk, but only for Arm->Arm calls. +class ARMV5LongLdrPcThunk final : public ARMThunk { public: - ARMV5ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} + ARMV5LongLdrPcThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 8; } void writeLong(uint8_t *buf) override; void addSymbols(ThunkSection &isec) override; - bool isCompatibleWith(const InputSection &isec, - const Relocation &rel) const override; }; -class ARMV5PILongThunk final : public ARMThunk { +// Implementations of Thunks for v4. BLX is not supported, and loads +// will not invoke Arm/Thumb state changes. +class ARMV4PILongBXThunk final : public ARMThunk { public: - ARMV5PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} + ARMV4PILongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; void addSymbols(ThunkSection &isec) override; - bool isCompatibleWith(const InputSection &isec, - const Relocation &rel) const override; }; -// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted -class ThumbV6MABSLongThunk final : public ThumbThunk { +class ARMV4PILongThunk final : public ARMThunk { public: - ThumbV6MABSLongThunk(Symbol &dest, int64_t addend) + ARMV4PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} + + uint32_t sizeLong() override { return 12; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + +class ThumbV4PILongBXThunk final : public ThumbThunk { +public: + ThumbV4PILongBXThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} + + uint32_t sizeLong() override { return 16; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + +class ThumbV4PILongThunk final : public ThumbThunk { +public: + ThumbV4PILongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} + + uint32_t sizeLong() override { return 20; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + +class ARMV4ABSLongBXThunk final : public ARMThunk { +public: + ARMV4ABSLongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} + + uint32_t sizeLong() override { return 12; } + void writeLong(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + +class ThumbV4ABSLongBXThunk final : public ThumbThunk { +public: + ThumbV4ABSLongBXThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } @@ -200,9 +258,9 @@ public: void addSymbols(ThunkSection &isec) override; }; -class ThumbV6MPILongThunk final : public ThumbThunk { +class ThumbV4ABSLongThunk final : public ThumbThunk { public: - ThumbV6MPILongThunk(Symbol &dest, int64_t addend) + ThumbV4ABSLongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } @@ -381,10 +439,10 @@ public: PPC64PILongBranchThunk(Symbol &dest, int64_t addend) : PPC64LongBranchThunk(dest, addend) { assert(!dest.isPreemptible); - if (Optional index = + if (std::optional index = in.ppc64LongBranchTarget->addEntry(&dest, addend)) { mainPart->relaDyn->addRelativeReloc( - target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8), + target->relativeRel, *in.ppc64LongBranchTarget, *index * UINT64_C(8), dest, addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther), target->symbolicRel, R_ABS); } @@ -399,24 +457,6 @@ public: } }; -// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte -// alignment. This gives a possible 26 bits of 'reach'. If the caller and -// callee do not use toc and the call offset is larger than 26 bits, -// we need to emit a pc-rel based long-branch thunk. The target address of -// the callee is computed with a PC-relative offset. -class PPC64PCRelLongBranchThunk final : public Thunk { -public: - PPC64PCRelLongBranchThunk(Symbol &dest, int64_t addend) - : Thunk(dest, addend) { - alignment = 16; - } - uint32_t size() override { return 32; } - void writeTo(uint8_t *buf) override; - void addSymbols(ThunkSection &isec) override; - bool isCompatibleWith(const InputSection &isec, - const Relocation &rel) const override; -}; - } // end anonymous namespace Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value, @@ -452,7 +492,7 @@ void AArch64ABSLongThunk::writeTo(uint8_t *buf) { } void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()), + addSymbol(saver().save("__AArch64AbsLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$x", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 8, isec); @@ -478,8 +518,8 @@ void AArch64ADRPThunk::writeTo(uint8_t *buf) { } void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__AArch64ADRPThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$x", STT_NOTYPE, 0, isec); } @@ -523,14 +563,22 @@ void ARMThunk::writeTo(uint8_t *buf) { bool ARMThunk::isCompatibleWith(const InputSection &isec, const Relocation &rel) const { + // v4T does not have BLX, so also deny R_ARM_THM_CALL + if (!config->armHasBlx && rel.type == R_ARM_THM_CALL) + return false; + // Thumb branch relocations can't use BLX return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; } -// This function returns true if the target is Thumb and is within 2^25, and -// it has not previously returned false (see comment for mayUseShortThunk). +// This function returns true if: +// the target is Thumb +// && is within branch range +// && this function has not previously returned false +// (see comment for mayUseShortThunk) +// && the arch supports Thumb branch range extension. bool ThumbThunk::getMayUseShortThunk() { - if (!mayUseShortThunk) + if (!mayUseShortThunk || !config->armJ1J2BranchEncoding) return false; uint64_t s = getARMThunkDestVA(destination); if ((s & 1) == 0) { @@ -561,6 +609,10 @@ void ThumbThunk::writeTo(uint8_t *buf) { bool ThumbThunk::isCompatibleWith(const InputSection &isec, const Relocation &rel) const { + // v4T does not have BLX, so also deny R_ARM_CALL + if (!config->armHasBlx && rel.type == R_ARM_CALL) + return false; + // ARM branch relocations can't use BLX return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32; } @@ -578,7 +630,7 @@ void ARMV7ABSLongThunk::writeLong(uint8_t *buf) { } void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__ARMv7ABSLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); } @@ -596,7 +648,7 @@ void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) { } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv7ABSLongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); } @@ -617,8 +669,8 @@ void ARMV7PILongThunk::writeLong(uint8_t *buf) { } void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__ARMV7PILongThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); } @@ -638,34 +690,134 @@ void ThumbV7PILongThunk::writeLong(uint8_t *buf) { } void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()), + addSymbol(saver().save("__ThumbV7PILongThunk_" + destination.getName()), + STT_FUNC, 1, isec); + addSymbol("$t", STT_NOTYPE, 0, isec); +} + +void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) { + // Most Thumb instructions cannot access the high registers r8 - r15. As the + // only register we can corrupt is r12 we must instead spill a low register + // to the stack to use as a scratch register. We push r1 even though we + // don't need to get some space to use for the return address. + const uint8_t data[] = { + 0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers + 0x01, 0x48, // ldr r0, [pc, #4] ; L1 + 0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S + 0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest + 0x00, 0x00, 0x00, 0x00 // L1: .word S + }; + uint64_t s = getARMThunkDestVA(destination); + memcpy(buf, data, sizeof(data)); + target->relocateNoSym(buf + 8, R_ARM_ABS32, s); +} + +void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv6MABSLongThunk_" + destination.getName()), + STT_FUNC, 1, isec); + addSymbol("$t", STT_NOTYPE, 0, isec); + addSymbol("$d", STT_NOTYPE, 8, isec); +} + +void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { + // Most Thumb instructions cannot access the high registers r8 - r15. As the + // only register we can corrupt is ip (r12) we must instead spill a low + // register to the stack to use as a scratch register. + const uint8_t data[] = { + 0x01, 0xb4, // P: push {r0} ; Obtain scratch register + 0x02, 0x48, // ldr r0, [pc, #8] ; L2 + 0x84, 0x46, // mov ip, r0 ; high to low register + 0x01, 0xbc, // pop {r0} ; restore scratch register + 0xe7, 0x44, // L1: add pc, ip ; transfer control + 0xc0, 0x46, // nop ; pad to 4-byte boundary + 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4) + }; + uint64_t s = getARMThunkDestVA(destination); + uint64_t p = getThunkTargetSym()->getVA() & ~0x1; + memcpy(buf, data, sizeof(data)); + target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); +} + +void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv6MPILongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); + addSymbol("$d", STT_NOTYPE, 12, isec); } -void ARMV5ABSLongThunk::writeLong(uint8_t *buf) { +void ARMV5LongLdrPcThunk::writeLong(uint8_t *buf) { const uint8_t data[] = { - 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1 + 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1 0x00, 0x00, 0x00, 0x00, // L1: .word S }; memcpy(buf, data, sizeof(data)); target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination)); } -void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()), +void ARMV5LongLdrPcThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__ARMv5LongLdrPcThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 4, isec); } -bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec, - const Relocation &rel) const { - // Thumb branch relocations can't use BLX - return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; +void ARMV4ABSLongBXThunk::writeLong(uint8_t *buf) { + const uint8_t data[] = { + 0x00, 0xc0, 0x9f, 0xe5, // ldr r12, [pc] ; L1 + 0x1c, 0xff, 0x2f, 0xe1, // bx r12 + 0x00, 0x00, 0x00, 0x00, // L1: .word S + }; + memcpy(buf, data, sizeof(data)); + target->relocateNoSym(buf + 8, R_ARM_ABS32, getARMThunkDestVA(destination)); +} + +void ARMV4ABSLongBXThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__ARMv4ABSLongBXThunk_" + destination.getName()), + STT_FUNC, 0, isec); + addSymbol("$a", STT_NOTYPE, 0, isec); + addSymbol("$d", STT_NOTYPE, 8, isec); +} + +void ThumbV4ABSLongBXThunk::writeLong(uint8_t *buf) { + const uint8_t data[] = { + 0x78, 0x47, // bx pc + 0xfd, 0xe7, // b #-6 ; Arm recommended sequence to follow bx pc + 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] ; L1 + 0x00, 0x00, 0x00, 0x00, // L1: .word S + }; + memcpy(buf, data, sizeof(data)); + target->relocateNoSym(buf + 8, R_ARM_ABS32, getARMThunkDestVA(destination)); +} + +void ThumbV4ABSLongBXThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv4ABSLongBXThunk_" + destination.getName()), + STT_FUNC, 1, isec); + addSymbol("$t", STT_NOTYPE, 0, isec); + addSymbol("$a", STT_NOTYPE, 4, isec); + addSymbol("$d", STT_NOTYPE, 8, isec); +} + +void ThumbV4ABSLongThunk::writeLong(uint8_t *buf) { + const uint8_t data[] = { + 0x78, 0x47, // bx pc + 0xfd, 0xe7, // b #-6 ; Arm recommended sequence to follow bx pc + 0x00, 0xc0, 0x9f, 0xe5, // ldr r12, [pc] ; L1 + 0x1c, 0xff, 0x2f, 0xe1, // bx r12 + 0x00, 0x00, 0x00, 0x00, // L1: .word S + }; + memcpy(buf, data, sizeof(data)); + target->relocateNoSym(buf + 12, R_ARM_ABS32, getARMThunkDestVA(destination)); +} + +void ThumbV4ABSLongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv4ABSLongThunk_" + destination.getName()), + STT_FUNC, 1, isec); + addSymbol("$t", STT_NOTYPE, 0, isec); + addSymbol("$a", STT_NOTYPE, 4, isec); + addSymbol("$d", STT_NOTYPE, 12, isec); } -void ARMV5PILongThunk::writeLong(uint8_t *buf) { +void ARMV4PILongBXThunk::writeLong(uint8_t *buf) { const uint8_t data[] = { 0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2 0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip @@ -678,67 +830,75 @@ void ARMV5PILongThunk::writeLong(uint8_t *buf) { target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); } -void ARMV5PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC, - 0, isec); +void ARMV4PILongBXThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__ARMv4PILongBXThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 12, isec); } -bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec, - const Relocation &rel) const { - // Thumb branch relocations can't use BLX - return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24; +void ARMV4PILongThunk::writeLong(uint8_t *buf) { + const uint8_t data[] = { + 0x00, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc] ; L2 + 0x0c, 0xf0, 0x8f, 0xe0, // L1: add pc, pc, r12 + 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8) + }; + uint64_t s = getARMThunkDestVA(destination); + uint64_t p = getThunkTargetSym()->getVA() & ~0x1; + memcpy(buf, data, sizeof(data)); + target->relocateNoSym(buf + 8, R_ARM_REL32, s - p - 12); } -void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) { - // Most Thumb instructions cannot access the high registers r8 - r15. As the - // only register we can corrupt is r12 we must instead spill a low register - // to the stack to use as a scratch register. We push r1 even though we - // don't need to get some space to use for the return address. +void ARMV4PILongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__ARMv4PILongThunk_" + destination.getName()), + STT_FUNC, 0, isec); + addSymbol("$a", STT_NOTYPE, 0, isec); + addSymbol("$d", STT_NOTYPE, 8, isec); +} + +void ThumbV4PILongBXThunk::writeLong(uint8_t *buf) { const uint8_t data[] = { - 0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers - 0x01, 0x48, // ldr r0, [pc, #4] ; L1 - 0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S - 0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest - 0x00, 0x00, 0x00, 0x00 // L1: .word S + 0x78, 0x47, // P: bx pc + 0xfd, 0xe7, // b #-6 ; Arm recommended sequence to follow bx pc + 0x00, 0xc0, 0x9f, 0xe5, // ldr r12, [pc] ; L2 + 0x0f, 0xf0, 0x8c, 0xe0, // L1: add pc, r12, pc + 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8) }; uint64_t s = getARMThunkDestVA(destination); + uint64_t p = getThunkTargetSym()->getVA() & ~0x1; memcpy(buf, data, sizeof(data)); - target->relocateNoSym(buf + 8, R_ARM_ABS32, s); + target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 16); } -void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()), +void ThumbV4PILongBXThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv4PILongBXThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); - addSymbol("$d", STT_NOTYPE, 8, isec); + addSymbol("$a", STT_NOTYPE, 4, isec); + addSymbol("$d", STT_NOTYPE, 12, isec); } -void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { - // Most Thumb instructions cannot access the high registers r8 - r15. As the - // only register we can corrupt is ip (r12) we must instead spill a low - // register to the stack to use as a scratch register. +void ThumbV4PILongThunk::writeLong(uint8_t *buf) { const uint8_t data[] = { - 0x01, 0xb4, // P: push {r0} ; Obtain scratch register - 0x02, 0x48, // ldr r0, [pc, #8] ; L2 - 0x84, 0x46, // mov ip, r0 ; high to low register - 0x01, 0xbc, // pop {r0} ; restore scratch register - 0xe7, 0x44, // L1: add pc, ip ; transfer control - 0xc0, 0x46, // nop ; pad to 4-byte boundary - 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4) + 0x78, 0x47, // P: bx pc + 0xfd, 0xe7, // b #-6 ; Arm recommended sequence to follow bx pc + 0x04, 0xc0, 0x9f, 0xe5, // ldr ip, [pc,#4] ; L2 + 0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip + 0x1c, 0xff, 0x2f, 0xe1, // bx ip + 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8) }; uint64_t s = getARMThunkDestVA(destination); uint64_t p = getThunkTargetSym()->getVA() & ~0x1; memcpy(buf, data, sizeof(data)); - target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); + target->relocateNoSym(buf + 16, R_ARM_REL32, s - p - 16); } -void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()), +void ThumbV4PILongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver().save("__Thumbv4PILongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); - addSymbol("$d", STT_NOTYPE, 12, isec); + addSymbol("$a", STT_NOTYPE, 4, isec); + addSymbol("$d", STT_NOTYPE, 16, isec); } // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. @@ -753,7 +913,7 @@ void MipsThunk::writeTo(uint8_t *buf) { } void MipsThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); } @@ -776,8 +936,9 @@ void MicroMipsThunk::writeTo(uint8_t *buf) { } void MicroMipsThunk::addSymbols(ThunkSection &isec) { - Defined *d = addSymbol( - saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); + Defined *d = + addSymbol(saver().save("__microLA25Thunk_" + destination.getName()), + STT_FUNC, 0, isec); d->stOther |= STO_MIPS_MICROMIPS; } @@ -800,8 +961,9 @@ void MicroMipsR6Thunk::writeTo(uint8_t *buf) { } void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { - Defined *d = addSymbol( - saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); + Defined *d = + addSymbol(saver().save("__microLA25Thunk_" + destination.getName()), + STT_FUNC, 0, isec); d->stOther |= STO_MIPS_MICROMIPS; } @@ -824,8 +986,9 @@ void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, // The stub loads an address relative to r30 (.got2+Addend). Addend is // almost always 0x8000. The address of .got2 is different in another object // file, so a stub cannot be shared. - offset = gotPltVA - (in.ppc32Got2->getParent()->getVA() + - file->ppc32Got2OutSecOff + addend); + offset = gotPltVA - + (in.ppc32Got2->getParent()->getVA() + + (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + addend); } else { // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is // currently the address of .got). @@ -860,7 +1023,7 @@ void PPC32PltCallStub::addSymbols(ThunkSection &isec) { else os << ".plt_pic32."; os << destination.getName(); - addSymbol(saver.save(os.str()), STT_FUNC, 0, isec); + addSymbol(saver().save(os.str()), STT_FUNC, 0, isec); } bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, @@ -869,7 +1032,7 @@ bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, } void PPC32LongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__LongThunk_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__LongThunk_" + destination.getName()), STT_FUNC, 0, isec); } @@ -913,8 +1076,8 @@ void PPC64PltCallStub::writeTo(uint8_t *buf) { } void PPC64PltCallStub::addSymbols(ThunkSection &isec) { - Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC, - 0, isec); + Defined *s = addSymbol(saver().save("__plt_" + destination.getName()), + STT_FUNC, 0, isec); s->needsTocRestore = true; s->file = destination.file; } @@ -932,25 +1095,18 @@ void PPC64R2SaveStub::writeTo(uint8_t *buf) { write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b } else if (isInt<34>(offset)) { int nextInstOffset; - if (!config->Power10Stub) { - uint64_t tocOffset = destination.getVA() - getPPC64TocBase(); - if (tocOffset >> 16 > 0) { - const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff); - const uint64_t addis = ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff); - write32(buf + 4, addis); // addis r12, r2 , top of offset - write32(buf + 8, addi); // addi r12, r12, bottom of offset - nextInstOffset = 12; - } else { - const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff); - write32(buf + 4, addi); // addi r12, r2, offset - nextInstOffset = 8; - } - } else { - const uint64_t paddi = PADDI_R12_NO_DISP | - (((offset >> 16) & 0x3ffff) << 32) | - (offset & 0xffff); - writePrefixedInstruction(buf + 4, paddi); // paddi r12, 0, func@pcrel, 1 + uint64_t tocOffset = destination.getVA() - getPPC64TocBase(); + if (tocOffset >> 16 > 0) { + const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff); + const uint64_t addis = + ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff); + write32(buf + 4, addis); // addis r12, r2 , top of offset + write32(buf + 8, addi); // addi r12, r12, bottom of offset nextInstOffset = 12; + } else { + const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff); + write32(buf + 4, addi); // addi r12, r2, offset + nextInstOffset = 8; } write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12 write32(buf + nextInstOffset + 4, BCTR); // bctr @@ -964,7 +1120,7 @@ void PPC64R2SaveStub::writeTo(uint8_t *buf) { } void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { - Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()), + Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()), STT_FUNC, 0, isec); s->needsTocRestore = true; } @@ -980,7 +1136,7 @@ void PPC64R12SetupStub::writeTo(uint8_t *buf) { reportRangeError(buf, offset, 34, destination, "R12 setup stub offset"); int nextInstOffset; - if (!config->Power10Stub) { + if (!config->power10Stubs) { uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8; write32(buf + 0, 0x7c0802a6); // mflr r12 write32(buf + 4, 0x429f0005); // bcl 20,31,.+4 @@ -1000,7 +1156,7 @@ void PPC64R12SetupStub::writeTo(uint8_t *buf) { } void PPC64R12SetupStub::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__gep_setup_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1013,7 +1169,7 @@ void PPC64PCRelPLTStub::writeTo(uint8_t *buf) { int nextInstOffset = 0; int64_t offset = destination.getGotPltVA() - getThunkTargetSym()->getVA(); - if (config->Power10Stub) { + if (config->power10Stubs) { if (!isInt<34>(offset)) reportRangeError(buf, offset, 34, destination, "PC-relative PLT stub offset"); @@ -1036,7 +1192,7 @@ void PPC64PCRelPLTStub::writeTo(uint8_t *buf) { } void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1052,7 +1208,7 @@ void PPC64LongBranchThunk::writeTo(uint8_t *buf) { } void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__long_branch_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1061,42 +1217,6 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec, return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; } -void PPC64PCRelLongBranchThunk::writeTo(uint8_t *buf) { - int64_t offset = destination.getVA() - getThunkTargetSym()->getVA(); - if (!isInt<34>(offset)) - reportRangeError(buf, offset, 34, destination, - "PC-relative long branch stub offset"); - - int nextInstOffset; - if (!config->Power10Stub) { - uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8; - write32(buf + 0, 0x7c0802a6); // mflr r12 - write32(buf + 4, 0x429f0005); // bcl 20,31,.+4 - write32(buf + 8, 0x7d6802a6); // mflr r11 - write32(buf + 12, 0x7d8803a6); // mtlr r12 - write32(buf + 16, 0x3d8b0000 | computeHiBits(off)); // addis r12,r11,off@ha - write32(buf + 20, 0x398c0000 | (off & 0xffff)); // addi r12,r12,off@l - nextInstOffset = 24; - } else { - uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) | - (offset & 0xffff); - writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func@pcrel, 1 - nextInstOffset = 8; - } - write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12 - write32(buf + nextInstOffset + 4, BCTR); // bctr -} - -void PPC64PCRelLongBranchThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__long_branch_pcrel_" + destination.getName()), - STT_FUNC, 0, isec); -} - -bool PPC64PCRelLongBranchThunk::isCompatibleWith(const InputSection &isec, - const Relocation &rel) const { - return rel.type == R_PPC64_REL24_NOTOC; -} - Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {} Thunk::~Thunk() = default; @@ -1110,12 +1230,50 @@ static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) { return make(s, a); } -// Creates a thunk for Thumb-ARM interworking. -// Arm Architectures v5 and v6 do not support Thumb2 technology. This means +// Creates a thunk for long branches or Thumb-ARM interworking. +// Arm Architectures v4t does not support Thumb2 technology, and does not +// support BLX or LDR Arm/Thumb state switching. This means that +// - MOVT and MOVW instructions cannot be used. +// - We can't rewrite BL in place to BLX. We will need thunks. +// +// TODO: use B for short Thumb->Arm thunks instead of LDR (this doesn't work for +// Arm->Thumb, as in Arm state no BX PC trick; it doesn't switch state). +static Thunk *addThunkArmv4(RelType reloc, Symbol &s, int64_t a) { + bool thumb_target = s.getVA(a) & 1; + + switch (reloc) { + case R_ARM_PC24: + case R_ARM_PLT32: + case R_ARM_JUMP24: + case R_ARM_CALL: + if (config->picThunk) { + if (thumb_target) + return make(s, a); + return make(s, a); + } + if (thumb_target) + return make(s, a); + return make(s, a); + case R_ARM_THM_CALL: + if (config->picThunk) { + if (thumb_target) + return make(s, a); + return make(s, a); + } + if (thumb_target) + return make(s, a); + return make(s, a); + } + fatal("relocation " + toString(reloc) + " to " + toString(s) + + " not supported for Armv4 or Armv4T target"); +} + +// Creates a thunk for Thumb-ARM interworking compatible with Armv5 and Armv6. +// Arm Architectures v5 and v6 do not support Thumb2 technology. This means that // - MOVT and MOVW instructions cannot be used // - Only Thumb relocation that can generate a Thunk is a BL, this can always // be transformed into a BLX -static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) { +static Thunk *addThunkArmv5v6(RelType reloc, Symbol &s, int64_t a) { switch (reloc) { case R_ARM_PC24: case R_ARM_PLT32: @@ -1123,8 +1281,8 @@ static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) { case R_ARM_CALL: case R_ARM_THM_CALL: if (config->picThunk) - return make(s, a); - return make(s, a); + return make(s, a); + return make(s, a); } fatal("relocation " + toString(reloc) + " to " + toString(s) + " not supported for Armv5 or Armv6 targets"); @@ -1167,9 +1325,11 @@ static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) { // of the input objects. InputFiles.cpp contains the mapping from ARM // architecture to flag. if (!config->armHasMovtMovw) { - if (!config->armJ1J2BranchEncoding) - return addThunkPreArmv7(reloc, s, a); - return addThunkV6M(reloc, s, a); + if (config->armJ1J2BranchEncoding) + return addThunkV6M(reloc, s, a); + if (config->armHasBlx) + return addThunkArmv5v6(reloc, s, a); + return addThunkArmv4(reloc, s, a); } switch (reloc) { @@ -1223,9 +1383,7 @@ static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) { return make(s, a); if (type == R_PPC64_REL24_NOTOC) - return (s.stOther >> 5) > 1 - ? (Thunk *)make(s) - : (Thunk *)make(s, a); + return make(s); if (config->picThunk) return make(s, a); diff --git a/gnu/llvm/lld/ELF/Thunks.h b/gnu/llvm/lld/ELF/Thunks.h index 5558da1a2c7..86aaee15f93 100644 --- a/gnu/llvm/lld/ELF/Thunks.h +++ b/gnu/llvm/lld/ELF/Thunks.h @@ -12,8 +12,7 @@ #include "llvm/ADT/SmallVector.h" #include "Relocations.h" -namespace lld { -namespace elf { +namespace lld::elf { class Defined; class InputFile; class Symbol; @@ -78,7 +77,6 @@ static inline uint16_t computeHiBits(uint32_t toCompute) { return (toCompute + 0x8000) >> 16; } -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/MachO/Arch/ARM.cpp b/gnu/llvm/lld/MachO/Arch/ARM.cpp index 42c7b893086..c20d3449405 100644 --- a/gnu/llvm/lld/MachO/Arch/ARM.cpp +++ b/gnu/llvm/lld/MachO/Arch/ARM.cpp @@ -32,38 +32,38 @@ struct ARM : TargetInfo { void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, uint64_t pc) const override; - void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override; void writeStubHelperHeader(uint8_t *buf) const override; - void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const override; + void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const override; + void relaxGotLoad(uint8_t *loc, uint8_t type) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; uint64_t getPageSize() const override { return 4 * 1024; } -}; + void handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const override; +}; } // namespace -const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const { - static const std::array relocAttrsArray{{ +static constexpr std::array relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"VANILLA", /* FIXME populate this */ B(_0)}, - {"PAIR", /* FIXME populate this */ B(_0)}, - {"SECTDIFF", /* FIXME populate this */ B(_0)}, - {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)}, - {"PB_LA_PTR", /* FIXME populate this */ B(_0)}, - {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"32BIT_BRANCH", /* FIXME populate this */ B(_0)}, - {"HALF", /* FIXME populate this */ B(_0)}, - {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)}, + {"VANILLA", /* FIXME populate this */ B(_0)}, + {"PAIR", /* FIXME populate this */ B(_0)}, + {"SECTDIFF", /* FIXME populate this */ B(_0)}, + {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)}, + {"PB_LA_PTR", /* FIXME populate this */ B(_0)}, + {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"32BIT_BRANCH", /* FIXME populate this */ B(_0)}, + {"HALF", /* FIXME populate this */ B(_0)}, + {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, relocation_info rel) const { @@ -116,7 +116,7 @@ void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, return; } else if (isBlx && !defined->thumb) { Bitfield::set(base, 0xe); // unconditional BL - Bitfield::set>(base, 1); + Bitfield::set>(base, true); isBlx = false; } } else { @@ -140,7 +140,7 @@ void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, } } -void ARM::writeStub(uint8_t *buf, const Symbol &sym) const { +void ARM::writeStub(uint8_t *buf, const Symbol &sym, uint64_t) const { fatal("TODO: implement this"); } @@ -148,11 +148,18 @@ void ARM::writeStubHelperHeader(uint8_t *buf) const { fatal("TODO: implement this"); } -void ARM::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, +void ARM::writeStubHelperEntry(uint8_t *buf, const Symbol &sym, uint64_t entryAddr) const { fatal("TODO: implement this"); } +void ARM::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const { + fatal("TODO: implement this"); +} + void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const { fatal("TODO: implement this"); } @@ -164,9 +171,44 @@ ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) { stubSize = 0 /* FIXME */; stubHelperHeaderSize = 0 /* FIXME */; stubHelperEntrySize = 0 /* FIXME */; + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) { static ARM t(cpuSubtype); return &t; } + +void ARM::handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const { + if (config->outputType == MH_OBJECT) + return; + + switch (r.type) { + case ARM_RELOC_BR24: + if (sym->getName().startswith("___dtrace_probe")) { + // change call site to a NOP + write32le(loc, 0xE1A00000); + } else if (sym->getName().startswith("___dtrace_isenabled")) { + // change call site to 'eor r0, r0, r0' + write32le(loc, 0xE0200000); + } else { + error("Unrecognized dtrace symbol prefix: " + toString(*sym)); + } + break; + case ARM_THUMB_RELOC_BR22: + if (sym->getName().startswith("___dtrace_probe")) { + // change 32-bit blx call site to two thumb NOPs + write32le(loc, 0x46C046C0); + } else if (sym->getName().startswith("___dtrace_isenabled")) { + // change 32-bit blx call site to 'nop', 'eor r0, r0' + write32le(loc, 0x46C04040); + } else { + error("Unrecognized dtrace symbol prefix: " + toString(*sym)); + } + break; + default: + llvm_unreachable("Unsupported dtrace relocation type for ARM"); + } +} diff --git a/gnu/llvm/lld/MachO/Arch/ARM64.cpp b/gnu/llvm/lld/MachO/Arch/ARM64.cpp index 36c3cc63928..6627d41fbec 100644 --- a/gnu/llvm/lld/MachO/Arch/ARM64.cpp +++ b/gnu/llvm/lld/MachO/Arch/ARM64.cpp @@ -13,10 +13,12 @@ #include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "mach-o/compact_unwind_encoding.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" using namespace llvm; @@ -29,12 +31,17 @@ namespace { struct ARM64 : ARM64Common { ARM64(); - void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override; void writeStubHelperHeader(uint8_t *buf) const override; - void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; + + void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const override; void populateThunk(InputSection *thunk, Symbol *funcSym) override; + void applyOptimizationHints(uint8_t *, const ObjFile &) const override; }; } // namespace @@ -45,31 +52,24 @@ struct ARM64 : ARM64Common { // absolute version of this relocation. The semantics of the absolute relocation // are weird -- it results in the value of the GOT slot being written, instead // of the address. Let's not support it unless we find a real-world use case. - -const RelocAttrs &ARM64::getRelocAttrs(uint8_t type) const { - static const std::array relocAttrsArray{{ +static constexpr std::array relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"UNSIGNED", - B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, - {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, - {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, - {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, - {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, - {"GOT_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, - {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, - {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, - {"TLVP_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, - {"ADDEND", B(ADDEND)}, + {"UNSIGNED", + B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, + {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, + {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, + {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, + {"GOT_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, + {"TLVP_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"ADDEND", B(ADDEND)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; static constexpr uint32_t stubCode[] = { 0x90000010, // 00: adrp x16, __la_symbol_ptr@page @@ -77,8 +77,9 @@ static constexpr uint32_t stubCode[] = { 0xd61f0200, // 08: br x16 }; -void ARM64::writeStub(uint8_t *buf8, const Symbol &sym) const { - ::writeStub(buf8, stubCode, sym); +void ARM64::writeStub(uint8_t *buf8, const Symbol &sym, + uint64_t pointerVA) const { + ::writeStub(buf8, stubCode, sym, pointerVA); } static constexpr uint32_t stubHelperHeaderCode[] = { @@ -100,11 +101,31 @@ static constexpr uint32_t stubHelperEntryCode[] = { 0x00000000, // 08: l0: .long 0 }; -void ARM64::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, +void ARM64::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym, uint64_t entryVA) const { ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA); } +static constexpr uint32_t objcStubsFastCode[] = { + 0x90000001, // adrp x1, __objc_selrefs@page + 0xf9400021, // ldr x1, [x1, @selector("foo")@pageoff] + 0x90000010, // adrp x16, _got@page + 0xf9400210, // ldr x16, [x16, _objc_msgSend@pageoff] + 0xd61f0200, // br x16 + 0xd4200020, // brk #0x1 + 0xd4200020, // brk #0x1 + 0xd4200020, // brk #0x1 +}; + +void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const { + ::writeObjCMsgSendStub(buf, objcStubsFastCode, sym, stubsAddr, + stubOffset, selrefsVA, selectorIndex, gotAddr, + msgSendIndex); +} + // A thunk is the relaxed variation of stubCode. We don't need the // extra indirection through a lazy pointer because the target address // is known at link time. @@ -134,9 +155,558 @@ ARM64::ARM64() : ARM64Common(LP64()) { stubSize = sizeof(stubCode); thunkSize = sizeof(thunkCode); - branchRange = maxIntN(28) - thunkSize; + + objcStubsFastSize = sizeof(objcStubsFastCode); + objcStubsAlignment = 32; + + // Branch immediate is two's complement 26 bits, which is implicitly + // multiplied by 4 (since all functions are 4-aligned: The branch range + // is -4*(2**(26-1))..4*(2**(26-1) - 1). + backwardBranchRange = 128 * 1024 * 1024; + forwardBranchRange = backwardBranchRange - 4; + + modeDwarfEncoding = UNWIND_ARM64_MODE_DWARF; + subtractorRelocType = ARM64_RELOC_SUBTRACTOR; + unsignedRelocType = ARM64_RELOC_UNSIGNED; + stubHelperHeaderSize = sizeof(stubHelperHeaderCode); stubHelperEntrySize = sizeof(stubHelperEntryCode); + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; +} + +namespace { +struct Adrp { + uint32_t destRegister; + int64_t addend; +}; + +struct Add { + uint8_t destRegister; + uint8_t srcRegister; + uint32_t addend; +}; + +enum ExtendType { ZeroExtend = 1, Sign64 = 2, Sign32 = 3 }; + +struct Ldr { + uint8_t destRegister; + uint8_t baseRegister; + uint8_t p2Size; + bool isFloat; + ExtendType extendType; + int64_t offset; +}; +} // namespace + +static bool parseAdrp(uint32_t insn, Adrp &adrp) { + if ((insn & 0x9f000000) != 0x90000000) + return false; + adrp.destRegister = insn & 0x1f; + uint64_t immHi = (insn >> 5) & 0x7ffff; + uint64_t immLo = (insn >> 29) & 0x3; + adrp.addend = SignExtend64<21>(immLo | (immHi << 2)) * 4096; + return true; +} + +static bool parseAdd(uint32_t insn, Add &add) { + if ((insn & 0xffc00000) != 0x91000000) + return false; + add.destRegister = insn & 0x1f; + add.srcRegister = (insn >> 5) & 0x1f; + add.addend = (insn >> 10) & 0xfff; + return true; +} + +static bool parseLdr(uint32_t insn, Ldr &ldr) { + ldr.destRegister = insn & 0x1f; + ldr.baseRegister = (insn >> 5) & 0x1f; + uint8_t size = insn >> 30; + uint8_t opc = (insn >> 22) & 3; + + if ((insn & 0x3fc00000) == 0x39400000) { + // LDR (immediate), LDRB (immediate), LDRH (immediate) + ldr.p2Size = size; + ldr.extendType = ZeroExtend; + ldr.isFloat = false; + } else if ((insn & 0x3f800000) == 0x39800000) { + // LDRSB (immediate), LDRSH (immediate), LDRSW (immediate) + ldr.p2Size = size; + ldr.extendType = static_cast(opc); + ldr.isFloat = false; + } else if ((insn & 0x3f400000) == 0x3d400000) { + // LDR (immediate, SIMD&FP) + ldr.extendType = ZeroExtend; + ldr.isFloat = true; + if (opc == 1) + ldr.p2Size = size; + else if (size == 0 && opc == 3) + ldr.p2Size = 4; + else + return false; + } else { + return false; + } + ldr.offset = ((insn >> 10) & 0xfff) << ldr.p2Size; + return true; +} + +static bool isValidAdrOffset(int32_t delta) { return isInt<21>(delta); } + +static void writeAdr(void *loc, uint32_t dest, int32_t delta) { + assert(isValidAdrOffset(delta)); + uint32_t opcode = 0x10000000; + uint32_t immHi = (delta & 0x001ffffc) << 3; + uint32_t immLo = (delta & 0x00000003) << 29; + write32le(loc, opcode | immHi | immLo | dest); +} + +static void writeNop(void *loc) { write32le(loc, 0xd503201f); } + +static bool isLiteralLdrEligible(const Ldr &ldr) { + return ldr.p2Size > 1 && isShiftedInt<19, 2>(ldr.offset); +} + +static void writeLiteralLdr(void *loc, const Ldr &ldr) { + assert(isLiteralLdrEligible(ldr)); + uint32_t imm19 = (ldr.offset / 4 & maskTrailingOnes(19)) << 5; + uint32_t opcode; + switch (ldr.p2Size) { + case 2: + if (ldr.isFloat) + opcode = 0x1c000000; + else + opcode = ldr.extendType == Sign64 ? 0x98000000 : 0x18000000; + break; + case 3: + opcode = ldr.isFloat ? 0x5c000000 : 0x58000000; + break; + case 4: + opcode = 0x9c000000; + break; + default: + llvm_unreachable("Invalid literal ldr size"); + } + write32le(loc, opcode | imm19 | ldr.destRegister); +} + +static bool isImmediateLdrEligible(const Ldr &ldr) { + // Note: We deviate from ld64's behavior, which converts to immediate loads + // only if ldr.offset < 4096, even though the offset is divided by the load's + // size in the 12-bit immediate operand. Only the unsigned offset variant is + // supported. + + uint32_t size = 1 << ldr.p2Size; + return ldr.offset >= 0 && (ldr.offset % size) == 0 && + isUInt<12>(ldr.offset >> ldr.p2Size); +} + +static void writeImmediateLdr(void *loc, const Ldr &ldr) { + assert(isImmediateLdrEligible(ldr)); + uint32_t opcode = 0x39000000; + if (ldr.isFloat) { + opcode |= 0x04000000; + assert(ldr.extendType == ZeroExtend); + } + opcode |= ldr.destRegister; + opcode |= ldr.baseRegister << 5; + uint8_t size, opc; + if (ldr.p2Size == 4) { + size = 0; + opc = 3; + } else { + opc = ldr.extendType; + size = ldr.p2Size; + } + uint32_t immBits = ldr.offset >> ldr.p2Size; + write32le(loc, opcode | (immBits << 10) | (opc << 22) | (size << 30)); +} + +// Transforms a pair of adrp+add instructions into an adr instruction if the +// target is within the +/- 1 MiB range allowed by the adr's 21 bit signed +// immediate offset. +// +// adrp xN, _foo@PAGE +// add xM, xN, _foo@PAGEOFF +// -> +// adr xM, _foo +// nop +static void applyAdrpAdd(uint8_t *buf, const ConcatInputSection *isec, + uint64_t offset1, uint64_t offset2) { + uint32_t ins1 = read32le(buf + offset1); + uint32_t ins2 = read32le(buf + offset2); + Adrp adrp; + Add add; + if (!parseAdrp(ins1, adrp) || !parseAdd(ins2, add)) + return; + if (adrp.destRegister != add.srcRegister) + return; + + uint64_t addr1 = isec->getVA() + offset1; + uint64_t referent = pageBits(addr1) + adrp.addend + add.addend; + int64_t delta = referent - addr1; + if (!isValidAdrOffset(delta)) + return; + + writeAdr(buf + offset1, add.destRegister, delta); + writeNop(buf + offset2); +} + +// Transforms two adrp instructions into a single adrp if their referent +// addresses are located on the same 4096 byte page. +// +// adrp xN, _foo@PAGE +// adrp xN, _bar@PAGE +// -> +// adrp xN, _foo@PAGE +// nop +static void applyAdrpAdrp(uint8_t *buf, const ConcatInputSection *isec, + uint64_t offset1, uint64_t offset2) { + uint32_t ins1 = read32le(buf + offset1); + uint32_t ins2 = read32le(buf + offset2); + Adrp adrp1, adrp2; + if (!parseAdrp(ins1, adrp1) || !parseAdrp(ins2, adrp2)) + return; + if (adrp1.destRegister != adrp2.destRegister) + return; + + uint64_t page1 = pageBits(offset1 + isec->getVA()) + adrp1.addend; + uint64_t page2 = pageBits(offset2 + isec->getVA()) + adrp2.addend; + if (page1 != page2) + return; + + writeNop(buf + offset2); +} + +// Transforms a pair of adrp+ldr (immediate) instructions into an ldr (literal) +// load from a PC-relative address if it is 4-byte aligned and within +/- 1 MiB, +// as ldr can encode a signed 19-bit offset that gets multiplied by 4. +// +// adrp xN, _foo@PAGE +// ldr xM, [xN, _foo@PAGEOFF] +// -> +// nop +// ldr xM, _foo +static void applyAdrpLdr(uint8_t *buf, const ConcatInputSection *isec, + uint64_t offset1, uint64_t offset2) { + uint32_t ins1 = read32le(buf + offset1); + uint32_t ins2 = read32le(buf + offset2); + Adrp adrp; + Ldr ldr; + if (!parseAdrp(ins1, adrp) || !parseLdr(ins2, ldr)) + return; + if (adrp.destRegister != ldr.baseRegister) + return; + + uint64_t addr1 = isec->getVA() + offset1; + uint64_t addr2 = isec->getVA() + offset2; + uint64_t referent = pageBits(addr1) + adrp.addend + ldr.offset; + ldr.offset = referent - addr2; + if (!isLiteralLdrEligible(ldr)) + return; + + writeNop(buf + offset1); + writeLiteralLdr(buf + offset2, ldr); +} + +// GOT loads are emitted by the compiler as a pair of adrp and ldr instructions, +// but they may be changed to adrp+add by relaxGotLoad(). This hint performs +// the AdrpLdr or AdrpAdd transformation depending on whether it was relaxed. +static void applyAdrpLdrGot(uint8_t *buf, const ConcatInputSection *isec, + uint64_t offset1, uint64_t offset2) { + uint32_t ins2 = read32le(buf + offset2); + Add add; + Ldr ldr; + if (parseAdd(ins2, add)) + applyAdrpAdd(buf, isec, offset1, offset2); + else if (parseLdr(ins2, ldr)) + applyAdrpLdr(buf, isec, offset1, offset2); +} + +// Optimizes an adrp+add+ldr sequence used for loading from a local symbol's +// address by loading directly if it's close enough, or to an adrp(p)+ldr +// sequence if it's not. +// +// adrp x0, _foo@PAGE +// add x1, x0, _foo@PAGEOFF +// ldr x2, [x1, #off] +static void applyAdrpAddLdr(uint8_t *buf, const ConcatInputSection *isec, + uint64_t offset1, uint64_t offset2, + uint64_t offset3) { + uint32_t ins1 = read32le(buf + offset1); + Adrp adrp; + if (!parseAdrp(ins1, adrp)) + return; + uint32_t ins2 = read32le(buf + offset2); + Add add; + if (!parseAdd(ins2, add)) + return; + uint32_t ins3 = read32le(buf + offset3); + Ldr ldr; + if (!parseLdr(ins3, ldr)) + return; + if (adrp.destRegister != add.srcRegister) + return; + if (add.destRegister != ldr.baseRegister) + return; + + // Load from the target address directly. + // nop + // nop + // ldr x2, [_foo + #off] + uint64_t addr1 = isec->getVA() + offset1; + uint64_t addr3 = isec->getVA() + offset3; + uint64_t referent = pageBits(addr1) + adrp.addend + add.addend; + Ldr literalLdr = ldr; + literalLdr.offset += referent - addr3; + if (isLiteralLdrEligible(literalLdr)) { + writeNop(buf + offset1); + writeNop(buf + offset2); + writeLiteralLdr(buf + offset3, literalLdr); + return; + } + + // Load the target address into a register and load from there indirectly. + // adr x1, _foo + // nop + // ldr x2, [x1, #off] + int64_t adrOffset = referent - addr1; + if (isValidAdrOffset(adrOffset)) { + writeAdr(buf + offset1, ldr.baseRegister, adrOffset); + // Note: ld64 moves the offset into the adr instruction for AdrpAddLdr, but + // not for AdrpLdrGotLdr. Its effect is the same either way. + writeNop(buf + offset2); + return; + } + + // Move the target's page offset into the ldr's immediate offset. + // adrp x0, _foo@PAGE + // nop + // ldr x2, [x0, _foo@PAGEOFF + #off] + Ldr immediateLdr = ldr; + immediateLdr.baseRegister = adrp.destRegister; + immediateLdr.offset += add.addend; + if (isImmediateLdrEligible(immediateLdr)) { + writeNop(buf + offset2); + writeImmediateLdr(buf + offset3, immediateLdr); + return; + } +} + +// Relaxes a GOT-indirect load. +// If the referenced symbol is external and its GOT entry is within +/- 1 MiB, +// the GOT entry can be loaded with a single literal ldr instruction. +// If the referenced symbol is local and thus has been relaxed to adrp+add+ldr, +// we perform the AdrpAddLdr transformation. +static void applyAdrpLdrGotLdr(uint8_t *buf, const ConcatInputSection *isec, + uint64_t offset1, uint64_t offset2, + uint64_t offset3) { + uint32_t ins2 = read32le(buf + offset2); + Add add; + Ldr ldr2; + + if (parseAdd(ins2, add)) { + applyAdrpAddLdr(buf, isec, offset1, offset2, offset3); + } else if (parseLdr(ins2, ldr2)) { + // adrp x1, _foo@GOTPAGE + // ldr x2, [x1, _foo@GOTPAGEOFF] + // ldr x3, [x2, #off] + + uint32_t ins1 = read32le(buf + offset1); + Adrp adrp; + if (!parseAdrp(ins1, adrp)) + return; + uint32_t ins3 = read32le(buf + offset3); + Ldr ldr3; + if (!parseLdr(ins3, ldr3)) + return; + + if (ldr2.baseRegister != adrp.destRegister) + return; + if (ldr3.baseRegister != ldr2.destRegister) + return; + // Loads from the GOT must be pointer sized. + if (ldr2.p2Size != 3 || ldr2.isFloat) + return; + + uint64_t addr1 = isec->getVA() + offset1; + uint64_t addr2 = isec->getVA() + offset2; + uint64_t referent = pageBits(addr1) + adrp.addend + ldr2.offset; + // Load the GOT entry's address directly. + // nop + // ldr x2, _foo@GOTPAGE + _foo@GOTPAGEOFF + // ldr x3, [x2, #off] + Ldr literalLdr = ldr2; + literalLdr.offset = referent - addr2; + if (isLiteralLdrEligible(literalLdr)) { + writeNop(buf + offset1); + writeLiteralLdr(buf + offset2, literalLdr); + } + } +} + +static uint64_t readValue(const uint8_t *&ptr, const uint8_t *end) { + unsigned int n = 0; + uint64_t value = decodeULEB128(ptr, &n, end); + ptr += n; + return value; +} + +template +static void forEachHint(ArrayRef data, Callback callback) { + std::array args; + + for (const uint8_t *p = data.begin(), *end = data.end(); p < end;) { + uint64_t type = readValue(p, end); + if (type == 0) + break; + + uint64_t argCount = readValue(p, end); + // All known LOH types as of 2022-09 have 3 or fewer arguments; skip others. + if (argCount > 3) { + for (unsigned i = 0; i < argCount; ++i) + readValue(p, end); + continue; + } + + for (unsigned i = 0; i < argCount; ++i) + args[i] = readValue(p, end); + callback(type, ArrayRef(args.data(), argCount)); + } +} + +// On RISC architectures like arm64, materializing a memory address generally +// takes multiple instructions. If the referenced symbol is located close enough +// in memory, fewer instructions are needed. +// +// Linker optimization hints record where addresses are computed. After +// addresses have been assigned, if possible, we change them to a shorter +// sequence of instructions. The size of the binary is not modified; the +// eliminated instructions are replaced with NOPs. This still leads to faster +// code as the CPU can skip over NOPs quickly. +// +// LOHs are specified by the LC_LINKER_OPTIMIZATION_HINTS load command, which +// points to a sequence of ULEB128-encoded numbers. Each entry specifies a +// transformation kind, and 2 or 3 addresses where the instructions are located. +void ARM64::applyOptimizationHints(uint8_t *outBuf, const ObjFile &obj) const { + ArrayRef data = obj.getOptimizationHints(); + if (data.empty()) + return; + + const ConcatInputSection *section = nullptr; + uint64_t sectionAddr = 0; + uint8_t *buf = nullptr; + + auto findSection = [&](uint64_t addr) { + if (section && addr >= sectionAddr && + addr < sectionAddr + section->getSize()) + return true; + + auto secIt = std::prev(llvm::upper_bound( + obj.sections, addr, + [](uint64_t off, const Section *sec) { return off < sec->addr; })); + const Section *sec = *secIt; + + auto subsecIt = std::prev(llvm::upper_bound( + sec->subsections, addr - sec->addr, + [](uint64_t off, Subsection subsec) { return off < subsec.offset; })); + const Subsection &subsec = *subsecIt; + const ConcatInputSection *isec = + dyn_cast_or_null(subsec.isec); + if (!isec || isec->shouldOmitFromOutput()) + return false; + + section = isec; + sectionAddr = subsec.offset + sec->addr; + buf = outBuf + section->outSecOff + section->parent->fileOff; + return true; + }; + + auto isValidOffset = [&](uint64_t offset) { + if (offset < sectionAddr || offset >= sectionAddr + section->getSize()) { + error(toString(&obj) + + ": linker optimization hint spans multiple sections"); + return false; + } + return true; + }; + + bool hasAdrpAdrp = false; + forEachHint(data, [&](uint64_t kind, ArrayRef args) { + if (kind == LOH_ARM64_ADRP_ADRP) { + hasAdrpAdrp = true; + return; + } + + if (!findSection(args[0])) + return; + switch (kind) { + case LOH_ARM64_ADRP_ADD: + if (isValidOffset(args[1])) + applyAdrpAdd(buf, section, args[0] - sectionAddr, + args[1] - sectionAddr); + break; + case LOH_ARM64_ADRP_LDR: + if (isValidOffset(args[1])) + applyAdrpLdr(buf, section, args[0] - sectionAddr, + args[1] - sectionAddr); + break; + case LOH_ARM64_ADRP_LDR_GOT: + if (isValidOffset(args[1])) + applyAdrpLdrGot(buf, section, args[0] - sectionAddr, + args[1] - sectionAddr); + break; + case LOH_ARM64_ADRP_ADD_LDR: + if (isValidOffset(args[1]) && isValidOffset(args[2])) + applyAdrpAddLdr(buf, section, args[0] - sectionAddr, + args[1] - sectionAddr, args[2] - sectionAddr); + break; + case LOH_ARM64_ADRP_LDR_GOT_LDR: + if (isValidOffset(args[1]) && isValidOffset(args[2])) + applyAdrpLdrGotLdr(buf, section, args[0] - sectionAddr, + args[1] - sectionAddr, args[2] - sectionAddr); + break; + case LOH_ARM64_ADRP_ADD_STR: + case LOH_ARM64_ADRP_LDR_GOT_STR: + // TODO: Implement these + break; + } + }); + + if (!hasAdrpAdrp) + return; + + // AdrpAdrp optimization hints are performed in a second pass because they + // might interfere with other transformations. For instance, consider the + // following input: + // + // adrp x0, _foo@PAGE + // add x1, x0, _foo@PAGEOFF + // adrp x0, _bar@PAGE + // add x2, x0, _bar@PAGEOFF + // + // If we perform the AdrpAdrp relaxation first, we get: + // + // adrp x0, _foo@PAGE + // add x1, x0, _foo@PAGEOFF + // nop + // add x2, x0, _bar@PAGEOFF + // + // If we then apply AdrpAdd to the first two instructions, the add will have a + // garbage value in x0: + // + // adr x1, _foo + // nop + // nop + // add x2, x0, _bar@PAGEOFF + forEachHint(data, [&](uint64_t kind, ArrayRef args) { + if (kind != LOH_ARM64_ADRP_ADRP) + return; + if (!findSection(args[0])) + return; + if (isValidOffset(args[1])) + applyAdrpAdrp(buf, section, args[0] - sectionAddr, args[1] - sectionAddr); + }); } TargetInfo *macho::createARM64TargetInfo() { diff --git a/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp b/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp index 67e7292fd6f..45cf0eac3c4 100644 --- a/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp +++ b/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp @@ -38,56 +38,57 @@ int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, } } +static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) { + switch (r.length) { + case 2: + checkInt(loc, r, value, 32); + write32le(loc, value); + break; + case 3: + write64le(loc, value); + break; + default: + llvm_unreachable("invalid r_length"); + } +} + // For instruction relocations (load, store, add), the base // instruction is pre-populated in the text section. A pre-populated // instruction has opcode & register-operand bits set, with immediate // operands zeroed. We read it from text, OR-in the immediate // operands, then write-back the completed instruction. - void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, uint64_t pc) const { + auto loc32 = reinterpret_cast(loc); uint32_t base = ((r.length == 2) ? read32le(loc) : 0); switch (r.type) { case ARM64_RELOC_BRANCH26: - value = encodeBranch26(r, base, value - pc); + encodeBranch26(loc32, r, base, value - pc); break; case ARM64_RELOC_SUBTRACTOR: case ARM64_RELOC_UNSIGNED: - if (r.length == 2) - checkInt(r, value, 32); + writeValue(loc, r, value); break; case ARM64_RELOC_POINTER_TO_GOT: if (r.pcrel) value -= pc; - checkInt(r, value, 32); + writeValue(loc, r, value); break; case ARM64_RELOC_PAGE21: case ARM64_RELOC_GOT_LOAD_PAGE21: - case ARM64_RELOC_TLVP_LOAD_PAGE21: { + case ARM64_RELOC_TLVP_LOAD_PAGE21: assert(r.pcrel); - value = encodePage21(r, base, pageBits(value) - pageBits(pc)); + encodePage21(loc32, r, base, pageBits(value) - pageBits(pc)); break; - } case ARM64_RELOC_PAGEOFF12: case ARM64_RELOC_GOT_LOAD_PAGEOFF12: case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: assert(!r.pcrel); - value = encodePageOff12(base, value); + encodePageOff12(loc32, r, base, value); break; default: llvm_unreachable("unexpected relocation type"); } - - switch (r.length) { - case 2: - write32le(loc, value); - break; - case 3: - write64le(loc, value); - break; - default: - llvm_unreachable("invalid r_length"); - } } void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const { @@ -108,3 +109,44 @@ void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const { instruction = ((instruction & 0x001fffff) | 0x91000000); write32le(loc, instruction); } + +void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const { + assert(r.type == ARM64_RELOC_BRANCH26); + + if (config->outputType == MH_OBJECT) + return; + + if (sym->getName().startswith("___dtrace_probe")) { + // change call site to a NOP + write32le(loc, 0xD503201F); + } else if (sym->getName().startswith("___dtrace_isenabled")) { + // change call site to 'MOVZ X0,0' + write32le(loc, 0xD2800000); + } else { + error("Unrecognized dtrace symbol prefix: " + toString(*sym)); + } +} + +static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align, + const Symbol *sym) { + std::string symbolHint; + if (sym) + symbolHint = " (" + toString(*sym) + ")"; + error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" + + llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) + + "-byte aligned"); +} + +void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r, + uint64_t va, int align) { + uint64_t off = reinterpret_cast(loc) - in.bufferStart; + const InputSection *isec = offsetToInputSection(&off); + std::string locStr = isec ? isec->getLocation(off) : "(invalid location)"; + ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast()); +} + +void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d, + uint64_t va, int align) { + ::reportUnalignedLdrStr(d.reason, va, align, d.symbol); +} diff --git a/gnu/llvm/lld/MachO/Arch/ARM64Common.h b/gnu/llvm/lld/MachO/Arch/ARM64Common.h index 934101caefb..8dc2412b29b 100644 --- a/gnu/llvm/lld/MachO/Arch/ARM64Common.h +++ b/gnu/llvm/lld/MachO/Arch/ARM64Common.h @@ -16,8 +16,7 @@ #include "llvm/BinaryFormat/MachO.h" -namespace lld { -namespace macho { +namespace lld::macho { struct ARM64Common : TargetInfo { template ARM64Common(LP lp) : TargetInfo(lp) {} @@ -29,6 +28,9 @@ struct ARM64Common : TargetInfo { void relaxGotLoad(uint8_t *loc, uint8_t type) const override; uint64_t getPageSize() const override { return 16 * 1024; } + + void handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const override; }; inline uint64_t bitField(uint64_t value, int right, int width, int left) { @@ -40,16 +42,18 @@ inline uint64_t bitField(uint64_t value, int right, int width, int left) { // | | imm26 | // +-----------+---------------------------------------------------+ -inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) { - checkInt(r, va, 28); +inline void encodeBranch26(uint32_t *loc, const Reloc &r, uint32_t base, + uint64_t va) { + checkInt(loc, r, va, 28); // Since branch destinations are 4-byte aligned, the 2 least- // significant bits are 0. They are right shifted off the end. - return (base | bitField(va, 2, 26, 0)); + llvm::support::endian::write32le(loc, base | bitField(va, 2, 26, 0)); } -inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) { - checkInt(d, va, 28); - return (base | bitField(va, 2, 26, 0)); +inline void encodeBranch26(uint32_t *loc, SymbolDiagnostic d, uint32_t base, + uint64_t va) { + checkInt(loc, d, va, 28); + llvm::support::endian::write32le(loc, base | bitField(va, 2, 26, 0)); } // 30 29 23 5 @@ -57,32 +61,45 @@ inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) { // | |ilo| | immhi | | // +-+---+---------+-------------------------------------+---------+ -inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) { - checkInt(r, va, 35); - return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); +inline void encodePage21(uint32_t *loc, const Reloc &r, uint32_t base, + uint64_t va) { + checkInt(loc, r, va, 35); + llvm::support::endian::write32le(loc, base | bitField(va, 12, 2, 29) | + bitField(va, 14, 19, 5)); } -inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) { - checkInt(d, va, 35); - return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); +inline void encodePage21(uint32_t *loc, SymbolDiagnostic d, uint32_t base, + uint64_t va) { + checkInt(loc, d, va, 35); + llvm::support::endian::write32le(loc, base | bitField(va, 12, 2, 29) | + bitField(va, 14, 19, 5)); } +void reportUnalignedLdrStr(void *loc, const Reloc &, uint64_t va, int align); +void reportUnalignedLdrStr(void *loc, SymbolDiagnostic, uint64_t va, int align); + // 21 10 // +-------------------+-----------------------+-------------------+ // | | imm12 | | // +-------------------+-----------------------+-------------------+ -inline uint64_t encodePageOff12(uint32_t base, uint64_t va) { +template +inline void encodePageOff12(uint32_t *loc, Target t, uint32_t base, + uint64_t va) { int scale = 0; if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store scale = base >> 30; if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant scale = 4; } + const int size = 1 << scale; + if ((va & (size - 1)) != 0) + reportUnalignedLdrStr(loc, t, va, size); // TODO(gkm): extract embedded addend and warn if != 0 // uint64_t addend = ((base & 0x003FFC00) >> 10); - return (base | bitField(va, scale, 12 - scale, 10)); + llvm::support::endian::write32le(loc, + base | bitField(va, scale, 12 - scale, 10)); } inline uint64_t pageBits(uint64_t address) { @@ -90,18 +107,15 @@ inline uint64_t pageBits(uint64_t address) { return address & pageMask; } -template inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3], - const macho::Symbol &sym) { + const macho::Symbol &sym, uint64_t pointerVA) { auto *buf32 = reinterpret_cast(buf8); constexpr size_t stubCodeSize = 3 * sizeof(uint32_t); + SymbolDiagnostic d = {&sym, "stub"}; uint64_t pcPageBits = pageBits(in.stubs->addr + sym.stubsIndex * stubCodeSize); - uint64_t lazyPointerVA = - in.lazyPointers->addr + sym.stubsIndex * LP::wordSize; - buf32[0] = encodePage21({&sym, "stub"}, stubCode[0], - pageBits(lazyPointerVA) - pcPageBits); - buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA); + encodePage21(&buf32[0], d, stubCode[0], pageBits(pointerVA) - pcPageBits); + encodePageOff12(&buf32[1], d, stubCode[1], pointerVA); buf32[2] = stubCode[2]; } @@ -114,31 +128,57 @@ inline void writeStubHelperHeader(uint8_t *buf8, }; uint64_t loaderVA = in.imageLoaderCache->getVA(); SymbolDiagnostic d = {nullptr, "stub header helper"}; - buf32[0] = encodePage21(d, stubHelperHeaderCode[0], - pageBits(loaderVA) - pcPageBits(0)); - buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA); + encodePage21(&buf32[0], d, stubHelperHeaderCode[0], + pageBits(loaderVA) - pcPageBits(0)); + encodePageOff12(&buf32[1], d, stubHelperHeaderCode[1], loaderVA); buf32[2] = stubHelperHeaderCode[2]; uint64_t binderVA = in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize; - buf32[3] = encodePage21(d, stubHelperHeaderCode[3], - pageBits(binderVA) - pcPageBits(3)); - buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA); + encodePage21(&buf32[3], d, stubHelperHeaderCode[3], + pageBits(binderVA) - pcPageBits(3)); + encodePageOff12(&buf32[4], d, stubHelperHeaderCode[4], binderVA); buf32[5] = stubHelperHeaderCode[5]; } inline void writeStubHelperEntry(uint8_t *buf8, const uint32_t stubHelperEntryCode[3], - const DylibSymbol &sym, uint64_t entryVA) { + const Symbol &sym, uint64_t entryVA) { auto *buf32 = reinterpret_cast(buf8); auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); }; uint64_t stubHelperHeaderVA = in.stubHelper->addr; buf32[0] = stubHelperEntryCode[0]; - buf32[1] = encodeBranch26({&sym, "stub helper"}, stubHelperEntryCode[1], - stubHelperHeaderVA - pcVA(1)); + encodeBranch26(&buf32[1], {&sym, "stub helper"}, stubHelperEntryCode[1], + stubHelperHeaderVA - pcVA(1)); buf32[2] = sym.lazyBindOffset; } -} // namespace macho -} // namespace lld +template +inline void +writeObjCMsgSendStub(uint8_t *buf, const uint32_t objcStubsFastCode[8], + Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset, + uint64_t selrefsVA, uint64_t selectorIndex, + uint64_t gotAddr, uint64_t msgSendIndex) { + SymbolDiagnostic d = {sym, sym->getName()}; + auto *buf32 = reinterpret_cast(buf); + + auto pcPageBits = [stubsAddr, stubOffset](int i) { + return pageBits(stubsAddr + stubOffset + i * sizeof(uint32_t)); + }; + + uint64_t selectorOffset = selectorIndex * LP::wordSize; + encodePage21(&buf32[0], d, objcStubsFastCode[0], + pageBits(selrefsVA + selectorOffset) - pcPageBits(0)); + encodePageOff12(&buf32[1], d, objcStubsFastCode[1], + selrefsVA + selectorOffset); + encodePage21(&buf32[2], d, objcStubsFastCode[2], + pageBits(gotAddr) - pcPageBits(2)); + encodePage21(&buf32[3], d, objcStubsFastCode[3], msgSendIndex * LP::wordSize); + buf32[4] = objcStubsFastCode[4]; + buf32[5] = objcStubsFastCode[5]; + buf32[6] = objcStubsFastCode[6]; + buf32[7] = objcStubsFastCode[7]; +} + +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp b/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp index f7b1439b893..c6bb6ee6c1c 100644 --- a/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp +++ b/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp @@ -29,40 +29,37 @@ namespace { struct ARM64_32 : ARM64Common { ARM64_32(); - void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override; void writeStubHelperHeader(uint8_t *buf) const override; - void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; + void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const override; }; } // namespace // These are very similar to ARM64's relocation attributes, except that we don't // have the BYTE8 flag set. -const RelocAttrs &ARM64_32::getRelocAttrs(uint8_t type) const { - static const std::array relocAttrsArray{{ +static constexpr std::array relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4)}, - {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, - {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, - {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, - {"GOT_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, - {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, - {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, - {"TLVP_LOAD_PAGEOFF12", - B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, - {"ADDEND", B(ADDEND)}, + {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4)}, + {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)}, + {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)}, + {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)}, + {"GOT_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)}, + {"TLVP_LOAD_PAGEOFF12", + B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"ADDEND", B(ADDEND)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; // The stub code is fairly similar to ARM64's, except that we load pointers into // 32-bit 'w' registers, instead of the 64-bit 'x' ones. @@ -73,8 +70,9 @@ static constexpr uint32_t stubCode[] = { 0xd61f0200, // 08: br x16 }; -void ARM64_32::writeStub(uint8_t *buf8, const Symbol &sym) const { - ::writeStub(buf8, stubCode, sym); +void ARM64_32::writeStub(uint8_t *buf8, const Symbol &sym, + uint64_t pointerVA) const { + ::writeStub(buf8, stubCode, sym, pointerVA); } static constexpr uint32_t stubHelperHeaderCode[] = { @@ -96,18 +94,32 @@ static constexpr uint32_t stubHelperEntryCode[] = { 0x00000000, // 08: l0: .long 0 }; -void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, +void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym, uint64_t entryVA) const { ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA); } +void ARM64_32::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, + uint64_t stubsAddr, uint64_t stubOffset, + uint64_t selrefsVA, uint64_t selectorIndex, + uint64_t gotAddr, + uint64_t msgSendIndex) const { + fatal("TODO: implement this"); +} + ARM64_32::ARM64_32() : ARM64Common(ILP32()) { cpuType = CPU_TYPE_ARM64_32; cpuSubtype = CPU_SUBTYPE_ARM64_V8; + modeDwarfEncoding = 0x04000000; // UNWIND_ARM_MODE_DWARF + subtractorRelocType = GENERIC_RELOC_INVALID; // FIXME + unsignedRelocType = GENERIC_RELOC_INVALID; // FIXME + stubSize = sizeof(stubCode); stubHelperHeaderSize = sizeof(stubHelperHeaderCode); stubHelperEntrySize = sizeof(stubHelperEntryCode); + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } TargetInfo *macho::createARM64_32TargetInfo() { diff --git a/gnu/llvm/lld/MachO/Arch/X86_64.cpp b/gnu/llvm/lld/MachO/Arch/X86_64.cpp index 7e2155801fc..048702f1290 100644 --- a/gnu/llvm/lld/MachO/Arch/X86_64.cpp +++ b/gnu/llvm/lld/MachO/Arch/X86_64.cpp @@ -12,6 +12,7 @@ #include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "mach-o/compact_unwind_encoding.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Endian.h" @@ -30,39 +31,40 @@ struct X86_64 : TargetInfo { void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, uint64_t relocVA) const override; - void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStub(uint8_t *buf, const Symbol &, + uint64_t pointerVA) const override; void writeStubHelperHeader(uint8_t *buf) const override; - void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const override; + void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const override; + void relaxGotLoad(uint8_t *loc, uint8_t type) const override; - const RelocAttrs &getRelocAttrs(uint8_t type) const override; uint64_t getPageSize() const override { return 4 * 1024; } -}; + void handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const override; +}; } // namespace -const RelocAttrs &X86_64::getRelocAttrs(uint8_t type) const { - static const std::array relocAttrsArray{{ +static constexpr std::array relocAttrsArray{{ #define B(x) RelocAttrBits::x - {"UNSIGNED", - B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, - {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, - {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, - {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, - {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, - {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, - {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, + {"UNSIGNED", + B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)}, + {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, + {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)}, + {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)}, + {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)}, + {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)}, + {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)}, #undef B - }}; - assert(type < relocAttrsArray.size() && "invalid relocation type"); - if (type >= relocAttrsArray.size()) - return invalidRelocAttrs; - return relocAttrsArray[type]; -} +}}; static int pcrelOffset(uint8_t type) { switch (type) { @@ -102,9 +104,9 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, switch (r.length) { case 2: if (r.type == X86_64_RELOC_UNSIGNED) - checkUInt(r, value, 32); + checkUInt(loc, r, value, 32); else - checkInt(r, value, 32); + checkInt(loc, r, value, 32); write32le(loc, value); break; case 3: @@ -127,7 +129,7 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr, uint64_t bufOff, uint64_t destAddr) { uint64_t rip = bufAddr + bufOff; - checkInt(d, destAddr - rip, 32); + checkInt(buf, d, destAddr - rip, 32); // For the instructions we care about, the RIP-relative address is always // stored in the last 4 bytes of the instruction. write32le(buf + bufOff - 4, destAddr - rip); @@ -137,11 +139,11 @@ static constexpr uint8_t stub[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip) }; -void X86_64::writeStub(uint8_t *buf, const Symbol &sym) const { +void X86_64::writeStub(uint8_t *buf, const Symbol &sym, + uint64_t pointerVA) const { memcpy(buf, stub, 2); // just copy the two nonzero bytes uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub); - writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), - in.lazyPointers->addr + sym.stubsIndex * LP64::wordSize); + writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA); } static constexpr uint8_t stubHelperHeader[] = { @@ -166,7 +168,7 @@ static constexpr uint8_t stubHelperEntry[] = { 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> }; -void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, +void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym, uint64_t entryAddr) const { memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); write32le(buf + 1, sym.lazyBindOffset); @@ -174,6 +176,24 @@ void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, sizeof(stubHelperEntry), in.stubHelper->addr); } +static constexpr uint8_t objcStubsFastCode[] = { + 0x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi + 0xff, 0x25, 0, 0, 0, 0, // 0x7: jmpq *_objc_msgSend@GOT(%rip) +}; + +void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr, + uint64_t stubOffset, uint64_t selrefsVA, + uint64_t selectorIndex, uint64_t gotAddr, + uint64_t msgSendIndex) const { + memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode)); + SymbolDiagnostic d = {sym, sym->getName()}; + uint64_t stubAddr = stubsAddr + stubOffset; + writeRipRelative(d, buf, stubAddr, 7, + selrefsVA + selectorIndex * LP64::wordSize); + writeRipRelative(d, buf, stubAddr, 0xd, + gotAddr + msgSendIndex * LP64::wordSize); +} + void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const { // Convert MOVQ to LEAQ if (loc[-2] != 0x8b) @@ -185,12 +205,41 @@ X86_64::X86_64() : TargetInfo(LP64()) { cpuType = CPU_TYPE_X86_64; cpuSubtype = CPU_SUBTYPE_X86_64_ALL; + modeDwarfEncoding = UNWIND_X86_MODE_DWARF; + subtractorRelocType = X86_64_RELOC_SUBTRACTOR; + unsignedRelocType = X86_64_RELOC_UNSIGNED; + stubSize = sizeof(stub); stubHelperHeaderSize = sizeof(stubHelperHeader); stubHelperEntrySize = sizeof(stubHelperEntry); + + objcStubsFastSize = sizeof(objcStubsFastCode); + objcStubsAlignment = 1; + + relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()}; } TargetInfo *macho::createX86_64TargetInfo() { static X86_64 t; return &t; } + +void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const { + assert(r.type == X86_64_RELOC_BRANCH); + + if (config->outputType == MH_OBJECT) + return; + + if (sym->getName().startswith("___dtrace_probe")) { + // change call site to a NOP + loc[-1] = 0x90; + write32le(loc, 0x00401F0F); + } else if (sym->getName().startswith("___dtrace_isenabled")) { + // change call site to a clear eax + loc[-1] = 0x33; + write32le(loc, 0x909090C0); + } else { + error("Unrecognized dtrace symbol prefix: " + toString(*sym)); + } +} diff --git a/gnu/llvm/lld/MachO/CMakeLists.txt b/gnu/llvm/lld/MachO/CMakeLists.txt index eff1812a6bb..ea26889267f 100644 --- a/gnu/llvm/lld/MachO/CMakeLists.txt +++ b/gnu/llvm/lld/MachO/CMakeLists.txt @@ -4,7 +4,7 @@ add_public_tablegen_target(MachOOptionsTableGen) include_directories(${LLVM_MAIN_SRC_DIR}/../libunwind/include) -add_lld_library(lldMachO2 +add_lld_library(lldMachO Arch/ARM.cpp Arch/ARM64.cpp Arch/ARM64Common.cpp @@ -14,6 +14,7 @@ add_lld_library(lldMachO2 Driver.cpp DriverUtils.cpp Dwarf.cpp + EhFrame.cpp ExportTrie.cpp ICF.cpp InputFiles.cpp @@ -25,6 +26,7 @@ add_lld_library(lldMachO2 OutputSection.cpp OutputSegment.cpp Relocations.cpp + SectionPriorities.cpp SymbolTable.cpp Symbols.cpp SyntheticSections.cpp @@ -36,8 +38,10 @@ add_lld_library(lldMachO2 ${LLVM_TARGETS_TO_BUILD} BinaryFormat BitReader + BitWriter Core DebugInfoDWARF + Demangle LTO MC ObjCARCOpts @@ -45,6 +49,7 @@ add_lld_library(lldMachO2 Option Passes Support + TargetParser TextAPI LINK_LIBS @@ -58,5 +63,5 @@ add_lld_library(lldMachO2 ) if(LLVM_HAVE_LIBXAR) - target_link_libraries(lldMachO2 PRIVATE ${XAR_LIB}) + target_link_libraries(lldMachO PRIVATE ${XAR_LIB}) endif() diff --git a/gnu/llvm/lld/MachO/ConcatOutputSection.cpp b/gnu/llvm/lld/MachO/ConcatOutputSection.cpp index 78590cff2ee..cbd3a2492d2 100644 --- a/gnu/llvm/lld/MachO/ConcatOutputSection.cpp +++ b/gnu/llvm/lld/MachO/ConcatOutputSection.cpp @@ -13,8 +13,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/TimeProfiler.h" @@ -53,19 +52,19 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) { // multiple thunks to the same destination distributed throughout a large // program so that all call sites can have one within range. // -// The optimal approach is to mix islands for distinations within two hops, +// The optimal approach is to mix islands for destinations within two hops, // and use thunks for destinations at greater distance. For now, we only // implement thunks. TODO: Adding support for branch islands! // // Internally -- as expressed in LLD's data structures -- a -// branch-range-extension thunk comprises ... +// branch-range-extension thunk consists of: // -// (1) new Defined privateExtern symbol for the thunk named +// (1) new Defined symbol for the thunk named // .thunk., which references ... // (2) new InputSection, which contains ... // (3.1) new data for the instructions to load & branch to the far address + // (3.2) new Relocs on instructions to load the far address, which reference ... -// (4.1) existing Defined extern symbol for the real function in __text, or +// (4.1) existing Defined symbol for the real function in __text, or // (4.2) existing DylibSymbol for the real function in a dylib // // Nearly-optimal thunk-placement algorithm features: @@ -85,15 +84,17 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) { // distant call sites might be unable to reach the same thunk, so multiple // thunks are necessary to serve all call sites in a very large program. A // thunkInfo stores state for all thunks associated with a particular -// function: (a) thunk symbol, (b) input section containing stub code, and -// (c) sequence number for the active thunk incarnation. When an old thunk -// goes out of range, we increment the sequence number and create a new -// thunk named .thunk.. +// function: +// (a) thunk symbol +// (b) input section containing stub code, and +// (c) sequence number for the active thunk incarnation. +// When an old thunk goes out of range, we increment the sequence number and +// create a new thunk named .thunk.. // -// * A thunk incarnation comprises (a) private-extern Defined symbol pointing -// to (b) an InputSection holding machine instructions (similar to a MachO -// stub), and (c) Reloc(s) that reference the real function for fixing-up -// the stub code. +// * A thunk consists of +// (a) a Defined symbol pointing to +// (b) an InputSection holding machine code (similar to a MachO stub), and +// (c) relocs referencing the real function for fixing up the stub code. // // * std::vector MergedInputSection::thunks: A vector parallel // to the inputs vector. We store new thunks via cheap vector append, rather @@ -109,7 +110,7 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) { // thus, we place thunks at monotonically increasing addresses. Once a thunk // is placed, it and all previous input-section addresses are final. // -// * MergedInputSection::finalize() and MergedInputSection::writeTo() merge +// * ConcatInputSection::finalize() and ConcatInputSection::writeTo() merge // the inputs and thunks vectors (both ordered by ascending address), which // is simple and cheap. @@ -120,31 +121,31 @@ DenseMap lld::macho::thunkMap; // instructions, whereas CISC (i.e., x86) generally doesn't. RISC only needs // thunks for programs so large that branch source & destination addresses // might differ more than the range of branch instruction(s). -bool ConcatOutputSection::needsThunks() const { +bool TextOutputSection::needsThunks() const { if (!target->usesThunks()) return false; uint64_t isecAddr = addr; - for (InputSection *isec : inputs) + for (ConcatInputSection *isec : inputs) isecAddr = alignTo(isecAddr, isec->align) + isec->getSize(); - if (isecAddr - addr + in.stubs->getSize() <= target->branchRange) + if (isecAddr - addr + in.stubs->getSize() <= + std::min(target->backwardBranchRange, target->forwardBranchRange)) return false; // Yes, this program is large enough to need thunks. - for (InputSection *isec : inputs) { + for (ConcatInputSection *isec : inputs) { for (Reloc &r : isec->relocs) { if (!target->hasAttr(r.type, RelocAttrBits::BRANCH)) continue; auto *sym = r.referent.get(); // Pre-populate the thunkMap and memoize call site counts for every // InputSection and ThunkInfo. We do this for the benefit of - // ConcatOutputSection::estimateStubsInRangeVA() + // estimateStubsInRangeVA(). ThunkInfo &thunkInfo = thunkMap[sym]; // Knowing ThunkInfo call site count will help us know whether or not we // might need to create more for this referent at the time we are - // estimating distance to __stubs in . + // estimating distance to __stubs in estimateStubsInRangeVA(). ++thunkInfo.callSiteCount; - // Knowing InputSection call site count will help us avoid work on those - // that have no BRANCH relocs. - ++isec->callSiteCount; + // We can avoid work on InputSections that have no BRANCH relocs. + isec->hasCallSites = true; } } return true; @@ -152,61 +153,69 @@ bool ConcatOutputSection::needsThunks() const { // Since __stubs is placed after __text, we must estimate the address // beyond which stubs are within range of a simple forward branch. -uint64_t ConcatOutputSection::estimateStubsInRangeVA(size_t callIdx) const { - uint64_t branchRange = target->branchRange; - size_t endIdx = inputs.size(); - ConcatInputSection *isec = inputs[callIdx]; - uint64_t isecVA = isec->getVA(); - // Tally the non-stub functions which still have call sites - // remaining to process, which yields the maximum number - // of thunks we might yet place. +// This is called exactly once, when the last input section has been finalized. +uint64_t TextOutputSection::estimateStubsInRangeVA(size_t callIdx) const { + // Tally the functions which still have call sites remaining to process, + // which yields the maximum number of thunks we might yet place. size_t maxPotentialThunks = 0; for (auto &tp : thunkMap) { ThunkInfo &ti = tp.second; - maxPotentialThunks += - !tp.first->isInStubs() && ti.callSitesUsed < ti.callSiteCount; + // This overcounts: Only sections that are in forward jump range from the + // currently-active section get finalized, and all input sections are + // finalized when estimateStubsInRangeVA() is called. So only backward + // jumps will need thunks, but we count all jumps. + if (ti.callSitesUsed < ti.callSiteCount) + maxPotentialThunks += 1; } // Tally the total size of input sections remaining to process. - uint64_t isecEnd = isec->getVA(); - for (size_t i = callIdx; i < endIdx; i++) { + uint64_t isecVA = inputs[callIdx]->getVA(); + uint64_t isecEnd = isecVA; + for (size_t i = callIdx; i < inputs.size(); i++) { InputSection *isec = inputs[i]; isecEnd = alignTo(isecEnd, isec->align) + isec->getSize(); } // Estimate the address after which call sites can safely call stubs // directly rather than through intermediary thunks. + uint64_t forwardBranchRange = target->forwardBranchRange; + assert(isecEnd > forwardBranchRange && + "should not run thunk insertion if all code fits in jump range"); + assert(isecEnd - isecVA <= forwardBranchRange && + "should only finalize sections in jump range"); uint64_t stubsInRangeVA = isecEnd + maxPotentialThunks * target->thunkSize + - in.stubs->getSize() - branchRange; + in.stubs->getSize() - forwardBranchRange; log("thunks = " + std::to_string(thunkMap.size()) + ", potential = " + std::to_string(maxPotentialThunks) + ", stubs = " + std::to_string(in.stubs->getSize()) + ", isecVA = " + - to_hexString(isecVA) + ", threshold = " + to_hexString(stubsInRangeVA) + - ", isecEnd = " + to_hexString(isecEnd) + - ", tail = " + to_hexString(isecEnd - isecVA) + - ", slop = " + to_hexString(branchRange - (isecEnd - isecVA))); + utohexstr(isecVA) + ", threshold = " + utohexstr(stubsInRangeVA) + + ", isecEnd = " + utohexstr(isecEnd) + + ", tail = " + utohexstr(isecEnd - isecVA) + + ", slop = " + utohexstr(forwardBranchRange - (isecEnd - isecVA))); return stubsInRangeVA; } -void ConcatOutputSection::finalize() { - uint64_t isecAddr = addr; - uint64_t isecFileOff = fileOff; - auto finalizeOne = [&](ConcatInputSection *isec) { - isecAddr = alignTo(isecAddr, isec->align); - isecFileOff = alignTo(isecFileOff, isec->align); - isec->outSecOff = isecAddr - addr; - isec->isFinal = true; - isecAddr += isec->getSize(); - isecFileOff += isec->getFileSize(); - }; +void ConcatOutputSection::finalizeOne(ConcatInputSection *isec) { + size = alignTo(size, isec->align); + fileSize = alignTo(fileSize, isec->align); + isec->outSecOff = size; + isec->isFinal = true; + size += isec->getSize(); + fileSize += isec->getFileSize(); +} + +void ConcatOutputSection::finalizeContents() { + for (ConcatInputSection *isec : inputs) + finalizeOne(isec); +} +void TextOutputSection::finalize() { if (!needsThunks()) { for (ConcatInputSection *isec : inputs) finalizeOne(isec); - size = isecAddr - addr; - fileSize = isecFileOff - fileOff; return; } - uint64_t branchRange = target->branchRange; + uint64_t forwardBranchRange = target->forwardBranchRange; + uint64_t backwardBranchRange = target->backwardBranchRange; uint64_t stubsInRangeVA = TargetInfo::outOfRangeVA; size_t thunkSize = target->thunkSize; size_t relocCount = 0; @@ -214,6 +223,11 @@ void ConcatOutputSection::finalize() { size_t thunkCallCount = 0; size_t thunkCount = 0; + // Walk all sections in order. Finalize all sections that are less than + // forwardBranchRange in front of it. + // isecVA is the address of the current section. + // addr + size is the start address of the first non-finalized section. + // inputs[finalIdx] is for finalization (address-assignment) size_t finalIdx = 0; // Kick-off by ensuring that the first input section has an address @@ -224,12 +238,22 @@ void ConcatOutputSection::finalize() { ConcatInputSection *isec = inputs[callIdx]; assert(isec->isFinal); uint64_t isecVA = isec->getVA(); - // Assign addresses up-to the forward branch-range limit - while (finalIdx < endIdx && - isecAddr + inputs[finalIdx]->getSize() < isecVA + branchRange) + + // Assign addresses up-to the forward branch-range limit. + // Every call instruction needs a small number of bytes (on Arm64: 4), + // and each inserted thunk needs a slightly larger number of bytes + // (on Arm64: 12). If a section starts with a branch instruction and + // contains several branch instructions in succession, then the distance + // from the current position to the position where the thunks are inserted + // grows. So leave room for a bunch of thunks. + unsigned slop = 1024 * thunkSize; + while (finalIdx < endIdx && addr + size + inputs[finalIdx]->getSize() < + isecVA + forwardBranchRange - slop) finalizeOne(inputs[finalIdx++]); - if (isec->callSiteCount == 0) + + if (!isec->hasCallSites) continue; + if (finalIdx == endIdx && stubsInRangeVA == TargetInfo::outOfRangeVA) { // When we have finalized all input sections, __stubs (destined // to follow __text) comes within range of forward branches and @@ -254,20 +278,22 @@ void ConcatOutputSection::finalize() { ++callSiteCount; // Calculate branch reachability boundaries uint64_t callVA = isecVA + r.offset; - uint64_t lowVA = branchRange < callVA ? callVA - branchRange : 0; - uint64_t highVA = callVA + branchRange; + uint64_t lowVA = + backwardBranchRange < callVA ? callVA - backwardBranchRange : 0; + uint64_t highVA = callVA + forwardBranchRange; // Calculate our call referent address auto *funcSym = r.referent.get(); ThunkInfo &thunkInfo = thunkMap[funcSym]; // The referent is not reachable, so we need to use a thunk ... if (funcSym->isInStubs() && callVA >= stubsInRangeVA) { + assert(callVA != TargetInfo::outOfRangeVA); // ... Oh, wait! We are close enough to the end that __stubs // are now within range of a simple forward branch. continue; } uint64_t funcVA = funcSym->resolveBranchVA(); ++thunkInfo.callSitesUsed; - if (lowVA < funcVA && funcVA < highVA) { + if (lowVA <= funcVA && funcVA <= highVA) { // The referent is reachable with a simple call instruction. continue; } @@ -276,40 +302,51 @@ void ConcatOutputSection::finalize() { // If an existing thunk is reachable, use it ... if (thunkInfo.sym) { uint64_t thunkVA = thunkInfo.isec->getVA(); - if (lowVA < thunkVA && thunkVA < highVA) { + if (lowVA <= thunkVA && thunkVA <= highVA) { r.referent = thunkInfo.sym; continue; } } - // ... otherwise, create a new thunk - if (isecAddr > highVA) { - // When there is small-to-no margin between highVA and - // isecAddr and the distance between subsequent call sites is - // smaller than thunkSize, then a new thunk can go out of - // range. Fix by unfinalizing inputs[finalIdx] to reduce the - // distance between callVA and highVA, then shift some thunks - // to occupy address-space formerly occupied by the - // unfinalized inputs[finalIdx]. + // ... otherwise, create a new thunk. + if (addr + size > highVA) { + // There were too many consecutive branch instructions for `slop` + // above. If you hit this: For the current algorithm, just bumping up + // slop above and trying again is probably simplest. (See also PR51578 + // comment 5). fatal(Twine(__FUNCTION__) + ": FIXME: thunk range overrun"); } thunkInfo.isec = - make(isec->getSegName(), isec->getName()); + makeSyntheticInputSection(isec->getSegName(), isec->getName()); thunkInfo.isec->parent = this; - StringRef thunkName = saver.save(funcSym->getName() + ".thunk." + - std::to_string(thunkInfo.sequence++)); - r.referent = thunkInfo.sym = symtab->addDefined( - thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, - /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true, - /*isThumb=*/false, /*isReferencedDynamically=*/false, - /*noDeadStrip=*/false); + + // This code runs after dead code removal. Need to set the `live` bit + // on the thunk isec so that asserts that check that only live sections + // get written are happy. + thunkInfo.isec->live = true; + + StringRef thunkName = saver().save(funcSym->getName() + ".thunk." + + std::to_string(thunkInfo.sequence++)); + if (!isa(funcSym) || cast(funcSym)->isExternal()) { + r.referent = thunkInfo.sym = symtab->addDefined( + thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, thunkSize, + /*isWeakDef=*/false, /*isPrivateExtern=*/true, + /*isThumb=*/false, /*isReferencedDynamically=*/false, + /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false); + } else { + r.referent = thunkInfo.sym = make( + thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, thunkSize, + /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/true, + /*includeInSymtab=*/true, /*isThumb=*/false, + /*isReferencedDynamically=*/false, /*noDeadStrip=*/false, + /*isWeakDefCanBeHidden=*/false); + } + thunkInfo.sym->used = true; target->populateThunk(thunkInfo.isec, funcSym); finalizeOne(thunkInfo.isec); thunks.push_back(thunkInfo.isec); ++thunkCount; } } - size = isecAddr - addr; - fileSize = isecFileOff - fileOff; log("thunks for " + parent->name + "," + name + ": funcs = " + std::to_string(thunkMap.size()) + @@ -320,11 +357,16 @@ void ConcatOutputSection::finalize() { } void ConcatOutputSection::writeTo(uint8_t *buf) const { + for (ConcatInputSection *isec : inputs) + isec->writeTo(buf + isec->outSecOff); +} + +void TextOutputSection::writeTo(uint8_t *buf) const { // Merge input sections from thunk & ordinary vectors size_t i = 0, ie = inputs.size(); size_t t = 0, te = thunks.size(); while (i < ie || t < te) { - while (i < ie && (t == te || inputs[i]->getSize() == 0 || + while (i < ie && (t == te || inputs[i]->empty() || inputs[i]->outSecOff < thunks[t]->outSecOff)) { inputs[i]->writeTo(buf + inputs[i]->outSecOff); ++i; @@ -364,8 +406,14 @@ ConcatOutputSection * ConcatOutputSection::getOrCreateForInput(const InputSection *isec) { NamePair names = maybeRenameSection({isec->getSegName(), isec->getName()}); ConcatOutputSection *&osec = concatOutputSections[names]; - if (!osec) - osec = make(names.second); + if (!osec) { + if (isec->getSegName() == segment_names::text && + isec->getName() != section_names::gccExceptTab && + isec->getName() != section_names::ehFrame) + osec = make(names.second); + else + osec = make(names.second); + } return osec; } diff --git a/gnu/llvm/lld/MachO/ConcatOutputSection.h b/gnu/llvm/lld/MachO/ConcatOutputSection.h index ec3d6bfbc34..9af661d0ab1 100644 --- a/gnu/llvm/lld/MachO/ConcatOutputSection.h +++ b/gnu/llvm/lld/MachO/ConcatOutputSection.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLD_MACHO_MERGED_OUTPUT_SECTION_H -#define LLD_MACHO_MERGED_OUTPUT_SECTION_H +#ifndef LLD_MACHO_CONCAT_OUTPUT_SECTION_H +#define LLD_MACHO_CONCAT_OUTPUT_SECTION_H #include "InputSection.h" #include "OutputSection.h" @@ -15,8 +15,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" -namespace lld { -namespace macho { +namespace lld::macho { class Defined; @@ -24,7 +23,7 @@ class Defined; // files that are labeled with the same segment and section name. This class // contains all such sections and writes the data from each section sequentially // in the final binary. -class ConcatOutputSection final : public OutputSection { +class ConcatOutputSection : public OutputSection { public: explicit ConcatOutputSection(StringRef name) : OutputSection(ConcatKind, name) {} @@ -37,27 +36,46 @@ public: uint64_t getSize() const override { return size; } uint64_t getFileSize() const override { return fileSize; } - void addInput(ConcatInputSection *input); - void finalize() override; - bool needsThunks() const; - uint64_t estimateStubsInRangeVA(size_t callIdx) const; + // Assign values to InputSection::outSecOff. In contrast to TextOutputSection, + // which does this in its implementation of `finalize()`, we can do this + // without `finalize()`'s sequential guarantees detailed in the block comment + // of `OutputSection::finalize()`. + virtual void finalizeContents(); + void addInput(ConcatInputSection *input); void writeTo(uint8_t *buf) const override; - std::vector inputs; - std::vector thunks; - static bool classof(const OutputSection *sec) { return sec->kind() == ConcatKind; } static ConcatOutputSection *getOrCreateForInput(const InputSection *); -private: - void finalizeFlags(InputSection *input); + std::vector inputs; +protected: size_t size = 0; uint64_t fileSize = 0; + void finalizeOne(ConcatInputSection *); + +private: + void finalizeFlags(InputSection *input); +}; + +// ConcatOutputSections that contain code (text) require special handling to +// support thunk insertion. +class TextOutputSection : public ConcatOutputSection { +public: + explicit TextOutputSection(StringRef name) : ConcatOutputSection(name) {} + void finalizeContents() override {} + void finalize() override; + bool needsThunks() const; + void writeTo(uint8_t *buf) const override; + +private: + uint64_t estimateStubsInRangeVA(size_t callIdx) const; + + std::vector thunks; }; // We maintain one ThunkInfo per real function. @@ -90,7 +108,6 @@ extern llvm::MapVector concatOutputSections; extern llvm::DenseMap thunkMap; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Config.h b/gnu/llvm/lld/MachO/Config.h index 0f47015b760..220fb99d2e2 100644 --- a/gnu/llvm/lld/MachO/Config.h +++ b/gnu/llvm/lld/MachO/Config.h @@ -12,6 +12,8 @@ #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/MachO.h" @@ -27,8 +29,8 @@ namespace lld { namespace macho { +class InputSection; class Symbol; -struct SymbolPriorityEntry; using NamePair = std::pair; using SectionRenameMap = llvm::DenseMap; @@ -42,8 +44,8 @@ struct PlatformInfo { inline uint32_t encodeVersion(const llvm::VersionTuple &version) { return ((version.getMajor() << 020) | - (version.getMinor().getValueOr(0) << 010) | - version.getSubminor().getValueOr(0)); + (version.getMinor().value_or(0) << 010) | + version.getSubminor().value_or(0)); } enum class NamespaceKind { @@ -66,6 +68,11 @@ enum class ICFLevel { all, }; +enum class ObjCStubsMode { + fast, + small, +}; + struct SectionAlign { llvm::StringRef segName; llvm::StringRef sectName; @@ -93,6 +100,13 @@ public: bool match(llvm::StringRef symbolName) const; }; +enum class SymtabPresence { + All, + None, + SelectivelyIncluded, + SelectivelyExcluded, +}; + struct Configuration { Symbol *entry = nullptr; bool hasReexports = false; @@ -101,12 +115,11 @@ struct Configuration { bool archMultiple = false; bool exportDynamic = false; bool forceLoadObjC = false; - bool forceLoadSwift = false; + bool forceLoadSwift = false; // Only applies to LC_LINKER_OPTIONs. bool staticLink = false; bool implicitDylibs = false; bool isPic = false; bool headerPadMaxInstallNames = false; - bool ltoNewPassManager = LLVM_ENABLE_NEW_PASS_MANAGER; bool markDeadStrippableDylib = false; bool printDylibSearch = false; bool printEachFile = false; @@ -118,9 +131,19 @@ struct Configuration { bool emitBitcodeBundle = false; bool emitDataInCodeInfo = false; bool emitEncryptionInfo = false; + bool emitInitOffsets = false; + bool emitChainedFixups = false; + bool thinLTOEmitImportsFiles; + bool thinLTOEmitIndexFiles; + bool thinLTOIndexOnly; bool timeTraceEnabled = false; bool dataConst = false; - bool dedupLiterals = true; + bool dedupStrings = true; + bool deadStripDuplicates = false; + bool omitDebugInfo = false; + bool warnDylibInstallName = false; + bool ignoreOptimizationHints = false; + bool forceExactCpuSubtypeMatch = false; uint32_t headerPad; uint32_t dylibCompatibilityVersion = 0; uint32_t dylibCurrentVersion = 0; @@ -144,19 +167,32 @@ struct Configuration { uint32_t ltoo = 2; llvm::CachePruningPolicy thinLTOCachePolicy; llvm::StringRef thinLTOCacheDir; + llvm::StringRef thinLTOIndexOnlyArg; + std::pair thinLTOObjectSuffixReplace; + std::pair thinLTOPrefixReplace; bool deadStripDylibs = false; bool demangle = false; bool deadStrip = false; + bool errorForArchMismatch = false; + bool ignoreAutoLink = false; + // ld64 allows invalid auto link options as long as the link succeeds. LLD + // does not, but there are cases in the wild where the invalid linker options + // exist. This allows users to ignore the specific invalid options in the case + // they can't easily fix them. + llvm::StringSet<> ignoreAutoLinkOptions; + bool strictAutoLink = false; PlatformInfo platformInfo; + std::optional secondaryPlatformInfo; NamespaceKind namespaceKind = NamespaceKind::twolevel; UndefinedSymbolTreatment undefinedSymbolTreatment = UndefinedSymbolTreatment::error; ICFLevel icfLevel = ICFLevel::none; + ObjCStubsMode objcStubsMode = ObjCStubsMode::fast; llvm::MachO::HeaderFileType outputType; std::vector systemLibraryRoots; std::vector librarySearchPaths; std::vector frameworkSearchPaths; - std::vector runtimePaths; + llvm::SmallVector runtimePaths; std::vector astPaths; std::vector explicitUndefineds; llvm::StringSet<> explicitDynamicLookups; @@ -165,37 +201,37 @@ struct Configuration { std::vector sectionAlignments; std::vector segmentProtections; - llvm::DenseMap priorities; + bool callGraphProfileSort = false; + llvm::StringRef printSymbolOrder; + SectionRenameMap sectionRenameMap; SegmentRenameMap segmentRenameMap; + bool hasExplicitExports = false; SymbolPatterns exportedSymbols; SymbolPatterns unexportedSymbols; + SymbolPatterns whyLive; + + std::vector> aliasedSymbols; - bool zeroModTime = false; + SymtabPresence localSymbolsPresence = SymtabPresence::All; + SymbolPatterns localSymbolPatterns; + llvm::SmallVector mllvmOpts; + + bool zeroModTime = true; + + llvm::StringRef osoPrefix; + + std::vector dyldEnvs; llvm::MachO::Architecture arch() const { return platformInfo.target.Arch; } - llvm::MachO::PlatformKind platform() const { + llvm::MachO::PlatformType platform() const { return platformInfo.target.Platform; } }; -// The symbol with the highest priority should be ordered first in the output -// section (modulo input section contiguity constraints). Using priority -// (highest first) instead of order (lowest first) has the convenient property -// that the default-constructed zero priority -- for symbols/sections without a -// user-defined order -- naturally ends up putting them at the end of the -// output. -struct SymbolPriorityEntry { - // The priority given to a matching symbol, regardless of which object file - // it originated from. - size_t anyObjectFile = 0; - // The priority given to a matching symbol from a particular object file. - llvm::DenseMap objectFiles; -}; - -extern Configuration *config; +extern std::unique_ptr config; } // namespace macho } // namespace lld diff --git a/gnu/llvm/lld/MachO/Driver.cpp b/gnu/llvm/lld/MachO/Driver.cpp index a8c11b6994b..fe8540e1080 100644 --- a/gnu/llvm/lld/MachO/Driver.cpp +++ b/gnu/llvm/lld/MachO/Driver.cpp @@ -15,6 +15,7 @@ #include "ObjC.h" #include "OutputSection.h" #include "OutputSegment.h" +#include "SectionPriorities.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" @@ -23,6 +24,7 @@ #include "Writer.h" #include "lld/Common/Args.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" @@ -59,8 +61,8 @@ using namespace llvm::sys; using namespace lld; using namespace lld::macho; -Configuration *macho::config; -DependencyTracker *macho::depTracker; +std::unique_ptr macho::config; +std::unique_ptr macho::depTracker; static HeaderFileType getOutputType(const InputArgList &args) { // TODO: -r, -dylinker, -preload... @@ -80,19 +82,39 @@ static HeaderFileType getOutputType(const InputArgList &args) { } } -static Optional findLibrary(StringRef name) { - if (config->searchDylibsFirst) { - if (Optional path = findPathCombination( - "lib" + name, config->librarySearchPaths, {".tbd", ".dylib"})) - return path; +static DenseMap resolvedLibraries; +static std::optional findLibrary(StringRef name) { + CachedHashStringRef key(name); + auto entry = resolvedLibraries.find(key); + if (entry != resolvedLibraries.end()) + return entry->second; + + auto doFind = [&] { + if (config->searchDylibsFirst) { + if (std::optional path = findPathCombination( + "lib" + name, config->librarySearchPaths, {".tbd", ".dylib"})) + return path; + return findPathCombination("lib" + name, config->librarySearchPaths, + {".a"}); + } return findPathCombination("lib" + name, config->librarySearchPaths, - {".a"}); - } - return findPathCombination("lib" + name, config->librarySearchPaths, - {".tbd", ".dylib", ".a"}); + {".tbd", ".dylib", ".a"}); + }; + + std::optional path = doFind(); + if (path) + resolvedLibraries[key] = *path; + + return path; } -static Optional findFramework(StringRef name) { +static DenseMap resolvedFrameworks; +static std::optional findFramework(StringRef name) { + CachedHashStringRef key(name); + auto entry = resolvedFrameworks.find(key); + if (entry != resolvedFrameworks.end()) + return entry->second; + SmallString<260> symlink; StringRef suffix; std::tie(name, suffix) = name.split(","); @@ -108,13 +130,13 @@ static Optional findFramework(StringRef name) { // only append suffix if realpath() succeeds Twine suffixed = location + suffix; if (fs::exists(suffixed)) - return suffixed.str(); + return resolvedFrameworks[key] = saver().save(suffixed.str()); } // Suffix lookup failed, fall through to the no-suffix case. } - if (Optional path = resolveDylibPath(symlink)) - return path; + if (std::optional path = resolveDylibPath(symlink.str())) + return resolvedFrameworks[key] = *path; } return {}; } @@ -145,7 +167,7 @@ getSearchPaths(unsigned optionCode, InputArgList &args, path::append(buffer, path); // Do not warn about paths that are computed via the syslib roots if (fs::is_directory(buffer)) { - paths.push_back(saver.save(buffer.str())); + paths.push_back(saver().save(buffer.str())); found = true; } } @@ -163,7 +185,7 @@ getSearchPaths(unsigned optionCode, InputArgList &args, SmallString<261> buffer(root); path::append(buffer, path); if (fs::is_directory(buffer)) - paths.push_back(saver.save(buffer.str())); + paths.push_back(saver().save(buffer.str())); } } return paths; @@ -174,7 +196,7 @@ static std::vector getSystemLibraryRoots(InputArgList &args) { for (const Arg *arg : args.filtered(OPT_syslibroot)) roots.push_back(arg->getValue()); // NOTE: the final `-syslibroot` being `/` will ignore all roots - if (roots.size() && roots.back() == "/") + if (!roots.empty() && roots.back() == "/") roots.clear(); // NOTE: roots can never be empty - add an empty root to simplify the library // and framework search path computation. @@ -203,10 +225,12 @@ static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) { val.toVector(ltoPolicy); }; for (const Arg *arg : - args.filtered(OPT_thinlto_cache_policy, OPT_prune_interval_lto, + args.filtered(OPT_thinlto_cache_policy_eq, OPT_prune_interval_lto, OPT_prune_after_lto, OPT_max_relative_cache_size_lto)) { switch (arg->getOption().getID()) { - case OPT_thinlto_cache_policy: add(arg->getValue()); break; + case OPT_thinlto_cache_policy_eq: + add(arg->getValue()); + break; case OPT_prune_interval_lto: if (!strcmp("-1", arg->getValue())) add("prune_interval=87600h"); // 10 years @@ -224,54 +248,28 @@ static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) { return CHECK(parseCachePruningPolicy(ltoPolicy), "invalid LTO cache policy"); } -namespace { -struct ArchiveMember { - MemoryBufferRef mbref; - uint32_t modTime; - uint64_t offsetInArchive; +// What caused a given library to be loaded. Only relevant for archives. +// Note that this does not tell us *how* we should load the library, i.e. +// whether we should do it lazily or eagerly (AKA force loading). The "how" is +// decided within addFile(). +enum class LoadType { + CommandLine, // Library was passed as a regular CLI argument + CommandLineForce, // Library was passed via `-force_load` + LCLinkerOption, // Library was passed via LC_LINKER_OPTIONS +}; + +struct ArchiveFileInfo { + ArchiveFile *file; + bool isCommandLineLoad; }; -} // namespace - -// Returns slices of MB by parsing MB as an archive file. -// Each slice consists of a member file in the archive. -static std::vector getArchiveMembers(MemoryBufferRef mb) { - std::unique_ptr file = - CHECK(Archive::create(mb), - mb.getBufferIdentifier() + ": failed to parse archive"); - Archive *archive = file.get(); - make>(std::move(file)); // take ownership - - std::vector v; - Error err = Error::success(); - - // Thin archives refer to .o files, so --reproduce needs the .o files too. - bool addToTar = archive->isThin() && tar; - - for (const Archive::Child &c : archive->children(err)) { - 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()); - uint32_t modTime = toTimeT( - CHECK(c.getLastModified(), mb.getBufferIdentifier() + - ": could not get the modification " - "time for a child of the archive")); - v.push_back({mbref, modTime, c.getChildOffset()}); - } - if (err) - fatal(mb.getBufferIdentifier() + - ": Archive::children failed: " + toString(std::move(err))); - - return v; -} -static DenseMap loadedArchives; +static DenseMap loadedArchives; -static InputFile *addFile(StringRef path, bool forceLoadArchive, - bool isExplicit = true, bool isBundleLoader = false) { - Optional buffer = readFile(path); +static InputFile *addFile(StringRef path, LoadType loadType, + bool isLazy = false, bool isExplicit = true, + bool isBundleLoader = false, + bool isForceHidden = false) { + std::optional buffer = readFile(path); if (!buffer) return nullptr; MemoryBufferRef mbref = *buffer; @@ -280,73 +278,101 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive, file_magic magic = identify_magic(mbref.getBuffer()); switch (magic) { case file_magic::archive: { + bool isCommandLineLoad = loadType != LoadType::LCLinkerOption; // Avoid loading archives twice. If the archives are being force-loaded, // loading them twice would create duplicate symbol errors. In the // non-force-loading case, this is just a minor performance optimization. // We don't take a reference to cachedFile here because the // loadArchiveMember() call below may recursively call addFile() and // invalidate this reference. - if (ArchiveFile *cachedFile = loadedArchives[path]) - return cachedFile; - - std::unique_ptr file = CHECK( - object::Archive::create(mbref), path + ": failed to parse archive"); - - if (!file->isEmpty() && !file->hasSymbolTable()) - error(path + ": archive has no index; run ranlib to add one"); - - if (config->allLoad || forceLoadArchive) { - if (Optional buffer = readFile(path)) { - for (const ArchiveMember &member : getArchiveMembers(*buffer)) { - if (Optional file = loadArchiveMember( - member.mbref, member.modTime, path, /*objCOnly=*/false, - member.offsetInArchive)) { - inputFiles.insert(*file); - printArchiveMemberLoad( - (forceLoadArchive ? "-force_load" : "-all_load"), - inputFiles.back()); + auto entry = loadedArchives.find(path); + + ArchiveFile *file; + if (entry == loadedArchives.end()) { + // No cached archive, we need to create a new one + std::unique_ptr archive = CHECK( + object::Archive::create(mbref), path + ": failed to parse archive"); + + if (!archive->isEmpty() && !archive->hasSymbolTable()) + error(path + ": archive has no index; run ranlib to add one"); + file = make(std::move(archive), isForceHidden); + } else { + file = entry->second.file; + // Command-line loads take precedence. If file is previously loaded via + // command line, or is loaded via LC_LINKER_OPTION and being loaded via + // LC_LINKER_OPTION again, using the cached archive is enough. + if (entry->second.isCommandLineLoad || !isCommandLineLoad) + return file; + } + + bool isLCLinkerForceLoad = loadType == LoadType::LCLinkerOption && + config->forceLoadSwift && + path::filename(path).startswith("libswift"); + if ((isCommandLineLoad && config->allLoad) || + loadType == LoadType::CommandLineForce || isLCLinkerForceLoad) { + if (std::optional buffer = readFile(path)) { + Error e = Error::success(); + for (const object::Archive::Child &c : file->getArchive().children(e)) { + StringRef reason; + switch (loadType) { + case LoadType::LCLinkerOption: + reason = "LC_LINKER_OPTION"; + break; + case LoadType::CommandLineForce: + reason = "-force_load"; + break; + case LoadType::CommandLine: + reason = "-all_load"; + break; } + if (Error e = file->fetch(c, reason)) + error(toString(file) + ": " + reason + + " failed to load archive member: " + toString(std::move(e))); } + if (e) + error(toString(file) + + ": Archive::children failed: " + toString(std::move(e))); } - } else if (config->forceLoadObjC) { - for (const object::Archive::Symbol &sym : file->symbols()) + } else if (isCommandLineLoad && config->forceLoadObjC) { + for (const object::Archive::Symbol &sym : file->getArchive().symbols()) if (sym.getName().startswith(objc::klass)) - symtab->addUndefined(sym.getName(), /*file=*/nullptr, - /*isWeakRef=*/false); + file->fetch(sym); // TODO: no need to look for ObjC sections for a given archive member if - // we already found that it contains an ObjC symbol. We should also - // consider creating a LazyObjFile class in order to avoid double-loading - // these files here and below (as part of the ArchiveFile). - if (Optional buffer = readFile(path)) { - for (const ArchiveMember &member : getArchiveMembers(*buffer)) { - if (Optional file = loadArchiveMember( - member.mbref, member.modTime, path, /*objCOnly=*/true, - member.offsetInArchive)) { - inputFiles.insert(*file); - printArchiveMemberLoad("-ObjC", inputFiles.back()); - } + // we already found that it contains an ObjC symbol. + if (std::optional buffer = readFile(path)) { + Error e = Error::success(); + for (const object::Archive::Child &c : file->getArchive().children(e)) { + Expected mb = c.getMemoryBufferRef(); + if (!mb || !hasObjCSection(*mb)) + continue; + if (Error e = file->fetch(c, "-ObjC")) + error(toString(file) + ": -ObjC failed to load archive member: " + + toString(std::move(e))); } + if (e) + error(toString(file) + + ": Archive::children failed: " + toString(std::move(e))); } } - newFile = loadedArchives[path] = make(std::move(file)); + file->addLazySymbols(); + loadedArchives[path] = ArchiveFileInfo{file, isCommandLineLoad}; + newFile = file; break; } case file_magic::macho_object: - newFile = make(mbref, getModTime(path), ""); + newFile = make(mbref, getModTime(path), "", isLazy); break; case file_magic::macho_dynamically_linked_shared_lib: case file_magic::macho_dynamically_linked_shared_lib_stub: case file_magic::tapi_file: - if (DylibFile *dylibFile = loadDylib(mbref)) { - if (isExplicit) - dylibFile->explicitlyLinked = true; + if (DylibFile *dylibFile = + loadDylib(mbref, nullptr, /*isBundleLoader=*/false, isExplicit)) newFile = dylibFile; - } break; case file_magic::bitcode: - newFile = make(mbref, "", 0); + newFile = make(mbref, "", 0, isLazy); break; case file_magic::macho_executable: case file_magic::macho_bundle: @@ -361,20 +387,34 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive, error(path + ": unhandled file type"); } if (newFile && !isa(newFile)) { + if ((isa(newFile) || isa(newFile)) && newFile->lazy && + config->forceLoadObjC) { + for (Symbol *sym : newFile->symbols) + if (sym && sym->getName().startswith(objc::klass)) { + extract(*newFile, "-ObjC"); + break; + } + if (newFile->lazy && hasObjCSection(mbref)) + extract(*newFile, "-ObjC"); + } + // printArchiveMemberLoad() prints both .a and .o names, so no need to - // print the .a name here. - if (config->printEachFile && magic != file_magic::archive) + // print the .a name here. Similarly skip lazy files. + if (config->printEachFile && magic != file_magic::archive && !isLazy) message(toString(newFile)); inputFiles.insert(newFile); } return newFile; } +static std::vector missingAutolinkWarnings; static void addLibrary(StringRef name, bool isNeeded, bool isWeak, - bool isReexport, bool isExplicit, bool forceLoad) { - if (Optional path = findLibrary(name)) { + bool isReexport, bool isHidden, bool isExplicit, + LoadType loadType, InputFile *originFile = nullptr) { + if (std::optional path = findLibrary(name)) { if (auto *dylibFile = dyn_cast_or_null( - addFile(*path, forceLoad, isExplicit))) { + addFile(*path, loadType, /*isLazy=*/false, isExplicit, + /*isBundleLoader=*/false, isHidden))) { if (isNeeded) dylibFile->forceNeeded = true; if (isWeak) @@ -386,14 +426,27 @@ static void addLibrary(StringRef name, bool isNeeded, bool isWeak, } return; } + if (loadType == LoadType::LCLinkerOption) { + assert(originFile); + missingAutolinkWarnings.push_back( + saver().save(toString(originFile) + + ": auto-linked library not found for -l" + name)); + return; + } error("library not found for -l" + name); } +static DenseSet loadedObjectFrameworks; static void addFramework(StringRef name, bool isNeeded, bool isWeak, - bool isReexport, bool isExplicit) { - if (Optional path = findFramework(name)) { - if (auto *dylibFile = dyn_cast_or_null( - addFile(*path, /*forceLoadArchive=*/false, isExplicit))) { + bool isReexport, bool isExplicit, LoadType loadType, + InputFile *originFile = nullptr) { + if (std::optional path = findFramework(name)) { + if (loadedObjectFrameworks.contains(*path)) + return; + + InputFile *file = + addFile(*path, loadType, /*isLazy=*/false, isExplicit, false); + if (auto *dylibFile = dyn_cast_or_null(file)) { if (isNeeded) dylibFile->forceNeeded = true; if (isWeak) @@ -402,16 +455,35 @@ static void addFramework(StringRef name, bool isNeeded, bool isWeak, config->hasReexports = true; dylibFile->reexport = true; } + } else if (isa_and_nonnull(file) || + isa_and_nonnull(file)) { + // Cache frameworks containing object or bitcode files to avoid duplicate + // symbols. Frameworks containing static archives are cached separately + // in addFile() to share caching with libraries, and frameworks + // containing dylibs should allow overwriting of attributes such as + // forceNeeded by subsequent loads + loadedObjectFrameworks.insert(*path); } return; } + if (loadType == LoadType::LCLinkerOption) { + assert(originFile); + missingAutolinkWarnings.push_back(saver().save( + toString(originFile) + + ": auto-linked framework not found for -framework " + name)); + return; + } error("framework not found for -framework " + name); } // Parses LC_LINKER_OPTION contents, which can add additional command line -// flags. +// flags. This directly parses the flags instead of using the standard argument +// parser to improve performance. void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) { - SmallVector argv; + if (config->ignoreAutoLink) + return; + + SmallVector argv; size_t offset = 0; for (unsigned i = 0; i < argc && offset < data.size(); ++i) { argv.push_back(data.data() + offset); @@ -420,109 +492,33 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) { if (argv.size() != argc || offset > data.size()) fatal(toString(f) + ": invalid LC_LINKER_OPTION"); - MachOOptTable table; - unsigned missingIndex, missingCount; - InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); - if (missingCount) - fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); - for (const Arg *arg : args.filtered(OPT_UNKNOWN)) - error("unknown argument: " + arg->getAsString(args)); - - for (const Arg *arg : args) { - switch (arg->getOption().getID()) { - case OPT_l: { - StringRef name = arg->getValue(); - bool forceLoad = - config->forceLoadSwift ? name.startswith("swift") : false; - addLibrary(name, /*isNeeded=*/false, /*isWeak=*/false, - /*isReexport=*/false, /*isExplicit=*/false, forceLoad); - break; - } - case OPT_framework: - addFramework(arg->getValue(), /*isNeeded=*/false, /*isWeak=*/false, - /*isReexport=*/false, /*isExplicit=*/false); - break; - default: - error(arg->getSpelling() + " is not allowed in LC_LINKER_OPTION"); - } + unsigned i = 0; + StringRef arg = argv[i]; + if (arg.consume_front("-l")) { + if (config->ignoreAutoLinkOptions.contains(arg)) + return; + addLibrary(arg, /*isNeeded=*/false, /*isWeak=*/false, + /*isReexport=*/false, /*isHidden=*/false, /*isExplicit=*/false, + LoadType::LCLinkerOption, f); + } else if (arg == "-framework") { + StringRef name = argv[++i]; + if (config->ignoreAutoLinkOptions.contains(name)) + return; + addFramework(name, /*isNeeded=*/false, /*isWeak=*/false, + /*isReexport=*/false, /*isExplicit=*/false, + LoadType::LCLinkerOption, f); + } else { + error(arg + " is not allowed in LC_LINKER_OPTION"); } } -static void addFileList(StringRef path) { - Optional buffer = readFile(path); +static void addFileList(StringRef path, bool isLazy) { + std::optional buffer = readFile(path); if (!buffer) return; MemoryBufferRef mbref = *buffer; for (StringRef path : args::getLines(mbref)) - addFile(rerootPath(path), /*forceLoadArchive=*/false); -} - -// An order file has one entry per line, in the following format: -// -// :: -// -// and are optional. If not specified, then that entry -// matches any symbol of that name. Parsing this format is not quite -// straightforward because the symbol name itself can contain colons, so when -// encountering a colon, we consider the preceding characters to decide if it -// can be a valid CPU type or file path. -// -// If a symbol is matched by multiple entries, then it takes the lowest-ordered -// entry (the one nearest to the front of the list.) -// -// The file can also have line comments that start with '#'. -static void parseOrderFile(StringRef path) { - Optional buffer = readFile(path); - if (!buffer) { - error("Could not read order file at " + path); - return; - } - - MemoryBufferRef mbref = *buffer; - size_t priority = std::numeric_limits::max(); - for (StringRef line : args::getLines(mbref)) { - StringRef objectFile, symbol; - line = line.take_until([](char c) { return c == '#'; }); // ignore comments - line = line.ltrim(); - - CPUType cpuType = StringSwitch(line) - .StartsWith("i386:", CPU_TYPE_I386) - .StartsWith("x86_64:", CPU_TYPE_X86_64) - .StartsWith("arm:", CPU_TYPE_ARM) - .StartsWith("arm64:", CPU_TYPE_ARM64) - .StartsWith("ppc:", CPU_TYPE_POWERPC) - .StartsWith("ppc64:", CPU_TYPE_POWERPC64) - .Default(CPU_TYPE_ANY); - - if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType) - continue; - - // Drop the CPU type as well as the colon - if (cpuType != CPU_TYPE_ANY) - line = line.drop_until([](char c) { return c == ':'; }).drop_front(); - - constexpr std::array fileEnds = {".o:", ".o):"}; - for (StringRef fileEnd : fileEnds) { - size_t pos = line.find(fileEnd); - if (pos != StringRef::npos) { - // Split the string around the colon - objectFile = line.take_front(pos + fileEnd.size() - 1); - line = line.drop_front(pos + fileEnd.size()); - break; - } - } - symbol = line.trim(); - - if (!symbol.empty()) { - SymbolPriorityEntry &entry = config->priorities[symbol]; - if (!objectFile.empty()) - entry.objectFiles.insert(std::make_pair(objectFile, priority)); - else - entry.anyObjectFile = std::max(entry.anyObjectFile, priority); - } - - --priority; - } + addFile(rerootPath(path), LoadType::CommandLine, isLazy); } // We expect sub-library names of the form "libfoo", which will match a dylib @@ -534,8 +530,7 @@ static bool markReexport(StringRef searchName, ArrayRef extensions) { if (auto *dylibFile = dyn_cast(file)) { StringRef filename = path::filename(dylibFile->getName()); if (filename.consume_front(searchName) && - (filename.empty() || - find(extensions, filename) != extensions.end())) { + (filename.empty() || llvm::is_contained(extensions, filename))) { dylibFile->reexport = true; return true; } @@ -555,23 +550,19 @@ static void initLLVM() { InitializeAllAsmParsers(); } -static void compileBitcodeFiles() { - // FIXME: Remove this once LTO.cpp honors config->exportDynamic. - if (config->exportDynamic) - for (InputFile *file : inputFiles) - if (isa(file)) { - warn("the effect of -export_dynamic on LTO is not yet implemented"); - break; - } - +static bool compileBitcodeFiles() { TimeTraceScope timeScope("LTO"); auto *lto = make(); for (InputFile *file : inputFiles) if (auto *bitcodeFile = dyn_cast(file)) - lto->add(*bitcodeFile); + if (!file->lazy) + lto->add(*bitcodeFile); - for (ObjFile *file : lto->compile()) + std::vector compiled = lto->compile(); + for (ObjFile *file : compiled) inputFiles.insert(file); + + return !compiled.empty(); } // Replaces common symbols with defined symbols residing in __common sections. @@ -590,9 +581,11 @@ static void replaceCommonSymbols() { // but it's not really worth supporting the linking of 64-bit programs on // 32-bit archs. ArrayRef data = {nullptr, static_cast(common->size)}; - auto *isec = make( - segment_names::data, section_names::common, common->getFile(), data, - common->align, S_ZEROFILL); + // FIXME avoid creating one Section per symbol? + auto *section = + make
(common->getFile(), segment_names::data, + section_names::common, S_ZEROFILL, /*addr=*/0); + auto *isec = make(*section, data, common->align); if (!osec) osec = ConcatOutputSection::getOrCreateForInput(isec); isec->parent = osec; @@ -600,14 +593,11 @@ static void replaceCommonSymbols() { // FIXME: CommonSymbol should store isReferencedDynamically, noDeadStrip // and pass them on here. - replaceSymbol(sym, sym->getName(), isec->getFile(), isec, - /*value=*/0, - /*size=*/0, - /*isWeakDef=*/false, - /*isExternal=*/true, common->privateExtern, - /*isThumb=*/false, - /*isReferencedDynamically=*/false, - /*noDeadStrip=*/false); + replaceSymbol( + sym, sym->getName(), common->getFile(), isec, /*value=*/0, common->size, + /*isWeakDef=*/false, /*isExternal=*/true, common->privateExtern, + /*includeInSymtab=*/true, /*isThumb=*/false, + /*isReferencedDynamically=*/false, /*noDeadStrip=*/false); } } @@ -626,7 +616,7 @@ static void initializeSectionRenameMap() { section_names::objcCatList, section_names::objcNonLazyCatList, section_names::objcProtoList, - section_names::objcImageInfo}; + section_names::objCImageInfo}; for (StringRef s : v) config->sectionRenameMap[{segment_names::data, s}] = { segment_names::dataConst, s}; @@ -651,59 +641,114 @@ static std::string lowerDash(StringRef s) { map_iterator(s.end(), toLowerDash)); } -// Has the side-effect of setting Config::platformInfo. -static PlatformKind parsePlatformVersion(const ArgList &args) { - const Arg *arg = args.getLastArg(OPT_platform_version); - if (!arg) { - error("must specify -platform_version"); - return PlatformKind::unknown; - } +struct PlatformVersion { + PlatformType platform = PLATFORM_UNKNOWN; + llvm::VersionTuple minimum; + llvm::VersionTuple sdk; +}; +static PlatformVersion parsePlatformVersion(const Arg *arg) { + assert(arg->getOption().getID() == OPT_platform_version); StringRef platformStr = arg->getValue(0); StringRef minVersionStr = arg->getValue(1); StringRef sdkVersionStr = arg->getValue(2); + PlatformVersion platformVersion; + // TODO(compnerd) see if we can generate this case list via XMACROS - PlatformKind platform = - StringSwitch(lowerDash(platformStr)) - .Cases("macos", "1", PlatformKind::macOS) - .Cases("ios", "2", PlatformKind::iOS) - .Cases("tvos", "3", PlatformKind::tvOS) - .Cases("watchos", "4", PlatformKind::watchOS) - .Cases("bridgeos", "5", PlatformKind::bridgeOS) - .Cases("mac-catalyst", "6", PlatformKind::macCatalyst) - .Cases("ios-simulator", "7", PlatformKind::iOSSimulator) - .Cases("tvos-simulator", "8", PlatformKind::tvOSSimulator) - .Cases("watchos-simulator", "9", PlatformKind::watchOSSimulator) - .Cases("driverkit", "10", PlatformKind::driverKit) - .Default(PlatformKind::unknown); - if (platform == PlatformKind::unknown) + platformVersion.platform = + StringSwitch(lowerDash(platformStr)) + .Cases("macos", "1", PLATFORM_MACOS) + .Cases("ios", "2", PLATFORM_IOS) + .Cases("tvos", "3", PLATFORM_TVOS) + .Cases("watchos", "4", PLATFORM_WATCHOS) + .Cases("bridgeos", "5", PLATFORM_BRIDGEOS) + .Cases("mac-catalyst", "6", PLATFORM_MACCATALYST) + .Cases("ios-simulator", "7", PLATFORM_IOSSIMULATOR) + .Cases("tvos-simulator", "8", PLATFORM_TVOSSIMULATOR) + .Cases("watchos-simulator", "9", PLATFORM_WATCHOSSIMULATOR) + .Cases("driverkit", "10", PLATFORM_DRIVERKIT) + .Default(PLATFORM_UNKNOWN); + if (platformVersion.platform == PLATFORM_UNKNOWN) error(Twine("malformed platform: ") + platformStr); // TODO: check validity of version strings, which varies by platform // NOTE: ld64 accepts version strings with 5 components // llvm::VersionTuple accepts no more than 4 components // Has Apple ever published version strings with 5 components? - if (config->platformInfo.minimum.tryParse(minVersionStr)) + if (platformVersion.minimum.tryParse(minVersionStr)) error(Twine("malformed minimum version: ") + minVersionStr); - if (config->platformInfo.sdk.tryParse(sdkVersionStr)) + if (platformVersion.sdk.tryParse(sdkVersionStr)) error(Twine("malformed sdk version: ") + sdkVersionStr); - return platform; + return platformVersion; +} + +// Has the side-effect of setting Config::platformInfo. +static PlatformType parsePlatformVersions(const ArgList &args) { + std::map platformVersions; + const PlatformVersion *lastVersionInfo = nullptr; + for (const Arg *arg : args.filtered(OPT_platform_version)) { + PlatformVersion version = parsePlatformVersion(arg); + + // For each platform, the last flag wins: + // `-platform_version macos 2 3 -platform_version macos 4 5` has the same + // effect as just passing `-platform_version macos 4 5`. + // FIXME: ld64 warns on multiple flags for one platform. Should we? + platformVersions[version.platform] = version; + lastVersionInfo = &platformVersions[version.platform]; + } + + if (platformVersions.empty()) { + error("must specify -platform_version"); + return PLATFORM_UNKNOWN; + } + if (platformVersions.size() > 2) { + error("must specify -platform_version at most twice"); + return PLATFORM_UNKNOWN; + } + if (platformVersions.size() == 2) { + bool isZipperedCatalyst = platformVersions.count(PLATFORM_MACOS) && + platformVersions.count(PLATFORM_MACCATALYST); + + if (!isZipperedCatalyst) { + error("lld supports writing zippered outputs only for " + "macos and mac-catalyst"); + } else if (config->outputType != MH_DYLIB && + config->outputType != MH_BUNDLE) { + error("writing zippered outputs only valid for -dylib and -bundle"); + } else { + config->platformInfo.minimum = platformVersions[PLATFORM_MACOS].minimum; + config->platformInfo.sdk = platformVersions[PLATFORM_MACOS].sdk; + config->secondaryPlatformInfo = PlatformInfo{}; + config->secondaryPlatformInfo->minimum = + platformVersions[PLATFORM_MACCATALYST].minimum; + config->secondaryPlatformInfo->sdk = + platformVersions[PLATFORM_MACCATALYST].sdk; + } + return PLATFORM_MACOS; + } + + config->platformInfo.minimum = lastVersionInfo->minimum; + config->platformInfo.sdk = lastVersionInfo->sdk; + return lastVersionInfo->platform; } // Has the side-effect of setting Config::target. static TargetInfo *createTargetInfo(InputArgList &args) { StringRef archName = args.getLastArgValue(OPT_arch); - if (archName.empty()) - fatal("must specify -arch"); - PlatformKind platform = parsePlatformVersion(args); + if (archName.empty()) { + error("must specify -arch"); + return nullptr; + } + PlatformType platform = parsePlatformVersions(args); config->platformInfo.target = MachO::Target(getArchitectureFromName(archName), platform); + if (config->secondaryPlatformInfo) { + config->secondaryPlatformInfo->target = + MachO::Target(getArchitectureFromName(archName), PLATFORM_MACCATALYST); + } - uint32_t cpuType; - uint32_t cpuSubtype; - std::tie(cpuType, cpuSubtype) = getCPUTypeFromArchitecture(config->arch()); - + auto [cpuType, cpuSubtype] = getCPUTypeFromArchitecture(config->arch()); switch (cpuType) { case CPU_TYPE_X86_64: return createX86_64TargetInfo(); @@ -714,7 +759,8 @@ static TargetInfo *createTargetInfo(InputArgList &args) { case CPU_TYPE_ARM: return createARMTargetInfo(cpuSubtype); default: - fatal("missing or unsupported -arch " + archName); + error("missing or unsupported -arch " + archName); + return nullptr; } } @@ -736,16 +782,15 @@ getUndefinedSymbolTreatment(const ArgList &args) { (treatment == UndefinedSymbolTreatment::warning || treatment == UndefinedSymbolTreatment::suppress)) { if (treatment == UndefinedSymbolTreatment::warning) - error("'-undefined warning' only valid with '-flat_namespace'"); + fatal("'-undefined warning' only valid with '-flat_namespace'"); else - error("'-undefined suppress' only valid with '-flat_namespace'"); + fatal("'-undefined suppress' only valid with '-flat_namespace'"); treatment = UndefinedSymbolTreatment::error; } return treatment; } static ICFLevel getICFLevel(const ArgList &args) { - bool noDeduplicate = args.hasArg(OPT_no_deduplicate); StringRef icfLevelStr = args.getLastArgValue(OPT_icf_eq); auto icfLevel = StringSwitch(icfLevelStr) .Cases("none", "", ICFLevel::none) @@ -756,17 +801,21 @@ static ICFLevel getICFLevel(const ArgList &args) { warn(Twine("unknown --icf=OPTION `") + icfLevelStr + "', defaulting to `none'"); icfLevel = ICFLevel::none; - } else if (icfLevel != ICFLevel::none && noDeduplicate) { - warn(Twine("`--icf=" + icfLevelStr + - "' conflicts with -no_deduplicate, setting to `none'")); - icfLevel = ICFLevel::none; - } else if (icfLevel == ICFLevel::safe) { - warn(Twine("`--icf=safe' is not yet implemented, reverting to `none'")); - icfLevel = ICFLevel::none; } return icfLevel; } +static ObjCStubsMode getObjCStubsMode(const ArgList &args) { + const Arg *arg = args.getLastArg(OPT_objc_stubs_fast, OPT_objc_stubs_small); + if (!arg) + return ObjCStubsMode::fast; + + if (arg->getOption().getID() == OPT_objc_stubs_small) + warn("-objc_stubs_small is not yet implemented, defaulting to " + "-objc_stubs_fast"); + return ObjCStubsMode::fast; +} + static void warnIfDeprecatedOption(const Option &opt) { if (!opt.getGroup().isValid()) return; @@ -794,6 +843,8 @@ static void warnIfUnimplementedOption(const Option &opt) { case OPT_grp_ignored: warn("Option `" + opt.getPrefixedName() + "' is ignored."); break; + case OPT_grp_ignored_silently: + break; default: warn("Option `" + opt.getPrefixedName() + "' is not yet implemented. Stay tuned..."); @@ -807,6 +858,20 @@ static const char *getReproduceOption(InputArgList &args) { return getenv("LLD_REPRODUCE"); } +// 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; +} + static void parseClangOption(StringRef opt, const Twine &msg) { std::string err; raw_string_ostream os(err); @@ -884,27 +949,41 @@ static std::vector parseSectAlign(const opt::InputArgList &args) { return sectAligns; } -PlatformKind macho::removeSimulator(PlatformKind platform) { +PlatformType macho::removeSimulator(PlatformType platform) { switch (platform) { - case PlatformKind::iOSSimulator: - return PlatformKind::iOS; - case PlatformKind::tvOSSimulator: - return PlatformKind::tvOS; - case PlatformKind::watchOSSimulator: - return PlatformKind::watchOS; + case PLATFORM_IOSSIMULATOR: + return PLATFORM_IOS; + case PLATFORM_TVOSSIMULATOR: + return PLATFORM_TVOS; + case PLATFORM_WATCHOSSIMULATOR: + return PLATFORM_WATCHOS; default: return platform; } } +static bool supportsNoPie() { + return !(config->arch() == AK_arm64 || config->arch() == AK_arm64e || + config->arch() == AK_arm64_32); +} + +static bool shouldAdhocSignByDefault(Architecture arch, PlatformType platform) { + if (arch != AK_arm64 && arch != AK_arm64e) + return false; + + return platform == PLATFORM_MACOS || platform == PLATFORM_IOSSIMULATOR || + platform == PLATFORM_TVOSSIMULATOR || + platform == PLATFORM_WATCHOSSIMULATOR; +} + static bool dataConstDefault(const InputArgList &args) { - static const std::vector> minVersion = { - {PlatformKind::macOS, VersionTuple(10, 15)}, - {PlatformKind::iOS, VersionTuple(13, 0)}, - {PlatformKind::tvOS, VersionTuple(13, 0)}, - {PlatformKind::watchOS, VersionTuple(6, 0)}, - {PlatformKind::bridgeOS, VersionTuple(4, 0)}}; - PlatformKind platform = removeSimulator(config->platformInfo.target.Platform); + static const std::array, 5> minVersion = + {{{PLATFORM_MACOS, VersionTuple(10, 15)}, + {PLATFORM_IOS, VersionTuple(13, 0)}, + {PLATFORM_TVOS, VersionTuple(13, 0)}, + {PLATFORM_WATCHOS, VersionTuple(6, 0)}, + {PLATFORM_BRIDGEOS, VersionTuple(4, 0)}}}; + PlatformType platform = removeSimulator(config->platformInfo.target.Platform); auto it = llvm::find_if(minVersion, [&](const auto &p) { return p.first == platform; }); if (it != minVersion.end()) @@ -913,7 +992,7 @@ static bool dataConstDefault(const InputArgList &args) { switch (config->outputType) { case MH_EXECUTE: - return !args.hasArg(OPT_no_pie); + return !(args.hasArg(OPT_no_pie) && supportsNoPie()); case MH_BUNDLE: // FIXME: return false when -final_name ... // has prefix "/System/Library/UserEventPlugins/" @@ -930,6 +1009,47 @@ static bool dataConstDefault(const InputArgList &args) { return false; } +static bool shouldEmitChainedFixups(const InputArgList &args) { + const Arg *arg = args.getLastArg(OPT_fixup_chains, OPT_no_fixup_chains); + if (arg && arg->getOption().matches(OPT_no_fixup_chains)) + return false; + + bool isRequested = arg != nullptr; + + // Version numbers taken from the Xcode 13.3 release notes. + static const std::array, 4> minVersion = + {{{PLATFORM_MACOS, VersionTuple(11, 0)}, + {PLATFORM_IOS, VersionTuple(13, 4)}, + {PLATFORM_TVOS, VersionTuple(14, 0)}, + {PLATFORM_WATCHOS, VersionTuple(7, 0)}}}; + PlatformType platform = removeSimulator(config->platformInfo.target.Platform); + auto it = llvm::find_if(minVersion, + [&](const auto &p) { return p.first == platform; }); + if (it != minVersion.end() && it->second > config->platformInfo.minimum) { + if (!isRequested) + return false; + + warn("-fixup_chains requires " + getPlatformName(config->platform()) + " " + + it->second.getAsString() + ", which is newer than target minimum of " + + config->platformInfo.minimum.getAsString()); + } + + if (!is_contained({AK_x86_64, AK_x86_64h, AK_arm64}, config->arch())) { + if (isRequested) + error("-fixup_chains is only supported on x86_64 and arm64 targets"); + return false; + } + + if (!config->isPic) { + if (isRequested) + error("-fixup_chains is incompatible with -no_pie"); + return false; + } + + // TODO: Enable by default once stable. + return isRequested; +} + void SymbolPatterns::clear() { literals.clear(); globs.clear(); @@ -959,32 +1079,37 @@ bool SymbolPatterns::match(StringRef symbolName) const { return matchLiteral(symbolName) || matchGlob(symbolName); } +static void parseSymbolPatternsFile(const Arg *arg, + SymbolPatterns &symbolPatterns) { + StringRef path = arg->getValue(); + std::optional buffer = readFile(path); + if (!buffer) { + error("Could not read symbol file: " + path); + return; + } + MemoryBufferRef mbref = *buffer; + for (StringRef line : args::getLines(mbref)) { + line = line.take_until([](char c) { return c == '#'; }).trim(); + if (!line.empty()) + symbolPatterns.insert(line); + } +} + static void handleSymbolPatterns(InputArgList &args, SymbolPatterns &symbolPatterns, unsigned singleOptionCode, unsigned listFileOptionCode) { for (const Arg *arg : args.filtered(singleOptionCode)) symbolPatterns.insert(arg->getValue()); - for (const Arg *arg : args.filtered(listFileOptionCode)) { - StringRef path = arg->getValue(); - Optional buffer = readFile(path); - if (!buffer) { - error("Could not read symbol file: " + path); - continue; - } - MemoryBufferRef mbref = *buffer; - for (StringRef line : args::getLines(mbref)) { - line = line.take_until([](char c) { return c == '#'; }).trim(); - if (!line.empty()) - symbolPatterns.insert(line); - } - } + for (const Arg *arg : args.filtered(listFileOptionCode)) + parseSymbolPatternsFile(arg, symbolPatterns); } -void createFiles(const InputArgList &args) { +static void createFiles(const InputArgList &args) { TimeTraceScope timeScope("Load input files"); // This loop should be reserved for options whose exact ordering matters. // Other options should be handled via filtered() and/or getLastArg(). + bool isLazy = false; for (const Arg *arg : args) { const Option &opt = arg->getOption(); warnIfDeprecatedOption(opt); @@ -992,38 +1117,45 @@ void createFiles(const InputArgList &args) { switch (opt.getID()) { case OPT_INPUT: - addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false); + addFile(rerootPath(arg->getValue()), LoadType::CommandLine, isLazy); break; case OPT_needed_library: if (auto *dylibFile = dyn_cast_or_null( - addFile(rerootPath(arg->getValue()), false))) + addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) dylibFile->forceNeeded = true; break; case OPT_reexport_library: - if (auto *dylibFile = dyn_cast_or_null(addFile( - rerootPath(arg->getValue()), /*forceLoadArchive=*/false))) { + if (auto *dylibFile = dyn_cast_or_null( + addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) { config->hasReexports = true; dylibFile->reexport = true; } break; case OPT_weak_library: if (auto *dylibFile = dyn_cast_or_null( - addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false))) + addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) dylibFile->forceWeakImport = true; break; case OPT_filelist: - addFileList(arg->getValue()); + addFileList(arg->getValue(), isLazy); break; case OPT_force_load: - addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/true); + addFile(rerootPath(arg->getValue()), LoadType::CommandLineForce); + break; + case OPT_load_hidden: + addFile(rerootPath(arg->getValue()), LoadType::CommandLine, + /*isLazy=*/false, /*isExplicit=*/true, /*isBundleLoader=*/false, + /*isForceHidden=*/true); break; case OPT_l: case OPT_needed_l: case OPT_reexport_l: case OPT_weak_l: + case OPT_hidden_l: addLibrary(arg->getValue(), opt.getID() == OPT_needed_l, opt.getID() == OPT_weak_l, opt.getID() == OPT_reexport_l, - /*isExplicit=*/true, /*forceLoad=*/false); + opt.getID() == OPT_hidden_l, + /*isExplicit=*/true, LoadType::CommandLine); break; case OPT_framework: case OPT_needed_framework: @@ -1031,7 +1163,18 @@ void createFiles(const InputArgList &args) { case OPT_weak_framework: addFramework(arg->getValue(), opt.getID() == OPT_needed_framework, opt.getID() == OPT_weak_framework, - opt.getID() == OPT_reexport_framework, /*isExplicit=*/true); + opt.getID() == OPT_reexport_framework, /*isExplicit=*/true, + LoadType::CommandLine); + break; + case OPT_start_lib: + if (isLazy) + error("nested --start-lib"); + isLazy = true; + break; + case OPT_end_lib: + if (!isLazy) + error("stray --end-lib"); + isLazy = false; break; default: break; @@ -1043,15 +1186,19 @@ static void gatherInputSections() { TimeTraceScope timeScope("Gathering input sections"); int inputOrder = 0; for (const InputFile *file : inputFiles) { - for (const SubsectionMap &map : file->subsections) { + for (const Section *section : file->sections) { + // Compact unwind entries require special handling elsewhere. (In + // contrast, EH frames are handled like regular ConcatInputSections.) + if (section->name == section_names::compactUnwind) + continue; ConcatOutputSection *osec = nullptr; - for (const SubsectionEntry &entry : map) { - if (auto *isec = dyn_cast(entry.isec)) { + for (const Subsection &subsection : section->subsections) { + if (auto *isec = dyn_cast(subsection.isec)) { if (isec->isCoalescedWeak()) continue; - if (isec->getSegName() == segment_names::ld) { - assert(isec->getName() == section_names::compactUnwind); - in.unwindInfo->addInput(isec); + if (config->emitInitOffsets && + sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS) { + in.initOffsets->addInput(isec); continue; } isec->outSecOff = inputOrder++; @@ -1059,11 +1206,19 @@ static void gatherInputSections() { osec = ConcatOutputSection::getOrCreateForInput(isec); isec->parent = osec; inputSections.push_back(isec); - } else if (auto *isec = dyn_cast(entry.isec)) { - if (in.cStringSection->inputOrder == UnspecifiedInputOrder) - in.cStringSection->inputOrder = inputOrder++; - in.cStringSection->addInput(isec); - } else if (auto *isec = dyn_cast(entry.isec)) { + } else if (auto *isec = + dyn_cast(subsection.isec)) { + if (isec->getName() == section_names::objcMethname) { + if (in.objcMethnameSection->inputOrder == UnspecifiedInputOrder) + in.objcMethnameSection->inputOrder = inputOrder++; + in.objcMethnameSection->addInput(isec); + } else { + if (in.cStringSection->inputOrder == UnspecifiedInputOrder) + in.cStringSection->inputOrder = inputOrder++; + in.cStringSection->addInput(isec); + } + } else if (auto *isec = + dyn_cast(subsection.isec)) { if (in.wordLiteralSection->inputOrder == UnspecifiedInputOrder) in.wordLiteralSection->inputOrder = inputOrder++; in.wordLiteralSection->addInput(isec); @@ -1072,17 +1227,48 @@ static void gatherInputSections() { } } } + if (!file->objCImageInfo.empty()) + in.objCImageInfo->addFile(file); } assert(inputOrder <= UnspecifiedInputOrder); } static void foldIdenticalLiterals() { + TimeTraceScope timeScope("Fold identical literals"); // We always create a cStringSection, regardless of whether dedupLiterals is // true. If it isn't, we simply create a non-deduplicating CStringSection. // Either way, we must unconditionally finalize it here. in.cStringSection->finalizeContents(); - if (in.wordLiteralSection) - in.wordLiteralSection->finalizeContents(); + in.objcMethnameSection->finalizeContents(); + in.wordLiteralSection->finalizeContents(); +} + +static void addSynthenticMethnames() { + std::string &data = *make(); + llvm::raw_string_ostream os(data); + const int prefixLength = ObjCStubsSection::symbolPrefix.size(); + for (Symbol *sym : symtab->getSymbols()) + if (isa(sym)) + if (sym->getName().startswith(ObjCStubsSection::symbolPrefix)) + os << sym->getName().drop_front(prefixLength) << '\0'; + + if (data.empty()) + return; + + const auto *buf = reinterpret_cast(data.c_str()); + Section §ion = *make
(/*file=*/nullptr, segment_names::text, + section_names::objcMethname, + S_CSTRING_LITERALS, /*addr=*/0); + + auto *isec = + make(section, ArrayRef{buf, data.size()}, + /*align=*/1, /*dedupLiterals=*/true); + isec->splitIntoPieces(); + for (auto &piece : isec->pieces) + piece.live = true; + section.subsections.push_back({0, isec}); + in.objcMethnameSection->addInput(isec); + in.objcMethnameSection->isec->markLive(0); } static void referenceStubBinder() { @@ -1097,29 +1283,124 @@ static void referenceStubBinder() { // dyld_stub_binder is in libSystem.dylib, which is usually linked in. This // isn't needed for correctness, but the presence of that symbol suppresses // "no symbols" diagnostics from `nm`. - // StubHelperSection::setup() adds a reference and errors out if + // StubHelperSection::setUp() adds a reference and errors out if // dyld_stub_binder doesn't exist in case it is actually needed. symtab->addUndefined("dyld_stub_binder", /*file=*/nullptr, /*isWeak=*/false); } -bool macho::link(ArrayRef argsArr, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; +static void createAliases() { + for (const auto &pair : config->aliasedSymbols) { + if (const auto &sym = symtab->find(pair.first)) { + if (const auto &defined = dyn_cast(sym)) { + symtab->aliasDefined(defined, pair.second, defined->getFile()) + ->noDeadStrip = true; + } else { + error("TODO: support aliasing to symbols of kind " + + Twine(sym->kind())); + } + } else { + warn("undefined base symbol '" + pair.first + "' for alias '" + + pair.second + "'\n"); + } + } + + for (const InputFile *file : inputFiles) { + if (auto *objFile = dyn_cast(file)) { + for (const AliasSymbol *alias : objFile->aliases) { + if (const auto &aliased = symtab->find(alias->getAliasedName())) { + if (const auto &defined = dyn_cast(aliased)) { + symtab->aliasDefined(defined, alias->getName(), alias->getFile(), + alias->privateExtern); + } else { + // Common, dylib, and undefined symbols are all valid alias + // referents (undefineds can become valid Defined symbols later on + // in the link.) + error("TODO: support aliasing to symbols of kind " + + Twine(aliased->kind())); + } + } else { + // This shouldn't happen since MC generates undefined symbols to + // represent the alias referents. Thus we fatal() instead of just + // warning here. + fatal("unable to find alias referent " + alias->getAliasedName() + + " for " + alias->getName()); + } + } + } + } +} + +static void handleExplicitExports() { + if (config->hasExplicitExports) { + parallelForEach(symtab->getSymbols(), [](Symbol *sym) { + if (auto *defined = dyn_cast(sym)) { + StringRef symbolName = defined->getName(); + if (config->exportedSymbols.match(symbolName)) { + if (defined->privateExtern) { + if (defined->weakDefCanBeHidden) { + // weak_def_can_be_hidden symbols behave similarly to + // private_extern symbols in most cases, except for when + // it is explicitly exported. + // The former can be exported but the latter cannot. + defined->privateExtern = false; + } else { + warn("cannot export hidden symbol " + toString(*defined) + + "\n>>> defined in " + toString(defined->getFile())); + } + } + } else { + defined->privateExtern = true; + } + } + }); + } else if (!config->unexportedSymbols.empty()) { + parallelForEach(symtab->getSymbols(), [](Symbol *sym) { + if (auto *defined = dyn_cast(sym)) + if (config->unexportedSymbols.match(defined->getName())) + defined->privateExtern = true; + }); + } +} - errorHandler().cleanupCallback = []() { freeArena(); }; +bool macho::link(ArrayRef argsArr, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, + bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new CommonLinkerContext; + + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.cleanupCallback = []() { + resolvedFrameworks.clear(); + resolvedLibraries.clear(); + cachedReads.clear(); + concatOutputSections.clear(); + inputFiles.clear(); + inputSections.clear(); + loadedArchives.clear(); + loadedObjectFrameworks.clear(); + missingAutolinkWarnings.clear(); + syntheticSections.clear(); + thunkMap.clear(); + + firstTLVDataSection = nullptr; + tar = nullptr; + memset(&in, 0, sizeof(in)); + + resetLoadedDylibs(); + resetOutputSegments(); + resetWriter(); + InputFile::resetIdCount(); + }; - errorHandler().logName = args::getFilenameWithoutExe(argsArr[0]); - stderrOS.enable_colors(stderrOS.has_colors()); + ctx->e.logName = args::getFilenameWithoutExe(argsArr[0]); MachOOptTable parser; InputArgList args = parser.parse(argsArr.slice(1)); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now " - "(use --error-limit=0 to see all errors)"; - errorHandler().errorLimit = args::getInteger(args, OPT_error_limit_eq, 20); - errorHandler().verbose = args.hasArg(OPT_verbose); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now " + "(use --error-limit=0 to see all errors)"; + ctx->e.errorLimit = args::getInteger(args, OPT_error_limit_eq, 20); + ctx->e.verbose = args.hasArg(OPT_verbose); if (args.hasArg(OPT_help_hidden)) { parser.printHelp(argsArr[0], /*showHidden=*/true); @@ -1134,11 +1415,70 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, return true; } - config = make(); - symtab = make(); + config = std::make_unique(); + symtab = std::make_unique(); + config->outputType = getOutputType(args); target = createTargetInfo(args); - depTracker = - make(args.getLastArgValue(OPT_dependency_info)); + depTracker = std::make_unique( + args.getLastArgValue(OPT_dependency_info)); + if (errorCount()) + return false; + + if (args.hasArg(OPT_pagezero_size)) { + uint64_t pagezeroSize = args::getHex(args, OPT_pagezero_size, 0); + + // ld64 does something really weird. It attempts to realign the value to the + // page size, but assumes the page size is 4K. This doesn't work with most + // of Apple's ARM64 devices, which use a page size of 16K. This means that + // it will first 4K align it by rounding down, then round up to 16K. This + // probably only happened because no one using this arg with anything other + // then 0, so no one checked if it did what is what it says it does. + + // So we are not copying this weird behavior and doing the it in a logical + // way, by always rounding down to page size. + if (!isAligned(Align(target->getPageSize()), pagezeroSize)) { + pagezeroSize -= pagezeroSize % target->getPageSize(); + warn("__PAGEZERO size is not page aligned, rounding down to 0x" + + Twine::utohexstr(pagezeroSize)); + } + + target->pageZeroSize = pagezeroSize; + } + + config->osoPrefix = args.getLastArgValue(OPT_oso_prefix); + if (!config->osoPrefix.empty()) { + // Expand special characters, such as ".", "..", or "~", if present. + // Note: LD64 only expands "." and not other special characters. + // That seems silly to imitate so we will not try to follow it, but rather + // just use real_path() to do it. + + // The max path length is 4096, in theory. However that seems quite long + // and seems unlikely that any one would want to strip everything from the + // path. Hence we've picked a reasonably large number here. + SmallString<1024> expanded; + if (!fs::real_path(config->osoPrefix, expanded, + /*expand_tilde=*/true)) { + // Note: LD64 expands "." to be `/` + // (ie., it has a slash suffix) whereas real_path() doesn't. + // So we have to append '/' to be consistent. + StringRef sep = sys::path::get_separator(); + // real_path removes trailing slashes as part of the normalization, but + // these are meaningful for our text based stripping + if (config->osoPrefix.equals(".") || config->osoPrefix.endswith(sep)) + expanded += sep; + config->osoPrefix = saver().save(expanded.str()); + } + } + + bool pie = args.hasFlag(OPT_pie, OPT_no_pie, true); + if (!supportsNoPie() && !pie) { + warn("-no_pie ignored for arm64"); + pie = true; + } + + config->isPic = config->outputType == MH_DYLIB || + config->outputType == MH_BUNDLE || + (config->outputType == MH_EXECUTE && pie); // Must be set before any InputSections and Symbols are created. config->deadStrip = args.hasArg(OPT_dead_strip); @@ -1193,29 +1533,57 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, args.hasArg(OPT_print_dylib_search) || getenv("RC_TRACE_DYLIB_SEARCHING"); config->printEachFile = args.hasArg(OPT_t); config->printWhyLoad = args.hasArg(OPT_why_load); - config->outputType = getOutputType(args); + config->omitDebugInfo = args.hasArg(OPT_S); + config->errorForArchMismatch = args.hasArg(OPT_arch_errors_fatal); if (const Arg *arg = args.getLastArg(OPT_bundle_loader)) { if (config->outputType != MH_BUNDLE) error("-bundle_loader can only be used with MachO bundle output"); - addFile(arg->getValue(), /*forceLoadArchive=*/false, /*isExplicit=*/false, - /*isBundleLoader=*/true); + addFile(arg->getValue(), LoadType::CommandLine, /*isLazy=*/false, + /*isExplicit=*/false, /*isBundleLoader=*/true); } + for (auto *arg : args.filtered(OPT_dyld_env)) { + StringRef envPair(arg->getValue()); + if (!envPair.contains('=')) + error("-dyld_env's argument is malformed. Expected " + "-dyld_env =, got `" + + envPair + "`"); + config->dyldEnvs.push_back(envPair); + } + if (!config->dyldEnvs.empty() && config->outputType != MH_EXECUTE) + error("-dyld_env can only be used when creating executable output"); + if (const Arg *arg = args.getLastArg(OPT_umbrella)) { if (config->outputType != MH_DYLIB) warn("-umbrella used, but not creating dylib"); config->umbrella = arg->getValue(); } config->ltoObjPath = args.getLastArgValue(OPT_object_path_lto); - config->ltoNewPassManager = - args.hasFlag(OPT_no_lto_legacy_pass_manager, OPT_lto_legacy_pass_manager, - LLVM_ENABLE_NEW_PASS_MANAGER); config->ltoo = args::getInteger(args, OPT_lto_O, 2); if (config->ltoo > 3) error("--lto-O: invalid optimization level: " + Twine(config->ltoo)); config->thinLTOCacheDir = args.getLastArgValue(OPT_cache_path_lto); config->thinLTOCachePolicy = getLTOCachePolicy(args); + config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); + config->thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) || + args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_eq); + config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_eq); + config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq); + config->thinLTOObjectSuffixReplace = + getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); + config->thinLTOPrefixReplace = + getOldNewOptions(args, OPT_thinlto_prefix_replace_eq); + if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) { + if (args.hasArg(OPT_thinlto_object_suffix_replace_eq)) + error("--thinlto-object-suffix-replace is not supported with " + "--thinlto-emit-index-files"); + else if (args.hasArg(OPT_thinlto_prefix_replace_eq)) + error("--thinlto-prefix-replace is not supported with " + "--thinlto-emit-index-files"); + } config->runtimePaths = args::getStrings(args, OPT_rpath); - config->allLoad = args.hasArg(OPT_all_load); + config->allLoad = args.hasFlag(OPT_all_load, OPT_noall_load, false); config->archMultiple = args.hasArg(OPT_arch_multiple); config->applicationExtension = args.hasFlag( OPT_application_extension, OPT_no_application_extension, false); @@ -1230,15 +1598,38 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, config->emitBitcodeBundle = args.hasArg(OPT_bitcode_bundle); config->emitDataInCodeInfo = args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, true); + config->emitChainedFixups = shouldEmitChainedFixups(args); + config->emitInitOffsets = + config->emitChainedFixups || args.hasArg(OPT_init_offsets); config->icfLevel = getICFLevel(args); - config->dedupLiterals = args.hasArg(OPT_deduplicate_literals) || - config->icfLevel != ICFLevel::none; + config->dedupStrings = + args.hasFlag(OPT_deduplicate_strings, OPT_no_deduplicate_strings, true); + config->deadStripDuplicates = args.hasArg(OPT_dead_strip_duplicates); + config->warnDylibInstallName = args.hasFlag( + OPT_warn_dylib_install_name, OPT_no_warn_dylib_install_name, false); + config->ignoreOptimizationHints = args.hasArg(OPT_ignore_optimization_hints); + config->callGraphProfileSort = args.hasFlag( + OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); + config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order_eq); + config->forceExactCpuSubtypeMatch = + getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH"); + config->objcStubsMode = getObjCStubsMode(args); + config->ignoreAutoLink = args.hasArg(OPT_ignore_auto_link); + for (const Arg *arg : args.filtered(OPT_ignore_auto_link_option)) + config->ignoreAutoLinkOptions.insert(arg->getValue()); + config->strictAutoLink = args.hasArg(OPT_strict_auto_link); + + for (const Arg *arg : args.filtered(OPT_alias)) { + config->aliasedSymbols.push_back( + std::make_pair(arg->getValue(0), arg->getValue(1))); + } // FIXME: Add a commandline flag for this too. - config->zeroModTime = getenv("ZERO_AR_DATE"); + if (const char *zero = getenv("ZERO_AR_DATE")) + config->zeroModTime = strcmp(zero, "0") != 0; - std::array encryptablePlatforms{ - PlatformKind::iOS, PlatformKind::watchOS, PlatformKind::tvOS}; + std::array encryptablePlatforms{ + PLATFORM_IOS, PLATFORM_WATCHOS, PLATFORM_TVOS}; config->emitEncryptionInfo = args.hasFlag(OPT_encryptable, OPT_no_encryption, is_contained(encryptablePlatforms, config->platform())); @@ -1249,8 +1640,10 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, #endif if (const Arg *arg = args.getLastArg(OPT_install_name)) { - if (config->outputType != MH_DYLIB) - warn(arg->getAsString(args) + ": ignored, only has effect with -dylib"); + if (config->warnDylibInstallName && config->outputType != MH_DYLIB) + warn( + arg->getAsString(args) + + ": ignored, only has effect with -dylib [--warn-dylib-install-name]"); else config->installName = arg->getValue(); } else if (config->outputType == MH_DYLIB) { @@ -1330,45 +1723,96 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, config->segmentProtections.push_back({segName, maxProt, initProt}); } + config->hasExplicitExports = + args.hasArg(OPT_no_exported_symbols) || + args.hasArgNoClaim(OPT_exported_symbol, OPT_exported_symbols_list); handleSymbolPatterns(args, config->exportedSymbols, OPT_exported_symbol, OPT_exported_symbols_list); handleSymbolPatterns(args, config->unexportedSymbols, OPT_unexported_symbol, OPT_unexported_symbols_list); - if (!config->exportedSymbols.empty() && !config->unexportedSymbols.empty()) { - error("cannot use both -exported_symbol* and -unexported_symbol* options\n" - ">>> ignoring unexports"); - config->unexportedSymbols.clear(); + if (config->hasExplicitExports && !config->unexportedSymbols.empty()) + error("cannot use both -exported_symbol* and -unexported_symbol* options"); + + if (args.hasArg(OPT_no_exported_symbols) && !config->exportedSymbols.empty()) + error("cannot use both -exported_symbol* and -no_exported_symbols options"); + + // Imitating LD64's: + // -non_global_symbols_no_strip_list and -non_global_symbols_strip_list can't + // both be present. + // But -x can be used with either of these two, in which case, the last arg + // takes effect. + // (TODO: This is kind of confusing - considering disallowing using them + // together for a more straightforward behaviour) + { + bool includeLocal = false; + bool excludeLocal = false; + for (const Arg *arg : + args.filtered(OPT_x, OPT_non_global_symbols_no_strip_list, + OPT_non_global_symbols_strip_list)) { + switch (arg->getOption().getID()) { + case OPT_x: + config->localSymbolsPresence = SymtabPresence::None; + break; + case OPT_non_global_symbols_no_strip_list: + if (excludeLocal) { + error("cannot use both -non_global_symbols_no_strip_list and " + "-non_global_symbols_strip_list"); + } else { + includeLocal = true; + config->localSymbolsPresence = SymtabPresence::SelectivelyIncluded; + parseSymbolPatternsFile(arg, config->localSymbolPatterns); + } + break; + case OPT_non_global_symbols_strip_list: + if (includeLocal) { + error("cannot use both -non_global_symbols_no_strip_list and " + "-non_global_symbols_strip_list"); + } else { + excludeLocal = true; + config->localSymbolsPresence = SymtabPresence::SelectivelyExcluded; + parseSymbolPatternsFile(arg, config->localSymbolPatterns); + } + break; + default: + llvm_unreachable("unexpected option"); + } + } } // Explicitly-exported literal symbols must be defined, but might - // languish in an archive if unreferenced elsewhere. Light a fire - // under those lazy symbols! + // languish in an archive if unreferenced elsewhere or if they are in the + // non-global strip list. Light a fire under those lazy symbols! for (const CachedHashStringRef &cachedName : config->exportedSymbols.literals) symtab->addUndefined(cachedName.val(), /*file=*/nullptr, /*isWeakRef=*/false); + for (const Arg *arg : args.filtered(OPT_why_live)) + config->whyLive.insert(arg->getValue()); + if (!config->whyLive.empty() && !config->deadStrip) + warn("-why_live has no effect without -dead_strip, ignoring"); + config->saveTemps = args.hasArg(OPT_save_temps); config->adhocCodesign = args.hasFlag( OPT_adhoc_codesign, OPT_no_adhoc_codesign, - (config->arch() == AK_arm64 || config->arch() == AK_arm64e) && - config->platform() == PlatformKind::macOS); + shouldAdhocSignByDefault(config->arch(), config->platform())); if (args.hasArg(OPT_v)) { - message(getLLDVersion()); + message(getLLDVersion(), lld::errs()); message(StringRef("Library search paths:") + - (config->librarySearchPaths.empty() - ? "" - : "\n\t" + join(config->librarySearchPaths, "\n\t"))); + (config->librarySearchPaths.empty() + ? "" + : "\n\t" + join(config->librarySearchPaths, "\n\t")), + lld::errs()); message(StringRef("Framework search paths:") + - (config->frameworkSearchPaths.empty() - ? "" - : "\n\t" + join(config->frameworkSearchPaths, "\n\t"))); + (config->frameworkSearchPaths.empty() + ? "" + : "\n\t" + join(config->frameworkSearchPaths, "\n\t")), + lld::errs()); } config->progName = argsArr[0]; - config->timeTraceEnabled = args.hasArg( - OPT_time_trace, OPT_time_trace_granularity_eq, OPT_time_trace_file_eq); + config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq); config->timeTraceGranularity = args::getInteger(args, OPT_time_trace_granularity_eq, 500); @@ -1382,11 +1826,6 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, initLLVM(); // must be run before any call to addFile() createFiles(args); - config->isPic = config->outputType == MH_DYLIB || - config->outputType == MH_BUNDLE || - (config->outputType == MH_EXECUTE && - args.hasFlag(OPT_pie, OPT_no_pie, true)); - // Now that all dylibs have been loaded, search for those that should be // re-exported. { @@ -1407,60 +1846,71 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, reexportHandler(arg, extensions); } + cl::ResetAllOptionOccurrences(); + // Parse LTO options. if (const Arg *arg = args.getLastArg(OPT_mcpu)) - parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())), arg->getSpelling()); - for (const Arg *arg : args.filtered(OPT_mllvm)) + for (const Arg *arg : args.filtered(OPT_mllvm)) { parseClangOption(arg->getValue(), arg->getSpelling()); + config->mllvmOpts.emplace_back(arg->getValue()); + } - compileBitcodeFiles(); + createSyntheticSections(); + createSyntheticSymbols(); + addSynthenticMethnames(); + + createAliases(); + // If we are in "explicit exports" mode, hide everything that isn't + // explicitly exported. Do this before running LTO so that LTO can better + // optimize. + handleExplicitExports(); + + bool didCompileBitcodeFiles = compileBitcodeFiles(); + + // If --thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in compileBitcodeFiles, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return errorCount() == 0; + + // LTO may emit a non-hidden (extern) object file symbol even if the + // corresponding bitcode symbol is hidden. In particular, this happens for + // cross-module references to hidden symbols under ThinLTO. Thus, if we + // compiled any bitcode files, we must redo the symbol hiding. + if (didCompileBitcodeFiles) + handleExplicitExports(); replaceCommonSymbols(); StringRef orderFile = args.getLastArgValue(OPT_order_file); if (!orderFile.empty()) - parseOrderFile(orderFile); + priorityBuilder.parseOrderFile(orderFile); referenceStubBinder(); // FIXME: should terminate the link early based on errors encountered so // far? - createSyntheticSections(); - createSyntheticSymbols(); - - if (!config->exportedSymbols.empty()) { - for (Symbol *sym : symtab->getSymbols()) { - if (auto *defined = dyn_cast(sym)) { - StringRef symbolName = defined->getName(); - if (config->exportedSymbols.match(symbolName)) { - if (defined->privateExtern) { - error("cannot export hidden symbol " + symbolName + - "\n>>> defined in " + toString(defined->getFile())); - } - } else { - defined->privateExtern = true; - } - } - } - } else if (!config->unexportedSymbols.empty()) { - for (Symbol *sym : symtab->getSymbols()) - if (auto *defined = dyn_cast(sym)) - if (config->unexportedSymbols.match(defined->getName())) - defined->privateExtern = true; - } - for (const Arg *arg : args.filtered(OPT_sectcreate)) { StringRef segName = arg->getValue(0); StringRef sectName = arg->getValue(1); StringRef fileName = arg->getValue(2); - Optional buffer = readFile(fileName); + std::optional buffer = readFile(fileName); if (buffer) inputFiles.insert(make(*buffer, segName, sectName)); } + for (const Arg *arg : args.filtered(OPT_add_empty_section)) { + StringRef segName = arg->getValue(0); + StringRef sectName = arg->getValue(1); + inputFiles.insert(make(MemoryBufferRef(), segName, sectName)); + } + gatherInputSections(); + if (config->callGraphProfileSort) + priorityBuilder.extractCallGraphProfile(); if (config->deadStrip) markLive(); @@ -1468,8 +1918,13 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, // ICF assumes that all literals have been folded already, so we must run // foldIdenticalLiterals before foldIdenticalSections. foldIdenticalLiterals(); - if (config->icfLevel != ICFLevel::none) - foldIdenticalSections(); + if (config->icfLevel != ICFLevel::none) { + if (config->icfLevel == ICFLevel::safe) + markAddrSigSymbols(); + foldIdenticalSections(/*onlyCfStrings=*/false); + } else if (config->dedupStrings) { + foldIdenticalSections(/*onlyCfStrings=*/true); + } // Write to an output file. if (target->wordSize == 8) @@ -1481,18 +1936,15 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, } if (config->timeTraceEnabled) { - if (auto E = timeTraceProfilerWrite( - args.getLastArgValue(OPT_time_trace_file_eq).str(), - config->outputFile)) { - handleAllErrors(std::move(E), - [&](const StringError &SE) { error(SE.getMessage()); }); - } + checkError(timeTraceProfilerWrite( + args.getLastArgValue(OPT_time_trace_eq).str(), config->outputFile)); timeTraceProfilerCleanup(); } - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); + if (errorCount() != 0 || config->strictAutoLink) + for (const auto &warning : missingAutolinkWarnings) + warn(warning); - return !errorCount(); + return errorCount() == 0; } diff --git a/gnu/llvm/lld/MachO/Driver.h b/gnu/llvm/lld/MachO/Driver.h index 10f307e780c..bed752ff625 100644 --- a/gnu/llvm/lld/MachO/Driver.h +++ b/gnu/llvm/lld/MachO/Driver.h @@ -10,29 +10,22 @@ #define LLD_MACHO_DRIVER_H #include "lld/Common/LLVM.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" #include "llvm/Option/OptTable.h" #include "llvm/Support/MemoryBuffer.h" +#include #include #include -namespace llvm { -namespace MachO { -class InterfaceFile; -enum class PlatformKind : unsigned; -} // namespace MachO -} // namespace llvm - -namespace lld { -namespace macho { +namespace lld::macho { class DylibFile; class InputFile; -class MachOOptTable : public llvm::opt::OptTable { +class MachOOptTable : public llvm::opt::GenericOptTable { public: MachOOptTable(); llvm::opt::InputArgList parse(ArrayRef argv); @@ -52,14 +45,16 @@ void parseLCLinkerOption(InputFile *, unsigned argc, StringRef data); std::string createResponseFile(const llvm::opt::InputArgList &args); // Check for both libfoo.dylib and libfoo.tbd (in that order). -llvm::Optional resolveDylibPath(llvm::StringRef path); +std::optional resolveDylibPath(llvm::StringRef path); DylibFile *loadDylib(llvm::MemoryBufferRef mbref, DylibFile *umbrella = nullptr, - bool isBundleLoader = false); + bool isBundleLoader = false, + bool explicitlyLinked = false); +void resetLoadedDylibs(); // Search for all possible combinations of `{root}/{name}.{extension}`. // If \p extensions are not specified, then just search for `{root}/{name}`. -llvm::Optional +std::optional findPathCombination(const llvm::Twine &name, const std::vector &roots, ArrayRef extensions = {""}); @@ -68,17 +63,12 @@ findPathCombination(const llvm::Twine &name, // rerooted. llvm::StringRef rerootPath(llvm::StringRef path); -llvm::Optional loadArchiveMember(MemoryBufferRef, uint32_t modTime, - StringRef archiveName, - bool objCOnly, - uint64_t offsetInArchive); - uint32_t getModTime(llvm::StringRef path); void printArchiveMemberLoad(StringRef reason, const InputFile *); // Map simulator platforms to their underlying device platform. -llvm::MachO::PlatformKind removeSimulator(llvm::MachO::PlatformKind platform); +llvm::MachO::PlatformType removeSimulator(llvm::MachO::PlatformType platform); // Helper class to export dependency info. class DependencyTracker { @@ -91,9 +81,8 @@ public: notFounds.insert(path.str()); } - // Writes the dependencies to specified path. - // The content is sorted by its Op Code, then within each section, - // alphabetical order. + // Writes the dependencies to specified path. The content is first sorted by + // OpCode and then by the filename (in alphabetical order). void write(llvm::StringRef version, const llvm::SetVector &inputs, llvm::StringRef output); @@ -119,9 +108,8 @@ private: std::set notFounds; }; -extern DependencyTracker *depTracker; +extern std::unique_ptr depTracker; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/DriverUtils.cpp b/gnu/llvm/lld/MachO/DriverUtils.cpp index fc25182d914..d1f8ec28cbb 100644 --- a/gnu/llvm/lld/MachO/DriverUtils.cpp +++ b/gnu/llvm/lld/MachO/DriverUtils.cpp @@ -13,12 +13,10 @@ #include "Target.h" #include "lld/Common/Args.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/Bitcode/BitcodeReader.h" #include "llvm/LTO/LTO.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -37,12 +35,15 @@ using namespace lld; using namespace lld::macho; // Create prefix string literals used in Options.td -#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE; +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef NAME(NAME##_init, \ + std::size(NAME##_init) - 1); #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td -static const OptTable::Info optInfo[] = { +static constexpr OptTable::Info optInfo[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, @@ -50,7 +51,7 @@ static const OptTable::Info optInfo[] = { #undef OPTION }; -MachOOptTable::MachOOptTable() : OptTable(optInfo) {} +MachOOptTable::MachOOptTable() : GenericOptTable(optInfo) {} // Set color diagnostics according to --color-diagnostics={auto,always,never} // or --no-color-diagnostics flags. @@ -83,12 +84,13 @@ InputArgList MachOOptTable::parse(ArrayRef argv) { // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, cl::TokenizeGNUCommandLine, vec); + cl::ExpandResponseFiles(saver(), cl::TokenizeGNUCommandLine, vec); InputArgList args = ParseArgs(vec, missingIndex, missingCount); // Handle -fatal_warnings early since it converts missing argument warnings // to errors. errorHandler().fatalWarnings = args.hasArg(OPT_fatal_warnings); + errorHandler().suppressWarnings = args.hasArg(OPT_w); if (missingCount) error(Twine(args.getArgString(missingIndex)) + ": missing argument"); @@ -145,12 +147,13 @@ std::string macho::createResponseFile(const InputArgList &args) { os << "-o " << quote(path::filename(arg->getValue())) << "\n"; break; case OPT_filelist: - if (Optional buffer = readFile(arg->getValue())) + if (std::optional buffer = readFile(arg->getValue())) for (StringRef path : args::getLines(*buffer)) os << quote(rewriteInputPath(path)) << "\n"; break; case OPT_force_load: case OPT_weak_library: + case OPT_load_hidden: os << arg->getSpelling() << " " << quote(rewriteInputPath(arg->getValue())) << "\n"; break; @@ -159,7 +162,6 @@ std::string macho::createResponseFile(const InputArgList &args) { case OPT_bundle_loader: case OPT_exported_symbols_list: case OPT_order_file: - case OPT_rpath: case OPT_syslibroot: case OPT_unexported_symbols_list: os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue())) @@ -184,20 +186,20 @@ static void searchedDylib(const Twine &path, bool found) { depTracker->logFileNotFound(path); } -Optional macho::resolveDylibPath(StringRef dylibPath) { +std::optional macho::resolveDylibPath(StringRef dylibPath) { // TODO: if a tbd and dylib are both present, we should check to make sure // they are consistent. - bool dylibExists = fs::exists(dylibPath); - searchedDylib(dylibPath, dylibExists); - if (dylibExists) - return std::string(dylibPath); - SmallString<261> tbdPath = dylibPath; path::replace_extension(tbdPath, ".tbd"); bool tbdExists = fs::exists(tbdPath); searchedDylib(tbdPath, tbdExists); if (tbdExists) - return std::string(tbdPath); + return saver().save(tbdPath.str()); + + bool dylibExists = fs::exists(dylibPath); + searchedDylib(dylibPath, dylibExists); + if (dylibExists) + return saver().save(dylibPath); return {}; } @@ -206,11 +208,14 @@ Optional macho::resolveDylibPath(StringRef dylibPath) { static DenseMap loadedDylibs; DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, - bool isBundleLoader) { + bool isBundleLoader, bool explicitlyLinked) { CachedHashStringRef path(mbref.getBufferIdentifier()); DylibFile *&file = loadedDylibs[path]; - if (file) + if (file) { + if (explicitlyLinked) + file->setExplicitlyLinked(); return file; + } DylibFile *newFile; file_magic magic = identify_magic(mbref.getBuffer()); @@ -221,7 +226,8 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, ": " + toString(result.takeError())); return nullptr; } - file = make(**result, umbrella, isBundleLoader); + file = + make(**result, umbrella, isBundleLoader, explicitlyLinked); // parseReexports() can recursively call loadDylib(). That's fine since // we wrote the DylibFile we just loaded to the loadDylib cache via the @@ -236,7 +242,7 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, magic == file_magic::macho_dynamically_linked_shared_lib_stub || magic == file_magic::macho_executable || magic == file_magic::macho_bundle); - file = make(mbref, umbrella, isBundleLoader); + file = make(mbref, umbrella, isBundleLoader, explicitlyLinked); // parseLoadCommands() can also recursively call loadDylib(). See comment // in previous block for why this means we must copy `file` here. @@ -247,7 +253,9 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, return newFile; } -Optional +void macho::resetLoadedDylibs() { loadedDylibs.clear(); } + +std::optional macho::findPathCombination(const Twine &name, const std::vector &roots, ArrayRef extensions) { @@ -260,7 +268,7 @@ macho::findPathCombination(const Twine &name, bool exists = fs::exists(location); searchedDylib(location, exists); if (exists) - return saver.save(location.str()); + return saver().save(location.str()); } } return {}; @@ -270,37 +278,13 @@ StringRef macho::rerootPath(StringRef path) { if (!path::is_absolute(path, path::Style::posix) || path.endswith(".o")) return path; - if (Optional rerootedPath = + if (std::optional rerootedPath = findPathCombination(path, config->systemLibraryRoots)) return *rerootedPath; return path; } -Optional macho::loadArchiveMember(MemoryBufferRef mb, - uint32_t modTime, - StringRef archiveName, - bool objCOnly, - uint64_t offsetInArchive) { - if (config->zeroModTime) - modTime = 0; - - switch (identify_magic(mb.getBuffer())) { - case file_magic::macho_object: - if (!objCOnly || hasObjCSection(mb)) - return make(mb, modTime, archiveName); - return None; - case file_magic::bitcode: - if (!objCOnly || check(isBitcodeContainingObjCCategory(mb))) - return make(mb, archiveName, offsetInArchive); - return None; - default: - error(archiveName + ": archive member " + mb.getBufferIdentifier() + - " has unhandled file type"); - return None; - } -} - uint32_t macho::getModTime(StringRef path) { if (config->zeroModTime) return 0; diff --git a/gnu/llvm/lld/MachO/Dwarf.cpp b/gnu/llvm/lld/MachO/Dwarf.cpp index c142cc1b169..47dc51e6196 100644 --- a/gnu/llvm/lld/MachO/Dwarf.cpp +++ b/gnu/llvm/lld/MachO/Dwarf.cpp @@ -20,15 +20,17 @@ using namespace llvm; std::unique_ptr DwarfObject::create(ObjFile *obj) { auto dObj = std::make_unique(); bool hasDwarfInfo = false; - // LLD only needs to extract the source file path from the debug info, so we - // initialize DwarfObject with just the sections necessary to get that path. - // The debugger will locate the debug info via the object file paths that we - // emit in our STABS symbols, so we don't need to process & emit them - // ourselves. + // LLD only needs to extract the source file path and line numbers from the + // debug info, so we initialize DwarfObject with just the sections necessary + // to get that path. The debugger will locate the debug info via the object + // file paths that we emit in our STABS symbols, so we don't need to process & + // emit them ourselves. for (const InputSection *isec : obj->debugSections) { if (StringRef *s = StringSwitch(isec->getName()) .Case(section_names::debugInfo, &dObj->infoSection.Data) + .Case(section_names::debugLine, &dObj->lineSection.Data) + .Case(section_names::debugStrOffs, &dObj->strOffsSection.Data) .Case(section_names::debugAbbrev, &dObj->abbrevSection) .Case(section_names::debugStr, &dObj->strSection) .Default(nullptr)) { diff --git a/gnu/llvm/lld/MachO/Dwarf.h b/gnu/llvm/lld/MachO/Dwarf.h index 119f2778fc6..31a9f82ee00 100644 --- a/gnu/llvm/lld/MachO/Dwarf.h +++ b/gnu/llvm/lld/MachO/Dwarf.h @@ -12,8 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DWARF/DWARFObject.h" -namespace lld { -namespace macho { +namespace lld::macho { class ObjFile; @@ -23,10 +22,10 @@ class DwarfObject final : public llvm::DWARFObject { public: bool isLittleEndian() const override { return true; } - llvm::Optional find(const llvm::DWARFSection &sec, - uint64_t pos) const override { + std::optional find(const llvm::DWARFSection &sec, + uint64_t pos) const override { // TODO: implement this - return llvm::None; + return std::nullopt; } void forEachInfoSections( @@ -37,17 +36,26 @@ public: llvm::StringRef getAbbrevSection() const override { return abbrevSection; } llvm::StringRef getStrSection() const override { return strSection; } + llvm::DWARFSection const &getLineSection() const override { + return lineSection; + } + + llvm::DWARFSection const &getStrOffsetsSection() const override { + return strOffsSection; + } + // Returns an instance of DwarfObject if the given object file has the // relevant DWARF debug sections. static std::unique_ptr create(ObjFile *); private: llvm::DWARFSection infoSection; + llvm::DWARFSection lineSection; + llvm::DWARFSection strOffsSection; llvm::StringRef abbrevSection; llvm::StringRef strSection; }; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/EhFrame.cpp b/gnu/llvm/lld/MachO/EhFrame.cpp new file mode 100644 index 00000000000..55a85f316cd --- /dev/null +++ b/gnu/llvm/lld/MachO/EhFrame.cpp @@ -0,0 +1,140 @@ +//===- EhFrame.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 "EhFrame.h" +#include "InputFiles.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace lld; +using namespace lld::macho; +using namespace llvm::support::endian; + +uint64_t EhReader::readLength(size_t *off) const { + const size_t errOff = *off; + if (*off + 4 > data.size()) + failOn(errOff, "CIE/FDE too small"); + uint64_t len = read32le(data.data() + *off); + *off += 4; + if (len == dwarf::DW_LENGTH_DWARF64) { + // FIXME: test this DWARF64 code path + if (*off + 8 > data.size()) + failOn(errOff, "CIE/FDE too small"); + len = read64le(data.data() + *off); + *off += 8; + } + if (*off + len > data.size()) + failOn(errOff, "CIE/FDE extends past the end of the section"); + return len; +} + +void EhReader::skipValidLength(size_t *off) const { + uint32_t len = read32le(data.data() + *off); + *off += 4; + if (len == dwarf::DW_LENGTH_DWARF64) + *off += 8; +} + +// Read a byte and advance off by one byte. +uint8_t EhReader::readByte(size_t *off) const { + if (*off + 1 > data.size()) + failOn(*off, "unexpected end of CIE/FDE"); + return data[(*off)++]; +} + +uint32_t EhReader::readU32(size_t *off) const { + if (*off + 4 > data.size()) + failOn(*off, "unexpected end of CIE/FDE"); + uint32_t v = read32le(data.data() + *off); + *off += 4; + return v; +} + +uint64_t EhReader::readPointer(size_t *off, uint8_t size) const { + if (*off + size > data.size()) + failOn(*off, "unexpected end of CIE/FDE"); + uint64_t v; + if (size == 8) + v = read64le(data.data() + *off); + else { + assert(size == 4); + v = read32le(data.data() + *off); + } + *off += size; + return v; +} + +// Read a null-terminated string. +StringRef EhReader::readString(size_t *off) const { + if (*off > data.size()) + failOn(*off, "corrupted CIE (failed to read string)"); + const size_t maxlen = data.size() - *off; + auto *c = reinterpret_cast(data.data() + *off); + size_t len = strnlen(c, maxlen); + if (len == maxlen) // we failed to find the null terminator + failOn(*off, "corrupted CIE (failed to read string)"); + *off += len + 1; // skip the null byte too + return StringRef(c, len); +} + +void EhReader::skipLeb128(size_t *off) const { + const size_t errOff = *off; + while (*off < data.size()) { + uint8_t val = data[(*off)++]; + if ((val & 0x80) == 0) + return; + } + failOn(errOff, "corrupted CIE (failed to read LEB128)"); +} + +void EhReader::failOn(size_t errOff, const Twine &msg) const { + fatal(toString(file) + ":(__eh_frame+0x" + + Twine::utohexstr(dataOff + errOff) + "): " + msg); +} + +/* + * Create a pair of relocs to write the value of: + * `b - (offset + a)` if Invert == false + * `(a + offset) - b` if Invert == true + */ +template +static void createSubtraction(PointerUnion a, + PointerUnion b, + uint64_t off, uint8_t length, + SmallVectorImpl *newRelocs) { + auto subtrahend = a; + auto minuend = b; + if (Invert) + std::swap(subtrahend, minuend); + assert(subtrahend.is()); + Reloc subtrahendReloc(target->subtractorRelocType, /*pcrel=*/false, length, + off, /*addend=*/0, subtrahend); + Reloc minuendReloc(target->unsignedRelocType, /*pcrel=*/false, length, off, + (Invert ? 1 : -1) * off, minuend); + newRelocs->push_back(subtrahendReloc); + newRelocs->push_back(minuendReloc); +} + +void EhRelocator::makePcRel(uint64_t off, + PointerUnion target, + uint8_t length) { + createSubtraction(isec->symbols[0], target, off, length, &newRelocs); +} + +void EhRelocator::makeNegativePcRel( + uint64_t off, PointerUnion target, + uint8_t length) { + createSubtraction(isec, target, off, length, &newRelocs); +} + +void EhRelocator::commit() { + isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end()); +} diff --git a/gnu/llvm/lld/MachO/EhFrame.h b/gnu/llvm/lld/MachO/EhFrame.h new file mode 100644 index 00000000000..d5afab3e15a --- /dev/null +++ b/gnu/llvm/lld/MachO/EhFrame.h @@ -0,0 +1,116 @@ +//===- EhFrame.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_MACHO_EH_FRAME_H +#define LLD_MACHO_EH_FRAME_H + +#include "InputSection.h" +#include "Relocations.h" + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" + +/* + * NOTE: The main bulk of the EH frame parsing logic is in InputFiles.cpp as it + * is closely coupled with other file parsing logic; EhFrame.h just contains a + * few helpers. + */ + +/* + * === The EH frame format === + * + * EH frames can either be Common Information Entries (CIEs) or Frame + * Description Entries (FDEs). CIEs contain information that is common amongst + * several FDEs. Each FDE contains a pointer to its CIE. Thus all the EH frame + * entries together form a forest of two-level trees, with CIEs as the roots + * and FDEs as the leaves. Note that a CIE must precede the FDEs which point + * to it. + * + * A CIE comprises the following fields in order: + * 1. Length of the entry (4 or 12 bytes) + * 2. CIE offset (4 bytes; always 0 for CIEs) + * 3. CIE version (byte) + * 4. Null-terminated augmentation string + * 5-8. LEB128 values that we don't care about + * 9. Augmentation data, to be interpreted using the aug string + * 10. DWARF instructions (ignored by LLD) + * + * An FDE comprises of the following: + * 1. Length of the entry (4 or 12 bytes) + * 2. CIE offset (4 bytes pcrel offset that points backwards to this FDE's CIE) + * 3. Function address (pointer-sized pcrel offset) + * 4. (std::optional) Augmentation data length + * 5. (std::optional) LSDA address (pointer-sized pcrel offset) + * 6. DWARF instructions (ignored by LLD) + */ +namespace lld::macho { + +class EhReader { +public: + EhReader(const ObjFile *file, ArrayRef data, size_t dataOff) + : file(file), data(data), dataOff(dataOff) {} + size_t size() const { return data.size(); } + // Read and validate the length field. + uint64_t readLength(size_t *off) const; + // Skip the length field without doing validation. + void skipValidLength(size_t *off) const; + uint8_t readByte(size_t *off) const; + uint32_t readU32(size_t *off) const; + uint64_t readPointer(size_t *off, uint8_t size) const; + StringRef readString(size_t *off) const; + void skipLeb128(size_t *off) const; + void failOn(size_t errOff, const Twine &msg) const; + +private: + const ObjFile *file; + ArrayRef data; + // The offset of the data array within its section. Used only for error + // reporting. + const size_t dataOff; +}; + +// The EH frame format, when emitted by llvm-mc, consists of a number of +// "abs-ified" relocations, i.e. relocations that are implicitly encoded as +// pcrel offsets in the section data. The offsets refer to the locations of +// symbols in the input object file. When we ingest these EH frames, we convert +// these implicit relocations into explicit Relocs. +// +// These pcrel relocations are semantically similar to X86_64_RELOC_SIGNED_4. +// However, we need this operation to be cross-platform, and ARM does not have a +// similar relocation that is applicable. We therefore use the more verbose (but +// more generic) subtractor relocation to encode these pcrel values. ld64 +// appears to do something similar -- its `-r` output contains these explicit +// subtractor relocations. +class EhRelocator { +public: + EhRelocator(InputSection *isec) : isec(isec) {} + + // For the next two methods, let `PC` denote `isec address + off`. + // Create relocs writing the value of target - PC to PC. + void makePcRel(uint64_t off, + llvm::PointerUnion target, + uint8_t length); + // Create relocs writing the value of PC - target to PC. + void makeNegativePcRel(uint64_t off, + llvm::PointerUnion target, + uint8_t length); + // Insert the new relocations into isec->relocs. + void commit(); + +private: + InputSection *isec; + // Insert new relocs here so that we don't invalidate iterators into the + // existing relocs vector. + SmallVector newRelocs; +}; + +} // namespace lld::macho + +#endif diff --git a/gnu/llvm/lld/MachO/ExportTrie.cpp b/gnu/llvm/lld/MachO/ExportTrie.cpp index 372690a20df..3ca8d350be1 100644 --- a/gnu/llvm/lld/MachO/ExportTrie.cpp +++ b/gnu/llvm/lld/MachO/ExportTrie.cpp @@ -39,9 +39,9 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" -#include "llvm/ADT/Optional.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/LEB128.h" +#include using namespace llvm; using namespace lld; @@ -81,7 +81,7 @@ struct ExportInfo { struct macho::TrieNode { std::vector edges; - Optional info; + std::optional info; // Estimated offset from the start of the serialized trie to the current node. // This will converge to the true offset when updateOffset() is run to a // fixpoint. @@ -145,8 +145,13 @@ void TrieNode::writeTo(uint8_t *buf) const { } } +TrieBuilder::~TrieBuilder() { + for (TrieNode *node : nodes) + delete node; +} + TrieNode *TrieBuilder::makeNode() { - auto *node = make(); + auto *node = new TrieNode(); nodes.emplace_back(node); return node; } diff --git a/gnu/llvm/lld/MachO/ExportTrie.h b/gnu/llvm/lld/MachO/ExportTrie.h index a43f4f2cce9..aa7e3b0d4a1 100644 --- a/gnu/llvm/lld/MachO/ExportTrie.h +++ b/gnu/llvm/lld/MachO/ExportTrie.h @@ -14,14 +14,14 @@ #include -namespace lld { -namespace macho { +namespace lld::macho { struct TrieNode; class Symbol; class TrieBuilder { public: + ~TrieBuilder(); void setImageBase(uint64_t addr) { imageBase = addr; } void addSymbol(const Symbol &sym) { exported.push_back(&sym); } // Returns the size in bytes of the serialized trie. @@ -43,7 +43,6 @@ using TrieEntryCallback = void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/ICF.cpp b/gnu/llvm/lld/MachO/ICF.cpp index 370a325125c..3dfc46412e8 100644 --- a/gnu/llvm/lld/MachO/ICF.cpp +++ b/gnu/llvm/lld/MachO/ICF.cpp @@ -8,12 +8,17 @@ #include "ICF.h" #include "ConcatOutputSection.h" +#include "Config.h" #include "InputSection.h" +#include "SymbolTable.h" #include "Symbols.h" #include "UnwindInfoSection.h" +#include "lld/Common/CommonLinkerContext.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/TimeProfiler.h" +#include "llvm/Support/xxhash.h" #include @@ -21,23 +26,34 @@ using namespace llvm; using namespace lld; using namespace lld::macho; +static constexpr bool verboseDiagnostics = false; + class ICF { public: ICF(std::vector &inputs); - void run(); - void segregate(size_t begin, size_t end, - std::function - equals); + + using EqualsFn = bool (ICF::*)(const ConcatInputSection *, + const ConcatInputSection *); + void segregate(size_t begin, size_t end, EqualsFn); size_t findBoundary(size_t begin, size_t end); void forEachClassRange(size_t begin, size_t end, - std::function func); - void forEachClass(std::function func); + llvm::function_ref func); + void forEachClass(llvm::function_ref func); + + bool equalsConstant(const ConcatInputSection *ia, + const ConcatInputSection *ib); + bool equalsVariable(const ConcatInputSection *ia, + const ConcatInputSection *ib); // ICF needs a copy of the inputs vector because its equivalence-class // segregation algorithm destroys the proper sequence. std::vector icfInputs; + + unsigned icfPass = 0; + std::atomic icfRepeat{false}; + std::atomic equalsConstantCount{0}; + std::atomic equalsVariableCount{0}; }; ICF::ICF(std::vector &inputs) { @@ -74,13 +90,12 @@ ICF::ICF(std::vector &inputs) { // FIXME(gkm): implement keep-unique attributes // FIXME(gkm): implement address-significance tables for MachO object files -static unsigned icfPass = 0; -static std::atomic icfRepeat{false}; - // Compare "non-moving" parts of two ConcatInputSections, namely everything // except references to other ConcatInputSections. -static bool equalsConstant(const ConcatInputSection *ia, - const ConcatInputSection *ib) { +bool ICF::equalsConstant(const ConcatInputSection *ia, + const ConcatInputSection *ib) { + if (verboseDiagnostics) + ++equalsConstantCount; // We can only fold within the same OutputSection. if (ia->parent != ib->parent) return false; @@ -99,31 +114,33 @@ static bool equalsConstant(const ConcatInputSection *ia, return false; if (ra.offset != rb.offset) return false; - if (ra.addend != rb.addend) - return false; if (ra.referent.is() != rb.referent.is()) return false; InputSection *isecA, *isecB; + + uint64_t valueA = 0; + uint64_t valueB = 0; if (ra.referent.is()) { const auto *sa = ra.referent.get(); const auto *sb = rb.referent.get(); if (sa->kind() != sb->kind()) return false; - if (isa(sa)) { - const auto *da = cast(sa); - const auto *db = cast(sb); - if (da->isec && db->isec) { - isecA = da->isec; - isecB = db->isec; - } else { - assert(da->isAbsolute() && db->isAbsolute()); - return da->value == db->value; - } - } else { - assert(isa(sa)); - return sa == sb; + // ICF runs before Undefineds are treated (and potentially converted into + // DylibSymbols). + if (isa(sa) || isa(sa)) + return sa == sb && ra.addend == rb.addend; + assert(isa(sa)); + const auto *da = cast(sa); + const auto *db = cast(sb); + if (!da->isec || !db->isec) { + assert(da->isAbsolute() && db->isAbsolute()); + return da->value + ra.addend == db->value + rb.addend; } + isecA = da->isec; + valueA = da->value; + isecB = db->isec; + valueB = db->value; } else { isecA = ra.referent.get(); isecB = rb.referent.get(); @@ -135,10 +152,20 @@ static bool equalsConstant(const ConcatInputSection *ia, assert(isecA->kind() == isecB->kind()); // We will compare ConcatInputSection contents in equalsVariable. if (isa(isecA)) - return true; + return ra.addend == rb.addend; // Else we have two literal sections. References to them are equal iff their // offsets in the output section are equal. - return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend); + if (ra.referent.is()) + // For symbol relocs, we compare the contents at the symbol address. We + // don't do `getOffset(value + addend)` because value + addend may not be + // a valid offset in the literal section. + return isecA->getOffset(valueA) == isecB->getOffset(valueB) && + ra.addend == rb.addend; + else { + assert(valueA == 0 && valueB == 0); + // For section relocs, we compare the content at the section offset. + return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend); + } }; return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), f); @@ -146,10 +173,12 @@ static bool equalsConstant(const ConcatInputSection *ia, // Compare the "moving" parts of two ConcatInputSections -- i.e. everything not // handled by equalsConstant(). -static bool equalsVariable(const ConcatInputSection *ia, - const ConcatInputSection *ib) { +bool ICF::equalsVariable(const ConcatInputSection *ia, + const ConcatInputSection *ib) { + if (verboseDiagnostics) + ++equalsVariableCount; assert(ia->relocs.size() == ib->relocs.size()); - auto f = [](const Reloc &ra, const Reloc &rb) { + auto f = [this](const Reloc &ra, const Reloc &rb) { // We already filtered out mismatching values/addends in equalsConstant. if (ra.referent == rb.referent) return true; @@ -176,8 +205,31 @@ static bool equalsVariable(const ConcatInputSection *ia, } return isecA->icfEqClass[icfPass % 2] == isecB->icfEqClass[icfPass % 2]; }; - return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), - f); + if (!std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), f)) + return false; + + // If there are symbols with associated unwind info, check that the unwind + // info matches. For simplicity, we only handle the case where there are only + // symbols at offset zero within the section (which is typically the case with + // .subsections_via_symbols.) + auto hasUnwind = [](Defined *d) { return d->unwindEntry != nullptr; }; + auto itA = std::find_if(ia->symbols.begin(), ia->symbols.end(), hasUnwind); + auto itB = std::find_if(ib->symbols.begin(), ib->symbols.end(), hasUnwind); + if (itA == ia->symbols.end()) + return itB == ib->symbols.end(); + if (itB == ib->symbols.end()) + return false; + const Defined *da = *itA; + const Defined *db = *itB; + if (da->unwindEntry->icfEqClass[icfPass % 2] != + db->unwindEntry->icfEqClass[icfPass % 2] || + da->value != 0 || db->value != 0) + return false; + auto isZero = [](Defined *d) { return d->value == 0; }; + return std::find_if_not(std::next(itA), ia->symbols.end(), isZero) == + ia->symbols.end() && + std::find_if_not(std::next(itB), ib->symbols.end(), isZero) == + ib->symbols.end(); } // Find the first InputSection after BEGIN whose equivalence class differs @@ -191,7 +243,7 @@ size_t ICF::findBoundary(size_t begin, size_t end) { // Invoke FUNC on subranges with matching equivalence class void ICF::forEachClassRange(size_t begin, size_t end, - std::function func) { + llvm::function_ref func) { while (begin < end) { size_t mid = findBoundary(begin, end); func(begin, mid); @@ -201,7 +253,7 @@ void ICF::forEachClassRange(size_t begin, size_t end, // Split icfInputs into shards, then parallelize invocation of FUNC on subranges // with matching equivalence class -void ICF::forEachClass(std::function func) { +void ICF::forEachClass(llvm::function_ref func) { // Only use threads when the benefits outweigh the overhead. const size_t threadingThreshold = 1024; if (icfInputs.size() < threadingThreshold) { @@ -218,10 +270,10 @@ void ICF::forEachClass(std::function func) { size_t boundaries[shards + 1]; boundaries[0] = 0; boundaries[shards] = icfInputs.size(); - parallelForEachN(1, shards, [&](size_t i) { + parallelFor(1, shards, [&](size_t i) { boundaries[i] = findBoundary((i - 1) * step, icfInputs.size()); }); - parallelForEachN(1, shards + 1, [&](size_t i) { + parallelFor(1, shards + 1, [&](size_t i) { if (boundaries[i - 1] < boundaries[i]) { forEachClassRange(boundaries[i - 1], boundaries[i], func); } @@ -233,27 +285,28 @@ void ICF::run() { // Into each origin-section hash, combine all reloc referent section hashes. for (icfPass = 0; icfPass < 2; ++icfPass) { parallelForEach(icfInputs, [&](ConcatInputSection *isec) { - uint64_t hash = isec->icfEqClass[icfPass % 2]; + uint32_t hash = isec->icfEqClass[icfPass % 2]; for (const Reloc &r : isec->relocs) { if (auto *sym = r.referent.dyn_cast()) { - if (auto *dylibSym = dyn_cast(sym)) - hash += dylibSym->stubsHelperIndex; - else if (auto *defined = dyn_cast(sym)) { + if (auto *defined = dyn_cast(sym)) { if (defined->isec) { - if (auto isec = dyn_cast(defined->isec)) - hash += defined->value + isec->icfEqClass[icfPass % 2]; + if (auto referentIsec = + dyn_cast(defined->isec)) + hash += defined->value + referentIsec->icfEqClass[icfPass % 2]; else hash += defined->isec->kind() + defined->isec->getOffset(defined->value); } else { hash += defined->value; } - } else - llvm_unreachable("foldIdenticalSections symbol kind"); + } else { + // ICF runs before Undefined diags + assert(isa(sym) || isa(sym)); + } } } // Set MSB to 1 to avoid collisions with non-hashed classes. - isec->icfEqClass[(icfPass + 1) % 2] = hash | (1ull << 63); + isec->icfEqClass[(icfPass + 1) % 2] = hash | (1ull << 31); }); } @@ -261,17 +314,22 @@ void ICF::run() { icfInputs, [](const ConcatInputSection *a, const ConcatInputSection *b) { return a->icfEqClass[0] < b->icfEqClass[0]; }); - forEachClass( - [&](size_t begin, size_t end) { segregate(begin, end, equalsConstant); }); + forEachClass([&](size_t begin, size_t end) { + segregate(begin, end, &ICF::equalsConstant); + }); // Split equivalence groups by comparing relocations until convergence do { icfRepeat = false; forEachClass([&](size_t begin, size_t end) { - segregate(begin, end, equalsVariable); + segregate(begin, end, &ICF::equalsVariable); }); } while (icfRepeat); log("ICF needed " + Twine(icfPass) + " iterations"); + if (verboseDiagnostics) { + log("equalsConstant() called " + Twine(equalsConstantCount) + " times"); + log("equalsVariable() called " + Twine(equalsVariableCount) + " times"); + } // Fold sections within equivalence classes forEachClass([&](size_t begin, size_t end) { @@ -284,18 +342,15 @@ void ICF::run() { } // Split an equivalence class into smaller classes. -void ICF::segregate( - size_t begin, size_t end, - std::function - equals) { +void ICF::segregate(size_t begin, size_t end, EqualsFn equals) { while (begin < end) { // Divide [begin, end) into two. Let mid be the start index of the // second group. - auto bound = std::stable_partition(icfInputs.begin() + begin + 1, - icfInputs.begin() + end, - [&](ConcatInputSection *isec) { - return equals(icfInputs[begin], isec); - }); + auto bound = std::stable_partition( + icfInputs.begin() + begin + 1, icfInputs.begin() + end, + [&](ConcatInputSection *isec) { + return (this->*equals)(icfInputs[begin], isec); + }); size_t mid = bound - icfInputs.begin(); // Split [begin, end) into [begin, mid) and [mid, end). We use mid as an @@ -311,23 +366,36 @@ void ICF::segregate( } } -template -DenseSet findFunctionsWithUnwindInfo() { - DenseSet result; - for (ConcatInputSection *isec : in.unwindInfo->getInputs()) { - for (size_t i = 0; i < isec->relocs.size(); ++i) { - Reloc &r = isec->relocs[i]; - assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED)); - if (r.offset % sizeof(CompactUnwindEntry) != - offsetof(CompactUnwindEntry, functionAddress)) - continue; - result.insert(r.referent.get()); +void macho::markSymAsAddrSig(Symbol *s) { + if (auto *d = dyn_cast_or_null(s)) + if (d->isec) + d->isec->keepUnique = true; +} + +void macho::markAddrSigSymbols() { + TimeTraceScope timeScope("Mark addrsig symbols"); + for (InputFile *file : inputFiles) { + ObjFile *obj = dyn_cast(file); + if (!obj) + continue; + + Section *addrSigSection = obj->addrSigSection; + if (!addrSigSection) + continue; + assert(addrSigSection->subsections.size() == 1); + + const InputSection *isec = addrSigSection->subsections[0].isec; + + for (const Reloc &r : isec->relocs) { + if (auto *sym = r.referent.dyn_cast()) + markSymAsAddrSig(sym); + else + error(toString(isec) + ": unexpected section relocation"); } } - return result; } -void macho::foldIdenticalSections() { +void macho::foldIdenticalSections(bool onlyCfStrings) { TimeTraceScope timeScope("Fold Identical Code Sections"); // The ICF equivalence-class segregation algorithm relies on pre-computed // hashes of InputSection::data for the ConcatOutputSection::inputs and all @@ -336,11 +404,6 @@ void macho::foldIdenticalSections() { // parallelization. Therefore, we hash every InputSection here where we have // them all accessible as simple vectors. - // ICF can't fold functions with unwind info - DenseSet functionsWithUnwindInfo = - target->wordSize == 8 ? findFunctionsWithUnwindInfo() - : findFunctionsWithUnwindInfo(); - // If an InputSection is ineligible for ICF, we give it a unique ID to force // it into an unfoldable singleton equivalence class. Begin the unique-ID // space at inputSections.size(), so that it will never intersect with @@ -348,22 +411,55 @@ void macho::foldIdenticalSections() { // coexist with equivalence-class IDs, this is not necessary, but might help // someone keep the numbers straight in case we ever need to debug the // ICF::segregate() - std::vector hashable; + std::vector foldable; uint64_t icfUniqueID = inputSections.size(); for (ConcatInputSection *isec : inputSections) { - // FIXME: consider non-code __text sections as hashable? - bool isHashable = (isCodeSection(isec) || isCfStringSection(isec)) && - !isec->shouldOmitFromOutput() && - !functionsWithUnwindInfo.contains(isec) && - isec->isHashableForICF(); - if (isHashable) - hashable.push_back(isec); - else + bool isFoldableWithAddendsRemoved = isCfStringSection(isec) || + isClassRefsSection(isec) || + isSelRefsSection(isec); + // NOTE: __objc_selrefs is typically marked as no_dead_strip by MC, but we + // can still fold it. + bool hasFoldableFlags = (isSelRefsSection(isec) || + sectionType(isec->getFlags()) == MachO::S_REGULAR); + // FIXME: consider non-code __text sections as foldable? + bool isFoldable = (!onlyCfStrings || isCfStringSection(isec)) && + (isCodeSection(isec) || isFoldableWithAddendsRemoved || + isGccExceptTabSection(isec)) && + !isec->keepUnique && !isec->hasAltEntry && + !isec->shouldOmitFromOutput() && hasFoldableFlags; + if (isFoldable) { + foldable.push_back(isec); + for (Defined *d : isec->symbols) + if (d->unwindEntry) + foldable.push_back(d->unwindEntry); + + // Some sections have embedded addends that foil ICF's hashing / equality + // checks. (We can ignore embedded addends when doing ICF because the same + // information gets recorded in our Reloc structs.) We therefore create a + // mutable copy of the section data and zero out the embedded addends + // before performing any hashing / equality checks. + if (isFoldableWithAddendsRemoved) { + // We have to do this copying serially as the BumpPtrAllocator is not + // thread-safe. FIXME: Make a thread-safe allocator. + MutableArrayRef copy = isec->data.copy(bAlloc()); + for (const Reloc &r : isec->relocs) + target->relocateOne(copy.data() + r.offset, r, /*va=*/0, + /*relocVA=*/0); + isec->data = copy; + } + } else if (!isEhFrameSection(isec)) { + // EH frames are gathered as foldables from unwindEntry above; give a + // unique ID to everything else. isec->icfEqClass[0] = ++icfUniqueID; + } } - parallelForEach(hashable, - [](ConcatInputSection *isec) { isec->hashForICF(); }); + parallelForEach(foldable, [](ConcatInputSection *isec) { + assert(isec->icfEqClass[0] == 0); // don't overwrite a unique ID! + // Turn-on the top bit to guarantee that valid hashes have no collisions + // with the small-integer unique IDs for ICF-ineligible sections + isec->icfEqClass[0] = xxHash64(isec->data) | (1ull << 31); + }); // Now that every input section is either hashed or marked as unique, run the // segregation algorithm to detect foldable subsections. - ICF(hashable).run(); + ICF(foldable).run(); } diff --git a/gnu/llvm/lld/MachO/ICF.h b/gnu/llvm/lld/MachO/ICF.h index 9500a946601..34ceb1cf284 100644 --- a/gnu/llvm/lld/MachO/ICF.h +++ b/gnu/llvm/lld/MachO/ICF.h @@ -9,15 +9,17 @@ #ifndef LLD_MACHO_ICF_H #define LLD_MACHO_ICF_H +#include "InputFiles.h" #include "lld/Common/LLVM.h" #include -namespace lld { -namespace macho { +namespace lld::macho { +class Symbol; -void foldIdenticalSections(); +void markAddrSigSymbols(); +void markSymAsAddrSig(Symbol *s); +void foldIdenticalSections(bool onlyCfStrings); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/InputFiles.cpp b/gnu/llvm/lld/MachO/InputFiles.cpp index a4fb9035193..e1289103faf 100644 --- a/gnu/llvm/lld/MachO/InputFiles.cpp +++ b/gnu/llvm/lld/MachO/InputFiles.cpp @@ -45,6 +45,7 @@ #include "Config.h" #include "Driver.h" #include "Dwarf.h" +#include "EhFrame.h" #include "ExportTrie.h" #include "InputSection.h" #include "MachOStructs.h" @@ -56,20 +57,25 @@ #include "SyntheticSections.h" #include "Target.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/DWARF.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/iterator.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/LTO/LTO.h" +#include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/TextAPI/Architecture.h" #include "llvm/TextAPI/InterfaceFile.h" +#include +#include + using namespace llvm; using namespace llvm::MachO; using namespace llvm::support::endian; @@ -92,6 +98,10 @@ std::string lld::toString(const InputFile *f) { return (f->archiveName + "(" + path::filename(f->getName()) + ")").str(); } +std::string lld::toString(const Section &sec) { + return (toString(sec.file) + ":(" + sec.name + ")").str(); +} + SetVector macho::inputFiles; std::unique_ptr macho::tar; int InputFile::idCount = 0; @@ -109,10 +119,11 @@ static std::vector getPlatformInfos(const InputFile *input) { const char *hdr = input->mb.getBufferStart(); + // "Zippered" object files can have multiple LC_BUILD_VERSION load commands. std::vector platformInfos; for (auto *cmd : findCommands(hdr, LC_BUILD_VERSION)) { PlatformInfo info; - info.target.Platform = static_cast(cmd->platform); + info.target.Platform = static_cast(cmd->platform); info.minimum = decodeVersion(cmd->minos); platformInfos.emplace_back(std::move(info)); } @@ -122,16 +133,16 @@ static std::vector getPlatformInfos(const InputFile *input) { PlatformInfo info; switch (cmd->cmd) { case LC_VERSION_MIN_MACOSX: - info.target.Platform = PlatformKind::macOS; + info.target.Platform = PLATFORM_MACOS; break; case LC_VERSION_MIN_IPHONEOS: - info.target.Platform = PlatformKind::iOS; + info.target.Platform = PLATFORM_IOS; break; case LC_VERSION_MIN_TVOS: - info.target.Platform = PlatformKind::tvOS; + info.target.Platform = PLATFORM_TVOS; break; case LC_VERSION_MIN_WATCHOS: - info.target.Platform = PlatformKind::watchOS; + info.target.Platform = PLATFORM_WATCHOS; break; } info.minimum = decodeVersion(cmd->version); @@ -173,12 +184,23 @@ static bool checkCompatibility(const InputFile *input) { return true; } +// This cache mostly exists to store system libraries (and .tbds) as they're +// loaded, rather than the input archives, which are already cached at a higher +// level, and other files like the filelist that are only read once. +// Theoretically this caching could be more efficient by hoisting it, but that +// would require altering many callers to track the state. +DenseMap macho::cachedReads; // Open a given file path and return it as a memory-mapped file. -Optional macho::readFile(StringRef path) { +std::optional macho::readFile(StringRef path) { + CachedHashStringRef key(path); + auto entry = cachedReads.find(key); + if (entry != cachedReads.end()) + return entry->second; + ErrorOr> mbOrErr = MemoryBuffer::getFile(path); if (std::error_code ec = mbOrErr.getError()) { error("cannot open " + path + ": " + ec.message()); - return None; + return std::nullopt; } std::unique_ptr &mb = *mbOrErr; @@ -192,24 +214,38 @@ Optional macho::readFile(StringRef path) { read32be(&hdr->magic) != FAT_MAGIC) { if (tar) tar->append(relativeToRoot(path), mbref.getBuffer()); - return mbref; + return cachedReads[key] = mbref; } + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); + // Object files and archive files may be fat files, which contain multiple // real files for different CPU ISAs. Here, we search for a file that matches // with the current link target and returns it as a MemoryBufferRef. const auto *arch = reinterpret_cast(buf + sizeof(*hdr)); + auto getArchName = [](uint32_t cpuType, uint32_t cpuSubtype) { + return getArchitectureName(getArchitectureFromCpuType(cpuType, cpuSubtype)); + }; + std::vector archs; for (uint32_t i = 0, n = read32be(&hdr->nfat_arch); i < n; ++i) { if (reinterpret_cast(arch + i + 1) > buf + mbref.getBufferSize()) { error(path + ": fat_arch struct extends beyond end of file"); - return None; + return std::nullopt; } - if (read32be(&arch[i].cputype) != static_cast(target->cpuType) || - read32be(&arch[i].cpusubtype) != target->cpuSubtype) + uint32_t cpuType = read32be(&arch[i].cputype); + uint32_t cpuSubtype = + read32be(&arch[i].cpusubtype) & ~MachO::CPU_SUBTYPE_MASK; + + // FIXME: LD64 has a more complex fallback logic here. + // Consider implementing that as well? + if (cpuType != static_cast(target->cpuType) || + cpuSubtype != target->cpuSubtype) { + archs.emplace_back(getArchName(cpuType, cpuSubtype)); continue; + } uint32_t offset = read32be(&arch[i].offset); uint32_t size = read32be(&arch[i].size); @@ -217,86 +253,194 @@ Optional macho::readFile(StringRef path) { error(path + ": slice extends beyond end of file"); if (tar) tar->append(relativeToRoot(path), mbref.getBuffer()); - return MemoryBufferRef(StringRef(buf + offset, size), path.copy(bAlloc)); + return cachedReads[key] = MemoryBufferRef(StringRef(buf + offset, size), + path.copy(bAlloc)); } - error("unable to find matching architecture in " + path); - return None; + auto targetArchName = getArchName(target->cpuType, target->cpuSubtype); + warn(path + ": ignoring file because it is universal (" + join(archs, ",") + + ") but does not contain the " + targetArchName + " architecture"); + return std::nullopt; } InputFile::InputFile(Kind kind, const InterfaceFile &interface) - : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {} + : id(idCount++), fileKind(kind), name(saver().save(interface.getPath())) {} + +// Some sections comprise of fixed-size records, so instead of splitting them at +// symbol boundaries, we split them based on size. Records are distinct from +// literals in that they may contain references to other sections, instead of +// being leaf nodes in the InputSection graph. +// +// Note that "record" is a term I came up with. In contrast, "literal" is a term +// used by the Mach-O format. +static std::optional getRecordSize(StringRef segname, StringRef name) { + if (name == section_names::compactUnwind) { + if (segname == segment_names::ld) + return target->wordSize == 8 ? 32 : 20; + } + if (!config->dedupStrings) + return {}; + + if (name == section_names::cfString && segname == segment_names::data) + return target->wordSize == 8 ? 32 : 16; + + if (config->icfLevel == ICFLevel::none) + return {}; + + if (name == section_names::objcClassRefs && segname == segment_names::data) + return target->wordSize; + + if (name == section_names::objcSelrefs && segname == segment_names::data) + return target->wordSize; + return {}; +} -template -void ObjFile::parseSections(ArrayRef
sections) { - subsections.reserve(sections.size()); +static Error parseCallGraph(ArrayRef data, + std::vector &callGraph) { + TimeTraceScope timeScope("Parsing call graph section"); + BinaryStreamReader reader(data, support::little); + while (!reader.empty()) { + uint32_t fromIndex, toIndex; + uint64_t count; + if (Error err = reader.readInteger(fromIndex)) + return err; + if (Error err = reader.readInteger(toIndex)) + return err; + if (Error err = reader.readInteger(count)) + return err; + callGraph.emplace_back(fromIndex, toIndex, count); + } + return Error::success(); +} + +// Parse the sequence of sections within a single LC_SEGMENT(_64). +// Split each section into subsections. +template +void ObjFile::parseSections(ArrayRef sectionHeaders) { + sections.reserve(sectionHeaders.size()); auto *buf = reinterpret_cast(mb.getBufferStart()); - for (const Section &sec : sections) { + for (const SectionHeader &sec : sectionHeaders) { StringRef name = StringRef(sec.sectname, strnlen(sec.sectname, sizeof(sec.sectname))); StringRef segname = StringRef(sec.segname, strnlen(sec.segname, sizeof(sec.segname))); - ArrayRef data = {isZeroFill(sec.flags) ? nullptr - : buf + sec.offset, - static_cast(sec.size)}; + sections.push_back(make
(this, segname, name, sec.flags, sec.addr)); if (sec.align >= 32) { error("alignment " + std::to_string(sec.align) + " of section " + name + " is too large"); - subsections.push_back({}); continue; } + Section §ion = *sections.back(); uint32_t align = 1 << sec.align; - uint32_t flags = sec.flags; - - if (sectionType(sec.flags) == S_CSTRING_LITERALS || - (config->dedupLiterals && isWordLiteralSection(sec.flags))) { - if (sec.nreloc && config->dedupLiterals) - fatal(toString(this) + " contains relocations in " + sec.segname + "," + - sec.sectname + - ", so LLD cannot deduplicate literals. Try re-running without " - "--deduplicate-literals."); - - InputSection *isec; - if (sectionType(sec.flags) == S_CSTRING_LITERALS) { - isec = - make(segname, name, this, data, align, flags); - // FIXME: parallelize this? - cast(isec)->splitIntoPieces(); - } else { - isec = make(segname, name, this, data, align, - flags); + ArrayRef data = {isZeroFill(sec.flags) ? nullptr + : buf + sec.offset, + static_cast(sec.size)}; + + auto splitRecords = [&](size_t recordSize) -> void { + if (data.empty()) + return; + Subsections &subsections = section.subsections; + subsections.reserve(data.size() / recordSize); + for (uint64_t off = 0; off < data.size(); off += recordSize) { + auto *isec = make( + section, data.slice(off, std::min(data.size(), recordSize)), align); + subsections.push_back({off, isec}); } - subsections.push_back({{0, isec}}); - } else if (config->icfLevel != ICFLevel::none && - (name == section_names::cfString && - segname == segment_names::data)) { - uint64_t literalSize = target->wordSize == 8 ? 32 : 16; - subsections.push_back({}); - SubsectionMap &subsecMap = subsections.back(); - for (uint64_t off = 0; off < data.size(); off += literalSize) - subsecMap.push_back( - {off, make(segname, name, this, - data.slice(off, literalSize), align, - flags)}); + section.doneSplitting = true; + }; + + if (sectionType(sec.flags) == S_CSTRING_LITERALS) { + if (sec.nreloc) + fatal(toString(this) + ": " + sec.segname + "," + sec.sectname + + " contains relocations, which is unsupported"); + bool dedupLiterals = + name == section_names::objcMethname || config->dedupStrings; + InputSection *isec = + make(section, data, align, dedupLiterals); + // FIXME: parallelize this? + cast(isec)->splitIntoPieces(); + section.subsections.push_back({0, isec}); + } else if (isWordLiteralSection(sec.flags)) { + if (sec.nreloc) + fatal(toString(this) + ": " + sec.segname + "," + sec.sectname + + " contains relocations, which is unsupported"); + InputSection *isec = make(section, data, align); + section.subsections.push_back({0, isec}); + } else if (auto recordSize = getRecordSize(segname, name)) { + splitRecords(*recordSize); + } else if (name == section_names::ehFrame && + segname == segment_names::text) { + splitEhFrames(data, *sections.back()); + } else if (segname == segment_names::llvm) { + if (config->callGraphProfileSort && name == section_names::cgProfile) + checkError(parseCallGraph(data, callGraph)); + // ld64 does not appear to emit contents from sections within the __LLVM + // segment. Symbols within those sections point to bitcode metadata + // instead of actual symbols. Global symbols within those sections could + // have the same name without causing duplicate symbol errors. To avoid + // spurious duplicate symbol errors, we do not parse these sections. + // TODO: Evaluate whether the bitcode metadata is needed. + } else if (name == section_names::objCImageInfo && + segname == segment_names::data) { + objCImageInfo = data; } else { - auto *isec = - make(segname, name, this, data, align, flags); - if (!(isDebugSection(isec->getFlags()) && - isec->getSegName() == segment_names::dwarf)) { - subsections.push_back({{0, isec}}); - } else { + if (name == section_names::addrSig) + addrSigSection = sections.back(); + + auto *isec = make(section, data, align); + if (isDebugSection(isec->getFlags()) && + isec->getSegName() == segment_names::dwarf) { // Instead of emitting DWARF sections, we emit STABS symbols to the // object files that contain them. We filter them out early to avoid - // parsing their relocations unnecessarily. But we must still push an - // empty map to ensure the indices line up for the remaining sections. - subsections.push_back({}); + // parsing their relocations unnecessarily. debugSections.push_back(isec); + } else { + section.subsections.push_back({0, isec}); } } } } +void ObjFile::splitEhFrames(ArrayRef data, Section &ehFrameSection) { + EhReader reader(this, data, /*dataOff=*/0); + size_t off = 0; + while (off < reader.size()) { + uint64_t frameOff = off; + uint64_t length = reader.readLength(&off); + if (length == 0) + break; + uint64_t fullLength = length + (off - frameOff); + off += length; + // We hard-code an alignment of 1 here because we don't actually want our + // EH frames to be aligned to the section alignment. EH frame decoders don't + // expect this alignment. Moreover, each EH frame must start where the + // previous one ends, and where it ends is indicated by the length field. + // Unless we update the length field (troublesome), we should keep the + // alignment to 1. + // Note that we still want to preserve the alignment of the overall section, + // just not of the individual EH frames. + ehFrameSection.subsections.push_back( + {frameOff, make(ehFrameSection, + data.slice(frameOff, fullLength), + /*align=*/1)}); + } + ehFrameSection.doneSplitting = true; +} + +template +static Section *findContainingSection(const std::vector
§ions, + T *offset) { + static_assert(std::is_same::value || + std::is_same::value, + "unexpected type for offset"); + auto it = std::prev(llvm::upper_bound( + sections, *offset, + [](uint64_t value, const Section *sec) { return value < sec->addr; })); + *offset -= (*it)->addr; + return *it; +} + // Find the subsection corresponding to the greatest section offset that is <= // that of the given offset. // @@ -304,18 +448,35 @@ void ObjFile::parseSections(ArrayRef
sections) { // any subsection splitting has occurred). It will be updated to represent the // same location as an offset relative to the start of the containing // subsection. -static InputSection *findContainingSubsection(SubsectionMap &map, - uint64_t *offset) { +template +static InputSection *findContainingSubsection(const Section §ion, + T *offset) { + static_assert(std::is_same::value || + std::is_same::value, + "unexpected type for offset"); auto it = std::prev(llvm::upper_bound( - map, *offset, [](uint64_t value, SubsectionEntry subsecEntry) { - return value < subsecEntry.offset; - })); + section.subsections, *offset, + [](uint64_t value, Subsection subsec) { return value < subsec.offset; })); *offset -= it->offset; return it->isec; } -template -static bool validateRelocationInfo(InputFile *file, const Section &sec, +// Find a symbol at offset `off` within `isec`. +static Defined *findSymbolAtOffset(const ConcatInputSection *isec, + uint64_t off) { + auto it = llvm::lower_bound(isec->symbols, off, [](Defined *d, uint64_t off) { + return d->value < off; + }); + // The offset should point at the exact address of a symbol (with no addend.) + if (it == isec->symbols.end() || (*it)->value != off) { + assert(isec->wasCoalesced); + return nullptr; + } + return *it; +} + +template +static bool validateRelocationInfo(InputFile *file, const SectionHeader &sec, relocation_info rel) { const RelocAttrs &relocAttrs = target->getRelocAttrs(rel.r_type); bool valid = true; @@ -346,14 +507,15 @@ static bool validateRelocationInfo(InputFile *file, const Section &sec, return valid; } -template -void ObjFile::parseRelocations(ArrayRef
sectionHeaders, - const Section &sec, SubsectionMap &subsecMap) { +template +void ObjFile::parseRelocations(ArrayRef sectionHeaders, + const SectionHeader &sec, Section §ion) { auto *buf = reinterpret_cast(mb.getBufferStart()); ArrayRef relInfos( reinterpret_cast(buf + sec.reloff), sec.nreloc); - auto subsecIt = subsecMap.rbegin(); + Subsections &subsections = section.subsections; + auto subsecIt = subsections.rbegin(); for (size_t i = 0; i < relInfos.size(); i++) { // Paired relocations serve as Mach-O's method for attaching a // supplemental datum to a primary relocation record. ELF does not @@ -369,19 +531,20 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, // ARM64_RELOC_BRANCH26 or ARM64_RELOC_PAGE21/PAGEOFF12 holds the // base symbolic address. // - // Note: X86 does not use *_RELOC_ADDEND because it can embed an - // addend into the instruction stream. On X86, a relocatable address - // field always occupies an entire contiguous sequence of byte(s), - // so there is no need to merge opcode bits with address - // bits. Therefore, it's easy and convenient to store addends in the - // instruction-stream bytes that would otherwise contain zeroes. By - // contrast, RISC ISAs such as ARM64 mix opcode bits with with - // address bits so that bitwise arithmetic is necessary to extract - // and insert them. Storing addends in the instruction stream is - // possible, but inconvenient and more costly at link time. + // Note: X86 does not use *_RELOC_ADDEND because it can embed an addend into + // the instruction stream. On X86, a relocatable address field always + // occupies an entire contiguous sequence of byte(s), so there is no need to + // merge opcode bits with address bits. Therefore, it's easy and convenient + // to store addends in the instruction-stream bytes that would otherwise + // contain zeroes. By contrast, RISC ISAs such as ARM64 mix opcode bits with + // address bits so that bitwise arithmetic is necessary to extract and + // insert them. Storing addends in the instruction stream is possible, but + // inconvenient and more costly at link time. - int64_t pairedAddend = 0; relocation_info relInfo = relInfos[i]; + bool isSubtrahend = + target->hasAttr(relInfo.r_type, RelocAttrBits::SUBTRAHEND); + int64_t pairedAddend = 0; if (target->hasAttr(relInfo.r_type, RelocAttrBits::ADDEND)) { pairedAddend = SignExtend64<24>(relInfo.r_symbolnum); relInfo = relInfos[++i]; @@ -392,8 +555,6 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, if (relInfo.r_address & R_SCATTERED) fatal("TODO: Scattered relocations not supported"); - bool isSubtrahend = - target->hasAttr(relInfo.r_type, RelocAttrBits::SUBTRAHEND); int64_t embeddedAddend = target->getEmbeddedAddend(mb, sec.offset, relInfo); assert(!(embeddedAddend && pairedAddend)); int64_t totalAddend = pairedAddend + embeddedAddend; @@ -407,7 +568,8 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, r.addend = isSubtrahend ? 0 : totalAddend; } else { assert(!isSubtrahend); - const Section &referentSec = sectionHeaders[relInfo.r_symbolnum - 1]; + const SectionHeader &referentSecHead = + sectionHeaders[relInfo.r_symbolnum - 1]; uint64_t referentOffset; if (relInfo.r_pcrel) { // The implicit addend for pcrel section relocations is the pcrel offset @@ -417,14 +579,14 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, // have pcrel section relocations. We may want to factor this out into // the arch-specific .cpp file. assert(target->hasAttr(r.type, RelocAttrBits::BYTE4)); - referentOffset = - sec.addr + relInfo.r_address + 4 + totalAddend - referentSec.addr; + referentOffset = sec.addr + relInfo.r_address + 4 + totalAddend - + referentSecHead.addr; } else { // The addend for a non-pcrel relocation is its absolute address. - referentOffset = totalAddend - referentSec.addr; + referentOffset = totalAddend - referentSecHead.addr; } - SubsectionMap &referentSubsecMap = subsections[relInfo.r_symbolnum - 1]; - r.referent = findContainingSubsection(referentSubsecMap, &referentOffset); + r.referent = findContainingSubsection(*sections[relInfo.r_symbolnum - 1], + &referentOffset); r.addend = referentOffset; } @@ -434,14 +596,14 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, // unsorted relocations (in `-r` mode), so we have a fallback for that // uncommon case. InputSection *subsec; - while (subsecIt != subsecMap.rend() && subsecIt->offset > r.offset) + while (subsecIt != subsections.rend() && subsecIt->offset > r.offset) ++subsecIt; - if (subsecIt == subsecMap.rend() || + if (subsecIt == subsections.rend() || subsecIt->offset + subsecIt->isec->getSize() <= r.offset) { - subsec = findContainingSubsection(subsecMap, &r.offset); + subsec = findContainingSubsection(section, &r.offset); // Now that we know the relocs are unsorted, avoid trying the 'fast path' // for the other relocations. - subsecIt = subsecMap.rend(); + subsecIt = subsections.rend(); } else { subsec = subsecIt->isec; r.offset -= subsecIt->offset; @@ -462,10 +624,8 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, } else { uint64_t referentOffset = totalAddend - sectionHeaders[minuendInfo.r_symbolnum - 1].addr; - SubsectionMap &referentSubsecMap = - subsections[minuendInfo.r_symbolnum - 1]; - p.referent = - findContainingSubsection(referentSubsecMap, &referentOffset); + p.referent = findContainingSubsection( + *sections[minuendInfo.r_symbolnum - 1], &referentOffset); p.addend = referentOffset; } subsec->relocs.push_back(p); @@ -473,10 +633,16 @@ void ObjFile::parseRelocations(ArrayRef
sectionHeaders, } } +// Symbols with `l` or `L` as a prefix are linker-private and never appear in +// the output. +static bool isPrivateLabel(StringRef name) { + return name.startswith("l") || name.startswith("L"); +} + template static macho::Symbol *createDefined(const NList &sym, StringRef name, InputSection *isec, uint64_t value, - uint64_t size) { + uint64_t size, bool forceHidden) { // Symbol scope is determined by sym.n_type & (N_EXT | N_PEXT): // N_EXT: Global symbols. These go in the symbol table during the link, // and also in the export table of the output so that the dynamic @@ -495,7 +661,10 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name, (sym.n_desc & (N_WEAK_DEF | N_WEAK_REF)) == (N_WEAK_DEF | N_WEAK_REF); if (sym.n_type & N_EXT) { - bool isPrivateExtern = sym.n_type & N_PEXT; + // -load_hidden makes us treat global symbols as linkage unit scoped. + // Duplicates are reported but the symbol does not go in the export trie. + bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden; + // lld's behavior for merging symbols is slightly different from ld64: // ld64 picks the winning symbol based on several criteria (see // pickBetweenRegularAtoms() in ld64's SymbolTable.cpp), while lld @@ -520,23 +689,27 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name, // with ld64's semantics, because it means the non-private-extern // definition will continue to take priority if more private extern // definitions are encountered. With lld's semantics there's no observable - // difference between a symbol that's isWeakDefCanBeHidden or one that's - // privateExtern -- neither makes it into the dynamic symbol table. So just - // promote isWeakDefCanBeHidden to isPrivateExtern here. - if (isWeakDefCanBeHidden) + // difference between a symbol that's isWeakDefCanBeHidden(autohide) or one + // that's privateExtern -- neither makes it into the dynamic symbol table, + // unless the autohide symbol is explicitly exported. + // But if a symbol is both privateExtern and autohide then it can't + // be exported. + // So we nullify the autohide flag when privateExtern is present + // and promote the symbol to privateExtern when it is not already. + if (isWeakDefCanBeHidden && isPrivateExtern) + isWeakDefCanBeHidden = false; + else if (isWeakDefCanBeHidden) isPrivateExtern = true; - return symtab->addDefined( name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF, isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF, - sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP); + sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP, + isWeakDefCanBeHidden); } - - assert(!isWeakDefCanBeHidden && - "weak_def_can_be_hidden on already-hidden symbol?"); + bool includeInSymtab = !isPrivateLabel(name) && !isEhFrameSection(isec); return make( name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF, - /*isExternal=*/false, /*isPrivateExtern=*/false, + /*isExternal=*/false, /*isPrivateExtern=*/false, includeInSymtab, sym.n_desc & N_ARM_THUMB_DEF, sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP); } @@ -545,37 +718,52 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name, // InputSection. They cannot be weak. template static macho::Symbol *createAbsolute(const NList &sym, InputFile *file, - StringRef name) { + StringRef name, bool forceHidden) { if (sym.n_type & N_EXT) { + bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden; return symtab->addDefined( name, file, nullptr, sym.n_value, /*size=*/0, - /*isWeakDef=*/false, sym.n_type & N_PEXT, sym.n_desc & N_ARM_THUMB_DEF, - /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP); + /*isWeakDef=*/false, isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF, + /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP, + /*isWeakDefCanBeHidden=*/false); } return make(name, file, nullptr, sym.n_value, /*size=*/0, /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false, - sym.n_desc & N_ARM_THUMB_DEF, + /*includeInSymtab=*/true, sym.n_desc & N_ARM_THUMB_DEF, /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP); } template macho::Symbol *ObjFile::parseNonSectionSymbol(const NList &sym, - StringRef name) { + const char *strtab) { + StringRef name = StringRef(strtab + sym.n_strx); uint8_t type = sym.n_type & N_TYPE; + bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden; switch (type) { case N_UNDF: return sym.n_value == 0 ? symtab->addUndefined(name, this, sym.n_desc & N_WEAK_REF) : symtab->addCommon(name, this, sym.n_value, 1 << GET_COMM_ALIGN(sym.n_desc), - sym.n_type & N_PEXT); + isPrivateExtern); case N_ABS: - return createAbsolute(sym, this, name); + return createAbsolute(sym, this, name, forceHidden); + case N_INDR: { + // Not much point in making local aliases -- relocs in the current file can + // just refer to the actual symbol itself. ld64 ignores these symbols too. + if (!(sym.n_type & N_EXT)) + return nullptr; + StringRef aliasedName = StringRef(strtab + sym.n_value); + // isPrivateExtern is the only symbol flag that has an impact on the final + // aliased symbol. + auto alias = make(this, name, aliasedName, isPrivateExtern); + aliases.push_back(alias); + return alias; + } case N_PBUD: - case N_INDR: - error("TODO: support symbols of type " + std::to_string(type)); + error("TODO: support symbols of type N_PBUD"); return nullptr; case N_SECT: llvm_unreachable( @@ -585,8 +773,7 @@ macho::Symbol *ObjFile::parseNonSectionSymbol(const NList &sym, } } -template -static bool isUndef(const NList &sym) { +template static bool isUndef(const NList &sym) { return (sym.n_type & N_TYPE) == N_UNDF && sym.n_value == 0; } @@ -597,7 +784,7 @@ void ObjFile::parseSymbols(ArrayRef sectionHeaders, using NList = typename LP::nlist; // Groups indices of the symbols by the sections that contain them. - std::vector> symbolsBySection(subsections.size()); + std::vector> symbolsBySection(sections.size()); symbols.resize(nList.size()); SmallVector undefineds; for (uint32_t i = 0; i < nList.size(); ++i) { @@ -608,65 +795,78 @@ void ObjFile::parseSymbols(ArrayRef sectionHeaders, if (sym.n_type & N_STAB) continue; - StringRef name = strtab + sym.n_strx; if ((sym.n_type & N_TYPE) == N_SECT) { - SubsectionMap &subsecMap = subsections[sym.n_sect - 1]; + Subsections &subsections = sections[sym.n_sect - 1]->subsections; // parseSections() may have chosen not to parse this section. - if (subsecMap.empty()) + if (subsections.empty()) continue; symbolsBySection[sym.n_sect - 1].push_back(i); } else if (isUndef(sym)) { undefineds.push_back(i); } else { - symbols[i] = parseNonSectionSymbol(sym, name); + symbols[i] = parseNonSectionSymbol(sym, strtab); } } - for (size_t i = 0; i < subsections.size(); ++i) { - SubsectionMap &subsecMap = subsections[i]; - if (subsecMap.empty()) + for (size_t i = 0; i < sections.size(); ++i) { + Subsections &subsections = sections[i]->subsections; + if (subsections.empty()) continue; - std::vector &symbolIndices = symbolsBySection[i]; uint64_t sectionAddr = sectionHeaders[i].addr; uint32_t sectionAlign = 1u << sectionHeaders[i].align; - InputSection *isec = subsecMap.back().isec; - // __cfstring has already been split into subsections during + // Some sections have already been split into subsections during // parseSections(), so we simply need to match Symbols to the corresponding // subsection here. - if (config->icfLevel != ICFLevel::none && isCfStringSection(isec)) { + if (sections[i]->doneSplitting) { for (size_t j = 0; j < symbolIndices.size(); ++j) { - uint32_t symIndex = symbolIndices[j]; + const uint32_t symIndex = symbolIndices[j]; const NList &sym = nList[symIndex]; StringRef name = strtab + sym.n_strx; uint64_t symbolOffset = sym.n_value - sectionAddr; - InputSection *isec = findContainingSubsection(subsecMap, &symbolOffset); + InputSection *isec = + findContainingSubsection(*sections[i], &symbolOffset); if (symbolOffset != 0) { - error(toString(this) + ": __cfstring contains symbol " + name + + error(toString(*sections[i]) + ": symbol " + name + " at misaligned offset"); continue; } - symbols[symIndex] = createDefined(sym, name, isec, 0, isec->getSize()); + symbols[symIndex] = + createDefined(sym, name, isec, 0, isec->getSize(), forceHidden); } continue; } + sections[i]->doneSplitting = true; + + auto getSymName = [strtab](const NList& sym) -> StringRef { + return StringRef(strtab + sym.n_strx); + }; // Calculate symbol sizes and create subsections by splitting the sections // along symbol boundaries. - // We populate subsecMap by repeatedly splitting the last (highest address) - // subsection. + // We populate subsections by repeatedly splitting the last (highest + // address) subsection. llvm::stable_sort(symbolIndices, [&](uint32_t lhs, uint32_t rhs) { + // Put private-label symbols that have no flags after other symbols at the + // same address. + StringRef lhsName = getSymName(nList[lhs]); + StringRef rhsName = getSymName(nList[rhs]); + if (nList[lhs].n_value == nList[rhs].n_value) { + if (isPrivateLabel(lhsName) && isPrivateLabel(rhsName)) + return nList[lhs].n_desc > nList[rhs].n_desc; + return !isPrivateLabel(lhsName) && isPrivateLabel(rhsName); + } return nList[lhs].n_value < nList[rhs].n_value; }); - SubsectionEntry subsecEntry = subsecMap.back(); for (size_t j = 0; j < symbolIndices.size(); ++j) { - uint32_t symIndex = symbolIndices[j]; + const uint32_t symIndex = symbolIndices[j]; const NList &sym = nList[symIndex]; - StringRef name = strtab + sym.n_strx; - InputSection *isec = subsecEntry.isec; + StringRef name = getSymName(sym); + Subsection &subsec = subsections.back(); + InputSection *isec = subsec.isec; - uint64_t subsecAddr = sectionAddr + subsecEntry.offset; + uint64_t subsecAddr = sectionAddr + subsec.offset; size_t symbolOffset = sym.n_value - subsecAddr; uint64_t symbolSize = j + 1 < symbolIndices.size() @@ -681,14 +881,23 @@ void ObjFile::parseSymbols(ArrayRef sectionHeaders, // 4. If we have a literal section (e.g. __cstring and __literal4). if (!subsectionsViaSymbols || symbolOffset == 0 || sym.n_desc & N_ALT_ENTRY || !isa(isec)) { - symbols[symIndex] = - createDefined(sym, name, isec, symbolOffset, symbolSize); + isec->hasAltEntry = symbolOffset != 0; + // If we have an private-label symbol that's an alias, and that alias + // doesn't have any flags of its own, then we can just reuse the aliased + // symbol. Our sorting step above ensures that any such symbols will + // appear after the non-private-label ones. See weak-def-alias-ignored.s + // for the motivation behind this. + if (symbolOffset == 0 && isPrivateLabel(name) && j != 0 && + sym.n_desc == 0) + symbols[symIndex] = symbols[symbolIndices[j - 1]]; + else + symbols[symIndex] = createDefined(sym, name, isec, symbolOffset, + symbolSize, forceHidden); continue; } auto *concatIsec = cast(isec); auto *nextIsec = make(*concatIsec); - nextIsec->numRefs = 0; nextIsec->wasCoalesced = false; if (isZeroFill(isec->getFlags())) { // Zero-fill sections have NULL data.data() non-zero data.size() @@ -701,14 +910,13 @@ void ObjFile::parseSymbols(ArrayRef sectionHeaders, // By construction, the symbol will be at offset zero in the new // subsection. - symbols[symIndex] = - createDefined(sym, name, nextIsec, /*value=*/0, symbolSize); + symbols[symIndex] = createDefined(sym, name, nextIsec, /*value=*/0, + symbolSize, forceHidden); // TODO: ld64 appears to preserve the original alignment as well as each // subsection's offset from the last aligned address. We should consider // emulating that behavior. nextIsec->align = MinAlign(sectionAlign, sym.n_value); - subsecMap.push_back({sym.n_value - sectionAddr, nextIsec}); - subsecEntry = subsecMap.back(); + subsections.push_back({sym.n_value - sectionAddr, nextIsec}); } } @@ -718,11 +926,8 @@ void ObjFile::parseSymbols(ArrayRef sectionHeaders, // symbol resolution behavior. In addition, a set of interconnected symbols // will all be resolved to the same file, instead of being resolved to // different files. - for (unsigned i : undefineds) { - const NList &sym = nList[i]; - StringRef name = strtab + sym.n_strx; - symbols[i] = parseNonSectionSymbol(sym, name); - } + for (unsigned i : undefineds) + symbols[i] = parseNonSectionSymbol(nList[i], strtab); } OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName, @@ -730,36 +935,52 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName, : InputFile(OpaqueKind, mb) { const auto *buf = reinterpret_cast(mb.getBufferStart()); ArrayRef data = {buf, mb.getBufferSize()}; - ConcatInputSection *isec = - make(segName.take_front(16), sectName.take_front(16), - /*file=*/this, data); + sections.push_back(make
(/*file=*/this, segName.take_front(16), + sectName.take_front(16), + /*flags=*/0, /*addr=*/0)); + Section §ion = *sections.back(); + ConcatInputSection *isec = make(section, data); isec->live = true; - subsections.push_back({{0, isec}}); + section.subsections.push_back({0, isec}); } -ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName) - : InputFile(ObjKind, mb), modTime(modTime) { +ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName, + bool lazy, bool forceHidden) + : InputFile(ObjKind, mb, lazy), modTime(modTime), forceHidden(forceHidden) { this->archiveName = std::string(archiveName); - if (target->wordSize == 8) - parse(); - else - parse(); + if (lazy) { + if (target->wordSize == 8) + parseLazy(); + else + parseLazy(); + } else { + if (target->wordSize == 8) + parse(); + else + parse(); + } } template void ObjFile::parse() { using Header = typename LP::mach_header; using SegmentCommand = typename LP::segment_command; - using Section = typename LP::section; + using SectionHeader = typename LP::section; using NList = typename LP::nlist; auto *buf = reinterpret_cast(mb.getBufferStart()); auto *hdr = reinterpret_cast(mb.getBufferStart()); - Architecture arch = getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype); - if (arch != config->arch()) { - error(toString(this) + " has architecture " + getArchitectureName(arch) + - " which is incompatible with target architecture " + - getArchitectureName(config->arch())); + uint32_t cpuType; + std::tie(cpuType, std::ignore) = getCPUTypeFromArchitecture(config->arch()); + if (hdr->cputype != cpuType) { + Architecture arch = + getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype); + auto msg = config->errorForArchMismatch + ? static_cast(error) + : warn; + msg(toString(this) + " has architecture " + getArchitectureName(arch) + + " which is incompatible with target architecture " + + getArchitectureName(config->arch())); return; } @@ -772,11 +993,11 @@ template void ObjFile::parse() { parseLCLinkerOption(this, cmd->count, data); } - ArrayRef
sectionHeaders; + ArrayRef sectionHeaders; if (const load_command *cmd = findCommand(hdr, LP::segmentLCType)) { auto *c = reinterpret_cast(cmd); - sectionHeaders = - ArrayRef
{reinterpret_cast(c + 1), c->nsects}; + sectionHeaders = ArrayRef{ + reinterpret_cast(c + 1), c->nsects}; parseSections(sectionHeaders); } @@ -792,13 +1013,51 @@ template void ObjFile::parse() { // The relocations may refer to the symbols, so we parse them after we have // parsed all the symbols. - for (size_t i = 0, n = subsections.size(); i < n; ++i) - if (!subsections[i].empty()) - parseRelocations(sectionHeaders, sectionHeaders[i], subsections[i]); + for (size_t i = 0, n = sections.size(); i < n; ++i) + if (!sections[i]->subsections.empty()) + parseRelocations(sectionHeaders, sectionHeaders[i], *sections[i]); parseDebugInfo(); - if (config->emitDataInCodeInfo) - parseDataInCode(); + + Section *ehFrameSection = nullptr; + Section *compactUnwindSection = nullptr; + for (Section *sec : sections) { + Section **s = StringSwitch
(sec->name) + .Case(section_names::compactUnwind, &compactUnwindSection) + .Case(section_names::ehFrame, &ehFrameSection) + .Default(nullptr); + if (s) + *s = sec; + } + if (compactUnwindSection) + registerCompactUnwind(*compactUnwindSection); + if (ehFrameSection) + registerEhFrames(*ehFrameSection); +} + +template void ObjFile::parseLazy() { + using Header = typename LP::mach_header; + using NList = typename LP::nlist; + + auto *buf = reinterpret_cast(mb.getBufferStart()); + auto *hdr = reinterpret_cast(mb.getBufferStart()); + const load_command *cmd = findCommand(hdr, LC_SYMTAB); + if (!cmd) + return; + auto *c = reinterpret_cast(cmd); + ArrayRef nList(reinterpret_cast(buf + c->symoff), + c->nsyms); + const char *strtab = reinterpret_cast(buf) + c->stroff; + symbols.resize(nList.size()); + for (const auto &[i, sym] : llvm::enumerate(nList)) { + if ((sym.n_type & N_EXT) && !isUndef(sym)) { + // TODO: Bound checking + StringRef name = strtab + sym.n_strx; + symbols[i] = symtab->addLazyObject(name, *this); + if (!lazy) + break; + } + } } void ObjFile::parseDebugInfo() { @@ -806,6 +1065,8 @@ void ObjFile::parseDebugInfo() { if (!dObj) return; + // We do not re-use the context from getDwarf() here as that function + // constructs an expensive DWARFCache object. auto *ctx = make( std::move(dObj), "", [&](Error err) { @@ -821,27 +1082,459 @@ void ObjFile::parseDebugInfo() { // FIXME: There can be more than one compile unit per object file. See // PR48637. auto it = units.begin(); - compileUnit = it->get(); + compileUnit = it != units.end() ? it->get() : nullptr; } -void ObjFile::parseDataInCode() { +ArrayRef ObjFile::getDataInCode() const { const auto *buf = reinterpret_cast(mb.getBufferStart()); const load_command *cmd = findCommand(buf, LC_DATA_IN_CODE); if (!cmd) - return; + return {}; const auto *c = reinterpret_cast(cmd); - dataInCodeEntries = { - reinterpret_cast(buf + c->dataoff), - c->datasize / sizeof(data_in_code_entry)}; - assert(is_sorted(dataInCodeEntries, [](const data_in_code_entry &lhs, - const data_in_code_entry &rhs) { - return lhs.offset < rhs.offset; - })); + return {reinterpret_cast(buf + c->dataoff), + c->datasize / sizeof(data_in_code_entry)}; +} + +ArrayRef ObjFile::getOptimizationHints() const { + const auto *buf = reinterpret_cast(mb.getBufferStart()); + if (auto *cmd = + findCommand(buf, LC_LINKER_OPTIMIZATION_HINT)) + return {buf + cmd->dataoff, cmd->datasize}; + return {}; } +// Create pointers from symbols to their associated compact unwind entries. +void ObjFile::registerCompactUnwind(Section &compactUnwindSection) { + for (const Subsection &subsection : compactUnwindSection.subsections) { + ConcatInputSection *isec = cast(subsection.isec); + // Hack!! Each compact unwind entry (CUE) has its UNSIGNED relocations embed + // their addends in its data. Thus if ICF operated naively and compared the + // entire contents of each CUE, entries with identical unwind info but e.g. + // belonging to different functions would never be considered equivalent. To + // work around this problem, we remove some parts of the data containing the + // embedded addends. In particular, we remove the function address and LSDA + // pointers. Since these locations are at the start and end of the entry, + // we can do this using a simple, efficient slice rather than performing a + // copy. We are not losing any information here because the embedded + // addends have already been parsed in the corresponding Reloc structs. + // + // Removing these pointers would not be safe if they were pointers to + // absolute symbols. In that case, there would be no corresponding + // relocation. However, (AFAIK) MC cannot emit references to absolute + // symbols for either the function address or the LSDA. However, it *can* do + // so for the personality pointer, so we are not slicing that field away. + // + // Note that we do not adjust the offsets of the corresponding relocations; + // instead, we rely on `relocateCompactUnwind()` to correctly handle these + // truncated input sections. + isec->data = isec->data.slice(target->wordSize, 8 + target->wordSize); + uint32_t encoding = read32le(isec->data.data() + sizeof(uint32_t)); + // llvm-mc omits CU entries for functions that need DWARF encoding, but + // `ld -r` doesn't. We can ignore them because we will re-synthesize these + // CU entries from the DWARF info during the output phase. + if ((encoding & static_cast(UNWIND_MODE_MASK)) == + target->modeDwarfEncoding) + continue; + + ConcatInputSection *referentIsec; + for (auto it = isec->relocs.begin(); it != isec->relocs.end();) { + Reloc &r = *it; + // CUE::functionAddress is at offset 0. Skip personality & LSDA relocs. + if (r.offset != 0) { + ++it; + continue; + } + uint64_t add = r.addend; + if (auto *sym = cast_or_null(r.referent.dyn_cast())) { + // Check whether the symbol defined in this file is the prevailing one. + // Skip if it is e.g. a weak def that didn't prevail. + if (sym->getFile() != this) { + ++it; + continue; + } + add += sym->value; + referentIsec = cast(sym->isec); + } else { + referentIsec = + cast(r.referent.dyn_cast()); + } + // Unwind info lives in __DATA, and finalization of __TEXT will occur + // before finalization of __DATA. Moreover, the finalization of unwind + // info depends on the exact addresses that it references. So it is safe + // for compact unwind to reference addresses in __TEXT, but not addresses + // in any other segment. + if (referentIsec->getSegName() != segment_names::text) + error(isec->getLocation(r.offset) + " references section " + + referentIsec->getName() + " which is not in segment __TEXT"); + // The functionAddress relocations are typically section relocations. + // However, unwind info operates on a per-symbol basis, so we search for + // the function symbol here. + Defined *d = findSymbolAtOffset(referentIsec, add); + if (!d) { + ++it; + continue; + } + d->unwindEntry = isec; + // Now that the symbol points to the unwind entry, we can remove the reloc + // that points from the unwind entry back to the symbol. + // + // First, the symbol keeps the unwind entry alive (and not vice versa), so + // this keeps dead-stripping simple. + // + // Moreover, it reduces the work that ICF needs to do to figure out if + // functions with unwind info are foldable. + // + // However, this does make it possible for ICF to fold CUEs that point to + // distinct functions (if the CUEs are otherwise identical). + // UnwindInfoSection takes care of this by re-duplicating the CUEs so that + // each one can hold a distinct functionAddress value. + // + // Given that clang emits relocations in reverse order of address, this + // relocation should be at the end of the vector for most of our input + // object files, so this erase() is typically an O(1) operation. + it = isec->relocs.erase(it); + } + } +} + +struct CIE { + macho::Symbol *personalitySymbol = nullptr; + bool fdesHaveAug = false; + uint8_t lsdaPtrSize = 0; // 0 => no LSDA + uint8_t funcPtrSize = 0; +}; + +static uint8_t pointerEncodingToSize(uint8_t enc) { + switch (enc & 0xf) { + case dwarf::DW_EH_PE_absptr: + return target->wordSize; + case dwarf::DW_EH_PE_sdata4: + return 4; + case dwarf::DW_EH_PE_sdata8: + // ld64 doesn't actually support sdata8, but this seems simple enough... + return 8; + default: + return 0; + }; +} + +static CIE parseCIE(const InputSection *isec, const EhReader &reader, + size_t off) { + // Handling the full generality of possible DWARF encodings would be a major + // pain. We instead take advantage of our knowledge of how llvm-mc encodes + // DWARF and handle just that. + constexpr uint8_t expectedPersonalityEnc = + dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_sdata4; + + CIE cie; + uint8_t version = reader.readByte(&off); + if (version != 1 && version != 3) + fatal("Expected CIE version of 1 or 3, got " + Twine(version)); + StringRef aug = reader.readString(&off); + reader.skipLeb128(&off); // skip code alignment + reader.skipLeb128(&off); // skip data alignment + reader.skipLeb128(&off); // skip return address register + reader.skipLeb128(&off); // skip aug data length + uint64_t personalityAddrOff = 0; + for (char c : aug) { + switch (c) { + case 'z': + cie.fdesHaveAug = true; + break; + case 'P': { + uint8_t personalityEnc = reader.readByte(&off); + if (personalityEnc != expectedPersonalityEnc) + reader.failOn(off, "unexpected personality encoding 0x" + + Twine::utohexstr(personalityEnc)); + personalityAddrOff = off; + off += 4; + break; + } + case 'L': { + uint8_t lsdaEnc = reader.readByte(&off); + cie.lsdaPtrSize = pointerEncodingToSize(lsdaEnc); + if (cie.lsdaPtrSize == 0) + reader.failOn(off, "unexpected LSDA encoding 0x" + + Twine::utohexstr(lsdaEnc)); + break; + } + case 'R': { + uint8_t pointerEnc = reader.readByte(&off); + cie.funcPtrSize = pointerEncodingToSize(pointerEnc); + if (cie.funcPtrSize == 0 || !(pointerEnc & dwarf::DW_EH_PE_pcrel)) + reader.failOn(off, "unexpected pointer encoding 0x" + + Twine::utohexstr(pointerEnc)); + break; + } + default: + break; + } + } + if (personalityAddrOff != 0) { + auto personalityRelocIt = + llvm::find_if(isec->relocs, [=](const macho::Reloc &r) { + return r.offset == personalityAddrOff; + }); + if (personalityRelocIt == isec->relocs.end()) + reader.failOn(off, "Failed to locate relocation for personality symbol"); + cie.personalitySymbol = personalityRelocIt->referent.get(); + } + return cie; +} + +// EH frame target addresses may be encoded as pcrel offsets. However, instead +// of using an actual pcrel reloc, ld64 emits subtractor relocations instead. +// This function recovers the target address from the subtractors, essentially +// performing the inverse operation of EhRelocator. +// +// Concretely, we expect our relocations to write the value of `PC - +// target_addr` to `PC`. `PC` itself is denoted by a minuend relocation that +// points to a symbol plus an addend. +// +// It is important that the minuend relocation point to a symbol within the +// same section as the fixup value, since sections may get moved around. +// +// For example, for arm64, llvm-mc emits relocations for the target function +// address like so: +// +// ltmp: +// +// ... +// +// ... multiple FDEs ... +// +// +// ... +// +// If any of the FDEs in `multiple FDEs` get dead-stripped, then `FDE start` +// will move to an earlier address, and `ltmp + pcrel offset` will no longer +// reflect an accurate pcrel value. To avoid this problem, we "canonicalize" +// our relocation by adding an `EH_Frame` symbol at `FDE start`, and updating +// the reloc to be `target function address - (EH_Frame + new pcrel offset)`. +// +// If `Invert` is set, then we instead expect `target_addr - PC` to be written +// to `PC`. +template +Defined * +targetSymFromCanonicalSubtractor(const InputSection *isec, + std::vector::iterator relocIt) { + macho::Reloc &subtrahend = *relocIt; + macho::Reloc &minuend = *std::next(relocIt); + assert(target->hasAttr(subtrahend.type, RelocAttrBits::SUBTRAHEND)); + assert(target->hasAttr(minuend.type, RelocAttrBits::UNSIGNED)); + // Note: pcSym may *not* be exactly at the PC; there's usually a non-zero + // addend. + auto *pcSym = cast(subtrahend.referent.get()); + Defined *target = + cast_or_null(minuend.referent.dyn_cast()); + if (!pcSym) { + auto *targetIsec = + cast(minuend.referent.get()); + target = findSymbolAtOffset(targetIsec, minuend.addend); + } + if (Invert) + std::swap(pcSym, target); + if (pcSym->isec == isec) { + if (pcSym->value - (Invert ? -1 : 1) * minuend.addend != subtrahend.offset) + fatal("invalid FDE relocation in __eh_frame"); + } else { + // Ensure the pcReloc points to a symbol within the current EH frame. + // HACK: we should really verify that the original relocation's semantics + // are preserved. In particular, we should have + // `oldSym->value + oldOffset == newSym + newOffset`. However, we don't + // have an easy way to access the offsets from this point in the code; some + // refactoring is needed for that. + macho::Reloc &pcReloc = Invert ? minuend : subtrahend; + pcReloc.referent = isec->symbols[0]; + assert(isec->symbols[0]->value == 0); + minuend.addend = pcReloc.offset * (Invert ? 1LL : -1LL); + } + return target; +} + +Defined *findSymbolAtAddress(const std::vector
§ions, + uint64_t addr) { + Section *sec = findContainingSection(sections, &addr); + auto *isec = cast(findContainingSubsection(*sec, &addr)); + return findSymbolAtOffset(isec, addr); +} + +// For symbols that don't have compact unwind info, associate them with the more +// general-purpose (and verbose) DWARF unwind info found in __eh_frame. +// +// This requires us to parse the contents of __eh_frame. See EhFrame.h for a +// description of its format. +// +// While parsing, we also look for what MC calls "abs-ified" relocations -- they +// are relocations which are implicitly encoded as offsets in the section data. +// We convert them into explicit Reloc structs so that the EH frames can be +// handled just like a regular ConcatInputSection later in our output phase. +// +// We also need to handle the case where our input object file has explicit +// relocations. This is the case when e.g. it's the output of `ld -r`. We only +// look for the "abs-ified" relocation if an explicit relocation is absent. +void ObjFile::registerEhFrames(Section &ehFrameSection) { + DenseMap cieMap; + for (const Subsection &subsec : ehFrameSection.subsections) { + auto *isec = cast(subsec.isec); + uint64_t isecOff = subsec.offset; + + // Subtractor relocs require the subtrahend to be a symbol reloc. Ensure + // that all EH frames have an associated symbol so that we can generate + // subtractor relocs that reference them. + if (isec->symbols.size() == 0) + make("EH_Frame", isec->getFile(), isec, /*value=*/0, + isec->getSize(), /*isWeakDef=*/false, /*isExternal=*/false, + /*isPrivateExtern=*/false, /*includeInSymtab=*/false, + /*isThumb=*/false, /*isReferencedDynamically=*/false, + /*noDeadStrip=*/false); + else if (isec->symbols[0]->value != 0) + fatal("found symbol at unexpected offset in __eh_frame"); + + EhReader reader(this, isec->data, subsec.offset); + size_t dataOff = 0; // Offset from the start of the EH frame. + reader.skipValidLength(&dataOff); // readLength() already validated this. + // cieOffOff is the offset from the start of the EH frame to the cieOff + // value, which is itself an offset from the current PC to a CIE. + const size_t cieOffOff = dataOff; + + EhRelocator ehRelocator(isec); + auto cieOffRelocIt = llvm::find_if( + isec->relocs, [=](const Reloc &r) { return r.offset == cieOffOff; }); + InputSection *cieIsec = nullptr; + if (cieOffRelocIt != isec->relocs.end()) { + // We already have an explicit relocation for the CIE offset. + cieIsec = + targetSymFromCanonicalSubtractor(isec, cieOffRelocIt) + ->isec; + dataOff += sizeof(uint32_t); + } else { + // If we haven't found a relocation, then the CIE offset is most likely + // embedded in the section data (AKA an "abs-ified" reloc.). Parse that + // and generate a Reloc struct. + uint32_t cieMinuend = reader.readU32(&dataOff); + if (cieMinuend == 0) { + cieIsec = isec; + } else { + uint32_t cieOff = isecOff + dataOff - cieMinuend; + cieIsec = findContainingSubsection(ehFrameSection, &cieOff); + if (cieIsec == nullptr) + fatal("failed to find CIE"); + } + if (cieIsec != isec) + ehRelocator.makeNegativePcRel(cieOffOff, cieIsec->symbols[0], + /*length=*/2); + } + if (cieIsec == isec) { + cieMap[cieIsec] = parseCIE(isec, reader, dataOff); + continue; + } + + assert(cieMap.count(cieIsec)); + const CIE &cie = cieMap[cieIsec]; + // Offset of the function address within the EH frame. + const size_t funcAddrOff = dataOff; + uint64_t funcAddr = reader.readPointer(&dataOff, cie.funcPtrSize) + + ehFrameSection.addr + isecOff + funcAddrOff; + uint32_t funcLength = reader.readPointer(&dataOff, cie.funcPtrSize); + size_t lsdaAddrOff = 0; // Offset of the LSDA address within the EH frame. + std::optional lsdaAddrOpt; + if (cie.fdesHaveAug) { + reader.skipLeb128(&dataOff); + lsdaAddrOff = dataOff; + if (cie.lsdaPtrSize != 0) { + uint64_t lsdaOff = reader.readPointer(&dataOff, cie.lsdaPtrSize); + if (lsdaOff != 0) // FIXME possible to test this? + lsdaAddrOpt = ehFrameSection.addr + isecOff + lsdaAddrOff + lsdaOff; + } + } + + auto funcAddrRelocIt = isec->relocs.end(); + auto lsdaAddrRelocIt = isec->relocs.end(); + for (auto it = isec->relocs.begin(); it != isec->relocs.end(); ++it) { + if (it->offset == funcAddrOff) + funcAddrRelocIt = it++; // Found subtrahend; skip over minuend reloc + else if (lsdaAddrOpt && it->offset == lsdaAddrOff) + lsdaAddrRelocIt = it++; // Found subtrahend; skip over minuend reloc + } + + Defined *funcSym; + if (funcAddrRelocIt != isec->relocs.end()) { + funcSym = targetSymFromCanonicalSubtractor(isec, funcAddrRelocIt); + // Canonicalize the symbol. If there are multiple symbols at the same + // address, we want both `registerEhFrame` and `registerCompactUnwind` + // to register the unwind entry under same symbol. + // This is not particularly efficient, but we should run into this case + // infrequently (only when handling the output of `ld -r`). + if (funcSym->isec) + funcSym = findSymbolAtOffset(cast(funcSym->isec), + funcSym->value); + } else { + funcSym = findSymbolAtAddress(sections, funcAddr); + ehRelocator.makePcRel(funcAddrOff, funcSym, target->p2WordSize); + } + // The symbol has been coalesced, or already has a compact unwind entry. + if (!funcSym || funcSym->getFile() != this || funcSym->unwindEntry) { + // We must prune unused FDEs for correctness, so we cannot rely on + // -dead_strip being enabled. + isec->live = false; + continue; + } + + InputSection *lsdaIsec = nullptr; + if (lsdaAddrRelocIt != isec->relocs.end()) { + lsdaIsec = targetSymFromCanonicalSubtractor(isec, lsdaAddrRelocIt)->isec; + } else if (lsdaAddrOpt) { + uint64_t lsdaAddr = *lsdaAddrOpt; + Section *sec = findContainingSection(sections, &lsdaAddr); + lsdaIsec = + cast(findContainingSubsection(*sec, &lsdaAddr)); + ehRelocator.makePcRel(lsdaAddrOff, lsdaIsec, target->p2WordSize); + } + + fdes[isec] = {funcLength, cie.personalitySymbol, lsdaIsec}; + funcSym->unwindEntry = isec; + ehRelocator.commit(); + } + + // __eh_frame is marked as S_ATTR_LIVE_SUPPORT in input files, because FDEs + // are normally required to be kept alive if they reference a live symbol. + // However, we've explicitly created a dependency from a symbol to its FDE, so + // dead-stripping will just work as usual, and S_ATTR_LIVE_SUPPORT will only + // serve to incorrectly prevent us from dead-stripping duplicate FDEs for a + // live symbol (e.g. if there were multiple weak copies). Remove this flag to + // let dead-stripping proceed correctly. + ehFrameSection.flags &= ~S_ATTR_LIVE_SUPPORT; +} + +std::string ObjFile::sourceFile() const { + SmallString<261> dir(compileUnit->getCompilationDir()); + StringRef sep = sys::path::get_separator(); + // We don't use `path::append` here because we want an empty `dir` to result + // in an absolute path. `append` would give us a relative path for that case. + if (!dir.endswith(sep)) + dir += sep; + return (dir + compileUnit->getUnitDIE().getShortName()).str(); +} + +lld::DWARFCache *ObjFile::getDwarf() { + llvm::call_once(initDwarf, [this]() { + auto dwObj = DwarfObject::create(this); + if (!dwObj) + return; + dwarfCache = std::make_unique(std::make_unique( + std::move(dwObj), "", + [&](Error err) { warn(getName() + ": " + toString(std::move(err))); }, + [&](Error warning) { + warn(getName() + ": " + toString(std::move(warning))); + })); + }); + + return dwarfCache.get(); +} // The path can point to either a dylib or a .tbd file. static DylibFile *loadDylib(StringRef path, DylibFile *umbrella) { - Optional mbref = readFile(path); + std::optional mbref = readFile(path); if (!mbref) { error("could not read dylib file at " + path); return nullptr; @@ -871,10 +1564,11 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella, for (StringRef dir : config->frameworkSearchPaths) { SmallString<128> candidate = dir; path::append(candidate, frameworkName); - if (Optional dylibPath = resolveDylibPath(candidate)) + if (std::optional dylibPath = + resolveDylibPath(candidate.str())) return loadDylib(*dylibPath, umbrella); } - } else if (Optional dylibPath = findPathCombination( + } else if (std::optional dylibPath = findPathCombination( stem, config->librarySearchPaths, {".tbd", ".dylib"})) return loadDylib(*dylibPath, umbrella); } @@ -882,7 +1576,7 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella, // 2. As absolute path. if (path::is_absolute(path, path::Style::posix)) for (StringRef root : config->systemLibraryRoots) - if (Optional dylibPath = + if (std::optional dylibPath = resolveDylibPath((root + path).str())) return loadDylib(*dylibPath, umbrella); @@ -912,7 +1606,7 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella, path::remove_filename(newPath); } path::append(newPath, rpath, path.drop_front(strlen("@rpath/"))); - if (Optional dylibPath = resolveDylibPath(newPath)) + if (std::optional dylibPath = resolveDylibPath(newPath.str())) return loadDylib(*dylibPath, umbrella); } } @@ -923,14 +1617,15 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella, make_pointee_range(currentTopLevelTapi->documents())) { assert(child.documents().empty()); if (path == child.getInstallName()) { - auto file = make(child, umbrella); + auto file = make(child, umbrella, /*isBundleLoader=*/false, + /*explicitlyLinked=*/false); file->parseReexports(child); return file; } } } - if (Optional dylibPath = resolveDylibPath(path)) + if (std::optional dylibPath = resolveDylibPath(path)) return loadDylib(*dylibPath, umbrella); return nullptr; @@ -956,23 +1651,23 @@ static bool isImplicitlyLinked(StringRef path) { return false; } -static void loadReexport(StringRef path, DylibFile *umbrella, +void DylibFile::loadReexport(StringRef path, DylibFile *umbrella, const InterfaceFile *currentTopLevelTapi) { DylibFile *reexport = findDylib(path, umbrella, currentTopLevelTapi); if (!reexport) - error("unable to locate re-export with install name " + path); + error(toString(this) + ": unable to locate re-export with install name " + + path); } DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella, - bool isBundleLoader) + bool isBundleLoader, bool explicitlyLinked) : InputFile(DylibKind, mb), refState(RefState::Unreferenced), - isBundleLoader(isBundleLoader) { + explicitlyLinked(explicitlyLinked), isBundleLoader(isBundleLoader) { assert(!isBundleLoader || !umbrella); if (umbrella == nullptr) umbrella = this; this->umbrella = umbrella; - auto *buf = reinterpret_cast(mb.getBufferStart()); auto *hdr = reinterpret_cast(mb.getBufferStart()); // Initialize installName. @@ -985,7 +1680,7 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella, } else if (!isBundleLoader) { // macho_executable and macho_bundle don't have LC_ID_DYLIB, // so it's OK. - error("dylib " + toString(this) + " missing LC_ID_DYLIB load command"); + error(toString(this) + ": dylib missing LC_ID_DYLIB load command"); return; } @@ -1007,24 +1702,56 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella, // Initialize symbols. exportingFile = isImplicitlyLinked(installName) ? this : this->umbrella; - if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) { - auto *c = reinterpret_cast(cmd); - parseTrie(buf + c->export_off, c->export_size, - [&](const Twine &name, uint64_t flags) { - StringRef savedName = saver.save(name); - if (handleLDSymbol(savedName)) - return; - bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; - symbols.push_back(symtab->addDylib(savedName, exportingFile, - isWeakDef, isTlv)); - }); + + const auto *dyldInfo = findCommand(hdr, LC_DYLD_INFO_ONLY); + const auto *exportsTrie = + findCommand(hdr, LC_DYLD_EXPORTS_TRIE); + if (dyldInfo && exportsTrie) { + // It's unclear what should happen in this case. Maybe we should only error + // out if the two load commands refer to different data? + error(toString(this) + + ": dylib has both LC_DYLD_INFO_ONLY and LC_DYLD_EXPORTS_TRIE"); + return; + } else if (dyldInfo) { + parseExportedSymbols(dyldInfo->export_off, dyldInfo->export_size); + } else if (exportsTrie) { + parseExportedSymbols(exportsTrie->dataoff, exportsTrie->datasize); } else { - error("LC_DYLD_INFO_ONLY not found in " + toString(this)); + error("No LC_DYLD_INFO_ONLY or LC_DYLD_EXPORTS_TRIE found in " + + toString(this)); return; } } +void DylibFile::parseExportedSymbols(uint32_t offset, uint32_t size) { + struct TrieEntry { + StringRef name; + uint64_t flags; + }; + + auto *buf = reinterpret_cast(mb.getBufferStart()); + std::vector entries; + // Find all the $ld$* symbols to process first. + parseTrie(buf + offset, size, [&](const Twine &name, uint64_t flags) { + StringRef savedName = saver().save(name); + if (handleLDSymbol(savedName)) + return; + entries.push_back({savedName, flags}); + }); + + // Process the "normal" symbols. + for (TrieEntry &entry : entries) { + if (exportingFile->hiddenSymbols.contains(CachedHashStringRef(entry.name))) + continue; + + bool isWeakDef = entry.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + bool isTlv = entry.flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; + + symbols.push_back( + symtab->addDylib(entry.name, exportingFile, isWeakDef, isTlv)); + } +} + void DylibFile::parseLoadCommands(MemoryBufferRef mb) { auto *hdr = reinterpret_cast(mb.getBufferStart()); const uint8_t *p = reinterpret_cast(mb.getBufferStart()) + @@ -1057,24 +1784,63 @@ void DylibFile::parseLoadCommands(MemoryBufferRef mb) { } } -// Some versions of XCode ship with .tbd files that don't have the right +// Some versions of Xcode ship with .tbd files that don't have the right // platform settings. -static constexpr std::array skipPlatformChecks{ +constexpr std::array skipPlatformChecks{ "/usr/lib/system/libsystem_kernel.dylib", "/usr/lib/system/libsystem_platform.dylib", "/usr/lib/system/libsystem_pthread.dylib"}; +static bool skipPlatformCheckForCatalyst(const InterfaceFile &interface, + bool explicitlyLinked) { + // Catalyst outputs can link against implicitly linked macOS-only libraries. + if (config->platform() != PLATFORM_MACCATALYST || explicitlyLinked) + return false; + return is_contained(interface.targets(), + MachO::Target(config->arch(), PLATFORM_MACOS)); +} + +static bool isArchABICompatible(ArchitectureSet archSet, + Architecture targetArch) { + uint32_t cpuType; + uint32_t targetCpuType; + std::tie(targetCpuType, std::ignore) = getCPUTypeFromArchitecture(targetArch); + + return llvm::any_of(archSet, [&](const auto &p) { + std::tie(cpuType, std::ignore) = getCPUTypeFromArchitecture(p); + return cpuType == targetCpuType; + }); +} + +static bool isTargetPlatformArchCompatible( + InterfaceFile::const_target_range interfaceTargets, Target target) { + if (is_contained(interfaceTargets, target)) + return true; + + if (config->forceExactCpuSubtypeMatch) + return false; + + ArchitectureSet archSet; + for (const auto &p : interfaceTargets) + if (p.Platform == target.Platform) + archSet.set(p.Arch); + if (archSet.empty()) + return false; + + return isArchABICompatible(archSet, target.Arch); +} + DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, - bool isBundleLoader) + bool isBundleLoader, bool explicitlyLinked) : InputFile(DylibKind, interface), refState(RefState::Unreferenced), - isBundleLoader(isBundleLoader) { + explicitlyLinked(explicitlyLinked), isBundleLoader(isBundleLoader) { // FIXME: Add test for the missing TBD code path. if (umbrella == nullptr) umbrella = this; this->umbrella = umbrella; - installName = saver.save(interface.getInstallName()); + installName = saver().save(interface.getInstallName()); compatibilityVersion = interface.getCompatibilityVersion().rawValue(); currentVersion = interface.getCurrentVersion().rawValue(); @@ -1083,7 +1849,9 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, inputFiles.insert(this); if (!is_contained(skipPlatformChecks, installName) && - !is_contained(interface.targets(), config->platformInfo.target)) { + !isTargetPlatformArchCompatible(interface.targets(), + config->platformInfo.target) && + !skipPlatformCheckForCatalyst(interface, explicitlyLinked)) { error(toString(this) + " is incompatible with " + std::string(config->platformInfo.target)); return; @@ -1092,51 +1860,108 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, checkAppExtensionSafety(interface.isApplicationExtensionSafe()); exportingFile = isImplicitlyLinked(installName) ? this : umbrella; - auto addSymbol = [&](const Twine &name) -> void { - symbols.push_back(symtab->addDylib(saver.save(name), exportingFile, - /*isWeakDef=*/false, - /*isTlv=*/false)); + auto addSymbol = [&](const llvm::MachO::Symbol &symbol, + const Twine &name) -> void { + StringRef savedName = saver().save(name); + if (exportingFile->hiddenSymbols.contains(CachedHashStringRef(savedName))) + return; + + symbols.push_back(symtab->addDylib(savedName, exportingFile, + symbol.isWeakDefined(), + symbol.isThreadLocalValue())); }; - // TODO(compnerd) filter out symbols based on the target platform - // TODO: handle weak defs, thread locals + + std::vector normalSymbols; + normalSymbols.reserve(interface.symbolsCount()); for (const auto *symbol : interface.symbols()) { - if (!symbol->getArchitectures().has(config->arch())) + if (!isArchABICompatible(symbol->getArchitectures(), config->arch())) continue; - if (handleLDSymbol(symbol->getName())) continue; switch (symbol->getKind()) { case SymbolKind::GlobalSymbol: - addSymbol(symbol->getName()); + case SymbolKind::ObjectiveCClass: + case SymbolKind::ObjectiveCClassEHType: + case SymbolKind::ObjectiveCInstanceVariable: + normalSymbols.push_back(symbol); + } + } + + // TODO(compnerd) filter out symbols based on the target platform + for (const auto *symbol : normalSymbols) { + switch (symbol->getKind()) { + case SymbolKind::GlobalSymbol: + addSymbol(*symbol, symbol->getName()); break; case SymbolKind::ObjectiveCClass: // XXX ld64 only creates these symbols when -ObjC is passed in. We may // want to emulate that. - addSymbol(objc::klass + symbol->getName()); - addSymbol(objc::metaclass + symbol->getName()); + addSymbol(*symbol, objc::klass + symbol->getName()); + addSymbol(*symbol, objc::metaclass + symbol->getName()); break; case SymbolKind::ObjectiveCClassEHType: - addSymbol(objc::ehtype + symbol->getName()); + addSymbol(*symbol, objc::ehtype + symbol->getName()); break; case SymbolKind::ObjectiveCInstanceVariable: - addSymbol(objc::ivar + symbol->getName()); + addSymbol(*symbol, objc::ivar + symbol->getName()); break; } } } +DylibFile::DylibFile(DylibFile *umbrella) + : InputFile(DylibKind, MemoryBufferRef{}), refState(RefState::Unreferenced), + explicitlyLinked(false), isBundleLoader(false) { + if (umbrella == nullptr) + umbrella = this; + this->umbrella = umbrella; +} + void DylibFile::parseReexports(const InterfaceFile &interface) { const InterfaceFile *topLevel = interface.getParent() == nullptr ? &interface : interface.getParent(); - for (InterfaceFileRef intfRef : interface.reexportedLibraries()) { + for (const InterfaceFileRef &intfRef : interface.reexportedLibraries()) { InterfaceFile::const_target_range targets = intfRef.targets(); if (is_contained(skipPlatformChecks, intfRef.getInstallName()) || - is_contained(targets, config->platformInfo.target)) + isTargetPlatformArchCompatible(targets, config->platformInfo.target)) loadReexport(intfRef.getInstallName(), exportingFile, topLevel); } } +bool DylibFile::isExplicitlyLinked() const { + if (!explicitlyLinked) + return false; + + // If this dylib was explicitly linked, but at least one of the symbols + // of the synthetic dylibs it created via $ld$previous symbols is + // referenced, then that synthetic dylib fulfils the explicit linkedness + // and we can deadstrip this dylib if it's unreferenced. + for (const auto *dylib : extraDylibs) + if (dylib->isReferenced()) + return false; + + return true; +} + +DylibFile *DylibFile::getSyntheticDylib(StringRef installName, + uint32_t currentVersion, + uint32_t compatVersion) { + for (DylibFile *dylib : extraDylibs) + if (dylib->installName == installName) { + // FIXME: Check what to do if different $ld$previous symbols + // request the same dylib, but with different versions. + return dylib; + } + + auto *dylib = make(umbrella == this ? nullptr : umbrella); + dylib->installName = saver().save(installName); + dylib->currentVersion = currentVersion; + dylib->compatibilityVersion = compatVersion; + extraDylibs.push_back(dylib); + return dylib; +} + // $ld$ symbols modify the properties/behavior of the library (e.g. its install // name, compatibility version or hide/add symbols) for specific target // versions. @@ -1151,6 +1976,8 @@ bool DylibFile::handleLDSymbol(StringRef originalName) { handleLDPreviousSymbol(name, originalName); else if (action == "install_name") handleLDInstallNameSymbol(name, originalName); + else if (action == "hide") + handleLDHideSymbol(name, originalName); return true; } @@ -1170,10 +1997,9 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) { std::tie(platformStr, name) = name.split('$'); std::tie(startVersion, name) = name.split('$'); std::tie(endVersion, name) = name.split('$'); - std::tie(symbolName, rest) = name.split('$'); - // TODO: ld64 contains some logic for non-empty symbolName as well. - if (!symbolName.empty()) - return; + std::tie(symbolName, rest) = name.rsplit('$'); + + // FIXME: Does this do the right thing for zippered files? unsigned platform; if (platformStr.getAsInteger(10, platform) || platform != static_cast(config->platform())) @@ -1181,30 +2007,57 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) { VersionTuple start; if (start.tryParse(startVersion)) { - warn("failed to parse start version, symbol '" + originalName + - "' ignored"); + warn(toString(this) + ": failed to parse start version, symbol '" + + originalName + "' ignored"); return; } VersionTuple end; if (end.tryParse(endVersion)) { - warn("failed to parse end version, symbol '" + originalName + "' ignored"); + warn(toString(this) + ": failed to parse end version, symbol '" + + originalName + "' ignored"); return; } if (config->platformInfo.minimum < start || config->platformInfo.minimum >= end) return; - this->installName = saver.save(installName); - + // Initialized to compatibilityVersion for the symbolName branch below. + uint32_t newCompatibilityVersion = compatibilityVersion; + uint32_t newCurrentVersionForSymbol = currentVersion; if (!compatVersion.empty()) { VersionTuple cVersion; if (cVersion.tryParse(compatVersion)) { - warn("failed to parse compatibility version, symbol '" + originalName + + warn(toString(this) + + ": failed to parse compatibility version, symbol '" + originalName + "' ignored"); return; } - compatibilityVersion = encodeVersion(cVersion); + newCompatibilityVersion = encodeVersion(cVersion); + newCurrentVersionForSymbol = newCompatibilityVersion; + } + + if (!symbolName.empty()) { + // A $ld$previous$ symbol with symbol name adds a symbol with that name to + // a dylib with given name and version. + auto *dylib = getSyntheticDylib(installName, newCurrentVersionForSymbol, + newCompatibilityVersion); + + // The tbd file usually contains the $ld$previous symbol for an old version, + // and then the symbol itself later, for newer deployment targets, like so: + // symbols: [ + // '$ld$previous$/Another$$1$3.0$14.0$_zzz$', + // _zzz, + // ] + // Since the symbols are sorted, adding them to the symtab in the given + // order means the $ld$previous version of _zzz will prevail, as desired. + dylib->symbols.push_back(symtab->addDylib( + saver().save(symbolName), dylib, /*isWeakDef=*/false, /*isTlv=*/false)); + return; } + + // A $ld$previous$ symbol without symbol name modifies the dylib it's in. + this->installName = saver().save(installName); + this->compatibilityVersion = newCompatibilityVersion; } void DylibFile::handleLDInstallNameSymbol(StringRef name, @@ -1214,9 +2067,33 @@ void DylibFile::handleLDInstallNameSymbol(StringRef name, std::tie(condition, installName) = name.split('$'); VersionTuple version; if (!condition.consume_front("os") || version.tryParse(condition)) - warn("failed to parse os version, symbol '" + originalName + "' ignored"); + warn(toString(this) + ": failed to parse os version, symbol '" + + originalName + "' ignored"); else if (version == config->platformInfo.minimum) - this->installName = saver.save(installName); + this->installName = saver().save(installName); +} + +void DylibFile::handleLDHideSymbol(StringRef name, StringRef originalName) { + StringRef symbolName; + bool shouldHide = true; + if (name.startswith("os")) { + // If it's hidden based on versions. + name = name.drop_front(2); + StringRef minVersion; + std::tie(minVersion, symbolName) = name.split('$'); + VersionTuple versionTup; + if (versionTup.tryParse(minVersion)) { + warn(toString(this) + ": failed to parse hidden version, symbol `" + originalName + + "` ignored."); + return; + } + shouldHide = versionTup == config->platformInfo.minimum; + } else { + symbolName = name; + } + + if (shouldHide) + exportingFile->hiddenSymbols.insert(CachedHashStringRef(symbolName)); } void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const { @@ -1224,59 +2101,85 @@ void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const { warn("using '-application_extension' with unsafe dylib: " + toString(this)); } -ArchiveFile::ArchiveFile(std::unique_ptr &&f) - : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) { +ArchiveFile::ArchiveFile(std::unique_ptr &&f, bool forceHidden) + : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)), + forceHidden(forceHidden) {} + +void ArchiveFile::addLazySymbols() { for (const object::Archive::Symbol &sym : file->symbols()) - symtab->addLazy(sym.getName(), this, sym); + symtab->addLazyArchive(sym.getName(), this, sym); } -void ArchiveFile::fetch(const object::Archive::Symbol &sym) { - object::Archive::Child c = - CHECK(sym.getMember(), toString(this) + - ": could not get the member for symbol " + - toMachOString(sym)); +static Expected +loadArchiveMember(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName, + uint64_t offsetInArchive, bool forceHidden) { + if (config->zeroModTime) + modTime = 0; + + switch (identify_magic(mb.getBuffer())) { + case file_magic::macho_object: + return make(mb, modTime, archiveName, /*lazy=*/false, forceHidden); + case file_magic::bitcode: + return make(mb, archiveName, offsetInArchive, /*lazy=*/false, + forceHidden); + default: + return createStringError(inconvertibleErrorCode(), + mb.getBufferIdentifier() + + " has unhandled file type"); + } +} +Error ArchiveFile::fetch(const object::Archive::Child &c, StringRef reason) { if (!seen.insert(c.getChildOffset()).second) - return; + return Error::success(); - MemoryBufferRef mb = - CHECK(c.getMemoryBufferRef(), - toString(this) + - ": could not get the buffer for the member defining symbol " + - toMachOString(sym)); + Expected mb = c.getMemoryBufferRef(); + if (!mb) + return mb.takeError(); + // Thin archives refer to .o files, so --reproduce needs the .o files too. if (tar && c.getParent()->isThin()) - tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); + tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb->getBuffer()); + + Expected> modTime = c.getLastModified(); + if (!modTime) + return modTime.takeError(); - uint32_t modTime = toTimeT( - CHECK(c.getLastModified(), toString(this) + - ": could not get the modification time " - "for the member defining symbol " + - toMachOString(sym))); + Expected file = loadArchiveMember( + *mb, toTimeT(*modTime), getName(), c.getChildOffset(), forceHidden); + + if (!file) + return file.takeError(); + + inputFiles.insert(*file); + printArchiveMemberLoad(reason, *file); + return Error::success(); +} + +void ArchiveFile::fetch(const object::Archive::Symbol &sym) { + object::Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member defining symbol " + + toMachOString(sym)); // `sym` is owned by a LazySym, which will be replace<>()d by make // and become invalid after that call. Copy it to the stack so we can refer // to it later. const object::Archive::Symbol symCopy = sym; - if (Optional file = loadArchiveMember( - mb, modTime, getName(), /*objCOnly=*/false, c.getChildOffset())) { - inputFiles.insert(*file); - // ld64 doesn't demangle sym here even with -demangle. - // Match that: intentionally don't call toMachOString(). - printArchiveMemberLoad(symCopy.getName(), *file); - } + // ld64 doesn't demangle sym here even with -demangle. + // Match that: intentionally don't call toMachOString(). + if (Error e = fetch(c, symCopy.getName())) + error(toString(this) + ": could not get the member defining symbol " + + toMachOString(symCopy) + ": " + toString(std::move(e))); } static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym, BitcodeFile &file) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); - // TODO: support weak references if (objSym.isUndefined()) - return symtab->addUndefined(name, &file, /*isWeakRef=*/false); - - assert(!objSym.isCommon() && "TODO: support common symbols in LTO"); + return symtab->addUndefined(name, &file, /*isWeakRef=*/objSym.isWeak()); // TODO: Write a test demonstrating why computing isPrivateExtern before // LTO compilation is important. @@ -1291,37 +2194,91 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym, case GlobalValue::DefaultVisibility: break; } + isPrivateExtern = isPrivateExtern || objSym.canBeOmittedFromSymbolTable() || + file.forceHidden; + + if (objSym.isCommon()) + return symtab->addCommon(name, &file, objSym.getCommonSize(), + objSym.getCommonAlignment(), isPrivateExtern); return symtab->addDefined(name, &file, /*isec=*/nullptr, /*value=*/0, /*size=*/0, objSym.isWeak(), isPrivateExtern, /*isThumb=*/false, /*isReferencedDynamically=*/false, - /*noDeadStrip=*/false); + /*noDeadStrip=*/false, + /*isWeakDefCanBeHidden=*/false); } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : InputFile(BitcodeKind, mb) { + uint64_t offsetInArchive, bool lazy, bool forceHidden) + : InputFile(BitcodeKind, mb, lazy), forceHidden(forceHidden) { + this->archiveName = std::string(archiveName); 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 members with the same name are provided, this causes a // collision and ThinLTO can't proceed. // So, we append the archive name to disambiguate two members with the same // name from multiple different archives, and offset within the archive to // disambiguate two members of the same name from a single archive. - MemoryBufferRef mbref( - mb.getBuffer(), - saver.save(archiveName.empty() ? path - : archiveName + sys::path::filename(path) + - utostr(offsetInArchive))); + MemoryBufferRef mbref(mb.getBuffer(), + saver().save(archiveName.empty() + ? path + : archiveName + + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); + if (lazy) + parseLazy(); + else + parse(); +} +void BitcodeFile::parse() { // Convert LTO Symbols to LLD Symbols in order to perform resolution. The // "winning" symbol will then be marked as Prevailing at LTO compilation // time. + symbols.clear(); for (const lto::InputFile::Symbol &objSym : obj->symbols()) symbols.push_back(createBitcodeSymbol(objSym, *this)); } +void BitcodeFile::parseLazy() { + symbols.resize(obj->symbols().size()); + for (const auto &[i, objSym] : llvm::enumerate(obj->symbols())) { + if (!objSym.isUndefined()) { + symbols[i] = symtab->addLazyObject(saver().save(objSym.getName()), *this); + if (!lazy) + break; + } + } +} + +std::string macho::replaceThinLTOSuffix(StringRef path) { + auto [suffix, repl] = config->thinLTOObjectSuffixReplace; + if (path.consume_back(suffix)) + return (path + repl).str(); + return std::string(path); +} + +void macho::extract(InputFile &file, StringRef reason) { + if (!file.lazy) + return; + file.lazy = false; + + printArchiveMemberLoad(reason, &file); + if (auto *bitcode = dyn_cast(&file)) { + bitcode->parse(); + } else { + auto &f = cast(file); + if (target->wordSize == 8) + f.parse(); + else + f.parse(); + } +} + template void ObjFile::parse(); diff --git a/gnu/llvm/lld/MachO/InputFiles.h b/gnu/llvm/lld/MachO/InputFiles.h index 0101fb71c8a..66d46e46fa7 100644 --- a/gnu/llvm/lld/MachO/InputFiles.h +++ b/gnu/llvm/lld/MachO/InputFiles.h @@ -12,14 +12,17 @@ #include "MachOStructs.h" #include "Target.h" +#include "lld/Common/DWARF.h" #include "lld/Common/LLVM.h" #include "lld/Common/Memory.h" +#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SetVector.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Object/Archive.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Threading.h" #include "llvm/TextAPI/TextAPIReader.h" #include @@ -40,6 +43,8 @@ namespace macho { struct PlatformInfo; class ConcatInputSection; class Symbol; +class Defined; +class AliasSymbol; struct Reloc; enum class RefState : uint8_t; @@ -50,11 +55,52 @@ extern std::unique_ptr tar; // If .subsections_via_symbols is set, each InputSection will be split along // symbol boundaries. The field offset represents the offset of the subsection // from the start of the original pre-split InputSection. -struct SubsectionEntry { - uint64_t offset; - InputSection *isec; +struct Subsection { + uint64_t offset = 0; + InputSection *isec = nullptr; +}; + +using Subsections = std::vector; +class InputFile; + +class Section { +public: + InputFile *file; + StringRef segname; + StringRef name; + uint32_t flags; + uint64_t addr; + Subsections subsections; + + Section(InputFile *file, StringRef segname, StringRef name, uint32_t flags, + uint64_t addr) + : file(file), segname(segname), name(name), flags(flags), addr(addr) {} + // Ensure pointers to Sections are never invalidated. + Section(const Section &) = delete; + Section &operator=(const Section &) = delete; + Section(Section &&) = delete; + Section &operator=(Section &&) = delete; + +private: + // Whether we have already split this section into individual subsections. + // For sections that cannot be split (e.g. literal sections), this is always + // false. + bool doneSplitting = false; + friend class ObjFile; +}; + +// Represents a call graph profile edge. +struct CallGraphEntry { + // The index of the caller in the symbol table. + uint32_t fromIndex; + // The index of the callee in the symbol table. + uint32_t toIndex; + // Number of calls from callee to caller in the profile. + uint64_t count; + + CallGraphEntry(uint32_t fromIndex, uint32_t toIndex, uint64_t count) + : fromIndex(fromIndex), toIndex(toIndex), count(count) {} }; -using SubsectionMap = std::vector; class InputFile { public: @@ -69,21 +115,28 @@ public: virtual ~InputFile() = default; Kind kind() const { return fileKind; } StringRef getName() const { return name; } + static void resetIdCount() { idCount = 0; } MemoryBufferRef mb; std::vector symbols; - std::vector subsections; - // Provides an easy way to sort InputFiles deterministically. - const int id; + std::vector
sections; + ArrayRef objCImageInfo; // If not empty, this stores the name of the archive containing this file. // We use this string for creating error messages. std::string archiveName; + // Provides an easy way to sort InputFiles deterministically. + const int id; + + // True if this is a lazy ObjFile or BitcodeFile. + bool lazy = false; + protected: - InputFile(Kind kind, MemoryBufferRef mb) - : mb(mb), id(idCount++), fileKind(kind), name(mb.getBufferIdentifier()) {} + InputFile(Kind kind, MemoryBufferRef mb, bool lazy = false) + : mb(mb), id(idCount++), lazy(lazy), fileKind(kind), + name(mb.getBufferIdentifier()) {} InputFile(Kind, const llvm::MachO::InterfaceFile &); @@ -94,31 +147,55 @@ private: static int idCount; }; +struct FDE { + uint32_t funcLength; + Symbol *personality; + InputSection *lsda; +}; + // .o file class ObjFile final : public InputFile { public: - ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName); + ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName, + bool lazy = false, bool forceHidden = false); + ArrayRef getDataInCode() const; + ArrayRef getOptimizationHints() const; + template void parse(); + static bool classof(const InputFile *f) { return f->kind() == ObjKind; } + std::string sourceFile() const; + // Parses line table information for diagnostics. compileUnit should be used + // for other purposes. + lld::DWARFCache *getDwarf(); + llvm::DWARFUnit *compileUnit = nullptr; + std::unique_ptr dwarfCache; + Section *addrSigSection = nullptr; const uint32_t modTime; + bool forceHidden; std::vector debugSections; - ArrayRef dataInCodeEntries; + std::vector callGraph; + llvm::DenseMap fdes; + std::vector aliases; private: - template void parse(); - template void parseSections(ArrayRef
); + llvm::once_flag initDwarf; + template void parseLazy(); + template void parseSections(ArrayRef); template void parseSymbols(ArrayRef sectionHeaders, ArrayRef nList, const char *strtab, bool subsectionsViaSymbols); template - Symbol *parseNonSectionSymbol(const NList &sym, StringRef name); - template - void parseRelocations(ArrayRef
sectionHeaders, const Section &, - SubsectionMap &); + Symbol *parseNonSectionSymbol(const NList &sym, const char *strtab); + template + void parseRelocations(ArrayRef sectionHeaders, + const SectionHeader &, Section &); void parseDebugInfo(); - void parseDataInCode(); + void splitEhFrames(ArrayRef dataArr, Section &ehFrameSection); + void registerCompactUnwind(Section &compactUnwindSection); + void registerEhFrames(Section &ehFrameSection); }; // command-line -sectcreate file @@ -139,13 +216,17 @@ public: // to the root. On the other hand, if a dylib is being directly loaded // (through an -lfoo flag), then `umbrella` should be a nullptr. explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, - bool isBundleLoader = false); + bool isBundleLoader, bool explicitlyLinked); explicit DylibFile(const llvm::MachO::InterfaceFile &interface, - DylibFile *umbrella = nullptr, - bool isBundleLoader = false); + DylibFile *umbrella, bool isBundleLoader, + bool explicitlyLinked); + explicit DylibFile(DylibFile *umbrella); void parseLoadCommands(MemoryBufferRef mb); void parseReexports(const llvm::MachO::InterfaceFile &interface); + bool isReferenced() const { return numReferencedSymbols > 0; } + bool isExplicitlyLinked() const; + void setExplicitlyLinked() { explicitlyLinked = true; } static bool classof(const InputFile *f) { return f->kind() == DylibKind; } @@ -156,56 +237,87 @@ public: uint32_t compatibilityVersion = 0; uint32_t currentVersion = 0; int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel + unsigned numReferencedSymbols = 0; RefState refState; bool reexport = false; bool forceNeeded = false; bool forceWeakImport = false; bool deadStrippable = false; - bool explicitlyLinked = false; - - unsigned numReferencedSymbols = 0; - bool isReferenced() const { return numReferencedSymbols > 0; } +private: + bool explicitlyLinked = false; // Access via isExplicitlyLinked(). +public: // An executable can be used as a bundle loader that will load the output // file being linked, and that contains symbols referenced, but not // implemented in the bundle. When used like this, it is very similar - // to a Dylib, so we re-used the same class to represent it. + // to a dylib, so we've used the same class to represent it. bool isBundleLoader; + // Synthetic Dylib objects created by $ld$previous symbols in this dylib. + // Usually empty. These synthetic dylibs won't have synthetic dylibs + // themselves. + SmallVector extraDylibs; + private: + DylibFile *getSyntheticDylib(StringRef installName, uint32_t currentVersion, + uint32_t compatVersion); + bool handleLDSymbol(StringRef originalName); void handleLDPreviousSymbol(StringRef name, StringRef originalName); void handleLDInstallNameSymbol(StringRef name, StringRef originalName); + void handleLDHideSymbol(StringRef name, StringRef originalName); void checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const; + void parseExportedSymbols(uint32_t offset, uint32_t size); + void loadReexport(StringRef path, DylibFile *umbrella, + const llvm::MachO::InterfaceFile *currentTopLevelTapi); + + llvm::DenseSet hiddenSymbols; }; // .a file class ArchiveFile final : public InputFile { public: - explicit ArchiveFile(std::unique_ptr &&file); + explicit ArchiveFile(std::unique_ptr &&file, + bool forceHidden); + void addLazySymbols(); + void fetch(const llvm::object::Archive::Symbol &); + // LLD normally doesn't use Error for error-handling, but the underlying + // Archive library does, so this is the cleanest way to wrap it. + Error fetch(const llvm::object::Archive::Child &, StringRef reason); + const llvm::object::Archive &getArchive() const { return *file; }; static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } - void fetch(const llvm::object::Archive::Symbol &sym); private: std::unique_ptr file; // Keep track of children fetched from the archive by tracking // which address offsets have been fetched already. llvm::DenseSet seen; + // Load all symbols with hidden visibility (-load_hidden). + bool forceHidden; }; class BitcodeFile final : public InputFile { public: explicit BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); + uint64_t offsetInArchive, bool lazy = false, + bool forceHidden = false); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + void parse(); std::unique_ptr obj; + bool forceHidden; + +private: + void parseLazy(); }; extern llvm::SetVector inputFiles; +extern llvm::DenseMap cachedReads; + +std::optional readFile(StringRef path); -llvm::Optional readFile(StringRef path); +void extract(InputFile &file, StringRef reason); namespace detail { @@ -245,9 +357,11 @@ std::vector findCommands(const void *anyHdr, return detail::findCommands(anyHdr, 0, types...); } +std::string replaceThinLTOSuffix(StringRef path); } // namespace macho std::string toString(const macho::InputFile *file); +std::string toString(const macho::Section &); } // namespace lld #endif diff --git a/gnu/llvm/lld/MachO/InputSection.cpp b/gnu/llvm/lld/MachO/InputSection.cpp index eb5acf6ffed..1d8d5847713 100644 --- a/gnu/llvm/lld/MachO/InputSection.cpp +++ b/gnu/llvm/lld/MachO/InputSection.cpp @@ -16,6 +16,8 @@ #include "Target.h" #include "UnwindInfoSection.h" #include "Writer.h" + +#include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" #include "llvm/Support/xxhash.h" @@ -26,6 +28,14 @@ using namespace llvm::support; using namespace lld; using namespace lld::macho; +// Verify ConcatInputSection's size on 64-bit builds. The size of std::vector +// can differ based on STL debug levels (e.g. iterator debugging on MSVC's STL), +// so account for that. +static_assert(sizeof(void *) != 8 || + sizeof(ConcatInputSection) == sizeof(std::vector) + 88, + "Try to minimize ConcatInputSection's size, we create many " + "instances of it"); + std::vector macho::inputSections; uint64_t InputSection::getFileSize() const { @@ -47,55 +57,104 @@ static uint64_t resolveSymbolVA(const Symbol *sym, uint8_t type) { return sym->getVA(); } -// ICF needs to hash any section that might potentially be duplicated so -// that it can match on content rather than identity. -bool ConcatInputSection::isHashableForICF() const { - switch (sectionType(getFlags())) { - case S_REGULAR: - return true; - case S_CSTRING_LITERALS: - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - case S_LITERAL_POINTERS: - llvm_unreachable("found unexpected literal type in ConcatInputSection"); - case S_ZEROFILL: - case S_GB_ZEROFILL: - case S_NON_LAZY_SYMBOL_POINTERS: - case S_LAZY_SYMBOL_POINTERS: - case S_SYMBOL_STUBS: - case S_MOD_INIT_FUNC_POINTERS: - case S_MOD_TERM_FUNC_POINTERS: - case S_COALESCED: - case S_INTERPOSING: - case S_DTRACE_DOF: - case S_LAZY_DYLIB_SYMBOL_POINTERS: - case S_THREAD_LOCAL_REGULAR: - case S_THREAD_LOCAL_ZEROFILL: - case S_THREAD_LOCAL_VARIABLES: - case S_THREAD_LOCAL_VARIABLE_POINTERS: - case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: - return false; - default: - llvm_unreachable("Section type"); +const Defined *InputSection::getContainingSymbol(uint64_t off) const { + auto *nextSym = llvm::upper_bound( + symbols, off, [](uint64_t a, const Defined *b) { return a < b->value; }); + if (nextSym == symbols.begin()) + return nullptr; + return *std::prev(nextSym); +} + +std::string InputSection::getLocation(uint64_t off) const { + // First, try to find a symbol that's near the offset. Use it as a reference + // point. + if (auto *sym = getContainingSymbol(off)) + return (toString(getFile()) + ":(symbol " + toString(*sym) + "+0x" + + Twine::utohexstr(off - sym->value) + ")") + .str(); + + // If that fails, use the section itself as a reference point. + for (const Subsection &subsec : section.subsections) { + if (subsec.isec == this) { + off += subsec.offset; + break; + } } + + return (toString(getFile()) + ":(" + getName() + "+0x" + + Twine::utohexstr(off) + ")") + .str(); } -void ConcatInputSection::hashForICF() { - assert(data.data()); // zeroFill section data has nullptr with non-zero size - assert(icfEqClass[0] == 0); // don't overwrite a unique ID! - // Turn-on the top bit to guarantee that valid hashes have no collisions - // with the small-integer unique IDs for ICF-ineligible sections - icfEqClass[0] = xxHash64(data) | (1ull << 63); +std::string InputSection::getSourceLocation(uint64_t off) const { + auto *obj = dyn_cast_or_null(getFile()); + if (!obj) + return {}; + + DWARFCache *dwarf = obj->getDwarf(); + if (!dwarf) + return std::string(); + + for (const Subsection &subsec : section.subsections) { + if (subsec.isec == this) { + off += subsec.offset; + break; + } + } + + auto createMsg = [&](StringRef path, unsigned line) { + std::string filename = sys::path::filename(path).str(); + std::string lineStr = (":" + Twine(line)).str(); + if (filename == path) + return filename + lineStr; + return (filename + lineStr + " (" + path + lineStr + ")").str(); + }; + + // First, look up a function for a given offset. + if (std::optional li = dwarf->getDILineInfo( + section.addr + off, object::SectionedAddress::UndefSection)) + return createMsg(li->FileName, li->Line); + + // If it failed, look up again as a variable. + if (const Defined *sym = getContainingSymbol(off)) { + // Symbols are generally prefixed with an underscore, which is not included + // in the debug information. + StringRef symName = sym->getName(); + if (!symName.empty() && symName[0] == '_') + symName = symName.substr(1); + + if (std::optional> fileLine = + dwarf->getVariableLoc(symName)) + return createMsg(fileLine->first, fileLine->second); + } + + // Try to get the source file's name from the DWARF information. + if (obj->compileUnit) + return obj->sourceFile(); + + return {}; } void ConcatInputSection::foldIdentical(ConcatInputSection *copy) { align = std::max(align, copy->align); copy->live = false; copy->wasCoalesced = true; - numRefs += copy->numRefs; - copy->numRefs = 0; copy->replacement = this; + for (auto ©Sym : copy->symbols) { + copySym->wasIdenticalCodeFolded = true; + copySym->size = 0; + } + + symbols.insert(symbols.end(), copy->symbols.begin(), copy->symbols.end()); + copy->symbols.clear(); + + // Remove duplicate compact unwind info for symbols at the same address. + if (symbols.empty()) + return; + for (auto it = symbols.begin() + 1; it != symbols.end(); ++it) { + assert((*it)->value == 0); + (*it)->unwindEntry = nullptr; + } } void ConcatInputSection::writeTo(uint8_t *buf) { @@ -110,6 +169,9 @@ void ConcatInputSection::writeTo(uint8_t *buf) { const Reloc &r = relocs[i]; uint8_t *loc = buf + r.offset; uint64_t referentVA = 0; + + const bool needsFixup = config->emitChainedFixups && + target->hasAttr(r.type, RelocAttrBits::UNSIGNED); if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) { const Symbol *fromSym = r.referent.get(); const Reloc &minuend = relocs[++i]; @@ -126,34 +188,59 @@ void ConcatInputSection::writeTo(uint8_t *buf) { if (target->hasAttr(r.type, RelocAttrBits::LOAD) && !referentSym->isInGot()) target->relaxGotLoad(loc, r.type); + // For dtrace symbols, do not handle them as normal undefined symbols + if (referentSym->getName().startswith("___dtrace_")) { + // Change dtrace call site to pre-defined instructions + target->handleDtraceReloc(referentSym, r, loc); + continue; + } referentVA = resolveSymbolVA(referentSym, r.type) + r.addend; - if (isThreadLocalVariables(getFlags())) { + if (isThreadLocalVariables(getFlags()) && isa(referentSym)) { // References from thread-local variable sections are treated as offsets // relative to the start of the thread-local data memory area, which // is initialized via copying all the TLV data sections (which are all // contiguous). - if (isa(referentSym)) - referentVA -= firstTLVDataSection->addr; + referentVA -= firstTLVDataSection->addr; + } else if (needsFixup) { + writeChainedFixup(loc, referentSym, r.addend); + continue; } } else if (auto *referentIsec = r.referent.dyn_cast()) { assert(!::shouldOmitFromOutput(referentIsec)); referentVA = referentIsec->getVA(r.addend); + + if (needsFixup) { + writeChainedRebase(loc, referentVA); + continue; + } } target->relocateOne(loc, r, referentVA, getVA() + r.offset); } } +ConcatInputSection *macho::makeSyntheticInputSection(StringRef segName, + StringRef sectName, + uint32_t flags, + ArrayRef data, + uint32_t align) { + Section §ion = + *make
(/*file=*/nullptr, segName, sectName, flags, /*addr=*/0); + auto isec = make(section, data, align); + section.subsections.push_back({0, isec}); + return isec; +} + void CStringInputSection::splitIntoPieces() { size_t off = 0; StringRef s = toStringRef(data); while (!s.empty()) { size_t end = s.find(0); if (end == StringRef::npos) - fatal(toString(this) + ": string is not null terminated"); - size_t size = end + 1; - uint32_t hash = config->dedupLiterals ? xxHash64(s.substr(0, size)) : 0; + fatal(getLocation(off) + ": string is not null terminated"); + uint32_t hash = deduplicateLiterals ? xxHash64(s.take_front(end)) : 0; pieces.emplace_back(off, hash); + size_t size = end + 1; // include null terminator s = s.substr(size); off += size; } @@ -178,13 +265,11 @@ uint64_t CStringInputSection::getOffset(uint64_t off) const { return piece.outSecOff + addend; } -WordLiteralInputSection::WordLiteralInputSection(StringRef segname, - StringRef name, - InputFile *file, +WordLiteralInputSection::WordLiteralInputSection(const Section §ion, ArrayRef data, - uint32_t align, uint32_t flags) - : InputSection(WordLiteralKind, segname, name, file, data, align, flags) { - switch (sectionType(flags)) { + uint32_t align) + : InputSection(WordLiteralKind, section, data, align) { + switch (sectionType(getFlags())) { case S_4BYTE_LITERALS: power2LiteralSize = 2; break; @@ -203,14 +288,14 @@ WordLiteralInputSection::WordLiteralInputSection(StringRef segname, uint64_t WordLiteralInputSection::getOffset(uint64_t off) const { auto *osec = cast(parent); - const uint8_t *buf = data.data(); + const uintptr_t buf = reinterpret_cast(data.data()); switch (sectionType(getFlags())) { case S_4BYTE_LITERALS: - return osec->getLiteral4Offset(buf + off); + return osec->getLiteral4Offset(buf + (off & ~3LLU)) | (off & 3); case S_8BYTE_LITERALS: - return osec->getLiteral8Offset(buf + off); + return osec->getLiteral8Offset(buf + (off & ~7LLU)) | (off & 7); case S_16BYTE_LITERALS: - return osec->getLiteral16Offset(buf + off); + return osec->getLiteral16Offset(buf + (off & ~15LLU)) | (off & 15); default: llvm_unreachable("invalid literal section type"); } @@ -238,6 +323,26 @@ bool macho::isCfStringSection(const InputSection *isec) { isec->getSegName() == segment_names::data; } +bool macho::isClassRefsSection(const InputSection *isec) { + return isec->getName() == section_names::objcClassRefs && + isec->getSegName() == segment_names::data; +} + +bool macho::isSelRefsSection(const InputSection *isec) { + return isec->getName() == section_names::objcSelrefs && + isec->getSegName() == segment_names::data; +} + +bool macho::isEhFrameSection(const InputSection *isec) { + return isec->getName() == section_names::ehFrame && + isec->getSegName() == segment_names::text; +} + +bool macho::isGccExceptTabSection(const InputSection *isec) { + return isec->getName() == section_names::gccExceptTab && + isec->getSegName() == segment_names::text; +} + std::string lld::toString(const InputSection *isec) { return (toString(isec->getFile()) + ":(" + isec->getName() + ")").str(); } diff --git a/gnu/llvm/lld/MachO/InputSection.h b/gnu/llvm/lld/MachO/InputSection.h index a1045708704..5a6a205f904 100644 --- a/gnu/llvm/lld/MachO/InputSection.h +++ b/gnu/llvm/lld/MachO/InputSection.h @@ -11,12 +11,14 @@ #include "Config.h" #include "Relocations.h" +#include "Symbols.h" #include "lld/Common/LLVM.h" #include "lld/Common/Memory.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/TinyPtrVector.h" #include "llvm/BinaryFormat/MachO.h" namespace lld { @@ -24,71 +26,75 @@ namespace macho { class InputFile; class OutputSection; -class Defined; class InputSection { public: - enum Kind { + enum Kind : uint8_t { ConcatKind, CStringLiteralKind, WordLiteralKind, }; - Kind kind() const { return shared->sectionKind; } + Kind kind() const { return sectionKind; } virtual ~InputSection() = default; virtual uint64_t getSize() const { return data.size(); } - InputFile *getFile() const { return shared->file; } - StringRef getName() const { return shared->name; } - StringRef getSegName() const { return shared->segname; } - uint32_t getFlags() const { return shared->flags; } + virtual bool empty() const { return data.empty(); } + InputFile *getFile() const { return section.file; } + StringRef getName() const { return section.name; } + StringRef getSegName() const { return section.segname; } + uint32_t getFlags() const { return section.flags; } uint64_t getFileSize() const; // Translates \p off -- an offset relative to this InputSection -- into an // offset from the beginning of its parent OutputSection. virtual uint64_t getOffset(uint64_t off) const = 0; // The offset from the beginning of the file. uint64_t getVA(uint64_t off) const; + // Return a user-friendly string for use in diagnostics. + // Format: /path/to/object.o:(symbol _func+0x123) + std::string getLocation(uint64_t off) const; + // Return the source line corresponding to an address, or the empty string. + // Format: Source.cpp:123 (/path/to/Source.cpp:123) + std::string getSourceLocation(uint64_t off) const; // Whether the data at \p off in this InputSection is live. virtual bool isLive(uint64_t off) const = 0; virtual void markLive(uint64_t off) = 0; virtual InputSection *canonical() { return this; } + virtual const InputSection *canonical() const { return this; } - OutputSection *parent = nullptr; +protected: + InputSection(Kind kind, const Section §ion, ArrayRef data, + uint32_t align) + : sectionKind(kind), keepUnique(false), hasAltEntry(false), align(align), + data(data), section(section) {} - uint32_t align = 1; - uint32_t callSiteCount : 31; + InputSection(const InputSection &rhs) + : sectionKind(rhs.sectionKind), keepUnique(false), hasAltEntry(false), + align(rhs.align), data(rhs.data), section(rhs.section) {} + + Kind sectionKind; + +public: // is address assigned? - uint32_t isFinal : 1; + bool isFinal = false; + // keep the address of the symbol(s) in this section unique in the final + // binary ? + bool keepUnique : 1; + // Does this section have symbols at offsets other than zero? (NOTE: only + // applies to ConcatInputSections.) + bool hasAltEntry : 1; + uint32_t align = 1; + OutputSection *parent = nullptr; ArrayRef data; std::vector relocs; + // The symbols that belong to this InputSection, sorted by value. With + // .subsections_via_symbols, there is typically only one element here. + llvm::TinyPtrVector symbols; protected: - // The fields in this struct are immutable. Since we create a lot of - // InputSections with identical values for them (due to - // .subsections_via_symbols), factoring them out into a shared struct reduces - // memory consumption and makes copying cheaper. - struct Shared { - InputFile *file; - StringRef name; - StringRef segname; - uint32_t flags; - Kind sectionKind; - Shared(InputFile *file, StringRef name, StringRef segname, uint32_t flags, - Kind kind) - : file(file), name(name), segname(segname), flags(flags), - sectionKind(kind) {} - }; + const Section §ion; - InputSection(Kind kind, StringRef segname, StringRef name) - : callSiteCount(0), isFinal(false), - shared(make(nullptr, name, segname, 0, kind)) {} - - InputSection(Kind kind, StringRef segname, StringRef name, InputFile *file, - ArrayRef data, uint32_t align, uint32_t flags) - : align(align), callSiteCount(0), isFinal(false), data(data), - shared(make(file, name, segname, flags, kind)) {} - - const Shared *const shared; + const Defined *getContainingSymbol(uint64_t off) const; }; // ConcatInputSections are combined into (Concat)OutputSections through simple @@ -96,27 +102,24 @@ protected: // contents merged before output. class ConcatInputSection final : public InputSection { public: - ConcatInputSection(StringRef segname, StringRef name) - : InputSection(ConcatKind, segname, name) {} - - ConcatInputSection(StringRef segname, StringRef name, InputFile *file, - ArrayRef data, uint32_t align = 1, - uint32_t flags = 0) - : InputSection(ConcatKind, segname, name, file, data, align, flags) {} + ConcatInputSection(const Section §ion, ArrayRef data, + uint32_t align = 1) + : InputSection(ConcatKind, section, data, align) {} uint64_t getOffset(uint64_t off) const override { return outSecOff + off; } uint64_t getVA() const { return InputSection::getVA(0); } // ConcatInputSections are entirely live or dead, so the offset is irrelevant. bool isLive(uint64_t off) const override { return live; } void markLive(uint64_t off) override { live = true; } - bool isCoalescedWeak() const { return wasCoalesced && numRefs == 0; } + bool isCoalescedWeak() const { return wasCoalesced && symbols.empty(); } bool shouldOmitFromOutput() const { return !live || isCoalescedWeak(); } - bool isHashableForICF() const; - void hashForICF(); void writeTo(uint8_t *buf); void foldIdentical(ConcatInputSection *redundant); - InputSection *canonical() override { + ConcatInputSection *canonical() override { + return replacement ? replacement : this; + } + const InputSection *canonical() const override { return replacement ? replacement : this; } @@ -125,9 +128,9 @@ public: } // Points to the surviving section after this one is folded by ICF - InputSection *replacement = nullptr; + ConcatInputSection *replacement = nullptr; // Equivalence-class ID for ICF - uint64_t icfEqClass[2] = {0, 0}; + uint32_t icfEqClass[2] = {0, 0}; // With subsections_via_symbols, most symbols have their own InputSection, // and for weak symbols (e.g. from inline functions), only the @@ -136,18 +139,19 @@ public: // first and not copied to the output. bool wasCoalesced = false; bool live = !config->deadStrip; - // How many symbols refer to this InputSection. - uint32_t numRefs = 0; + bool hasCallSites = false; // This variable has two usages. Initially, it represents the input order. // After assignAddresses is called, it represents the offset from the // beginning of the output section this section was assigned to. uint64_t outSecOff = 0; }; -// Verify ConcatInputSection's size on 64-bit builds. -static_assert(sizeof(int) != 8 || sizeof(ConcatInputSection) == 112, - "Try to minimize ConcatInputSection's size, we create many " - "instances of it"); +// Initialize a fake InputSection that does not belong to any InputFile. +ConcatInputSection *makeSyntheticInputSection(StringRef segName, + StringRef sectName, + uint32_t flags = 0, + ArrayRef data = {}, + uint32_t align = 1); // Helper functions to make it easy to sprinkle asserts. @@ -190,10 +194,11 @@ static_assert(sizeof(StringPiece) == 16, "StringPiece is too big!"); // conservative behavior we can certainly implement that. class CStringInputSection final : public InputSection { public: - CStringInputSection(StringRef segname, StringRef name, InputFile *file, - ArrayRef data, uint32_t align, uint32_t flags) - : InputSection(CStringLiteralKind, segname, name, file, data, align, - flags) {} + CStringInputSection(const Section §ion, ArrayRef data, + uint32_t align, bool dedupLiterals) + : InputSection(CStringLiteralKind, section, data, align), + deduplicateLiterals(dedupLiterals) {} + uint64_t getOffset(uint64_t off) const override; bool isLive(uint64_t off) const override { return getStringPiece(off).live; } void markLive(uint64_t off) override { getStringPiece(off).live = true; } @@ -206,8 +211,10 @@ public: LLVM_ATTRIBUTE_ALWAYS_INLINE StringRef getStringRef(size_t i) const { size_t begin = pieces[i].inSecOff; + // The endpoint should be *at* the null terminator, not after. This matches + // the behavior of StringRef(const char *Str). size_t end = - (pieces.size() - 1 == i) ? data.size() : pieces[i + 1].inSecOff; + ((pieces.size() - 1 == i) ? data.size() : pieces[i + 1].inSecOff) - 1; return toStringRef(data.slice(begin, end - begin)); } @@ -215,7 +222,7 @@ public: // string merging is enabled, so we want to inline. LLVM_ATTRIBUTE_ALWAYS_INLINE llvm::CachedHashStringRef getCachedHashStringRef(size_t i) const { - assert(config->dedupLiterals); + assert(deduplicateLiterals); return {getStringRef(i), pieces[i].hash}; } @@ -223,19 +230,21 @@ public: return isec->kind() == CStringLiteralKind; } + bool deduplicateLiterals = false; std::vector pieces; }; class WordLiteralInputSection final : public InputSection { public: - WordLiteralInputSection(StringRef segname, StringRef name, InputFile *file, - ArrayRef data, uint32_t align, - uint32_t flags); + WordLiteralInputSection(const Section §ion, ArrayRef data, + uint32_t align); uint64_t getOffset(uint64_t off) const override; bool isLive(uint64_t off) const override { return live[off >> power2LiteralSize]; } - void markLive(uint64_t off) override { live[off >> power2LiteralSize] = 1; } + void markLive(uint64_t off) override { + live[off >> power2LiteralSize] = true; + } static bool classof(const InputSection *isec) { return isec->kind() == WordLiteralKind; @@ -277,8 +286,11 @@ inline bool isWordLiteralSection(uint32_t flags) { } bool isCodeSection(const InputSection *); - bool isCfStringSection(const InputSection *); +bool isClassRefsSection(const InputSection *); +bool isSelRefsSection(const InputSection *); +bool isEhFrameSection(const InputSection *); +bool isGccExceptTabSection(const InputSection *); extern std::vector inputSections; @@ -290,20 +302,26 @@ constexpr const char binding[] = "__binding"; constexpr const char bitcodeBundle[] = "__bundle"; constexpr const char cString[] = "__cstring"; constexpr const char cfString[] = "__cfstring"; +constexpr const char cgProfile[] = "__cg_profile"; +constexpr const char chainFixups[] = "__chainfixups"; constexpr const char codeSignature[] = "__code_signature"; constexpr const char common[] = "__common"; constexpr const char compactUnwind[] = "__compact_unwind"; constexpr const char data[] = "__data"; constexpr const char debugAbbrev[] = "__debug_abbrev"; constexpr const char debugInfo[] = "__debug_info"; +constexpr const char debugLine[] = "__debug_line"; constexpr const char debugStr[] = "__debug_str"; +constexpr const char debugStrOffs[] = "__debug_str_offs"; constexpr const char ehFrame[] = "__eh_frame"; +constexpr const char gccExceptTab[] = "__gcc_except_tab"; constexpr const char export_[] = "__export"; constexpr const char dataInCode[] = "__data_in_code"; constexpr const char functionStarts[] = "__func_starts"; constexpr const char got[] = "__got"; constexpr const char header[] = "__mach_header"; constexpr const char indirectSymbolTable[] = "__ind_sym_tab"; +constexpr const char initOffsets[] = "__init_offsets"; constexpr const char const_[] = "__const"; constexpr const char lazySymbolPtr[] = "__la_symbol_ptr"; constexpr const char lazyBinding[] = "__lazy_binding"; @@ -313,8 +331,12 @@ constexpr const char moduleTermFunc[] = "__mod_term_func"; constexpr const char nonLazySymbolPtr[] = "__nl_symbol_ptr"; constexpr const char objcCatList[] = "__objc_catlist"; constexpr const char objcClassList[] = "__objc_classlist"; +constexpr const char objcClassRefs[] = "__objc_classrefs"; constexpr const char objcConst[] = "__objc_const"; -constexpr const char objcImageInfo[] = "__objc_imageinfo"; +constexpr const char objCImageInfo[] = "__objc_imageinfo"; +constexpr const char objcStubs[] = "__objc_stubs"; +constexpr const char objcSelrefs[] = "__objc_selrefs"; +constexpr const char objcMethname[] = "__objc_methname"; constexpr const char objcNonLazyCatList[] = "__objc_nlcatlist"; constexpr const char objcNonLazyClassList[] = "__objc_nlclslist"; constexpr const char objcProtoList[] = "__objc_protolist"; @@ -334,6 +356,7 @@ constexpr const char threadVars[] = "__thread_vars"; constexpr const char unwindInfo[] = "__unwind_info"; constexpr const char weakBinding[] = "__weak_binding"; constexpr const char zeroFill[] = "__zerofill"; +constexpr const char addrSig[] = "__llvm_addrsig"; } // namespace section_names diff --git a/gnu/llvm/lld/MachO/LTO.cpp b/gnu/llvm/lld/MachO/LTO.cpp index 366193a27eb..2f5e9d06f39 100644 --- a/gnu/llvm/lld/MachO/LTO.cpp +++ b/gnu/llvm/lld/MachO/LTO.cpp @@ -14,12 +14,13 @@ #include "Target.h" #include "lld/Common/Args.h" -#include "lld/Common/ErrorHandler.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" -#include "llvm/LTO/Caching.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/LTO/Config.h" #include "llvm/LTO/LTO.h" +#include "llvm/Support/Caching.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -31,16 +32,41 @@ using namespace llvm; using namespace llvm::MachO; using namespace llvm::sys; +// Creates an empty file to store a list of object files for final +// linking of distributed ThinLTO. +static std::unique_ptr openFile(StringRef file) { + std::error_code ec; + auto ret = + 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 modulePath) { + return lto::getThinLTOOutputFile( + std::string(modulePath), std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second)); +} + static lto::Config createConfig() { lto::Config c; c.Options = initTargetOptionsFromCodeGenFlags(); + c.Options.EmitAddrsig = config->icfLevel == ICFLevel::safe; + for (StringRef C : config->mllvmOpts) + c.MllvmArgs.emplace_back(C.str()); c.CodeModel = getCodeModelFromCMModel(); c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); - c.UseNewPM = config->ltoNewPassManager; + c.DiagHandler = diagnosticHandler; c.PreCodeGenPassesHook = [](legacy::PassManager &pm) { pm.add(createObjCARCContractPass()); }; + + c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); + c.TimeTraceEnabled = config->timeTraceEnabled; c.TimeTraceGranularity = config->timeTraceGranularity; c.OptLevel = config->ltoo; @@ -51,18 +77,54 @@ static lto::Config createConfig() { return c; } +// If `originalPath` exists, hardlinks `path` to `originalPath`. If that fails, +// or `originalPath` is not set, saves `buffer` to `path`. +static void saveOrHardlinkBuffer(StringRef buffer, const Twine &path, + std::optional originalPath) { + if (originalPath) { + auto err = fs::create_hard_link(*originalPath, path); + if (!err) + return; + } + saveBuffer(buffer, path); +} + BitcodeCompiler::BitcodeCompiler() { - lto::ThinBackend backend = lto::createInProcessThinBackend( - heavyweight_hardware_concurrency(config->thinLTOJobs)); + // Initialize indexFile. + if (!config->thinLTOIndexOnlyArg.empty()) + indexFile = openFile(config->thinLTOIndexOnlyArg); + + // Initialize ltoObj. + lto::ThinBackend backend; + auto onIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; + if (config->thinLTOIndexOnly) { + backend = lto::createWriteIndexesThinBackend( + std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second), + config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite); + } else { + backend = lto::createInProcessThinBackend( + llvm::heavyweight_hardware_concurrency(config->thinLTOJobs), + onIndexWrite, config->thinLTOEmitIndexFiles, + config->thinLTOEmitImportsFiles); + } + ltoObj = std::make_unique(createConfig(), backend); } void BitcodeCompiler::add(BitcodeFile &f) { - ArrayRef objSyms = f.obj->symbols(); + lto::InputFile &obj = *f.obj; + + if (config->thinLTOEmitIndexFiles) + thinIndices.insert(obj.getName()); + + ArrayRef objSyms = obj.symbols(); std::vector resols; resols.reserve(objSyms.size()); // Provide a resolution to the LTO API for each symbol. + bool exportDynamic = + config->outputType != MH_EXECUTE || config->exportDynamic; auto symIt = f.symbols.begin(); for (const lto::InputFile::Symbol &objSym : objSyms) { resols.emplace_back(); @@ -76,24 +138,61 @@ void BitcodeCompiler::add(BitcodeFile &f) { // be removed. r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; - // FIXME: What about other output types? And we can probably be less - // restrictive with -flat_namespace, but it's an infrequent use case. - // FIXME: Honor config->exportDynamic. - r.VisibleToRegularObj = config->outputType != MH_EXECUTE || - config->namespaceKind == NamespaceKind::flat || - sym->isUsedInRegularObj; + if (const auto *defined = dyn_cast(sym)) { + r.ExportDynamic = + defined->isExternal() && !defined->privateExtern && exportDynamic; + r.FinalDefinitionInLinkageUnit = + !defined->isExternalWeakDef() && !defined->interposable; + } else if (const auto *common = dyn_cast(sym)) { + r.ExportDynamic = !common->privateExtern && exportDynamic; + r.FinalDefinitionInLinkageUnit = true; + } + + r.VisibleToRegularObj = + sym->isUsedInRegularObj || (r.Prevailing && r.ExportDynamic); // Un-define the symbol so that we don't get duplicate symbol errors when we // load the ObjFile emitted by LTO compilation. if (r.Prevailing) replaceSymbol(sym, sym->getName(), sym->getFile(), - RefState::Strong); + RefState::Strong, /*wasBitcodeSymbol=*/true); // TODO: set the other resolution configs properly } checkError(ltoObj->add(std::move(f.obj), resols)); } +// If LazyObjFile has not been added to link, emit empty index files. +// This is needed because this is what GNU gold plugin does and we have a +// distributed build system that depends on that behavior. +static void thinLTOCreateEmptyIndexFiles() { + DenseSet linkedBitCodeFiles; + for (InputFile *file : inputFiles) + if (auto *f = dyn_cast(file)) + if (!f->lazy) + linkedBitCodeFiles.insert(f->getName()); + + for (InputFile *file : inputFiles) { + if (auto *f = dyn_cast(file)) { + if (!f->lazy) + continue; + if (linkedBitCodeFiles.contains(f->getName())) + continue; + std::string path = + replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName())); + std::unique_ptr os = openFile(path + ".thinlto.bc"); + if (!os) + continue; + + ModuleSummaryIndex m(/*HaveGVs=*/false); + m.setSkipModuleByDistributedBackend(); + writeIndexToFile(m, *os); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } + } +} + // Merge all the bitcode files we have seen, codegen the result // and return the resulting ObjectFile(s). std::vector BitcodeCompiler::compile() { @@ -104,53 +203,105 @@ std::vector BitcodeCompiler::compile() { // The -cache_path_lto 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; + FileCache cache; if (!config->thinLTOCacheDir.empty()) - cache = check( - lto::localCache(config->thinLTOCacheDir, - [&](size_t task, std::unique_ptr mb) { - files[task] = std::move(mb); - })); + cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir, + [&](size_t task, const Twine &moduleName, + std::unique_ptr mb) { + files[task] = std::move(mb); + })); checkError(ltoObj->run( - [&](size_t task) { - return std::make_unique( + [&](size_t task, const Twine &moduleName) { + return std::make_unique( std::make_unique(buf[task])); }, cache)); - if (!config->thinLTOCacheDir.empty()) - pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); + // 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"); + } + + if (config->thinLTOEmitIndexFiles) + thinLTOCreateEmptyIndexFiles(); + + // In ThinLTO mode, Clang passes a temporary directory in -object_path_lto, + // while the argument is a single file in FullLTO mode. + bool objPathIsDir = true; + if (!config->ltoObjPath.empty()) { + if (std::error_code ec = fs::create_directories(config->ltoObjPath)) + fatal("cannot create LTO object path " + config->ltoObjPath + ": " + + ec.message()); + + if (!fs::is_directory(config->ltoObjPath)) { + objPathIsDir = false; + unsigned objCount = + count_if(buf, [](const SmallString<0> &b) { return !b.empty(); }); + if (objCount > 1) + fatal("-object_path_lto must specify a directory when using ThinLTO"); + } + } + + auto outputFilePath = [objPathIsDir](int i) { + SmallString<261> filePath("/tmp/lto.tmp"); + if (!config->ltoObjPath.empty()) { + filePath = config->ltoObjPath; + if (objPathIsDir) + path::append(filePath, Twine(i) + "." + + getArchitectureName(config->arch()) + + ".lto.o"); + } + return filePath; + }; - if (config->saveTemps) { - if (!buf[0].empty()) - saveBuffer(buf[0], config->outputFile + ".lto.o"); - for (unsigned i = 1; i != maxTasks; ++i) - saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o"); + // 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], outputFilePath(0)); + if (indexFile) + indexFile->close(); + return {}; } - if (!config->ltoObjPath.empty()) - fs::create_directories(config->ltoObjPath); + if (!config->thinLTOCacheDir.empty()) + pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files); std::vector ret; - for (unsigned i = 0; i != maxTasks; ++i) { - if (buf[i].empty()) + for (unsigned i = 0; i < maxTasks; ++i) { + // Get the native object contents either from the cache or from memory. Do + // not use the cached MemoryBuffer directly to ensure dsymutil does not + // race with the cache pruner. + StringRef objBuf; + std::optional cachePath; + if (files[i]) { + objBuf = files[i]->getBuffer(); + cachePath = files[i]->getBufferIdentifier(); + } else { + objBuf = buf[i]; + } + if (objBuf.empty()) continue; - SmallString<261> filePath("/tmp/lto.tmp"); + + // FIXME: should `saveTemps` and `ltoObjPath` use the same file name? + if (config->saveTemps) + saveBuffer(objBuf, + config->outputFile + ((i == 0) ? "" : Twine(i)) + ".lto.o"); + + auto filePath = outputFilePath(i); uint32_t modTime = 0; if (!config->ltoObjPath.empty()) { - filePath = config->ltoObjPath; - path::append(filePath, Twine(i) + "." + - getArchitectureName(config->arch()) + - ".lto.o"); - saveBuffer(buf[i], filePath); + saveOrHardlinkBuffer(objBuf, filePath, cachePath); modTime = getModTime(filePath); } ret.push_back(make( - MemoryBufferRef(buf[i], saver.save(filePath.str())), modTime, "")); + MemoryBufferRef(objBuf, saver().save(filePath.str())), modTime, "")); } - for (std::unique_ptr &file : files) - if (file) - ret.push_back(make(*file, 0, "")); + return ret; } diff --git a/gnu/llvm/lld/MachO/LTO.h b/gnu/llvm/lld/MachO/LTO.h index d64016fb588..f07b1e39e01 100644 --- a/gnu/llvm/lld/MachO/LTO.h +++ b/gnu/llvm/lld/MachO/LTO.h @@ -9,19 +9,19 @@ #ifndef LLD_MACHO_LTO_H #define LLD_MACHO_LTO_H +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" #include #include -namespace llvm { -namespace lto { +namespace llvm::lto { class LTO; -} // namespace lto -} // namespace llvm +} // namespace llvm::lto -namespace lld { -namespace macho { +namespace lld::macho { class BitcodeFile; class ObjFile; @@ -37,9 +37,10 @@ private: std::unique_ptr ltoObj; std::vector> buf; std::vector> files; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; }; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/MachOStructs.h b/gnu/llvm/lld/MachO/MachOStructs.h index 44715bf29da..42390a2cc15 100644 --- a/gnu/llvm/lld/MachO/MachOStructs.h +++ b/gnu/llvm/lld/MachO/MachOStructs.h @@ -17,9 +17,7 @@ #include "llvm/Support/Endian.h" -namespace lld { - -namespace structs { +namespace lld::structs { struct nlist_64 { llvm::support::ulittle32_t n_strx; @@ -44,8 +42,6 @@ struct entry_point_command { llvm::support::ulittle64_t stacksize; }; -} // namespace structs - -} // namespace lld +} // namespace lld::structs #endif diff --git a/gnu/llvm/lld/MachO/MapFile.cpp b/gnu/llvm/lld/MachO/MapFile.cpp index 79471eecbd5..c16e046dcd1 100644 --- a/gnu/llvm/lld/MachO/MapFile.cpp +++ b/gnu/llvm/lld/MachO/MapFile.cpp @@ -6,9 +6,10 @@ // //===----------------------------------------------------------------------===// // -// This file implements the -map option. It shows lists in order and -// hierarchically the outputFile, arch, input files, output sections and -// symbol: +// This file implements the -map option, which maps address ranges to their +// respective contents, plus the input file these contents were originally from. +// The contents (typically symbols) are listed in address order. Dead-stripped +// contents are included as well. // // # Path: test // # Arch: x86_84 @@ -16,22 +17,28 @@ // [ 0] linker synthesized // [ 1] a.o // # Sections: -// # Address Size Segment Section -// 0x1000005C0 0x0000004C __TEXT __text +// # Address Size Segment Section +// 0x1000005C0 0x0000004C __TEXT __text // # Symbols: -// # Address File Name -// 0x1000005C0 [ 1] _main +// # Address Size File Name +// 0x1000005C0 0x00000001 [ 1] _main +// # Dead Stripped Symbols: +// # Size File Name +// <> 0x00000001 [ 1] _foo // //===----------------------------------------------------------------------===// #include "MapFile.h" +#include "ConcatOutputSection.h" #include "Config.h" #include "InputFiles.h" #include "InputSection.h" -#include "OutputSection.h" #include "OutputSegment.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/TimeProfiler.h" @@ -40,55 +47,103 @@ using namespace llvm::sys; using namespace lld; using namespace lld::macho; -using SymbolMapTy = DenseMap>; - -// Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef syms) { - SymbolMapTy ret; - for (Defined *dr : syms) - ret[dr->isec].push_back(dr); - - // Sort symbols by address. We want to print out symbols in the order they - // appear in the output file rather than the order they appeared in the input - // files. - for (auto &it : ret) - parallelSort( - it.second.begin(), it.second.end(), [](Defined *a, Defined *b) { - return a->getVA() != b->getVA() ? a->getVA() < b->getVA() - : a->getName() < b->getName(); - }); - return ret; -} +struct CStringInfo { + uint32_t fileIndex; + StringRef str; +}; + +struct MapInfo { + SmallVector files; + SmallVector deadSymbols; + DenseMap>> + liveCStringsForSection; + SmallVector deadCStrings; +}; + +static MapInfo gatherMapInfo() { + MapInfo info; + for (InputFile *file : inputFiles) { + bool isReferencedFile = false; + + if (isa(file) || isa(file)) { + uint32_t fileIndex = info.files.size() + 1; -// Returns a list of all symbols that we want to print out. -static std::vector getSymbols() { - std::vector v; - for (InputFile *file : inputFiles) - if (isa(file)) - for (Symbol *sym : file->symbols) + // Gather the dead symbols. We don't have to bother with the live ones + // because we will pick them up as we iterate over the OutputSections + // later. + for (Symbol *sym : file->symbols) { if (auto *d = dyn_cast_or_null(sym)) - if (d->isLive() && d->isec && d->getFile() == file) { - assert(!shouldOmitFromOutput(d->isec)); - v.push_back(d); + // Only emit the prevailing definition of a symbol. Also, don't emit + // the symbol if it is part of a cstring section (we use the literal + // value instead, similar to ld64) + if (d->isec && d->getFile() == file && + !isa(d->isec)) { + isReferencedFile = true; + if (!d->isLive()) + info.deadSymbols.push_back(d); } - return v; + } + + // Gather all the cstrings (both live and dead). A CString(Output)Section + // doesn't provide us a way of figuring out which InputSections its + // cstring contents came from, so we need to build up that mapping here. + for (const Section *sec : file->sections) { + for (const Subsection &subsec : sec->subsections) { + if (auto isec = dyn_cast(subsec.isec)) { + auto &liveCStrings = info.liveCStringsForSection[isec->parent]; + for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) { + if (piece.live) + liveCStrings.push_back({isec->parent->addr + piece.outSecOff, + {fileIndex, isec->getStringRef(i)}}); + else + info.deadCStrings.push_back({fileIndex, isec->getStringRef(i)}); + isReferencedFile = true; + } + } else { + break; + } + } + } + } else if (const auto *dylibFile = dyn_cast(file)) { + isReferencedFile = dylibFile->isReferenced(); + } + + if (isReferencedFile) + info.files.push_back(file); + } + + // cstrings are not stored in sorted order in their OutputSections, so we sort + // them here. + for (auto &liveCStrings : info.liveCStringsForSection) + parallelSort(liveCStrings.second, [](const auto &p1, const auto &p2) { + return p1.first < p2.first; + }); + return info; +} + +// For printing the contents of the __stubs and __la_symbol_ptr sections. +void printStubsEntries( + raw_fd_ostream &os, + const DenseMap &readerToFileOrdinal, + const OutputSection *osec, size_t entrySize) { + for (const Symbol *sym : in.stubs->getEntries()) + os << format("0x%08llX\t0x%08zX\t[%3u] %s\n", + osec->addr + sym->stubsIndex * entrySize, entrySize, + readerToFileOrdinal.lookup(sym->getFile()), + sym->getName().str().data()); } -// Construct a map from symbols to their stringified representations. -// Demangling symbols (which is what toString() does) is slow, so -// we do that in batch using parallel-for. -static DenseMap -getSymbolStrings(ArrayRef syms) { - std::vector str(syms.size()); - parallelForEachN(0, syms.size(), [&](size_t i) { - raw_string_ostream os(str[i]); - os << 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 printNonLazyPointerSection(raw_fd_ostream &os, + NonLazyPointerSectionBase *osec) { + // ld64 considers stubs to belong to particular files, but considers GOT + // entries to be linker-synthesized. Not sure why they made that decision, but + // I think we can follow suit unless there's demand for better symbol-to-file + // associations. + for (const Symbol *sym : osec->getEntries()) + os << format("0x%08llX\t0x%08zX\t[ 0] non-lazy-pointer-to-local: %s\n", + osec->addr + sym->gotIndex * target->wordSize, + target->wordSize, sym->getName().str().data()); } void macho::writeMapFile() { @@ -105,31 +160,21 @@ void macho::writeMapFile() { return; } - // Dump output path. os << format("# Path: %s\n", config->outputFile.str().c_str()); - - // Dump output architecture. os << format("# Arch: %s\n", getArchitectureName(config->arch()).str().c_str()); - // Dump table of object files. + MapInfo info = gatherMapInfo(); + os << "# Object files:\n"; os << format("[%3u] %s\n", 0, (const char *)"linker synthesized"); uint32_t fileIndex = 1; DenseMap readerToFileOrdinal; - for (InputFile *file : inputFiles) { - if (isa(file)) { - os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str()); - readerToFileOrdinal[file] = fileIndex++; - } + for (InputFile *file : info.files) { + os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str()); + readerToFileOrdinal[file] = fileIndex++; } - // Collect symbol info that we want to print out. - std::vector syms = getSymbols(); - SymbolMapTy sectionSyms = getSectionSyms(syms); - DenseMap symStr = getSymbolStrings(syms); - - // Dump table of sections os << "# Sections:\n"; os << "# Address\tSize \tSegment\tSection\n"; for (OutputSegment *seg : outputSegments) @@ -141,19 +186,63 @@ void macho::writeMapFile() { seg->name.str().c_str(), osec->name.str().c_str()); } - // Dump table of symbols os << "# Symbols:\n"; - os << "# Address\t File Name\n"; - for (InputSection *isec : inputSections) { - auto symsIt = sectionSyms.find(isec); - assert(!shouldOmitFromOutput(isec) || (symsIt == sectionSyms.end())); - if (symsIt == sectionSyms.end()) - continue; - for (Symbol *sym : symsIt->second) { - os << format("0x%08llX\t[%3u] %s\n", sym->getVA(), - readerToFileOrdinal[sym->getFile()], symStr[sym].c_str()); + os << "# Address\tSize \tFile Name\n"; + for (const OutputSegment *seg : outputSegments) { + for (const OutputSection *osec : seg->getSections()) { + if (auto *concatOsec = dyn_cast(osec)) { + for (const InputSection *isec : concatOsec->inputs) { + for (Defined *sym : isec->symbols) + os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(), + sym->size, readerToFileOrdinal[sym->getFile()], + sym->getName().str().data()); + } + } else if (osec == in.cStringSection || osec == in.objcMethnameSection) { + const auto &liveCStrings = info.liveCStringsForSection.lookup(osec); + uint64_t lastAddr = 0; // strings will never start at address 0, so this + // is a sentinel value + for (const auto &[addr, info] : liveCStrings) { + uint64_t size = 0; + if (addr != lastAddr) + size = info.str.size() + 1; // include null terminator + lastAddr = addr; + os << format("0x%08llX\t0x%08llX\t[%3u] literal string: ", addr, size, + info.fileIndex); + os.write_escaped(info.str) << "\n"; + } + } else if (osec == (void *)in.unwindInfo) { + os << format("0x%08llX\t0x%08llX\t[ 0] compact unwind info\n", + osec->addr, osec->getSize()); + } else if (osec == in.stubs) { + printStubsEntries(os, readerToFileOrdinal, osec, target->stubSize); + } else if (osec == in.lazyPointers) { + printStubsEntries(os, readerToFileOrdinal, osec, target->wordSize); + } else if (osec == in.stubHelper) { + // yes, ld64 calls it "helper helper"... + os << format("0x%08llX\t0x%08llX\t[ 0] helper helper\n", osec->addr, + osec->getSize()); + } else if (osec == in.got) { + printNonLazyPointerSection(os, in.got); + } else if (osec == in.tlvPointers) { + printNonLazyPointerSection(os, in.tlvPointers); + } + // TODO print other synthetic sections } } - // TODO: when we implement -dead_strip, we should dump dead stripped symbols + if (config->deadStrip) { + os << "# Dead Stripped Symbols:\n"; + os << "# \tSize \tFile Name\n"; + for (Defined *sym : info.deadSymbols) { + assert(!sym->isLive()); + os << format("<>\t0x%08llX\t[%3u] %s\n", sym->size, + readerToFileOrdinal[sym->getFile()], + sym->getName().str().data()); + } + for (CStringInfo &cstrInfo : info.deadCStrings) { + os << format("<>\t0x%08zX\t[%3u] literal string: ", + cstrInfo.str.size() + 1, cstrInfo.fileIndex); + os.write_escaped(cstrInfo.str) << "\n"; + } + } } diff --git a/gnu/llvm/lld/MachO/MapFile.h b/gnu/llvm/lld/MachO/MapFile.h index bf16ffdd038..37436d797d7 100644 --- a/gnu/llvm/lld/MachO/MapFile.h +++ b/gnu/llvm/lld/MachO/MapFile.h @@ -9,10 +9,8 @@ #ifndef LLD_MACHO_MAPFILE_H #define LLD_MACHO_MAPFILE_H -namespace lld { -namespace macho { +namespace lld::macho { void writeMapFile(); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/MarkLive.cpp b/gnu/llvm/lld/MachO/MarkLive.cpp index 11669126098..a37213d5613 100644 --- a/gnu/llvm/lld/MachO/MarkLive.cpp +++ b/gnu/llvm/lld/MachO/MarkLive.cpp @@ -12,71 +12,227 @@ #include "SymbolTable.h" #include "Symbols.h" #include "UnwindInfoSection.h" -#include "mach-o/compact_unwind_encoding.h" + +#include "lld/Common/ErrorHandler.h" #include "llvm/Support/TimeProfiler.h" -namespace lld { -namespace macho { +#include "mach-o/compact_unwind_encoding.h" + +namespace lld::macho { using namespace llvm; using namespace llvm::MachO; +struct WhyLiveEntry { + InputSection *isec; + // Keep track of the entry that caused us to mark `isec` as live. + const WhyLiveEntry *prev; + + WhyLiveEntry(InputSection *isec, const WhyLiveEntry *prev) + : isec(isec), prev(prev) {} +}; + +// Type-erased interface to MarkLiveImpl. Used for adding roots to the liveness +// graph. +class MarkLive { +public: + virtual void enqueue(InputSection *isec, uint64_t off) = 0; + virtual void addSym(Symbol *s) = 0; + virtual void markTransitively() = 0; + virtual ~MarkLive() = default; +}; + +template class MarkLiveImpl : public MarkLive { +public: + // -why_live is a rarely used option, so we don't want support for that flag + // to slow down the main -dead_strip code path. As such, we employ templates + // to avoid the usage of WhyLiveEntry in the main code path. This saves us + // from needless allocations and pointer indirections. + using WorklistEntry = + std::conditional_t; + + void enqueue(InputSection *isec, uint64_t off) override { + enqueue(isec, off, nullptr); + } + void addSym(Symbol *s) override { addSym(s, nullptr); } + void markTransitively() override; + +private: + void enqueue(InputSection *isec, uint64_t off, const WorklistEntry *prev); + void addSym(Symbol *s, const WorklistEntry *prev); + const InputSection *getInputSection(const WorklistEntry *) const; + WorklistEntry *makeEntry(InputSection *, const WorklistEntry *prev) const; + + // We build up a worklist of sections which have been marked as live. We + // only push into the worklist when we discover an unmarked section, and we + // mark as we push, so sections never appear twice in the list. Literal + // sections cannot contain references to other sections, so we only store + // ConcatInputSections in our worklist. + SmallVector worklist; +}; + +template +void MarkLiveImpl::enqueue( + InputSection *isec, uint64_t off, + const typename MarkLiveImpl::WorklistEntry *prev) { + if (isec->isLive(off)) + return; + isec->markLive(off); + if (auto s = dyn_cast(isec)) { + assert(!s->isCoalescedWeak()); + worklist.push_back(makeEntry(s, prev)); + } +} + +static void printWhyLive(const Symbol *s, const WhyLiveEntry *prev) { + std::string out = toString(*s) + " from " + toString(s->getFile()); + int indent = 2; + for (const WhyLiveEntry *entry = prev; entry; + entry = entry->prev, indent += 2) { + const TinyPtrVector &symbols = entry->isec->symbols; + // With .subsections_with_symbols set, most isecs will have exactly one + // entry in their symbols vector, so we just print the first one. + if (!symbols.empty()) + out += "\n" + std::string(indent, ' ') + toString(*symbols.front()) + + " from " + toString(symbols.front()->getFile()); + } + message(out); +} + +template +void MarkLiveImpl::addSym( + Symbol *s, + const typename MarkLiveImpl::WorklistEntry *prev) { + if (s->used) + return; + s->used = true; + if constexpr (RecordWhyLive) + if (!config->whyLive.empty() && config->whyLive.match(s->getName())) + printWhyLive(s, prev); + if (auto *d = dyn_cast(s)) { + if (d->isec) + enqueue(d->isec, d->value, prev); + if (d->unwindEntry) + enqueue(d->unwindEntry, 0, prev); + } +} + +template +const InputSection *MarkLiveImpl::getInputSection( + const MarkLiveImpl::WorklistEntry *entry) const { + if constexpr (RecordWhyLive) + return entry->isec; + else + return entry; +} + +template +typename MarkLiveImpl::WorklistEntry * +MarkLiveImpl::makeEntry( + InputSection *isec, + const MarkLiveImpl::WorklistEntry *prev) const { + if constexpr (RecordWhyLive) { + if (!isec) { + assert(!prev); + return nullptr; + } + return make(isec, prev); + } else { + return isec; + } +} + +template +void MarkLiveImpl::markTransitively() { + do { + // Mark things reachable from GC roots as live. + while (!worklist.empty()) { + WorklistEntry *entry = worklist.pop_back_val(); + // Entries that get placed onto the worklist always contain + // ConcatInputSections. `WhyLiveEntry::prev` may point to entries that + // contain other types of InputSections (due to S_ATTR_LIVE_SUPPORT), but + // those entries should never be pushed onto the worklist. + auto *isec = cast(getInputSection(entry)); + assert(isec->live && "We mark as live when pushing onto the worklist!"); + + // Mark all symbols listed in the relocation table for this section. + for (const Reloc &r : isec->relocs) { + if (auto *s = r.referent.dyn_cast()) + addSym(s, entry); + else + enqueue(r.referent.get(), r.addend, entry); + } + for (Defined *d : getInputSection(entry)->symbols) + addSym(d, entry); + } + + // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live + // section. Process them in a second pass. + for (ConcatInputSection *isec : inputSections) { + // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a + // separate vector and only walking that here is faster. + if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live) + continue; + + for (const Reloc &r : isec->relocs) { + if (auto *s = r.referent.dyn_cast()) { + if (s->isLive()) { + InputSection *referentIsec = nullptr; + if (auto *d = dyn_cast(s)) + referentIsec = d->isec; + enqueue(isec, 0, makeEntry(referentIsec, nullptr)); + } + } else { + auto *referentIsec = r.referent.get(); + if (referentIsec->isLive(r.addend)) + enqueue(isec, 0, makeEntry(referentIsec, nullptr)); + } + } + } + + // S_ATTR_LIVE_SUPPORT could have marked additional sections live, + // which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live. + // Iterate. In practice, the second iteration won't mark additional + // S_ATTR_LIVE_SUPPORT sections live. + } while (!worklist.empty()); +} + // Set live bit on for each reachable chunk. Unmarked (unreachable) // InputSections will be ignored by Writer, so they will be excluded // from the final output. void markLive() { TimeTraceScope timeScope("markLive"); - - // We build up a worklist of sections which have been marked as live. We only - // push into the worklist when we discover an unmarked section, and we mark - // as we push, so sections never appear twice in the list. - // Literal sections cannot contain references to other sections, so we only - // store ConcatInputSections in our worklist. - SmallVector worklist; - - auto enqueue = [&](InputSection *isec, uint64_t off) { - if (isec->isLive(off)) - return; - isec->markLive(off); - if (auto s = dyn_cast(isec)) { - assert(!s->isCoalescedWeak()); - worklist.push_back(s); - } - }; - - auto addSym = [&](Symbol *s) { - s->used = true; - if (auto *d = dyn_cast(s)) - if (d->isec) - enqueue(d->isec, d->value); - }; - + MarkLive *marker; + if (config->whyLive.empty()) + marker = make>(); + else + marker = make>(); // Add GC roots. if (config->entry) - addSym(config->entry); + marker->addSym(config->entry); for (Symbol *sym : symtab->getSymbols()) { if (auto *defined = dyn_cast(sym)) { // -exported_symbol(s_list) if (!config->exportedSymbols.empty() && config->exportedSymbols.match(defined->getName())) { - // FIXME: Instead of doing this here, maybe the Driver code doing - // the matching should add them to explicitUndefineds? Then the - // explicitUndefineds code below would handle this automatically. - assert(!defined->privateExtern && - "should have been rejected by driver"); - addSym(defined); + // NOTE: Even though exporting private externs is an ill-defined + // operation, we are purposely not checking for privateExtern in + // order to follow ld64's behavior of treating all exported private + // extern symbols as live, irrespective of whether they are autohide. + marker->addSym(defined); continue; } // public symbols explicitly marked .no_dead_strip if (defined->referencedDynamically || defined->noDeadStrip) { - addSym(defined); + marker->addSym(defined); continue; } - // FIXME: When we implement these flags, make symbols from them GC roots: + // FIXME: When we implement these flags, make symbols from them GC + // roots: // * -reexported_symbol(s_list) - // * -alias(-list) + // * -alias_list // * -init // In dylibs and bundles and in executables with -export_dynamic, @@ -84,105 +240,45 @@ void markLive() { bool externsAreRoots = config->outputType != MH_EXECUTE || config->exportDynamic; if (externsAreRoots && !defined->privateExtern) { - addSym(defined); + marker->addSym(defined); continue; } } } // -u symbols for (Symbol *sym : config->explicitUndefineds) - if (auto *defined = dyn_cast(sym)) - addSym(defined); + marker->addSym(sym); // local symbols explicitly marked .no_dead_strip for (const InputFile *file : inputFiles) if (auto *objFile = dyn_cast(file)) for (Symbol *sym : objFile->symbols) if (auto *defined = dyn_cast_or_null(sym)) if (!defined->isExternal() && defined->noDeadStrip) - addSym(defined); + marker->addSym(defined); if (auto *stubBinder = dyn_cast_or_null(symtab->find("dyld_stub_binder"))) - addSym(stubBinder); + marker->addSym(stubBinder); for (ConcatInputSection *isec : inputSections) { // Sections marked no_dead_strip if (isec->getFlags() & S_ATTR_NO_DEAD_STRIP) { - enqueue(isec, 0); + marker->enqueue(isec, 0); continue; } // mod_init_funcs, mod_term_funcs sections if (sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS || sectionType(isec->getFlags()) == S_MOD_TERM_FUNC_POINTERS) { - enqueue(isec, 0); + assert(!config->emitInitOffsets || + sectionType(isec->getFlags()) != S_MOD_INIT_FUNC_POINTERS); + marker->enqueue(isec, 0); continue; } } - // Dead strip runs before UnwindInfoSection handling so we need to keep - // __LD,__compact_unwind alive here. - // But that section contains absolute references to __TEXT,__text and - // keeps most code alive due to that. So we can't just enqueue() the - // section: We must skip the relocations for the functionAddress - // in each CompactUnwindEntry. - // See also scanEhFrameSection() in lld/ELF/MarkLive.cpp. - for (ConcatInputSection *isec : in.unwindInfo->getInputs()) { - isec->live = true; - const int compactUnwindEntrySize = - target->wordSize == 8 ? sizeof(CompactUnwindEntry) - : sizeof(CompactUnwindEntry); - for (const Reloc &r : isec->relocs) { - // This is the relocation for the address of the function itself. - // Ignore it, else these would keep everything alive. - if (r.offset % compactUnwindEntrySize == 0) - continue; - - if (auto *s = r.referent.dyn_cast()) - addSym(s); - else - enqueue(r.referent.get(), r.addend); - } - } + for (ConcatInputSection *isec : in.initOffsets->inputs()) + marker->enqueue(isec, 0); - do { - // Mark things reachable from GC roots as live. - while (!worklist.empty()) { - ConcatInputSection *s = worklist.pop_back_val(); - assert(s->live && "We mark as live when pushing onto the worklist!"); - - // Mark all symbols listed in the relocation table for this section. - for (const Reloc &r : s->relocs) { - if (auto *s = r.referent.dyn_cast()) - addSym(s); - else - enqueue(r.referent.get(), r.addend); - } - } - - // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live section. - // Process them in a second pass. - for (ConcatInputSection *isec : inputSections) { - // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a - // separate vector and only walking that here is faster. - if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live) - continue; - - for (const Reloc &r : isec->relocs) { - bool referentLive; - if (auto *s = r.referent.dyn_cast()) - referentLive = s->isLive(); - else - referentLive = r.referent.get()->isLive(r.addend); - if (referentLive) - enqueue(isec, 0); - } - } - - // S_ATTR_LIVE_SUPPORT could have marked additional sections live, - // which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live. - // Iterate. In practice, the second iteration won't mark additional - // S_ATTR_LIVE_SUPPORT sections live. - } while (!worklist.empty()); + marker->markTransitively(); } -} // namespace macho -} // namespace lld +} // namespace lld::macho diff --git a/gnu/llvm/lld/MachO/MarkLive.h b/gnu/llvm/lld/MachO/MarkLive.h index 4db657c5728..d7fe7ffa200 100644 --- a/gnu/llvm/lld/MachO/MarkLive.h +++ b/gnu/llvm/lld/MachO/MarkLive.h @@ -9,12 +9,10 @@ #ifndef LLD_MACHO_MARKLIVE_H #define LLD_MACHO_MARKLIVE_H -namespace lld { -namespace macho { +namespace lld::macho { void markLive(); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif // LLD_MACHO_MARKLIVE_H diff --git a/gnu/llvm/lld/MachO/ObjC.cpp b/gnu/llvm/lld/MachO/ObjC.cpp index 7ed800827f3..d484c4029f6 100644 --- a/gnu/llvm/lld/MachO/ObjC.cpp +++ b/gnu/llvm/lld/MachO/ObjC.cpp @@ -12,15 +12,17 @@ #include "OutputSegment.h" #include "Target.h" +#include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Bitcode/BitcodeReader.h" using namespace llvm; using namespace llvm::MachO; using namespace lld; using namespace lld::macho; -template static bool hasObjCSection(MemoryBufferRef mb) { - using Section = typename LP::section; +template static bool objectHasObjCSection(MemoryBufferRef mb) { + using SectionHeader = typename LP::section; auto *hdr = reinterpret_cast(mb.getBufferStart()); @@ -29,16 +31,17 @@ template static bool hasObjCSection(MemoryBufferRef mb) { if (const auto *c = findCommand(hdr, LP::segmentLCType)) { - auto sectionHeaders = - ArrayRef
{reinterpret_cast(c + 1), c->nsects}; - for (const Section &sec : sectionHeaders) { - StringRef sectname(sec.sectname, - strnlen(sec.sectname, sizeof(sec.sectname))); - StringRef segname(sec.segname, strnlen(sec.segname, sizeof(sec.segname))); + auto sectionHeaders = ArrayRef{ + reinterpret_cast(c + 1), c->nsects}; + for (const SectionHeader &secHead : sectionHeaders) { + StringRef sectname(secHead.sectname, + strnlen(secHead.sectname, sizeof(secHead.sectname))); + StringRef segname(secHead.segname, + strnlen(secHead.segname, sizeof(secHead.segname))); if ((segname == segment_names::data && sectname == section_names::objcCatList) || (segname == segment_names::text && - sectname == section_names::swift)) { + sectname.startswith(section_names::swift))) { return true; } } @@ -46,9 +49,20 @@ template static bool hasObjCSection(MemoryBufferRef mb) { return false; } -bool macho::hasObjCSection(MemoryBufferRef mb) { +static bool objectHasObjCSection(MemoryBufferRef mb) { if (target->wordSize == 8) - return ::hasObjCSection(mb); + return ::objectHasObjCSection(mb); else - return ::hasObjCSection(mb); + return ::objectHasObjCSection(mb); +} + +bool macho::hasObjCSection(MemoryBufferRef mb) { + switch (identify_magic(mb.getBuffer())) { + case file_magic::macho_object: + return objectHasObjCSection(mb); + case file_magic::bitcode: + return check(isBitcodeContainingObjCCategory(mb)); + default: + return false; + } } diff --git a/gnu/llvm/lld/MachO/ObjC.h b/gnu/llvm/lld/MachO/ObjC.h index 8db459ad8e2..67fa4114db0 100644 --- a/gnu/llvm/lld/MachO/ObjC.h +++ b/gnu/llvm/lld/MachO/ObjC.h @@ -11,8 +11,7 @@ #include "llvm/Support/MemoryBuffer.h" -namespace lld { -namespace macho { +namespace lld::macho { namespace objc { @@ -25,7 +24,6 @@ constexpr const char ivar[] = "_OBJC_IVAR_$_"; bool hasObjCSection(llvm::MemoryBufferRef); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Options.td b/gnu/llvm/lld/MachO/Options.td index cda857d605b..f05835de040 100644 --- a/gnu/llvm/lld/MachO/Options.td +++ b/gnu/llvm/lld/MachO/Options.td @@ -28,9 +28,22 @@ def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, def threads_eq : Joined<["--"], "threads=">, HelpText<"Number of threads. '1' disables multi-threading. By default all available hardware threads are used">, Group; +def thinlto_emit_imports_files: Flag<["--"], "thinlto-emit-imports-files">, + Group; +def thinlto_emit_index_files: Flag<["--"], "thinlto-emit-index-files">, + Group; +def thinlto_index_only: Flag<["--"], "thinlto-index-only">, + Group; +def thinlto_index_only_eq: Joined<["--"], "thinlto-index-only=">, + Group; def thinlto_jobs_eq : Joined<["--"], "thinlto-jobs=">, HelpText<"Number of ThinLTO jobs. Default to --threads=">, Group; +def thinlto_object_suffix_replace_eq: + Joined<["--"], "thinlto-object-suffix-replace=">, + Group; +def thinlto_prefix_replace_eq: Joined<["--"], "thinlto-prefix-replace=">, + Group; def reproduce: Separate<["--"], "reproduce">, Group; def reproduce_eq: Joined<["--"], "reproduce=">, @@ -40,22 +53,28 @@ def reproduce_eq: Joined<["--"], "reproduce=">, def version: Flag<["--"], "version">, HelpText<"Display the version number and exit">, Group; -def lto_legacy_pass_manager: Flag<["--"], "lto-legacy-pass-manager">, - HelpText<"Use the legacy pass manager in LLVM">, - Group; def no_lto_legacy_pass_manager : Flag<["--"], "no-lto-legacy-pass-manager">, HelpText<"Use the new pass manager in LLVM">, Group; -def time_trace: Flag<["--"], "time-trace">, HelpText<"Record time trace">, +def time_trace_eq: Joined<["--"], "time-trace=">, + HelpText<"Record time trace to ">, + MetaVarName<"">, + Group; +def : Flag<["--"], "time-trace">, + Alias, + HelpText<"Record time trace to file next to output">, Group; def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">, HelpText<"Minimum time granularity (in microseconds) traced by time profiler">, Group; -def time_trace_file_eq: Joined<["--"], "time-trace-file=">, - HelpText<"Specify time trace output file">, +def deduplicate_strings: Flag<["--"], "deduplicate-strings">, + HelpText<"Enable string deduplication">, + Group; +def no_deduplicate_strings: Flag<["--"], "no-deduplicate-strings">, + HelpText<"Disable string deduplication. This helps uncover cases of comparing string addresses instead of equality and might have a link time performance benefit.">, Group; -def deduplicate_literals: Flag<["--"], "deduplicate-literals">, - HelpText<"Enable literal deduplication. This is implied by --icf={safe,all}">, +def dead_strip_duplicates: Flag<["--"], "dead-strip-duplicates">, + HelpText<"Do not error on duplicate symbols that will be dead stripped.">, Group; def print_dylib_search: Flag<["--"], "print-dylib-search">, HelpText<"Print which paths lld searched when trying to find dylibs">, @@ -68,11 +87,39 @@ def lto_O: Joined<["--"], "lto-O">, HelpText<"Set optimization level for LTO (default: 2)">, MetaVarName<"">, Group; -def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">, +def thinlto_cache_policy_eq: Joined<["--"], "thinlto-cache-policy=">, HelpText<"Pruning policy for the ThinLTO cache">, Group; def O : JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">; +def start_lib: Flag<["--"], "start-lib">, + HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">; +def end_lib: Flag<["--"], "end-lib">, + HelpText<"End a grouping of objects that should be treated as if they were together in an archive">; +def no_warn_dylib_install_name: Flag<["--"], "no-warn-dylib-install-name">, + HelpText<"Do not warn on -install_name if -dylib is not passed (default)">, + Group; +def warn_dylib_install_name: Flag<["--"], "warn-dylib-install-name">, + HelpText<"Warn on -install_name if -dylib is not passed">, + Group; +def call_graph_profile_sort: Flag<["--"], "call-graph-profile-sort">, + HelpText<"Reorder sections with call graph profile (default)">, + Group; +def no_call_graph_profile_sort : Flag<["--"], "no-call-graph-profile-sort">, + HelpText<"Do not reorder sections with call graph profile">, + Group; +def print_symbol_order_eq: Joined<["--"], "print-symbol-order=">, + HelpText<"Print a symbol order specified by --call-graph-profile-sort into the specified file">, + Group; +def ignore_auto_link_option : Separate<["--"], "ignore-auto-link-option">, + Group; +def ignore_auto_link_option_eq : Joined<["--"], "ignore-auto-link-option=">, + Alias(ignore_auto_link_option)>, + HelpText<"Ignore a single auto-linked library or framework. Useful to ignore invalid options that ld64 ignores">, + Group; +def strict_auto_link : Flag<["--"], "strict-auto-link">, + HelpText<"Always warn for missing frameworks or libraries if they are loaded via LC_LINKER_OPTIONS">, + Group; // This is a complete Options.td compiled from Apple's ld(1) manpage // dated 2018-03-07 and cross checked with ld64 source code in repo @@ -208,6 +255,9 @@ def F : JoinedOrSeparate<["-"], "F">, def all_load : Flag<["-"], "all_load">, HelpText<"Load all members of all static archive libraries">, Group; +def noall_load : Flag<["-"], "noall_load">, + HelpText<"Don't load all static members from archives, this is the default, this negates -all_load">, + Group; def ObjC : Flag<["-"], "ObjC">, HelpText<"Load all members of static archives that are an Objective-C class or category.">, Group; @@ -218,6 +268,14 @@ def force_load : Separate<["-"], "force_load">, def force_load_swift_libs : Flag<["-"], "force_load_swift_libs">, HelpText<"Apply -force_load to libraries listed in LC_LINKER_OPTIONS whose names start with 'swift'">, Group; +def load_hidden : Separate<["-"], "load_hidden">, + MetaVarName<"">, + HelpText<"Load all symbols from static library with hidden visibility">, + Group; +def hidden_l : Joined<["-"], "hidden-l">, + MetaVarName<"">, + HelpText<"Like -l, but load all symbols with hidden visibility">, + Group; def grp_content : OptionGroup<"content">, HelpText<"ADDITIONAL CONTENT">; @@ -230,6 +288,10 @@ def segcreate : MultiArg<["-"], "segcreate", 3>, Alias, HelpText<"Alias for -sectcreate">, Group; +def add_empty_section : MultiArg<["-"], "add_empty_section", 2>, + MetaVarName<"
">, + HelpText<"Create an empty
in ">, + Group; def filelist : Separate<["-"], "filelist">, MetaVarName<"">, HelpText<"Read names of files to link from ">, @@ -289,7 +351,8 @@ def no_branch_islands : Flag<["-"], "no_branch_islands">, Flags<[HelpHidden]>, Group; def no_deduplicate : Flag<["-"], "no_deduplicate">, - HelpText<"Disable code deduplicaiton (synonym for `--icf=none')">, + HelpText<"Disable code deduplication (synonym for `--icf=none')">, + Alias, AliasArgs<["none"]>, Group; def grp_version : OptionGroup<"version">, HelpText<"VERSION TARGETING">; @@ -417,7 +480,6 @@ def no_pie : Flag<["-"], "no_pie">, def pagezero_size : Separate<["-"], "pagezero_size">, MetaVarName<"">, HelpText<"Size of unreadable segment at address zero is hex (default is 4KB on 32-bit and 4GB on 64-bit)">, - Flags<[HelpHidden]>, Group; def stack_size : Separate<["-"], "stack_size">, MetaVarName<"">, @@ -460,6 +522,9 @@ def exported_symbols_list : Separate<["-"], "exported_symbols_list">, MetaVarName<"">, HelpText<"Symbols specified in remain global, while others become private externs">, Group; +def no_exported_symbols : Flag<["-"], "no_exported_symbols">, + HelpText<"Don't export any symbols from the binary, useful for main executables that don't have plugins">, + Group; def unexported_symbol : Separate<["-"], "unexported_symbol">, MetaVarName<"">, HelpText<"Global becomes private extern">, @@ -476,7 +541,6 @@ def reexported_symbols_list : Separate<["-"], "reexported_symbols_list">, def alias : MultiArg<["-"], "alias", 2>, MetaVarName<" ">, HelpText<"Create a symbol alias with default global visibility">, - Flags<[HelpHidden]>, Group; def alias_list : Separate<["-"], "alias_list">, MetaVarName<"">, @@ -523,7 +587,6 @@ def whyload : Flag<["-"], "whyload">, def why_live : Separate<["-"], "why_live">, MetaVarName<"">, HelpText<"Log a chain of references to , for use with -dead_strip">, - Flags<[HelpHidden]>, Group; def print_statistics : Flag<["-"], "print_statistics">, HelpText<"Log the linker's memory and CPU usage">, @@ -556,26 +619,21 @@ def grp_symtab : OptionGroup<"symtab">, HelpText<"SYMBOL TABLE">; def S : Flag<["-"], "S">, HelpText<"Strip debug information (STABS or DWARF) from the output">, - Flags<[HelpHidden]>, Group; def x : Flag<["-"], "x">, HelpText<"Exclude non-global symbols from the output symbol table">, - Flags<[HelpHidden]>, Group; def non_global_symbols_strip_list : Separate<["-"], "non_global_symbols_strip_list">, MetaVarName<"">, HelpText<"Specify in the non-global symbols that should be removed from the output symbol table">, - Flags<[HelpHidden]>, Group; def non_global_symbols_no_strip_list : Separate<["-"], "non_global_symbols_no_strip_list">, MetaVarName<"">, HelpText<"Specify in the non-global symbols that should remain in the output symbol table">, - Flags<[HelpHidden]>, Group; def oso_prefix : Separate<["-"], "oso_prefix">, MetaVarName<"">, HelpText<"Remove the prefix from OSO symbols in the debug map">, - Flags<[HelpHidden]>, Group; def add_ast_path : Separate<["-"], "add_ast_path">, MetaVarName<"">, @@ -863,7 +921,6 @@ def no_arch_warnings : Flag<["-"], "no_arch_warnings">, Group; def arch_errors_fatal : Flag<["-"], "arch_errors_fatal">, HelpText<"Escalate to errors any warnings about inputs whose architecture does not match the -arch option">, - Flags<[HelpHidden]>, Group; def e : Separate<["-"], "e">, MetaVarName<"">, @@ -871,7 +928,6 @@ def e : Separate<["-"], "e">, Group; def w : Flag<["-"], "w">, HelpText<"Suppress all warnings">, - Flags<[HelpHidden]>, Group; def final_output : Separate<["-"], "final_output">, MetaVarName<"">, @@ -947,6 +1003,22 @@ def mllvm : Separate<["-"], "mllvm">, def mcpu : Separate<["-"], "mcpu">, HelpText<"Processor family target for LTO code generation">, Group; +def no_dtrace_dof : Flag<["-"], "no_dtrace_dof">, + HelpText<"Disable dtrace-dof processing (default).">, + Group; +def objc_stubs_fast : Flag<["-"], "objc_stubs_fast">, + HelpText<"Produce larger stubs for Objective-C method calls with fewer jumps (default).">, + Group; +def objc_stubs_small : Flag<["-"], "objc_stubs_small">, + HelpText<"Produce smaller stubs for Objective-C method calls with more jumps.">, + Group; +def dyld_env : Separate<["-"], "dyld_env">, + MetaVarName<"">, + HelpText<"Specifies a LC_DYLD_ENVIRONMENT variable value pair.">, + Group; +def ignore_auto_link : Flag<["-"], "ignore_auto_link">, + HelpText<"Ignore LC_LINKER_OPTIONs">, + Group; def grp_deprecated : OptionGroup<"deprecated">, HelpText<"DEPRECATED">; @@ -973,10 +1045,6 @@ def no_dead_strip_inits_and_terms : Flag<["-"], "no_dead_strip_inits_and_terms"> HelpText<"Unnecessary option: initialization and termination are roots of the dead strip graph, so never dead stripped">, Flags<[HelpHidden]>, Group; -def noall_load : Flag<["-"], "noall_load">, - HelpText<"Unnecessary option: this is already the default">, - Flags<[HelpHidden]>, - Group; def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE">; @@ -1155,7 +1223,7 @@ def allow_simulator_linking_to_macosx_dylibs : Flag<["-"], "allow_simulator_link HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; -def bitcode_process_mode : Flag<["-"], "bitcode_process_mode">, +def bitcode_process_mode : Separate<["-"], "bitcode_process_mode">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; @@ -1179,10 +1247,6 @@ def debug_snapshot : Flag<["-"], "debug_snapshot">, Group; def demangle : Flag<["-"], "demangle">, HelpText<"Demangle symbol names in diagnostics">; -def dyld_env : Flag<["-"], "dyld_env">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, - Group; def encryptable : Flag<["-"], "encryptable">, HelpText<"Generate the LC_ENCRYPTION_INFO load command">, Group; @@ -1194,8 +1258,10 @@ def executable_path : Flag<["-"], "executable_path">, Flags<[HelpHidden]>, Group; def fixup_chains : Flag<["-"], "fixup_chains">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, + HelpText<"Emit chained fixups">, + Group; +def no_fixup_chains : Flag<["-"], "no_fixup_chains">, + HelpText<"Emit fixup information as classic dyld opcodes">, Group; def fixup_chains_section : Flag<["-"], "fixup_chains_section">, HelpText<"This option is undocumented in ld64">, @@ -1213,11 +1279,11 @@ def force_symbols_coalesce_list : Flag<["-"], "force_symbols_coalesce_list">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; -def force_symbols_not_weak_list : Flag<["-"], "force_symbols_not_weak_list">, +def force_symbols_not_weak_list : Separate<["-"], "force_symbols_not_weak_list">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; -def force_symbols_weak_list : Flag<["-"], "force_symbols_weak_list">, +def force_symbols_weak_list : Separate<["-"], "force_symbols_weak_list">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; @@ -1232,17 +1298,11 @@ def i : Flag<["-"], "i">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; -def ignore_auto_link : Flag<["-"], "ignore_auto_link">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, - Group; def ignore_optimization_hints : Flag<["-"], "ignore_optimization_hints">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, + HelpText<"Ignore Linker Optimization Hints">, Group; def init_offsets : Flag<["-"], "init_offsets">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, + HelpText<"Store __TEXT segment offsets of static initializers">, Group; def keep_dwarf_unwind : Flag<["-"], "keep_dwarf_unwind">, HelpText<"This option is undocumented in ld64">, @@ -1272,18 +1332,10 @@ def no_compact_unwind : Flag<["-"], "no_compact_unwind">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; -def no_dtrace_dof : Flag<["-"], "no_dtrace_dof">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, - Group; def no_new_main : Flag<["-"], "no_new_main">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, Group; -def objc_abi_version : Separate<["-"], "objc_abi_version">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, - Group; def pause : Flag<["-"], "pause">, HelpText<"This option is undocumented in ld64">, Flags<[HelpHidden]>, @@ -1327,3 +1379,10 @@ def new_linker : Flag<["-"], "new_linker">, HelpText<"This option is ignored in ld64">, Flags<[HelpHidden]>, Group; + +def grp_ignored_silently : OptionGroup<"ignored_silently">, HelpText<"IGNORED SILENTLY">; + +def objc_abi_version : Separate<["-"], "objc_abi_version">, + HelpText<"This option only applies to i386 in ld64">, + Flags<[HelpHidden]>, + Group; diff --git a/gnu/llvm/lld/MachO/OutputSection.cpp b/gnu/llvm/lld/MachO/OutputSection.cpp index 8d7a29c2916..461d4f82b18 100644 --- a/gnu/llvm/lld/MachO/OutputSection.cpp +++ b/gnu/llvm/lld/MachO/OutputSection.cpp @@ -13,9 +13,7 @@ using namespace llvm; using namespace lld; using namespace lld::macho; -uint64_t OutputSection::getSegmentOffset() const { - return addr - parent->addr; -} +uint64_t OutputSection::getSegmentOffset() const { return addr - parent->addr; } void OutputSection::assignAddressesToStartEndSymbols() { for (Defined *d : sectionStartSymbols) diff --git a/gnu/llvm/lld/MachO/OutputSection.h b/gnu/llvm/lld/MachO/OutputSection.h index eb554854cc8..5297a03c2cf 100644 --- a/gnu/llvm/lld/MachO/OutputSection.h +++ b/gnu/llvm/lld/MachO/OutputSection.h @@ -16,8 +16,7 @@ #include -namespace lld { -namespace macho { +namespace lld::macho { class Defined; class InputSection; @@ -58,13 +57,23 @@ public: // Unneeded sections are omitted entirely (header and body). virtual bool isNeeded() const { return true; } - virtual void finalize() { - // TODO investigate refactoring synthetic section finalization logic into - // overrides of this function. - } + // The implementations of this method can assume that it is only called right + // before addresses get assigned to this particular OutputSection. In + // particular, this means that it gets called only after addresses have been + // assigned to output sections that occur earlier in the output binary. + // Naturally, this means different sections' finalize() methods cannot execute + // concurrently with each other. As such, avoid using this method for + // operations that do not require this strict sequential guarantee. + // + // Operations that need to occur late in the linking process, but which do not + // need the sequential guarantee, should be named `finalizeContents()`. See + // e.g. LinkEditSection::finalizeContents() and + // CStringSection::finalizeContents(). + virtual void finalize() {} virtual void writeTo(uint8_t *buf) const = 0; + // Handle section$start$ and section$end$ symbols. void assignAddressesToStartEndSymbols(); StringRef name; @@ -88,7 +97,6 @@ private: Kind sectionKind; }; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/OutputSegment.cpp b/gnu/llvm/lld/MachO/OutputSegment.cpp index 3bbaf7f0304..a887bc4d515 100644 --- a/gnu/llvm/lld/MachO/OutputSegment.cpp +++ b/gnu/llvm/lld/MachO/OutputSegment.cpp @@ -44,6 +44,12 @@ static uint32_t maxProt(StringRef name) { return initProt(name); } +static uint32_t flags(StringRef name) { + // If we ever implement shared cache output support, SG_READ_ONLY should not + // be used for dylibs that can be placed in it. + return name == segment_names::dataConst ? (uint32_t)SG_READ_ONLY : 0; +} + size_t OutputSegment::numNonHiddenSections() const { size_t count = 0; for (const OutputSection *osec : sections) @@ -84,10 +90,12 @@ static int sectionOrder(OutputSection *osec) { // Sections are uniquely identified by their segment + section name. if (segname == segment_names::text) { return StringSwitch(osec->name) - .Case(section_names::header, -4) - .Case(section_names::text, -3) - .Case(section_names::stubs, -2) - .Case(section_names::stubHelper, -1) + .Case(section_names::header, -6) + .Case(section_names::text, -5) + .Case(section_names::stubs, -4) + .Case(section_names::stubHelper, -3) + .Case(section_names::objcStubs, -2) + .Case(section_names::initOffsets, -1) .Case(section_names::unwindInfo, std::numeric_limits::max() - 1) .Case(section_names::ehFrame, std::numeric_limits::max()) .Default(osec->inputOrder); @@ -118,6 +126,7 @@ static int sectionOrder(OutputSection *osec) { } } else if (segname == segment_names::linkEdit) { return StringSwitch(osec->name) + .Case(section_names::chainFixups, -11) .Case(section_names::rebase, -10) .Case(section_names::binding, -9) .Case(section_names::weakBinding, -8) @@ -161,6 +170,11 @@ void macho::sortOutputSegments() { static DenseMap nameToOutputSegment; std::vector macho::outputSegments; +void macho::resetOutputSegments() { + outputSegments.clear(); + nameToOutputSegment.clear(); +} + static StringRef maybeRenameSegment(StringRef name) { auto newName = config->segmentRenameMap.find(name); if (newName != config->segmentRenameMap.end()) @@ -179,6 +193,7 @@ OutputSegment *macho::getOrCreateOutputSegment(StringRef name) { segRef->name = name; segRef->maxProt = maxProt(name); segRef->initProt = initProt(name); + segRef->flags = flags(name); outputSegments.push_back(segRef); return segRef; diff --git a/gnu/llvm/lld/MachO/OutputSegment.h b/gnu/llvm/lld/MachO/OutputSegment.h index b3863f4148d..7a0c4a2065a 100644 --- a/gnu/llvm/lld/MachO/OutputSegment.h +++ b/gnu/llvm/lld/MachO/OutputSegment.h @@ -17,8 +17,7 @@ #include #include -namespace lld { -namespace macho { +namespace lld::macho { namespace segment_names { @@ -56,6 +55,7 @@ public: StringRef name; uint32_t maxProt = 0; uint32_t initProt = 0; + uint32_t flags = 0; uint8_t index; llvm::TinyPtrVector segmentStartSymbols; @@ -68,10 +68,10 @@ private: extern std::vector outputSegments; void sortOutputSegments(); +void resetOutputSegments(); OutputSegment *getOrCreateOutputSegment(StringRef name); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Relocations.cpp b/gnu/llvm/lld/MachO/Relocations.cpp index 03cb6973b6a..9e5ac69612c 100644 --- a/gnu/llvm/lld/MachO/Relocations.cpp +++ b/gnu/llvm/lld/MachO/Relocations.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Relocations.h" +#include "ConcatOutputSection.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -17,37 +18,86 @@ using namespace llvm; using namespace lld; using namespace lld::macho; +static_assert(sizeof(void *) != 8 || sizeof(Reloc) == 24, + "Try to minimize Reloc's size; we create many instances"); + bool macho::validateSymbolRelocation(const Symbol *sym, const InputSection *isec, const Reloc &r) { const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type); bool valid = true; - auto message = [relocAttrs, sym, isec, &valid](const Twine &diagnostic) { + auto message = [&](const Twine &diagnostic) { valid = false; - return (relocAttrs.name + " relocation " + diagnostic + " for `" + - sym->getName() + "' in " + toString(isec)) + return (isec->getLocation(r.offset) + ": " + relocAttrs.name + + " relocation " + diagnostic) .str(); }; if (relocAttrs.hasAttr(RelocAttrBits::TLV) != sym->isTlv()) - error(message(Twine("requires that variable ") + + error(message(Twine("requires that symbol ") + sym->getName() + " " + (sym->isTlv() ? "not " : "") + "be thread-local")); return valid; } -void macho::reportRangeError(const Reloc &r, const Twine &v, uint8_t bits, - int64_t min, uint64_t max) { +// Given an offset in the output buffer, figure out which ConcatInputSection (if +// any) maps to it. At the same time, update the offset such that it is relative +// to the InputSection rather than to the output buffer. +// +// Obtaining the InputSection allows us to have better error diagnostics. +// However, many of our relocation-handling methods do not take the InputSection +// as a parameter. Since we are already passing the buffer offsets to our Target +// methods, this function allows us to emit better errors without threading an +// additional InputSection argument through the call stack. +// +// This is implemented as a slow linear search through OutputSegments, +// OutputSections, and finally the InputSections themselves. However, this +// function should be called only on error paths, so some overhead is fine. +InputSection *macho::offsetToInputSection(uint64_t *off) { + for (OutputSegment *seg : outputSegments) { + if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize) + continue; + + const std::vector §ions = seg->getSections(); + size_t osecIdx = 0; + for (; osecIdx < sections.size(); ++osecIdx) + if (*off < sections[osecIdx]->fileOff) + break; + assert(osecIdx > 0); + // We should be only calling this function on offsets that belong to + // ConcatOutputSections. + auto *osec = cast(sections[osecIdx - 1]); + *off -= osec->fileOff; + + size_t isecIdx = 0; + for (; isecIdx < osec->inputs.size(); ++isecIdx) { + const ConcatInputSection *isec = osec->inputs[isecIdx]; + if (*off < isec->outSecOff) + break; + } + assert(isecIdx > 0); + ConcatInputSection *isec = osec->inputs[isecIdx - 1]; + *off -= isec->outSecOff; + return isec; + } + return nullptr; +} + +void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v, + uint8_t bits, int64_t min, uint64_t max) { std::string hint; + uint64_t off = reinterpret_cast(loc) - in.bufferStart; + const InputSection *isec = offsetToInputSection(&off); + std::string locStr = isec ? isec->getLocation(off) : "(invalid location)"; if (auto *sym = r.referent.dyn_cast()) hint = "; references " + toString(*sym); - // TODO: get location of reloc using something like LLD-ELF's getErrorPlace() - error("relocation " + target->getRelocAttrs(r.type).name + + error(locStr + ": relocation " + target->getRelocAttrs(r.type).name + " is out of range: " + v + " is not in [" + Twine(min) + ", " + Twine(max) + "]" + hint); } -void macho::reportRangeError(SymbolDiagnostic d, const Twine &v, uint8_t bits, - int64_t min, uint64_t max) { +void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v, + uint8_t bits, int64_t min, uint64_t max) { + // FIXME: should we use `loc` somehow to provide a better error message? std::string hint; if (d.symbol) hint = "; references " + toString(*d.symbol); diff --git a/gnu/llvm/lld/MachO/Relocations.h b/gnu/llvm/lld/MachO/Relocations.h index 91b2d00f26a..023d25a795a 100644 --- a/gnu/llvm/lld/MachO/Relocations.h +++ b/gnu/llvm/lld/MachO/Relocations.h @@ -17,8 +17,7 @@ #include #include -namespace lld { -namespace macho { +namespace lld::macho { LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); class Symbol; @@ -56,11 +55,18 @@ struct Reloc { uint8_t length = 0; // The offset from the start of the subsection that this relocation belongs // to. - uint64_t offset = 0; + uint32_t offset = 0; // Adding this offset to the address of the referent symbol or subsection // gives the destination that this relocation refers to. int64_t addend = 0; llvm::PointerUnion referent = nullptr; + + Reloc() = default; + + Reloc(uint8_t type, bool pcrel, uint8_t length, uint32_t offset, + int64_t addend, llvm::PointerUnion referent) + : type(type), pcrel(pcrel), length(length), offset(offset), + addend(addend), referent(referent) {} }; bool validateSymbolRelocation(const Symbol *, const InputSection *, @@ -70,28 +76,28 @@ bool validateSymbolRelocation(const Symbol *, const InputSection *, * v: The value the relocation is attempting to encode * bits: The number of bits actually available to encode this relocation */ -void reportRangeError(const Reloc &, const llvm::Twine &v, uint8_t bits, - int64_t min, uint64_t max); +void reportRangeError(void *loc, const Reloc &, const llvm::Twine &v, + uint8_t bits, int64_t min, uint64_t max); struct SymbolDiagnostic { const Symbol *symbol; llvm::StringRef reason; }; -void reportRangeError(SymbolDiagnostic, const llvm::Twine &v, uint8_t bits, - int64_t min, uint64_t max); +void reportRangeError(void *loc, SymbolDiagnostic, const llvm::Twine &v, + uint8_t bits, int64_t min, uint64_t max); template -inline void checkInt(Diagnostic d, int64_t v, int bits) { +inline void checkInt(void *loc, Diagnostic d, int64_t v, int bits) { if (v != llvm::SignExtend64(v, bits)) - reportRangeError(d, llvm::Twine(v), bits, llvm::minIntN(bits), + reportRangeError(loc, d, llvm::Twine(v), bits, llvm::minIntN(bits), llvm::maxIntN(bits)); } template -inline void checkUInt(Diagnostic d, uint64_t v, int bits) { +inline void checkUInt(void *loc, Diagnostic d, uint64_t v, int bits) { if ((v >> bits) != 0) - reportRangeError(d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits)); + reportRangeError(loc, d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits)); } inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) { @@ -107,9 +113,10 @@ inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) { } } +InputSection *offsetToInputSection(uint64_t *); + extern const RelocAttrs invalidRelocAttrs; -} // namespace macho -} // namespace lld +} // namespace lld::Macho #endif diff --git a/gnu/llvm/lld/MachO/SectionPriorities.cpp b/gnu/llvm/lld/MachO/SectionPriorities.cpp new file mode 100644 index 00000000000..976ea03980e --- /dev/null +++ b/gnu/llvm/lld/MachO/SectionPriorities.cpp @@ -0,0 +1,386 @@ +//===- SectionPriorities.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 is based on the ELF port, see ELF/CallGraphSort.cpp for the details +/// about the algorithm. +/// +//===----------------------------------------------------------------------===// + +#include "SectionPriorities.h" +#include "Config.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" + +#include "lld/Common/Args.h" +#include "lld/Common/CommonLinkerContext.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TimeProfiler.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::sys; +using namespace lld; +using namespace lld::macho; + +PriorityBuilder macho::priorityBuilder; + +namespace { + +size_t highestAvailablePriority = std::numeric_limits::max(); + +struct Edge { + int from; + uint64_t weight; +}; + +struct Cluster { + 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); + } + + int next; + int prev; + uint64_t size; + uint64_t weight = 0; + uint64_t initialWeight = 0; + Edge bestPred = {-1, 0}; +}; + +class CallGraphSort { +public: + CallGraphSort(const MapVector &profile); + + DenseMap run(); + +private: + std::vector clusters; + std::vector sections; +}; +// Maximum amount the combined cluster density can be worse than the original +// cluster to consider merging. +constexpr int MAX_DENSITY_DEGRADATION = 8; +} // end anonymous namespace + +// Take the edge list in callGraphProfile, resolve symbol names to Symbols, and +// generate a graph between InputSections with the provided weights. +CallGraphSort::CallGraphSort(const MapVector &profile) { + DenseMap secToCluster; + + auto getOrCreateCluster = [&](const InputSection *isec) -> int { + 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 (const std::pair &c : profile) { + const auto fromSec = c.first.first->canonical(); + const auto toSec = c.first.second->canonical(); + 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 (fromSec->parent != toSec->parent) + continue; + + int from = getOrCreateCluster(fromSec); + int to = getOrCreateCluster(toSec); + + 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; +} + +// 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.size = 0; + from.weight = 0; +} + +// Group InputSections into clusters using the Call-Chain Clustering heuristic +// then sort the clusters by density. +DenseMap CallGraphSort::run() { + const uint64_t maxClusterSize = target->getPageSize(); + + // Cluster indices sorted by density. + std::vector sorted(clusters.size()); + // For union-find. + std::vector leaders(clusters.size()); + + 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 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; + + int predL = getLeader(leaders, c.bestPred.from); + // Already in the same cluster. + if (l == predL) + continue; + + Cluster *predC = &clusters[predL]; + if (c.size + predC->size > maxClusterSize) + continue; + + if (isNewDensityBad(*predC, c)) + continue; + + leaders[l] = predL; + mergeClusters(clusters, *predC, predL, c, l); + } + // 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(); + }); + + DenseMap orderMap; + + // Sections will be sorted by decreasing order. Absent sections will have + // priority 0 and be placed at the end of sections. + // NB: This is opposite from COFF/ELF to be compatible with the existing + // order-file code. + int curOrder = highestAvailablePriority; + for (int leader : sorted) { + for (int i = leader;;) { + orderMap[sections[i]] = curOrder--; + i = clusters[i].next; + if (i == leader) + break; + } + } + if (!config->printSymbolOrder.empty()) { + std::error_code ec; + 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 decreasing curOrder + // Instead of sorting all the orderMap, just repeat the loops above. + for (int leader : sorted) + for (int i = leader;;) { + const InputSection *isec = sections[i]; + // 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 : isec->getFile()->symbols) { + if (auto *d = dyn_cast_or_null(sym)) { + if (d->isec == isec) + os << sym->getName() << "\n"; + } + } + i = clusters[i].next; + if (i == leader) + break; + } + } + + return orderMap; +} + +std::optional +macho::PriorityBuilder::getSymbolPriority(const Defined *sym) { + if (sym->isAbsolute()) + return std::nullopt; + + auto it = priorities.find(sym->getName()); + if (it == priorities.end()) + return std::nullopt; + const SymbolPriorityEntry &entry = it->second; + const InputFile *f = sym->isec->getFile(); + if (!f) + return entry.anyObjectFile; + // We don't use toString(InputFile *) here because it returns the full path + // for object files, and we only want the basename. + StringRef filename; + if (f->archiveName.empty()) + filename = path::filename(f->getName()); + else + filename = saver().save(path::filename(f->archiveName) + "(" + + path::filename(f->getName()) + ")"); + return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile); +} + +void macho::PriorityBuilder::extractCallGraphProfile() { + TimeTraceScope timeScope("Extract call graph profile"); + bool hasOrderFile = !priorities.empty(); + for (const InputFile *file : inputFiles) { + auto *obj = dyn_cast_or_null(file); + if (!obj) + continue; + for (const CallGraphEntry &entry : obj->callGraph) { + assert(entry.fromIndex < obj->symbols.size() && + entry.toIndex < obj->symbols.size()); + auto *fromSym = dyn_cast_or_null(obj->symbols[entry.fromIndex]); + auto *toSym = dyn_cast_or_null(obj->symbols[entry.toIndex]); + if (fromSym && toSym && + (!hasOrderFile || + (!getSymbolPriority(fromSym) && !getSymbolPriority(toSym)))) + callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count; + } + } +} + +void macho::PriorityBuilder::parseOrderFile(StringRef path) { + assert(callGraphProfile.empty() && + "Order file must be parsed before call graph profile is processed"); + std::optional buffer = readFile(path); + if (!buffer) { + error("Could not read order file at " + path); + return; + } + + MemoryBufferRef mbref = *buffer; + for (StringRef line : args::getLines(mbref)) { + StringRef objectFile, symbol; + line = line.take_until([](char c) { return c == '#'; }); // ignore comments + line = line.ltrim(); + + CPUType cpuType = StringSwitch(line) + .StartsWith("i386:", CPU_TYPE_I386) + .StartsWith("x86_64:", CPU_TYPE_X86_64) + .StartsWith("arm:", CPU_TYPE_ARM) + .StartsWith("arm64:", CPU_TYPE_ARM64) + .StartsWith("ppc:", CPU_TYPE_POWERPC) + .StartsWith("ppc64:", CPU_TYPE_POWERPC64) + .Default(CPU_TYPE_ANY); + + if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType) + continue; + + // Drop the CPU type as well as the colon + if (cpuType != CPU_TYPE_ANY) + line = line.drop_until([](char c) { return c == ':'; }).drop_front(); + + constexpr std::array fileEnds = {".o:", ".o):"}; + for (StringRef fileEnd : fileEnds) { + size_t pos = line.find(fileEnd); + if (pos != StringRef::npos) { + // Split the string around the colon + objectFile = line.take_front(pos + fileEnd.size() - 1); + line = line.drop_front(pos + fileEnd.size()); + break; + } + } + symbol = line.trim(); + + if (!symbol.empty()) { + SymbolPriorityEntry &entry = priorities[symbol]; + if (!objectFile.empty()) + entry.objectFiles.insert( + std::make_pair(objectFile, highestAvailablePriority)); + else + entry.anyObjectFile = + std::max(entry.anyObjectFile, highestAvailablePriority); + } + + --highestAvailablePriority; + } +} + +DenseMap +macho::PriorityBuilder::buildInputSectionPriorities() { + DenseMap sectionPriorities; + if (config->callGraphProfileSort) { + // Sort sections by the profile data provided by __LLVM,__cg_profile + // sections. + // + // This first builds a call graph based on the profile data then merges + // sections according to the C³ heuristic. All clusters are then sorted by a + // density metric to further improve locality. + TimeTraceScope timeScope("Call graph profile sort"); + sectionPriorities = CallGraphSort(callGraphProfile).run(); + } + + if (priorities.empty()) + return sectionPriorities; + + auto addSym = [&](const Defined *sym) { + std::optional symbolPriority = getSymbolPriority(sym); + if (!symbolPriority) + return; + size_t &priority = sectionPriorities[sym->isec]; + priority = std::max(priority, *symbolPriority); + }; + + // TODO: Make sure this handles weak symbols correctly. + for (const InputFile *file : inputFiles) { + if (isa(file)) + for (Symbol *sym : file->symbols) + if (auto *d = dyn_cast_or_null(sym)) + addSym(d); + } + + return sectionPriorities; +} diff --git a/gnu/llvm/lld/MachO/SectionPriorities.h b/gnu/llvm/lld/MachO/SectionPriorities.h new file mode 100644 index 00000000000..9906ea4c214 --- /dev/null +++ b/gnu/llvm/lld/MachO/SectionPriorities.h @@ -0,0 +1,80 @@ +//===- SectionPriorities.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_MACHO_SECTION_PRIORITIES_H +#define LLD_MACHO_SECTION_PRIORITIES_H + +#include "InputSection.h" +#include "llvm/ADT/DenseMap.h" + +namespace lld::macho { + +using SectionPair = std::pair; + +class PriorityBuilder { +public: + // Reads every input section's call graph profile, and combines them into + // callGraphProfile. If an order file is present, any edges where one or both + // of the vertices are specified in the order file are discarded. + void extractCallGraphProfile(); + + // Reads the order file at `path` into config->priorities. + // + // An order file has one entry per line, in the following format: + // + // :: + // + // and are optional. If not specified, then that entry + // matches any symbol of that name. Parsing this format is not quite + // straightforward because the symbol name itself can contain colons, so when + // encountering a colon, we consider the preceding characters to decide if it + // can be a valid CPU type or file path. + // + // If a symbol is matched by multiple entries, then it takes the + // lowest-ordered entry (the one nearest to the front of the list.) + // + // The file can also have line comments that start with '#'. + void parseOrderFile(StringRef path); + + // Returns layout priorities for some or all input sections. Sections are laid + // out in decreasing order; that is, a higher priority section will be closer + // to the beginning of its output section. + // + // If either an order file or a call graph profile are present, this is used + // as the source of priorities. If both are present, the order file takes + // precedence, but the call graph profile is still used for symbols that don't + // appear in the order file. If neither is present, an empty map is returned. + // + // Each section gets assigned the priority of the highest-priority symbol it + // contains. + llvm::DenseMap buildInputSectionPriorities(); + +private: + // The symbol with the highest priority should be ordered first in the output + // section (modulo input section contiguity constraints). Using priority + // (highest first) instead of order (lowest first) has the convenient property + // that the default-constructed zero priority -- for symbols/sections without + // a user-defined order -- naturally ends up putting them at the end of the + // output. + struct SymbolPriorityEntry { + // The priority given to a matching symbol, regardless of which object file + // it originated from. + size_t anyObjectFile = 0; + // The priority given to a matching symbol from a particular object file. + llvm::DenseMap objectFiles; + }; + + std::optional getSymbolPriority(const Defined *sym); + llvm::DenseMap priorities; + llvm::MapVector callGraphProfile; +}; + +extern PriorityBuilder priorityBuilder; +} // namespace lld::macho + +#endif diff --git a/gnu/llvm/lld/MachO/SymbolTable.cpp b/gnu/llvm/lld/MachO/SymbolTable.cpp index c5808a89bf5..03631239ccb 100644 --- a/gnu/llvm/lld/MachO/SymbolTable.cpp +++ b/gnu/llvm/lld/MachO/SymbolTable.cpp @@ -10,10 +10,12 @@ #include "ConcatOutputSection.h" #include "Config.h" #include "InputFiles.h" +#include "InputSection.h" #include "Symbols.h" #include "SyntheticSections.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "llvm/Demangle/Demangle.h" using namespace llvm; using namespace lld; @@ -44,72 +46,113 @@ std::pair SymbolTable::insert(StringRef name, return {sym, p.second}; } +namespace { +struct DuplicateSymbolDiag { + // Pair containing source location and source file + const std::pair src1; + const std::pair src2; + const Symbol *sym; + + DuplicateSymbolDiag(const std::pair src1, + const std::pair src2, + const Symbol *sym) + : src1(src1), src2(src2), sym(sym) {} +}; +SmallVector dupSymDiags; +} // namespace + Defined *SymbolTable::addDefined(StringRef name, InputFile *file, InputSection *isec, uint64_t value, uint64_t size, bool isWeakDef, bool isPrivateExtern, bool isThumb, - bool isReferencedDynamically, - bool noDeadStrip) { - Symbol *s; - bool wasInserted; + bool isReferencedDynamically, bool noDeadStrip, + bool isWeakDefCanBeHidden) { bool overridesWeakDef = false; - std::tie(s, wasInserted) = insert(name, file); + auto [s, wasInserted] = insert(name, file); - assert(!isWeakDef || (isa(file) && !isec) || - (isa(file) && file == isec->getFile())); + assert(!file || !isa(file) || !isec); if (!wasInserted) { if (auto *defined = dyn_cast(s)) { if (isWeakDef) { + // See further comment in createDefined() in InputFiles.cpp if (defined->isWeakDef()) { - // Both old and new symbol weak (e.g. inline function in two TUs): - // If one of them isn't private extern, the merged symbol isn't. defined->privateExtern &= isPrivateExtern; + defined->weakDefCanBeHidden &= isWeakDefCanBeHidden; defined->referencedDynamically |= isReferencedDynamically; defined->noDeadStrip |= noDeadStrip; - - // FIXME: Handle this for bitcode files. - // FIXME: We currently only do this if both symbols are weak. - // We could do this if either is weak (but getting the - // case where !isWeakDef && defined->isWeakDef() right - // requires some care and testing). - if (auto concatIsec = dyn_cast_or_null(isec)) - concatIsec->wasCoalesced = true; } - + // FIXME: Handle this for bitcode files. + if (auto concatIsec = dyn_cast_or_null(isec)) + concatIsec->wasCoalesced = true; return defined; } - if (!defined->isWeakDef()) - error("duplicate symbol: " + name + "\n>>> defined in " + - toString(defined->getFile()) + "\n>>> defined in " + - toString(file)); + + if (defined->isWeakDef()) { + // FIXME: Handle this for bitcode files. + if (auto concatIsec = + dyn_cast_or_null(defined->isec)) { + concatIsec->wasCoalesced = true; + concatIsec->symbols.erase(llvm::find(concatIsec->symbols, defined)); + } + } else { + std::string srcLoc1 = defined->getSourceLocation(); + std::string srcLoc2 = isec ? isec->getSourceLocation(value) : ""; + std::string srcFile1 = toString(defined->getFile()); + std::string srcFile2 = toString(file); + + dupSymDiags.push_back({make_pair(srcLoc1, srcFile1), + make_pair(srcLoc2, srcFile2), defined}); + } + } else if (auto *dysym = dyn_cast(s)) { overridesWeakDef = !isWeakDef && dysym->isWeakDef(); dysym->unreference(); + } else if (auto *undef = dyn_cast(s)) { + // Preserve the original bitcode file name (instead of using the object + // file name). + if (undef->wasBitcodeSymbol) + file = undef->getFile(); } // Defined symbols take priority over other types of symbols, so in case // of a name conflict, we fall through to the replaceSymbol() call below. } + // With -flat_namespace, all extern symbols in dylibs are interposable. + // FIXME: Add support for `-interposable` (PR53680). + bool interposable = config->namespaceKind == NamespaceKind::flat && + config->outputType != MachO::MH_EXECUTE && + !isPrivateExtern; Defined *defined = replaceSymbol( s, name, file, isec, value, size, isWeakDef, /*isExternal=*/true, - isPrivateExtern, isThumb, isReferencedDynamically, noDeadStrip); - defined->overridesWeakDef = overridesWeakDef; + isPrivateExtern, /*includeInSymtab=*/true, isThumb, + isReferencedDynamically, noDeadStrip, overridesWeakDef, + isWeakDefCanBeHidden, interposable); return defined; } +Defined *SymbolTable::aliasDefined(Defined *src, StringRef target, + InputFile *newFile, bool makePrivateExtern) { + bool isPrivateExtern = makePrivateExtern || src->privateExtern; + return addDefined(target, newFile, src->isec, src->value, src->size, + src->isWeakDef(), isPrivateExtern, src->thumb, + src->referencedDynamically, src->noDeadStrip, + src->weakDefCanBeHidden); +} + Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file, bool isWeakRef) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name, file); + auto [s, wasInserted] = insert(name, file); RefState refState = isWeakRef ? RefState::Weak : RefState::Strong; if (wasInserted) - replaceSymbol(s, name, file, refState); - else if (auto *lazy = dyn_cast(s)) + replaceSymbol(s, name, file, refState, + /*wasBitcodeSymbol=*/false); + else if (auto *lazy = dyn_cast(s)) lazy->fetchArchiveMember(); + else if (isa(s)) + extract(*s->getFile(), s->getName()); else if (auto *dynsym = dyn_cast(s)) dynsym->reference(refState); else if (auto *undefined = dyn_cast(s)) @@ -119,9 +162,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file, Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size, uint32_t align, bool isPrivateExtern) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name, file); + auto [s, wasInserted] = insert(name, file); if (!wasInserted) { if (auto *common = dyn_cast(s)) { @@ -140,9 +181,7 @@ Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size, Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name, file); + auto [s, wasInserted] = insert(name, file); RefState refState = RefState::Unreferenced; if (!wasInserted) { @@ -173,16 +212,40 @@ Symbol *SymbolTable::addDynamicLookup(StringRef name) { return addDylib(name, /*file=*/nullptr, /*isWeakDef=*/false, /*isTlv=*/false); } -Symbol *SymbolTable::addLazy(StringRef name, ArchiveFile *file, - const object::Archive::Symbol &sym) { - Symbol *s; - bool wasInserted; - std::tie(s, wasInserted) = insert(name, file); +Symbol *SymbolTable::addLazyArchive(StringRef name, ArchiveFile *file, + const object::Archive::Symbol &sym) { + auto [s, wasInserted] = insert(name, file); - if (wasInserted) - replaceSymbol(s, file, sym); - else if (isa(s) || (isa(s) && s->isWeakDef())) + if (wasInserted) { + replaceSymbol(s, file, sym); + } else if (isa(s)) { file->fetch(sym); + } else if (auto *dysym = dyn_cast(s)) { + if (dysym->isWeakDef()) { + if (dysym->getRefState() != RefState::Unreferenced) + file->fetch(sym); + else + replaceSymbol(s, file, sym); + } + } + return s; +} + +Symbol *SymbolTable::addLazyObject(StringRef name, InputFile &file) { + auto [s, wasInserted] = insert(name, &file); + + if (wasInserted) { + replaceSymbol(s, file, name); + } else if (isa(s)) { + extract(file, name); + } else if (auto *dysym = dyn_cast(s)) { + if (dysym->isWeakDef()) { + if (dysym->getRefState() != RefState::Unreferenced) + extract(file, name); + else + replaceSymbol(s, file, name); + } + } return s; } @@ -190,10 +253,12 @@ Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec, uint64_t value, bool isPrivateExtern, bool includeInSymtab, bool referencedDynamically) { - Defined *s = addDefined(name, nullptr, isec, value, /*size=*/0, - /*isWeakDef=*/false, isPrivateExtern, - /*isThumb=*/false, referencedDynamically, - /*noDeadStrip=*/false); + assert(!isec || !isec->getFile()); // See makeSyntheticInputSection(). + Defined *s = + addDefined(name, /*file=*/nullptr, isec, value, /*size=*/0, + /*isWeakDef=*/false, isPrivateExtern, /*isThumb=*/false, + referencedDynamically, /*noDeadStrip=*/false, + /*isWeakDefCanBeHidden=*/false); s->includeInSymtab = includeInSymtab; return s; } @@ -211,8 +276,7 @@ static Defined *createBoundarySymbol(const Undefined &sym) { static void handleSectionBoundarySymbol(const Undefined &sym, StringRef segSect, Boundary which) { - StringRef segName, sectName; - std::tie(segName, sectName) = segSect.split('$'); + auto [segName, sectName] = segSect.split('$'); // Attach the symbol to any InputSection that will end up in the right // OutputSection -- it doesn't matter which one we pick. @@ -230,7 +294,7 @@ static void handleSectionBoundarySymbol(const Undefined &sym, StringRef segSect, } if (!osec) { - ConcatInputSection *isec = make(segName, sectName); + ConcatInputSection *isec = makeSyntheticInputSection(segName, sectName); // This runs after markLive() and is only called for Undefineds that are // live. Marking the isec live ensures an OutputSection is created that the @@ -259,50 +323,289 @@ static void handleSegmentBoundarySymbol(const Undefined &sym, StringRef segName, seg->segmentEndSymbols.push_back(createBoundarySymbol(sym)); } -void lld::macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) { +// Try to find a definition for an undefined symbol. +// Returns true if a definition was found and no diagnostics are needed. +static bool recoverFromUndefinedSymbol(const Undefined &sym) { // Handle start/end symbols. StringRef name = sym.getName(); - if (name.consume_front("section$start$")) - return handleSectionBoundarySymbol(sym, name, Boundary::Start); - if (name.consume_front("section$end$")) - return handleSectionBoundarySymbol(sym, name, Boundary::End); - if (name.consume_front("segment$start$")) - return handleSegmentBoundarySymbol(sym, name, Boundary::Start); - if (name.consume_front("segment$end$")) - return handleSegmentBoundarySymbol(sym, name, Boundary::End); + if (name.consume_front("section$start$")) { + handleSectionBoundarySymbol(sym, name, Boundary::Start); + return true; + } + if (name.consume_front("section$end$")) { + handleSectionBoundarySymbol(sym, name, Boundary::End); + return true; + } + if (name.consume_front("segment$start$")) { + handleSegmentBoundarySymbol(sym, name, Boundary::Start); + return true; + } + if (name.consume_front("segment$end$")) { + handleSegmentBoundarySymbol(sym, name, Boundary::End); + return true; + } + + // Leave dtrace symbols, since we will handle them when we do the relocation + if (name.startswith("___dtrace_")) + return true; // Handle -U. if (config->explicitDynamicLookups.count(sym.getName())) { symtab->addDynamicLookup(sym.getName()); - return; + return true; } // Handle -undefined. - auto message = [source, &sym]() { - std::string message = "undefined symbol"; - if (config->archMultiple) - message += (" for arch " + getArchitectureName(config->arch())).str(); - message += ": " + toString(sym); - if (!source.empty()) - message += "\n>>> referenced by " + source.str(); - else - message += "\n>>> referenced by " + toString(sym.getFile()); - return message; - }; - switch (config->undefinedSymbolTreatment) { - case UndefinedSymbolTreatment::error: - error(message()); - break; - case UndefinedSymbolTreatment::warning: - warn(message()); - LLVM_FALLTHROUGH; - case UndefinedSymbolTreatment::dynamic_lookup: - case UndefinedSymbolTreatment::suppress: + if (config->undefinedSymbolTreatment == + UndefinedSymbolTreatment::dynamic_lookup || + config->undefinedSymbolTreatment == UndefinedSymbolTreatment::suppress) { symtab->addDynamicLookup(sym.getName()); - break; - case UndefinedSymbolTreatment::unknown: - llvm_unreachable("unknown -undefined TREATMENT"); + return true; } + + // We do not return true here, as we still need to print diagnostics. + if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::warning) + symtab->addDynamicLookup(sym.getName()); + + return false; +} + +namespace { +struct UndefinedDiag { + struct SectionAndOffset { + const InputSection *isec; + uint64_t offset; + }; + + std::vector codeReferences; + std::vector otherReferences; +}; + +MapVector undefs; +} + +void macho::reportPendingDuplicateSymbols() { + for (const auto &duplicate : dupSymDiags) { + if (!config->deadStripDuplicates || duplicate.sym->isLive()) { + std::string message = + "duplicate symbol: " + toString(*duplicate.sym) + "\n>>> defined in "; + if (!duplicate.src1.first.empty()) + message += duplicate.src1.first + "\n>>> "; + message += duplicate.src1.second + "\n>>> defined in "; + if (!duplicate.src2.first.empty()) + message += duplicate.src2.first + "\n>>> "; + error(message + duplicate.src2.second); + } + } +} + +// Check whether the definition name def is a mangled function name that matches +// the reference name ref. +static bool canSuggestExternCForCXX(StringRef ref, StringRef def) { + llvm::ItaniumPartialDemangler d; + std::string name = def.str(); + if (d.partialDemangle(name.c_str())) + return false; + char *buf = d.getFunctionName(nullptr, nullptr); + if (!buf) + return false; + bool ret = ref == buf; + free(buf); + return ret; +} + +// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns +// the suggested symbol, which is either in the symbol table, or in the same +// file of sym. +static const Symbol *getAlternativeSpelling(const Undefined &sym, + std::string &pre_hint, + std::string &post_hint) { + DenseMap map; + if (sym.getFile() && sym.getFile()->kind() == InputFile::ObjKind) { + // Build a map of local defined symbols. + for (const Symbol *s : sym.getFile()->symbols) + if (auto *defined = dyn_cast_or_null(s)) + if (!defined->isExternal()) + map.try_emplace(s->getName(), s); + } + + auto suggest = [&](StringRef newName) -> const Symbol * { + // If defined locally. + if (const Symbol *s = map.lookup(newName)) + return s; + + // If in the symbol table and not undefined. + if (const Symbol *s = symtab->find(newName)) + if (dyn_cast(s) == nullptr) + return s; + + return nullptr; + }; + + // This loop enumerates all strings of Levenshtein distance 1 as typo + // correction candidates and suggests the one that exists as a non-undefined + // symbol. + StringRef name = sym.getName(); + for (size_t i = 0, e = name.size(); i != e + 1; ++i) { + // Insert a character before name[i]. + std::string newName = (name.substr(0, i) + "0" + name.substr(i)).str(); + for (char c = '0'; c <= 'z'; ++c) { + newName[i] = c; + if (const Symbol *s = suggest(newName)) + return s; + } + if (i == e) + break; + + // Substitute name[i]. + newName = std::string(name); + for (char c = '0'; c <= 'z'; ++c) { + newName[i] = c; + if (const Symbol *s = suggest(newName)) + return s; + } + + // Transpose name[i] and name[i+1]. This is of edit distance 2 but it is + // common. + if (i + 1 < e) { + newName[i] = name[i + 1]; + newName[i + 1] = name[i]; + if (const Symbol *s = suggest(newName)) + return s; + } + + // Delete name[i]. + newName = (name.substr(0, i) + name.substr(i + 1)).str(); + if (const Symbol *s = suggest(newName)) + return s; + } + + // Case mismatch, e.g. Foo vs FOO. + for (auto &it : map) + if (name.equals_insensitive(it.first)) + return it.second; + for (Symbol *sym : symtab->getSymbols()) + if (dyn_cast(sym) == nullptr && + name.equals_insensitive(sym->getName())) + return sym; + + // The reference may be a mangled name while the definition is not. Suggest a + // missing extern "C". + if (name.startswith("__Z")) { + std::string buf = name.str(); + llvm::ItaniumPartialDemangler d; + if (!d.partialDemangle(buf.c_str())) + if (char *buf = d.getFunctionName(nullptr, nullptr)) { + const Symbol *s = suggest((Twine("_") + buf).str()); + free(buf); + if (s) { + pre_hint = ": extern \"C\" "; + return s; + } + } + } else { + StringRef name_without_underscore = name; + name_without_underscore.consume_front("_"); + const Symbol *s = nullptr; + for (auto &it : map) + if (canSuggestExternCForCXX(name_without_underscore, it.first)) { + s = it.second; + break; + } + if (!s) + for (Symbol *sym : symtab->getSymbols()) + if (canSuggestExternCForCXX(name_without_underscore, sym->getName())) { + s = sym; + break; + } + if (s) { + pre_hint = " to declare "; + post_hint = " as extern \"C\"?"; + return s; + } + } + + return nullptr; +} + +static void reportUndefinedSymbol(const Undefined &sym, + const UndefinedDiag &locations, + bool correctSpelling) { + std::string message = "undefined symbol"; + if (config->archMultiple) + message += (" for arch " + getArchitectureName(config->arch())).str(); + message += ": " + toString(sym); + + const size_t maxUndefinedReferences = 3; + size_t i = 0; + for (const std::string &loc : locations.otherReferences) { + if (i >= maxUndefinedReferences) + break; + message += "\n>>> referenced by " + loc; + ++i; + } + + for (const UndefinedDiag::SectionAndOffset &loc : locations.codeReferences) { + if (i >= maxUndefinedReferences) + break; + message += "\n>>> referenced by "; + std::string src = loc.isec->getSourceLocation(loc.offset); + if (!src.empty()) + message += src + "\n>>> "; + message += loc.isec->getLocation(loc.offset); + ++i; + } + + size_t totalReferences = + locations.otherReferences.size() + locations.codeReferences.size(); + if (totalReferences > i) + message += + ("\n>>> referenced " + Twine(totalReferences - i) + " more times") + .str(); + + if (correctSpelling) { + std::string pre_hint = ": ", post_hint; + if (const Symbol *corrected = + getAlternativeSpelling(sym, pre_hint, post_hint)) { + message += + "\n>>> did you mean" + pre_hint + toString(*corrected) + post_hint; + if (corrected->getFile()) + message += "\n>>> defined in: " + toString(corrected->getFile()); + } + } + + if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error) + error(message); + else if (config->undefinedSymbolTreatment == + UndefinedSymbolTreatment::warning) + warn(message); + else + assert(false && "diagnostics make sense for -undefined error|warning only"); +} + +void macho::reportPendingUndefinedSymbols() { + // Enable spell corrector for the first 2 diagnostics. + for (const auto &[i, undef] : llvm::enumerate(undefs)) + reportUndefinedSymbol(*undef.first, undef.second, i < 2); + + // This function is called multiple times during execution. Clear the printed + // diagnostics to avoid printing the same things again the next time. + undefs.clear(); +} + +void macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) { + if (recoverFromUndefinedSymbol(sym)) + return; + + undefs[&sym].otherReferences.push_back(source.str()); +} + +void macho::treatUndefinedSymbol(const Undefined &sym, const InputSection *isec, + uint64_t offset) { + if (recoverFromUndefinedSymbol(sym)) + return; + + undefs[&sym].codeReferences.push_back({isec, offset}); } -SymbolTable *macho::symtab; +std::unique_ptr macho::symtab; diff --git a/gnu/llvm/lld/MachO/SymbolTable.h b/gnu/llvm/lld/MachO/SymbolTable.h index 17f1ecbd346..ea02367edc2 100644 --- a/gnu/llvm/lld/MachO/SymbolTable.h +++ b/gnu/llvm/lld/MachO/SymbolTable.h @@ -16,8 +16,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/Object/Archive.h" -namespace lld { -namespace macho { +namespace lld::macho { class ArchiveFile; class DylibFile; @@ -40,7 +39,11 @@ public: Defined *addDefined(StringRef name, InputFile *, InputSection *, uint64_t value, uint64_t size, bool isWeakDef, bool isPrivateExtern, bool isThumb, - bool isReferencedDynamically, bool noDeadStrip); + bool isReferencedDynamically, bool noDeadStrip, + bool isWeakDefCanBeHidden); + + Defined *aliasDefined(Defined *src, StringRef target, InputFile *newFile, + bool makePrivateExtern = false); Symbol *addUndefined(StringRef name, InputFile *, bool isWeakRef); @@ -50,8 +53,9 @@ public: Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv); Symbol *addDynamicLookup(StringRef name); - Symbol *addLazy(StringRef name, ArchiveFile *file, - const llvm::object::Archive::Symbol &sym); + Symbol *addLazyArchive(StringRef name, ArchiveFile *file, + const llvm::object::Archive::Symbol &sym); + Symbol *addLazyObject(StringRef name, InputFile &file); Defined *addSynthetic(StringRef name, InputSection *, uint64_t value, bool isPrivateExtern, bool includeInSymtab, @@ -67,11 +71,16 @@ private: std::vector symVector; }; -void treatUndefinedSymbol(const Undefined &, StringRef source = ""); +void reportPendingUndefinedSymbols(); +void reportPendingDuplicateSymbols(); + +// Call reportPendingUndefinedSymbols() to emit diagnostics. +void treatUndefinedSymbol(const Undefined &, StringRef source); +void treatUndefinedSymbol(const Undefined &, const InputSection *, + uint64_t offset); -extern SymbolTable *symtab; +extern std::unique_ptr symtab; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Symbols.cpp b/gnu/llvm/lld/MachO/Symbols.cpp index 47f30d4141f..cb3b271a191 100644 --- a/gnu/llvm/lld/MachO/Symbols.cpp +++ b/gnu/llvm/lld/MachO/Symbols.cpp @@ -9,48 +9,80 @@ #include "Symbols.h" #include "InputFiles.h" #include "SyntheticSections.h" +#include "llvm/Demangle/Demangle.h" using namespace llvm; using namespace lld; using namespace lld::macho; -// Returns a symbol for an error message. -static std::string demangle(StringRef symName) { - if (config->demangle) - return demangleItanium(symName); - return std::string(symName); +static_assert(sizeof(void *) != 8 || sizeof(Symbol) == 56, + "Try to minimize Symbol's size; we create many instances"); + +// The Microsoft ABI doesn't support using parent class tail padding for child +// members, hence the _MSC_VER check. +#if !defined(_MSC_VER) +static_assert(sizeof(void *) != 8 || sizeof(Defined) == 88, + "Try to minimize Defined's size; we create many instances"); +#endif + +static_assert(sizeof(SymbolUnion) == sizeof(Defined), + "Defined should be the largest Symbol kind"); + +// Returns a symbol name for an error message. +static std::string maybeDemangleSymbol(StringRef symName) { + if (config->demangle) { + symName.consume_front("_"); + return demangle(symName.str()); + } + return symName.str(); } -std::string lld::toString(const Symbol &sym) { return demangle(sym.getName()); } +std::string lld::toString(const Symbol &sym) { + return maybeDemangleSymbol(sym.getName()); +} std::string lld::toMachOString(const object::Archive::Symbol &b) { - return demangle(b.getName()); + return maybeDemangleSymbol(b.getName()); } uint64_t Symbol::getStubVA() const { return in.stubs->getVA(stubsIndex); } +uint64_t Symbol::getLazyPtrVA() const { + return in.lazyPointers->getVA(stubsIndex); +} uint64_t Symbol::getGotVA() const { return in.got->getVA(gotIndex); } uint64_t Symbol::getTlvVA() const { return in.tlvPointers->getVA(gotIndex); } -bool Symbol::isLive() const { - if (isa(this) || isa(this)) - return used; - - if (auto *d = dyn_cast(this)) { - // Non-absolute symbols might be alive because their section is - // no_dead_strip or live_support. In that case, the section will know - // that it's live but `used` might be false. Non-absolute symbols always - // have to use the section's `live` bit as source of truth. - if (d->isAbsolute()) - return used; - return d->isec->canonical()->isLive(d->value); +Defined::Defined(StringRefZ name, InputFile *file, InputSection *isec, + uint64_t value, uint64_t size, bool isWeakDef, bool isExternal, + bool isPrivateExtern, bool includeInSymtab, bool isThumb, + bool isReferencedDynamically, bool noDeadStrip, + bool canOverrideWeakDef, bool isWeakDefCanBeHidden, + bool interposable) + : Symbol(DefinedKind, name, file), overridesWeakDef(canOverrideWeakDef), + privateExtern(isPrivateExtern), includeInSymtab(includeInSymtab), + wasIdenticalCodeFolded(false), thumb(isThumb), + referencedDynamically(isReferencedDynamically), noDeadStrip(noDeadStrip), + interposable(interposable), weakDefCanBeHidden(isWeakDefCanBeHidden), + weakDef(isWeakDef), external(isExternal), isec(isec), value(value), + size(size) { + if (isec) { + isec->symbols.push_back(this); + // Maintain sorted order. + for (auto it = isec->symbols.rbegin(), rend = isec->symbols.rend(); + it != rend; ++it) { + auto next = std::next(it); + if (next == rend) + break; + if ((*it)->value < (*next)->value) + std::swap(*next, *it); + else + break; + } } +} - assert(!isa(this) && - "replaceCommonSymbols() runs before dead code stripping, and isLive() " - "should only be called after dead code stripping"); - - // Assume any other kind of symbol is live. - return true; +bool Defined::isTlv() const { + return !isAbsolute() && isThreadLocalVariables(isec->getFlags()); } uint64_t Defined::getVA() const { @@ -59,7 +91,7 @@ uint64_t Defined::getVA() const { if (isAbsolute()) return value; - if (!isec->canonical()->isFinal) { + if (!isec->isFinal) { // A target arch that does not use thunks ought never ask for // the address of a function that has not yet been finalized. assert(target->usesThunks()); @@ -70,11 +102,28 @@ uint64_t Defined::getVA() const { // expedient to return a contrived out-of-range address. return TargetInfo::outOfRangeVA; } - return isec->canonical()->getVA(value); + return isec->getVA(value); +} + +ObjFile *Defined::getObjectFile() const { + return isec ? dyn_cast_or_null(isec->getFile()) : nullptr; +} + +void Defined::canonicalize() { + if (unwindEntry) + unwindEntry = unwindEntry->canonical(); + if (isec) + isec = isec->canonical(); +} + +std::string Defined::getSourceLocation() { + if (!isec) + return {}; + return isec->getSourceLocation(value); } uint64_t DylibSymbol::getVA() const { return isInStubs() ? getStubVA() : Symbol::getVA(); } -void LazySymbol::fetchArchiveMember() { getFile()->fetch(sym); } +void LazyArchive::fetchArchiveMember() { getFile()->fetch(sym); } diff --git a/gnu/llvm/lld/MachO/Symbols.h b/gnu/llvm/lld/MachO/Symbols.h index f7aac7c5cde..6113f2d7b0e 100644 --- a/gnu/llvm/lld/MachO/Symbols.h +++ b/gnu/llvm/lld/MachO/Symbols.h @@ -9,18 +9,16 @@ #ifndef LLD_MACHO_SYMBOLS_H #define LLD_MACHO_SYMBOLS_H +#include "Config.h" #include "InputFiles.h" -#include "InputSection.h" #include "Target.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Strings.h" + #include "llvm/Object/Archive.h" #include "llvm/Support/MathExtras.h" namespace lld { namespace macho { -class InputSection; class MachHeaderSection; struct StringRefZ { @@ -38,7 +36,9 @@ public: UndefinedKind, CommonKind, DylibKind, - LazyKind, + LazyArchiveKind, + LazyObjectKind, + AliasKind, }; virtual ~Symbol() {} @@ -51,7 +51,10 @@ public: return {nameData, nameSize}; } - bool isLive() const; + bool isLive() const { return used; } + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } virtual uint64_t getVA() const { return 0; } @@ -60,7 +63,7 @@ public: // Only undefined or dylib symbols can be weak references. A weak reference // need not be satisfied at runtime, e.g. due to the symbol not being // available on a given target platform. - virtual bool isWeakRef() const { llvm_unreachable("cannot be a weak ref"); } + virtual bool isWeakRef() const { return false; } virtual bool isTlv() const { llvm_unreachable("cannot be TLV"); } @@ -71,6 +74,7 @@ public: bool isInStubs() const { return stubsIndex != UINT32_MAX; } uint64_t getStubVA() const; + uint64_t getLazyPtrVA() const; uint64_t getGotVA() const; uint64_t getTlvVA() const; uint64_t resolveBranchVA() const { @@ -84,29 +88,29 @@ public: // on whether it is a thread-local. A given symbol cannot be referenced by // both these sections at once. uint32_t gotIndex = UINT32_MAX; - + uint32_t lazyBindOffset = UINT32_MAX; + uint32_t stubsHelperIndex = UINT32_MAX; uint32_t stubsIndex = UINT32_MAX; - uint32_t symtabIndex = UINT32_MAX; InputFile *getFile() const { return file; } protected: Symbol(Kind k, StringRefZ name, InputFile *file) - : symbolKind(k), nameData(name.data), nameSize(name.size), file(file), + : symbolKind(k), nameData(name.data), file(file), nameSize(name.size), isUsedInRegularObj(!file || isa(file)), used(!config->deadStrip) {} Kind symbolKind; const char *nameData; - mutable uint32_t nameSize; InputFile *file; + mutable uint32_t nameSize; public: // True if this symbol was referenced by a regular (non-bitcode) object. bool isUsedInRegularObj : 1; - // True if an undefined or dylib symbol is used from a live section. + // True if this symbol is used from a live section. bool used : 1; }; @@ -114,43 +118,42 @@ class Defined : public Symbol { public: Defined(StringRefZ name, InputFile *file, InputSection *isec, uint64_t value, uint64_t size, bool isWeakDef, bool isExternal, bool isPrivateExtern, - bool isThumb, bool isReferencedDynamically, bool noDeadStrip) - : Symbol(DefinedKind, name, file), isec(isec), value(value), size(size), - overridesWeakDef(false), privateExtern(isPrivateExtern), - includeInSymtab(true), thumb(isThumb), - referencedDynamically(isReferencedDynamically), - noDeadStrip(noDeadStrip), weakDef(isWeakDef), external(isExternal) { - if (auto concatIsec = dyn_cast_or_null(isec)) - concatIsec->numRefs++; - } + bool includeInSymtab, bool isThumb, bool isReferencedDynamically, + bool noDeadStrip, bool canOverrideWeakDef = false, + bool isWeakDefCanBeHidden = false, bool interposable = false); bool isWeakDef() const override { return weakDef; } bool isExternalWeakDef() const { return isWeakDef() && isExternal() && !privateExtern; } - bool isTlv() const override { - return !isAbsolute() && isThreadLocalVariables(isec->getFlags()); - } + bool isTlv() const override; bool isExternal() const { return external; } bool isAbsolute() const { return isec == nullptr; } uint64_t getVA() const override; - static bool classof(const Symbol *s) { return s->kind() == DefinedKind; } + // Returns the object file that this symbol was defined in. This value differs + // from `getFile()` if the symbol originated from a bitcode file. + ObjFile *getObjectFile() const; - InputSection *isec; - // Contains the offset from the containing subsection. Note that this is - // different from nlist::n_value, which is the absolute address of the symbol. - uint64_t value; - // size is only calculated for regular (non-bitcode) symbols. - uint64_t size; + std::string getSourceLocation(); + + // Ensure this symbol's pointers to InputSections point to their canonical + // copies. + void canonicalize(); + + static bool classof(const Symbol *s) { return s->kind() == DefinedKind; } + // Place the bitfields first so that they can get placed in the tail padding + // of the parent class, on platforms which support it. bool overridesWeakDef : 1; // Whether this symbol should appear in the output binary's export trie. bool privateExtern : 1; // Whether this symbol should appear in the output symbol table. bool includeInSymtab : 1; + // Whether this symbol was folded into a different symbol during ICF. + bool wasIdenticalCodeFolded : 1; // Only relevant when compiling for Thumb-supporting arm32 archs. bool thumb : 1; // Symbols marked referencedDynamically won't be removed from the output's @@ -165,10 +168,30 @@ public: // metadata. This is information only for the static linker and not written // to the output. bool noDeadStrip : 1; + // Whether references to this symbol can be interposed at runtime to point to + // a different symbol definition (with the same name). For example, if both + // dylib A and B define an interposable symbol _foo, and we load A before B at + // runtime, then all references to _foo within dylib B will point to the + // definition in dylib A. + // + // Only extern symbols may be interposable. + bool interposable : 1; + + bool weakDefCanBeHidden : 1; private: const bool weakDef : 1; const bool external : 1; + +public: + InputSection *isec; + // Contains the offset from the containing subsection. Note that this is + // different from nlist::n_value, which is the absolute address of the symbol. + uint64_t value; + // size is only calculated for regular (non-bitcode) symbols. + uint64_t size; + // This can be a subsection of either __compact_unwind or __eh_frame. + ConcatInputSection *unwindEntry = nullptr; }; // This enum does double-duty: as a symbol property, it indicates whether & how @@ -180,8 +203,10 @@ enum class RefState : uint8_t { Unreferenced = 0, Weak = 1, Strong = 2 }; class Undefined : public Symbol { public: - Undefined(StringRefZ name, InputFile *file, RefState refState) - : Symbol(UndefinedKind, name, file), refState(refState) { + Undefined(StringRefZ name, InputFile *file, RefState refState, + bool wasBitcodeSymbol) + : Symbol(UndefinedKind, name, file), refState(refState), + wasBitcodeSymbol(wasBitcodeSymbol) { assert(refState != RefState::Unreferenced); } @@ -190,6 +215,7 @@ public: static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; } RefState refState : 2; + bool wasBitcodeSymbol; }; // On Unix, it is traditionally allowed to write variable definitions without @@ -236,7 +262,12 @@ public: uint64_t getVA() const override; bool isWeakDef() const override { return weakDef; } - bool isWeakRef() const override { return refState == RefState::Weak; } + + // Symbols from weak libraries/frameworks are also weakly-referenced. + bool isWeakRef() const override { + return refState == RefState::Weak || + (file && getFile()->umbrella->forceWeakImport); + } bool isReferenced() const { return refState != RefState::Unreferenced; } bool isTlv() const override { return tlv; } bool isDynamicLookup() const { return file == nullptr; } @@ -249,9 +280,6 @@ public: static bool classof(const Symbol *s) { return s->kind() == DylibKind; } - uint32_t stubsHelperIndex = UINT32_MAX; - uint32_t lazyBindOffset = UINT32_MAX; - RefState getRefState() const { return refState; } void reference(RefState newState) { @@ -275,26 +303,60 @@ private: const bool tlv : 1; }; -class LazySymbol : public Symbol { +class LazyArchive : public Symbol { public: - LazySymbol(ArchiveFile *file, const llvm::object::Archive::Symbol &sym) - : Symbol(LazyKind, sym.getName(), file), sym(sym) {} + LazyArchive(ArchiveFile *file, const llvm::object::Archive::Symbol &sym) + : Symbol(LazyArchiveKind, sym.getName(), file), sym(sym) {} ArchiveFile *getFile() const { return cast(file); } void fetchArchiveMember(); - static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; } private: const llvm::object::Archive::Symbol sym; }; +// A defined symbol in an ObjFile/BitcodeFile surrounded by --start-lib and +// --end-lib. +class LazyObject : public Symbol { +public: + LazyObject(InputFile &file, StringRef name) + : Symbol(LazyObjectKind, name, &file) { + isUsedInRegularObj = false; + } + + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } +}; + +// Represents N_INDR symbols. Note that if we are given valid, linkable inputs, +// then all AliasSymbol instances will be converted into one of the other Symbol +// types after `createAliases()` runs. +class AliasSymbol final : public Symbol { +public: + AliasSymbol(InputFile *file, StringRef name, StringRef aliasedName, + bool isPrivateExtern) + : Symbol(AliasKind, name, file), privateExtern(isPrivateExtern), + aliasedName(aliasedName) {} + + StringRef getAliasedName() const { return aliasedName; } + + static bool classof(const Symbol *s) { return s->kind() == AliasKind; } + + const bool privateExtern; + +private: + StringRef aliasedName; +}; + union SymbolUnion { alignas(Defined) char a[sizeof(Defined)]; alignas(Undefined) char b[sizeof(Undefined)]; alignas(CommonSymbol) char c[sizeof(CommonSymbol)]; alignas(DylibSymbol) char d[sizeof(DylibSymbol)]; - alignas(LazySymbol) char e[sizeof(LazySymbol)]; + alignas(LazyArchive) char e[sizeof(LazyArchive)]; + alignas(LazyObject) char f[sizeof(LazyObject)]; + alignas(AliasSymbol) char g[sizeof(AliasSymbol)]; }; template @@ -313,6 +375,14 @@ T *replaceSymbol(Symbol *s, ArgT &&...arg) { return sym; } +// Can a symbol's address only be resolved at runtime? +inline bool needsBinding(const Symbol *sym) { + if (isa(sym)) + return true; + if (const auto *defined = dyn_cast(sym)) + return defined->isExternalWeakDef() || defined->interposable; + return false; +} } // namespace macho std::string toString(const macho::Symbol &); diff --git a/gnu/llvm/lld/MachO/SyntheticSections.cpp b/gnu/llvm/lld/MachO/SyntheticSections.cpp index f4934067727..267f97ac0ba 100644 --- a/gnu/llvm/lld/MachO/SyntheticSections.cpp +++ b/gnu/llvm/lld/MachO/SyntheticSections.cpp @@ -16,23 +16,30 @@ #include "SymbolTable.h" #include "Symbols.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" -#include "llvm/Support/SHA256.h" +#include "llvm/Support/xxhash.h" #if defined(__APPLE__) #include + +#define COMMON_DIGEST_FOR_OPENSSL +#include +#else +#include "llvm/Support/SHA256.h" #endif #ifdef LLVM_HAVE_LIBXAR #include +extern "C" { #include +} #endif using namespace llvm; @@ -42,13 +49,27 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::macho; +// Reads `len` bytes at data and writes the 32-byte SHA256 checksum to `output`. +static void sha256(const uint8_t *data, size_t len, uint8_t *output) { +#if defined(__APPLE__) + // FIXME: Make LLVM's SHA256 faster and use it unconditionally. See PR56121 + // for some notes on this. + CC_SHA256(data, len, output); +#else + ArrayRef block(data, len); + std::array hash = SHA256::hash(block); + static_assert(hash.size() == CodeSignatureSection::hashSize); + memcpy(output, hash.data(), hash.size()); +#endif +} + InStruct macho::in; std::vector macho::syntheticSections; SyntheticSection::SyntheticSection(const char *segname, const char *name) : OutputSection(SyntheticKind, name) { std::tie(this->segname, this->name) = maybeRenameSection({segname, name}); - isec = make(segname, name); + isec = makeSyntheticInputSection(segname, name); isec->parent = this; syntheticSections.push_back(this); } @@ -83,13 +104,23 @@ static uint32_t cpuSubtype() { if (config->outputType == MH_EXECUTE && !config->staticLink && target->cpuSubtype == CPU_SUBTYPE_X86_64_ALL && - config->platform() == PlatformKind::macOS && + config->platform() == PLATFORM_MACOS && config->platformInfo.minimum >= VersionTuple(10, 5)) subtype |= CPU_SUBTYPE_LIB64; return subtype; } +static bool hasWeakBinding() { + return config->emitChainedFixups ? in.chainedFixups->hasWeakBinding() + : in.weakBinding->hasEntry(); +} + +static bool hasNonWeakDefinition() { + return config->emitChainedFixups ? in.chainedFixups->hasNonWeakDefinition() + : in.weakBinding->hasNonWeakDefinition(); +} + void MachHeaderSection::writeTo(uint8_t *buf) const { auto *hdr = reinterpret_cast(buf); hdr->magic = target->magic; @@ -115,10 +146,10 @@ void MachHeaderSection::writeTo(uint8_t *buf) const { if (config->outputType == MH_DYLIB && config->applicationExtension) hdr->flags |= MH_APP_EXTENSION_SAFE; - if (in.exports->hasWeakSymbol || in.weakBinding->hasNonWeakDefinition()) + if (in.exports->hasWeakSymbol || hasNonWeakDefinition()) hdr->flags |= MH_WEAK_DEFINES; - if (in.exports->hasWeakSymbol || in.weakBinding->hasEntry()) + if (in.exports->hasWeakSymbol || hasWeakBinding()) hdr->flags |= MH_BINDS_TO_WEAK; for (const OutputSegment *seg : outputSegments) { @@ -144,52 +175,108 @@ RebaseSection::RebaseSection() : LinkEditSection(segment_names::linkEdit, section_names::rebase) {} namespace { -struct Rebase { - OutputSegment *segment = nullptr; - uint64_t offset = 0; - uint64_t consecutiveCount = 0; +struct RebaseState { + uint64_t sequenceLength; + uint64_t skipLength; }; } // namespace -// Rebase opcodes allow us to describe a contiguous sequence of rebase location -// using a single DO_REBASE opcode. To take advantage of it, we delay emitting -// `DO_REBASE` until we have reached the end of a contiguous sequence. -static void encodeDoRebase(Rebase &rebase, raw_svector_ostream &os) { - assert(rebase.consecutiveCount != 0); - if (rebase.consecutiveCount <= REBASE_IMMEDIATE_MASK) { - os << static_cast(REBASE_OPCODE_DO_REBASE_IMM_TIMES | - rebase.consecutiveCount); +static void emitIncrement(uint64_t incr, raw_svector_ostream &os) { + assert(incr != 0); + + if ((incr >> target->p2WordSize) <= REBASE_IMMEDIATE_MASK && + (incr % target->wordSize) == 0) { + os << static_cast(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | + (incr >> target->p2WordSize)); } else { - os << static_cast(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); - encodeULEB128(rebase.consecutiveCount, os); + os << static_cast(REBASE_OPCODE_ADD_ADDR_ULEB); + encodeULEB128(incr, os); } - rebase.consecutiveCount = 0; } -static void encodeRebase(const OutputSection *osec, uint64_t outSecOff, - Rebase &lastRebase, raw_svector_ostream &os) { - OutputSegment *seg = osec->parent; - uint64_t offset = osec->getSegmentOffset() + outSecOff; - if (lastRebase.segment != seg || lastRebase.offset != offset) { - if (lastRebase.consecutiveCount != 0) - encodeDoRebase(lastRebase, os); - - if (lastRebase.segment != seg) { - os << static_cast(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | - seg->index); - encodeULEB128(offset, os); - lastRebase.segment = seg; - lastRebase.offset = offset; +static void flushRebase(const RebaseState &state, raw_svector_ostream &os) { + assert(state.sequenceLength > 0); + + if (state.skipLength == target->wordSize) { + if (state.sequenceLength <= REBASE_IMMEDIATE_MASK) { + os << static_cast(REBASE_OPCODE_DO_REBASE_IMM_TIMES | + state.sequenceLength); } else { - assert(lastRebase.offset != offset); - os << static_cast(REBASE_OPCODE_ADD_ADDR_ULEB); - encodeULEB128(offset - lastRebase.offset, os); - lastRebase.offset = offset; + os << static_cast(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + encodeULEB128(state.sequenceLength, os); } + } else if (state.sequenceLength == 1) { + os << static_cast(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + encodeULEB128(state.skipLength - target->wordSize, os); + } else { + os << static_cast( + REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + encodeULEB128(state.sequenceLength, os); + encodeULEB128(state.skipLength - target->wordSize, os); } - ++lastRebase.consecutiveCount; - // DO_REBASE causes dyld to both perform the binding and increment the offset - lastRebase.offset += target->wordSize; +} + +// Rebases are communicated to dyld using a bytecode, whose opcodes cause the +// memory location at a specific address to be rebased and/or the address to be +// incremented. +// +// Opcode REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB is the most generic +// one, encoding a series of evenly spaced addresses. This algorithm works by +// splitting up the sorted list of addresses into such chunks. If the locations +// are consecutive or the sequence consists of a single location, flushRebase +// will use a smaller, more specialized encoding. +static void encodeRebases(const OutputSegment *seg, + MutableArrayRef locations, + raw_svector_ostream &os) { + // dyld operates on segments. Translate section offsets into segment offsets. + for (Location &loc : locations) + loc.offset = + loc.isec->parent->getSegmentOffset() + loc.isec->getOffset(loc.offset); + // The algorithm assumes that locations are unique. + Location *end = + llvm::unique(locations, [](const Location &a, const Location &b) { + return a.offset == b.offset; + }); + size_t count = end - locations.begin(); + + os << static_cast(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | + seg->index); + assert(!locations.empty()); + uint64_t offset = locations[0].offset; + encodeULEB128(offset, os); + + RebaseState state{1, target->wordSize}; + + for (size_t i = 1; i < count; ++i) { + offset = locations[i].offset; + + uint64_t skip = offset - locations[i - 1].offset; + assert(skip != 0 && "duplicate locations should have been weeded out"); + + if (skip == state.skipLength) { + ++state.sequenceLength; + } else if (state.sequenceLength == 1) { + ++state.sequenceLength; + state.skipLength = skip; + } else if (skip < state.skipLength) { + // The address is lower than what the rebase pointer would be if the last + // location would be part of a sequence. We start a new sequence from the + // previous location. + --state.sequenceLength; + flushRebase(state, os); + + state.sequenceLength = 2; + state.skipLength = skip; + } else { + // The address is at some positive offset from the rebase pointer. We + // start a new sequence which begins with the current location. + flushRebase(state, os); + emitIncrement(skip - state.skipLength, os); + state.sequenceLength = 1; + state.skipLength = target->wordSize; + } + } + flushRebase(state, os); } void RebaseSection::finalizeContents() { @@ -197,19 +284,20 @@ void RebaseSection::finalizeContents() { return; raw_svector_ostream os{contents}; - Rebase lastRebase; - os << static_cast(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER); llvm::sort(locations, [](const Location &a, const Location &b) { return a.isec->getVA(a.offset) < b.isec->getVA(b.offset); }); - for (const Location &loc : locations) - encodeRebase(loc.isec->parent, loc.isec->getOffset(loc.offset), lastRebase, - os); - if (lastRebase.consecutiveCount != 0) - encodeDoRebase(lastRebase, os); + for (size_t i = 0, count = locations.size(); i < count;) { + const OutputSegment *seg = locations[i].isec->parent->parent; + size_t j = i + 1; + while (j < count && locations[j].isec->parent->parent == seg) + ++j; + encodeRebases(seg, {locations.data() + i, locations.data() + j}, os); + i = j; + } os << static_cast(REBASE_OPCODE_DONE); } @@ -226,6 +314,16 @@ NonLazyPointerSectionBase::NonLazyPointerSectionBase(const char *segname, void macho::addNonLazyBindingEntries(const Symbol *sym, const InputSection *isec, uint64_t offset, int64_t addend) { + if (config->emitChainedFixups) { + if (needsBinding(sym)) + in.chainedFixups->addBinding(sym, isec, offset, addend); + else if (isa(sym)) + in.chainedFixups->addRebase(isec, offset); + else + llvm_unreachable("cannot bind to an undefined symbol"); + return; + } + if (const auto *dysym = dyn_cast(sym)) { in.binding->addEntry(dysym, isec, offset, addend); if (dysym->isWeakDef()) @@ -234,6 +332,8 @@ void macho::addNonLazyBindingEntries(const Symbol *sym, in.rebase->addEntry(isec, offset); if (defined->isExternalWeakDef()) in.weakBinding->addEntry(sym, isec, offset, addend); + else if (defined->interposable) + in.binding->addEntry(sym, isec, offset, addend); } else { // Undefined symbols are filtered out in scanRelocations(); we should never // get here @@ -250,14 +350,56 @@ void NonLazyPointerSectionBase::addEntry(Symbol *sym) { } } +void macho::writeChainedRebase(uint8_t *buf, uint64_t targetVA) { + assert(config->emitChainedFixups); + assert(target->wordSize == 8 && "Only 64-bit platforms are supported"); + auto *rebase = reinterpret_cast(buf); + rebase->target = targetVA & 0xf'ffff'ffff; + rebase->high8 = (targetVA >> 56); + rebase->reserved = 0; + rebase->next = 0; + rebase->bind = 0; + + // The fixup format places a 64 GiB limit on the output's size. + // Should we handle this gracefully? + uint64_t encodedVA = rebase->target | ((uint64_t)rebase->high8 << 56); + if (encodedVA != targetVA) + error("rebase target address 0x" + Twine::utohexstr(targetVA) + + " does not fit into chained fixup. Re-link with -no_fixup_chains"); +} + +static void writeChainedBind(uint8_t *buf, const Symbol *sym, int64_t addend) { + assert(config->emitChainedFixups); + assert(target->wordSize == 8 && "Only 64-bit platforms are supported"); + auto *bind = reinterpret_cast(buf); + auto [ordinal, inlineAddend] = in.chainedFixups->getBinding(sym, addend); + bind->ordinal = ordinal; + bind->addend = inlineAddend; + bind->reserved = 0; + bind->next = 0; + bind->bind = 1; +} + +void macho::writeChainedFixup(uint8_t *buf, const Symbol *sym, int64_t addend) { + if (needsBinding(sym)) + writeChainedBind(buf, sym, addend); + else + writeChainedRebase(buf, sym->getVA() + addend); +} + void NonLazyPointerSectionBase::writeTo(uint8_t *buf) const { - for (size_t i = 0, n = entries.size(); i < n; ++i) - if (auto *defined = dyn_cast(entries[i])) - write64le(&buf[i * target->wordSize], defined->getVA()); + if (config->emitChainedFixups) { + for (const auto &[i, entry] : llvm::enumerate(entries)) + writeChainedFixup(&buf[i * target->wordSize], entry, 0); + } else { + for (const auto &[i, entry] : llvm::enumerate(entries)) + if (auto *defined = dyn_cast(entry)) + write64le(&buf[i * target->wordSize], defined->getVA()); + } } GotSection::GotSection() - : NonLazyPointerSectionBase(segment_names::dataConst, section_names::got) { + : NonLazyPointerSectionBase(segment_names::data, section_names::got) { flags = S_NON_LAZY_SYMBOL_POINTERS; } @@ -416,6 +558,13 @@ static int16_t ordinalForDylibSymbol(const DylibSymbol &dysym) { return dysym.getFile()->ordinal; } +static int16_t ordinalForSymbol(const Symbol &sym) { + if (const auto *dysym = dyn_cast(&sym)) + return ordinalForDylibSymbol(*dysym); + assert(cast(&sym)->interposable); + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; +} + static void encodeDylibOrdinal(int16_t ordinal, raw_svector_ostream &os) { if (ordinal <= 0) { os << static_cast(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @@ -485,14 +634,14 @@ void BindingSection::finalizeContents() { int16_t lastOrdinal = 0; for (auto &p : sortBindings(bindingsMap)) { - const DylibSymbol *sym = p.first; + const Symbol *sym = p.first; std::vector &bindings = p.second; uint8_t flags = BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM; if (sym->isWeakRef()) flags |= BIND_SYMBOL_FLAGS_WEAK_IMPORT; os << flags << sym->getName() << '\0' << static_cast(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER); - int16_t ordinal = ordinalForDylibSymbol(*sym); + int16_t ordinal = ordinalForSymbol(*sym); if (ordinal != lastOrdinal) { encodeDylibOrdinal(ordinal, os); lastOrdinal = ordinal; @@ -565,18 +714,52 @@ uint64_t StubsSection::getSize() const { void StubsSection::writeTo(uint8_t *buf) const { size_t off = 0; for (const Symbol *sym : entries) { - target->writeStub(buf + off, *sym); + uint64_t pointerVA = + config->emitChainedFixups ? sym->getGotVA() : sym->getLazyPtrVA(); + target->writeStub(buf + off, *sym, pointerVA); off += target->stubSize; } } void StubsSection::finalize() { isFinal = true; } -bool StubsSection::addEntry(Symbol *sym) { +static void addBindingsForStub(Symbol *sym) { + assert(!config->emitChainedFixups); + if (auto *dysym = dyn_cast(sym)) { + if (sym->isWeakDef()) { + in.binding->addEntry(dysym, in.lazyPointers->isec, + sym->stubsIndex * target->wordSize); + in.weakBinding->addEntry(sym, in.lazyPointers->isec, + sym->stubsIndex * target->wordSize); + } else { + in.lazyBinding->addEntry(dysym); + } + } else if (auto *defined = dyn_cast(sym)) { + if (defined->isExternalWeakDef()) { + in.rebase->addEntry(in.lazyPointers->isec, + sym->stubsIndex * target->wordSize); + in.weakBinding->addEntry(sym, in.lazyPointers->isec, + sym->stubsIndex * target->wordSize); + } else if (defined->interposable) { + in.lazyBinding->addEntry(sym); + } else { + llvm_unreachable("invalid stub target"); + } + } else { + llvm_unreachable("invalid stub target symbol type"); + } +} + +void StubsSection::addEntry(Symbol *sym) { bool inserted = entries.insert(sym); - if (inserted) + if (inserted) { sym->stubsIndex = entries.size() - 1; - return inserted; + + if (config->emitChainedFixups) + in.got->addEntry(sym); + else + addBindingsForStub(sym); + } } StubHelperSection::StubHelperSection() @@ -595,13 +778,13 @@ bool StubHelperSection::isNeeded() const { return in.lazyBinding->isNeeded(); } void StubHelperSection::writeTo(uint8_t *buf) const { target->writeStubHelperHeader(buf); size_t off = target->stubHelperHeaderSize; - for (const DylibSymbol *sym : in.lazyBinding->getEntries()) { + for (const Symbol *sym : in.lazyBinding->getEntries()) { target->writeStubHelperEntry(buf + off, *sym, addr + off); off += target->stubHelperEntrySize; } } -void StubHelperSection::setup() { +void StubHelperSection::setUp() { Symbol *binder = symtab->addUndefined("dyld_stub_binder", /*file=*/nullptr, /*isWeakRef=*/false); if (auto *undefined = dyn_cast(binder)) @@ -619,14 +802,89 @@ void StubHelperSection::setup() { ConcatOutputSection::getOrCreateForInput(in.imageLoaderCache); inputSections.push_back(in.imageLoaderCache); // Since this isn't in the symbol table or in any input file, the noDeadStrip - // argument doesn't matter. It's kept alive by ImageLoaderCacheSection() - // setting `live` to true on the backing InputSection. + // argument doesn't matter. dyldPrivate = make("__dyld_private", nullptr, in.imageLoaderCache, 0, 0, /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false, + /*includeInSymtab=*/true, /*isThumb=*/false, /*isReferencedDynamically=*/false, /*noDeadStrip=*/false); + dyldPrivate->used = true; +} + +ObjCStubsSection::ObjCStubsSection() + : SyntheticSection(segment_names::text, section_names::objcStubs) { + flags = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + align = target->objcStubsAlignment; +} + +void ObjCStubsSection::addEntry(Symbol *sym) { + assert(sym->getName().startswith(symbolPrefix) && "not an objc stub"); + StringRef methname = sym->getName().drop_front(symbolPrefix.size()); + offsets.push_back( + in.objcMethnameSection->getStringOffset(methname).outSecOff); + Defined *newSym = replaceSymbol( + sym, sym->getName(), nullptr, isec, + /*value=*/symbols.size() * target->objcStubsFastSize, + /*size=*/target->objcStubsFastSize, + /*isWeakDef=*/false, /*isExternal=*/true, /*isPrivateExtern=*/true, + /*includeInSymtab=*/true, /*isThumb=*/false, + /*isReferencedDynamically=*/false, /*noDeadStrip=*/false); + symbols.push_back(newSym); +} + +void ObjCStubsSection::setUp() { + Symbol *objcMsgSend = symtab->addUndefined("_objc_msgSend", /*file=*/nullptr, + /*isWeakRef=*/false); + objcMsgSend->used = true; + in.got->addEntry(objcMsgSend); + assert(objcMsgSend->isInGot()); + objcMsgSendGotIndex = objcMsgSend->gotIndex; + + size_t size = offsets.size() * target->wordSize; + uint8_t *selrefsData = bAlloc().Allocate(size); + for (size_t i = 0, n = offsets.size(); i < n; ++i) + write64le(&selrefsData[i * target->wordSize], offsets[i]); + + in.objcSelrefs = + makeSyntheticInputSection(segment_names::data, section_names::objcSelrefs, + S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, + ArrayRef{selrefsData, size}, + /*align=*/target->wordSize); + in.objcSelrefs->live = true; + + for (size_t i = 0, n = offsets.size(); i < n; ++i) { + in.objcSelrefs->relocs.push_back( + {/*type=*/target->unsignedRelocType, + /*pcrel=*/false, /*length=*/3, + /*offset=*/static_cast(i * target->wordSize), + /*addend=*/offsets[i] * in.objcMethnameSection->align, + /*referent=*/in.objcMethnameSection->isec}); + } + + in.objcSelrefs->parent = + ConcatOutputSection::getOrCreateForInput(in.objcSelrefs); + inputSections.push_back(in.objcSelrefs); + in.objcSelrefs->isFinal = true; +} + +uint64_t ObjCStubsSection::getSize() const { + return target->objcStubsFastSize * symbols.size(); +} + +void ObjCStubsSection::writeTo(uint8_t *buf) const { + assert(in.objcSelrefs->live); + assert(in.objcSelrefs->isFinal); + + uint64_t stubOffset = 0; + for (size_t i = 0, n = symbols.size(); i < n; ++i) { + Defined *sym = symbols[i]; + target->writeObjCMsgSendStub(buf + stubOffset, sym, in.objcStubs->addr, + stubOffset, in.objcSelrefs->getVA(), i, + in.got->addr, objcMsgSendGotIndex); + stubOffset += target->objcStubsFastSize; + } } LazyPointerSection::LazyPointerSection() @@ -666,7 +924,7 @@ LazyBindingSection::LazyBindingSection() void LazyBindingSection::finalizeContents() { // TODO: Just precompute output size here instead of writing to a temporary // buffer - for (DylibSymbol *sym : entries) + for (Symbol *sym : entries) sym->lazyBindOffset = encode(*sym); } @@ -674,11 +932,12 @@ void LazyBindingSection::writeTo(uint8_t *buf) const { memcpy(buf, contents.data(), contents.size()); } -void LazyBindingSection::addEntry(DylibSymbol *dysym) { - if (entries.insert(dysym)) { - dysym->stubsHelperIndex = entries.size() - 1; +void LazyBindingSection::addEntry(Symbol *sym) { + assert(!config->emitChainedFixups && "Chained fixups always bind eagerly"); + if (entries.insert(sym)) { + sym->stubsHelperIndex = entries.size() - 1; in.rebase->addEntry(in.lazyPointers->isec, - dysym->stubsIndex * target->wordSize); + sym->stubsIndex * target->wordSize); } } @@ -688,15 +947,15 @@ void LazyBindingSection::addEntry(DylibSymbol *dysym) { // BIND_OPCODE_DONE terminator. As such, unlike in the non-lazy-binding case, // we cannot encode just the differences between symbols; we have to emit the // complete bind information for each symbol. -uint32_t LazyBindingSection::encode(const DylibSymbol &sym) { +uint32_t LazyBindingSection::encode(const Symbol &sym) { uint32_t opstreamOffset = contents.size(); OutputSegment *dataSeg = in.lazyPointers->parent; os << static_cast(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | dataSeg->index); - uint64_t offset = in.lazyPointers->addr - dataSeg->addr + - sym.stubsIndex * target->wordSize; + uint64_t offset = + in.lazyPointers->addr - dataSeg->addr + sym.stubsIndex * target->wordSize; encodeULEB128(offset, os); - encodeDylibOrdinal(ordinalForDylibSymbol(sym), os); + encodeDylibOrdinal(ordinalForSymbol(sym), os); uint8_t flags = BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM; if (sym.isWeakRef()) @@ -731,42 +990,36 @@ DataInCodeSection::DataInCodeSection() template static std::vector collectDataInCodeEntries() { - using SegmentCommand = typename LP::segment_command; - using Section = typename LP::section; - std::vector dataInCodeEntries; for (const InputFile *inputFile : inputFiles) { if (!isa(inputFile)) continue; const ObjFile *objFile = cast(inputFile); - const auto *c = reinterpret_cast( - findCommand(objFile->mb.getBufferStart(), LP::segmentLCType)); - if (!c) - continue; - ArrayRef
sections{reinterpret_cast(c + 1), - c->nsects}; - - ArrayRef entries = objFile->dataInCodeEntries; + ArrayRef entries = objFile->getDataInCode(); if (entries.empty()) continue; + + assert(is_sorted(entries, [](const data_in_code_entry &lhs, + const data_in_code_entry &rhs) { + return lhs.offset < rhs.offset; + })); // For each code subsection find 'data in code' entries residing in it. // Compute the new offset values as // + - <__TEXT address>. - for (size_t i = 0, n = sections.size(); i < n; ++i) { - const SubsectionMap &subsecMap = objFile->subsections[i]; - for (const SubsectionEntry &subsecEntry : subsecMap) { - const InputSection *isec = subsecEntry.isec; + for (const Section *section : objFile->sections) { + for (const Subsection &subsec : section->subsections) { + const InputSection *isec = subsec.isec; if (!isCodeSection(isec)) continue; if (cast(isec)->shouldOmitFromOutput()) continue; - const uint64_t beginAddr = sections[i].addr + subsecEntry.offset; + const uint64_t beginAddr = section->addr + subsec.offset; auto it = llvm::lower_bound( entries, beginAddr, [](const MachO::data_in_code_entry &entry, uint64_t addr) { return entry.offset < addr; }); - const uint64_t endAddr = beginAddr + isec->getFileSize(); + const uint64_t endAddr = beginAddr + isec->getSize(); for (const auto end = entries.end(); it != end && it->offset + it->length <= endAddr; ++it) dataInCodeEntries.push_back( @@ -776,6 +1029,12 @@ static std::vector collectDataInCodeEntries() { } } } + + // ld64 emits the table in sorted order too. + llvm::sort(dataInCodeEntries, + [](const data_in_code_entry &lhs, const data_in_code_entry &rhs) { + return lhs.offset < rhs.offset; + }); return dataInCodeEntries; } @@ -795,16 +1054,18 @@ FunctionStartsSection::FunctionStartsSection() void FunctionStartsSection::finalizeContents() { raw_svector_ostream os{contents}; std::vector addrs; - for (const Symbol *sym : symtab->getSymbols()) { - if (const auto *defined = dyn_cast(sym)) { - if (!defined->isec || !isCodeSection(defined->isec) || !defined->isLive()) - continue; - if (const auto *concatIsec = dyn_cast(defined->isec)) - if (concatIsec->shouldOmitFromOutput()) - continue; - // TODO: Add support for thumbs, in that case - // the lowest bit of nextAddr needs to be set to 1. - addrs.push_back(defined->getVA()); + for (const InputFile *file : inputFiles) { + if (auto *objFile = dyn_cast(file)) { + for (const Symbol *sym : objFile->symbols) { + if (const auto *defined = dyn_cast_or_null(sym)) { + if (!defined->isec || !isCodeSection(defined->isec) || + !defined->isLive()) + continue; + // TODO: Add support for thumbs, in that case + // the lowest bit of nextAddr needs to be set to 1. + addrs.push_back(defined->getVA()); + } + } } } llvm::sort(addrs); @@ -827,16 +1088,9 @@ SymtabSection::SymtabSection(StringTableSection &stringTableSection) : LinkEditSection(segment_names::linkEdit, section_names::symbolTable), stringTableSection(stringTableSection) {} -void SymtabSection::emitBeginSourceStab(DWARFUnit *compileUnit) { +void SymtabSection::emitBeginSourceStab(StringRef sourceFile) { StabsEntry stab(N_SO); - SmallString<261> dir(compileUnit->getCompilationDir()); - StringRef sep = sys::path::get_separator(); - // We don't use `path::append` here because we want an empty `dir` to result - // in an absolute path. `append` would give us a relative path for that case. - if (!dir.endswith(sep)) - dir += sep; - stab.strx = stringTableSection.addString( - saver.save(dir + compileUnit->getUnitDIE().getShortName())); + stab.strx = stringTableSection.addString(saver().save(sourceFile)); stabs.emplace_back(std::move(stab)); } @@ -858,7 +1112,10 @@ void SymtabSection::emitObjectFileStab(ObjFile *file) { if (!file->archiveName.empty()) path.append({"(", file->getName(), ")"}); - stab.strx = stringTableSection.addString(saver.save(path.str())); + StringRef adjustedPath = saver().save(path.str()); + adjustedPath.consume_front(config->osoPrefix); + + stab.strx = stringTableSection.addString(adjustedPath); stab.desc = 1; stab.value = file->modTime; stabs.emplace_back(std::move(stab)); @@ -871,38 +1128,54 @@ void SymtabSection::emitEndFunStab(Defined *defined) { } void SymtabSection::emitStabs() { + if (config->omitDebugInfo) + return; + for (const std::string &s : config->astPaths) { StabsEntry astStab(N_AST); astStab.strx = stringTableSection.addString(s); stabs.emplace_back(std::move(astStab)); } - std::vector symbolsNeedingStabs; + // Cache the file ID for each symbol in an std::pair for faster sorting. + using SortingPair = std::pair; + std::vector symbolsNeedingStabs; for (const SymtabEntry &entry : concat(localSymbols, externalSymbols)) { Symbol *sym = entry.sym; assert(sym->isLive() && "dead symbols should not be in localSymbols, externalSymbols"); if (auto *defined = dyn_cast(sym)) { + // Excluded symbols should have been filtered out in finalizeContents(). + assert(defined->includeInSymtab); + if (defined->isAbsolute()) continue; - InputSection *isec = defined->isec; - ObjFile *file = dyn_cast_or_null(isec->getFile()); + + // Constant-folded symbols go in the executable's symbol table, but don't + // get a stabs entry. + if (defined->wasIdenticalCodeFolded) + continue; + + ObjFile *file = defined->getObjectFile(); if (!file || !file->compileUnit) continue; - symbolsNeedingStabs.push_back(defined); + + symbolsNeedingStabs.emplace_back(defined, defined->isec->getFile()->id); } } - llvm::stable_sort(symbolsNeedingStabs, [&](Defined *a, Defined *b) { - return a->isec->getFile()->id < b->isec->getFile()->id; - }); + llvm::stable_sort(symbolsNeedingStabs, + [&](const SortingPair &a, const SortingPair &b) { + return a.second < b.second; + }); // Emit STABS symbols so that dsymutil and/or the debugger can map address // regions in the final binary to the source and object files from which they // originated. InputFile *lastFile = nullptr; - for (Defined *defined : symbolsNeedingStabs) { + for (SortingPair &pair : symbolsNeedingStabs) { + Defined *defined = pair.first; InputSection *isec = defined->isec; ObjFile *file = cast(isec->getFile()); @@ -911,12 +1184,12 @@ void SymtabSection::emitStabs() { emitEndSourceStab(); lastFile = file; - emitBeginSourceStab(file->compileUnit); + emitBeginSourceStab(file->sourceFile()); emitObjectFileStab(file); } StabsEntry symStab; - symStab.sect = defined->isec->canonical()->parent->index; + symStab.sect = defined->isec->parent->index; symStab.strx = stringTableSection.addString(defined->getName()); symStab.value = defined->getVA(); @@ -940,16 +1213,42 @@ void SymtabSection::finalizeContents() { symbols.push_back({sym, strx}); }; + std::function localSymbolsHandler; + switch (config->localSymbolsPresence) { + case SymtabPresence::All: + localSymbolsHandler = [&](Symbol *sym) { addSymbol(localSymbols, sym); }; + break; + case SymtabPresence::None: + localSymbolsHandler = [&](Symbol *) { /* Do nothing*/ }; + break; + case SymtabPresence::SelectivelyIncluded: + localSymbolsHandler = [&](Symbol *sym) { + if (config->localSymbolPatterns.match(sym->getName())) + addSymbol(localSymbols, sym); + }; + break; + case SymtabPresence::SelectivelyExcluded: + localSymbolsHandler = [&](Symbol *sym) { + if (!config->localSymbolPatterns.match(sym->getName())) + addSymbol(localSymbols, sym); + }; + break; + } + // Local symbols aren't in the SymbolTable, so we walk the list of object // files to gather them. - for (const InputFile *file : inputFiles) { - if (auto *objFile = dyn_cast(file)) { - for (Symbol *sym : objFile->symbols) { - if (auto *defined = dyn_cast_or_null(sym)) { - if (!defined->isExternal() && defined->isLive()) { - StringRef name = defined->getName(); - if (!name.startswith("l") && !name.startswith("L")) - addSymbol(localSymbols, sym); + // But if `-x` is set, then we don't need to. localSymbolsHandler() will do + // the right thing regardless, but this check is a perf optimization because + // iterating through all the input files and their symbols is expensive. + if (config->localSymbolsPresence != SymtabPresence::None) { + for (const InputFile *file : inputFiles) { + if (auto *objFile = dyn_cast(file)) { + for (Symbol *sym : objFile->symbols) { + if (auto *defined = dyn_cast_or_null(sym)) { + if (defined->isExternal() || !defined->isLive() || + !defined->includeInSymtab) + continue; + localSymbolsHandler(sym); } } } @@ -958,8 +1257,8 @@ void SymtabSection::finalizeContents() { // __dyld_private is a local symbol too. It's linker-created and doesn't // exist in any object file. - if (Defined *dyldPrivate = in.stubHelper->dyldPrivate) - addSymbol(localSymbols, dyldPrivate); + if (in.stubHelper && in.stubHelper->dyldPrivate) + localSymbolsHandler(in.stubHelper->dyldPrivate); for (Symbol *sym : symtab->getSymbols()) { if (!sym->isLive()) @@ -969,7 +1268,7 @@ void SymtabSection::finalizeContents() { continue; assert(defined->isExternal()); if (defined->privateExtern) - addSymbol(localSymbols, defined); + localSymbolsHandler(defined); else addSymbol(externalSymbols, defined); } else if (auto *dysym = dyn_cast(sym)) { @@ -1041,7 +1340,7 @@ template void SymtabSectionImpl::writeTo(uint8_t *buf) const { nList->n_value = defined->value; } else { nList->n_type = scope | N_SECT; - nList->n_sect = defined->isec->canonical()->parent->index; + nList->n_sect = defined->isec->parent->index; // For the N_SECT symbol type, n_value is the address of the symbol nList->n_value = defined->getVA(); } @@ -1081,8 +1380,12 @@ IndirectSymtabSection::IndirectSymtabSection() section_names::indirectSymbolTable) {} uint32_t IndirectSymtabSection::getNumSymbols() const { - return in.got->getEntries().size() + in.tlvPointers->getEntries().size() + - 2 * in.stubs->getEntries().size(); + uint32_t size = in.got->getEntries().size() + + in.tlvPointers->getEntries().size() + + in.stubs->getEntries().size(); + if (!config->emitChainedFixups) + size += in.stubs->getEntries().size(); + return size; } bool IndirectSymtabSection::isNeeded() const { @@ -1097,13 +1400,19 @@ void IndirectSymtabSection::finalizeContents() { in.tlvPointers->reserved1 = off; off += in.tlvPointers->getEntries().size(); in.stubs->reserved1 = off; - off += in.stubs->getEntries().size(); - in.lazyPointers->reserved1 = off; + if (in.lazyPointers) { + off += in.stubs->getEntries().size(); + in.lazyPointers->reserved1 = off; + } } static uint32_t indirectValue(const Symbol *sym) { - return sym->symtabIndex != UINT32_MAX ? sym->symtabIndex - : INDIRECT_SYMBOL_LOCAL; + if (sym->symtabIndex == UINT32_MAX) + return INDIRECT_SYMBOL_LOCAL; + if (auto *defined = dyn_cast(sym)) + if (defined->privateExtern) + return INDIRECT_SYMBOL_LOCAL; + return sym->symtabIndex; } void IndirectSymtabSection::writeTo(uint8_t *buf) const { @@ -1120,14 +1429,17 @@ void IndirectSymtabSection::writeTo(uint8_t *buf) const { write32le(buf + off * sizeof(uint32_t), indirectValue(sym)); ++off; } - // There is a 1:1 correspondence between stubs and LazyPointerSection - // entries. But giving __stubs and __la_symbol_ptr the same reserved1 - // (the offset into the indirect symbol table) so that they both refer - // to the same range of offsets confuses `strip`, so write the stubs - // symbol table offsets a second time. - for (const Symbol *sym : in.stubs->getEntries()) { - write32le(buf + off * sizeof(uint32_t), indirectValue(sym)); - ++off; + + if (in.lazyPointers) { + // There is a 1:1 correspondence between stubs and LazyPointerSection + // entries. But giving __stubs and __la_symbol_ptr the same reserved1 + // (the offset into the indirect symbol table) so that they both refer + // to the same range of offsets confuses `strip`, so write the stubs + // symbol table offsets a second time. + for (const Symbol *sym : in.stubs->getEntries()) { + write32le(buf + off * sizeof(uint32_t), indirectValue(sym)); + ++off; + } } } @@ -1149,8 +1461,8 @@ void StringTableSection::writeTo(uint8_t *buf) const { } } -static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0, ""); -static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0, ""); +static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0); +static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0); CodeSignatureSection::CodeSignatureSection() : LinkEditSection(segment_names::linkEdit, section_names::codeSignature) { @@ -1160,6 +1472,9 @@ CodeSignatureSection::CodeSignatureSection() size_t slashIndex = fileName.rfind("/"); if (slashIndex != std::string::npos) fileName = fileName.drop_front(slashIndex + 1); + + // NOTE: Any changes to these calculations should be repeated + // in llvm-objcopy's MachOLayoutBuilder::layoutTail. allHeadersSize = alignTo<16>(fixedHeadersSize + fileName.size() + 1); fileNamePad = allHeadersSize - fixedHeadersSize - fileName.size(); } @@ -1173,20 +1488,14 @@ uint64_t CodeSignatureSection::getRawSize() const { } void CodeSignatureSection::writeHashes(uint8_t *buf) const { - uint8_t *code = buf; - uint8_t *codeEnd = buf + fileOff; - uint8_t *hashes = codeEnd + allHeadersSize; - while (code < codeEnd) { - StringRef block(reinterpret_cast(code), - std::min(codeEnd - code, static_cast(blockSize))); - SHA256 hasher; - hasher.update(block); - StringRef hash = hasher.final(); - assert(hash.size() == hashSize); - memcpy(hashes, hash.data(), hashSize); - code += blockSize; - hashes += hashSize; - } + // NOTE: Changes to this functionality should be repeated in llvm-objcopy's + // MachOWriter::writeSignatureData. + uint8_t *hashes = buf + fileOff + allHeadersSize; + parallelFor(0, getBlockCount(), [&](size_t i) { + sha256(buf + i * blockSize, + std::min(static_cast(fileOff - i * blockSize), blockSize), + hashes + i * hashSize); + }); #if defined(__APPLE__) // This is macOS-specific work-around and makes no sense for any // other host OS. See https://openradar.appspot.com/FB8914231 @@ -1203,6 +1512,8 @@ void CodeSignatureSection::writeHashes(uint8_t *buf) const { } void CodeSignatureSection::writeTo(uint8_t *buf) const { + // NOTE: Changes to this functionality should be repeated in llvm-objcopy's + // MachOWriter::writeSignatureData. uint32_t signatureSize = static_cast(getSize()); auto *superBlob = reinterpret_cast(buf); write32be(&superBlob->magic, CSMAGIC_EMBEDDED_SIGNATURE); @@ -1268,7 +1579,10 @@ void BitcodeBundleSection::finalize() { using namespace llvm::sys::fs; CHECK_EC(createTemporaryFile("bitcode-bundle", "xar", xarPath)); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" xar_t xar(xar_open(xarPath.data(), O_RDWR)); +#pragma clang diagnostic pop if (!xar) fatal("failed to open XAR temporary file at " + xarPath); CHECK_EC(xar_opt_set(xar, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE)); @@ -1295,8 +1609,8 @@ void BitcodeBundleSection::writeTo(uint8_t *buf) const { remove(xarPath); } -CStringSection::CStringSection() - : SyntheticSection(segment_names::text, section_names::cString) { +CStringSection::CStringSection(const char *name) + : SyntheticSection(segment_names::text, name) { flags = S_CSTRING_LITERALS; } @@ -1309,11 +1623,11 @@ void CStringSection::addInput(CStringInputSection *isec) { void CStringSection::writeTo(uint8_t *buf) const { for (const CStringInputSection *isec : inputs) { - for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) { - if (!isec->pieces[i].live) + for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) { + if (!piece.live) continue; StringRef string = isec->getStringRef(i); - memcpy(buf + isec->pieces[i].outSecOff, string.data(), string.size()); + memcpy(buf + piece.outSecOff, string.data(), string.size()); } } } @@ -1321,71 +1635,117 @@ void CStringSection::writeTo(uint8_t *buf) const { void CStringSection::finalizeContents() { uint64_t offset = 0; for (CStringInputSection *isec : inputs) { - for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) { - if (!isec->pieces[i].live) + for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) { + if (!piece.live) continue; - uint32_t pieceAlign = MinAlign(isec->pieces[i].inSecOff, align); + // See comment above DeduplicatedCStringSection for how alignment is + // handled. + uint32_t pieceAlign = 1 + << countTrailingZeros(isec->align | piece.inSecOff); offset = alignTo(offset, pieceAlign); - isec->pieces[i].outSecOff = offset; + piece.outSecOff = offset; isec->isFinal = true; StringRef string = isec->getStringRef(i); - offset += string.size(); + offset += string.size() + 1; // account for null terminator } } size = offset; } + // Mergeable cstring literals are found under the __TEXT,__cstring section. In // contrast to ELF, which puts strings that need different alignments into // different sections, clang's Mach-O backend puts them all in one section. // Strings that need to be aligned have the .p2align directive emitted before -// them, which simply translates into zero padding in the object file. +// them, which simply translates into zero padding in the object file. In other +// words, we have to infer the desired alignment of these cstrings from their +// addresses. // -// I *think* ld64 extracts the desired per-string alignment from this data by -// preserving each string's offset from the last section-aligned address. I'm -// not entirely certain since it doesn't seem consistent about doing this, and -// in fact doesn't seem to be correct in general: we can in fact can induce ld64 -// to produce a crashing binary just by linking in an additional object file -// that only contains a duplicate cstring at a different alignment. See PR50563 -// for details. +// We differ slightly from ld64 in how we've chosen to align these cstrings. +// Both LLD and ld64 preserve the number of trailing zeros in each cstring's +// address in the input object files. When deduplicating identical cstrings, +// both linkers pick the cstring whose address has more trailing zeros, and +// preserve the alignment of that address in the final binary. However, ld64 +// goes a step further and also preserves the offset of the cstring from the +// last section-aligned address. I.e. if a cstring is at offset 18 in the +// input, with a section alignment of 16, then both LLD and ld64 will ensure the +// final address is 2-byte aligned (since 18 == 16 + 2). But ld64 will also +// ensure that the final address is of the form 16 * k + 2 for some k. // -// On x86_64, the cstrings we've seen so far that require special alignment are -// all accessed by SIMD operations -- x86_64 requires SIMD accesses to be -// 16-byte-aligned. arm64 also seems to require 16-byte-alignment in some cases -// (PR50791), but I haven't tracked down the root cause. So for now, I'm just -// aligning all strings to 16 bytes. This is indeed wasteful, but -// implementation-wise it's simpler than preserving per-string -// alignment+offsets. It also avoids the aforementioned crash after -// deduplication of differently-aligned strings. Finally, the overhead is not -// huge: using 16-byte alignment (vs no alignment) is only a 0.5% size overhead -// when linking chromium_framework on x86_64. -DeduplicatedCStringSection::DeduplicatedCStringSection() - : builder(StringTableBuilder::RAW, /*Alignment=*/16) {} - +// Note that ld64's heuristic means that a dedup'ed cstring's final address is +// dependent on the order of the input object files. E.g. if in addition to the +// cstring at offset 18 above, we have a duplicate one in another file with a +// `.cstring` section alignment of 2 and an offset of zero, then ld64 will pick +// the cstring from the object file earlier on the command line (since both have +// the same number of trailing zeros in their address). So the final cstring may +// either be at some address `16 * k + 2` or at some address `2 * k`. +// +// I've opted not to follow this behavior primarily for implementation +// simplicity, and secondarily to save a few more bytes. It's not clear to me +// that preserving the section alignment + offset is ever necessary, and there +// are many cases that are clearly redundant. In particular, if an x86_64 object +// file contains some strings that are accessed via SIMD instructions, then the +// .cstring section in the object file will be 16-byte-aligned (since SIMD +// requires its operand addresses to be 16-byte aligned). However, there will +// typically also be other cstrings in the same file that aren't used via SIMD +// and don't need this alignment. They will be emitted at some arbitrary address +// `A`, but ld64 will treat them as being 16-byte aligned with an offset of `16 +// % A`. void DeduplicatedCStringSection::finalizeContents() { - // Add all string pieces to the string table builder to create section - // contents. - for (const CStringInputSection *isec : inputs) - for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) - if (isec->pieces[i].live) - builder.add(isec->getCachedHashStringRef(i)); - - // Fix the string table content. After this, the contents will never change. - builder.finalizeInOrder(); - - // finalize() fixed tail-optimized strings, so we can now get - // offsets of strings. Get an offset for each string and save it - // to a corresponding SectionPiece for easy access. + // Find the largest alignment required for each string. + for (const CStringInputSection *isec : inputs) { + for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) { + if (!piece.live) + continue; + auto s = isec->getCachedHashStringRef(i); + assert(isec->align != 0); + uint8_t trailingZeros = countTrailingZeros(isec->align | piece.inSecOff); + auto it = stringOffsetMap.insert( + std::make_pair(s, StringOffset(trailingZeros))); + if (!it.second && it.first->second.trailingZeros < trailingZeros) + it.first->second.trailingZeros = trailingZeros; + } + } + + // Assign an offset for each string and save it to the corresponding + // StringPieces for easy access. for (CStringInputSection *isec : inputs) { - for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) { - if (!isec->pieces[i].live) + for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) { + if (!piece.live) continue; - isec->pieces[i].outSecOff = - builder.getOffset(isec->getCachedHashStringRef(i)); - isec->isFinal = true; + auto s = isec->getCachedHashStringRef(i); + auto it = stringOffsetMap.find(s); + assert(it != stringOffsetMap.end()); + StringOffset &offsetInfo = it->second; + if (offsetInfo.outSecOff == UINT64_MAX) { + offsetInfo.outSecOff = alignTo(size, 1ULL << offsetInfo.trailingZeros); + size = + offsetInfo.outSecOff + s.size() + 1; // account for null terminator + } + piece.outSecOff = offsetInfo.outSecOff; } + isec->isFinal = true; + } +} + +void DeduplicatedCStringSection::writeTo(uint8_t *buf) const { + for (const auto &p : stringOffsetMap) { + StringRef data = p.first.val(); + uint64_t off = p.second.outSecOff; + if (!data.empty()) + memcpy(buf + off, data.data(), data.size()); } } +DeduplicatedCStringSection::StringOffset +DeduplicatedCStringSection::getStringOffset(StringRef str) const { + // StringPiece uses 31 bits to store the hashes, so we replicate that + uint32_t hash = xxHash64(str) & 0x7fffffff; + auto offset = stringOffsetMap.find(CachedHashStringRef(str, hash)); + assert(offset != stringOffsetMap.end() && + "Looked-up strings should always exist in section"); + return offset->second; +} + // This section is actually emitted as __TEXT,__const by ld64, but clang may // emit input sections of that name, and LLD doesn't currently support mixing // synthetic and concat-type OutputSections. To work around this, I've given @@ -1456,10 +1816,152 @@ void WordLiteralSection::writeTo(uint8_t *buf) const { memcpy(buf + p.second * 4, &p.first, 4); } +ObjCImageInfoSection::ObjCImageInfoSection() + : SyntheticSection(segment_names::data, section_names::objCImageInfo) {} + +ObjCImageInfoSection::ImageInfo +ObjCImageInfoSection::parseImageInfo(const InputFile *file) { + ImageInfo info; + ArrayRef data = file->objCImageInfo; + // The image info struct has the following layout: + // struct { + // uint32_t version; + // uint32_t flags; + // }; + if (data.size() < 8) { + warn(toString(file) + ": invalid __objc_imageinfo size"); + return info; + } + + auto *buf = reinterpret_cast(data.data()); + if (read32le(buf) != 0) { + warn(toString(file) + ": invalid __objc_imageinfo version"); + return info; + } + + uint32_t flags = read32le(buf + 1); + info.swiftVersion = (flags >> 8) & 0xff; + info.hasCategoryClassProperties = flags & 0x40; + return info; +} + +static std::string swiftVersionString(uint8_t version) { + switch (version) { + case 1: + return "1.0"; + case 2: + return "1.1"; + case 3: + return "2.0"; + case 4: + return "3.0"; + case 5: + return "4.0"; + default: + return ("0x" + Twine::utohexstr(version)).str(); + } +} + +// Validate each object file's __objc_imageinfo and use them to generate the +// image info for the output binary. Only two pieces of info are relevant: +// 1. The Swift version (should be identical across inputs) +// 2. `bool hasCategoryClassProperties` (true only if true for all inputs) +void ObjCImageInfoSection::finalizeContents() { + assert(files.size() != 0); // should have already been checked via isNeeded() + + info.hasCategoryClassProperties = true; + const InputFile *firstFile; + for (auto file : files) { + ImageInfo inputInfo = parseImageInfo(file); + info.hasCategoryClassProperties &= inputInfo.hasCategoryClassProperties; + + // swiftVersion 0 means no Swift is present, so no version checking required + if (inputInfo.swiftVersion == 0) + continue; + + if (info.swiftVersion != 0 && info.swiftVersion != inputInfo.swiftVersion) { + error("Swift version mismatch: " + toString(firstFile) + " has version " + + swiftVersionString(info.swiftVersion) + " but " + toString(file) + + " has version " + swiftVersionString(inputInfo.swiftVersion)); + } else { + info.swiftVersion = inputInfo.swiftVersion; + firstFile = file; + } + } +} + +void ObjCImageInfoSection::writeTo(uint8_t *buf) const { + uint32_t flags = info.hasCategoryClassProperties ? 0x40 : 0x0; + flags |= info.swiftVersion << 8; + write32le(buf + 4, flags); +} + +InitOffsetsSection::InitOffsetsSection() + : SyntheticSection(segment_names::text, section_names::initOffsets) { + flags = S_INIT_FUNC_OFFSETS; + align = 4; // This section contains 32-bit integers. +} + +uint64_t InitOffsetsSection::getSize() const { + size_t count = 0; + for (const ConcatInputSection *isec : sections) + count += isec->relocs.size(); + return count * sizeof(uint32_t); +} + +void InitOffsetsSection::writeTo(uint8_t *buf) const { + // FIXME: Add function specified by -init when that argument is implemented. + for (ConcatInputSection *isec : sections) { + for (const Reloc &rel : isec->relocs) { + const Symbol *referent = rel.referent.dyn_cast(); + assert(referent && "section relocation should have been rejected"); + uint64_t offset = referent->getVA() - in.header->addr; + // FIXME: Can we handle this gracefully? + if (offset > UINT32_MAX) + fatal(isec->getLocation(rel.offset) + ": offset to initializer " + + referent->getName() + " (" + utohexstr(offset) + + ") does not fit in 32 bits"); + + // Entries need to be added in the order they appear in the section, but + // relocations aren't guaranteed to be sorted. + size_t index = rel.offset >> target->p2WordSize; + write32le(&buf[index * sizeof(uint32_t)], offset); + } + buf += isec->relocs.size() * sizeof(uint32_t); + } +} + +// The inputs are __mod_init_func sections, which contain pointers to +// initializer functions, therefore all relocations should be of the UNSIGNED +// type. InitOffsetsSection stores offsets, so if the initializer's address is +// not known at link time, stub-indirection has to be used. +void InitOffsetsSection::setUp() { + for (const ConcatInputSection *isec : sections) { + for (const Reloc &rel : isec->relocs) { + RelocAttrs attrs = target->getRelocAttrs(rel.type); + if (!attrs.hasAttr(RelocAttrBits::UNSIGNED)) + error(isec->getLocation(rel.offset) + + ": unsupported relocation type: " + attrs.name); + if (rel.addend != 0) + error(isec->getLocation(rel.offset) + + ": relocation addend is not representable in __init_offsets"); + if (rel.referent.is()) + error(isec->getLocation(rel.offset) + + ": unexpected section relocation"); + + Symbol *sym = rel.referent.dyn_cast(); + if (auto *undefined = dyn_cast(sym)) + treatUndefinedSymbol(*undefined, isec, rel.offset); + if (needsBinding(sym)) + in.stubs->addEntry(sym); + } + } +} + void macho::createSyntheticSymbols() { auto addHeaderSymbol = [](const char *name) { symtab->addSynthetic(name, in.header->isec, /*value=*/0, - /*privateExtern=*/true, /*includeInSymtab=*/false, + /*isPrivateExtern=*/true, /*includeInSymtab=*/false, /*referencedDynamically=*/false); }; @@ -1472,11 +1974,11 @@ void macho::createSyntheticSymbols() { // Otherwise, it's an absolute symbol. if (config->isPic) symtab->addSynthetic("__mh_execute_header", in.header->isec, /*value=*/0, - /*privateExtern=*/false, /*includeInSymtab=*/true, + /*isPrivateExtern=*/false, /*includeInSymtab=*/true, /*referencedDynamically=*/true); else symtab->addSynthetic("__mh_execute_header", /*isec=*/nullptr, /*value=*/0, - /*privateExtern=*/false, /*includeInSymtab=*/true, + /*isPrivateExtern=*/false, /*includeInSymtab=*/true, /*referencedDynamically=*/true); break; @@ -1508,5 +2010,247 @@ void macho::createSyntheticSymbols() { addHeaderSymbol("___dso_handle"); } +ChainedFixupsSection::ChainedFixupsSection() + : LinkEditSection(segment_names::linkEdit, section_names::chainFixups) {} + +bool ChainedFixupsSection::isNeeded() const { + assert(config->emitChainedFixups); + // dyld always expects LC_DYLD_CHAINED_FIXUPS to point to a valid + // dyld_chained_fixups_header, so we create this section even if there aren't + // any fixups. + return true; +} + +static bool needsWeakBind(const Symbol &sym) { + if (auto *dysym = dyn_cast(&sym)) + return dysym->isWeakDef(); + if (auto *defined = dyn_cast(&sym)) + return defined->isExternalWeakDef(); + return false; +} + +void ChainedFixupsSection::addBinding(const Symbol *sym, + const InputSection *isec, uint64_t offset, + int64_t addend) { + locations.emplace_back(isec, offset); + int64_t outlineAddend = (addend < 0 || addend > 0xFF) ? addend : 0; + auto [it, inserted] = bindings.insert( + {{sym, outlineAddend}, static_cast(bindings.size())}); + + if (inserted) { + symtabSize += sym->getName().size() + 1; + hasWeakBind = hasWeakBind || needsWeakBind(*sym); + if (!isInt<23>(outlineAddend)) + needsLargeAddend = true; + else if (outlineAddend != 0) + needsAddend = true; + } +} + +std::pair +ChainedFixupsSection::getBinding(const Symbol *sym, int64_t addend) const { + int64_t outlineAddend = (addend < 0 || addend > 0xFF) ? addend : 0; + auto it = bindings.find({sym, outlineAddend}); + assert(it != bindings.end() && "binding not found in the imports table"); + if (outlineAddend == 0) + return {it->second, addend}; + return {it->second, 0}; +} + +static size_t writeImport(uint8_t *buf, int format, uint32_t libOrdinal, + bool weakRef, uint32_t nameOffset, int64_t addend) { + switch (format) { + case DYLD_CHAINED_IMPORT: { + auto *import = reinterpret_cast(buf); + import->lib_ordinal = libOrdinal; + import->weak_import = weakRef; + import->name_offset = nameOffset; + return sizeof(dyld_chained_import); + } + case DYLD_CHAINED_IMPORT_ADDEND: { + auto *import = reinterpret_cast(buf); + import->lib_ordinal = libOrdinal; + import->weak_import = weakRef; + import->name_offset = nameOffset; + import->addend = addend; + return sizeof(dyld_chained_import_addend); + } + case DYLD_CHAINED_IMPORT_ADDEND64: { + auto *import = reinterpret_cast(buf); + import->lib_ordinal = libOrdinal; + import->weak_import = weakRef; + import->name_offset = nameOffset; + import->addend = addend; + return sizeof(dyld_chained_import_addend64); + } + default: + llvm_unreachable("Unknown import format"); + } +} + +size_t ChainedFixupsSection::SegmentInfo::getSize() const { + assert(pageStarts.size() > 0 && "SegmentInfo for segment with no fixups?"); + return alignTo<8>(sizeof(dyld_chained_starts_in_segment) + + pageStarts.back().first * sizeof(uint16_t)); +} + +size_t ChainedFixupsSection::SegmentInfo::writeTo(uint8_t *buf) const { + auto *segInfo = reinterpret_cast(buf); + segInfo->size = getSize(); + segInfo->page_size = target->getPageSize(); + // FIXME: Use DYLD_CHAINED_PTR_64_OFFSET on newer OS versions. + segInfo->pointer_format = DYLD_CHAINED_PTR_64; + segInfo->segment_offset = oseg->addr - in.header->addr; + segInfo->max_valid_pointer = 0; // not used on 64-bit + segInfo->page_count = pageStarts.back().first + 1; + + uint16_t *starts = segInfo->page_start; + for (size_t i = 0; i < segInfo->page_count; ++i) + starts[i] = DYLD_CHAINED_PTR_START_NONE; + + for (auto [pageIdx, startAddr] : pageStarts) + starts[pageIdx] = startAddr; + return segInfo->size; +} + +static size_t importEntrySize(int format) { + switch (format) { + case DYLD_CHAINED_IMPORT: + return sizeof(dyld_chained_import); + case DYLD_CHAINED_IMPORT_ADDEND: + return sizeof(dyld_chained_import_addend); + case DYLD_CHAINED_IMPORT_ADDEND64: + return sizeof(dyld_chained_import_addend64); + default: + llvm_unreachable("Unknown import format"); + } +} + +// This is step 3 of the algorithm described in the class comment of +// ChainedFixupsSection. +// +// LC_DYLD_CHAINED_FIXUPS data consists of (in this order): +// * A dyld_chained_fixups_header +// * A dyld_chained_starts_in_image +// * One dyld_chained_starts_in_segment per segment +// * List of all imports (dyld_chained_import, dyld_chained_import_addend, or +// dyld_chained_import_addend64) +// * Names of imported symbols +void ChainedFixupsSection::writeTo(uint8_t *buf) const { + auto *header = reinterpret_cast(buf); + header->fixups_version = 0; + header->imports_count = bindings.size(); + header->imports_format = importFormat; + header->symbols_format = 0; + + buf += alignTo<8>(sizeof(*header)); + + auto curOffset = [&buf, &header]() -> uint32_t { + return buf - reinterpret_cast(header); + }; + + header->starts_offset = curOffset(); + + auto *imageInfo = reinterpret_cast(buf); + imageInfo->seg_count = outputSegments.size(); + uint32_t *segStarts = imageInfo->seg_info_offset; + + // dyld_chained_starts_in_image ends in a flexible array member containing an + // uint32_t for each segment. Leave room for it, and fill it via segStarts. + buf += alignTo<8>(offsetof(dyld_chained_starts_in_image, seg_info_offset) + + outputSegments.size() * sizeof(uint32_t)); + + // Initialize all offsets to 0, which indicates that the segment does not have + // fixups. Those that do have them will be filled in below. + for (size_t i = 0; i < outputSegments.size(); ++i) + segStarts[i] = 0; + + for (const SegmentInfo &seg : fixupSegments) { + segStarts[seg.oseg->index] = curOffset() - header->starts_offset; + buf += seg.writeTo(buf); + } + + // Write imports table. + header->imports_offset = curOffset(); + uint64_t nameOffset = 0; + for (auto [import, idx] : bindings) { + const Symbol &sym = *import.first; + int16_t libOrdinal = needsWeakBind(sym) + ? (int64_t)BIND_SPECIAL_DYLIB_WEAK_LOOKUP + : ordinalForSymbol(sym); + buf += writeImport(buf, importFormat, libOrdinal, sym.isWeakRef(), + nameOffset, import.second); + nameOffset += sym.getName().size() + 1; + } + + // Write imported symbol names. + header->symbols_offset = curOffset(); + for (auto [import, idx] : bindings) { + StringRef name = import.first->getName(); + memcpy(buf, name.data(), name.size()); + buf += name.size() + 1; // account for null terminator + } + + assert(curOffset() == getRawSize()); +} + +// This is step 2 of the algorithm described in the class comment of +// ChainedFixupsSection. +void ChainedFixupsSection::finalizeContents() { + assert(target->wordSize == 8 && "Only 64-bit platforms are supported"); + assert(config->emitChainedFixups); + + if (!isUInt<32>(symtabSize)) + error("cannot encode chained fixups: imported symbols table size " + + Twine(symtabSize) + " exceeds 4 GiB"); + + if (needsLargeAddend || !isUInt<23>(symtabSize)) + importFormat = DYLD_CHAINED_IMPORT_ADDEND64; + else if (needsAddend) + importFormat = DYLD_CHAINED_IMPORT_ADDEND; + else + importFormat = DYLD_CHAINED_IMPORT; + + for (Location &loc : locations) + loc.offset = + loc.isec->parent->getSegmentOffset() + loc.isec->getOffset(loc.offset); + + llvm::sort(locations, [](const Location &a, const Location &b) { + const OutputSegment *segA = a.isec->parent->parent; + const OutputSegment *segB = b.isec->parent->parent; + if (segA == segB) + return a.offset < b.offset; + return segA->addr < segB->addr; + }); + + auto sameSegment = [](const Location &a, const Location &b) { + return a.isec->parent->parent == b.isec->parent->parent; + }; + + const uint64_t pageSize = target->getPageSize(); + for (size_t i = 0, count = locations.size(); i < count;) { + const Location &firstLoc = locations[i]; + fixupSegments.emplace_back(firstLoc.isec->parent->parent); + while (i < count && sameSegment(locations[i], firstLoc)) { + uint32_t pageIdx = locations[i].offset / pageSize; + fixupSegments.back().pageStarts.emplace_back( + pageIdx, locations[i].offset % pageSize); + ++i; + while (i < count && sameSegment(locations[i], firstLoc) && + locations[i].offset / pageSize == pageIdx) + ++i; + } + } + + // Compute expected encoded size. + size = alignTo<8>(sizeof(dyld_chained_fixups_header)); + size += alignTo<8>(offsetof(dyld_chained_starts_in_image, seg_info_offset) + + outputSegments.size() * sizeof(uint32_t)); + for (const SegmentInfo &seg : fixupSegments) + size += seg.getSize(); + size += importEntrySize(importFormat) * bindings.size(); + size += symtabSize; +} + template SymtabSection *macho::makeSymtabSection(StringTableSection &); template SymtabSection *macho::makeSymtabSection(StringTableSection &); diff --git a/gnu/llvm/lld/MachO/SyntheticSections.h b/gnu/llvm/lld/MachO/SyntheticSections.h index bbb7adc37cb..b17e991c414 100644 --- a/gnu/llvm/lld/MachO/SyntheticSections.h +++ b/gnu/llvm/lld/MachO/SyntheticSections.h @@ -20,7 +20,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SetVector.h" -#include "llvm/MC/StringTableBuilder.h" +#include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" @@ -30,8 +30,7 @@ namespace llvm { class DWARFUnit; } // namespace llvm -namespace lld { -namespace macho { +namespace lld::macho { class Defined; class DylibSymbol; @@ -62,12 +61,14 @@ public: align = target->wordSize; } + // Implementations of this method can assume that the regular (non-__LINKEDIT) + // sections already have their addresses assigned. virtual void finalizeContents() {} // Sections in __LINKEDIT are special: their offsets are recorded in the // load commands like LC_DYLD_INFO_ONLY and LC_SYMTAB, instead of in section // headers. - bool isHidden() const override final { return true; } + bool isHidden() const final { return true; } virtual uint64_t getRawSize() const = 0; @@ -77,9 +78,7 @@ public: // // NOTE: This assumes that the extra bytes required for alignment can be // zero-valued bytes. - uint64_t getSize() const override final { - return llvm::alignTo(getRawSize(), align); - } + uint64_t getSize() const final { return llvm::alignTo(getRawSize(), align); } }; // The header of the Mach-O file, which must have a file offset of zero. @@ -103,6 +102,7 @@ class PageZeroSection final : public SyntheticSection { public: PageZeroSection(); bool isHidden() const override { return true; } + bool isNeeded() const override { return target->pageZeroSize != 0; } uint64_t getSize() const override { return target->pageZeroSize; } uint64_t getFileSize() const override { return 0; } void writeTo(uint8_t *buf) const override {} @@ -189,13 +189,13 @@ public: bool isNeeded() const override { return !bindingsMap.empty(); } void writeTo(uint8_t *buf) const override; - void addEntry(const DylibSymbol *dysym, const InputSection *isec, - uint64_t offset, int64_t addend = 0) { + void addEntry(const Symbol *dysym, const InputSection *isec, uint64_t offset, + int64_t addend = 0) { bindingsMap[dysym].emplace_back(addend, Location(isec, offset)); } private: - BindingsMap bindingsMap; + BindingsMap bindingsMap; SmallVector contents; }; @@ -269,6 +269,12 @@ private: // order that the weak bindings may overwrite the non-lazy bindings if an // appropriate symbol is found at runtime. However, the bound addresses will // still be written (non-lazily) into the LazyPointerSection. +// +// Symbols are always bound eagerly when chained fixups are used. In that case, +// StubsSection contains indirect jumps to addresses stored in the GotSection. +// The GOT directly contains the fixup entries, which will be replaced by the +// address of the target symbols on load. LazyPointerSection and +// StubHelperSection are not used. class StubsSection final : public SyntheticSection { public: @@ -278,9 +284,9 @@ public: void finalize() override; void writeTo(uint8_t *buf) const override; const llvm::SetVector &getEntries() const { return entries; } - // Returns whether the symbol was added. Note that every stubs entry will - // have a corresponding entry in the LazyPointerSection. - bool addEntry(Symbol *); + // Creates a stub for the symbol and the corresponding entry in the + // LazyPointerSection. + void addEntry(Symbol *); uint64_t getVA(uint32_t stubsIndex) const { assert(isFinal || target->usesThunks()); // ConcatOutputSection::finalize() can seek the address of a @@ -303,12 +309,36 @@ public: bool isNeeded() const override; void writeTo(uint8_t *buf) const override; - void setup(); + void setUp(); DylibSymbol *stubBinder = nullptr; Defined *dyldPrivate = nullptr; }; +// Objective-C stubs are hoisted objc_msgSend calls per selector called in the +// program. Apple Clang produces undefined symbols to each stub, such as +// '_objc_msgSend$foo', which are then synthesized by the linker. The stubs +// load the particular selector 'foo' from __objc_selrefs, setting it to the +// first argument of the objc_msgSend call, and then jumps to objc_msgSend. The +// actual stub contents are mirrored from ld64. +class ObjCStubsSection final : public SyntheticSection { +public: + ObjCStubsSection(); + void addEntry(Symbol *sym); + uint64_t getSize() const override; + bool isNeeded() const override { return !symbols.empty(); } + void finalize() override { isec->isFinal = true; } + void writeTo(uint8_t *buf) const override; + void setUp(); + + static constexpr llvm::StringLiteral symbolPrefix = "_objc_msgSend$"; + +private: + std::vector symbols; + std::vector offsets; + int objcMsgSendGotIndex = 0; +}; + // Note that this section may also be targeted by non-lazy bindings. In // particular, this happens when branch relocations target weak symbols. class LazyPointerSection final : public SyntheticSection { @@ -317,6 +347,9 @@ public: uint64_t getSize() const override; bool isNeeded() const override; void writeTo(uint8_t *buf) const override; + uint64_t getVA(uint32_t index) const { + return addr + (index << target->p2WordSize); + } }; class LazyBindingSection final : public LinkEditSection { @@ -328,13 +361,13 @@ public: void writeTo(uint8_t *buf) const override; // Note that every entry here will by referenced by a corresponding entry in // the StubHelperSection. - void addEntry(DylibSymbol *dysym); - const llvm::SetVector &getEntries() const { return entries; } + void addEntry(Symbol *dysym); + const llvm::SetVector &getEntries() const { return entries; } private: - uint32_t encode(const DylibSymbol &); + uint32_t encode(const Symbol &); - llvm::SetVector entries; + llvm::SetVector entries; SmallVector contents; llvm::raw_svector_ostream os{contents}; }; @@ -345,6 +378,7 @@ public: ExportSection(); void finalizeContents() override; uint64_t getRawSize() const override { return size; } + bool isNeeded() const override { return size; } void writeTo(uint8_t *buf) const override; bool hasWeakSymbol = false; @@ -354,8 +388,9 @@ private: size_t size = 0; }; -// Stores 'data in code' entries that describe the locations of -// data regions inside code sections. +// Stores 'data in code' entries that describe the locations of data regions +// inside code sections. This is used by llvm-objdump to distinguish jump tables +// and stop them from being disassembled as instructions. class DataInCodeSection final : public LinkEditSection { public: DataInCodeSection(); @@ -431,7 +466,7 @@ public: uint32_t getNumUndefinedSymbols() const { return undefinedSymbols.size(); } private: - void emitBeginSourceStab(llvm::DWARFUnit *compileUnit); + void emitBeginSourceStab(StringRef); void emitEndSourceStab(); void emitObjectFileStab(ObjFile *); void emitEndFunStab(Defined *); @@ -476,6 +511,8 @@ public: // The code signature comes at the very end of the linked output file. class CodeSignatureSection final : public LinkEditSection { public: + // NOTE: These values are duplicated in llvm-objcopy's MachO/Object.h file + // and any changes here, should be repeated there. static constexpr uint8_t blockSizeShift = 12; static constexpr size_t blockSize = (1 << blockSizeShift); // 4 KiB static constexpr size_t hashSize = 256 / 8; @@ -510,7 +547,7 @@ private: class CStringSection : public SyntheticSection { public: - CStringSection(); + CStringSection(const char *name); void addInput(CStringInputSection *); uint64_t getSize() const override { return size; } virtual void finalizeContents(); @@ -525,13 +562,23 @@ private: class DeduplicatedCStringSection final : public CStringSection { public: - DeduplicatedCStringSection(); - uint64_t getSize() const override { return builder.getSize(); } + DeduplicatedCStringSection(const char *name) : CStringSection(name){}; + uint64_t getSize() const override { return size; } void finalizeContents() override; - void writeTo(uint8_t *buf) const override { builder.write(buf); } + void writeTo(uint8_t *buf) const override; + + struct StringOffset { + uint8_t trailingZeros; + uint64_t outSecOff = UINT64_MAX; + + explicit StringOffset(uint8_t zeros) : trailingZeros(zeros) {} + }; + + StringOffset getStringOffset(StringRef str) const; private: - llvm::StringTableBuilder builder; + llvm::DenseMap stringOffsetMap; + size_t size = 0; }; /* @@ -543,7 +590,7 @@ public: using UInt128 = std::pair; // I don't think the standard guarantees the size of a pair, so let's make // sure it's exact -- that way we can construct it via `mmap`. - static_assert(sizeof(UInt128) == 16, ""); + static_assert(sizeof(UInt128) == 16); WordLiteralSection(); void addInput(WordLiteralInputSection *); @@ -560,16 +607,16 @@ public: !literal8Map.empty(); } - uint64_t getLiteral16Offset(const uint8_t *buf) const { + uint64_t getLiteral16Offset(uintptr_t buf) const { return literal16Map.at(*reinterpret_cast(buf)) * 16; } - uint64_t getLiteral8Offset(const uint8_t *buf) const { + uint64_t getLiteral8Offset(uintptr_t buf) const { return literal16Map.size() * 16 + literal8Map.at(*reinterpret_cast(buf)) * 8; } - uint64_t getLiteral4Offset(const uint8_t *buf) const { + uint64_t getLiteral4Offset(uintptr_t buf) const { return literal16Map.size() * 16 + literal8Map.size() * 8 + literal4Map.at(*reinterpret_cast(buf)) * 4; } @@ -588,9 +635,163 @@ private: std::unordered_map literal4Map; }; +class ObjCImageInfoSection final : public SyntheticSection { +public: + ObjCImageInfoSection(); + bool isNeeded() const override { return !files.empty(); } + uint64_t getSize() const override { return 8; } + void addFile(const InputFile *file) { + assert(!file->objCImageInfo.empty()); + files.push_back(file); + } + void finalizeContents(); + void writeTo(uint8_t *buf) const override; + +private: + struct ImageInfo { + uint8_t swiftVersion = 0; + bool hasCategoryClassProperties = false; + } info; + static ImageInfo parseImageInfo(const InputFile *); + std::vector files; // files with image info +}; + +// This section stores 32-bit __TEXT segment offsets of initializer functions. +// +// The compiler stores pointers to initializers in __mod_init_func. These need +// to be fixed up at load time, which takes time and dirties memory. By +// synthesizing InitOffsetsSection from them, this data can live in the +// read-only __TEXT segment instead. This section is used by default when +// chained fixups are enabled. +// +// There is no similar counterpart to __mod_term_func, as that section is +// deprecated, and static destructors are instead handled by registering them +// via __cxa_atexit from an autogenerated initializer function (see D121736). +class InitOffsetsSection final : public SyntheticSection { +public: + InitOffsetsSection(); + bool isNeeded() const override { return !sections.empty(); } + uint64_t getSize() const override; + void writeTo(uint8_t *buf) const override; + void setUp(); + + void addInput(ConcatInputSection *isec) { sections.push_back(isec); } + const std::vector &inputs() const { return sections; } + +private: + std::vector sections; +}; + +// Chained fixups are a replacement for classic dyld opcodes. In this format, +// most of the metadata necessary for binding symbols and rebasing addresses is +// stored directly in the memory location that will have the fixup applied. +// +// The fixups form singly linked lists; each one covering a single page in +// memory. The __LINKEDIT,__chainfixups section stores the page offset of the +// first fixup of each page; the rest can be found by walking the chain using +// the offset that is embedded in each entry. +// +// This setup allows pages to be relocated lazily at page-in time and without +// being dirtied. The kernel can discard and load them again as needed. This +// technique, called page-in linking, was introduced in macOS 13. +// +// The benefits of this format are: +// - smaller __LINKEDIT segment, as most of the fixup information is stored in +// the data segment +// - faster startup, since not all relocations need to be done upfront +// - slightly lower memory usage, as fewer pages are dirtied +// +// Userspace x86_64 and arm64 binaries have two types of fixup entries: +// - Rebase entries contain an absolute address, to which the object's load +// address will be added to get the final value. This is used for loading +// the address of a symbol defined in the same binary. +// - Binding entries are mostly used for symbols imported from other dylibs, +// but for weakly bound and interposable symbols as well. They are looked up +// by a (symbol name, library) pair stored in __chainfixups. This import +// entry also encodes whether the import is weak (i.e. if the symbol is +// missing, it should be set to null instead of producing a load error). +// The fixup encodes an ordinal associated with the import, and an optional +// addend. +// +// The entries are tightly packed 64-bit bitfields. One of the bits specifies +// which kind of fixup to interpret them as. +// +// LLD generates the fixup data in 5 stages: +// 1. While scanning relocations, we make a note of each location that needs +// a fixup by calling addRebase() or addBinding(). During this, we assign +// a unique ordinal for each (symbol name, library, addend) import tuple. +// 2. After addresses have been assigned to all sections, and thus the memory +// layout of the linked image is final; finalizeContents() is called. Here, +// the page offsets of the chain start entries are calculated. +// 3. ChainedFixupsSection::writeTo() writes the page start offsets and the +// imports table to the output file. +// 4. Each section's fixup entries are encoded and written to disk in +// ConcatInputSection::writeTo(), but without writing the offsets that form +// the chain. +// 5. Finally, each page's (which might correspond to multiple sections) +// fixups are linked together in Writer::buildFixupChains(). +class ChainedFixupsSection final : public LinkEditSection { +public: + ChainedFixupsSection(); + void finalizeContents() override; + uint64_t getRawSize() const override { return size; } + bool isNeeded() const override; + void writeTo(uint8_t *buf) const override; + + void addRebase(const InputSection *isec, uint64_t offset) { + locations.emplace_back(isec, offset); + } + void addBinding(const Symbol *dysym, const InputSection *isec, + uint64_t offset, int64_t addend = 0); + + void setHasNonWeakDefinition() { hasNonWeakDef = true; } + + // Returns an (ordinal, inline addend) tuple used by dyld_chained_ptr_64_bind. + std::pair getBinding(const Symbol *sym, + int64_t addend) const; + + const std::vector &getLocations() const { return locations; } + + bool hasWeakBinding() const { return hasWeakBind; } + bool hasNonWeakDefinition() const { return hasNonWeakDef; } + +private: + // Location::offset initially stores the offset within an InputSection, but + // contains output segment offsets after finalizeContents(). + std::vector locations; + // (target symbol, addend) => import ordinal + llvm::MapVector, uint32_t> bindings; + + struct SegmentInfo { + SegmentInfo(const OutputSegment *oseg) : oseg(oseg) {} + + const OutputSegment *oseg; + // (page index, fixup starts offset) + llvm::SmallVector> pageStarts; + + size_t getSize() const; + size_t writeTo(uint8_t *buf) const; + }; + llvm::SmallVector fixupSegments; + + size_t symtabSize = 0; + size_t size = 0; + + bool needsAddend = false; + bool needsLargeAddend = false; + bool hasWeakBind = false; + bool hasNonWeakDef = false; + llvm::MachO::ChainedImportFormat importFormat; +}; + +void writeChainedRebase(uint8_t *buf, uint64_t targetVA); +void writeChainedFixup(uint8_t *buf, const Symbol *sym, int64_t addend); + struct InStruct { + const uint8_t *bufferStart = nullptr; MachHeaderSection *header = nullptr; CStringSection *cStringSection = nullptr; + DeduplicatedCStringSection *objcMethnameSection = nullptr; WordLiteralSection *wordLiteralSection = nullptr; RebaseSection *rebase = nullptr; BindingSection *binding = nullptr; @@ -602,8 +803,13 @@ struct InStruct { LazyPointerSection *lazyPointers = nullptr; StubsSection *stubs = nullptr; StubHelperSection *stubHelper = nullptr; + ObjCStubsSection *objcStubs = nullptr; + ConcatInputSection *objcSelrefs = nullptr; UnwindInfoSection *unwindInfo = nullptr; + ObjCImageInfoSection *objCImageInfo = nullptr; ConcatInputSection *imageLoaderCache = nullptr; + InitOffsetsSection *initOffsets = nullptr; + ChainedFixupsSection *chainedFixups = nullptr; }; extern InStruct in; @@ -611,7 +817,6 @@ extern std::vector syntheticSections; void createSyntheticSymbols(); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Target.h b/gnu/llvm/lld/MachO/Target.h index a5da7644a84..44a85521ace 100644 --- a/gnu/llvm/lld/MachO/Target.h +++ b/gnu/llvm/lld/MachO/Target.h @@ -14,19 +14,31 @@ #include "llvm/ADT/BitmaskEnum.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include #include -namespace lld { -namespace macho { +#include "mach-o/compact_unwind_encoding.h" + +namespace lld::macho { LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); class Symbol; class Defined; class DylibSymbol; class InputSection; +class ObjFile; + +static_assert(static_cast(UNWIND_X86_64_MODE_MASK) == + static_cast(UNWIND_X86_MODE_MASK) && + static_cast(UNWIND_ARM64_MODE_MASK) == + static_cast(UNWIND_X86_64_MODE_MASK)); + +// Since the mode masks have the same value on all targets, define +// a common one for convenience. +constexpr uint32_t UNWIND_MODE_MASK = UNWIND_X86_64_MODE_MASK; class TargetInfo { public: @@ -37,6 +49,7 @@ public: pageZeroSize = LP::pageZeroSize; headerSize = sizeof(typename LP::mach_header); wordSize = LP::wordSize; + p2WordSize = llvm::CTLog2(); } virtual ~TargetInfo() = default; @@ -50,11 +63,18 @@ public: // Write code for lazy binding. See the comments on StubsSection for more // details. - virtual void writeStub(uint8_t *buf, const Symbol &) const = 0; + virtual void writeStub(uint8_t *buf, const Symbol &, + uint64_t pointerVA) const = 0; virtual void writeStubHelperHeader(uint8_t *buf) const = 0; - virtual void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + virtual void writeStubHelperEntry(uint8_t *buf, const Symbol &, uint64_t entryAddr) const = 0; + virtual void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, + uint64_t stubsAddr, uint64_t stubOffset, + uint64_t selrefsVA, uint64_t selectorIndex, + uint64_t gotAddr, + uint64_t msgSendIndex) const = 0; + // Symbols may be referenced via either the GOT or the stubs section, // depending on the relocation type. prepareSymbolRelocation() will set up the // GOT/stubs entries, and resolveSymbolVA() will return the addresses of those @@ -62,20 +82,35 @@ public: // on a level of address indirection. virtual void relaxGotLoad(uint8_t *loc, uint8_t type) const = 0; - virtual const RelocAttrs &getRelocAttrs(uint8_t type) const = 0; - virtual uint64_t getPageSize() const = 0; virtual void populateThunk(InputSection *thunk, Symbol *funcSym) { llvm_unreachable("target does not use thunks"); } + const RelocAttrs &getRelocAttrs(uint8_t type) const { + assert(type < relocAttrs.size() && "invalid relocation type"); + if (type >= relocAttrs.size()) + return invalidRelocAttrs; + return relocAttrs[type]; + } + bool hasAttr(uint8_t type, RelocAttrBits bit) const { return getRelocAttrs(type).hasAttr(bit); } bool usesThunks() const { return thunkSize > 0; } + // For now, handleDtraceReloc only implements -no_dtrace_dof, and ensures + // that the linking would not fail even when there are user-provided dtrace + // symbols. However, unlike ld64, lld currently does not emit __dof sections. + virtual void handleDtraceReloc(const Symbol *sym, const Reloc &r, + uint8_t *loc) const { + llvm_unreachable("Unsupported architecture for dtrace symbols"); + } + + virtual void applyOptimizationHints(uint8_t *, const ObjFile &) const {}; + uint32_t magic; llvm::MachO::CPUType cpuType; uint32_t cpuSubtype; @@ -85,10 +120,20 @@ public: size_t stubSize; size_t stubHelperHeaderSize; size_t stubHelperEntrySize; + size_t objcStubsFastSize; + size_t objcStubsAlignment; + uint8_t p2WordSize; size_t wordSize; size_t thunkSize = 0; - uint64_t branchRange = 0; + uint64_t forwardBranchRange = 0; + uint64_t backwardBranchRange = 0; + + uint32_t modeDwarfEncoding; + uint8_t subtractorRelocType; + uint8_t unsignedRelocType; + + llvm::ArrayRef relocAttrs; // We contrive this value as sufficiently far from any valid address that it // will always be out-of-range for any architecture. UINT64_MAX is not a @@ -136,7 +181,6 @@ struct ILP32 { extern TargetInfo *target; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/UnwindInfoSection.cpp b/gnu/llvm/lld/MachO/UnwindInfoSection.cpp index 3efc646c2ad..470f33523dd 100644 --- a/gnu/llvm/lld/MachO/UnwindInfoSection.cpp +++ b/gnu/llvm/lld/MachO/UnwindInfoSection.cpp @@ -7,8 +7,6 @@ //===----------------------------------------------------------------------===// #include "UnwindInfoSection.h" -#include "ConcatOutputSection.h" -#include "Config.h" #include "InputSection.h" #include "OutputSection.h" #include "OutputSegment.h" @@ -19,12 +17,18 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Parallel.h" + +#include "mach-o/compact_unwind_encoding.h" + +#include using namespace llvm; using namespace llvm::MachO; +using namespace llvm::support::endian; using namespace lld; using namespace lld::macho; @@ -78,20 +82,57 @@ using namespace lld::macho; // advantage, achieving a 3-order-of-magnitude reduction in the // number of entries. // -// * The __TEXT,__unwind_info format can accommodate up to 127 unique -// encodings for the space-efficient compressed format. In practice, -// fewer than a dozen unique encodings are used by C++ programs of -// all sizes. Therefore, we don't even bother implementing the regular -// non-compressed format. Time will tell if anyone in the field ever -// overflows the 127-encodings limit. -// // Refer to the definition of unwind_info_section_header in // compact_unwind_encoding.h for an overview of the format we are encoding // here. -// TODO(gkm): prune __eh_frame entries superseded by __unwind_info, PR50410 // TODO(gkm): how do we align the 2nd-level pages? +// The offsets of various fields in the on-disk representation of each compact +// unwind entry. +struct CompactUnwindOffsets { + uint32_t functionAddress; + uint32_t functionLength; + uint32_t encoding; + uint32_t personality; + uint32_t lsda; + + CompactUnwindOffsets(size_t wordSize) { + if (wordSize == 8) + init(); + else { + assert(wordSize == 4); + init(); + } + } + +private: + template void init() { + functionAddress = offsetof(Layout, functionAddress); + functionLength = offsetof(Layout, functionLength); + encoding = offsetof(Layout, encoding); + personality = offsetof(Layout, personality); + lsda = offsetof(Layout, lsda); + } + + template struct Layout { + Ptr functionAddress; + uint32_t functionLength; + compact_unwind_encoding_t encoding; + Ptr personality; + Ptr lsda; + }; +}; + +// LLD's internal representation of a compact unwind entry. +struct CompactUnwindEntry { + uint64_t functionAddress; + uint32_t functionLength; + compact_unwind_encoding_t encoding; + Symbol *personality; + InputSection *lsda; +}; + using EncodingMap = DenseMap; struct SecondLevelPage { @@ -103,49 +144,94 @@ struct SecondLevelPage { EncodingMap localEncodingIndexes; }; -template +// UnwindInfoSectionImpl allows us to avoid cluttering our header file with a +// lengthy definition of UnwindInfoSection. class UnwindInfoSectionImpl final : public UnwindInfoSection { public: - void prepareRelocations(ConcatInputSection *) override; - void addInput(ConcatInputSection *) override; + UnwindInfoSectionImpl() : cuOffsets(target->wordSize) {} + uint64_t getSize() const override { return unwindInfoSize; } + void prepare() override; void finalize() override; void writeTo(uint8_t *buf) const override; private: + void prepareRelocations(ConcatInputSection *); + void relocateCompactUnwind(std::vector &); + void encodePersonalities(); + Symbol *canonicalizePersonality(Symbol *); + + uint64_t unwindInfoSize = 0; + std::vector symbolsVec; + CompactUnwindOffsets cuOffsets; std::vector> commonEncodings; EncodingMap commonEncodingIndexes; - // Indices of personality functions within the GOT. - std::vector personalities; + // The entries here will be in the same order as their originating symbols + // in symbolsVec. + std::vector cuEntries; + // Indices into the cuEntries vector. + std::vector cuIndices; + std::vector personalities; SmallDenseMap, Symbol *> personalityTable; - std::vector lsdaEntries; - // Map of function offset (from the image base) to an index within the LSDA - // array. - DenseMap functionToLsdaIndex; - std::vector> cuVector; - std::vector *> cuPtrVector; + // Indices into cuEntries for CUEs with a non-null LSDA. + std::vector entriesWithLsda; + // Map of cuEntries index to an index within the LSDA array. + DenseMap lsdaIndex; std::vector secondLevelPages; uint64_t level2PagesOffset = 0; + // The highest-address function plus its size. The unwinder needs this to + // determine the address range that is covered by unwind info. + uint64_t cueEndBoundary = 0; }; UnwindInfoSection::UnwindInfoSection() : SyntheticSection(segment_names::text, section_names::unwindInfo) { align = 4; - compactUnwindSection = - make(section_names::compactUnwind); } -void UnwindInfoSection::prepareRelocations() { - for (ConcatInputSection *isec : compactUnwindSection->inputs) - prepareRelocations(isec); +// Record function symbols that may need entries emitted in __unwind_info, which +// stores unwind data for address ranges. +// +// Note that if several adjacent functions have the same unwind encoding and +// personality function and no LSDA, they share one unwind entry. For this to +// work, functions without unwind info need explicit "no unwind info" unwind +// entries -- else the unwinder would think they have the unwind info of the +// closest function with unwind info right before in the image. Thus, we add +// function symbols for each unique address regardless of whether they have +// associated unwind info. +void UnwindInfoSection::addSymbol(const Defined *d) { + if (d->unwindEntry) + allEntriesAreOmitted = false; + // We don't yet know the final output address of this symbol, but we know that + // they are uniquely determined by a combination of the isec and value, so + // we use that as the key here. + auto p = symbols.insert({{d->isec, d->value}, d}); + // If we have multiple symbols at the same address, only one of them can have + // an associated unwind entry. + if (!p.second && d->unwindEntry) { + assert(p.first->second == d || !p.first->second->unwindEntry); + p.first->second = d; + } } -template -void UnwindInfoSectionImpl::addInput(ConcatInputSection *isec) { - assert(isec->getSegName() == segment_names::ld && - isec->getName() == section_names::compactUnwind); - isec->parent = compactUnwindSection; - compactUnwindSection->addInput(isec); +void UnwindInfoSectionImpl::prepare() { + // This iteration needs to be deterministic, since prepareRelocations may add + // entries to the GOT. Hence the use of a MapVector for + // UnwindInfoSection::symbols. + for (const Defined *d : make_second_range(symbols)) + if (d->unwindEntry) { + if (d->unwindEntry->getName() == section_names::compactUnwind) { + prepareRelocations(d->unwindEntry); + } else { + // We don't have to add entries to the GOT here because FDEs have + // explicit GOT relocations, so Writer::scanRelocations() will add those + // GOT entries. However, we still need to canonicalize the personality + // pointers (like prepareRelocations() does for CU entries) in order + // to avoid overflowing the 3-personality limit. + FDE &fde = cast(d->getFile())->fdes[d->unwindEntry]; + fde.personality = canonicalizePersonality(fde.personality); + } + } } // Compact unwind relocations have different semantics, so we handle them in a @@ -153,8 +239,7 @@ void UnwindInfoSectionImpl::addInput(ConcatInputSection *isec) { // rebase opcodes for __LD,__compact_unwind, because that section doesn't // actually end up in the final binary. Second, personality pointers always // reside in the GOT and must be treated specially. -template -void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) { +void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) { assert(!isec->shouldOmitFromOutput() && "__compact_unwind section should not be omitted"); @@ -166,30 +251,46 @@ void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) { for (size_t i = 0; i < isec->relocs.size(); ++i) { Reloc &r = isec->relocs[i]; assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED)); - - if (r.offset % sizeof(CompactUnwindEntry) == 0) { - InputSection *referentIsec; - if (auto *isec = r.referent.dyn_cast()) - referentIsec = isec; - else - referentIsec = cast(r.referent.dyn_cast())->isec; - - if (!cast(referentIsec)->shouldOmitFromOutput()) - allEntriesAreOmitted = false; - continue; - } - - if (r.offset % sizeof(CompactUnwindEntry) != - offsetof(CompactUnwindEntry, personality)) + // Since compact unwind sections aren't part of the inputSections vector, + // they don't get canonicalized by scanRelocations(), so we have to do the + // canonicalization here. + if (auto *referentIsec = r.referent.dyn_cast()) + r.referent = referentIsec->canonical(); + + // Functions and LSDA entries always reside in the same object file as the + // compact unwind entries that references them, and thus appear as section + // relocs. There is no need to prepare them. We only prepare relocs for + // personality functions. + if (r.offset != cuOffsets.personality) continue; if (auto *s = r.referent.dyn_cast()) { + // Personality functions are nearly always system-defined (e.g., + // ___gxx_personality_v0 for C++) and relocated as dylib symbols. When an + // application provides its own personality function, it might be + // referenced by an extern Defined symbol reloc, or a local section reloc. + if (auto *defined = dyn_cast(s)) { + // XXX(vyng) This is a special case for handling duplicate personality + // symbols. Note that LD64's behavior is a bit different and it is + // inconsistent with how symbol resolution usually work + // + // So we've decided not to follow it. Instead, simply pick the symbol + // with the same name from the symbol table to replace the local one. + // + // (See discussions/alternatives already considered on D107533) + if (!defined->isExternal()) + if (Symbol *sym = symtab->find(defined->getName())) + if (!sym->isLazy()) + r.referent = s = sym; + } if (auto *undefined = dyn_cast(s)) { - treatUndefinedSymbol(*undefined); + treatUndefinedSymbol(*undefined, isec, r.offset); // treatUndefinedSymbol() can replace s with a DylibSymbol; re-check. if (isa(s)) continue; } + + // Similar to canonicalizePersonality(), but we also register a GOT entry. if (auto *defined = dyn_cast(s)) { // Check if we have created a synthetic symbol at the same address. Symbol *&personality = @@ -202,6 +303,7 @@ void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) { } continue; } + assert(isa(s)); in.got->addEntry(s); continue; @@ -219,8 +321,10 @@ void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) { s = make("", /*file=*/nullptr, referentIsec, r.addend, /*size=*/0, /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false, + /*includeInSymtab=*/true, /*isThumb=*/false, /*isReferencedDynamically=*/false, /*noDeadStrip=*/false); + s->used = true; in.got->addEntry(s); } r.referent = s; @@ -229,119 +333,86 @@ void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) { } } -// Unwind info lives in __DATA, and finalization of __TEXT will occur before -// finalization of __DATA. Moreover, the finalization of unwind info depends on -// the exact addresses that it references. So it is safe for compact unwind to -// reference addresses in __TEXT, but not addresses in any other segment. -static ConcatInputSection *checkTextSegment(InputSection *isec) { - if (isec->getSegName() != segment_names::text) - error("compact unwind references address in " + toString(isec) + - " which is not in segment __TEXT"); - // __text should always be a ConcatInputSection. - return cast(isec); +Symbol *UnwindInfoSectionImpl::canonicalizePersonality(Symbol *personality) { + if (auto *defined = dyn_cast_or_null(personality)) { + // Check if we have created a synthetic symbol at the same address. + Symbol *&synth = personalityTable[{defined->isec, defined->value}]; + if (synth == nullptr) + synth = defined; + else if (synth != defined) + return synth; + } + return personality; } -template -constexpr Ptr TombstoneValue = std::numeric_limits::max(); - // We need to apply the relocations to the pre-link compact unwind section // before converting it to post-link form. There should only be absolute // relocations here: since we are not emitting the pre-link CU section, there // is no source address to make a relative location meaningful. -template -static void -relocateCompactUnwind(ConcatOutputSection *compactUnwindSection, - std::vector> &cuVector) { - for (const ConcatInputSection *isec : compactUnwindSection->inputs) { - assert(isec->parent == compactUnwindSection); - - uint8_t *buf = - reinterpret_cast(cuVector.data()) + isec->outSecOff; - memcpy(buf, isec->data.data(), isec->data.size()); - - for (const Reloc &r : isec->relocs) { - uint64_t referentVA = TombstoneValue; - if (auto *referentSym = r.referent.dyn_cast()) { - if (!isa(referentSym)) { - if (auto *defined = dyn_cast(referentSym)) - checkTextSegment(defined->isec); - // At this point in the link, we may not yet know the final address of - // the GOT, so we just encode the index. We make it a 1-based index so - // that we can distinguish the null pointer case. - referentVA = referentSym->gotIndex + 1; - } - } else { - auto *referentIsec = r.referent.get(); - ConcatInputSection *concatIsec = checkTextSegment(referentIsec); - if (!concatIsec->shouldOmitFromOutput()) - referentVA = referentIsec->getVA(r.addend); +void UnwindInfoSectionImpl::relocateCompactUnwind( + std::vector &cuEntries) { + parallelFor(0, symbolsVec.size(), [&](size_t i) { + CompactUnwindEntry &cu = cuEntries[i]; + const Defined *d = symbolsVec[i].second; + cu.functionAddress = d->getVA(); + if (!d->unwindEntry) + return; + + // If we have DWARF unwind info, create a CU entry that points to it. + if (d->unwindEntry->getName() == section_names::ehFrame) { + cu.encoding = target->modeDwarfEncoding | d->unwindEntry->outSecOff; + const FDE &fde = cast(d->getFile())->fdes[d->unwindEntry]; + cu.functionLength = fde.funcLength; + cu.personality = fde.personality; + cu.lsda = fde.lsda; + return; + } + + assert(d->unwindEntry->getName() == section_names::compactUnwind); + + auto buf = reinterpret_cast(d->unwindEntry->data.data()) - + target->wordSize; + cu.functionLength = + support::endian::read32le(buf + cuOffsets.functionLength); + cu.encoding = support::endian::read32le(buf + cuOffsets.encoding); + for (const Reloc &r : d->unwindEntry->relocs) { + if (r.offset == cuOffsets.personality) { + cu.personality = r.referent.get(); + } else if (r.offset == cuOffsets.lsda) { + if (auto *referentSym = r.referent.dyn_cast()) + cu.lsda = cast(referentSym)->isec; + else + cu.lsda = r.referent.get(); } - writeAddress(buf + r.offset, referentVA, r.length); } - } + }); } // There should only be a handful of unique personality pointers, so we can // encode them as 2-bit indices into a small array. -template -static void -encodePersonalities(const std::vector *> &cuPtrVector, - std::vector &personalities) { - for (CompactUnwindEntry *cu : cuPtrVector) { - if (cu->personality == 0) +void UnwindInfoSectionImpl::encodePersonalities() { + for (size_t idx : cuIndices) { + CompactUnwindEntry &cu = cuEntries[idx]; + if (cu.personality == nullptr) continue; // Linear search is fast enough for a small array. - auto it = find(personalities, cu->personality); + auto it = find(personalities, cu.personality); uint32_t personalityIndex; // 1-based index if (it != personalities.end()) { personalityIndex = std::distance(personalities.begin(), it) + 1; } else { - personalities.push_back(cu->personality); + personalities.push_back(cu.personality); personalityIndex = personalities.size(); } - cu->encoding |= + cu.encoding |= personalityIndex << countTrailingZeros( static_cast(UNWIND_PERSONALITY_MASK)); } if (personalities.size() > 3) - error("too many personalities (" + std::to_string(personalities.size()) + + error("too many personalities (" + Twine(personalities.size()) + ") for compact unwind to encode"); } -// __unwind_info stores unwind data for address ranges. If several -// adjacent functions have the same unwind encoding, LSDA, and personality -// function, they share one unwind entry. For this to work, functions without -// unwind info need explicit "no unwind info" unwind entries -- else the -// unwinder would think they have the unwind info of the closest function -// with unwind info right before in the image. -template -static void addEntriesForFunctionsWithoutUnwindInfo( - std::vector> &cuVector) { - DenseSet hasUnwindInfo; - for (CompactUnwindEntry &cuEntry : cuVector) - if (cuEntry.functionAddress != TombstoneValue) - hasUnwindInfo.insert(cuEntry.functionAddress); - - // Add explicit "has no unwind info" entries for all global and local symbols - // without unwind info. - auto markNoUnwindInfo = [&cuVector, &hasUnwindInfo](const Defined *d) { - if (d->isLive() && d->isec && isCodeSection(d->isec)) { - Ptr ptr = d->getVA(); - if (!hasUnwindInfo.count(ptr)) - cuVector.push_back({ptr, 0, 0, 0, 0}); - } - }; - for (Symbol *sym : symtab->getSymbols()) - if (auto *d = dyn_cast(sym)) - markNoUnwindInfo(d); - for (const InputFile *file : inputFiles) - if (auto *objFile = dyn_cast(file)) - for (Symbol *sym : objFile->symbols) - if (auto *d = dyn_cast_or_null(sym)) - if (!d->isExternal()) - markNoUnwindInfo(d); -} - static bool canFoldEncoding(compact_unwind_encoding_t encoding) { // From compact_unwind_encoding.h: // UNWIND_X86_64_MODE_STACK_IND: @@ -355,10 +426,10 @@ static bool canFoldEncoding(compact_unwind_encoding_t encoding) { // of the unwind info's unwind address, two functions that have identical // unwind info can't be folded if it's using this encoding since both // entries need unique addresses. - static_assert(UNWIND_X86_64_MODE_MASK == UNWIND_X86_MODE_MASK, ""); - static_assert(UNWIND_X86_64_MODE_STACK_IND == UNWIND_X86_MODE_STACK_IND, ""); + static_assert(static_cast(UNWIND_X86_64_MODE_STACK_IND) == + static_cast(UNWIND_X86_MODE_STACK_IND)); if ((target->cpuType == CPU_TYPE_X86_64 || target->cpuType == CPU_TYPE_X86) && - (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND) { + (encoding & UNWIND_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND) { // FIXME: Consider passing in the two function addresses and getting // their two stack sizes off the `subq` and only returning false if they're // actually different. @@ -368,87 +439,86 @@ static bool canFoldEncoding(compact_unwind_encoding_t encoding) { } // Scan the __LD,__compact_unwind entries and compute the space needs of -// __TEXT,__unwind_info and __TEXT,__eh_frame -template void UnwindInfoSectionImpl::finalize() { - if (compactUnwindSection == nullptr) +// __TEXT,__unwind_info and __TEXT,__eh_frame. +void UnwindInfoSectionImpl::finalize() { + if (symbols.empty()) return; // At this point, the address space for __TEXT,__text has been // assigned, so we can relocate the __LD,__compact_unwind entries // into a temporary buffer. Relocation is necessary in order to sort // the CU entries by function address. Sorting is necessary so that - // we can fold adjacent CU entries with identical - // encoding+personality+lsda. Folding is necessary because it reduces - // the number of CU entries by as much as 3 orders of magnitude! - compactUnwindSection->finalize(); - assert(compactUnwindSection->getSize() % sizeof(CompactUnwindEntry) == - 0); - size_t cuCount = - compactUnwindSection->getSize() / sizeof(CompactUnwindEntry); - cuVector.resize(cuCount); - relocateCompactUnwind(compactUnwindSection, cuVector); - - addEntriesForFunctionsWithoutUnwindInfo(cuVector); + // we can fold adjacent CU entries with identical encoding+personality + // and without any LSDA. Folding is necessary because it reduces the + // number of CU entries by as much as 3 orders of magnitude! + cuEntries.resize(symbols.size()); + // The "map" part of the symbols MapVector was only needed for deduplication + // in addSymbol(). Now that we are done adding, move the contents to a plain + // std::vector for indexed access. + symbolsVec = symbols.takeVector(); + relocateCompactUnwind(cuEntries); // Rather than sort & fold the 32-byte entries directly, we create a - // vector of pointers to entries and sort & fold that instead. - cuPtrVector.reserve(cuVector.size()); - for (CompactUnwindEntry &cuEntry : cuVector) - cuPtrVector.emplace_back(&cuEntry); - llvm::sort(cuPtrVector, [](const CompactUnwindEntry *a, - const CompactUnwindEntry *b) { - return a->functionAddress < b->functionAddress; + // vector of indices to entries and sort & fold that instead. + cuIndices.resize(cuEntries.size()); + std::iota(cuIndices.begin(), cuIndices.end(), 0); + llvm::sort(cuIndices, [&](size_t a, size_t b) { + return cuEntries[a].functionAddress < cuEntries[b].functionAddress; }); - // Dead-stripped functions get a functionAddress of TombstoneValue in - // relocateCompactUnwind(). Filter them out here. - // FIXME: This doesn't yet collect associated data like LSDAs kept - // alive only by a now-removed CompactUnwindEntry or other comdat-like - // data (`kindNoneGroupSubordinate*` in ld64). - CompactUnwindEntry tombstone; - tombstone.functionAddress = TombstoneValue; - cuPtrVector.erase( - std::lower_bound(cuPtrVector.begin(), cuPtrVector.end(), &tombstone, - [](const CompactUnwindEntry *a, - const CompactUnwindEntry *b) { - return a->functionAddress < b->functionAddress; - }), - cuPtrVector.end()); - - // If there are no entries left after adding explicit "no unwind info" - // entries and removing entries for dead-stripped functions, don't write - // an __unwind_info section at all. - assert(allEntriesAreOmitted == cuPtrVector.empty()); - if (cuPtrVector.empty()) - return; + // Record the ending boundary before we fold the entries. + cueEndBoundary = cuEntries[cuIndices.back()].functionAddress + + cuEntries[cuIndices.back()].functionLength; - // Fold adjacent entries with matching encoding+personality+lsda - // We use three iterators on the same cuPtrVector to fold in-situ: + // Fold adjacent entries with matching encoding+personality and without LSDA + // We use three iterators on the same cuIndices to fold in-situ: // (1) `foldBegin` is the first of a potential sequence of matching entries // (2) `foldEnd` is the first non-matching entry after `foldBegin`. // The semi-open interval [ foldBegin .. foldEnd ) contains a range // entries that can be folded into a single entry and written to ... // (3) `foldWrite` - auto foldWrite = cuPtrVector.begin(); - for (auto foldBegin = cuPtrVector.begin(); foldBegin < cuPtrVector.end();) { + auto foldWrite = cuIndices.begin(); + for (auto foldBegin = cuIndices.begin(); foldBegin < cuIndices.end();) { auto foldEnd = foldBegin; - while (++foldEnd < cuPtrVector.end() && - (*foldBegin)->encoding == (*foldEnd)->encoding && - (*foldBegin)->personality == (*foldEnd)->personality && - (*foldBegin)->lsda == (*foldEnd)->lsda && - canFoldEncoding((*foldEnd)->encoding)) + // Common LSDA encodings (e.g. for C++ and Objective-C) contain offsets from + // a base address. The base address is normally not contained directly in + // the LSDA, and in that case, the personality function treats the starting + // address of the function (which is computed by the unwinder) as the base + // address and interprets the LSDA accordingly. The unwinder computes the + // starting address of a function as the address associated with its CU + // entry. For this reason, we cannot fold adjacent entries if they have an + // LSDA, because folding would make the unwinder compute the wrong starting + // address for the functions with the folded entries, which in turn would + // cause the personality function to misinterpret the LSDA for those + // functions. In the very rare case where the base address is encoded + // directly in the LSDA, two functions at different addresses would + // necessarily have different LSDAs, so their CU entries would not have been + // folded anyway. + while (++foldEnd < cuIndices.end() && + cuEntries[*foldBegin].encoding == cuEntries[*foldEnd].encoding && + !cuEntries[*foldBegin].lsda && !cuEntries[*foldEnd].lsda && + // If we've gotten to this point, we don't have an LSDA, which should + // also imply that we don't have a personality function, since in all + // likelihood a personality function needs the LSDA to do anything + // useful. It can be technically valid to have a personality function + // and no LSDA though (e.g. the C++ personality __gxx_personality_v0 + // is just a no-op without LSDA), so we still check for personality + // function equivalence to handle that case. + cuEntries[*foldBegin].personality == + cuEntries[*foldEnd].personality && + canFoldEncoding(cuEntries[*foldEnd].encoding)) ; *foldWrite++ = *foldBegin; foldBegin = foldEnd; } - cuPtrVector.erase(foldWrite, cuPtrVector.end()); + cuIndices.erase(foldWrite, cuIndices.end()); - encodePersonalities(cuPtrVector, personalities); + encodePersonalities(); // Count frequencies of the folded encodings EncodingMap encodingFrequencies; - for (const CompactUnwindEntry *cuPtrEntry : cuPtrVector) - encodingFrequencies[cuPtrEntry->encoding]++; + for (size_t idx : cuIndices) + encodingFrequencies[cuEntries[idx].encoding]++; // Make a vector of encodings, sorted by descending frequency for (const auto &frequency : encodingFrequencies) @@ -481,19 +551,21 @@ template void UnwindInfoSectionImpl::finalize() { // and 127..255 references a local per-second-level-page table. // First we try the compact format and determine how many entries fit. // If more entries fit in the regular format, we use that. - for (size_t i = 0; i < cuPtrVector.size();) { + for (size_t i = 0; i < cuIndices.size();) { + size_t idx = cuIndices[i]; secondLevelPages.emplace_back(); SecondLevelPage &page = secondLevelPages.back(); page.entryIndex = i; - uintptr_t functionAddressMax = - cuPtrVector[i]->functionAddress + COMPRESSED_ENTRY_FUNC_OFFSET_MASK; + uint64_t functionAddressMax = + cuEntries[idx].functionAddress + COMPRESSED_ENTRY_FUNC_OFFSET_MASK; size_t n = commonEncodings.size(); size_t wordsRemaining = SECOND_LEVEL_PAGE_WORDS - sizeof(unwind_info_compressed_second_level_page_header) / sizeof(uint32_t); - while (wordsRemaining >= 1 && i < cuPtrVector.size()) { - const CompactUnwindEntry *cuPtr = cuPtrVector[i]; + while (wordsRemaining >= 1 && i < cuIndices.size()) { + idx = cuIndices[i]; + const CompactUnwindEntry *cuPtr = &cuEntries[idx]; if (cuPtr->functionAddress >= functionAddressMax) { break; } else if (commonEncodingIndexes.count(cuPtr->encoding) || @@ -511,47 +583,43 @@ template void UnwindInfoSectionImpl::finalize() { } page.entryCount = i - page.entryIndex; - // If this is not the final page, see if it's possible to fit more - // entries by using the regular format. This can happen when there - // are many unique encodings, and we we saturated the local - // encoding table early. - if (i < cuPtrVector.size() && + // If this is not the final page, see if it's possible to fit more entries + // by using the regular format. This can happen when there are many unique + // encodings, and we saturated the local encoding table early. + if (i < cuIndices.size() && page.entryCount < REGULAR_SECOND_LEVEL_ENTRIES_MAX) { page.kind = UNWIND_SECOND_LEVEL_REGULAR; page.entryCount = std::min(REGULAR_SECOND_LEVEL_ENTRIES_MAX, - cuPtrVector.size() - page.entryIndex); + cuIndices.size() - page.entryIndex); i = page.entryIndex + page.entryCount; } else { page.kind = UNWIND_SECOND_LEVEL_COMPRESSED; } } - for (const CompactUnwindEntry *cu : cuPtrVector) { - uint32_t functionOffset = cu->functionAddress - in.header->addr; - functionToLsdaIndex[functionOffset] = lsdaEntries.size(); - if (cu->lsda != 0) - lsdaEntries.push_back( - {functionOffset, static_cast(cu->lsda - in.header->addr)}); + for (size_t idx : cuIndices) { + lsdaIndex[idx] = entriesWithLsda.size(); + if (cuEntries[idx].lsda) + entriesWithLsda.push_back(idx); } // compute size of __TEXT,__unwind_info section - level2PagesOffset = - sizeof(unwind_info_section_header) + - commonEncodings.size() * sizeof(uint32_t) + - personalities.size() * sizeof(uint32_t) + - // The extra second-level-page entry is for the sentinel - (secondLevelPages.size() + 1) * - sizeof(unwind_info_section_header_index_entry) + - lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry); + level2PagesOffset = sizeof(unwind_info_section_header) + + commonEncodings.size() * sizeof(uint32_t) + + personalities.size() * sizeof(uint32_t) + + // The extra second-level-page entry is for the sentinel + (secondLevelPages.size() + 1) * + sizeof(unwind_info_section_header_index_entry) + + entriesWithLsda.size() * + sizeof(unwind_info_section_header_lsda_index_entry); unwindInfoSize = level2PagesOffset + secondLevelPages.size() * SECOND_LEVEL_PAGE_BYTES; } // All inputs are relocated and output addresses are known, so write! -template -void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { - assert(!cuPtrVector.empty() && "call only if there is unwind info"); +void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { + assert(!cuIndices.empty() && "call only if there is unwind info"); // section header auto *uip = reinterpret_cast(buf); @@ -572,9 +640,11 @@ void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { *i32p++ = encoding.first; // Personalities - for (const uint32_t &personality : personalities) - *i32p++ = - in.got->addr + (personality - 1) * target->wordSize - in.header->addr; + for (const Symbol *personality : personalities) + *i32p++ = personality->getGotVA() - in.header->addr; + + // FIXME: LD64 checks and warns aboutgaps or overlapse in cuEntries address + // ranges. We should do the same too // Level-1 index uint32_t lsdaOffset = @@ -583,39 +653,42 @@ void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { uint64_t l2PagesOffset = level2PagesOffset; auto *iep = reinterpret_cast(i32p); for (const SecondLevelPage &page : secondLevelPages) { - iep->functionOffset = - cuPtrVector[page.entryIndex]->functionAddress - in.header->addr; + size_t idx = cuIndices[page.entryIndex]; + iep->functionOffset = cuEntries[idx].functionAddress - in.header->addr; iep->secondLevelPagesSectionOffset = l2PagesOffset; iep->lsdaIndexArraySectionOffset = - lsdaOffset + functionToLsdaIndex.lookup(iep->functionOffset) * + lsdaOffset + lsdaIndex.lookup(idx) * sizeof(unwind_info_section_header_lsda_index_entry); iep++; l2PagesOffset += SECOND_LEVEL_PAGE_BYTES; } // Level-1 sentinel - const CompactUnwindEntry &cuEnd = *cuPtrVector.back(); - assert(cuEnd.functionAddress != TombstoneValue); - iep->functionOffset = - cuEnd.functionAddress - in.header->addr + cuEnd.functionLength; + // XXX(vyng): Note that LD64 adds +1 here. + // Unsure whether it's a bug or it's their workaround for something else. + // See comments from https://reviews.llvm.org/D138320. + iep->functionOffset = cueEndBoundary - in.header->addr; iep->secondLevelPagesSectionOffset = 0; iep->lsdaIndexArraySectionOffset = - lsdaOffset + - lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry); + lsdaOffset + entriesWithLsda.size() * + sizeof(unwind_info_section_header_lsda_index_entry); iep++; // LSDAs - size_t lsdaBytes = - lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry); - if (lsdaBytes > 0) - memcpy(iep, lsdaEntries.data(), lsdaBytes); + auto *lep = + reinterpret_cast(iep); + for (size_t idx : entriesWithLsda) { + const CompactUnwindEntry &cu = cuEntries[idx]; + lep->lsdaOffset = cu.lsda->getVA(/*off=*/0) - in.header->addr; + lep->functionOffset = cu.functionAddress - in.header->addr; + lep++; + } // Level-2 pages - auto *pp = reinterpret_cast(reinterpret_cast(iep) + - lsdaBytes); + auto *pp = reinterpret_cast(lep); for (const SecondLevelPage &page : secondLevelPages) { if (page.kind == UNWIND_SECOND_LEVEL_COMPRESSED) { uintptr_t functionAddressBase = - cuPtrVector[page.entryIndex]->functionAddress; + cuEntries[cuIndices[page.entryIndex]].functionAddress; auto *p2p = reinterpret_cast( pp); @@ -628,14 +701,15 @@ void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { p2p->encodingsCount = page.localEncodings.size(); auto *ep = reinterpret_cast(&p2p[1]); for (size_t i = 0; i < page.entryCount; i++) { - const CompactUnwindEntry *cuep = cuPtrVector[page.entryIndex + i]; - auto it = commonEncodingIndexes.find(cuep->encoding); + const CompactUnwindEntry &cue = + cuEntries[cuIndices[page.entryIndex + i]]; + auto it = commonEncodingIndexes.find(cue.encoding); if (it == commonEncodingIndexes.end()) - it = page.localEncodingIndexes.find(cuep->encoding); + it = page.localEncodingIndexes.find(cue.encoding); *ep++ = (it->second << COMPRESSED_ENTRY_FUNC_OFFSET_BITS) | - (cuep->functionAddress - functionAddressBase); + (cue.functionAddress - functionAddressBase); } - if (page.localEncodings.size() != 0) + if (!page.localEncodings.empty()) memcpy(ep, page.localEncodings.data(), page.localEncodings.size() * sizeof(uint32_t)); } else { @@ -647,9 +721,10 @@ void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { p2p->entryCount = page.entryCount; auto *ep = reinterpret_cast(&p2p[1]); for (size_t i = 0; i < page.entryCount; i++) { - const CompactUnwindEntry *cuep = cuPtrVector[page.entryIndex + i]; - *ep++ = cuep->functionAddress; - *ep++ = cuep->encoding; + const CompactUnwindEntry &cue = + cuEntries[cuIndices[page.entryIndex + i]]; + *ep++ = cue.functionAddress; + *ep++ = cue.encoding; } } pp += SECOND_LEVEL_PAGE_WORDS; @@ -657,8 +732,5 @@ void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const { } UnwindInfoSection *macho::makeUnwindInfoSection() { - if (target->wordSize == 8) - return make>(); - else - return make>(); + return make(); } diff --git a/gnu/llvm/lld/MachO/UnwindInfoSection.h b/gnu/llvm/lld/MachO/UnwindInfoSection.h index fca11de6eeb..826573b0c44 100644 --- a/gnu/llvm/lld/MachO/UnwindInfoSection.h +++ b/gnu/llvm/lld/MachO/UnwindInfoSection.h @@ -11,44 +11,29 @@ #include "ConcatOutputSection.h" #include "SyntheticSections.h" +#include "llvm/ADT/MapVector.h" -#include "mach-o/compact_unwind_encoding.h" - -namespace lld { -namespace macho { - -template struct CompactUnwindEntry { - Ptr functionAddress; - uint32_t functionLength; - compact_unwind_encoding_t encoding; - Ptr personality; - Ptr lsda; -}; +namespace lld::macho { class UnwindInfoSection : public SyntheticSection { public: - bool isNeeded() const override { - return !compactUnwindSection->inputs.empty() && !allEntriesAreOmitted; - } - uint64_t getSize() const override { return unwindInfoSize; } - virtual void addInput(ConcatInputSection *) = 0; - std::vector getInputs() { - return compactUnwindSection->inputs; - } - void prepareRelocations(); + // If all functions are free of unwind info, we can omit the unwind info + // section entirely. + bool isNeeded() const override { return !allEntriesAreOmitted; } + void addSymbol(const Defined *); + virtual void prepare() = 0; protected: UnwindInfoSection(); - virtual void prepareRelocations(ConcatInputSection *) = 0; - ConcatOutputSection *compactUnwindSection; - uint64_t unwindInfoSize = 0; + llvm::MapVector, + const Defined *> + symbols; bool allEntriesAreOmitted = true; }; UnwindInfoSection *makeUnwindInfoSection(); -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MachO/Writer.cpp b/gnu/llvm/lld/MachO/Writer.cpp index d9c9cf57005..a09920687c5 100644 --- a/gnu/llvm/lld/MachO/Writer.cpp +++ b/gnu/llvm/lld/MachO/Writer.cpp @@ -14,6 +14,7 @@ #include "MapFile.h" #include "OutputSection.h" #include "OutputSegment.h" +#include "SectionPriorities.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" @@ -21,14 +22,13 @@ #include "UnwindInfoSection.h" #include "lld/Common/Arrays.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/LEB128.h" -#include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" +#include "llvm/Support/ThreadPool.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" @@ -58,12 +58,15 @@ public: void openFile(); void writeSections(); + void applyOptimizationHints(); + void buildFixupChains(); void writeUuid(); void writeCodeSignature(); void writeOutputFile(); template void run(); + ThreadPool threadPool; std::unique_ptr &buffer; uint64_t addr = 0; uint64_t fileOff = 0; @@ -130,8 +133,8 @@ public: LCSubFramework(StringRef umbrella) : umbrella(umbrella) {} uint32_t getSize() const override { - return alignTo(sizeof(sub_framework_command) + umbrella.size() + 1, - target->wordSize); + return alignToPowerOf2(sizeof(sub_framework_command) + umbrella.size() + 1, + target->wordSize); } void writeTo(uint8_t *buf) const override { @@ -227,7 +230,7 @@ public: void writeTo(uint8_t *buf) const override { using SegmentCommand = typename LP::segment_command; - using Section = typename LP::section; + using SectionHeader = typename LP::section; auto *c = reinterpret_cast(buf); buf += sizeof(SegmentCommand); @@ -243,13 +246,14 @@ public: c->vmsize = seg->vmSize; c->filesize = seg->fileSize; c->nsects = seg->numNonHiddenSections(); + c->flags = seg->flags; for (const OutputSection *osec : seg->getSections()) { if (osec->isHidden()) continue; - auto *sectHdr = reinterpret_cast
(buf); - buf += sizeof(Section); + auto *sectHdr = reinterpret_cast(buf); + buf += sizeof(SectionHeader); memcpy(sectHdr->sectname, osec->name.data(), osec->name.size()); memcpy(sectHdr->segname, name.data(), name.size()); @@ -324,7 +328,8 @@ public: } uint32_t getSize() const override { - return alignTo(sizeof(dylib_command) + path.size() + 1, 8); + return alignToPowerOf2(sizeof(dylib_command) + path.size() + 1, + target->wordSize); } void writeTo(uint8_t *buf) const override { @@ -343,6 +348,7 @@ public: } static uint32_t getInstanceCount() { return instanceCount; } + static void resetInstanceCount() { instanceCount = 0; } private: LoadCommandType type; @@ -357,7 +363,8 @@ uint32_t LCDylib::instanceCount = 0; class LCLoadDylinker final : public LoadCommand { public: uint32_t getSize() const override { - return alignTo(sizeof(dylinker_command) + path.size() + 1, 8); + return alignToPowerOf2(sizeof(dylinker_command) + path.size() + 1, + target->wordSize); } void writeTo(uint8_t *buf) const override { @@ -383,7 +390,8 @@ public: explicit LCRPath(StringRef path) : path(path) {} uint32_t getSize() const override { - return alignTo(sizeof(rpath_command) + path.size() + 1, target->wordSize); + return alignToPowerOf2(sizeof(rpath_command) + path.size() + 1, + target->wordSize); } void writeTo(uint8_t *buf) const override { @@ -402,6 +410,31 @@ private: StringRef path; }; +class LCDyldEnv final : public LoadCommand { +public: + explicit LCDyldEnv(StringRef name) : name(name) {} + + uint32_t getSize() const override { + return alignToPowerOf2(sizeof(dyld_env_command) + name.size() + 1, + target->wordSize); + } + + void writeTo(uint8_t *buf) const override { + auto *c = reinterpret_cast(buf); + buf += sizeof(dyld_env_command); + + c->cmd = LC_DYLD_ENVIRONMENT; + c->cmdsize = getSize(); + c->name = sizeof(dyld_env_command); + + memcpy(buf, name.data(), name.size()); + buf[name.size()] = '\0'; + } + +private: + StringRef name; +}; + class LCMinVersion final : public LoadCommand { public: explicit LCMinVersion(const PlatformInfo &platformInfo) @@ -412,19 +445,19 @@ public: void writeTo(uint8_t *buf) const override { auto *c = reinterpret_cast(buf); switch (platformInfo.target.Platform) { - case PlatformKind::macOS: + case PLATFORM_MACOS: c->cmd = LC_VERSION_MIN_MACOSX; break; - case PlatformKind::iOS: - case PlatformKind::iOSSimulator: + case PLATFORM_IOS: + case PLATFORM_IOSSIMULATOR: c->cmd = LC_VERSION_MIN_IPHONEOS; break; - case PlatformKind::tvOS: - case PlatformKind::tvOSSimulator: + case PLATFORM_TVOS: + case PLATFORM_TVOSSIMULATOR: c->cmd = LC_VERSION_MIN_TVOS; break; - case PlatformKind::watchOS: - case PlatformKind::watchOSSimulator: + case PLATFORM_WATCHOS: + case PLATFORM_WATCHOSSIMULATOR: c->cmd = LC_VERSION_MIN_WATCHOS; break; default: @@ -455,9 +488,11 @@ public: auto *c = reinterpret_cast(buf); c->cmd = LC_BUILD_VERSION; c->cmdsize = getSize(); + c->platform = static_cast(platformInfo.target.Platform); c->minos = encodeVersion(platformInfo.minimum); c->sdk = encodeVersion(platformInfo.sdk); + c->ntools = ntools; auto *t = reinterpret_cast(&c[1]); t->tool = TOOL_LD; @@ -546,6 +581,40 @@ public: CodeSignatureSection *section; }; +class LCExportsTrie final : public LoadCommand { +public: + LCExportsTrie(ExportSection *section) : section(section) {} + + uint32_t getSize() const override { return sizeof(linkedit_data_command); } + + void writeTo(uint8_t *buf) const override { + auto *c = reinterpret_cast(buf); + c->cmd = LC_DYLD_EXPORTS_TRIE; + c->cmdsize = getSize(); + c->dataoff = section->fileOff; + c->datasize = section->getSize(); + } + + ExportSection *section; +}; + +class LCChainedFixups final : public LoadCommand { +public: + LCChainedFixups(ChainedFixupsSection *section) : section(section) {} + + uint32_t getSize() const override { return sizeof(linkedit_data_command); } + + void writeTo(uint8_t *buf) const override { + auto *c = reinterpret_cast(buf); + c->cmd = LC_DYLD_CHAINED_FIXUPS; + c->cmdsize = getSize(); + c->dataoff = section->fileOff; + c->datasize = section->getSize(); + } + + ChainedFixupsSection *section; +}; + } // namespace void Writer::treatSpecialUndefineds() { @@ -569,50 +638,14 @@ void Writer::treatSpecialUndefineds() { } } -// Add stubs and bindings where necessary (e.g. if the symbol is a -// DylibSymbol.) -static void prepareBranchTarget(Symbol *sym) { - if (auto *dysym = dyn_cast(sym)) { - if (in.stubs->addEntry(dysym)) { - if (sym->isWeakDef()) { - in.binding->addEntry(dysym, in.lazyPointers->isec, - sym->stubsIndex * target->wordSize); - in.weakBinding->addEntry(sym, in.lazyPointers->isec, - sym->stubsIndex * target->wordSize); - } else { - in.lazyBinding->addEntry(dysym); - } - } - } else if (auto *defined = dyn_cast(sym)) { - if (defined->isExternalWeakDef()) { - if (in.stubs->addEntry(sym)) { - in.rebase->addEntry(in.lazyPointers->isec, - sym->stubsIndex * target->wordSize); - in.weakBinding->addEntry(sym, in.lazyPointers->isec, - sym->stubsIndex * target->wordSize); - } - } - } else { - llvm_unreachable("invalid branch target symbol type"); - } -} - -// Can a symbol's address can only be resolved at runtime? -static bool needsBinding(const Symbol *sym) { - if (isa(sym)) - return true; - if (const auto *defined = dyn_cast(sym)) - return defined->isExternalWeakDef(); - return false; -} - static void prepareSymbolRelocation(Symbol *sym, const InputSection *isec, - const Reloc &r) { + const lld::macho::Reloc &r) { assert(sym->isLive()); const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type); if (relocAttrs.hasAttr(RelocAttrBits::BRANCH)) { - prepareBranchTarget(sym); + if (needsBinding(sym)) + in.stubs->addEntry(sym); } else if (relocAttrs.hasAttr(RelocAttrBits::GOT)) { if (relocAttrs.hasAttr(RelocAttrBits::POINTER) || needsBinding(sym)) in.got->addEntry(sym); @@ -640,7 +673,7 @@ void Writer::scanRelocations() { continue; for (auto it = isec->relocs.begin(); it != isec->relocs.end(); ++it) { - Reloc &r = *it; + lld::macho::Reloc &r = *it; if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) { // Skip over the following UNSIGNED relocation -- it's just there as the // minuend, and doesn't have the usual UNSIGNED semantics. We don't want @@ -650,7 +683,7 @@ void Writer::scanRelocations() { } if (auto *sym = r.referent.dyn_cast()) { if (auto *undefined = dyn_cast(sym)) - treatUndefinedSymbol(*undefined); + treatUndefinedSymbol(*undefined, isec, r.offset); // treatUndefinedSymbol() can replace sym with a DylibSymbol; re-check. if (!isa(sym) && validateSymbolRelocation(sym, isec, r)) prepareSymbolRelocation(sym, isec, r); @@ -660,41 +693,74 @@ void Writer::scanRelocations() { // too... auto *referentIsec = r.referent.get(); r.referent = referentIsec->canonical(); - if (!r.pcrel) - in.rebase->addEntry(isec, r.offset); + if (!r.pcrel) { + if (config->emitChainedFixups) + in.chainedFixups->addRebase(isec, r.offset); + else + in.rebase->addEntry(isec, r.offset); + } } } } - in.unwindInfo->prepareRelocations(); + in.unwindInfo->prepare(); +} + +static void addNonWeakDefinition(const Defined *defined) { + if (config->emitChainedFixups) + in.chainedFixups->setHasNonWeakDefinition(); + else + in.weakBinding->addNonWeakDefinition(defined); } void Writer::scanSymbols() { TimeTraceScope timeScope("Scan symbols"); - for (const Symbol *sym : symtab->getSymbols()) { - if (const auto *defined = dyn_cast(sym)) { - if (defined->overridesWeakDef && defined->isLive()) - in.weakBinding->addNonWeakDefinition(defined); + for (Symbol *sym : symtab->getSymbols()) { + if (auto *defined = dyn_cast(sym)) { + if (!defined->isLive()) + continue; + defined->canonicalize(); + if (defined->overridesWeakDef) + addNonWeakDefinition(defined); + if (!defined->isAbsolute() && isCodeSection(defined->isec)) + in.unwindInfo->addSymbol(defined); } else if (const auto *dysym = dyn_cast(sym)) { // This branch intentionally doesn't check isLive(). if (dysym->isDynamicLookup()) continue; dysym->getFile()->refState = std::max(dysym->getFile()->refState, dysym->getRefState()); + } else if (isa(sym)) { + if (sym->getName().startswith(ObjCStubsSection::symbolPrefix)) + in.objcStubs->addEntry(sym); } } + + for (const InputFile *file : inputFiles) { + if (auto *objFile = dyn_cast(file)) + for (Symbol *sym : objFile->symbols) { + if (auto *defined = dyn_cast_or_null(sym)) { + if (!defined->isLive()) + continue; + defined->canonicalize(); + if (!defined->isExternal() && !defined->isAbsolute() && + isCodeSection(defined->isec)) + in.unwindInfo->addSymbol(defined); + } + } + } } // TODO: ld64 enforces the old load commands in a few other cases. static bool useLCBuildVersion(const PlatformInfo &platformInfo) { - static const std::vector> minVersion = { - {PlatformKind::macOS, VersionTuple(10, 14)}, - {PlatformKind::iOS, VersionTuple(12, 0)}, - {PlatformKind::iOSSimulator, VersionTuple(13, 0)}, - {PlatformKind::tvOS, VersionTuple(12, 0)}, - {PlatformKind::tvOSSimulator, VersionTuple(13, 0)}, - {PlatformKind::watchOS, VersionTuple(5, 0)}, - {PlatformKind::watchOSSimulator, VersionTuple(6, 0)}}; + static const std::array, 7> minVersion = + {{{PLATFORM_MACOS, VersionTuple(10, 14)}, + {PLATFORM_IOS, VersionTuple(12, 0)}, + {PLATFORM_IOSSIMULATOR, VersionTuple(13, 0)}, + {PLATFORM_TVOS, VersionTuple(12, 0)}, + {PLATFORM_TVOSSIMULATOR, VersionTuple(13, 0)}, + {PLATFORM_WATCHOS, VersionTuple(5, 0)}, + {PLATFORM_WATCHOSSIMULATOR, VersionTuple(6, 0)}}}; auto it = llvm::find_if(minVersion, [&](const auto &p) { return p.first == platformInfo.target.Platform; }); @@ -708,8 +774,13 @@ template void Writer::createLoadCommands() { seg->index = segIndex++; } - in.header->addLoadCommand(make( - in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports)); + if (config->emitChainedFixups) { + in.header->addLoadCommand(make(in.chainedFixups)); + in.header->addLoadCommand(make(in.exports)); + } else { + in.header->addLoadCommand(make( + in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports)); + } in.header->addLoadCommand(make(symtabSection, stringTableSection)); in.header->addLoadCommand( make(symtabSection, indirectSymtabSection)); @@ -743,76 +814,94 @@ template void Writer::createLoadCommands() { else in.header->addLoadCommand(make(config->platformInfo)); + if (config->secondaryPlatformInfo) { + in.header->addLoadCommand( + make(*config->secondaryPlatformInfo)); + } + // This is down here to match ld64's load command order. if (config->outputType == MH_EXECUTE) in.header->addLoadCommand(make()); + // See ld64's OutputFile::buildDylibOrdinalMapping for the corresponding + // library ordinal computation code in ld64. int64_t dylibOrdinal = 1; DenseMap ordinalForInstallName; + + std::vector dylibFiles; for (InputFile *file : inputFiles) { - if (auto *dylibFile = dyn_cast(file)) { - if (dylibFile->isBundleLoader) { - dylibFile->ordinal = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; - // Shortcut since bundle-loader does not re-export the symbols. + if (auto *dylibFile = dyn_cast(file)) + dylibFiles.push_back(dylibFile); + } + for (size_t i = 0; i < dylibFiles.size(); ++i) + dylibFiles.insert(dylibFiles.end(), dylibFiles[i]->extraDylibs.begin(), + dylibFiles[i]->extraDylibs.end()); - dylibFile->reexport = false; - continue; - } + for (DylibFile *dylibFile : dylibFiles) { + if (dylibFile->isBundleLoader) { + dylibFile->ordinal = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; + // Shortcut since bundle-loader does not re-export the symbols. - // Don't emit load commands for a dylib that is not referenced if: - // - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER -- - // if it's on the linker command line, it's explicit) - // - or it's marked MH_DEAD_STRIPPABLE_DYLIB - // - or the flag -dead_strip_dylibs is used - // FIXME: `isReferenced()` is currently computed before dead code - // stripping, so references from dead code keep a dylib alive. This - // matches ld64, but it's something we should do better. - if (!dylibFile->isReferenced() && !dylibFile->forceNeeded && - (!dylibFile->explicitlyLinked || dylibFile->deadStrippable || - config->deadStripDylibs)) - continue; + dylibFile->reexport = false; + continue; + } - // Several DylibFiles can have the same installName. Only emit a single - // load command for that installName and give all these DylibFiles the - // same ordinal. - // This can happen in several cases: - // - a new framework could change its installName to an older - // framework name via an $ld$ symbol depending on platform_version - // - symlinks (for example, libpthread.tbd is a symlink to libSystem.tbd; - // Foo.framework/Foo.tbd is usually a symlink to - // Foo.framework/Versions/Current/Foo.tbd, where - // Foo.framework/Versions/Current is usually a symlink to - // Foo.framework/Versions/A) - // - a framework can be linked both explicitly on the linker - // command line and implicitly as a reexport from a different - // framework. The re-export will usually point to the tbd file - // in Foo.framework/Versions/A/Foo.tbd, while the explicit link will - // usually find Foo.framework/Foo.tbd. These are usually symlinks, - // but in a --reproduce archive they will be identical but distinct - // files. - // In the first case, *semantically distinct* DylibFiles will have the - // same installName. - int64_t &ordinal = ordinalForInstallName[dylibFile->installName]; - if (ordinal) { - dylibFile->ordinal = ordinal; - continue; - } + // Don't emit load commands for a dylib that is not referenced if: + // - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER -- + // if it's on the linker command line, it's explicit) + // - or it's marked MH_DEAD_STRIPPABLE_DYLIB + // - or the flag -dead_strip_dylibs is used + // FIXME: `isReferenced()` is currently computed before dead code + // stripping, so references from dead code keep a dylib alive. This + // matches ld64, but it's something we should do better. + if (!dylibFile->isReferenced() && !dylibFile->forceNeeded && + (!dylibFile->isExplicitlyLinked() || dylibFile->deadStrippable || + config->deadStripDylibs)) + continue; - ordinal = dylibFile->ordinal = dylibOrdinal++; - LoadCommandType lcType = - dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak - ? LC_LOAD_WEAK_DYLIB - : LC_LOAD_DYLIB; - in.header->addLoadCommand(make(lcType, dylibFile->installName, - dylibFile->compatibilityVersion, - dylibFile->currentVersion)); - - if (dylibFile->reexport) - in.header->addLoadCommand( - make(LC_REEXPORT_DYLIB, dylibFile->installName)); + // Several DylibFiles can have the same installName. Only emit a single + // load command for that installName and give all these DylibFiles the + // same ordinal. + // This can happen in several cases: + // - a new framework could change its installName to an older + // framework name via an $ld$ symbol depending on platform_version + // - symlinks (for example, libpthread.tbd is a symlink to libSystem.tbd; + // Foo.framework/Foo.tbd is usually a symlink to + // Foo.framework/Versions/Current/Foo.tbd, where + // Foo.framework/Versions/Current is usually a symlink to + // Foo.framework/Versions/A) + // - a framework can be linked both explicitly on the linker + // command line and implicitly as a reexport from a different + // framework. The re-export will usually point to the tbd file + // in Foo.framework/Versions/A/Foo.tbd, while the explicit link will + // usually find Foo.framework/Foo.tbd. These are usually symlinks, + // but in a --reproduce archive they will be identical but distinct + // files. + // In the first case, *semantically distinct* DylibFiles will have the + // same installName. + int64_t &ordinal = ordinalForInstallName[dylibFile->installName]; + if (ordinal) { + dylibFile->ordinal = ordinal; + continue; } + + ordinal = dylibFile->ordinal = dylibOrdinal++; + LoadCommandType lcType = + dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak + ? LC_LOAD_WEAK_DYLIB + : LC_LOAD_DYLIB; + in.header->addLoadCommand(make(lcType, dylibFile->installName, + dylibFile->compatibilityVersion, + dylibFile->currentVersion)); + + if (dylibFile->reexport) + in.header->addLoadCommand( + make(LC_REEXPORT_DYLIB, dylibFile->installName)); } + for (const auto &dyldEnv : config->dyldEnvs) + in.header->addLoadCommand(make(dyldEnv)); + if (functionStartsSection) in.header->addLoadCommand(make(functionStartsSection)); if (dataInCodeSection) @@ -827,52 +916,6 @@ template void Writer::createLoadCommands() { : 0)); } -static size_t getSymbolPriority(const SymbolPriorityEntry &entry, - const InputFile *f) { - // We don't use toString(InputFile *) here because it returns the full path - // for object files, and we only want the basename. - StringRef filename; - if (f->archiveName.empty()) - filename = path::filename(f->getName()); - else - filename = saver.save(path::filename(f->archiveName) + "(" + - path::filename(f->getName()) + ")"); - return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile); -} - -// Each section gets assigned the priority of the highest-priority symbol it -// contains. -static DenseMap buildInputSectionPriorities() { - DenseMap sectionPriorities; - - if (config->priorities.empty()) - return sectionPriorities; - - auto addSym = [&](Defined &sym) { - if (sym.isAbsolute()) - return; - - auto it = config->priorities.find(sym.getName()); - if (it == config->priorities.end()) - return; - - SymbolPriorityEntry &entry = it->second; - size_t &priority = sectionPriorities[sym.isec]; - priority = - std::max(priority, getSymbolPriority(entry, sym.isec->getFile())); - }; - - // TODO: Make sure this handles weak symbols correctly. - for (const InputFile *file : inputFiles) { - if (isa(file)) - for (Symbol *sym : file->symbols) - if (auto *d = dyn_cast_or_null(sym)) - addSym(*d); - } - - return sectionPriorities; -} - // Sorting only can happen once all outputs have been collected. Here we sort // segments, output sections within each segment, and input sections within each // output segment. @@ -881,25 +924,40 @@ static void sortSegmentsAndSections() { sortOutputSegments(); DenseMap isecPriorities = - buildInputSectionPriorities(); + priorityBuilder.buildInputSectionPriorities(); uint32_t sectionIndex = 0; for (OutputSegment *seg : outputSegments) { seg->sortOutputSections(); + // References from thread-local variable sections are treated as offsets + // relative to the start of the thread-local data memory area, which + // is initialized via copying all the TLV data sections (which are all + // contiguous). If later data sections require a greater alignment than + // earlier ones, the offsets of data within those sections won't be + // guaranteed to aligned unless we normalize alignments. We therefore use + // the largest alignment for all TLV data sections. + uint32_t tlvAlign = 0; + for (const OutputSection *osec : seg->getSections()) + if (isThreadLocalData(osec->flags) && osec->align > tlvAlign) + tlvAlign = osec->align; + for (OutputSection *osec : seg->getSections()) { // Now that the output sections are sorted, assign the final // output section indices. if (!osec->isHidden()) osec->index = ++sectionIndex; - if (!firstTLVDataSection && isThreadLocalData(osec->flags)) - firstTLVDataSection = osec; + if (isThreadLocalData(osec->flags)) { + if (!firstTLVDataSection) + firstTLVDataSection = osec; + osec->align = tlvAlign; + } if (!isecPriorities.empty()) { if (auto *merged = dyn_cast(osec)) { - llvm::stable_sort(merged->inputs, - [&](InputSection *a, InputSection *b) { - return isecPriorities[a] > isecPriorities[b]; - }); + llvm::stable_sort( + merged->inputs, [&](InputSection *a, InputSection *b) { + return isecPriorities.lookup(a) > isecPriorities.lookup(b); + }); } } } @@ -948,13 +1006,30 @@ template void Writer::createOutputSections() { StringRef segname = it.first.first; ConcatOutputSection *osec = it.second; assert(segname != segment_names::ld); - if (osec->isNeeded()) + if (osec->isNeeded()) { + // See comment in ObjFile::splitEhFrames() + if (osec->name == section_names::ehFrame && + segname == segment_names::text) + osec->align = target->wordSize; + + // MC keeps the default 1-byte alignment for __thread_vars, even though it + // contains pointers that are fixed up by dyld, which requires proper + // alignment. + if (isThreadLocalVariables(osec->flags)) + osec->align = std::max(osec->align, target->wordSize); + getOrCreateOutputSegment(segname)->addOutputSection(osec); + } } for (SyntheticSection *ssec : syntheticSections) { auto it = concatOutputSections.find({ssec->segname, ssec->name}); - if (ssec->isNeeded()) { + // We add all LinkEdit sections here because we don't know if they are + // needed until their finalizeContents() methods get called later. While + // this means that we add some redundant sections to __LINKEDIT, there is + // is no redundancy in the output, as we do not emit section headers for + // any LinkEdit sections. + if (ssec->isNeeded() || ssec->segname == segment_names::linkEdit) { if (it == concatOutputSections.end()) { getOrCreateOutputSegment(ssec->segname)->addOutputSection(ssec); } else { @@ -973,6 +1048,21 @@ template void Writer::createOutputSections() { void Writer::finalizeAddresses() { TimeTraceScope timeScope("Finalize addresses"); uint64_t pageSize = target->getPageSize(); + + // We could parallelize this loop, but local benchmarking indicates it is + // faster to do it all in the main thread. + for (OutputSegment *seg : outputSegments) { + if (seg == linkEditSegment) + continue; + for (OutputSection *osec : seg->getSections()) { + if (!osec->isNeeded()) + continue; + // Other kinds of OutputSections have already been finalized. + if (auto concatOsec = dyn_cast(osec)) + concatOsec->finalizeContents(); + } + } + // Ensure that segments (and the sections they contain) are allocated // addresses in ascending order, which dyld requires. // @@ -988,8 +1078,8 @@ void Writer::finalizeAddresses() { // `fileOff + fileSize == next segment fileOff`. So we call alignTo() before // (instead of after) computing fileSize to ensure that the segments are // contiguous. We handle addr / vmSize similarly for the same reason. - fileOff = alignTo(fileOff, pageSize); - addr = alignTo(addr, pageSize); + fileOff = alignToPowerOf2(fileOff, pageSize); + addr = alignToPowerOf2(addr, pageSize); seg->vmSize = addr - seg->addr; seg->fileSize = fileOff - seg->fileOff; seg->assignAddressesToStartEndSymbols(); @@ -999,21 +1089,21 @@ void Writer::finalizeAddresses() { void Writer::finalizeLinkEditSegment() { TimeTraceScope timeScope("Finalize __LINKEDIT segment"); // Fill __LINKEDIT contents. - std::vector linkEditSections{ - in.rebase, - in.binding, - in.weakBinding, - in.lazyBinding, - in.exports, - symtabSection, - indirectSymtabSection, - dataInCodeSection, - functionStartsSection, + std::array linkEditSections{ + in.rebase, in.binding, + in.weakBinding, in.lazyBinding, + in.exports, in.chainedFixups, + symtabSection, indirectSymtabSection, + dataInCodeSection, functionStartsSection, }; - parallelForEach(linkEditSections, [](LinkEditSection *osec) { + SmallVector> threadFutures; + threadFutures.reserve(linkEditSections.size()); + for (LinkEditSection *osec : linkEditSections) if (osec) - osec->finalizeContents(); - }); + threadFutures.emplace_back(threadPool.async( + [](LinkEditSection *osec) { osec->finalizeContents(); }, osec)); + for (std::shared_future &future : threadFutures) + future.wait(); // Now that __LINKEDIT is filled out, do a proper calculation of its // addresses and offsets. @@ -1048,17 +1138,33 @@ void Writer::openFile() { FileOutputBuffer::F_executable); if (!bufferOrErr) - error("failed to open " + config->outputFile + ": " + + fatal("failed to open " + config->outputFile + ": " + llvm::toString(bufferOrErr.takeError())); - else - buffer = std::move(*bufferOrErr); + buffer = std::move(*bufferOrErr); + in.bufferStart = buffer->getBufferStart(); } void Writer::writeSections() { uint8_t *buf = buffer->getBufferStart(); + std::vector osecs; for (const OutputSegment *seg : outputSegments) - for (const OutputSection *osec : seg->getSections()) - osec->writeTo(buf + osec->fileOff); + append_range(osecs, seg->getSections()); + + parallelForEach(osecs.begin(), osecs.end(), [&](const OutputSection *osec) { + osec->writeTo(buf + osec->fileOff); + }); +} + +void Writer::applyOptimizationHints() { + if (config->arch() != AK_arm64 || config->ignoreOptimizationHints) + return; + + uint8_t *buf = buffer->getBufferStart(); + TimeTraceScope timeScope("Apply linker optimization hints"); + parallelForEach(inputFiles, [buf](const InputFile *file) { + if (const auto *objFile = dyn_cast(file)) + target->applyOptimizationHints(buf, *objFile); + }); } // In order to utilize multiple cores, we first split the buffer into chunks, @@ -1066,46 +1172,131 @@ void Writer::writeSections() { // values. void Writer::writeUuid() { TimeTraceScope timeScope("Computing UUID"); + ArrayRef data{buffer->getBufferStart(), buffer->getBufferEnd()}; unsigned chunkCount = parallel::strategy.compute_thread_count() * 10; // Round-up integer division size_t chunkSize = (data.size() + chunkCount - 1) / chunkCount; std::vector> chunks = split(data, chunkSize); - std::vector hashes(chunks.size()); - parallelForEachN(0, chunks.size(), - [&](size_t i) { hashes[i] = xxHash64(chunks[i]); }); + // Leave one slot for filename + std::vector hashes(chunks.size() + 1); + SmallVector> threadFutures; + threadFutures.reserve(chunks.size()); + for (size_t i = 0; i < chunks.size(); ++i) + threadFutures.emplace_back(threadPool.async( + [&](size_t j) { hashes[j] = xxHash64(chunks[j]); }, i)); + for (std::shared_future &future : threadFutures) + future.wait(); + // Append the output filename so that identical binaries with different names + // don't get the same UUID. + hashes[chunks.size()] = xxHash64(sys::path::filename(config->finalOutput)); uint64_t digest = xxHash64({reinterpret_cast(hashes.data()), hashes.size() * sizeof(uint64_t)}); uuidCommand->writeUuid(digest); } +// This is step 5 of the algorithm described in the class comment of +// ChainedFixupsSection. +void Writer::buildFixupChains() { + if (!config->emitChainedFixups) + return; + + const std::vector &loc = in.chainedFixups->getLocations(); + if (loc.empty()) + return; + + TimeTraceScope timeScope("Build fixup chains"); + + const uint64_t pageSize = target->getPageSize(); + constexpr uint32_t stride = 4; // for DYLD_CHAINED_PTR_64 + + for (size_t i = 0, count = loc.size(); i < count;) { + const OutputSegment *oseg = loc[i].isec->parent->parent; + uint8_t *buf = buffer->getBufferStart() + oseg->fileOff; + uint64_t pageIdx = loc[i].offset / pageSize; + ++i; + + while (i < count && loc[i].isec->parent->parent == oseg && + (loc[i].offset / pageSize) == pageIdx) { + uint64_t offset = loc[i].offset - loc[i - 1].offset; + + auto fail = [&](Twine message) { + error(loc[i].isec->getSegName() + "," + loc[i].isec->getName() + + ", offset " + + Twine(loc[i].offset - loc[i].isec->parent->getSegmentOffset()) + + ": " + message); + }; + + if (offset < target->wordSize) + return fail("fixups overlap"); + if (offset % stride != 0) + return fail( + "fixups are unaligned (offset " + Twine(offset) + + " is not a multiple of the stride). Re-link with -no_fixup_chains"); + + // The "next" field is in the same location for bind and rebase entries. + reinterpret_cast(buf + loc[i - 1].offset) + ->next = offset / stride; + ++i; + } + } +} + void Writer::writeCodeSignature() { - if (codeSignatureSection) + if (codeSignatureSection) { + TimeTraceScope timeScope("Write code signature"); codeSignatureSection->writeHashes(buffer->getBufferStart()); + } } void Writer::writeOutputFile() { TimeTraceScope timeScope("Write output file"); openFile(); + reportPendingUndefinedSymbols(); if (errorCount()) return; writeSections(); + applyOptimizationHints(); + buildFixupChains(); writeUuid(); writeCodeSignature(); if (auto e = buffer->commit()) - error("failed to write to the output file: " + toString(std::move(e))); + fatal("failed to write output '" + buffer->getPath() + + "': " + toString(std::move(e))); } template void Writer::run() { treatSpecialUndefineds(); - if (config->entry && !isa(config->entry)) - prepareBranchTarget(config->entry); - scanRelocations(); - if (in.stubHelper->isNeeded()) - in.stubHelper->setup(); + if (config->entry && needsBinding(config->entry)) + in.stubs->addEntry(config->entry); + + // Canonicalization of all pointers to InputSections should be handled by + // these two scan* methods. I.e. from this point onward, for all live + // InputSections, we should have `isec->canonical() == isec`. scanSymbols(); + if (in.objcStubs->isNeeded()) + in.objcStubs->setUp(); + scanRelocations(); + if (in.initOffsets->isNeeded()) + in.initOffsets->setUp(); + + // Do not proceed if there were undefined or duplicate symbols. + reportPendingUndefinedSymbols(); + reportPendingDuplicateSymbols(); + if (errorCount()) + return; + + if (in.stubHelper && in.stubHelper->isNeeded()) + in.stubHelper->setUp(); + + if (in.objCImageInfo->isNeeded()) + in.objCImageInfo->finalizeContents(); + + // At this point, we should know exactly which output sections are needed, + // courtesy of scanSymbols() and scanRelocations(). createOutputSections(); + // After this point, we create no new segments; HOWEVER, we might // yet create branch-range extension thunks for architectures whose // hardware call instructions have limited range, e.g., ARM(64). @@ -1114,42 +1305,58 @@ template void Writer::run() { sortSegmentsAndSections(); createLoadCommands(); finalizeAddresses(); + threadPool.async([&] { + if (LLVM_ENABLE_THREADS && config->timeTraceEnabled) + timeTraceProfilerInitialize(config->timeTraceGranularity, "writeMapFile"); + writeMapFile(); + if (LLVM_ENABLE_THREADS && config->timeTraceEnabled) + timeTraceProfilerFinishThread(); + }); finalizeLinkEditSegment(); - writeMapFile(); writeOutputFile(); } template void macho::writeResult() { Writer().run(); } +void macho::resetWriter() { LCDylib::resetInstanceCount(); } + void macho::createSyntheticSections() { in.header = make(); - if (config->dedupLiterals) { - in.cStringSection = make(); + if (config->dedupStrings) + in.cStringSection = + make(section_names::cString); + else + in.cStringSection = make(section_names::cString); + in.objcMethnameSection = + make(section_names::objcMethname); + in.wordLiteralSection = make(); + if (config->emitChainedFixups) { + in.chainedFixups = make(); } else { - in.cStringSection = make(); + in.rebase = make(); + in.binding = make(); + in.weakBinding = make(); + in.lazyBinding = make(); + in.lazyPointers = make(); + in.stubHelper = make(); } - in.wordLiteralSection = - config->dedupLiterals ? make() : nullptr; - in.rebase = make(); - in.binding = make(); - in.weakBinding = make(); - in.lazyBinding = make(); in.exports = make(); in.got = make(); in.tlvPointers = make(); - in.lazyPointers = make(); in.stubs = make(); - in.stubHelper = make(); + in.objcStubs = make(); in.unwindInfo = makeUnwindInfoSection(); + in.objCImageInfo = make(); + in.initOffsets = make(); // This section contains space for just a single word, and will be used by // dyld to cache an address to the image loader it uses. - uint8_t *arr = bAlloc.Allocate(target->wordSize); + uint8_t *arr = bAlloc().Allocate(target->wordSize); memset(arr, 0, target->wordSize); - in.imageLoaderCache = make( - segment_names::data, section_names::data, /*file=*/nullptr, + in.imageLoaderCache = makeSyntheticInputSection( + segment_names::data, section_names::data, S_REGULAR, ArrayRef{arr, target->wordSize}, - /*align=*/target->wordSize, /*flags=*/S_REGULAR); + /*align=*/target->wordSize); // References from dyld are not visible to us, so ensure this section is // always treated as live. in.imageLoaderCache->live = true; diff --git a/gnu/llvm/lld/MachO/Writer.h b/gnu/llvm/lld/MachO/Writer.h index 56f6f7ae6fe..066a0fd5fd3 100644 --- a/gnu/llvm/lld/MachO/Writer.h +++ b/gnu/llvm/lld/MachO/Writer.h @@ -11,8 +11,7 @@ #include -namespace lld { -namespace macho { +namespace lld::macho { class OutputSection; class InputSection; @@ -26,6 +25,7 @@ public: }; template void writeResult(); +void resetWriter(); void createSyntheticSections(); @@ -35,7 +35,6 @@ void addNonLazyBindingEntries(const Symbol *, const InputSection *, extern OutputSection *firstTLVDataSection; -} // namespace macho -} // namespace lld +} // namespace lld::macho #endif diff --git a/gnu/llvm/lld/MinGW/CMakeLists.txt b/gnu/llvm/lld/MinGW/CMakeLists.txt index 1dc04d73eca..35eb6f89f53 100644 --- a/gnu/llvm/lld/MinGW/CMakeLists.txt +++ b/gnu/llvm/lld/MinGW/CMakeLists.txt @@ -8,6 +8,7 @@ add_lld_library(lldMinGW LINK_COMPONENTS Option Support + TargetParser LINK_LIBS lldCOFF diff --git a/gnu/llvm/lld/MinGW/Driver.cpp b/gnu/llvm/lld/MinGW/Driver.cpp index 7c6b865a2e3..9cb21b730bf 100644 --- a/gnu/llvm/lld/MinGW/Driver.cpp +++ b/gnu/llvm/lld/MinGW/Driver.cpp @@ -29,11 +29,11 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Driver.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Version.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" @@ -44,6 +44,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" +#include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include @@ -61,12 +62,15 @@ enum { }; // Create prefix string literals used in Options.td -#define PREFIX(NAME, VALUE) static const char *const NAME[] = VALUE; +#define PREFIX(NAME, VALUE) \ + static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \ + static constexpr llvm::ArrayRef NAME( \ + NAME##_init, std::size(NAME##_init) - 1); #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td -static const opt::OptTable::Info infoTable[] = { +static constexpr opt::OptTable::Info infoTable[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, @@ -75,9 +79,9 @@ static const opt::OptTable::Info infoTable[] = { }; namespace { -class MinGWOptTable : public opt::OptTable { +class MinGWOptTable : public opt::GenericOptTable { public: - MinGWOptTable() : OptTable(infoTable, false) {} + MinGWOptTable() : opt::GenericOptTable(infoTable, false) {} opt::InputArgList parse(ArrayRef argv); }; } // namespace @@ -100,7 +104,7 @@ opt::InputArgList MinGWOptTable::parse(ArrayRef argv) { unsigned missingCount; SmallVector vec(argv.data(), argv.data() + argv.size()); - cl::ExpandResponseFiles(saver, getQuotingStyle(), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(), vec); opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); if (missingCount) @@ -111,12 +115,13 @@ opt::InputArgList MinGWOptTable::parse(ArrayRef argv) { } // Find a file by concatenating given paths. -static Optional findFile(StringRef path1, const Twine &path2) { +static std::optional findFile(StringRef path1, + const Twine &path2) { SmallString<128> s; sys::path::append(s, path1, path2); if (sys::fs::exists(s)) return std::string(s); - return None; + return std::nullopt; } // This is for -lfoo. We'll look for libfoo.dll.a or libfoo.a from search paths. @@ -124,7 +129,7 @@ static std::string searchLibrary(StringRef name, ArrayRef searchPaths, bool bStatic) { if (name.startswith(":")) { for (StringRef dir : searchPaths) - if (Optional s = findFile(dir, name.substr(1))) + if (std::optional s = findFile(dir, name.substr(1))) return *s; error("unable to find library -l" + name); return ""; @@ -132,19 +137,19 @@ searchLibrary(StringRef name, ArrayRef searchPaths, bool bStatic) { for (StringRef dir : searchPaths) { if (!bStatic) { - if (Optional s = findFile(dir, "lib" + name + ".dll.a")) + if (std::optional s = findFile(dir, "lib" + name + ".dll.a")) return *s; - if (Optional s = findFile(dir, name + ".dll.a")) + if (std::optional s = findFile(dir, name + ".dll.a")) return *s; } - if (Optional s = findFile(dir, "lib" + name + ".a")) + if (std::optional s = findFile(dir, "lib" + name + ".a")) + return *s; + if (std::optional s = findFile(dir, name + ".lib")) return *s; if (!bStatic) { - if (Optional s = findFile(dir, name + ".lib")) - return *s; - if (Optional s = findFile(dir, "lib" + name + ".dll")) + if (std::optional s = findFile(dir, "lib" + name + ".dll")) return *s; - if (Optional s = findFile(dir, name + ".dll")) + if (std::optional s = findFile(dir, name + ".dll")) return *s; } } @@ -154,12 +159,11 @@ searchLibrary(StringRef name, ArrayRef searchPaths, bool bStatic) { // Convert Unix-ish command line arguments to Windows-ish ones and // then call coff::link. -bool mingw::link(ArrayRef argsArr, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - stderrOS.enable_colors(stderrOS.has_colors()); +bool mingw::link(ArrayRef argsArr, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, + bool disableOutput) { + auto *ctx = new CommonLinkerContext; + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); MinGWOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); @@ -265,6 +269,8 @@ bool mingw::link(ArrayRef argsArr, bool canExitEarly, add("-filealign:" + StringRef(a->getValue())); if (auto *a = args.getLastArg(OPT_section_alignment)) add("-align:" + StringRef(a->getValue())); + if (auto *a = args.getLastArg(OPT_heap)) + add("-heap:" + StringRef(a->getValue())); if (auto *a = args.getLastArg(OPT_o)) add("-out:" + StringRef(a->getValue())); @@ -322,6 +328,9 @@ bool mingw::link(ArrayRef argsArr, bool canExitEarly, if (args.hasFlag(OPT_disable_tsaware, OPT_tsaware, false)) add("-tsaware:no"); + if (args.hasFlag(OPT_disable_reloc_section, OPT_enable_reloc_section, false)) + add("-fixed"); + if (args.hasFlag(OPT_no_insert_timestamp, OPT_insert_timestamp, false)) add("-timestamp:0"); @@ -375,6 +384,26 @@ bool mingw::link(ArrayRef argsArr, bool canExitEarly, error("unknown parameter: -m" + s); } + if (args.hasFlag(OPT_guard_cf, OPT_no_guard_cf, false)) { + if (args.hasFlag(OPT_guard_longjmp, OPT_no_guard_longjmp, true)) + add("-guard:cf,longjmp"); + else + add("-guard:cf,nolongjmp"); + } else if (args.hasFlag(OPT_guard_longjmp, OPT_no_guard_longjmp, false)) { + auto *a = args.getLastArg(OPT_guard_longjmp); + warn("parameter " + a->getSpelling() + + " only takes effect when used with --guard-cf"); + } + + if (auto *a = args.getLastArg(OPT_error_limit)) { + int n; + StringRef s = a->getValue(); + if (s.getAsInteger(10, n)) + error(a->getSpelling() + ": number expected, but got " + s); + else + add("-errorlimit:" + s); + } + for (auto *a : args.filtered(OPT_mllvm)) add("-mllvm:" + StringRef(a->getValue())); @@ -394,6 +423,8 @@ bool mingw::link(ArrayRef argsArr, bool canExitEarly, add("-delayload:" + StringRef(a->getValue())); for (auto *a : args.filtered(OPT_wrap)) add("-wrap:" + StringRef(a->getValue())); + for (auto *a : args.filtered(OPT_exclude_symbols)) + add("-exclude-symbols:" + StringRef(a->getValue())); std::vector searchPaths; for (auto *a : args.filtered(OPT_L)) { @@ -445,5 +476,9 @@ bool mingw::link(ArrayRef argsArr, bool canExitEarly, // Pass the actual binary name, to make error messages be printed with // the right prefix. vec[0] = argsArr[0]; - return coff::link(vec, canExitEarly, stdoutOS, stderrOS); + + // The context will be re-created in the COFF driver. + lld::CommonLinkerContext::destroy(); + + return coff::link(vec, stdoutOS, stderrOS, exitEarly, disableOutput); } diff --git a/gnu/llvm/lld/MinGW/Options.td b/gnu/llvm/lld/MinGW/Options.td index 50ac71bced8..c9c60638a4e 100644 --- a/gnu/llvm/lld/MinGW/Options.td +++ b/gnu/llvm/lld/MinGW/Options.td @@ -31,6 +31,11 @@ multiclass B_disable { def disable_ # NAME: Flag<["--", "-"], "disable-" # name>, HelpText; } +multiclass B_enable_disable { + def enable_ # NAME: Flag<["--", "-"], "enable-" # name>, HelpText; + def disable_ # NAME: Flag<["--", "-"], "disable-" # name>, HelpText; +} + def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, HelpText<"Add a directory to the library search path">; defm allow_multiple_definition: B<"allow-multiple-definition", @@ -57,6 +62,8 @@ def enable_stdcall_fixup: F<"enable-stdcall-fixup">, defm entry: Eq<"entry", "Name of entry point symbol">, MetaVarName<"">; def exclude_all_symbols: F<"exclude-all-symbols">, HelpText<"Don't automatically export any symbols">; +defm exclude_symbols: Eq<"exclude-symbols", + "Exclude symbols from automatic export">, MetaVarName<"">; def export_all_symbols: F<"export-all-symbols">, HelpText<"Export all symbols even if a def file or dllexport attributes are used">; defm fatal_warnings: B<"fatal-warnings", @@ -66,6 +73,7 @@ defm file_alignment: Eq<"file-alignment", "Set file alignment">; defm gc_sections: B<"gc-sections", "Remove unused sections", "Don't remove unused sections">; +defm heap: Eq<"heap", "Set size of the initial heap">; def help: F<"help">, HelpText<"Print option help">; defm high_entropy_va: B_disable<"high-entropy-va", "Set the 'high entropy VA' flag", "Don't set the 'high entropy VA' flag">; @@ -97,6 +105,8 @@ def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"">, HelpText<"Path to file to write output">; defm out_implib: Eq<"out-implib", "Import library name">; defm output_def: Eq<"output-def", "Output def file">; +defm reloc_section: B_enable_disable<"reloc-section", + "Enable base relocations", "Disable base relocations">; defm section_alignment: Eq<"section-alignment", "Set section alignment">; def shared: F<"shared">, HelpText<"Build a shared object">; defm subs: Eq<"subsystem", "Specify subsystem">; @@ -131,6 +141,13 @@ defm pdb: Eq<"pdb", "Output PDB debug info file, chosen implicitly if the argume defm thinlto_cache_dir: EqLong<"thinlto-cache-dir", "Path to ThinLTO cached object file directory">; defm Xlink : Eq<"Xlink", "Pass to the COFF linker">, MetaVarName<"">; +defm guard_cf : B<"guard-cf", "Enable Control Flow Guard" , + "Do not enable Control Flow Guard (default)">; +defm guard_longjmp : B<"guard-longjmp", + "Enable Control Flow Guard long jump hardening (default for --guard-cf)" , + "Do not enable Control Flow Guard long jump hardening">; +defm error_limit: + EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">; // Alias def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias; @@ -146,6 +163,7 @@ def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias; // Ignored options def: Joined<["-"], "O">; +def: F<"as-needed">; def: F<"build-id">; def: F<"disable-auto-image-base">; def: F<"enable-auto-image-base">; diff --git a/gnu/llvm/lld/cmake/modules/AddLLD.cmake b/gnu/llvm/lld/cmake/modules/AddLLD.cmake index 9883475d1aa..d3924f7243d 100644 --- a/gnu/llvm/lld/cmake/modules/AddLLD.cmake +++ b/gnu/llvm/lld/cmake/modules/AddLLD.cmake @@ -1,3 +1,4 @@ +include(GNUInstallDirs) include(LLVMDistributionSupport) macro(add_lld_library name) @@ -19,7 +20,7 @@ macro(add_lld_library name) ${export_to_lldtargets} LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} - RUNTIME DESTINATION bin) + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES) add_llvm_install_targets(install-${name} @@ -46,7 +47,7 @@ macro(add_lld_tool name) get_target_export_arg(${name} LLD export_to_lldtargets) install(TARGETS ${name} ${export_to_lldtargets} - RUNTIME DESTINATION bin + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT ${name}) if(NOT CMAKE_CONFIGURATION_TYPES) @@ -59,7 +60,7 @@ macro(add_lld_tool name) endmacro() macro(add_lld_symlink name dest) - add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE) + llvm_add_tool_symlink(LLD ${name} ${dest} ALWAYS_GENERATE) # Always generate install targets - llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE) + llvm_install_symlink(LLD ${name} ${dest} ALWAYS_GENERATE) endmacro() diff --git a/gnu/llvm/lld/cmake/modules/CMakeLists.txt b/gnu/llvm/lld/cmake/modules/CMakeLists.txt index 62d03fa901d..66db0354da6 100644 --- a/gnu/llvm/lld/cmake/modules/CMakeLists.txt +++ b/gnu/llvm/lld/cmake/modules/CMakeLists.txt @@ -1,12 +1,21 @@ +include(GNUInstallPackageDir) +include(ExtendPath) +include(FindPrefixFromConfig) + # Generate a list of CMake library targets so that other CMake projects can # link against them. LLVM calls its version of this file LLVMExports.cmake, but # the usual CMake convention seems to be ${Project}Targets.cmake. -set(LLD_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/lld) -set(lld_cmake_builddir "${CMAKE_BINARY_DIR}/${LLD_INSTALL_PACKAGE_DIR}") +set(LLD_INSTALL_PACKAGE_DIR "${CMAKE_INSTALL_PACKAGEDIR}/lld" CACHE STRING + "Path for CMake subdirectory for LLD (defaults to '${CMAKE_INSTALL_PACKAGEDIR}/lld')") +# CMAKE_INSTALL_PACKAGEDIR might be absolute, so don't reuse below. +set(lld_cmake_builddir "${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/lld") # Keep this in sync with llvm/cmake/CMakeLists.txt! -set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm) -set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}") +set(LLVM_INSTALL_PACKAGE_DIR "${CMAKE_INSTALL_PACKAGEDIR}/llvm" CACHE STRING + "Path for CMake subdirectory for LLVM (defaults to '${CMAKE_INSTALL_PACKAGEDIR}/llvm')") +# CMAKE_INSTALL_PACKAGEDIR might be absolute, so don't reuse below. +string(REPLACE "${CMAKE_CFG_INTDIR}" "." llvm_cmake_builddir "${LLVM_LIBRARY_DIR}") +set(llvm_cmake_builddir "${llvm_cmake_builddir}/cmake/llvm") get_property(LLD_EXPORTS GLOBAL PROPERTY LLD_EXPORTS) export(TARGETS ${LLD_EXPORTS} FILE ${lld_cmake_builddir}/LLDTargets.cmake) @@ -23,28 +32,27 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfig.cmake.in ${lld_cmake_builddir}/LLDConfig.cmake @ONLY) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfigVersion.cmake.in + ${lld_cmake_builddir}/LLDConfigVersion.cmake + @ONLY) set(LLD_CONFIG_CMAKE_DIR) set(LLD_CONFIG_LLVM_CMAKE_DIR) # Generate LLDConfig.cmake for the install tree. -set(LLD_CONFIG_CODE " -# Compute the installation prefix from this LLVMConfig.cmake file location. -get_filename_component(LLD_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)") -# Construct the proper number of get_filename_component(... PATH) -# calls to compute the installation prefix. -string(REGEX REPLACE "/" ";" _count "${LLD_INSTALL_PACKAGE_DIR}") -foreach(p ${_count}) - set(LLD_CONFIG_CODE "${LLD_CONFIG_CODE} -get_filename_component(LLD_INSTALL_PREFIX \"\${LLD_INSTALL_PREFIX}\" PATH)") -endforeach(p) -set(LLD_CONFIG_CMAKE_DIR "\${LLD_INSTALL_PREFIX}/${LLD_INSTALL_PACKAGE_DIR}") -set(LLD_CONFIG_LLVM_CMAKE_DIR "\${LLD_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}") +find_prefix_from_config(LLD_CONFIG_CODE LLD_INSTALL_PREFIX "${LLD_INSTALL_PACKAGE_DIR}") +extend_path(LLD_CONFIG_CMAKE_DIR "\${LLD_INSTALL_PREFIX}" "${LLD_INSTALL_PACKAGE_DIR}") +extend_path(LLD_CONFIG_LLVM_CMAKE_DIR "\${LLD_INSTALL_PREFIX}" "${LLVM_INSTALL_PACKAGE_DIR}") get_config_exports_includes(LLD LLD_CONFIG_INCLUDE_EXPORTS) -set(LLD_CONFIG_INCLUDE_DIRS "\${LLD_INSTALL_PREFIX}/include") +extend_path(LLD_CONFIG_INCLUDE_DIRS "\${LLD_INSTALL_PREFIX}" "${CMAKE_INSTALL_INCLUDEDIR}") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfig.cmake @ONLY) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfigVersion.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfigVersion.cmake + @ONLY) set(LLD_CONFIG_CODE) set(LLD_CONFIG_CMAKE_DIR) @@ -53,6 +61,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfigVersion.cmake DESTINATION ${LLD_INSTALL_PACKAGE_DIR} COMPONENT lld-cmake-exports) diff --git a/gnu/llvm/lld/cmake/modules/LLDConfig.cmake.in b/gnu/llvm/lld/cmake/modules/LLDConfig.cmake.in index 38ca4296eb3..6c48de68476 100644 --- a/gnu/llvm/lld/cmake/modules/LLDConfig.cmake.in +++ b/gnu/llvm/lld/cmake/modules/LLDConfig.cmake.in @@ -2,7 +2,8 @@ @LLD_CONFIG_CODE@ -find_package(LLVM REQUIRED CONFIG +set(LLVM_VERSION @LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@.@LLVM_VERSION_PATCH@) +find_package(LLVM ${LLVM_VERSION} EXACT REQUIRED CONFIG HINTS "@LLD_CONFIG_LLVM_CMAKE_DIR@") set(LLD_EXPORTED_TARGETS "@LLD_EXPORTS@") diff --git a/gnu/llvm/lld/cmake/modules/LLDConfigVersion.cmake.in b/gnu/llvm/lld/cmake/modules/LLDConfigVersion.cmake.in new file mode 100644 index 00000000000..e9ac4ed2da7 --- /dev/null +++ b/gnu/llvm/lld/cmake/modules/LLDConfigVersion.cmake.in @@ -0,0 +1,13 @@ +set(PACKAGE_VERSION "@PACKAGE_VERSION@") + +# LLVM is API-compatible only with matching major.minor versions +# and patch versions not less than that requested. +if("@LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@" VERSION_EQUAL + "${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}" + AND NOT "@LLVM_VERSION_PATCH@" VERSION_LESS "${PACKAGE_FIND_VERSION_PATCH}") + set(PACKAGE_VERSION_COMPATIBLE 1) + if("@LLVM_VERSION_PATCH@" VERSION_EQUAL + "${PACKAGE_FIND_VERSION_PATCH}") + set(PACKAGE_VERSION_EXACT 1) + endif() +endif() diff --git a/gnu/llvm/lld/docs/ELF/linker_script.rst b/gnu/llvm/lld/docs/ELF/linker_script.rst index af29996e92d..bc2037595e5 100644 --- a/gnu/llvm/lld/docs/ELF/linker_script.rst +++ b/gnu/llvm/lld/docs/ELF/linker_script.rst @@ -61,13 +61,16 @@ In the case where no linker script has been provided or every ``SECTIONS`` command is followed by ``INSERT``, LLD applies built-in rules which are similar to GNU ld's internal linker scripts. -- Align the first section in a ``PT_LOAD`` segment according to ``-z noseparate-code``, - ``-z separate-code``, or ``-z separate-loadable-segments`` -- Define ``__bss_start``, ``end``, ``_end``, ``etext``, ``_etext``, ``edata``, ``_edata`` -- Sort ``.ctors.*``/``.dtors.*``/``.init_array.*``/``.fini_array.*`` and PowerPC64 specific ``.toc`` +- Align the first section in a ``PT_LOAD`` segment according to + ``-z noseparate-code``, ``-z separate-code``, or + ``-z separate-loadable-segments`` +- Define ``__bss_start``, ``end``, ``_end``, ``etext``, ``_etext``, ``edata``, + ``_edata`` +- Sort ``.ctors.*``/``.dtors.*``/``.init_array.*``/``.fini_array.*`` and + PowerPC64 specific ``.toc`` - Place input ``.text.*`` into output ``.text``, and handle certain variants - (``.text.hot.``, ``.text.unknown.``, ``.text.unlikely.``, etc) in the precense of - ``-z keep-text-section-prefix``. + (``.text.hot.``, ``.text.unknown.``, ``.text.unlikely.``, etc) in the + presence of ``-z keep-text-section-prefix``. Output section description ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -94,6 +97,21 @@ The presence of ``address`` can cause the condition unsatisfied. LLD will warn. GNU ld from Binutils 2.35 onwards will reduce sh_addralign so that sh_addr=0 (modulo sh_addralign). +Output section type +------------------- + +When an *OutputSection* *S* has ``(type)``, LLD will set ``sh_type`` or +``sh_flags`` of *S*. ``type`` is one of: + +- ``NOLOAD``: set ``sh_type`` to ``SHT_NOBITS``. +- ``COPY``, ``INFO``, ``OVERLAY``: clear the ``SHF_ALLOC`` bit in ``sh_flags``. +- ``TYPE=``: set ``sh_type`` to the specified value. ```` must be + an integer or one of ``SHT_PROGBITS, SHT_NOTE, SHT_NOBITS, SHT_INIT_ARRAY, + SHT_FINI_ARRAY, SHT_PREINIT_ARRAY``. + +When ``sh_type`` is specified, it is an error if an input section in *S* has a +different type. + Output section alignment ------------------------ diff --git a/gnu/llvm/lld/docs/ELF/start-stop-gc.rst b/gnu/llvm/lld/docs/ELF/start-stop-gc.rst new file mode 100644 index 00000000000..0baa1cae246 --- /dev/null +++ b/gnu/llvm/lld/docs/ELF/start-stop-gc.rst @@ -0,0 +1,66 @@ +-z start-stop-gc +================ + +If your ``-Wl,--gc-sections`` build fail with a linker error like this: + + error: undefined symbol: __start_meta + >>> referenced by {{.*}} + >>> the encapsulation symbol needs to be retained under --gc-sections properly; consider -z nostart-stop-gc (see https://lld.llvm.org/start-stop-gc) + +it is likely your C identifier name sections are not properly annotated to +suffice under ``--gc-sections``. + +``__start_meta`` and ``__stop_meta`` are sometimed called encapsulation +symbols. In October 2015, GNU ld switched behavior and made a ``__start_meta`` +reference from a live section retain all ``meta`` input sections. This +conservative behavior works for existing code which does not take GC into fair +consideration, but unnecessarily increases sizes for modern metadata section +usage which desires precise GC. + +GNU ld 2.37 added ``-z start-stop-gc`` to restore the traditional behavior +ld.lld 13.0.0 defaults to ``-z start-stop-gc`` and supports ``-z nostart-stop-gc`` +to switch to the conservative behavior. + +The Apple ld64 linker has a similar ``section$start`` feature and always +allowed GC (like ``-z start-stop-gc``). + +Annotate C identifier name sections +----------------------------------- + +A C identifier name section (``meta``) sometimes depends on another section. +Let that section reference ``meta`` via a relocation. + +.. code-block:: c + + asm(".pushsection .init_array,\"aw\",@init_array\n" \ + ".reloc ., R_AARCH64_NONE, meta\n" \ + ".popsection\n") + +If a relocation is inconvenient, consider using ``__attribute__((retain))`` +(GCC 11 with modern binutils, Clang 13). + +.. code-block:: c + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wattributes" + __attribute__((retain,used,section("meta"))) + static const char dummy[0]; + #pragma GCC diagnostic pop + +GCC before 11 and Clang before 13 do not recognize ``__attribute__((retain))``, +so ``-Wattributes`` may need to be ignored. On ELF targets, +``__attribute__((used))`` prevents compiler discarding, but does not affect +linker ``--gc-sections``. + +In a macro, you may use: + +.. code-block:: c + + _Pragma("GCC diagnostic push") + _Pragma("GCC diagnostic ignored \"-Wattributes\"") + ... + _Pragma("GCC diagnostic pop") + +If you use the ``SECTIONS`` command in a linker script, use +`the ``KEEP`` keyword `_, e.g. +``meta : { KEEP(*(meta)) }`` diff --git a/gnu/llvm/lld/docs/MachO/index.rst b/gnu/llvm/lld/docs/MachO/index.rst new file mode 100644 index 00000000000..d6b3a558d32 --- /dev/null +++ b/gnu/llvm/lld/docs/MachO/index.rst @@ -0,0 +1,62 @@ +Mach-O LLD Port +=============== + +LLD is a linker from the LLVM project that is a drop-in replacement +for system linkers and runs much faster than them. It also provides +features that are useful for toolchain developers. This document +will describe the Mach-O port. + +Features +-------- + +- LLD is a drop-in replacement for Apple's Mach-O linker, ld64, that accepts the + same command line arguments. + +- LLD is very fast. When you link a large program on a multicore + machine, you can expect that LLD runs more than twice as fast as the ld64 + linker. + +Download +-------- + +LLD is available as a pre-built binary by going to the `latest release `_, +downloading the appropriate bundle (``clang+llvm---.tar.xz``), +decompressing it, and locating the binary at ``bin/ld64.lld``. Note +that if ``ld64.lld`` is moved out of ``bin``, it must still be accompanied +by its sibling file ``lld``, as ``ld64.lld`` is technically a symlink to ``lld``. + +Build +----- + +The easiest way to build LLD is to +check out the entire LLVM projects/sub-projects from a git mirror and +build that tree. You need ``cmake`` and of course a C++ compiler. + +.. code-block:: console + + $ git clone https://github.com/llvm/llvm-project llvm-project + $ mkdir build + $ cd build + $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS='lld' ../llvm-project/llvm + $ ninja check-lld-macho + +Then you can find output binary at ``build/bin/ld64.lld``. Note +that if ``ld64.lld`` is moved out of ``bin``, it must still be accompanied +by its sibling file ``lld``, as ``ld64.lld`` is technically a symlink to ``lld``. + +Using LLD +--------- + +LLD can be used by adding ``-fuse-ld=/path/to/ld64.lld`` to the linker flags. +For Xcode, this can be done by adding it to "Other linker flags" in the build +settings. For Bazel, this can be done with ``--linkopt`` or with +`rules_apple_linker `_. + +.. seealso:: + + :doc:`ld64-vs-lld` has more info on the differences between the two linkers. + +.. toctree:: + :hidden: + + ld64-vs-lld diff --git a/gnu/llvm/lld/docs/MachO/ld64-vs-lld.rst b/gnu/llvm/lld/docs/MachO/ld64-vs-lld.rst new file mode 100644 index 00000000000..327bf0ab716 --- /dev/null +++ b/gnu/llvm/lld/docs/MachO/ld64-vs-lld.rst @@ -0,0 +1,56 @@ +================= +ld64 vs LLD-MachO +================= + +This doc lists all significant deliberate differences in behavior between ld64 +and LLD-MachO. + +Dead Stripping Duplicate Symbols +******************************** +ld64 strips dead code before reporting duplicate symbols. By default, LLD does +the opposite. ld64's behavior hides ODR violations, so we have chosen not +to follow it. But, to make adoption easy, LLD can mimic this behavior via +the ``--dead-strip-duplicates`` flag. Usage of this flag is discouraged, and +this behavior should be fixed in the source. However, for sources that are not +within the user's control, this will mitigate users for adoption. + +``-no_deduplicate`` Flag +************************ +- ld64: This turns off ICF (deduplication pass) in the linker. +- LLD: This turns off ICF and string merging in the linker. + +String Alignment +**************** +LLD is `slightly less conservative about aligning cstrings +`_, allowing it to pack them more compactly. +This should not result in any meaningful semantic difference. + +ObjC Symbols Treatment +********************** +There are differences in how LLD and ld64 handle ObjC symbols loaded from +archives. + +- ld64: + 1. Duplicate ObjC symbols from the same archives will not raise an error. + ld64 will pick the first one. + 2. Duplicate ObjC symbols from different archives will raise a "duplicate + symbol" error. +- LLD: Duplicate symbols, regardless of which archives they are from, will + raise errors. + +Aliases +======= +ld64 treats all aliases as strong extern definitions. Having two aliases of the +same name, or an alias plus a regular extern symbol of the same name, both +result in duplicate symbol errors. LLD does not check for duplicate aliases; +instead we perform alias resolution first, and only then do we check for +duplicate symbols. In particular, we will not report a duplicate symbol error if +the aliased symbols turn out to be weak definitions, but ld64 will. + +``ZERO_AR_DATE`` enabled by default +*********************************** +ld64 has a special mode where it sets some stabs modification times to 0 for +hermetic builds, enabled by setting any value for the ``ZERO_AR_DATE`` +environment variable. LLD flips this default to perfer hermetic builds, but +allows disabling this behavior by setting ``ZERO_AR_DATE=0``. Any other value +of ``ZERO_AR_DATE`` will be ignored. diff --git a/gnu/llvm/lld/docs/ReleaseNotes.rst b/gnu/llvm/lld/docs/ReleaseNotes.rst index 50af6e7d793..a450923cded 100644 --- a/gnu/llvm/lld/docs/ReleaseNotes.rst +++ b/gnu/llvm/lld/docs/ReleaseNotes.rst @@ -1,19 +1,21 @@ -======================== -lld 13.0.0 Release Notes -======================== +=========================== +lld |release| Release Notes +=========================== .. contents:: :local: -.. warning:: - These are in-progress notes for the upcoming LLVM 13.0.0 release. - Release notes for previous releases can be found on - `the Download Page `_. +.. only:: PreRelease + + .. warning:: + These are in-progress notes for the upcoming LLVM |release| release. + Release notes for previous releases can be found on + `the Download Page `_. Introduction ============ -This document contains the release notes for the lld linker, release 13.0.0. +This document contains the release notes for the lld linker, release |release|. Here we describe the status of lld, including major improvements from the previous release. All lld releases may be downloaded from the `LLVM releases web site `_. @@ -24,169 +26,80 @@ Non-comprehensive list of changes in this release ELF Improvements ---------------- -* ``-z start-stop-gc`` is now supported and becomes the default. - (`D96914 `_) - (`rG6d2d3bd0 `_) -* ``--shuffle-sections=`` has been changed to ``--shuffle-sections==``. - If seed is -1, the matched input sections are reversed. - (`D98445 `_) - (`D98679 `_) -* ``-Bsymbolic -Bsymbolic-functions`` has been changed to behave the same as ``-Bsymbolic-functions``. This matches GNU ld. - (`D102461 `_) -* ``-Bno-symbolic`` has been added. - (`D102461 `_) -* A new linker script command ``OVERWRITE_SECTIONS`` has been added. - (`D103303 `_) -* ``-Bsymbolic-non-weak-functions`` has been added as a ``STB_GLOBAL`` subset of ``-Bsymbolic-functions``. - (`D102570 `_) -* ``--no-allow-shlib-undefined`` has been improved to catch more cases. - (`D101996 `_) -* ``__rela_iplt_start`` is no longer defined for -pie/-shared. - This makes GCC/Clang ``-static-pie`` built executables work. - (`rG8cb78e99 `_) -* IRELATIVE/TLSDESC relocations now support ``-z rel``. - (`D100544 `_) -* Section groups with a zero flag are now supported. - This is used by ``comdat nodeduplicate`` in LLVM IR. - (`D96636 `_) - (`D106228 `_) -* Defined symbols are now resolved before undefined symbols to stabilize the bheavior of archive member extraction. - (`D95985 `_) -* ``STB_WEAK`` symbols are now preferred over COMMON symbols as a fix to a ``--fortran-common`` regression. - (`D105945 `_) -* Absolute relocations referencing undef weak now produce dynamic relocations for -pie, matching GOT-generating relocations. - (`D105164 `_) -* Exported symbols are now communicated to the LTO library so as to make LTO - based whole program devirtualization (``-flto=thin -fwhole-program-vtables``) - work with shared objects. - (`D91583 `_) -* Whole program devirtualization now respects ``local:`` version nodes in a version script. - (`D98220 `_) - (`D98686 `_) -* ``local:`` version nodes in a version script now apply to non-default version symbols. - (`D107234 `_) -* If an object file defines both ``foo`` and ``foo@v1``, now only ``foo@v1`` will be in the output. - (`D107235 `_) -* Copy relocations on non-default version symbols are now supported. - (`D107535 `_) - -Linker script changes: - -* ``.``, ``$``, and double quotes can now be used in symbol names in expressions. - (`D98306 `_) - (`rGe7a7ad13 `_) -* Fixed value of ``.`` in the output section description of ``.tbss``. - (`D107288 `_) -* ``NOLOAD`` sections can now be placed in a ``PT_LOAD`` program header. - (`D103815 `_) -* ``OUTPUT_FORMAT(default, big, little)`` now consults ``-EL`` and ``-EB``. - (`D96214 `_) -* The ``OVERWRITE_SECTIONS`` command has been added. - (`D103303 `_) -* The section order within an ``INSERT AFTER`` command is now preserved. - (`D105158 `_) - -Architecture specific changes: - -* aarch64_be is now supported. - (`D96188 `_) -* The AMDGPU port now supports ``--amdhsa-code-object-version=4`` object files; - (`D95811 `_) -* The ARM port now accounts for PC biases in range extension thunk creation. - (`D97550 `_) -* The AVR port now computes ``e_flags``. - (`D99754 `_) -* The Mips port now omits unneeded dynamic relocations for PIE non-preemptible TLS. - (`D101382 `_) -* The PowerPC port now supports ``--power10-stubs=no`` to omit Power10 instructions from call stubs. - (`D94625 `_) -* Fixed a thunk creation bug in the PowerPC port when TOC/NOTOC calls are mixed. - (`D101837 `_) -* The RISC-V port now resolves undefined weak relocations to the current location if not using PLT. - (`D103001 `_) -* ``R_386_GOTOFF`` relocations from .debug_info are now allowed to be compatible with GCC. - (`D95994 `_) -* ``gotEntrySize`` has been added to improve support for the ILP32 ABI of x86-64. - (`D102569 `_) +* Link speed improved greatly compared with lld 15.0. Notably input section + initialization and relocation scanning are now parallel. + (`D130810 `_) + (`D133003 `_) +* ``ELFCOMPRESS_ZSTD`` compressed input sections are now supported. + (`D129406 `_) +* ``--compress-debug-sections=zstd`` is now available to compress debug + sections with zstd (``ELFCOMPRESS_ZSTD``). + (`D133548 `_) +* ``--no-warnings``/``-w`` is now available to suppress warnings. + (`D136569 `_) +* ``DT_RISCV_VARIANT_CC`` is now produced if at least one ``R_RISCV_JUMP_SLOT`` + relocation references a symbol with the ``STO_RISCV_VARIANT_CC`` bit. + (`D107951 `_) +* ``DT_STATIC_TLS`` is now set for AArch64/PPC32/PPC64 initial-exec TLS models + when producing a shared object. +* ``--no-undefined-version`` is now the default; symbols named in version + scripts that have no matching symbol in the output will be reported. Use + ``--undefined-version`` to revert to the old behavior. + (`D135402 `_) +* ``-V`` is now an alias for ``-v`` to support ``gcc -fuse-ld=lld -v`` on many targets. +* ``-r`` no longer defines ``__global_pointer$`` or ``_TLS_MODULE_BASE_``. +* A corner case of mixed GCC and Clang object files (``STB_WEAK`` and + ``STB_GNU_UNIQUE`` in different COMDATs) is now supported. + (`D136381 `_) +* The output ``SHT_RISCV_ATTRIBUTES`` section now merges all input components + instead of picking the first input component. + (`D138550 `_) +* For x86-32, ``-fno-plt`` GD/LD TLS models ``call *___tls_get_addr@GOT(%reg)`` + are now supported. Previous output might have runtime crash. +* Armv4(T) thunks are now supported. + (`D139888 `_) + (`D141272 `_) Breaking changes ---------------- -* ``--shuffle-sections=`` has been changed to ``--shuffle-sections==``. - Specify ``*`` as ```` to get the previous behavior. - COFF Improvements ----------------- -* Avoid thread exhaustion when running on 32 bit Windows. - (`D105506 `_) - -* Improve terminating the process on Windows while a thread pool might be - running. (`D102944 `_) +* The linker command line entry in ``S_ENVBLOCK`` of the PDB is now stripped + from input files, to align with MSVC behavior. + (`D137723 `_) +* Switched from SHA1 to BLAKE3 for PDB type hashing / ``-gcodeview-ghash`` + (`D137101 `_) +* Improvements to the PCH.OBJ files handling. Now LLD behaves the same as MSVC + link.exe when merging PCH.OBJ files that don't have the same signature. + (`D136762 `_) +* Changed the OrdinalBase for DLLs from 0 to 1, matching the output from + both MS link.exe and GNU ld. (`D134140 `_) MinGW Improvements ------------------ -* Support for linking directly against a DLL without using an import library - has been added. (`D104530 `_ and - `D104531 `_) - -* Fix linking with ``--export-all-symbols`` in combination with - ``-function-sections``. (`D101522 `_ and - `D101615 `_) - -* Fix automatic export of symbols from LTO objects. - (`D101569 `_) - -* Accept more spellings of some options. - (`D107237 `_ and - `D107253 `_) - -Mach-O Improvements -------------------- - -The Mach-O backend is now able to link several large, real-world programs, -though we are still working out the kinks. - -* arm64 is now supported as a target. (`D88629 `_) -* arm64_32 is now supported as a target. (`D99822 `_) -* Branch-range-extension thunks are now supported. (`D100818 `_) -* ``-dead_strip`` is now supported. (`D103324 `_) -* Support for identical code folding (``--icf=all``) has been added. - (`D103292 `_) -* Support for special ``$start`` and ``$end`` symbols for segment & sections has been - added. (`D106767 `_, `D106629 `_) -* ``$ld$previous`` symbols are now supported. (`D103505 `_) -* ``$ld$install_name`` symbols are now supported. (`D103746 `_) -* ``__mh_*_header`` symbols are now supported. (`D97007 `_) -* LC_CODE_SIGNATURE is now supported. (`D96164 `_) -* LC_FUNCTION_STARTS is now supported. (`D97260 `_) -* LC_DATA_IN_CODE is now supported. (`D103006 `_) -* Bind opcodes are more compactly encoded. (`D106128 `_, - `D105075 `_) -* LTO cache support has been added. (`D105922 `_) -* ``-application_extension`` is now supported. (`D105818 `_) -* ``-export_dynamic`` is now partially supported. (`D105482 `_) -* ``-arch_multiple`` is now supported. (`D105450 `_) -* ``-final_output`` is now supported. (`D105449 `_) -* ``-umbrella`` is now supported. (`D105448 `_) -* ``--print-dylib-search`` is now supported. (`D103985 `_) -* ``-force_load_swift_libs`` is now supported. (`D103709 `_) -* ``-reexport_framework``, ``-reexport_library``, ``-reexport-l`` are now supported. - (`D103497 `_) -* ``.weak_def_can_be_hidden`` is now supported. (`D101080 `_) -* ``-add_ast_path`` is now supported. (`D100076 `_) -* ``-segprot`` is now supported. (`D99389 `_) -* ``-dependency_info`` is now partially supported. (`D98559 `_) -* ``--time-trace`` is now supported. (`D98419 `_) -* ``-mark_dead_strippable_dylib`` is now supported. (`D98262 `_) -* ``-[un]exported_symbol[s_list]`` is now supported. (`D98223 `_) -* ``-flat_namespace`` is now supported. (`D97641 `_) -* ``-rename_section`` and ``-rename_segment`` are now supported. (`D97600 `_) -* ``-bundle_loader`` is now supported. (`D95913 `_) -* ``-map`` is now partially supported. (`D98323 `_) - -There were numerous other bug-fixes as well. +* The lld-specific options ``--guard-cf``, ``--no-guard-cf``, + ``--guard-longjmp`` and ``--no-guard-longjmp`` has been added to allow + enabling Control Flow Guard and long jump hardening. These options are + disabled by default, but enabling ``--guard-cf`` will also enable + ``--guard-longjmp`` unless ``--no-guard-longjmp`` is also specified. + ``--guard-longjmp`` depends on ``--guard-cf`` and cannot be used by itself. + Note that these features require the ``_load_config_used`` symbol to contain + the load config directory and be filled with the required symbols. + (`D132808 `_) + +* Pick up libraries named ``.lib`` when linked with ``-l``, even + if ``-static`` has been specified. This fixes conformance to what + GNU ld does. (`D135651 `_) + +* Unwinding in Rust code on i386 in MinGW builds has been fixed, by avoiding + to leave out the ``rust_eh_personality`` symbol. + (`D136879 `_) + +MachO Improvements +------------------ WebAssembly Improvements ------------------------ diff --git a/gnu/llvm/lld/docs/WebAssembly.rst b/gnu/llvm/lld/docs/WebAssembly.rst index c01df99cddb..dad3177e2c7 100644 --- a/gnu/llvm/lld/docs/WebAssembly.rst +++ b/gnu/llvm/lld/docs/WebAssembly.rst @@ -19,7 +19,7 @@ This is the object format that the llvm will produce when run with the Usage ----- -The WebAssembly version of lld is installed as **wasm-ld**. It shared many +The WebAssembly version of lld is installed as **wasm-ld**. It shared many common linker flags with **ld.lld** but also includes several WebAssembly-specific options: @@ -75,6 +75,11 @@ WebAssembly-specific options: flag which corresponds to ``--unresolve-symbols=ignore`` + ``--import-undefined``. +.. option:: --allow-undefined-file= + + Like ``--allow-undefined``, but the filename specified a flat list of + symbols, one per line, which are allowed to be undefined. + .. option:: --unresolved-symbols= This is a more full featured version of ``--allow-undefined``. @@ -92,6 +97,21 @@ WebAssembly-specific options: this is trivial. For direct function calls, the linker will generate a trapping stub function in place of the undefined function. + import-dynamic: + + Undefined symbols generate WebAssembly imports, including undefined data + symbols. This is somewhat similar to the --import-undefined option but + works all symbol types. This options puts limitations on the type of + relocations that are allowed for imported data symbols. Relocations that + require absolute data addresses (i.e. All R_WASM_MEMORY_ADDR_I32) will + generate an error if they cannot be resolved statically. For clang/llvm + this means inputs should be compiled with `-fPIC` (i.e. `pic` or + `dynamic-no-pic` relocation models). This options is useful for linking + binaries that are themselves static (non-relocatable) but whose undefined + symbols are resolved by a dynamic linker. Since the dynamic linking API is + experimental, this option currently requires `--experimental-pic` to also + be specified. + .. option:: --import-memory Import memory from the environment. @@ -167,11 +187,39 @@ Imports By default no undefined symbols are allowed in the final binary. The flag ``--allow-undefined`` results in a WebAssembly import being defined for each undefined symbol. It is then up to the runtime to provide such symbols. +``--allow-undefined-file`` is the same but allows a list of symbols to be +specified. Alternatively symbols can be marked in the source code as with the ``import_name`` and/or ``import_module`` clang attributes which signals that they are expected to be undefined at static link time. +Stub Libraries +~~~~~~~~~~~~~~ + +Another way to specify imports and exports is via a "stub library". This +feature is inspired by the ELF stub objects which are supported by the Solaris +linker. Stub libraries are text files that can be passed as normal linker +inputs, similar to how linker scripts can be passed to the ELF linker. The stub +library is a stand-in for a set of symbols that will be available at runtime, +but doesn't contain any actual code or data. Instead it contains just a list of +symbols, one per line. Each symbol can specify zero or more dependencies. +These dependencies are symbols that must be defined, and exported, by the output +module if the symbol is question is imported/required by the output module. + +For example, imagine the runtime provides an external symbol ``foo`` that +depends on the ``malloc`` and ``free``. This can be expressed simply as:: + + #STUB + foo: malloc,free + +Here we are saying that ``foo`` is allowed to be imported (undefined) but that +if it is imported, then the output module must also export ``malloc`` and +``free`` to the runtime. If ``foo`` is imported (undefined), but the output +module does not define ``malloc`` and ``free`` then the link will fail. + +Stub libraries must begin with ``#STUB`` on a line by itself. + Garbage Collection ~~~~~~~~~~~~~~~~~~ @@ -205,6 +253,6 @@ Missing features supported. - No support for creating shared libraries. The spec for shared libraries in WebAssembly is still in flux: - https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md + https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md -.. _linking: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md +.. _linking: https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md diff --git a/gnu/llvm/lld/docs/_templates/indexsidebar.html b/gnu/llvm/lld/docs/_templates/indexsidebar.html index 588be9309bd..f9ecb724153 100644 --- a/gnu/llvm/lld/docs/_templates/indexsidebar.html +++ b/gnu/llvm/lld/docs/_templates/indexsidebar.html @@ -1,4 +1,9 @@

Bugs

-

lld bugs should be reported at the - LLVM Bugzilla.

+

+To report bugs, please visit +PE/COFF, +ELF, +Mach-O, or +WebAssembly. +

diff --git a/gnu/llvm/lld/docs/conf.py b/gnu/llvm/lld/docs/conf.py index 2df1aa70816..95befddf80e 100644 --- a/gnu/llvm/lld/docs/conf.py +++ b/gnu/llvm/lld/docs/conf.py @@ -43,15 +43,6 @@ master_doc = 'index' project = u'lld' copyright = u'2011-%d, LLVM Project' % date.today().year -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short version. -version = '13' -# The full version, including alpha/beta/rc tags. -release = '13' - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None diff --git a/gnu/llvm/lld/docs/index.rst b/gnu/llvm/lld/docs/index.rst index 40da6d77cca..ce632033324 100644 --- a/gnu/llvm/lld/docs/index.rst +++ b/gnu/llvm/lld/docs/index.rst @@ -10,9 +10,7 @@ WebAssembly in descending order of completeness. Internally, LLD consists of several different linkers. The ELF port is the one that will be described in this document. The PE/COFF port is complete, including Windows debug info (PDB) support. The WebAssembly port is still a work in -progress (See :doc:`WebAssembly`). The Mach-O port is built based on a -different architecture than the others. For the details about Mach-O, please -read :doc:`AtomLLD`. +progress (See :doc:`WebAssembly`). Features -------- @@ -20,15 +18,6 @@ Features - LLD is a drop-in replacement for the GNU linkers that accepts the same command line arguments and linker scripts as GNU. - We are currently working closely with the FreeBSD project to make - LLD default system linker in future versions of the operating - system, so we are serious about addressing compatibility issues. As - of February 2017, LLD is able to link the entire FreeBSD/amd64 base - system including the kernel. With a few work-in-progress patches it - can link approximately 95% of the ports collection on AMD64. For the - details, see `FreeBSD quarterly status report - `_. - - LLD is very fast. When you link a large program on a multicore machine, you can expect that LLD runs more than twice as fast as the GNU gold linker. Your mileage may vary, though. @@ -170,7 +159,6 @@ document soon. :maxdepth: 1 NewLLD - AtomLLD WebAssembly windows_support missingkeyfunction @@ -178,4 +166,6 @@ document soon. Partitions ReleaseNotes ELF/linker_script + ELF/start-stop-gc ELF/warn_backrefs + MachO/index diff --git a/gnu/llvm/lld/include/lld/Common/Args.h b/gnu/llvm/lld/include/lld/Common/Args.h index 48f7b4079cd..40c8ee4e9d0 100644 --- a/gnu/llvm/lld/include/lld/Common/Args.h +++ b/gnu/llvm/lld/include/lld/Common/Args.h @@ -30,7 +30,8 @@ int64_t getInteger(llvm::opt::InputArgList &args, unsigned key, int64_t getHex(llvm::opt::InputArgList &args, unsigned key, int64_t Default); -std::vector getStrings(llvm::opt::InputArgList &args, int id); +llvm::SmallVector getStrings(llvm::opt::InputArgList &args, + int id); uint64_t getZOptionValue(llvm::opt::InputArgList &args, int id, StringRef key, uint64_t Default); diff --git a/gnu/llvm/lld/include/lld/Common/CommonLinkerContext.h b/gnu/llvm/lld/include/lld/Common/CommonLinkerContext.h new file mode 100644 index 00000000000..0627bbdc8bd --- /dev/null +++ b/gnu/llvm/lld/include/lld/Common/CommonLinkerContext.h @@ -0,0 +1,61 @@ +//===- CommonLinkerContext.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 +// +//===----------------------------------------------------------------------===// +// +// Entry point for all global state in lldCommon. The objective is for LLD to be +// used "as a library" in a thread-safe manner. +// +// Instead of program-wide globals or function-local statics, we prefer +// aggregating all "global" states into a heap-based structure +// (CommonLinkerContext). This also achieves deterministic initialization & +// shutdown for all "global" states. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_COMMONLINKINGCONTEXT_H +#define LLD_COMMON_COMMONLINKINGCONTEXT_H + +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/Support/StringSaver.h" + +namespace llvm { +class raw_ostream; +} // namespace llvm + +namespace lld { +struct SpecificAllocBase; +class CommonLinkerContext { +public: + CommonLinkerContext(); + virtual ~CommonLinkerContext(); + + static void destroy(); + + llvm::BumpPtrAllocator bAlloc; + llvm::StringSaver saver{bAlloc}; + llvm::DenseMap instances; + + ErrorHandler e; +}; + +// Retrieve the global state. Currently only one state can exist per process, +// but in the future we plan on supporting an arbitrary number of LLD instances +// in a single process. +CommonLinkerContext &commonContext(); + +template T &context() { + return static_cast(commonContext()); +} + +bool hasContext(); + +inline llvm::StringSaver &saver() { return context().saver; } +inline llvm::BumpPtrAllocator &bAlloc() { return context().bAlloc; } +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/include/lld/Common/DWARF.h b/gnu/llvm/lld/include/lld/Common/DWARF.h index b77985a6919..6310d7a876f 100644 --- a/gnu/llvm/lld/include/lld/Common/DWARF.h +++ b/gnu/llvm/lld/include/lld/Common/DWARF.h @@ -26,9 +26,9 @@ namespace lld { class DWARFCache { public: DWARFCache(std::unique_ptr dwarf); - llvm::Optional getDILineInfo(uint64_t offset, - uint64_t sectionIndex); - llvm::Optional> + std::optional getDILineInfo(uint64_t offset, + uint64_t sectionIndex); + std::optional> getVariableLoc(StringRef name); llvm::DWARFContext *getContext() { return dwarf.get(); } diff --git a/gnu/llvm/lld/include/lld/Common/Driver.h b/gnu/llvm/lld/include/lld/Common/Driver.h index eb5bc7b82b9..19573e6e100 100644 --- a/gnu/llvm/lld/include/lld/Common/Driver.h +++ b/gnu/llvm/lld/include/lld/Common/Driver.h @@ -28,34 +28,29 @@ SafeReturn safeLldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); namespace coff { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace mingw { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace elf { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); -} - -namespace mach_o { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace macho { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace wasm { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); -} +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } +} // namespace lld #endif diff --git a/gnu/llvm/lld/include/lld/Common/ErrorHandler.h b/gnu/llvm/lld/include/lld/Common/ErrorHandler.h index 95d92f3594b..0b69bb6202b 100644 --- a/gnu/llvm/lld/include/lld/Common/ErrorHandler.h +++ b/gnu/llvm/lld/include/lld/Common/ErrorHandler.h @@ -73,6 +73,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" +#include namespace llvm { class DiagnosticInfo; @@ -81,11 +82,6 @@ class raw_ostream; namespace lld { -// We wrap stdout and stderr so that you can pass alternative stdout/stderr as -// arguments to lld::*::link() functions. -extern llvm::raw_ostream *stdoutOS; -extern llvm::raw_ostream *stderrOS; - llvm::raw_ostream &outs(); llvm::raw_ostream &errs(); @@ -93,6 +89,11 @@ enum class ErrorTag { LibNotFound, SymbolNotFound }; class ErrorHandler { public: + ~ErrorHandler(); + + void initialize(llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, + bool exitEarly, bool disableOutput); + uint64_t errorCount = 0; uint64_t errorLimit = 20; StringRef errorLimitExceededMsg = "too many errors emitted, stopping now"; @@ -100,6 +101,7 @@ public: StringRef logName = "lld"; bool exitEarly = true; bool fatalWarnings = false; + bool suppressWarnings = false; bool verbose = false; bool vsDiagnostics = false; bool disableOutput = false; @@ -109,14 +111,12 @@ public: void error(const Twine &msg, ErrorTag tag, ArrayRef args); [[noreturn]] void fatal(const Twine &msg); void log(const Twine &msg); - void message(const Twine &msg); + void message(const Twine &msg, llvm::raw_ostream &s); void warn(const Twine &msg); - void reset() { - if (cleanupCallback) - cleanupCallback(); - *this = ErrorHandler(); - } + raw_ostream &outs(); + raw_ostream &errs(); + void flushStreams(); std::unique_ptr outputBuffer; @@ -124,20 +124,33 @@ private: using Colors = raw_ostream::Colors; std::string getLocation(const Twine &msg); + void reportDiagnostic(StringRef location, Colors c, StringRef diagKind, + const Twine &msg); + + // We want to separate multi-line messages with a newline. `sep` is "\n" + // if the last messages was multi-line. Otherwise "". + llvm::StringRef sep; + + // We wrap stdout and stderr so that you can pass alternative stdout/stderr as + // arguments to lld::*::link() functions. Since lld::outs() or lld::errs() can + // be indirectly called from multiple threads, we protect them using a mutex. + // In the future, we plan on supporting several concurrent linker contexts, + // which explains why the mutex is not a global but part of this context. + std::mutex mu; + llvm::raw_ostream *stdoutOS{}; + llvm::raw_ostream *stderrOS{}; }; /// Returns the default error handler. ErrorHandler &errorHandler(); -inline void error(const Twine &msg) { errorHandler().error(msg); } -inline void error(const Twine &msg, ErrorTag tag, ArrayRef args) { - errorHandler().error(msg, tag, args); -} -[[noreturn]] inline void fatal(const Twine &msg) { errorHandler().fatal(msg); } -inline void log(const Twine &msg) { errorHandler().log(msg); } -inline void message(const Twine &msg) { errorHandler().message(msg); } -inline void warn(const Twine &msg) { errorHandler().warn(msg); } -inline uint64_t errorCount() { return errorHandler().errorCount; } +void error(const Twine &msg); +void error(const Twine &msg, ErrorTag tag, ArrayRef args); +[[noreturn]] void fatal(const Twine &msg); +void log(const Twine &msg); +void message(const Twine &msg, llvm::raw_ostream &s = outs()); +void warn(const Twine &msg); +uint64_t errorCount(); [[noreturn]] void exitLld(int val); diff --git a/gnu/llvm/lld/include/lld/Common/LLVM.h b/gnu/llvm/lld/include/lld/Common/LLVM.h index c19364ad9f6..6872adf8d00 100644 --- a/gnu/llvm/lld/include/lld/Common/LLVM.h +++ b/gnu/llvm/lld/include/lld/Common/LLVM.h @@ -45,7 +45,6 @@ class WasmSymbol; namespace wasm { struct WasmTag; -struct WasmTagType; struct WasmFunction; struct WasmGlobal; struct WasmGlobalType; @@ -97,7 +96,6 @@ using llvm::wasm::WasmSignature; using llvm::wasm::WasmTable; using llvm::wasm::WasmTableType; using llvm::wasm::WasmTag; -using llvm::wasm::WasmTagType; } // end namespace lld. namespace std { diff --git a/gnu/llvm/lld/include/lld/Common/Memory.h b/gnu/llvm/lld/include/lld/Common/Memory.h index f516a327cfb..c7612a08c6b 100644 --- a/gnu/llvm/lld/include/lld/Common/Memory.h +++ b/gnu/llvm/lld/include/lld/Common/Memory.h @@ -22,47 +22,72 @@ #define LLD_COMMON_MEMORY_H #include "llvm/Support/Allocator.h" -#include "llvm/Support/StringSaver.h" -#include namespace lld { - -// Use this arena if your object doesn't have a destructor. -extern llvm::BumpPtrAllocator bAlloc; -extern llvm::StringSaver saver; - -void freeArena(); - -// These two classes are hack to keep track of all -// SpecificBumpPtrAllocator instances. +// A base class only used by the CommonLinkerContext to keep track of the +// SpecificAlloc<> instances. struct SpecificAllocBase { - SpecificAllocBase() { instances.push_back(this); } virtual ~SpecificAllocBase() = default; - virtual void reset() = 0; - static std::vector instances; + static SpecificAllocBase *getOrCreate(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)); }; +// An arena of specific types T, created on-demand. template struct SpecificAlloc : public SpecificAllocBase { - void reset() override { alloc.DestroyAll(); } + static SpecificAllocBase *create(void *storage) { + return new (storage) SpecificAlloc(); + } llvm::SpecificBumpPtrAllocator alloc; + static int tag; }; -// Use a static local for these singletons so they are only registered if an -// object of this instance is ever constructed. Otherwise we will create and -// register ELF allocators for COFF and the reverse. +// The address of this static member is only used as a key in +// CommonLinkerContext::instances. Its value does not matter. +template int SpecificAlloc::tag = 0; + +// Creates the arena on-demand on the first call; or returns it, if it was +// already created. template inline llvm::SpecificBumpPtrAllocator &getSpecificAllocSingleton() { - static SpecificAlloc instance; - return instance.alloc; + SpecificAllocBase *instance = SpecificAllocBase::getOrCreate( + &SpecificAlloc::tag, sizeof(SpecificAlloc), + alignof(SpecificAlloc), SpecificAlloc::create); + return ((SpecificAlloc *)instance)->alloc; } -// Use this arena if your object has a destructor. -// Your destructor will be invoked from freeArena(). +// Creates new instances of T off a (almost) contiguous arena/object pool. The +// instances are destroyed whenever lldMain() goes out of scope. template T *make(U &&... args) { return new (getSpecificAllocSingleton().Allocate()) T(std::forward(args)...); } +template +inline llvm::SpecificBumpPtrAllocator & +getSpecificAllocSingletonThreadLocal() { + thread_local SpecificAlloc instance; + return instance.alloc; +} + +// Create a new instance of T off a thread-local SpecificAlloc, used by code +// like parallel input section initialization. The use cases assume that the +// return value outlives the containing parallelForEach (if exists), which is +// currently guaranteed: when parallelForEach returns, the threads allocating +// the TLS are not destroyed. +// +// Note: Some ports (e.g. ELF) have lots of global states which are currently +// infeasible to remove, and context() just adds overhead with no benefit. The +// allocation performance is of higher importance, so we simply use thread_local +// allocators instead of doing context indirection and pthread_getspecific. +template T *makeThreadLocal(U &&...args) { + return new (getSpecificAllocSingletonThreadLocal().Allocate()) + T(std::forward(args)...); +} + +template T *makeThreadLocalN(size_t n) { + return new (getSpecificAllocSingletonThreadLocal().Allocate(n)) T[n]; +} + } // namespace lld #endif diff --git a/gnu/llvm/lld/include/lld/Common/Strings.h b/gnu/llvm/lld/include/lld/Common/Strings.h index 71126f61501..2216968fb82 100644 --- a/gnu/llvm/lld/include/lld/Common/Strings.h +++ b/gnu/llvm/lld/include/lld/Common/Strings.h @@ -10,18 +10,15 @@ #define LLD_STRINGS_H #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/GlobPattern.h" #include #include namespace lld { -// Returns a demangled C++ symbol name. If Name is not a mangled -// name, it returns name. -std::string demangleItanium(llvm::StringRef name); -std::vector parseHex(llvm::StringRef s); +llvm::SmallVector parseHex(llvm::StringRef s); bool isValidCIdentifier(llvm::StringRef s); // Write the contents of the a buffer to a file diff --git a/gnu/llvm/lld/include/lld/Common/TargetOptionsCommandFlags.h b/gnu/llvm/lld/include/lld/Common/TargetOptionsCommandFlags.h index 04428b500ab..9bc22d441e1 100644 --- a/gnu/llvm/lld/include/lld/Common/TargetOptionsCommandFlags.h +++ b/gnu/llvm/lld/include/lld/Common/TargetOptionsCommandFlags.h @@ -13,14 +13,14 @@ #ifndef LLD_COMMON_TARGETOPTIONSCOMMANDFLAGS_H #define LLD_COMMON_TARGETOPTIONSCOMMANDFLAGS_H -#include "llvm/ADT/Optional.h" #include "llvm/Support/CodeGen.h" #include "llvm/Target/TargetOptions.h" +#include namespace lld { llvm::TargetOptions initTargetOptionsFromCodeGenFlags(); -llvm::Optional getRelocModelFromCMModel(); -llvm::Optional getCodeModelFromCMModel(); +std::optional getRelocModelFromCMModel(); +std::optional getCodeModelFromCMModel(); std::string getCPUStr(); std::vector getMAttrs(); } diff --git a/gnu/llvm/lld/include/lld/Common/Timer.h b/gnu/llvm/lld/include/lld/Common/Timer.h index b37388cd38c..7aca966f663 100644 --- a/gnu/llvm/lld/include/lld/Common/Timer.h +++ b/gnu/llvm/lld/include/lld/Common/Timer.h @@ -38,7 +38,8 @@ class Timer { public: Timer(llvm::StringRef name, Timer &parent); - static Timer &root(); + // Creates the root timer. + explicit Timer(llvm::StringRef name); void addToTotal(std::chrono::nanoseconds time) { total += time.count(); } void print(); @@ -46,7 +47,6 @@ public: double millis() const; private: - explicit Timer(llvm::StringRef name); void print(int depth, double totalDuration, bool recurse = true) const; std::atomic total; diff --git a/gnu/llvm/lld/tools/lld/CMakeLists.txt b/gnu/llvm/lld/tools/lld/CMakeLists.txt index e77b2161a87..12628395680 100644 --- a/gnu/llvm/lld/tools/lld/CMakeLists.txt +++ b/gnu/llvm/lld/tools/lld/CMakeLists.txt @@ -1,31 +1,39 @@ set(LLVM_LINK_COMPONENTS Support + TargetParser ) add_lld_tool(lld lld.cpp SUPPORT_PLUGINS + GENERATE_DRIVER ) export_executable_symbols_for_plugins(lld) -target_link_libraries(lld +function(lld_target_link_libraries target type) + target_link_libraries(${target} ${type} ${ARGN}) + if (TARGET obj.${target}) + target_link_libraries(obj.${target} ${ARGN}) + endif() +endfunction() + +lld_target_link_libraries(lld PRIVATE lldCommon lldCOFF - lldDriver lldELF - lldMachO2 + lldMachO lldMinGW lldWasm ) install(TARGETS lld - RUNTIME DESTINATION bin) + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") if(NOT LLD_SYMLINKS_TO_CREATE) set(LLD_SYMLINKS_TO_CREATE - lld-link ld.lld ld64.lld ld64.lld.darwinnew ld64.lld.darwinold wasm-ld) + lld-link ld.lld ld64.lld wasm-ld) endif() foreach(link ${LLD_SYMLINKS_TO_CREATE}) diff --git a/gnu/llvm/lld/wasm/CMakeLists.txt b/gnu/llvm/lld/wasm/CMakeLists.txt index 427ba586661..6033bfbf9a5 100644 --- a/gnu/llvm/lld/wasm/CMakeLists.txt +++ b/gnu/llvm/lld/wasm/CMakeLists.txt @@ -29,6 +29,7 @@ add_lld_library(lldWasm Option Passes Support + TargetParser LINK_LIBS lldCommon diff --git a/gnu/llvm/lld/wasm/Config.h b/gnu/llvm/lld/wasm/Config.h index 7b20a6b0385..dba4a5c2d8f 100644 --- a/gnu/llvm/lld/wasm/Config.h +++ b/gnu/llvm/lld/wasm/Config.h @@ -9,16 +9,21 @@ #ifndef LLD_WASM_CONFIG_H #define LLD_WASM_CONFIG_H +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Support/CachePruning.h" +#include namespace lld { namespace wasm { +class InputFile; +class Symbol; + // For --unresolved-symbols. -enum class UnresolvedPolicy { ReportError, Warn, Ignore }; +enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic }; // This struct contains the global configuration for the linker. // Most fields are direct mapping from the command line options @@ -35,13 +40,15 @@ struct Configuration { bool exportAll; bool exportDynamic; bool exportTable; + bool extendedConst; bool growableTable; bool gcSections; - bool importMemory; + std::optional> memoryImport; + std::optional memoryExport; bool sharedMemory; bool importTable; bool importUndefined; - llvm::Optional is64; + std::optional is64; bool mergeDataSegments; bool pie; bool printGcSections; @@ -51,6 +58,7 @@ struct Configuration { bool stripAll; bool stripDebug; bool stackFirst; + bool isStatic = false; bool trace; uint64_t globalBase; uint64_t initialMemory; @@ -60,7 +68,6 @@ struct Configuration { unsigned ltoo; unsigned optimize; llvm::StringRef thinLTOJobs; - bool ltoNewPassManager; bool ltoDebugPassManager; UnresolvedPolicy unresolvedSymbols; @@ -68,16 +75,19 @@ struct Configuration { llvm::StringRef mapFile; llvm::StringRef outputFile; llvm::StringRef thinLTOCacheDir; + llvm::StringRef whyExtract; llvm::StringSet<> allowUndefinedSymbols; llvm::StringSet<> exportedSymbols; std::vector requiredExports; - std::vector searchPaths; + llvm::SmallVector searchPaths; llvm::CachePruningPolicy thinLTOCachePolicy; - llvm::Optional> features; + std::optional> features; + std::optional> extraFeatures; // The following config options do not directly correspond to any - // particualr command line options. + // particular command line options, and should probably be moved to seperate + // Ctx struct as in ELF/Config.h // True if we are creating position-independent code. bool isPic; @@ -91,6 +101,15 @@ struct Configuration { // for shared libraries (since they always added to a dynamic offset at // runtime). uint32_t tableBase = 0; + + // Will be set to true if bss data segments should be emitted. In most cases + // this is not necessary. + bool emitBssSegments = false; + + // A tuple of (reference, extractedFile, sym). Used by --why-extract=. + llvm::SmallVector, + 0> + whyExtractRecords; }; // The only instance of Configuration struct. diff --git a/gnu/llvm/lld/wasm/Driver.cpp b/gnu/llvm/lld/wasm/Driver.cpp index 7e0c030482f..762d4c97b09 100644 --- a/gnu/llvm/lld/wasm/Driver.cpp +++ b/gnu/llvm/lld/wasm/Driver.cpp @@ -14,6 +14,7 @@ #include "SymbolTable.h" #include "Writer.h" #include "lld/Common/Args.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" @@ -32,6 +33,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" +#include #define DEBUG_TYPE "lld" @@ -81,18 +83,15 @@ private: }; } // anonymous namespace -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; +bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new CommonLinkerContext; - errorHandler().cleanupCallback = []() { freeArena(); }; - - errorHandler().logName = args::getFilenameWithoutExe(args[0]); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now (use " - "-error-limit=0 to see all errors)"; - stderrOS.enable_colors(stderrOS.has_colors()); + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " + "-error-limit=0 to see all errors)"; config = make(); symtab = make(); @@ -100,22 +99,19 @@ bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, initLLVM(); LinkerDriver().linkerMain(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); - - return !errorCount(); + return errorCount() == 0; } // Create prefix string literals used in Options.td -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef NAME(NAME##_init, \ + std::size(NAME##_init) - 1); #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td -static const opt::OptTable::Info optInfo[] = { +static constexpr opt::OptTable::Info optInfo[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, @@ -124,9 +120,9 @@ static const opt::OptTable::Info optInfo[] = { }; namespace { -class WasmOptTable : public llvm::opt::OptTable { +class WasmOptTable : public opt::GenericOptTable { public: - WasmOptTable() : OptTable(optInfo) {} + WasmOptTable() : opt::GenericOptTable(optInfo) {} opt::InputArgList parse(ArrayRef argv); }; } // namespace @@ -168,12 +164,13 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { } // Find a file by concatenating given paths. -static Optional findFile(StringRef path1, const Twine &path2) { +static std::optional findFile(StringRef path1, + const Twine &path2) { SmallString<128> s; path::append(s, path1, path2); if (fs::exists(s)) return std::string(s); - return None; + return std::nullopt; } opt::InputArgList WasmOptTable::parse(ArrayRef argv) { @@ -189,10 +186,13 @@ opt::InputArgList WasmOptTable::parse(ArrayRef argv) { // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec); args = this->ParseArgs(vec, missingIndex, missingCount); handleColorDiagnostics(args); + if (missingCount) + error(Twine(args.getArgString(missingIndex)) + ": missing argument"); + for (auto *arg : args.filtered(OPT_UNKNOWN)) error("unknown argument: " + arg->getAsString(args)); return args; @@ -206,7 +206,7 @@ opt::InputArgList WasmOptTable::parse(ArrayRef argv) { // attribute/flag in the object file itself. // See: https://github.com/WebAssembly/tool-conventions/issues/35 static void readImportFile(StringRef filename) { - if (Optional buf = readFile(filename)) + if (std::optional buf = readFile(filename)) for (StringRef sym : args::getLines(*buf)) config->allowUndefinedSymbols.insert(sym); } @@ -239,8 +239,8 @@ std::vector static getArchiveMembers(MemoryBufferRef mb) { } void LinkerDriver::addFile(StringRef path) { - Optional buffer = readFile(path); - if (!buffer.hasValue()) + std::optional buffer = readFile(path); + if (!buffer) return; MemoryBufferRef mbref = *buffer; @@ -280,32 +280,69 @@ void LinkerDriver::addFile(StringRef path) { case file_magic::wasm_object: files.push_back(createObjectFile(mbref)); break; + case file_magic::unknown: + if (mbref.getBuffer().starts_with("#STUB")) { + files.push_back(make(mbref)); + break; + } + [[fallthrough]]; default: error("unknown file type: " + mbref.getBufferIdentifier()); } } -// Add a given library by searching it from input search paths. -void LinkerDriver::addLibrary(StringRef name) { +static std::optional findFromSearchPaths(StringRef path) { + for (StringRef dir : config->searchPaths) + if (std::optional s = findFile(dir, path)) + return s; + return std::nullopt; +} + +// This is for -l. We'll look for lib.a from +// search paths. +static std::optional searchLibraryBaseName(StringRef name) { for (StringRef dir : config->searchPaths) { - if (Optional s = findFile(dir, "lib" + name + ".a")) { - addFile(*s); - return; - } + // Currently we don't enable dyanmic linking at all unless -shared or -pie + // are used, so don't even look for .so files in that case.. + if (config->isPic && !config->isStatic) + if (std::optional s = findFile(dir, "lib" + name + ".so")) + return s; + if (std::optional s = findFile(dir, "lib" + name + ".a")) + return s; } + return std::nullopt; +} + +// This is for -l. +static std::optional searchLibrary(StringRef name) { + if (name.startswith(":")) + return findFromSearchPaths(name.substr(1)); + return searchLibraryBaseName(name); +} - error("unable to find library -l" + name); +// Add a given library by searching it from input search paths. +void LinkerDriver::addLibrary(StringRef name) { + if (std::optional path = searchLibrary(name)) + addFile(saver().save(*path)); + else + error("unable to find library -l" + name, ErrorTag::LibNotFound, {name}); } void LinkerDriver::createFiles(opt::InputArgList &args) { for (auto *arg : args) { switch (arg->getOption().getID()) { - case OPT_l: + case OPT_library: addLibrary(arg->getValue()); break; case OPT_INPUT: addFile(arg->getValue()); break; + case OPT_Bstatic: + config->isStatic = true; + break; + case OPT_Bdynamic: + config->isStatic = false; + break; case OPT_whole_archive: inWholeArchive = true; break; @@ -344,6 +381,8 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { StringRef s = arg->getValue(); if (s == "ignore-all") return UnresolvedPolicy::Ignore; + if (s == "import-dynamic") + return UnresolvedPolicy::ImportDynamic; if (s == "report-all") return errorOrWarn; error("unknown --unresolved-symbols value: " + s); @@ -366,17 +405,32 @@ static void readConfigs(opt::InputArgList &args) { config->exportAll = args.hasArg(OPT_export_all); config->exportTable = args.hasArg(OPT_export_table); config->growableTable = args.hasArg(OPT_growable_table); - errorHandler().fatalWarnings = - args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); - config->importMemory = args.hasArg(OPT_import_memory); + + if (args.hasArg(OPT_import_memory_with_name)) { + config->memoryImport = + args.getLastArgValue(OPT_import_memory_with_name).split(","); + } else if (args.hasArg(OPT_import_memory)) { + config->memoryImport = + std::pair(defaultModule, memoryName); + } else { + config->memoryImport = + std::optional>(); + } + + if (args.hasArg(OPT_export_memory_with_name)) { + config->memoryExport = + args.getLastArgValue(OPT_export_memory_with_name); + } else if (args.hasArg(OPT_export_memory)) { + config->memoryExport = memoryName; + } else { + config->memoryExport = std::optional(); + } + config->sharedMemory = args.hasArg(OPT_shared_memory); config->importTable = args.hasArg(OPT_import_table); config->importUndefined = args.hasArg(OPT_import_undefined); config->ltoo = args::getInteger(args, OPT_lto_O, 2); config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); - config->ltoNewPassManager = - args.hasFlag(OPT_no_lto_legacy_pass_manager, OPT_lto_legacy_pass_manager, - LLVM_ENABLE_NEW_PASS_MANAGER); config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager); config->mapFile = args.getLastArgValue(OPT_Map); config->optimize = args::getInteger(args, OPT_O, 1); @@ -391,7 +445,7 @@ static void readConfigs(opt::InputArgList &args) { config->printGcSections = args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); config->saveTemps = args.hasArg(OPT_save_temps); - config->searchPaths = args::getStrings(args, OPT_L); + config->searchPaths = args::getStrings(args, OPT_library_path); config->shared = args.hasArg(OPT_shared); config->stripAll = args.hasArg(OPT_strip_all); config->stripDebug = args.hasArg(OPT_strip_debug); @@ -402,11 +456,12 @@ static void readConfigs(opt::InputArgList &args) { parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); config->unresolvedSymbols = getUnresolvedSymbolPolicy(args); + config->whyExtract = args.getLastArgValue(OPT_why_extract); errorHandler().verbose = args.hasArg(OPT_verbose); LLVM_DEBUG(errorHandler().verbose = true); config->initialMemory = args::getInteger(args, OPT_initial_memory, 0); - config->globalBase = args::getInteger(args, OPT_global_base, 1024); + config->globalBase = args::getInteger(args, OPT_global_base, 0); config->maxMemory = args::getInteger(args, OPT_max_memory, 0); config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize); @@ -442,11 +497,18 @@ static void readConfigs(opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_features)) { config->features = - llvm::Optional>(std::vector()); + std::optional>(std::vector()); for (StringRef s : arg->getValues()) config->features->push_back(std::string(s)); } + if (auto *arg = args.getLastArg(OPT_extra_features)) { + config->extraFeatures = + std::optional>(std::vector()); + for (StringRef s : arg->getValues()) + config->extraFeatures->push_back(std::string(s)); + } + // Legacy --allow-undefined flag which is equivalent to // --unresolve-symbols=ignore + --import-undefined if (args.hasArg(OPT_allow_undefined)) { @@ -481,9 +543,20 @@ static void setConfigs() { } if (config->shared) { - config->importMemory = true; + if (config->memoryExport.has_value()) { + error("--export-memory is incompatible with --shared"); + } + if (!config->memoryImport.has_value()) { + config->memoryImport = + std::pair(defaultModule, memoryName); + } config->importUndefined = true; - config->unresolvedSymbols = UnresolvedPolicy::Ignore; + } + + // If neither export-memory nor import-memory is specified, default to + // exporting memory under its default name. + if (!config->memoryExport.has_value() && !config->memoryImport.has_value()) { + config->memoryExport = memoryName; } } @@ -523,6 +596,8 @@ static void checkOptions(opt::InputArgList &args) { error("-r and -pie may not be used together"); if (config->sharedMemory) error("-r and --shared-memory may not be used together"); + if (config->globalBase) + error("-r and --global-base may not by used together"); } // To begin to prepare for Module Linking-style shared libraries, start @@ -540,15 +615,30 @@ static void checkOptions(opt::InputArgList &args) { if (config->pie) { warn("creating PIEs, with -pie, is not yet stable"); } + + if (config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) { + warn("dynamic imports are not yet stable " + "(--unresolved-symbols=import-dynamic)"); + } } if (config->bsymbolic && !config->shared) { warn("-Bsymbolic is only meaningful when combined with -shared"); } + + if (config->globalBase && config->isPic) { + error("--global-base may not be used with -shared/-pie"); + } +} + +static const char *getReproduceOption(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return arg->getValue(); + return getenv("LLD_REPRODUCE"); } // Force Sym to be entered in the output. Used for -u or equivalent. -static Symbol *handleUndefined(StringRef name) { +static Symbol *handleUndefined(StringRef name, const char *option) { Symbol *sym = symtab->find(name); if (!sym) return nullptr; @@ -557,8 +647,11 @@ static Symbol *handleUndefined(StringRef name) { // eliminate it. Mark the symbol as "used" to prevent it. sym->isUsedInRegularObj = true; - if (auto *lazySym = dyn_cast(sym)) + if (auto *lazySym = dyn_cast(sym)) { lazySym->fetch(); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(option, sym->getFile(), *sym); + } return sym; } @@ -570,15 +663,53 @@ static void handleLibcall(StringRef name) { if (auto *lazySym = dyn_cast(sym)) { MemoryBufferRef mb = lazySym->getMemberBuffer(); - if (isBitcode(mb)) + if (isBitcode(mb)) { + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back("", sym->getFile(), + *sym); lazySym->fetch(); + } + } +} + +static void writeWhyExtract() { + if (config->whyExtract.empty()) + return; + + std::error_code ec; + raw_fd_ostream os(config->whyExtract, ec, sys::fs::OF_None); + if (ec) { + error("cannot open --why-extract= file " + config->whyExtract + ": " + + ec.message()); + return; + } + + os << "reference\textracted\tsymbol\n"; + for (auto &entry : config->whyExtractRecords) { + os << std::get<0>(entry) << '\t' << toString(std::get<1>(entry)) << '\t' + << toString(std::get<2>(entry)) << '\n'; + } +} + +// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker +static void demoteLazySymbols() { + for (Symbol *sym : symtab->symbols()) { + if (auto* s = dyn_cast(sym)) { + if (s->signature) { + LLVM_DEBUG(llvm::dbgs() + << "demoting lazy func: " << s->getName() << "\n"); + replaceSymbol(s, s->getName(), std::nullopt, + std::nullopt, WASM_SYMBOL_BINDING_WEAK, + s->getFile(), s->signature); + } + } } } static UndefinedGlobal * createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) { auto *sym = cast(symtab->addUndefinedGlobal( - name, None, None, WASM_SYMBOL_UNDEFINED, nullptr, type)); + name, std::nullopt, std::nullopt, WASM_SYMBOL_UNDEFINED, nullptr, type)); config->allowUndefinedSymbols.insert(sym->getName()); sym->isUsedInRegularObj = true; return sym; @@ -586,7 +717,7 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) { static InputGlobal *createGlobal(StringRef name, bool isMutable) { llvm::wasm::WasmGlobal wasmGlobal; - bool is64 = config->is64.getValueOr(false); + bool is64 = config->is64.value_or(false); wasmGlobal.Type = {uint8_t(is64 ? WASM_TYPE_I64 : WASM_TYPE_I32), isMutable}; wasmGlobal.InitExpr = intConst(0, is64); wasmGlobal.SymbolName = name; @@ -621,18 +752,18 @@ static void createSyntheticSymbols() { "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN, make(nullSignature, "__wasm_call_ctors")); - bool is64 = config->is64.getValueOr(false); + bool is64 = config->is64.value_or(false); if (config->isPic) { WasmSym::stackPointer = - createUndefinedGlobal("__stack_pointer", config->is64.getValueOr(false) + createUndefinedGlobal("__stack_pointer", config->is64.value_or(false) ? &mutableGlobalTypeI64 : &mutableGlobalTypeI32); // For PIC code, we import two global variables (__memory_base and // __table_base) from the environment and use these as the offset at // which to load our static data and function table. // See: - // https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md + // https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32; WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType); WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType); @@ -651,7 +782,7 @@ static void createSyntheticSymbols() { WasmSym::stackPointer->markLive(); } - if (config->sharedMemory && !config->relocatable) { + if (config->sharedMemory) { WasmSym::tlsBase = createGlobalVariable("__tls_base", true); WasmSym::tlsSize = createGlobalVariable("__tls_size", false); WasmSym::tlsAlign = createGlobalVariable("__tls_align", false); @@ -661,6 +792,17 @@ static void createSyntheticSymbols() { is64 ? i64ArgSignature : i32ArgSignature, "__wasm_init_tls")); } + + if (config->isPic || + config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) { + // For PIC code, or when dynamically importing addresses, we create + // synthetic functions that apply relocations. These get called from + // __wasm_call_ctors before the user-level constructors. + WasmSym::applyDataRelocs = symtab->addSyntheticFunction( + "__wasm_apply_data_relocs", + WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, + make(nullSignature, "__wasm_apply_data_relocs")); + } } static void createOptionalSymbols() { @@ -673,18 +815,21 @@ static void createOptionalSymbols() { WasmSym::dataEnd = symtab->addOptionalDataSymbol("__data_end"); if (!config->isPic) { + WasmSym::stackLow = symtab->addOptionalDataSymbol("__stack_low"); + WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high"); WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base"); WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base"); + WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end"); WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base"); WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base"); - if (config->is64.getValueOr(false)) + if (config->is64.value_or(false)) WasmSym::definedTableBase32 = symtab->addOptionalDataSymbol("__table_base32"); } // For non-shared memory programs we still need to define __tls_base since we // allow object files built with TLS to be linked into single threaded - // programs, and such object files can contains refernced to this symbol. + // programs, and such object files can contain references to this symbol. // // However, in this case __tls_base is immutable and points directly to the // start of the `.tdata` static segment. @@ -695,6 +840,53 @@ static void createOptionalSymbols() { WasmSym::tlsBase = createOptionalGlobal("__tls_base", false); } +static void processStubLibraries() { + log("-- processStubLibraries"); + for (auto &stub_file : symtab->stubFiles) { + LLVM_DEBUG(llvm::dbgs() + << "processing stub file: " << stub_file->getName() << "\n"); + for (auto [name, deps]: stub_file->symbolDependencies) { + auto* sym = symtab->find(name); + if (!sym || !sym->isUndefined() || !sym->isUsedInRegularObj || + sym->forceImport) { + LLVM_DEBUG(llvm::dbgs() << "stub not in needed: " << name << "\n"); + continue; + } + // The first stub library to define a given symbol sets this and + // definitions in later stub libraries are ignored. + sym->forceImport = true; + if (sym->traced) + message(toString(stub_file) + ": importing " + name); + else + LLVM_DEBUG(llvm::dbgs() + << toString(stub_file) << ": importing " << name << "\n"); + for (const auto dep : deps) { + auto* needed = symtab->find(dep); + if (!needed) { + error(toString(stub_file) + ": undefined symbol: " + dep + + ". Required by " + toString(*sym)); + } else if (needed->isUndefined()) { + error(toString(stub_file) + + ": undefined symbol: " + toString(*needed) + + ". Required by " + toString(*sym)); + } else { + LLVM_DEBUG(llvm::dbgs() + << "force export: " << toString(*needed) << "\n"); + needed->forceExport = true; + needed->isUsedInRegularObj = true; + if (auto *lazy = dyn_cast(needed)) { + lazy->fetch(); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(stub_file->getName(), + sym->getFile(), *sym); + } + } + } + } + } + log("-- done processStubLibraries"); +} + // Reconstructs command line arguments so that so that you can re-run // the same command with the same inputs. This is for --reproduce. static std::string createResponseFile(const opt::InputArgList &args) { @@ -738,8 +930,9 @@ struct WrappedSymbol { }; static Symbol *addUndefined(StringRef name) { - return symtab->addUndefinedFunction(name, None, None, WASM_SYMBOL_UNDEFINED, - nullptr, nullptr, false); + return symtab->addUndefinedFunction(name, std::nullopt, std::nullopt, + WASM_SYMBOL_UNDEFINED, nullptr, nullptr, + false); } // Handles -wrap option. @@ -760,8 +953,8 @@ static std::vector addWrappedSymbols(opt::InputArgList &args) { if (!sym) continue; - Symbol *real = addUndefined(saver.save("__real_" + name)); - Symbol *wrap = addUndefined(saver.save("__wrap_" + name)); + Symbol *real = addUndefined(saver().save("__real_" + name)); + Symbol *wrap = addUndefined(saver().save("__wrap_" + name)); v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten @@ -818,10 +1011,28 @@ static void splitSections() { }); } +static bool isKnownZFlag(StringRef s) { + // For now, we only support a very limited set of -z flags + return s.startswith("stack-size="); +} + +// Report a warning for an unknown -z option. +static void checkZOptions(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_z)) + if (!isKnownZFlag(arg->getValue())) + warn("unknown -z value: " + StringRef(arg->getValue())); +} + void LinkerDriver::linkerMain(ArrayRef argsArr) { WasmOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); + // Interpret these flags early because error()/warn() depend on them. + errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); + errorHandler().fatalWarnings = + args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); + checkZOptions(args); + // Handle --help if (args.hasArg(OPT_help)) { parser.printHelp(lld::outs(), @@ -837,8 +1048,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } // Handle --reproduce - if (auto *arg = args.getLastArg(OPT_reproduce)) { - StringRef path = arg->getValue(); + if (const char *path = getReproduceOption(args)) { Expected> errOrWriter = TarWriter::create(path, path::stem(path)); if (errOrWriter) { @@ -858,15 +1068,13 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { cl::ResetAllOptionOccurrences(); cl::ParseCommandLineOptions(v.size(), v.data()); - errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); - readConfigs(args); + setConfigs(); createFiles(args); if (errorCount()) return; - setConfigs(); checkOptions(args); if (errorCount()) return; @@ -907,16 +1115,16 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle the `--undefined ` options. for (auto *arg : args.filtered(OPT_undefined)) - handleUndefined(arg->getValue()); + handleUndefined(arg->getValue(), ""); // Handle the `--export ` options // This works like --undefined but also exports the symbol if its found for (auto &iter : config->exportedSymbols) - handleUndefined(iter.first()); + handleUndefined(iter.first(), "--export"); Symbol *entrySym = nullptr; if (!config->relocatable && !config->entry.empty()) { - entrySym = handleUndefined(config->entry); + entrySym = handleUndefined(config->entry, "--entry"); if (entrySym && entrySym->isDefined()) entrySym->forceExport = true; else @@ -933,7 +1141,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { !WasmSym::callCtors->isUsedInRegularObj && WasmSym::callCtors->getName() != config->entry && !config->exportedSymbols.count(WasmSym::callCtors->getName())) { - if (Symbol *callDtors = handleUndefined("__wasm_call_dtors")) { + if (Symbol *callDtors = + handleUndefined("__wasm_call_dtors", "")) { if (auto *callDtorsFunc = dyn_cast(callDtors)) { if (callDtorsFunc->signature && (!callDtorsFunc->signature->Params.empty() || @@ -947,8 +1156,6 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } } - createOptionalSymbols(); - if (errorCount()) return; @@ -970,12 +1177,18 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (errorCount()) return; + writeWhyExtract(); + // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. - symtab->addCombinedLTOObject(); + symtab->compileBitcodeFiles(); if (errorCount()) return; + processStubLibraries(); + + createOptionalSymbols(); + // Resolve any variant symbols that were created due to signature // mismatchs. symtab->handleSymbolVariants(); @@ -1008,6 +1221,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // collection. splitSections(); + // Any remaining lazy symbols should be demoted to Undefined + demoteLazySymbols(); + // Do size optimizations: garbage collection markLive(); diff --git a/gnu/llvm/lld/wasm/InputChunks.cpp b/gnu/llvm/lld/wasm/InputChunks.cpp index 1207f523f2e..53b5288801d 100644 --- a/gnu/llvm/lld/wasm/InputChunks.cpp +++ b/gnu/llvm/lld/wasm/InputChunks.cpp @@ -51,7 +51,7 @@ bool relocIs64(uint8_t relocType) { } std::string toString(const wasm::InputChunk *c) { - return (toString(c->file) + ":(" + c->getName() + ")").str(); + return (toString(c->file) + ":(" + c->name + ")").str(); } namespace wasm { @@ -116,7 +116,10 @@ void InputChunk::relocate(uint8_t *buf) const { LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName()); LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index << " offset=" << rel.Offset << "\n"); - auto value = file->calcNewValue(rel, tombstone, this); + // TODO(sbc): Check that the value is within the range of the + // relocation type below. Most likely we must error out here + // if its not with range. + uint64_t value = file->calcNewValue(rel, tombstone, this); switch (rel.Type) { case R_WASM_TYPE_INDEX_LEB: @@ -125,7 +128,7 @@ void InputChunk::relocate(uint8_t *buf) const { case R_WASM_TAG_INDEX_LEB: case R_WASM_MEMORY_ADDR_LEB: case R_WASM_TABLE_NUMBER_LEB: - encodeULEB128(value, loc, 5); + encodeULEB128(static_cast(value), loc, 5); break; case R_WASM_MEMORY_ADDR_LEB64: encodeULEB128(value, loc, 10); @@ -193,14 +196,14 @@ uint64_t InputChunk::getTombstone() const { } void InputFunction::setFunctionIndex(uint32_t index) { - LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName() - << " -> " << index << "\n"); + LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> " + << index << "\n"); assert(!hasFunctionIndex()); functionIndex = index; } void InputFunction::setTableIndex(uint32_t index) { - LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> " + LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> " << index << "\n"); assert(!hasTableIndex()); tableIndex = index; @@ -268,7 +271,7 @@ void InputFunction::calculateSize() { if (!file || !config->compressRelocations) return; - LLVM_DEBUG(dbgs() << "calculateSize: " << getName() << "\n"); + LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n"); const uint8_t *secStart = file->codeSection->Content.data(); const uint8_t *funcStart = secStart + getInputSectionOffset(); @@ -315,7 +318,7 @@ void InputFunction::writeCompressed(uint8_t *buf) const { decodeULEB128(funcStart, &count); funcStart += count; - LLVM_DEBUG(dbgs() << "write func: " << getName() << "\n"); + LLVM_DEBUG(dbgs() << "write func: " << name << "\n"); buf += encodeULEB128(compressedFuncSize, buf); const uint8_t *lastRelocEnd = funcStart; for (const WasmRelocation &rel : relocations) { @@ -336,7 +339,7 @@ void InputFunction::writeCompressed(uint8_t *buf) const { uint64_t InputChunk::getChunkOffset(uint64_t offset) const { if (const auto *ms = dyn_cast(this)) { - LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << getName() << "\n"); + LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n"); LLVM_DEBUG(dbgs() << "offset: " << offset << "\n"); LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset) << "\n"); @@ -355,13 +358,13 @@ uint64_t InputChunk::getVA(uint64_t offset) const { } // Generate code to apply relocations to the data section at runtime. -// This is only called when generating shared libaries (PIC) where address are +// This is only called when generating shared libraries (PIC) where address are // not known at static link time. void InputChunk::generateRelocationCode(raw_ostream &os) const { - LLVM_DEBUG(dbgs() << "generating runtime relocations: " << getName() + LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name << " count=" << relocations.size() << "\n"); - bool is64 = config->is64.getValueOr(false); + bool is64 = config->is64.value_or(false); unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD @@ -373,19 +376,26 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const { for (const WasmRelocation &rel : relocations) { uint64_t offset = getVA(rel.Offset) - getInputSectionOffset(); + Symbol *sym = file->getSymbol(rel); + if (!config->isPic && sym->isDefined()) + continue; + LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type) << " addend=" << rel.Addend << " index=" << rel.Index << " output offset=" << offset << "\n"); - // Get __memory_base - writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); - writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); - - // Add the offset of the relocation + // Calculate the address at which to apply the relocations writeU8(os, opcode_ptr_const, "CONST"); writeSleb128(os, offset, "offset"); - writeU8(os, opcode_ptr_add, "ADD"); + // In PIC mode we need to add the __memory_base + if (config->isPic) { + writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); + writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); + writeU8(os, opcode_ptr_add, "ADD"); + } + + // Now figure out what we want to store at this location bool is64 = relocIs64(rel.Type); unsigned opcode_reloc_const = is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; @@ -394,8 +404,6 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const { unsigned opcode_reloc_store = is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE; - Symbol *sym = file->getSymbol(rel); - // Now figure out what we want to store if (sym->hasGOTIndex()) { writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, sym->getGOTIndex(), "global index"); @@ -405,6 +413,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const { writeU8(os, opcode_reloc_add, "ADD"); } } else { + assert(config->isPic); const GlobalSymbol* baseSymbol = WasmSym::memoryBase; if (rel.Type == R_WASM_TABLE_INDEX_I32 || rel.Type == R_WASM_TABLE_INDEX_I64) diff --git a/gnu/llvm/lld/wasm/InputChunks.h b/gnu/llvm/lld/wasm/InputChunks.h index f1174d937ad..1e430832fb8 100644 --- a/gnu/llvm/lld/wasm/InputChunks.h +++ b/gnu/llvm/lld/wasm/InputChunks.h @@ -27,6 +27,7 @@ #include "llvm/ADT/CachedHashString.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/Wasm.h" +#include namespace lld { namespace wasm { @@ -49,8 +50,6 @@ public: StringRef name; StringRef debugName; - StringRef getName() const { return name; } - StringRef getDebugName() const { return debugName; } Kind kind() const { return (Kind)sectionKind; } uint32_t getSize() const; @@ -81,12 +80,7 @@ public: void writeRelocations(llvm::raw_ostream &os) const; void generateRelocationCode(raw_ostream &os) const; - bool isTLS() const { - // Older object files don't include WASM_SEG_FLAG_TLS and instead - // relied on the naming convention. - return flags & llvm::wasm::WASM_SEG_FLAG_TLS || name.startswith(".tdata") || - name.startswith(".tbss"); - } + bool isTLS() const { return flags & llvm::wasm::WASM_SEG_FLAG_TLS; } ObjFile *file; OutputSection *outputSec = nullptr; @@ -230,7 +224,8 @@ class SyntheticMergedChunk : public InputChunk { public: SyntheticMergedChunk(StringRef name, uint32_t alignment, uint32_t flags) : InputChunk(nullptr, InputChunk::MergedChunk, name, alignment, flags), - builder(llvm::StringTableBuilder::RAW, 1ULL << alignment) {} + builder(llvm::StringTableBuilder::RAW, llvm::Align(1ULL << alignment)) { + } static bool classof(const InputChunk *c) { return c->kind() == InputChunk::MergedChunk; @@ -256,9 +251,9 @@ class InputFunction : public InputChunk { public: InputFunction(const WasmSignature &s, const WasmFunction *func, ObjFile *f) : InputChunk(f, InputChunk::Function, func->SymbolName), signature(s), - function(func), exportName(func && func->ExportName.hasValue() - ? (*func->ExportName).str() - : llvm::Optional()) { + function(func), + exportName(func && func->ExportName ? (*func->ExportName).str() + : std::optional()) { inputSectionOffset = function->CodeSectionOffset; rawData = file->codeSection->Content.slice(inputSectionOffset, function->Size); @@ -274,18 +269,18 @@ public: c->kind() == InputChunk::SyntheticFunction; } - llvm::Optional getExportName() const { - return exportName.hasValue() ? llvm::Optional(*exportName) - : llvm::Optional(); + std::optional getExportName() const { + return exportName ? std::optional(*exportName) + : std::optional(); } void setExportName(std::string exportName) { this->exportName = exportName; } uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); } uint32_t getFunctionCodeOffset() const { return function->CodeOffset; } - uint32_t getFunctionIndex() const { return functionIndex.getValue(); } - bool hasFunctionIndex() const { return functionIndex.hasValue(); } + uint32_t getFunctionIndex() const { return *functionIndex; } + bool hasFunctionIndex() const { return functionIndex.has_value(); } void setFunctionIndex(uint32_t index); - uint32_t getTableIndex() const { return tableIndex.getValue(); } - bool hasTableIndex() const { return tableIndex.hasValue(); } + uint32_t getTableIndex() const { return *tableIndex; } + bool hasTableIndex() const { return tableIndex.has_value(); } void setTableIndex(uint32_t index); void writeCompressed(uint8_t *buf) const; @@ -305,9 +300,9 @@ public: const WasmFunction *function; protected: - llvm::Optional exportName; - llvm::Optional functionIndex; - llvm::Optional tableIndex; + std::optional exportName; + std::optional functionIndex; + std::optional tableIndex; uint32_t compressedFuncSize = 0; uint32_t compressedSize = 0; }; diff --git a/gnu/llvm/lld/wasm/InputElement.h b/gnu/llvm/lld/wasm/InputElement.h index d00e60b4ae9..46e21d7c7df 100644 --- a/gnu/llvm/lld/wasm/InputElement.h +++ b/gnu/llvm/lld/wasm/InputElement.h @@ -14,6 +14,7 @@ #include "WriterUtils.h" #include "lld/Common/LLVM.h" #include "llvm/Object/Wasm.h" +#include namespace lld { namespace wasm { @@ -27,8 +28,8 @@ protected: public: StringRef getName() const { return name; } - uint32_t getAssignedIndex() const { return assignedIndex.getValue(); } - bool hasAssignedIndex() const { return assignedIndex.hasValue(); } + uint32_t getAssignedIndex() const { return *assignedIndex; } + bool hasAssignedIndex() const { return assignedIndex.has_value(); } void assignIndex(uint32_t index) { assert(!hasAssignedIndex()); assignedIndex = index; @@ -39,17 +40,18 @@ public: protected: StringRef name; - llvm::Optional assignedIndex; + std::optional assignedIndex; }; inline WasmInitExpr intConst(uint64_t value, bool is64) { WasmInitExpr ie; + ie.Extended = false; if (is64) { - ie.Opcode = llvm::wasm::WASM_OPCODE_I64_CONST; - ie.Value.Int64 = static_cast(value); + ie.Inst.Opcode = llvm::wasm::WASM_OPCODE_I64_CONST; + ie.Inst.Value.Int64 = static_cast(value); } else { - ie.Opcode = llvm::wasm::WASM_OPCODE_I32_CONST; - ie.Value.Int32 = static_cast(value); + ie.Inst.Opcode = llvm::wasm::WASM_OPCODE_I32_CONST; + ie.Inst.Value.Int32 = static_cast(value); } return ie; } @@ -63,7 +65,7 @@ public: const WasmInitExpr &getInitExpr() const { return initExpr; } void setPointerValue(uint64_t value) { - initExpr = intConst(value, config->is64.getValueOr(false)); + initExpr = intConst(value, config->is64.value_or(false)); } private: @@ -74,14 +76,9 @@ private: class InputTag : public InputElement { public: InputTag(const WasmSignature &s, const WasmTag &t, ObjFile *f) - : InputElement(t.SymbolName, f), signature(s), type(t.Type) {} - - const WasmTagType &getType() const { return type; } + : InputElement(t.SymbolName, f), signature(s) {} const WasmSignature &signature; - -private: - WasmTagType type; }; class InputTable : public InputElement { diff --git a/gnu/llvm/lld/wasm/InputFiles.cpp b/gnu/llvm/lld/wasm/InputFiles.cpp index 35d8dd1ed7b..a03166da973 100644 --- a/gnu/llvm/lld/wasm/InputFiles.cpp +++ b/gnu/llvm/lld/wasm/InputFiles.cpp @@ -12,19 +12,22 @@ #include "InputElement.h" #include "OutputSegment.h" #include "SymbolTable.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/Args.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Reproduce.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Wasm.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" +#include #define DEBUG_TYPE "lld" using namespace llvm; using namespace llvm::object; using namespace llvm::wasm; +using namespace llvm::sys; namespace lld { @@ -43,10 +46,10 @@ namespace wasm { void InputFile::checkArch(Triple::ArchType arch) const { bool is64 = arch == Triple::wasm64; - if (is64 && !config->is64.hasValue()) { + if (is64 && !config->is64) { fatal(toString(this) + ": must specify -mwasm64 to process wasm64 object files"); - } else if (config->is64.getValueOr(false) != is64) { + } else if (config->is64.value_or(false) != is64) { fatal(toString(this) + ": wasm32 object file can't be linked in wasm64 mode"); } @@ -54,13 +57,13 @@ void InputFile::checkArch(Triple::ArchType arch) const { std::unique_ptr tar; -Optional readFile(StringRef path) { +std::optional readFile(StringRef path) { log("Loading: " + path); auto mbOrErr = MemoryBuffer::getFile(path); if (auto ec = mbOrErr.getError()) { error("cannot open " + path + ": " + ec.message()); - return None; + return std::nullopt; } std::unique_ptr &mb = *mbOrErr; MemoryBufferRef mbref = mb->getMemBufferRef(); @@ -71,7 +74,8 @@ Optional readFile(StringRef path) { return mbref; } -InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName) { +InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) { file_magic magic = identify_magic(mb.getBuffer()); if (magic == file_magic::wasm_object) { std::unique_ptr bin = @@ -83,18 +87,14 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName) { } if (magic == file_magic::bitcode) - return make(mb, archiveName); + return make(mb, archiveName, offsetInArchive); - fatal("unknown file type: " + mb.getBufferIdentifier()); -} + std::string name = mb.getBufferIdentifier().str(); + if (!archiveName.empty()) { + name = archiveName.str() + "(" + name + ")"; + } -void ObjFile::dumpInfo() const { - log("info for: " + toString(this) + - "\n Symbols : " + Twine(symbols.size()) + - "\n Function Imports : " + Twine(wasmObj->getNumImportedFunctions()) + - "\n Global Imports : " + Twine(wasmObj->getNumImportedGlobals()) + - "\n Tag Imports : " + Twine(wasmObj->getNumImportedTags()) + - "\n Table Imports : " + Twine(wasmObj->getNumImportedTables())); + fatal("unknown file type: " + name); } // Relocations contain either symbol or type indices. This function takes a @@ -113,7 +113,7 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &reloc) const { // Relocations can contain addend for combined sections. This function takes a // relocation and returns updated addend by offset in the output section. -uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { +int64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { switch (reloc.Type) { case R_WASM_MEMORY_ADDR_LEB: case R_WASM_MEMORY_ADDR_LEB64: @@ -175,18 +175,12 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, case R_WASM_MEMORY_ADDR_REL_SLEB64: case R_WASM_MEMORY_ADDR_I32: case R_WASM_MEMORY_ADDR_I64: + case R_WASM_MEMORY_ADDR_TLS_SLEB: + case R_WASM_MEMORY_ADDR_TLS_SLEB64: case R_WASM_MEMORY_ADDR_LOCREL_I32: { if (isa(sym) || sym->isUndefWeak()) return 0; auto D = cast(sym); - // Treat non-TLS relocation against symbols that live in the TLS segment - // like TLS relocations. This beaviour exists to support older object - // files created before we introduced TLS relocations. - // TODO(sbc): Remove this legacy behaviour one day. This will break - // backward compat with old object files built with `-fPIC`. - if (D->segment && D->segment->outputSeg->isTLS()) - return D->getOutputSegmentOffset() + reloc.Addend; - uint64_t value = D->getVA() + reloc.Addend; if (reloc.Type == R_WASM_MEMORY_ADDR_LOCREL_I32) { const auto *segment = cast(chunk); @@ -196,12 +190,6 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, } return value; } - case R_WASM_MEMORY_ADDR_TLS_SLEB: - case R_WASM_MEMORY_ADDR_TLS_SLEB64: - if (isa(sym) || sym->isUndefWeak()) - return 0; - // TLS relocations are relative to the start of the TLS output segment - return cast(sym)->getOutputSegmentOffset() + reloc.Addend; case R_WASM_TYPE_INDEX_LEB: return typeMap[reloc.Index]; case R_WASM_FUNCTION_INDEX_LEB: @@ -215,6 +203,9 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, return getTagSymbol(reloc.Index)->getTagIndex(); case R_WASM_FUNCTION_OFFSET_I32: case R_WASM_FUNCTION_OFFSET_I64: { + if (isa(sym)) { + return tombstone ? tombstone : reloc.Addend; + } auto *f = cast(sym); return f->function->getOffset(f->function->getFunctionCodeOffset() + reloc.Addend); @@ -345,10 +336,9 @@ void ObjFile::addLegacyIndirectFunctionTableIfNeeded( LLVM_DEBUG(dbgs() << "Synthesizing symbol for table import: " << info->Name << "\n"); const WasmGlobalType *globalType = nullptr; - const WasmTagType *tagType = nullptr; const WasmSignature *signature = nullptr; - auto *wasmSym = make(*info, globalType, &tableImport->Table, - tagType, signature); + auto *wasmSym = + make(*info, globalType, &tableImport->Table, signature); Symbol *sym = createUndefined(*wasmSym, false); // We're only sure it's a TableSymbol if the createUndefined succeeded. if (errorCount()) @@ -424,10 +414,12 @@ void ObjFile::parse(bool ignoreComdats) { tableEntries.resize(totalFunctions); for (const WasmElemSegment &seg : wasmObj->elements()) { int64_t offset; - if (seg.Offset.Opcode == WASM_OPCODE_I32_CONST) - offset = seg.Offset.Value.Int32; - else if (seg.Offset.Opcode == WASM_OPCODE_I64_CONST) - offset = seg.Offset.Value.Int64; + if (seg.Offset.Extended) + fatal(toString(this) + ": extended init exprs not supported"); + else if (seg.Offset.Inst.Opcode == WASM_OPCODE_I32_CONST) + offset = seg.Offset.Inst.Value.Int32; + else if (seg.Offset.Inst.Opcode == WASM_OPCODE_I64_CONST) + offset = seg.Offset.Inst.Value.Int64; else fatal(toString(this) + ": invalid table elements"); for (size_t index = 0; index < seg.Functions.size(); index++) { @@ -486,24 +478,28 @@ void ObjFile::parse(bool ignoreComdats) { // Populate `Segments`. for (const WasmSegment &s : wasmObj->dataSegments()) { InputChunk *seg; - if (shouldMerge(s)) { + if (shouldMerge(s)) seg = make(s, this); - } else + else seg = make(s, this); seg->discarded = isExcludedByComdat(seg); - + // Older object files did not include WASM_SEG_FLAG_TLS and instead + // relied on the naming convention. To maintain compat with such objects + // we still imply the TLS flag based on the name of the segment. + if (!seg->isTLS() && + (seg->name.startswith(".tdata") || seg->name.startswith(".tbss"))) + seg->flags |= WASM_SEG_FLAG_TLS; segments.emplace_back(seg); } setRelocs(segments, dataSection); // Populate `Functions`. ArrayRef funcs = wasmObj->functions(); - ArrayRef funcTypes = wasmObj->functionTypes(); ArrayRef types = wasmObj->types(); functions.reserve(funcs.size()); - for (size_t i = 0, e = funcs.size(); i != e; ++i) { - auto* func = make(types[funcTypes[i]], &funcs[i], this); + for (auto &f : funcs) { + auto *func = make(types[f.SigIndex], &f, this); func->discarded = isExcludedByComdat(func); functions.emplace_back(func); } @@ -519,7 +515,7 @@ void ObjFile::parse(bool ignoreComdats) { // Populate `Tags`. for (const WasmTag &t : wasmObj->tags()) - tags.emplace_back(make(types[t.Type.SigIndex], t, this)); + tags.emplace_back(make(types[t.SigIndex], t, this)); // Populate `Symbols` based on the symbols in the object. symbols.reserve(wasmObj->getNumberOfSymbols()); @@ -543,7 +539,7 @@ void ObjFile::parse(bool ignoreComdats) { addLegacyIndirectFunctionTableIfNeeded(tableSymbolCount); } -bool ObjFile::isExcludedByComdat(InputChunk *chunk) const { +bool ObjFile::isExcludedByComdat(const InputChunk *chunk) const { uint32_t c = chunk->getComdat(); if (c == UINT32_MAX) return false; @@ -592,6 +588,11 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) { InputChunk *seg = segments[sym.Info.DataRef.Segment]; auto offset = sym.Info.DataRef.Offset; auto size = sym.Info.DataRef.Size; + // Support older (e.g. llvm 13) object files that pre-date the per-symbol + // TLS flag, and symbols were assumed to be TLS by being defined in a TLS + // segment. + if (!(flags & WASM_SYMBOL_TLS) && seg->isTLS()) + flags |= WASM_SYMBOL_TLS; if (sym.isBindingLocal()) return make(name, flags, this, seg, offset, size); if (seg->discarded) @@ -664,12 +665,66 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &sym, bool isCalledDirectly) { return symtab->addUndefinedTable(name, sym.Info.ImportName, sym.Info.ImportModule, flags, this, sym.TableType); + case WASM_SYMBOL_TYPE_TAG: + if (sym.isBindingLocal()) + return make(name, sym.Info.ImportName, + sym.Info.ImportModule, flags, this, + sym.Signature); + return symtab->addUndefinedTag(name, sym.Info.ImportName, + sym.Info.ImportModule, flags, this, + sym.Signature); case WASM_SYMBOL_TYPE_SECTION: llvm_unreachable("section symbols cannot be undefined"); } llvm_unreachable("unknown symbol kind"); } + +StringRef strip(StringRef s) { + while (s.starts_with(" ")) { + s = s.drop_front(); + } + while (s.ends_with(" ")) { + s = s.drop_back(); + } + return s; +} + +void StubFile::parse() { + bool first = true; + + SmallVector lines; + mb.getBuffer().split(lines, '\n'); + for (StringRef line : lines) { + line = line.trim(); + + // File must begin with #STUB + if (first) { + assert(line == "#STUB"); + first = false; + } + + // Lines starting with # are considered comments + if (line.startswith("#")) + continue; + + StringRef sym; + StringRef rest; + std::tie(sym, rest) = line.split(':'); + sym = strip(sym); + rest = strip(rest); + + symbolDependencies[sym] = {}; + + while (rest.size()) { + StringRef dep; + std::tie(dep, rest) = rest.split(','); + dep = strip(dep); + symbolDependencies[sym].push_back(dep); + } + } +} + void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. LLVM_DEBUG(dbgs() << "Parsing library: " << toString(this) << "\n"); @@ -682,6 +737,7 @@ void ArchiveFile::parse() { ++count; } LLVM_DEBUG(dbgs() << "Read " << count << " symbols\n"); + (void) count; } void ArchiveFile::addMember(const Archive::Symbol *sym) { @@ -702,7 +758,7 @@ void ArchiveFile::addMember(const Archive::Symbol *sym) { "could not get the buffer for the member defining symbol " + sym->getName()); - InputFile *obj = createObjectFile(mb, getName()); + InputFile *obj = createObjectFile(mb, getName(), c.getChildOffset()); symtab->addFile(obj); } @@ -720,7 +776,7 @@ static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { static Symbol *createBitcodeSymbol(const std::vector &keptComdats, const lto::InputFile::Symbol &objSym, BitcodeFile &f) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); uint32_t flags = objSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0; flags |= mapVisibility(objSym.getVisibility()); @@ -731,8 +787,8 @@ static Symbol *createBitcodeSymbol(const std::vector &keptComdats, if (objSym.isUndefined() || excludedByComdat) { flags |= WASM_SYMBOL_UNDEFINED; if (objSym.isExecutable()) - return symtab->addUndefinedFunction(name, None, None, flags, &f, nullptr, - true); + return symtab->addUndefinedFunction(name, std::nullopt, std::nullopt, + flags, &f, nullptr, true); return symtab->addUndefinedData(name, flags, &f); } @@ -741,6 +797,32 @@ static Symbol *createBitcodeSymbol(const std::vector &keptComdats, return symtab->addDefinedData(name, flags, &f, nullptr, 0, 0); } +BitcodeFile::BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, m) { + this->archiveName = std::string(archiveName); + + std::string path = mb.getBufferIdentifier().str(); + + // 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. + StringRef name = archiveName.empty() + ? saver().save(path) + : saver().save(archiveName + "(" + path::filename(path) + + " at " + utostr(offsetInArchive) + ")"); + MemoryBufferRef mbref(mb.getBuffer(), name); + + obj = check(lto::InputFile::create(mbref)); + + // If this isn't part of an archive, it's eagerly linked, so mark it live. + if (archiveName.empty()) + markLive(); +} + bool BitcodeFile::doneLTO = false; void BitcodeFile::parse() { @@ -749,8 +831,6 @@ void BitcodeFile::parse() { return; } - obj = check(lto::InputFile::create(MemoryBufferRef( - mb.getBuffer(), saver.save(archiveName + mb.getBufferIdentifier())))); Triple t(obj->getTargetTriple()); if (!t.isWasm()) { error(toString(this) + ": machine type must be wasm32 or wasm64"); diff --git a/gnu/llvm/lld/wasm/InputFiles.h b/gnu/llvm/lld/wasm/InputFiles.h index f4ff8bbb1d1..4c46ae88d4e 100644 --- a/gnu/llvm/lld/wasm/InputFiles.h +++ b/gnu/llvm/lld/wasm/InputFiles.h @@ -18,6 +18,7 @@ #include "llvm/Object/Archive.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/MemoryBuffer.h" +#include #include namespace llvm { @@ -46,6 +47,7 @@ public: SharedKind, ArchiveKind, BitcodeKind, + StubKind, }; virtual ~InputFile() {} @@ -116,12 +118,10 @@ public: // Returns the underlying wasm file. const WasmObjectFile *getWasmObj() const { return wasmObj.get(); } - void dumpInfo() const; - uint32_t calcNewIndex(const WasmRelocation &reloc) const; uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, const InputChunk *chunk) const; - uint64_t calcNewAddend(const WasmRelocation &reloc) const; + int64_t calcNewAddend(const WasmRelocation &reloc) const; Symbol *getSymbol(const WasmRelocation &reloc) const { return symbols[reloc.Index]; }; @@ -156,7 +156,7 @@ private: Symbol *createDefined(const WasmSymbol &sym); Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly); - bool isExcludedByComdat(InputChunk *chunk) const; + bool isExcludedByComdat(const InputChunk *chunk) const; void addLegacyIndirectFunctionTableIfNeeded(uint32_t tableSymbolCount); std::unique_ptr wasmObj; @@ -172,14 +172,8 @@ public: // .bc file class BitcodeFile : public InputFile { public: - explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName) - : InputFile(BitcodeKind, m) { - this->archiveName = std::string(archiveName); - - // If this isn't part of an archive, it's eagerly linked, so mark it live. - if (archiveName.empty()) - markLive(); - } + BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } void parse(); @@ -190,16 +184,29 @@ public: static bool doneLTO; }; +// Stub libray (See docs/WebAssembly.rst) +class StubFile : public InputFile { +public: + explicit StubFile(MemoryBufferRef m) : InputFile(StubKind, m) {} + + static bool classof(const InputFile *f) { return f->kind() == StubKind; } + + void parse(); + + llvm::DenseMap> symbolDependencies; +}; + inline bool isBitcode(MemoryBufferRef mb) { return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; } // Will report a fatal() error if the input buffer is not a valid bitcode // or wasm object file. -InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = ""); +InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "", + uint64_t offsetInArchive = 0); // Opens a given file. -llvm::Optional readFile(StringRef path); +std::optional readFile(StringRef path); } // namespace wasm diff --git a/gnu/llvm/lld/wasm/LTO.cpp b/gnu/llvm/lld/wasm/LTO.cpp index 4f76fc0dccd..150e663ce7e 100644 --- a/gnu/llvm/lld/wasm/LTO.cpp +++ b/gnu/llvm/lld/wasm/LTO.cpp @@ -19,10 +19,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.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/Caching.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -52,11 +52,10 @@ static std::unique_ptr createLTO() { c.OptLevel = config->ltoo; c.MAttrs = getMAttrs(); c.CGOptLevel = args::getCGOptLevel(config->ltoo); - c.UseNewPM = config->ltoNewPassManager; c.DebugPassManager = config->ltoDebugPassManager; if (config->relocatable) - c.RelocModel = None; + c.RelocModel = std::nullopt; else if (config->isPic) c.RelocModel = Reloc::PIC_; else @@ -77,8 +76,9 @@ BitcodeCompiler::~BitcodeCompiler() = default; static void undefine(Symbol *s) { if (auto f = dyn_cast(s)) - replaceSymbol(f, f->getName(), None, None, 0, - f->getFile(), f->signature); + replaceSymbol(f, f->getName(), std::nullopt, + std::nullopt, 0, f->getFile(), + f->signature); else if (isa(s)) replaceSymbol(s, s->getName(), 0, s->getFile()); else @@ -127,23 +127,23 @@ std::vector BitcodeCompiler::compile() { // The --thinlto-cache-dir 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; + FileCache cache; if (!config->thinLTOCacheDir.empty()) - cache = check( - lto::localCache(config->thinLTOCacheDir, - [&](size_t task, std::unique_ptr mb) { - files[task] = std::move(mb); - })); + cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir, + [&](size_t task, const Twine &moduleName, + std::unique_ptr mb) { + files[task] = std::move(mb); + })); checkError(ltoObj->run( - [&](size_t task) { - return std::make_unique( + [&](size_t task, const Twine &moduleName) { + return std::make_unique( std::make_unique(buf[task])); }, cache)); if (!config->thinLTOCacheDir.empty()) - pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); + pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files); std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) { diff --git a/gnu/llvm/lld/wasm/MapFile.cpp b/gnu/llvm/lld/wasm/MapFile.cpp index 9dbab5046e2..288c189854d 100644 --- a/gnu/llvm/lld/wasm/MapFile.cpp +++ b/gnu/llvm/lld/wasm/MapFile.cpp @@ -28,7 +28,6 @@ #include "SyntheticSections.h" #include "lld/Common/Strings.h" #include "llvm/ADT/MapVector.h" -#include "llvm/ADT/SetVector.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" @@ -75,7 +74,7 @@ static SymbolMapTy getSectionSyms(ArrayRef syms) { static DenseMap getSymbolStrings(ArrayRef syms) { std::vector str(syms.size()); - parallelForEachN(0, syms.size(), [&](size_t i) { + parallelFor(0, syms.size(), [&](size_t i) { raw_string_ostream os(str[i]); auto *chunk = syms[i]->getChunk(); if (chunk == nullptr) diff --git a/gnu/llvm/lld/wasm/MarkLive.cpp b/gnu/llvm/lld/wasm/MarkLive.cpp index 3e5d234eecc..2b8c8e17787 100644 --- a/gnu/llvm/lld/wasm/MarkLive.cpp +++ b/gnu/llvm/lld/wasm/MarkLive.cpp @@ -90,7 +90,7 @@ void MarkLive::run() { enqueue(symtab->find(config->entry)); // We need to preserve any no-strip or exported symbol - for (Symbol *sym : symtab->getSymbols()) + for (Symbol *sym : symtab->symbols()) if (sym->isNoStrip() || sym->isExported()) enqueue(sym); diff --git a/gnu/llvm/lld/wasm/Options.td b/gnu/llvm/lld/wasm/Options.td index 40cb314c4ab..b0c727cc533 100644 --- a/gnu/llvm/lld/wasm/Options.td +++ b/gnu/llvm/lld/wasm/Options.td @@ -1,5 +1,23 @@ include "llvm/Option/OptParser.td" +// Convenience classes for long options which only accept two dashes. For lld +// specific or newer long options, we prefer two dashes to avoid collision with +// short options. For many others, we have to accept both forms to be compatible +// with GNU ld. +class FF : Flag<["--"], name>; +class JJ: Joined<["--"], name>; + +multiclass EEq { + def NAME: Separate<["--"], name>; + def NAME # _eq: Joined<["--"], name # "=">, Alias(NAME)>, + HelpText; +} + +multiclass BB { + def NAME: Flag<["--"], name>, HelpText; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText; +} + // For options whose names are multiple letters, either one dash or // two can precede the option name except those that start with 'o'. class F: Flag<["--", "-"], name>; @@ -17,14 +35,13 @@ multiclass B { def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText; } -multiclass BB { - def NAME: Flag<["--"], name>, HelpText; - def no_ # NAME: Flag<["--"], "no-" # name>, HelpText; -} - // The following flags are shared with the ELF linker def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">; +def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">; + +def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">; + defm color_diagnostics: B<"color-diagnostics", "Alias for --color-diagnostics=always", "Alias for --color-diagnostics=never">; @@ -51,8 +68,8 @@ defm export_dynamic: B<"export-dynamic", def entry: S<"entry">, MetaVarName<"">, HelpText<"Name of entry point symbol">; -def error_limit: J<"error-limit=">, - HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">; +defm error_limit: + EEq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">; def fatal_warnings: F<"fatal-warnings">, HelpText<"Treat warnings as errors">; @@ -61,21 +78,21 @@ defm gc_sections: B<"gc-sections", "Enable garbage collection of unused sections", "Disable garbage collection of unused sections">; -defm merge_data_segments: B<"merge-data-segments", +defm merge_data_segments: BB<"merge-data-segments", "Enable merging data segments", "Disable merging data segments">; def help: F<"help">, HelpText<"Print option help">; -def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"">, +def library: JoinedOrSeparate<["-"], "l">, MetaVarName<"">, HelpText<"Root name of library to use">; -def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, +def library_path: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, HelpText<"Add a directory to the library search path">; def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">; -def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">; +defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">; defm Map: Eq<"Map", "Print a link map to the specified file">; @@ -99,7 +116,7 @@ def print_map: F<"print-map">, def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; -defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">; +defm reproduce: EEq<"reproduce", "Dump linker invocation and input files for debugging">; defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">, MetaVarName<"[posix,windows]">; @@ -141,8 +158,9 @@ def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"