From: patrick Date: Fri, 17 Dec 2021 12:25:01 +0000 (+0000) Subject: Import LLVM 13.0.0 release. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=1cf9926b7b2f38d75866820814ced788e06a5cbf;p=openbsd Import LLVM 13.0.0 release. --- diff --git a/gnu/llvm/lld/.clang-tidy b/gnu/llvm/lld/.clang-tidy index 87ec2ff53af..acd9361b526 100644 --- a/gnu/llvm/lld/.clang-tidy +++ b/gnu/llvm/lld/.clang-tidy @@ -1,19 +1,8 @@ -# Almost identical to the top-level .clang-tidy, except that {Member,Parameter,Variable}Case use camelBack. -Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming' +InheritParentConfig: true CheckOptions: - - key: readability-identifier-naming.ClassCase - value: CamelCase - - key: readability-identifier-naming.EnumCase - value: CamelCase - - key: readability-identifier-naming.FunctionCase - value: camelBack - key: readability-identifier-naming.MemberCase value: camelBack - key: readability-identifier-naming.ParameterCase value: camelBack - - key: readability-identifier-naming.UnionCase - value: CamelCase - key: readability-identifier-naming.VariableCase value: camelBack - - key: readability-identifier-naming.IgnoreMainLikeFunctions - value: 1 diff --git a/gnu/llvm/lld/CMakeLists.txt b/gnu/llvm/lld/CMakeLists.txt index 040bb2c8f6d..2e99564f4e3 100644 --- a/gnu/llvm/lld/CMakeLists.txt +++ b/gnu/llvm/lld/CMakeLists.txt @@ -1,7 +1,7 @@ # Check if lld is built as a standalone project. if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) project(lld) - cmake_minimum_required(VERSION 3.4.3) + cmake_minimum_required(VERSION 3.13.4) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(LLD_BUILT_STANDALONE TRUE) @@ -35,6 +35,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR) + file(TO_CMAKE_PATH ${LLVM_CMAKE_PATH} LLVM_CMAKE_PATH) if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") message(FATAL_ERROR "LLVMConfig.cmake not found") @@ -54,42 +55,12 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) include(AddLLVM) include(TableGen) include(HandleLLVMOptions) + include(GetErrcMessages) include(CheckAtomic) if(LLVM_INCLUDE_TESTS) - if(CMAKE_VERSION VERSION_LESS 3.12) - include(FindPythonInterp) - if(NOT PYTHONINTERP_FOUND) - message(FATAL_ERROR - "Unable to find Python interpreter, required for testing. - - Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") - endif() - - if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7) - message(FATAL_ERROR "Python 2.7 or newer is required") - endif() - - add_executable(Python3::Interpeter IMPORTED) - set_target_properties(Python3::Interpreter PROPERTIES - IMPORTED_LOCATION ${PYTHON_EXECUTABLE}) - set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) - else() - find_package(Python3 COMPONENTS Interpreter) - if(NOT Python3_Interpreter_FOUND) - message(WARNING "Python3 not found, using python2 as a fallback") - find_package(Python2 COMPONENTS Interpreter REQUIRED) - if(Python2_VERSION VERSION_LESS 2.7) - message(SEND_ERROR "Python 2.7 or newer is required") - endif() - - # Treat python2 as python3 - add_executable(Python3::Interpreter IMPORTED) - set_target_properties(Python3::Interpreter PROPERTIES - IMPORTED_LOCATION ${Python2_EXECUTABLE}) - set(Python3_EXECUTABLE ${Python2_EXECUTABLE}) - endif() - endif() + find_package(Python3 ${LLVM_MINIMUM_PYTHON_VERSION} REQUIRED + COMPONENTS Interpreter) # Check prebuilt llvm/utils. if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} @@ -129,6 +100,8 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + get_errc_messages(LLVM_LIT_ERRC_MESSAGES) + # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. if(WIN32 AND NOT CYGWIN) set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") @@ -137,6 +110,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(LLVM_INCLUDE_TESTS OFF) endif() endif() + + if(LLVM_HAVE_LIBXAR) + set(XAR_LIB xar) + endif() endif() set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) @@ -193,6 +170,12 @@ endif() option(LLD_BUILD_TOOLS "Build the lld tools. If OFF, just generate build targets." ON) +option(LLD_DEFAULT_LD_LLD_IS_MINGW + "Use MinGW as the default backend for ld.lld. If OFF, ELF will be used." OFF) +if (LLD_DEFAULT_LD_LLD_IS_MINGW) + add_definitions("-DLLD_DEFAULT_LD_LLD_IS_MINGW=1") +endif() + if (MSVC) add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.' add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header. @@ -208,7 +191,6 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) DESTINATION include FILES_MATCHING PATTERN "*.h" - PATTERN ".svn" EXCLUDE ) endif() diff --git a/gnu/llvm/lld/CODE_OWNERS.TXT b/gnu/llvm/lld/CODE_OWNERS.TXT index f019a87553a..44972c0d345 100644 --- a/gnu/llvm/lld/CODE_OWNERS.TXT +++ b/gnu/llvm/lld/CODE_OWNERS.TXT @@ -15,8 +15,12 @@ D: COFF, ELF backends (COFF/* ELF/*) N: Lang Hames, Nick Kledzik E: lhames@gmail.com, kledzik@apple.com -D: Mach-O backend +D: Old Mach-O backend N: Sam Clegg E: sbc@chromium.org D: WebAssembly backend (wasm/*) + +N: Jez Ng, Greg McGary, Shoaib Meenai +E: jezng@fb.com, gkm@fb.com, smeenai@fb.com +D: New Mach-O backend diff --git a/gnu/llvm/lld/COFF/CMakeLists.txt b/gnu/llvm/lld/COFF/CMakeLists.txt index 4592ace373e..bbcd337b210 100644 --- a/gnu/llvm/lld/COFF/CMakeLists.txt +++ b/gnu/llvm/lld/COFF/CMakeLists.txt @@ -2,11 +2,8 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(COFFOptionsTableGen) -if(NOT LLD_BUILT_STANDALONE) - set(tablegen_deps intrinsics_gen) -endif() - add_lld_library(lldCOFF + CallGraphSort.cpp Chunks.cpp DebugTypes.cpp DLL.cpp @@ -48,5 +45,5 @@ add_lld_library(lldCOFF DEPENDS COFFOptionsTableGen - ${tablegen_deps} + intrinsics_gen ) diff --git a/gnu/llvm/lld/COFF/CallGraphSort.cpp b/gnu/llvm/lld/COFF/CallGraphSort.cpp new file mode 100644 index 00000000000..d3e5312ce7f --- /dev/null +++ b/gnu/llvm/lld/COFF/CallGraphSort.cpp @@ -0,0 +1,245 @@ +//===- CallGraphSort.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// This is based on the ELF port, see ELF/CallGraphSort.cpp for the details +/// about the algorithm. +/// +//===----------------------------------------------------------------------===// + +#include "CallGraphSort.h" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" + +#include + +using namespace llvm; +using namespace lld; +using namespace lld::coff; + +namespace { +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(); + + 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; + +// Maximum cluster size in bytes. +constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; +} // end anonymous namespace + +using SectionPair = std::pair; + +// Take the edge list in Config->CallGraphProfile, resolve symbol names to +// Symbols, and generate a graph between InputSections with the provided +// weights. +CallGraphSort::CallGraphSort() { + MapVector &profile = config->callGraphProfile; + DenseMap secToCluster; + + auto getOrCreateNode = [&](const SectionChunk *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 (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; + + // 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->getOutputSection() != toSec->getOutputSection()) + continue; + + int from = getOrCreateNode(fromSec); + int to = getOrCreateNode(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() { + std::vector sorted(clusters.size()); + 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); + if (l == predL) + continue; + + Cluster *predC = &clusters[predL]; + if (c.size + predC->size > MAX_CLUSTER_SIZE) + 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 increasing order. Absent sections will have + // priority 0 and be placed at the end of sections. + int curOrder = INT_MIN; + 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 increasing curOrder + // Instead of sorting all the orderMap, just repeat the loops above. + for (int leader : sorted) + for (int i = leader;;) { + const SectionChunk *sc = sections[i]; + + // Search all the symbols in the file of the section + // and find out a DefinedCOFF symbol with name that is within the + // section. + for (Symbol *sym : sc->file->getSymbols()) + if (auto *d = dyn_cast_or_null(sym)) + // Filter out non-COMDAT symbols and section symbols. + if (d->isCOMDAT && !d->getCOFFSymbol().isSection() && + sc == d->getChunk()) + os << sym->getName() << "\n"; + i = clusters[i].next; + if (i == leader) + break; + } + } + + return orderMap; +} + +// Sort sections by the profile data provided by /call-graph-ordering-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 +// metric to further improve locality. +DenseMap coff::computeCallGraphProfileOrder() { + return CallGraphSort().run(); +} diff --git a/gnu/llvm/lld/COFF/CallGraphSort.h b/gnu/llvm/lld/COFF/CallGraphSort.h new file mode 100644 index 00000000000..e4f37213744 --- /dev/null +++ b/gnu/llvm/lld/COFF/CallGraphSort.h @@ -0,0 +1,22 @@ +//===- CallGraphSort.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_CALL_GRAPH_SORT_H +#define LLD_COFF_CALL_GRAPH_SORT_H + +#include "llvm/ADT/DenseMap.h" + +namespace lld { +namespace coff { +class SectionChunk; + +llvm::DenseMap computeCallGraphProfileOrder(); +} // namespace coff +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/COFF/Chunks.cpp b/gnu/llvm/lld/COFF/Chunks.cpp index e04ceed505c..36d5f371326 100644 --- a/gnu/llvm/lld/COFF/Chunks.cpp +++ b/gnu/llvm/lld/COFF/Chunks.cpp @@ -32,12 +32,15 @@ namespace coff { SectionChunk::SectionChunk(ObjFile *f, const coff_section *h) : Chunk(SectionKind), file(f), header(h), repl(this) { // Initialize relocs. - setRelocs(file->getCOFFObj()->getRelocations(header)); + if (file) + setRelocs(file->getCOFFObj()->getRelocations(header)); // Initialize sectionName. StringRef sectionName; - if (Expected e = file->getCOFFObj()->getSectionName(header)) - sectionName = *e; + if (file) { + if (Expected e = file->getCOFFObj()->getSectionName(header)) + sectionName = *e; + } sectionNameData = sectionName.data(); sectionNameSize = sectionName.size(); @@ -49,7 +52,10 @@ 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. - live = !config->doGC || !isCOMDAT(); + if (config) + live = !config->doGC || !isCOMDAT(); + else + live = true; } // SectionChunk is one of the most frequently allocated classes, so it is @@ -357,9 +363,7 @@ void SectionChunk::writeTo(uint8_t *buf) const { // Apply relocations. size_t inputSize = getSize(); - for (size_t i = 0, e = relocsSize; i < e; i++) { - const coff_relocation &rel = relocsData[i]; - + for (const coff_relocation &rel : getRelocs()) { // Check for an invalid relocation offset. This check isn't perfect, because // we don't have the relocation size, which is only known after checking the // machine and relocation type. As a result, a relocation may overwrite the @@ -369,56 +373,108 @@ void SectionChunk::writeTo(uint8_t *buf) const { continue; } - uint8_t *off = buf + rel.VirtualAddress; + applyRelocation(buf + rel.VirtualAddress, rel); + } +} - auto *sym = - dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); +void SectionChunk::applyRelocation(uint8_t *off, + const coff_relocation &rel) const { + auto *sym = dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); - // Get the output section of the symbol for this relocation. The output - // 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; - - // 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 - // null. If it was discarded late, the output section will be null, unless - // it was an absolute or synthetic symbol. - if (!sym || - (!os && !isa(sym) && !isa(sym))) { - maybeReportRelocationToDiscarded(this, sym, rel); - continue; - } + // Get the output section of the symbol for this relocation. The output + // 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; - uint64_t s = sym->getRVA(); + // 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 + // null. If it was discarded late, the output section will be null, unless + // it was an absolute or synthetic symbol. + if (!sym || + (!os && !isa(sym) && !isa(sym))) { + maybeReportRelocationToDiscarded(this, sym, rel); + return; + } - // Compute the RVA of the relocation for relative relocations. - uint64_t p = rva + rel.VirtualAddress; - switch (config->machine) { - case AMD64: - applyRelX64(off, rel.Type, os, s, p); - break; - case I386: - applyRelX86(off, rel.Type, os, s, p); - break; - case ARMNT: - applyRelARM(off, rel.Type, os, s, p); - break; - case ARM64: - applyRelARM64(off, rel.Type, os, s, p); + uint64_t s = sym->getRVA(); + + // Compute the RVA of the relocation for relative relocations. + uint64_t p = rva + rel.VirtualAddress; + switch (config->machine) { + case AMD64: + applyRelX64(off, rel.Type, os, s, p); + break; + case I386: + applyRelX86(off, rel.Type, os, s, p); + break; + case ARMNT: + applyRelARM(off, rel.Type, os, s, p); + break; + case ARM64: + applyRelARM64(off, rel.Type, os, s, p); + break; + default: + llvm_unreachable("unknown machine type"); + } +} + +// Defend against unsorted relocations. This may be overly conservative. +void SectionChunk::sortRelocations() { + auto cmpByVa = [](const coff_relocation &l, const coff_relocation &r) { + return l.VirtualAddress < r.VirtualAddress; + }; + if (llvm::is_sorted(getRelocs(), cmpByVa)) + return; + warn("some relocations in " + file->getName() + " are not sorted"); + MutableArrayRef newRelocs( + bAlloc.Allocate(relocsSize), relocsSize); + memcpy(newRelocs.data(), relocsData, relocsSize * sizeof(coff_relocation)); + llvm::sort(newRelocs, cmpByVa); + setRelocs(newRelocs); +} + +// Similar to writeTo, but suitable for relocating a subsection of the overall +// section. +void SectionChunk::writeAndRelocateSubsection(ArrayRef sec, + ArrayRef subsec, + uint32_t &nextRelocIndex, + uint8_t *buf) const { + assert(!subsec.empty() && !sec.empty()); + assert(sec.begin() <= subsec.begin() && subsec.end() <= sec.end() && + "subsection is not part of this section"); + size_t vaBegin = std::distance(sec.begin(), subsec.begin()); + size_t vaEnd = std::distance(sec.begin(), subsec.end()); + memcpy(buf, subsec.data(), subsec.size()); + for (; nextRelocIndex < relocsSize; ++nextRelocIndex) { + const coff_relocation &rel = relocsData[nextRelocIndex]; + // Only apply relocations that apply to this subsection. These checks + // assume that all subsections completely contain their relocations. + // Relocations must not straddle the beginning or end of a subsection. + if (rel.VirtualAddress < vaBegin) + continue; + if (rel.VirtualAddress + 1 >= vaEnd) break; - default: - llvm_unreachable("unknown machine type"); - } + applyRelocation(&buf[rel.VirtualAddress - vaBegin], rel); } } void SectionChunk::addAssociative(SectionChunk *child) { - // Insert this child at the head of the list. + // Insert the child section into the list of associated children. Keep the + // list ordered by section name so that ICF does not depend on section order. assert(child->assocChildren == nullptr && "associated sections cannot have their own associated children"); - child->assocChildren = assocChildren; - assocChildren = child; + SectionChunk *prev = this; + SectionChunk *next = assocChildren; + for (; next != nullptr; prev = next, next = next->assocChildren) { + if (next->getSectionName() <= child->getSectionName()) + break; + } + + // Insert child between prev and next. + assert(prev->assocChildren == next); + prev->assocChildren = child; + child->assocChildren = next; } static uint8_t getBaserelType(const coff_relocation &rel) { @@ -426,6 +482,8 @@ static uint8_t getBaserelType(const coff_relocation &rel) { case AMD64: if (rel.Type == IMAGE_REL_AMD64_ADDR64) return IMAGE_REL_BASED_DIR64; + if (rel.Type == IMAGE_REL_AMD64_ADDR32) + return IMAGE_REL_BASED_HIGHLOW; return IMAGE_REL_BASED_ABSOLUTE; case I386: if (rel.Type == IMAGE_REL_I386_DIR32) @@ -451,8 +509,7 @@ static uint8_t getBaserelType(const coff_relocation &rel) { // fixed by the loader if load-time relocation is needed. // Only called when base relocation is enabled. void SectionChunk::getBaserels(std::vector *res) { - for (size_t i = 0, e = relocsSize; i < e; i++) { - const coff_relocation &rel = relocsData[i]; + for (const coff_relocation &rel : getRelocs()) { uint8_t ty = getBaserelType(rel); if (ty == IMAGE_REL_BASED_ABSOLUTE) continue; @@ -762,6 +819,27 @@ void RVATableChunk::writeTo(uint8_t *buf) const { "RVA tables should be de-duplicated"); } +void RVAFlagTableChunk::writeTo(uint8_t *buf) const { + struct RVAFlag { + ulittle32_t rva; + uint8_t flag; + }; + auto flags = + makeMutableArrayRef(reinterpret_cast(buf), syms.size()); + for (auto t : zip(syms, flags)) { + const auto &sym = std::get<0>(t); + auto &flag = std::get<1>(t); + flag.rva = sym.inputChunk->getRVA() + sym.offset; + flag.flag = 0; + } + llvm::sort(flags, + [](const RVAFlag &a, const RVAFlag &b) { return a.rva < b.rva; }); + assert(llvm::unique(flags, [](const RVAFlag &a, + const RVAFlag &b) { return a.rva == b.rva; }) == + flags.end() && + "RVA tables should be de-duplicated"); +} + // MinGW specific, for the "automatic import of variables from DLLs" feature. size_t PseudoRelocTableChunk::getSize() const { if (relocs.empty()) diff --git a/gnu/llvm/lld/COFF/Chunks.h b/gnu/llvm/lld/COFF/Chunks.h index 0528143383c..bdd3faa179a 100644 --- a/gnu/llvm/lld/COFF/Chunks.h +++ b/gnu/llvm/lld/COFF/Chunks.h @@ -86,8 +86,8 @@ public: // can be stored with 32 bits. uint32_t getRVA() const { return rva; } void setRVA(uint64_t v) { + // This may truncate. The writer checks for overflow later. rva = (uint32_t)v; - assert(rva == v && "RVA truncated"); } // Returns readable/writable/executable bits. @@ -204,6 +204,15 @@ public: ArrayRef getContents() const; void writeTo(uint8_t *buf) const; + // Defend against unsorted relocations. This may be overly conservative. + void sortRelocations(); + + // Write and relocate a portion of the section. This is intended to be called + // in a loop. Relocations must be sorted first. + void writeAndRelocateSubsection(ArrayRef sec, + ArrayRef subsec, + uint32_t &nextRelocIndex, uint8_t *buf) const; + uint32_t getOutputCharacteristics() const { return header->Characteristics & (permMask | typeMask); } @@ -212,6 +221,7 @@ public: } void getBaserels(std::vector *res); 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; void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, @@ -283,8 +293,12 @@ public: // Allow iteration over the associated child chunks for this section. llvm::iterator_range children() const { - return llvm::make_range(AssociatedIterator(assocChildren), - AssociatedIterator(nullptr)); + // Associated sections do not have children. The assocChildren field is + // part of the parent's list of children. + bool isAssoc = selection == llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE; + return llvm::make_range( + AssociatedIterator(isAssoc ? nullptr : assocChildren), + AssociatedIterator(nullptr)); } // The section ID this chunk belongs to in its Obj. @@ -574,6 +588,17 @@ private: SymbolRVASet syms; }; +// Table which contains symbol RVAs with flags. Used for /guard:ehcont. +class RVAFlagTableChunk : public NonSectionChunk { +public: + explicit RVAFlagTableChunk(SymbolRVASet s) : syms(std::move(s)) {} + size_t getSize() const override { return syms.size() * 5; } + void writeTo(uint8_t *buf) const override; + +private: + SymbolRVASet syms; +}; + // Windows-specific. // This class represents a block in .reloc section. // See the PE/COFF spec 5.6 for details. diff --git a/gnu/llvm/lld/COFF/Config.h b/gnu/llvm/lld/COFF/Config.h index 7c439176f3a..df883b779ee 100644 --- a/gnu/llvm/lld/COFF/Config.h +++ b/gnu/llvm/lld/COFF/Config.h @@ -9,6 +9,7 @@ #ifndef LLD_COFF_CONFIG_H #define LLD_COFF_CONFIG_H +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" @@ -29,6 +30,7 @@ class DefinedRelative; class StringChunk; class Symbol; class InputFile; +class SectionChunk; // Short aliases. static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; @@ -72,10 +74,19 @@ enum class DebugType { Fixup = 0x4, /// Relocation Table }; -enum class GuardCFLevel { - Off, - NoLongJmp, // Emit gfids but no longjmp tables - Full, // Enable all protections. +enum GuardCFLevel { + Off = 0x0, + CF = 0x1, /// Emit gfids tables + LongJmp = 0x2, /// Emit longjmp tables + EHCont = 0x4, /// Emit ehcont tables + All = 0x7 /// Enable all protections +}; + +enum class ICFLevel { + None, + Safe, // Safe ICF for all sections. + All, // Aggressive ICF for code, but safe ICF for data, similar to MSVC's + // behavior. }; // Global configuration. @@ -93,7 +104,7 @@ struct Configuration { std::string importName; bool demangle = true; bool doGC = true; - bool doICF = true; + ICFLevel doICF = ICFLevel::None; bool tailMerge; bool relocatable = true; bool forceMultiple = false; @@ -134,7 +145,7 @@ struct Configuration { bool saveTemps = false; // /guard:cf - GuardCFLevel guardCF = GuardCFLevel::Off; + int guardCF = GuardCFLevel::Off; // Used for SafeSEH. bool safeSEH = false; @@ -155,6 +166,11 @@ 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; + // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map merge; @@ -201,6 +217,21 @@ struct Configuration { // Used for /lto-obj-path: llvm::StringRef ltoObjPath; + // Used for /lto-cs-profile-generate: + bool ltoCSProfileGenerate = false; + + // Used for /lto-cs-profile-path + llvm::StringRef ltoCSProfileFile; + + // Used for /call-graph-ordering-file: + llvm::MapVector, + uint64_t> + callGraphProfile; + bool callGraphProfileSort = false; + + // Used for /print-symbol-order: + StringRef printSymbolOrder; + uint64_t align = 4096; uint64_t imageBase = -1; uint64_t fileAlign = 512; @@ -210,8 +241,12 @@ struct Configuration { uint64_t heapCommit = 4096; uint32_t majorImageVersion = 0; uint32_t minorImageVersion = 0; + // If changing the default os/subsys version here, update the default in + // the MinGW driver accordingly. uint32_t majorOSVersion = 6; uint32_t minorOSVersion = 0; + uint32_t majorSubsystemVersion = 6; + uint32_t minorSubsystemVersion = 0; uint32_t timestamp = 0; uint32_t functionPadMin = 0; bool dynamicBase = true; @@ -228,6 +263,7 @@ struct Configuration { bool warnLocallyDefinedImported = true; bool warnDebugInfoUnusable = true; bool warnLongSectionNames = true; + bool warnStdcallFixup = true; bool incremental = true; bool integrityCheck = false; bool killAt = false; @@ -238,6 +274,7 @@ struct Configuration { bool thinLTOIndexOnly; bool autoImport = false; bool pseudoRelocs = false; + bool stdcallFixup = false; }; extern Configuration *config; diff --git a/gnu/llvm/lld/COFF/DLL.cpp b/gnu/llvm/lld/COFF/DLL.cpp index 50301ad91b1..b9e12ef4b34 100644 --- a/gnu/llvm/lld/COFF/DLL.cpp +++ b/gnu/llvm/lld/COFF/DLL.cpp @@ -19,6 +19,7 @@ #include "DLL.h" #include "Chunks.h" +#include "SymbolTable.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" @@ -157,7 +158,6 @@ binImports(const std::vector &imports) { return v; } -// Export table // See Microsoft PE/COFF spec 4.3 for details. // A chunk for the delay import descriptor table etnry. @@ -524,6 +524,8 @@ public: if (e.forwardChunk) { write32le(p, e.forwardChunk->getRVA() | bit); } else { + assert(cast(e.sym)->getRVA() != 0 && + "Exported symbol unmapped"); write32le(p, cast(e.sym)->getRVA() | bit); } } @@ -653,9 +655,18 @@ void DelayLoadContents::create(Defined *h) { auto *c = make(extName, 0); names.push_back(make(c)); hintNames.push_back(c); + // Add a syntentic 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); + s->loadThunkSym = + cast(symtab->addSynthetic(symName, t)); } } thunks.push_back(tm); + StringRef tmName = + saver.save("__tailMerge_" + syms[0]->getDLLName().lower()); + symtab->addSynthetic(tmName, tm); // Terminate with null values. addresses.push_back(make(8)); names.push_back(make(8)); diff --git a/gnu/llvm/lld/COFF/DebugTypes.cpp b/gnu/llvm/lld/COFF/DebugTypes.cpp index abe3bb9eef5..97be5bc79ac 100644 --- a/gnu/llvm/lld/COFF/DebugTypes.cpp +++ b/gnu/llvm/lld/COFF/DebugTypes.cpp @@ -10,9 +10,12 @@ #include "Chunks.h" #include "Driver.h" #include "InputFiles.h" +#include "PDB.h" #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" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" @@ -20,7 +23,10 @@ #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" using namespace llvm; @@ -29,6 +35,8 @@ using namespace lld; using namespace lld::coff; namespace { +class TypeServerIpiSource; + // The TypeServerSource class represents a PDB type server, a file referenced by // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ // files, therefore there must be only once instance per OBJ lot. The file path @@ -46,32 +54,61 @@ public: auto expectedInfo = file.getPDBInfoStream(); if (!expectedInfo) return; - auto it = mappings.emplace(expectedInfo->getGuid(), this); + Guid = expectedInfo->getGuid(); + auto it = mappings.emplace(Guid, this); assert(it.second); (void)it; - tsIndexMap.isTypeServerMap = true; } - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; + + void loadGHashes() override; + void remapTpiWithGHashes(GHashState *g) override; + bool isDependency() const override { return true; } PDBInputFile *pdbInputFile = nullptr; - CVIndexMap tsIndexMap; + // TpiSource for IPI stream. + TypeServerIpiSource *ipiSrc = nullptr; + + // The PDB signature GUID. + codeview::GUID Guid; static std::map mappings; }; +// Companion to TypeServerSource. Stores the index map for the IPI stream in the +// PDB. Modeling PDBs with two sources for TPI and IPI helps establish the +// invariant of one type index space per source. +class TypeServerIpiSource : public TpiSource { +public: + explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {} + + friend class TypeServerSource; + + // All of the TpiSource methods are no-ops. The parent TypeServerSource + // handles both TPI and IPI. + Error mergeDebugT(TypeMerger *m) override { return Error::success(); } + void loadGHashes() override {} + void remapTpiWithGHashes(GHashState *g) override {} + bool isDependency() const override { return true; } +}; + // This class represents the debug type stream of an OBJ file that depends on a // PDB type server (see TypeServerSource). class UseTypeServerSource : public TpiSource { + Expected getTypeServerSource(); + public: UseTypeServerSource(ObjFile *f, TypeServer2Record ts) : TpiSource(UsingPDB, f), typeServerDependency(ts) {} - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; + + // No need to load ghashes from /Zi objects. + void loadGHashes() override {} + void remapTpiWithGHashes(GHashState *g) override; // Information about the PDB type server dependency, that needs to be loaded // in before merging this OBJ. @@ -92,14 +129,11 @@ public: if (!it.second) fatal("a PCH object with the same signature has already been provided (" + toString(it.first->second->file) + " and " + toString(file) + ")"); - precompIndexMap.isPrecompiledTypeMap = true; } - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; - bool isDependency() const override { return true; } + void loadGHashes() override; - CVIndexMap precompIndexMap; + bool isDependency() const override { return true; } static std::map mappings; }; @@ -111,30 +145,62 @@ public: UsePrecompSource(ObjFile *f, PrecompRecord precomp) : TpiSource(UsingPCH, f), precompDependency(precomp) {} - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; + + void loadGHashes() override; + void remapTpiWithGHashes(GHashState *g) override; +private: + Error mergeInPrecompHeaderObj(); + +public: // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. PrecompRecord precompDependency; }; } // namespace -static std::vector gc; +std::vector TpiSource::instances; +ArrayRef TpiSource::dependencySources; +ArrayRef TpiSource::objectSources; -TpiSource::TpiSource(TpiKind k, ObjFile *f) : kind(k), file(f) { - gc.push_back(this); +TpiSource::TpiSource(TpiKind k, ObjFile *f) + : kind(k), tpiSrcIdx(instances.size()), file(f) { + instances.push_back(this); } // Vtable key method. -TpiSource::~TpiSource() = default; +TpiSource::~TpiSource() { + // Silence any assertions about unchecked errors. + 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::makeTypeServerSource(PDBInputFile *pdbInputFile) { - return make(pdbInputFile); + // Type server sources come in pairs: the TPI stream, and the IPI stream. + auto *tpiSource = make(pdbInputFile); + if (pdbInputFile->session->getPDBFile().hasPDBIpiStream()) + tpiSource->ipiSrc = make(); + return tpiSource; } TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file, @@ -151,14 +217,68 @@ TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file, return make(file, precomp); } -void TpiSource::forEachSource(llvm::function_ref fn) { - for_each(gc, fn); -} - std::map TypeServerSource::mappings; std::map PrecompSource::mappings; +bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const { + if (ti.isSimple()) + return true; + + // This can be an item index or a type index. Choose the appropriate map. + ArrayRef tpiOrIpiMap = + (refKind == TiRefKind::IndexRef) ? ipiMap : tpiMap; + if (ti.toArrayIndex() >= tpiOrIpiMap.size()) + return false; + ti = tpiOrIpiMap[ti.toArrayIndex()]; + return true; +} + +void TpiSource::remapRecord(MutableArrayRef rec, + ArrayRef typeRefs) { + MutableArrayRef contents = rec.drop_front(sizeof(RecordPrefix)); + for (const TiReference &ref : typeRefs) { + unsigned byteSize = ref.Count * sizeof(TypeIndex); + if (contents.size() < ref.Offset + byteSize) + fatal("symbol record too short"); + + MutableArrayRef indices( + reinterpret_cast(contents.data() + ref.Offset), ref.Count); + for (TypeIndex &ti : indices) { + if (!remapTypeIndex(ti, ref.Kind)) { + if (config->verbose) { + uint16_t kind = + reinterpret_cast(rec.data())->RecordKind; + StringRef fname = file ? file->getName() : ""; + log("failed to remap type index in record of kind 0x" + + utohexstr(kind) + " in " + fname + " with bad " + + (ref.Kind == TiRefKind::IndexRef ? "item" : "type") + + " index 0x" + utohexstr(ti.getIndex())); + } + ti = TypeIndex(SimpleTypeKind::NotTranslated); + continue; + } + } + } +} + +void TpiSource::remapTypesInTypeRecord(MutableArrayRef rec) { + // TODO: Handle errors similar to symbols. + SmallVector typeRefs; + discoverTypeIndices(CVType(rec), typeRefs); + remapRecord(rec, typeRefs); +} + +bool TpiSource::remapTypesInSymbolRecord(MutableArrayRef rec) { + // Discover type index references in the record. Skip it if we don't + // know where they are. + SmallVector typeRefs; + if (!discoverTypeIndicesInSymbol(rec, typeRefs)) + return false; + remapRecord(rec, typeRefs); + return true; +} + // A COFF .debug$H section is currently a clang extension. This function checks // if a .debug$H section is in a format that we expect / understand, so that we // can ignore any sections which are coincidentally also named .debug$H but do @@ -189,46 +309,35 @@ static Optional> getDebugH(ObjFile *file) { static ArrayRef getHashesFromDebugH(ArrayRef debugH) { assert(canUseDebugH(debugH)); - debugH = debugH.drop_front(sizeof(object::debug_h_header)); uint32_t count = debugH.size() / sizeof(GloballyHashedType); return {reinterpret_cast(debugH.data()), count}; } // Merge .debug$T for a generic object file. -Expected TpiSource::mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) { +Error TpiSource::mergeDebugT(TypeMerger *m) { + assert(!config->debugGHashes && + "use remapTpiWithGHashes when ghash is enabled"); + CVTypeArray types; BinaryStreamReader reader(file->debugTypes, support::little); cantFail(reader.readArray(types, reader.getLength())); // When dealing with PCH.OBJ, some indices were already merged. - unsigned nbHeadIndices = indexMap->tpiMap.size(); - - if (config->debugGHashes) { - ArrayRef hashes; - std::vector ownedHashes; - if (Optional> debugH = getDebugH(file)) - hashes = getHashesFromDebugH(*debugH); - else { - ownedHashes = GloballyHashedType::hashTypes(types); - hashes = ownedHashes; - } + unsigned nbHeadIndices = indexMapStorage.size(); - if (auto err = mergeTypeAndIdRecords(m->globalIDTable, m->globalTypeTable, - indexMap->tpiMap, types, hashes, - file->pchSignature)) - fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(err))); - } else { - if (auto err = - mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMap->tpiMap, - types, file->pchSignature)) - fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(err))); - } + if (auto err = mergeTypeAndIdRecords( + m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature)) + fatal("codeview::mergeTypeAndIdRecords failed: " + + toString(std::move(err))); + + // In an object, there is only one mapping for both types and items. + tpiMap = indexMapStorage; + ipiMap = indexMapStorage; if (config->showSummary) { + nbTypeRecords = indexMapStorage.size() - nbHeadIndices; + nbTypeRecordsBytes = reader.getLength(); // Count how many times we saw each type record in our input. This // calculation requires a second pass over the type records to classify each // record as a type or index. This is slow, but this code executes when @@ -237,7 +346,7 @@ Expected TpiSource::mergeDebugT(TypeMerger *m, m->ipiCounts.resize(m->getIDTable().size()); uint32_t srcIdx = nbHeadIndices; for (CVType &ty : types) { - TypeIndex dstIdx = indexMap->tpiMap[srcIdx++]; + 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. if (dstIdx.isSimple()) @@ -248,12 +357,14 @@ Expected TpiSource::mergeDebugT(TypeMerger *m, } } - return indexMap; + return Error::success(); } // Merge types from a type server PDB. -Expected TypeServerSource::mergeDebugT(TypeMerger *m, - CVIndexMap *) { +Error TypeServerSource::mergeDebugT(TypeMerger *m) { + assert(!config->debugGHashes && + "use remapTpiWithGHashes when ghash is enabled"); + pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); Expected expectedTpi = pdbFile.getPDBTpiStream(); if (auto e = expectedTpi.takeError()) @@ -266,62 +377,44 @@ Expected TypeServerSource::mergeDebugT(TypeMerger *m, maybeIpi = &*expectedIpi; } - if (config->debugGHashes) { - // PDBs do not actually store global hashes, so when merging a type server - // PDB we have to synthesize global hashes. To do this, we first synthesize - // global hashes for the TPI stream, since it is independent, then we - // synthesize hashes for the IPI stream, using the hashes for the TPI stream - // as inputs. - auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); - Optional endPrecomp; - // Merge TPI first, because the IPI stream will reference type indices. - if (auto err = - mergeTypeRecords(m->globalTypeTable, tsIndexMap.tpiMap, - expectedTpi->typeArray(), tpiHashes, endPrecomp)) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); - - // Merge IPI. - if (maybeIpi) { - auto ipiHashes = - GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); - if (auto err = mergeIdRecords(m->globalIDTable, tsIndexMap.tpiMap, - tsIndexMap.ipiMap, maybeIpi->typeArray(), - ipiHashes)) - fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); - } - } else { - // Merge TPI first, because the IPI stream will reference type indices. - if (auto err = mergeTypeRecords(m->typeTable, tsIndexMap.tpiMap, - expectedTpi->typeArray())) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); - - // Merge IPI. - if (maybeIpi) { - if (auto err = mergeIdRecords(m->idTable, tsIndexMap.tpiMap, - tsIndexMap.ipiMap, maybeIpi->typeArray())) - fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); - } + // Merge TPI first, because the IPI stream will reference type indices. + if (auto err = mergeTypeRecords(m->typeTable, indexMapStorage, + expectedTpi->typeArray())) + fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + tpiMap = indexMapStorage; + + // Merge IPI. + if (maybeIpi) { + if (auto err = mergeIdRecords(m->idTable, tpiMap, ipiSrc->indexMapStorage, + maybeIpi->typeArray())) + fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + ipiMap = ipiSrc->indexMapStorage; } if (config->showSummary) { + nbTypeRecords = tpiMap.size() + ipiMap.size(); + nbTypeRecordsBytes = + expectedTpi->typeArray().getUnderlyingStream().getLength() + + (maybeIpi ? maybeIpi->typeArray().getUnderlyingStream().getLength() + : 0); + // Count how many times we saw each type record in our input. If a // destination type index is present in the source to destination type index // map, that means we saw it once in the input. Add it to our histogram. m->tpiCounts.resize(m->getTypeTable().size()); m->ipiCounts.resize(m->getIDTable().size()); - for (TypeIndex ti : tsIndexMap.tpiMap) + for (TypeIndex ti : tpiMap) if (!ti.isSimple()) ++m->tpiCounts[ti.toArrayIndex()]; - for (TypeIndex ti : tsIndexMap.ipiMap) + for (TypeIndex ti : ipiMap) if (!ti.isSimple()) ++m->ipiCounts[ti.toArrayIndex()]; } - return &tsIndexMap; + return Error::success(); } -Expected -UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { +Expected UseTypeServerSource::getTypeServerSource() { const codeview::GUID &tsId = typeServerDependency.getGuid(); StringRef tsPath = typeServerDependency.getName(); @@ -340,27 +433,38 @@ UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { return createFileError(tsPath, std::move(*pdb->loadErr)); tsSrc = (TypeServerSource *)pdb->debugTypesObj; + + // Just because a file with a matching name was found and it was an actual + // PDB file doesn't mean it matches. For it to match the InfoStream's GUID + // must match the GUID specified in the TypeServer2 record. + if (tsSrc->Guid != tsId) { + return createFileError(tsPath, + make_error( + pdb::pdb_error_code::signature_out_of_date)); + } } + return tsSrc; +} - pdb::PDBFile &pdbSession = tsSrc->pdbInputFile->session->getPDBFile(); +Error UseTypeServerSource::mergeDebugT(TypeMerger *m) { + Expected tsSrc = getTypeServerSource(); + if (!tsSrc) + return tsSrc.takeError(); + + pdb::PDBFile &pdbSession = (*tsSrc)->pdbInputFile->session->getPDBFile(); auto expectedInfo = pdbSession.getPDBInfoStream(); if (!expectedInfo) - return &tsSrc->tsIndexMap; - - // Just because a file with a matching name was found and it was an actual - // PDB file doesn't mean it matches. For it to match the InfoStream's GUID - // must match the GUID specified in the TypeServer2 record. - if (expectedInfo->getGuid() != typeServerDependency.getGuid()) - return createFileError( - tsPath, - make_error(pdb::pdb_error_code::signature_out_of_date)); + return expectedInfo.takeError(); - return &tsSrc->tsIndexMap; + // Reuse the type index map of the type server. + tpiMap = (*tsSrc)->tpiMap; + ipiMap = (*tsSrc)->ipiMap; + return Error::success(); } static bool equalsPath(StringRef path1, StringRef path2) { #if defined(_WIN32) - return path1.equals_lower(path2); + return path1.equals_insensitive(path2); #else return path1.equals(path2); #endif @@ -380,25 +484,28 @@ static PrecompSource *findObjByName(StringRef fileNameOnly) { return nullptr; } -Expected findPrecompMap(ObjFile *file, PrecompRecord &pr) { +static PrecompSource *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); - PrecompSource *precomp; auto it = PrecompSource::mappings.find(pr.getSignature()); if (it != PrecompSource::mappings.end()) { - precomp = it->second; - } else { - // Lookup by name - precomp = findObjByName(prFileName); + return it->second; } + // Lookup by name + return findObjByName(prFileName); +} + +static Expected findPrecompMap(ObjFile *file, + PrecompRecord &pr) { + PrecompSource *precomp = findPrecompSource(file, pr); if (!precomp) return createFileError( - prFileName, + pr.getPrecompFilePath(), make_error(pdb::pdb_error_code::no_matching_pch)); if (pr.getSignature() != file->pchSignature) @@ -411,63 +518,41 @@ Expected findPrecompMap(ObjFile *file, PrecompRecord &pr) { toString(precomp->file), make_error(pdb::pdb_error_code::no_matching_pch)); - return &precomp->precompIndexMap; + return precomp; } /// Merges a precompiled headers TPI map into the current TPI map. The /// precompiled headers object will also be loaded and remapped in the /// process. -static Expected -mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *indexMap, - PrecompRecord &precomp) { - auto e = findPrecompMap(file, precomp); +Error UsePrecompSource::mergeInPrecompHeaderObj() { + auto e = findPrecompMap(file, precompDependency); if (!e) return e.takeError(); - const CVIndexMap *precompIndexMap = *e; - assert(precompIndexMap->isPrecompiledTypeMap); + PrecompSource *precompSrc = *e; + if (precompSrc->tpiMap.empty()) + return Error::success(); - if (precompIndexMap->tpiMap.empty()) - return precompIndexMap; - - assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); - assert(precomp.getTypesCount() <= precompIndexMap->tpiMap.size()); + assert(precompDependency.getStartTypeIndex() == + TypeIndex::FirstNonSimpleIndex); + assert(precompDependency.getTypesCount() <= precompSrc->tpiMap.size()); // Use the previously remapped index map from the precompiled headers. - indexMap->tpiMap.append(precompIndexMap->tpiMap.begin(), - precompIndexMap->tpiMap.begin() + - precomp.getTypesCount()); - return indexMap; + indexMapStorage.insert(indexMapStorage.begin(), precompSrc->tpiMap.begin(), + precompSrc->tpiMap.begin() + + precompDependency.getTypesCount()); + + return Error::success(); } -Expected -UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { +Error UsePrecompSource::mergeDebugT(TypeMerger *m) { // This object was compiled with /Yu, so process the corresponding // precompiled headers object (/Yc) first. Some type indices in the current // object are referencing data in the precompiled headers object, so we need // both to be loaded. - auto e = mergeInPrecompHeaderObj(file, indexMap, precompDependency); - if (!e) - return e.takeError(); - - // Drop LF_PRECOMP record from the input stream, as it has been replaced - // with the precompiled headers Type stream in the mergeInPrecompHeaderObj() - // call above. Note that we can't just call Types.drop_front(), as we - // explicitly want to rebase the stream. - CVTypeArray types; - BinaryStreamReader reader(file->debugTypes, support::little); - cantFail(reader.readArray(types, reader.getLength())); - auto firstType = types.begin(); - file->debugTypes = file->debugTypes.drop_front(firstType->RecordData.size()); + if (Error e = mergeInPrecompHeaderObj()) + return e; - return TpiSource::mergeDebugT(m, indexMap); -} - -Expected PrecompSource::mergeDebugT(TypeMerger *m, - CVIndexMap *) { - // Note that we're not using the provided CVIndexMap. Instead, we use our - // local one. Precompiled headers objects need to save the index map for - // further reference by other objects which use the precompiled headers. - return TpiSource::mergeDebugT(m, &precompIndexMap); + return TpiSource::mergeDebugT(m); } uint32_t TpiSource::countTypeServerPDBs() { @@ -479,7 +564,636 @@ uint32_t TpiSource::countPrecompObjs() { } void TpiSource::clear() { - gc.clear(); + // Clean up any owned ghash allocations. + clearGHashes(); + TpiSource::instances.clear(); TypeServerSource::mappings.clear(); PrecompSource::mappings.clear(); } + +//===----------------------------------------------------------------------===// +// Parellel GHash type merging implementation. +//===----------------------------------------------------------------------===// + +void TpiSource::loadGHashes() { + if (Optional> debugH = getDebugH(file)) { + ghashes = getHashesFromDebugH(*debugH); + ownedGHashes = false; + } else { + CVTypeArray types; + BinaryStreamReader reader(file->debugTypes, support::little); + cantFail(reader.readArray(types, reader.getLength())); + assignGHashesFromVector(GloballyHashedType::hashTypes(types)); + } + + fillIsItemIndexFromDebugT(); +} + +// Copies ghashes from a vector into an array. These are long lived, so it's +// worth the time to copy these into an appropriately sized vector to reduce +// memory usage. +void TpiSource::assignGHashesFromVector( + std::vector &&hashVec) { + if (hashVec.empty()) + return; + GloballyHashedType *hashes = new GloballyHashedType[hashVec.size()]; + memcpy(hashes, hashVec.data(), hashVec.size() * sizeof(GloballyHashedType)); + ghashes = makeArrayRef(hashes, hashVec.size()); + ownedGHashes = true; +} + +// Faster way to iterate type records. forEachTypeChecked is faster than +// iterating CVTypeArray. It avoids virtual readBytes calls in inner loops. +static void forEachTypeChecked(ArrayRef types, + function_ref fn) { + checkError( + forEachCodeViewRecord(types, [fn](const CVType &ty) -> Error { + fn(ty); + return Error::success(); + })); +} + +// Walk over file->debugTypes and fill in the isItemIndex bit vector. +// TODO: Store this information in .debug$H so that we don't have to recompute +// it. This is the main bottleneck slowing down parallel ghashing with one +// thread over single-threaded ghashing. +void TpiSource::fillIsItemIndexFromDebugT() { + uint32_t index = 0; + isItemIndex.resize(ghashes.size()); + forEachTypeChecked(file->debugTypes, [&](const CVType &ty) { + if (isIdRecord(ty.kind())) + isItemIndex.set(index); + ++index; + }); +} + +void TpiSource::mergeTypeRecord(TypeIndex curIndex, CVType ty) { + // Decide if the merged type goes into TPI or IPI. + bool isItem = isIdRecord(ty.kind()); + MergedInfo &merged = isItem ? mergedIpi : mergedTpi; + + // Copy the type into our mutable buffer. + assert(ty.length() <= codeview::MaxRecordLength); + 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); + memcpy(newRec.data(), ty.data().data(), newSize); + + // Fix up the record prefix and padding bytes if it required resizing. + if (newSize != ty.length()) { + reinterpret_cast(newRec.data())->RecordLen = newSize - 2; + for (size_t i = ty.length(); i < newSize; ++i) + newRec[i] = LF_PAD0 + (newSize - i); + } + + // Remap the type indices in the new record. + remapTypesInTypeRecord(newRec); + uint32_t pdbHash = check(pdb::hashTypeRecord(CVType(newRec))); + merged.recSizes.push_back(static_cast(newSize)); + merged.recHashes.push_back(pdbHash); + + // Retain a mapping from PDB function id to PDB function type. This mapping is + // used during symbol processing to rewrite S_GPROC32_ID symbols to S_GPROC32 + // symbols. + if (ty.kind() == LF_FUNC_ID || ty.kind() == LF_MFUNC_ID) { + bool success = ty.length() >= 12; + TypeIndex funcId = curIndex; + if (success) + success &= remapTypeIndex(funcId, TiRefKind::IndexRef); + TypeIndex funcType = + *reinterpret_cast(&newRec.data()[8]); + if (success) { + funcIdToType.push_back({funcId, funcType}); + } else { + StringRef fname = file ? file->getName() : ""; + warn("corrupt LF_[M]FUNC_ID record 0x" + utohexstr(curIndex.getIndex()) + + " in " + fname); + } + } +} + +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())); + else + llvm::sort(uniqueTypes); + + // Accumulate all the unique types into one buffer in mergedTypes. + uint32_t ghashIndex = 0; + auto nextUniqueIndex = uniqueTypes.begin(); + assert(mergedTpi.recs.empty()); + assert(mergedIpi.recs.empty()); + + // Pre-compute the number of elements in advance to avoid std::vector resizes. + unsigned nbTpiRecs = 0; + unsigned nbIpiRecs = 0; + forEachTypeChecked(typeRecords, [&](const CVType &ty) { + if (nextUniqueIndex != uniqueTypes.end() && + *nextUniqueIndex == ghashIndex) { + assert(ty.length() <= codeview::MaxRecordLength); + size_t newSize = alignTo(ty.length(), 4); + (isIdRecord(ty.kind()) ? nbIpiRecs : nbTpiRecs) += newSize; + ++nextUniqueIndex; + } + ++ghashIndex; + }); + mergedTpi.recs.reserve(nbTpiRecs); + mergedIpi.recs.reserve(nbIpiRecs); + + // Do the actual type merge. + ghashIndex = 0; + nextUniqueIndex = uniqueTypes.begin(); + forEachTypeChecked(typeRecords, [&](const CVType &ty) { + if (nextUniqueIndex != uniqueTypes.end() && + *nextUniqueIndex == ghashIndex) { + mergeTypeRecord(beginIndex + ghashIndex, ty); + ++nextUniqueIndex; + } + ++ghashIndex; + }); + assert(nextUniqueIndex == uniqueTypes.end() && + "failed to merge all desired records"); + assert(uniqueTypes.size() == + mergedTpi.recSizes.size() + mergedIpi.recSizes.size() && + "missing desired record"); +} + +void TpiSource::remapTpiWithGHashes(GHashState *g) { + assert(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) { + nbTypeRecords = ghashes.size(); + nbTypeRecordsBytes = file->debugTypes.size(); + } +} + +// PDBs do not actually store global hashes, so when merging a type server +// PDB we have to synthesize global hashes. To do this, we first synthesize +// global hashes for the TPI stream, since it is independent, then we +// synthesize hashes for the IPI stream, using the hashes for the TPI stream +// as inputs. +void TypeServerSource::loadGHashes() { + // Don't hash twice. + if (!ghashes.empty()) + return; + pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); + + // Hash TPI stream. + Expected expectedTpi = pdbFile.getPDBTpiStream(); + if (auto e = expectedTpi.takeError()) + fatal("Type server does not have TPI stream: " + toString(std::move(e))); + assignGHashesFromVector( + GloballyHashedType::hashTypes(expectedTpi->typeArray())); + isItemIndex.resize(ghashes.size()); + + // Hash IPI stream, which depends on TPI ghashes. + if (!pdbFile.hasPDBIpiStream()) + return; + Expected expectedIpi = pdbFile.getPDBIpiStream(); + if (auto e = expectedIpi.takeError()) + fatal("error retrieving IPI stream: " + toString(std::move(e))); + ipiSrc->assignGHashesFromVector( + GloballyHashedType::hashIds(expectedIpi->typeArray(), ghashes)); + + // The IPI stream isItemIndex bitvector should be all ones. + ipiSrc->isItemIndex.resize(ipiSrc->ghashes.size()); + ipiSrc->isItemIndex.set(0, ipiSrc->ghashes.size()); +} + +// Flatten discontiguous PDB type arrays to bytes so that we can use +// forEachTypeChecked instead of CVTypeArray iteration. Copying all types from +// type servers is faster than iterating all object files compiled with /Z7 with +// CVTypeArray, which has high overheads due to the virtual interface of +// BinaryStream::readBytes. +static ArrayRef typeArrayToBytes(const CVTypeArray &types) { + BinaryStreamRef stream = types.getUnderlyingStream(); + ArrayRef debugTypes; + checkError(stream.readBytes(0, stream.getLength(), debugTypes)); + return debugTypes; +} + +// Merge types from a type server PDB. +void TypeServerSource::remapTpiWithGHashes(GHashState *g) { + assert(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. + pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); + pdb::TpiStream &tpi = check(pdbFile.getPDBTpiStream()); + fillMapFromGHashes(g); + tpiMap = indexMapStorage; + mergeUniqueTypeRecords(typeArrayToBytes(tpi.typeArray())); + if (pdbFile.hasPDBIpiStream()) { + pdb::TpiStream &ipi = check(pdbFile.getPDBIpiStream()); + ipiSrc->indexMapStorage.resize(ipiSrc->ghashes.size()); + ipiSrc->fillMapFromGHashes(g); + ipiMap = ipiSrc->indexMapStorage; + ipiSrc->tpiMap = tpiMap; + ipiSrc->ipiMap = ipiMap; + ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray())); + + if (config->showSummary) { + nbTypeRecords = ipiSrc->ghashes.size(); + nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength(); + } + } + + if (config->showSummary) { + nbTypeRecords += ghashes.size(); + nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength(); + } +} + +void UseTypeServerSource::remapTpiWithGHashes(GHashState *g) { + // No remapping to do with /Zi objects. Simply use the index map from the type + // server. Errors should have been reported earlier. Symbols from this object + // will be ignored. + Expected maybeTsSrc = getTypeServerSource(); + if (!maybeTsSrc) { + typeMergingError = + joinErrors(std::move(typeMergingError), maybeTsSrc.takeError()); + return; + } + TypeServerSource *tsSrc = *maybeTsSrc; + tpiMap = tsSrc->tpiMap; + ipiMap = tsSrc->ipiMap; +} + +void PrecompSource::loadGHashes() { + if (getDebugH(file)) { + warn("ignoring .debug$H section; pch with ghash is not implemented"); + } + + uint32_t ghashIdx = 0; + std::vector hashVec; + forEachTypeChecked(file->debugTypes, [&](const CVType &ty) { + // 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; + + hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec)); + isItemIndex.push_back(isIdRecord(ty.kind())); + ++ghashIdx; + }); + assignGHashesFromVector(std::move(hashVec)); +} + +void UsePrecompSource::loadGHashes() { + PrecompSource *pchSrc = findPrecompSource(file, precompDependency); + if (!pchSrc) + return; + + // 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. + std::vector hashVec = + pchSrc->ghashes.take_front(precompDependency.getTypesCount()); + forEachTypeChecked(file->debugTypes, [&](const CVType &ty) { + hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec)); + isItemIndex.push_back(isIdRecord(ty.kind())); + }); + hashVec.erase(hashVec.begin(), + hashVec.begin() + precompDependency.getTypesCount()); + assignGHashesFromVector(std::move(hashVec)); +} + +void UsePrecompSource::remapTpiWithGHashes(GHashState *g) { + fillMapFromGHashes(g); + // This object was compiled with /Yu, so process the corresponding + // precompiled headers object (/Yc) first. Some type indices in the current + // object are referencing data in the precompiled headers object, so we need + // both to be loaded. + if (Error e = mergeInPrecompHeaderObj()) { + typeMergingError = joinErrors(std::move(typeMergingError), std::move(e)); + return; + } + + tpiMap = indexMapStorage; + ipiMap = indexMapStorage; + mergeUniqueTypeRecords(file->debugTypes, + TypeIndex(precompDependency.getStartTypeIndex() + + precompDependency.getTypesCount())); + if (config->showSummary) { + nbTypeRecords = ghashes.size(); + nbTypeRecordsBytes = file->debugTypes.size(); + } +} + +namespace { +/// A concurrent hash table for global type hashing. It is based on this paper: +/// Concurrent Hash Tables: Fast and General(?)! +/// https://dl.acm.org/doi/10.1145/3309206 +/// +/// This hash table is meant to be used in two phases: +/// 1. concurrent insertions +/// 2. concurrent reads +/// It does not support lookup, deletion, or rehashing. It uses linear probing. +/// +/// The paper describes storing a key-value pair in two machine words. +/// Generally, the values stored in this map are type indices, and we can use +/// those values to recover the ghash key from a side table. This allows us to +/// shrink the table entries further at the cost of some loads, and sidesteps +/// the need for a 128 bit atomic compare-and-swap operation. +/// +/// During insertion, a priority function is used to decide which insertion +/// should be preferred. This ensures that the output is deterministic. For +/// ghashing, lower tpiSrcIdx values (earlier inputs) are preferred. +/// +class GHashCell; +struct GHashTable { + GHashCell *table = nullptr; + uint32_t tableSize = 0; + + GHashTable() = default; + ~GHashTable(); + + /// Initialize the table with the given size. Because the table cannot be + /// resized, the initial size of the table must be large enough to contain all + /// inputs, or insertion may not be able to find an empty cell. + void init(uint32_t newTableSize); + + /// 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); +}; + +/// A ghash table cell for deduplicating types from TpiSources. +class GHashCell { + uint64_t data = 0; + +public: + GHashCell() = default; + + // Construct data most to least significant so that sorting works well: + // - isItem + // - tpiSrcIdx + // - ghashIdx + // Add one to the tpiSrcIdx so that the 0th record from the 0th source has a + // non-zero representation. + GHashCell(bool isItem, uint32_t tpiSrcIdx, uint32_t ghashIdx) + : data((uint64_t(isItem) << 63U) | (uint64_t(tpiSrcIdx + 1) << 32ULL) | + ghashIdx) { + assert(tpiSrcIdx == getTpiSrcIdx() && "round trip failure"); + assert(ghashIdx == getGHashIdx() && "round trip failure"); + } + + explicit GHashCell(uint64_t data) : data(data) {} + + // The empty cell is all zeros. + bool isEmpty() const { return data == 0ULL; } + + /// Extract the tpiSrcIdx. + uint32_t getTpiSrcIdx() const { + return ((uint32_t)(data >> 32U) & 0x7FFFFFFF) - 1; + } + + /// Extract the index into the ghash array of the TpiSource. + uint32_t getGHashIdx() const { return (uint32_t)data; } + + bool isItem() const { return data & (1ULL << 63U); } + + /// Get the ghash key for this cell. + GloballyHashedType getGHash() const { + return TpiSource::instances[getTpiSrcIdx()]->ghashes[getGHashIdx()]; + } + + /// The priority function for the cell. The data is stored such that lower + /// tpiSrcIdx and ghashIdx values are preferred, which means that type record + /// from earlier sources are more likely to prevail. + friend inline bool operator<(const GHashCell &l, const GHashCell &r) { + return l.data < r.data; + } +}; +} // namespace + +namespace lld { +namespace 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 + +GHashTable::~GHashTable() { delete[] table; } + +void GHashTable::init(uint32_t newTableSize) { + table = new GHashCell[newTableSize]; + memset(table, 0, newTableSize * sizeof(GHashCell)); + tableSize = newTableSize; +} + +uint32_t GHashTable::insert(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 + // type records are. Swap the byte order for better entropy. A better ghash + // won't need this. + uint32_t startIdx = + ByteSwap_64(*reinterpret_cast(&ghash)) % tableSize; + + // Do a linear probe starting at startIdx. + uint32_t idx = startIdx; + while (true) { + // Run a compare and swap loop. There are four cases: + // - cell is empty: CAS into place and return + // - cell has matching key, earlier priority: do nothing, return + // - cell has matching key, later priority: CAS into place and return + // - 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) { + // 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) + return idx; + // Either the cell is empty, or our value is higher priority. Try to + // compare and swap. If it succeeds, we are done. + if (cellPtr->compare_exchange_weak(oldCell, newCell)) + return idx; + // If the CAS failed, check this cell again. + } + + // Advance the probe. Wrap around to the beginning if we run off the end. + ++idx; + idx = idx == tableSize ? 0 : idx; + if (idx == startIdx) { + // If this becomes an issue, we could mark failure and rehash from the + // beginning with a bigger table. There is no difference between rehashing + // internally and starting over. + report_fatal_error("ghash table is full"); + } + } + llvm_unreachable("left infloop"); +} + +TypeMerger::TypeMerger(llvm::BumpPtrAllocator &alloc) + : typeTable(alloc), idTable(alloc) {} + +TypeMerger::~TypeMerger() = default; + +void TypeMerger::mergeTypesWithGHash() { + // Load ghashes. Do type servers and PCH objects first. + { + ScopedTimer t1(loadGHashTimer); + parallelForEach(TpiSource::dependencySources, + [&](TpiSource *source) { source->loadGHashes(); }); + parallelForEach(TpiSource::objectSources, + [&](TpiSource *source) { source->loadGHashes(); }); + } + + ScopedTimer t2(mergeGHashTimer); + GHashState ghashState; + + // Estimate the size of hash table needed to deduplicate ghashes. This *must* + // be larger than the number of unique types, or hash table insertion may not + // be able to find a vacant slot. Summing the input types guarantees this, but + // it is a gross overestimate. The table size could be reduced to save memory, + // but it would require implementing rehashing, and this table is generally + // 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) + tableSize += source->ghashes.size(); + + // Cap the table size so that we can use 32-bit cell indices. Type indices are + // also 32-bit, so this is an inherent PDB file format limit anyway. + tableSize = + std::min(size_t(INT32_MAX) - TypeIndex::FirstNonSimpleIndex, tableSize); + ghashState.table.init(static_cast(tableSize)); + + // Insert ghashes in parallel. During concurrent insertion, we cannot observe + // the contents of the hash table cell, but we can remember the insertion + // 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]; + source->indexMapStorage.resize(source->ghashes.size()); + for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) { + if (source->shouldOmitFromPdb(i)) { + source->indexMapStorage[i] = TypeIndex(SimpleTypeKind::NotTranslated); + continue; + } + GloballyHashedType ghash = source->ghashes[i]; + bool isItem = source->isItemIndex.test(i); + uint32_t cellIdx = + ghashState.table.insert(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. + source->indexMapStorage[i] = TypeIndex::fromArrayIndex(cellIdx); + } + }); + + // Collect all non-empty cells and sort them. This will implicitly assign + // destination type indices, and partition the entries into type records and + // item records. It arranges types in this order: + // - type records + // - source 0, type 0... + // - source 1, type 1... + // - item records + // - source 0, type 1... + // - source 1, type 0... + std::vector entries; + for (const GHashCell &cell : + makeArrayRef(ghashState.table.table, tableSize)) { + if (!cell.isEmpty()) + entries.push_back(cell); + } + parallelSort(entries, std::less()); + log(formatv("ghash table load factor: {0:p} (size {1} / capacity {2})\n", + tableSize ? double(entries.size()) / tableSize : 0, + 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)); + assert((mid == entries.end() || mid->isItem()) && + (mid == entries.begin() || !std::prev(mid)->isItem()) && + "midpoint is not midpoint"); + uint32_t numTypes = std::distance(entries.begin(), mid); + uint32_t numItems = std::distance(mid, entries.end()); + log("Tpi record count: " + Twine(numTypes)); + log("Ipi record count: " + Twine(numItems)); + + // Make a list of the "unique" type records to merge for each tpi source. Type + // merging will skip indices not on this list. Store the destination PDB type + // index for these unique types in the tpiMap for each source. The entries for + // non-unique types will be filled in prior to type merging. + 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]; + source->uniqueTypes.push_back(cell.getGHashIdx()); + + // Update the ghash table to store the destination PDB type index in the + // table. + uint32_t pdbTypeIndex = i < numTypes ? i : i - numTypes; + uint32_t ghashCellIndex = + source->indexMapStorage[cell.getGHashIdx()].toArrayIndex(); + ghashState.table.table[ghashCellIndex] = + GHashCell(cell.isItem(), cell.getTpiSrcIdx(), pdbTypeIndex); + } + + // In parallel, remap all types. + for_each(TpiSource::dependencySources, [&](TpiSource *source) { + source->remapTpiWithGHashes(&ghashState); + }); + parallelForEach(TpiSource::objectSources, [&](TpiSource *source) { + source->remapTpiWithGHashes(&ghashState); + }); + + // Build a global map of from function ID to function type. + for (TpiSource *source : TpiSource::instances) { + for (auto idToType : source->funcIdToType) + funcIdToType.insert(idToType); + source->funcIdToType.clear(); + } + + TpiSource::clearGHashes(); +} + +/// Given the index into the ghash table for a particular type, return the type +/// index for that type in the output PDB. +static TypeIndex loadPdbTypeIndexFromCell(GHashState *g, + uint32_t ghashCellIdx) { + GHashCell cell = g->table.table[ghashCellIdx]; + return TypeIndex::fromArrayIndex(cell.getGHashIdx()); +} + +// 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) { + for (size_t i = 0, e = ghashes.size(); i < e; ++i) { + TypeIndex fakeCellIndex = indexMapStorage[i]; + if (fakeCellIndex.isSimple()) + indexMapStorage[i] = fakeCellIndex; + else + indexMapStorage[i] = + 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 24d79d83e4c..faad30b141e 100644 --- a/gnu/llvm/lld/COFF/DebugTypes.h +++ b/gnu/llvm/lld/COFF/DebugTypes.h @@ -9,30 +9,38 @@ #ifndef LLD_COFF_DEBUGTYPES_H #define LLD_COFF_DEBUGTYPES_H +#include "lld/Common/LLVM.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" namespace llvm { namespace codeview { -class PrecompRecord; -class TypeServer2Record; +struct GloballyHashedType; } // namespace codeview namespace pdb { class NativeSession; +class TpiStream; } } // namespace llvm namespace lld { namespace coff { +using llvm::codeview::GloballyHashedType; +using llvm::codeview::TypeIndex; + class ObjFile; class PDBInputFile; -struct CVIndexMap; class TypeMerger; +struct GHashState; class TpiSource { public: - enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; + enum TpiKind : uint8_t { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB }; TpiSource(TpiKind k, ObjFile *f); virtual ~TpiSource(); @@ -48,22 +56,134 @@ public: /// If the object does not use a type server PDB (compiled with /Z7), we merge /// all the type and item records from the .debug$S stream and fill in the /// caller-provided ObjectIndexMap. - virtual llvm::Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap); + virtual Error mergeDebugT(TypeMerger *m); + + /// Load global hashes, either by hashing types directly, or by loading them + /// from LLVM's .debug$H section. + virtual void loadGHashes(); + + /// Use global hashes to merge type information. + virtual void remapTpiWithGHashes(GHashState *g); + + // Remap a type index in place. + bool remapTypeIndex(TypeIndex &ti, llvm::codeview::TiRefKind refKind) const; + +protected: + void remapRecord(MutableArrayRef rec, + ArrayRef typeRefs); + + void mergeTypeRecord(TypeIndex curIndex, llvm::codeview::CVType ty); + + // Merge the type records listed in uniqueTypes. beginIndex is the TypeIndex + // of the first record in this source, typically 0x1000. When PCHs are + // involved, it may start higher. + void mergeUniqueTypeRecords( + ArrayRef debugTypes, + TypeIndex beginIndex = TypeIndex(TypeIndex::FirstNonSimpleIndex)); + + // Use the ghash table to construct a map from source type index to + // destination PDB type index. Usable for either TPI or IPI. + void fillMapFromGHashes(GHashState *m); + + // Copies ghashes from a vector into an array. These are long lived, so it's + // worth the time to copy these into an appropriately sized vector to reduce + // memory usage. + void assignGHashesFromVector(std::vector &&hashVec); + + // Walk over file->debugTypes and fill in the isItemIndex bit vector. + void fillIsItemIndexFromDebugT(); + +public: + bool remapTypesInSymbolRecord(MutableArrayRef rec); + + void remapTypesInTypeRecord(MutableArrayRef rec); + /// Is this a dependent file that needs to be processed first, before other /// OBJs? virtual bool isDependency() const { return false; } - static void forEachSource(llvm::function_ref fn); + /// Returns true if this type record should be omitted from the PDB, even if + /// it is unique. This prevents a record from being added to the input ghash + /// table. + bool shouldOmitFromPdb(uint32_t ghashIdx) { + return ghashIdx == endPrecompGHashIdx; + } + + /// 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; + +public: ObjFile *file; + + /// An error encountered during type merging, if any. + Error typeMergingError = Error::success(); + + // Storage for tpiMap or ipiMap, depending on the kind of source. + llvm::SmallVector indexMapStorage; + + // Source type index to PDB type index mapping for type and item records. + // These mappings will be the same for /Z7 objects, and distinct for /Zi + // objects. + llvm::ArrayRef tpiMap; + llvm::ArrayRef ipiMap; + + /// Array of global type hashes, indexed by TypeIndex. May be calculated on + /// demand, or present in input object files. + llvm::ArrayRef ghashes; + + /// When ghashing is used, record the mapping from LF_[M]FUNC_ID to function + /// type index here. Both indices are PDB indices, not object type indexes. + std::vector> funcIdToType; + + /// Indicates if a type record is an item index or a type index. + llvm::BitVector isItemIndex; + + /// A list of all "unique" type indices which must be merged into the final + /// PDB. GHash type deduplication produces this list, and it should be + /// considerably smaller than the input. + std::vector uniqueTypes; + + struct MergedInfo { + std::vector recs; + std::vector recSizes; + std::vector recHashes; + }; + + MergedInfo mergedTpi; + MergedInfo mergedIpi; + + uint64_t nbTypeRecords = 0; + uint64_t nbTypeRecordsBytes = 0; }; TpiSource *makeTpiSource(ObjFile *file); diff --git a/gnu/llvm/lld/COFF/Driver.cpp b/gnu/llvm/lld/COFF/Driver.cpp index 9ceccef8677..9ba0db31507 100644 --- a/gnu/llvm/lld/COFF/Driver.cpp +++ b/gnu/llvm/lld/COFF/Driver.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFFImportFile.h" @@ -34,6 +35,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" +#include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/LEB128.h" @@ -52,7 +54,7 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; -using llvm::sys::Process; +using namespace llvm::sys; namespace lld { namespace coff { @@ -67,6 +69,17 @@ bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, lld::stdoutOS = &stdoutOS; lld::stderrOS = &stderrOS; + 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" @@ -78,20 +91,16 @@ bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, symtab = make(); driver = make(); - driver->link(args); + driver->linkerMain(args); // Call exit() if we can to avoid calling destructors. if (canExitEarly) exitLld(errorCount() ? 1 : 0); - freeArena(); - ObjFile::instances.clear(); - ImportFile::instances.clear(); - BitcodeFile::instances.clear(); - memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); - TpiSource::clear(); - - return !errorCount(); + bool ret = errorCount() == 0; + if (!canExitEarly) + errorHandler().reset(); + return ret; } // Parse options of the form "old;new". @@ -140,17 +149,17 @@ using MBErrPair = std::pair, std::error_code>; // Create a std::future that opens and maps a file using the best strategy for // the host platform. static std::future createFutureForFile(std::string path) { -#if _WIN32 +#if _WIN64 // On Windows, file I/O is relatively slow so it is best to do this - // asynchronously. + // asynchronously. But 32-bit has issues with potentially launching tons + // of threads auto strategy = std::launch::async; #else auto strategy = std::launch::deferred; #endif return std::async(strategy, [=]() { - auto mbOrErr = MemoryBuffer::getFile(path, - /*FileSize*/ -1, - /*RequiresNullTerminator*/ false); + auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (!mbOrErr) return MBErrPair{nullptr, mbOrErr.getError()}; return MBErrPair{std::move(*mbOrErr), std::error_code()}; @@ -226,7 +235,11 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, error(filename + ": is not a native COFF file. Recompile without /GL"); break; case file_magic::pecoff_executable: - if (filename.endswith_lower(".dll")) { + if (config->mingw) { + symtab->addFile(make(mbref)); + break; + } + if (filename.endswith_insensitive(".dll")) { error(filename + ": bad file type. Did you specify a DLL instead of an " "import library?"); break; @@ -400,10 +413,21 @@ void LinkerDriver::parseDirectives(InputFile *file) { case OPT_section: parseSection(arg->getValue()); break; - case OPT_subsystem: + case OPT_stack: + parseNumbers(arg->getValue(), &config->stackReserve, + &config->stackCommit); + break; + case OPT_subsystem: { + bool gotVersion = false; parseSubsystem(arg->getValue(), &config->subsystem, - &config->majorOSVersion, &config->minorOSVersion); + &config->majorSubsystemVersion, + &config->minorSubsystemVersion, &gotVersion); + if (gotVersion) { + config->majorOSVersion = config->majorSubsystemVersion; + config->minorOSVersion = config->minorSubsystemVersion; + } break; + } // Only add flags here that link.exe accepts in // `#pragma comment(linker, "/flag")`-generated sections. case OPT_editandcontinue: @@ -455,7 +479,7 @@ Optional LinkerDriver::findFile(StringRef filename) { return None; } - if (path.endswith_lower(".lib")) + if (path.endswith_insensitive(".lib")) visitedLibs.insert(std::string(sys::path::filename(path))); return path; } @@ -606,6 +630,14 @@ static uint64_t getDefaultImageBase() { return config->dll ? 0x10000000 : 0x400000; } +static std::string rewritePath(StringRef s) { + if (fs::exists(s)) + return relativeToRoot(s); + return std::string(s); +} + +// 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, ArrayRef filePaths, ArrayRef searchPaths) { @@ -626,6 +658,24 @@ static std::string createResponseFile(const opt::InputArgList &args, case OPT_manifestinput: case OPT_manifestuac: break; + case OPT_call_graph_ordering_file: + case OPT_deffile: + case OPT_natvis: + os << arg->getSpelling() << quote(rewritePath(arg->getValue())) << '\n'; + break; + case OPT_order: { + StringRef orderFile = arg->getValue(); + orderFile.consume_front("@"); + os << arg->getSpelling() << '@' << quote(rewritePath(orderFile)) << '\n'; + break; + } + case OPT_pdbstream: { + const std::pair nameFile = + StringRef(arg->getValue()).split("="); + os << arg->getSpelling() << nameFile.first << '=' + << quote(rewritePath(nameFile.second)) << '\n'; + break; + } case OPT_implib: case OPT_pdb: case OPT_pdbstripped: @@ -648,7 +698,16 @@ static std::string createResponseFile(const opt::InputArgList &args, return std::string(data.str()); } -enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab }; +enum class DebugKind { + Unknown, + None, + Full, + FastLink, + GHash, + NoGHash, + Dwarf, + Symtab +}; static DebugKind parseDebugKind(const opt::InputArgList &args) { auto *a = args.getLastArg(OPT_debug, OPT_debug_opt); @@ -658,14 +717,15 @@ static DebugKind parseDebugKind(const opt::InputArgList &args) { return DebugKind::Full; DebugKind debug = StringSwitch(a->getValue()) - .CaseLower("none", DebugKind::None) - .CaseLower("full", DebugKind::Full) - .CaseLower("fastlink", DebugKind::FastLink) - // LLD extensions - .CaseLower("ghash", DebugKind::GHash) - .CaseLower("dwarf", DebugKind::Dwarf) - .CaseLower("symtab", DebugKind::Symtab) - .Default(DebugKind::Unknown); + .CaseLower("none", DebugKind::None) + .CaseLower("full", DebugKind::Full) + .CaseLower("fastlink", DebugKind::FastLink) + // LLD extensions + .CaseLower("ghash", DebugKind::GHash) + .CaseLower("noghash", DebugKind::NoGHash) + .CaseLower("dwarf", DebugKind::Dwarf) + .CaseLower("symtab", DebugKind::Symtab) + .Default(DebugKind::Unknown); if (debug == DebugKind::FastLink) { warn("/debug:fastlink unsupported; using /debug:full"); @@ -787,7 +847,7 @@ static void createImportLibrary(bool asLib) { // If the import library already exists, replace it only if the contents // have changed. ErrorOr> oldBuf = MemoryBuffer::getFile( - path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); + path, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (!oldBuf) { handleError(writeImportLibrary(libName, path, exports, config->machine, config->mingw)); @@ -807,7 +867,7 @@ static void createImportLibrary(bool asLib) { } std::unique_ptr newBuf = check(MemoryBuffer::getFile( - tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); + tmpName, /*IsText=*/false, /*RequiresNullTerminator=*/false)); if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { oldBuf->reset(); handleError(errorCodeToError(sys::fs::rename(tmpName, path))); @@ -817,11 +877,17 @@ static void createImportLibrary(bool asLib) { } static void parseModuleDefs(StringRef path) { - std::unique_ptr mb = CHECK( - MemoryBuffer::getFile(path, -1, false, true), "could not open " + 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)); + // Include in /reproduce: output if applicable. + 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)); @@ -903,8 +969,11 @@ static void parseOrderFile(StringRef arg) { // Open a file. StringRef path = arg.substr(1); - std::unique_ptr mb = CHECK( - MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); + std::unique_ptr mb = + CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, + /*RequiresNullTerminator=*/false, + /*IsVolatile=*/true), + "could not open " + path); // Parse a file. An order file contains one symbol per line. // All symbols that were not present in a given order file are @@ -922,6 +991,84 @@ static void parseOrderFile(StringRef arg) { else config->order[s] = INT_MIN + config->order.size(); } + + // Include in /reproduce: output if applicable. + driver->takeBuffer(std::move(mb)); +} + +static void parseCallGraphFile(StringRef path) { + std::unique_ptr mb = + CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, + /*RequiresNullTerminator=*/false, + /*IsVolatile=*/true), + "could not open " + path); + + // Build a map from symbol name to section. + DenseMap map; + for (ObjFile *file : ObjFile::instances) + for (Symbol *sym : file->getSymbols()) + if (sym) + map[sym->getName()] = sym; + + auto findSection = [&](StringRef name) -> SectionChunk * { + Symbol *sym = map.lookup(name); + if (!sym) { + if (config->warnMissingOrderSymbol) + warn(path + ": no such symbol: " + name); + return nullptr; + } + + if (DefinedCOFF *dr = dyn_cast_or_null(sym)) + return dyn_cast_or_null(dr->getChunk()); + return nullptr; + }; + + for (StringRef line : args::getLines(*mb)) { + SmallVector fields; + line.split(fields, ' '); + uint64_t count; + + if (fields.size() != 3 || !to_integer(fields[2], count)) { + error(path + ": parse error"); + return; + } + + if (SectionChunk *from = findSection(fields[0])) + if (SectionChunk *to = findSection(fields[1])) + config->callGraphProfile[{from, to}] += count; + } + + // Include in /reproduce: output if applicable. + driver->takeBuffer(std::move(mb)); +} + +static void readCallGraphsFromObjectFiles() { + for (ObjFile *obj : ObjFile::instances) { + if (obj->callgraphSec) { + ArrayRef contents; + cantFail( + obj->getCOFFObj()->getSectionContents(obj->callgraphSec, contents)); + BinaryStreamReader reader(contents, support::little); + while (!reader.empty()) { + uint32_t fromIndex, toIndex; + uint64_t count; + if (Error err = reader.readInteger(fromIndex)) + fatal(toString(obj) + ": Expected 32-bit integer"); + if (Error err = reader.readInteger(toIndex)) + fatal(toString(obj) + ": Expected 32-bit integer"); + if (Error err = reader.readInteger(count)) + fatal(toString(obj) + ": Expected 64-bit integer"); + auto *fromSym = dyn_cast_or_null(obj->getSymbol(fromIndex)); + auto *toSym = dyn_cast_or_null(obj->getSymbol(toIndex)); + if (!fromSym || !toSym) + continue; + auto *from = dyn_cast_or_null(fromSym->getChunk()); + auto *to = dyn_cast_or_null(toSym->getChunk()); + if (from && to) + config->callGraphProfile[{from, to}] += count; + } + } + } } static void markAddrsig(Symbol *s) { @@ -1000,9 +1147,9 @@ static void parsePDBAltPath(StringRef altPath) { // text between first and second % as variable name. buf.append(altPath.substr(cursor, firstMark - cursor)); StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1); - if (var.equals_lower("%_pdb%")) + if (var.equals_insensitive("%_pdb%")) buf.append(pdbBasename); - else if (var.equals_lower("%_ext%")) + else if (var.equals_insensitive("%_ext%")) buf.append(binaryExtension); else { warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + @@ -1056,10 +1203,10 @@ void LinkerDriver::convertResources() { // -exclude-all-symbols option, so that lld-link behaves like link.exe rather // than MinGW in the case that nothing is explicitly exported. void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { - if (!config->dll) - return; - if (!args.hasArg(OPT_export_all_symbols)) { + if (!config->dll) + return; + if (!config->exports.empty()) return; if (args.hasArg(OPT_exclude_all_symbols)) @@ -1077,12 +1224,18 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!exporter.shouldExport(def)) return; + if (!def->isGCRoot) { + def->isGCRoot = true; + config->gcroot.push_back(def); + } + Export e; e.name = def->getName(); e.sym = def; if (Chunk *c = def->getChunk()) if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) e.data = true; + s->isUsedInRegularObj = true; config->exports.push_back(e); }); } @@ -1104,10 +1257,15 @@ Optional getReproduceFile(const opt::InputArgList &args) { return std::string(path); } + // This is intentionally not guarded by OPT_lldignoreenv since writing + // a repro tar file doesn't affect the main output. + if (auto *path = getenv("LLD_REPRODUCE")) + return std::string(path); + return None; } -void LinkerDriver::link(ArrayRef argsArr) { +void LinkerDriver::linkerMain(ArrayRef argsArr) { ScopedTimer rootTimer(Timer::root()); // Needed for LTO. @@ -1119,7 +1277,9 @@ void LinkerDriver::link(ArrayRef argsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. - if (argsArr.size() > 1 && StringRef(argsArr[1]).equals_lower("/lib")) { + if (argsArr.size() > 1 && + (StringRef(argsArr[1]).equals_insensitive("/lib") || + StringRef(argsArr[1]).equals_insensitive("-lib"))) { if (llvm::libDriverMain(argsArr.slice(1)) != 0) fatal("lib failed"); return; @@ -1134,6 +1294,7 @@ void LinkerDriver::link(ArrayRef argsArr) { v.push_back("lld-link (LLVM option parsing)"); for (auto *arg : args.filtered(OPT_mllvm)) v.push_back(arg->getValue()); + cl::ResetAllOptionOccurrences(); cl::ParseCommandLineOptions(v.size(), v.data()); // Handle /errorlimit early, because error() depends on it. @@ -1172,7 +1333,7 @@ void LinkerDriver::link(ArrayRef argsArr) { // because it doesn't start with "/", but we deliberately chose "--" to // avoid conflict with /version and for compatibility with clang-cl. if (args.hasArg(OPT_dash_dash_version)) { - lld::outs() << getLLDVersion() << "\n"; + message(getLLDVersion()); return; } @@ -1248,7 +1409,7 @@ void LinkerDriver::link(ArrayRef argsArr) { // Handle /debug DebugKind debug = parseDebugKind(args); if (debug == DebugKind::Full || debug == DebugKind::Dwarf || - debug == DebugKind::GHash) { + debug == DebugKind::GHash || debug == DebugKind::NoGHash) { config->debug = true; config->incremental = true; } @@ -1271,7 +1432,8 @@ void LinkerDriver::link(ArrayRef argsArr) { // Handle /pdb bool shouldCreatePDB = - (debug == DebugKind::Full || debug == DebugKind::GHash); + (debug == DebugKind::Full || debug == DebugKind::GHash || + debug == DebugKind::NoGHash); if (shouldCreatePDB) { if (auto *arg = args.getLastArg(OPT_pdb)) config->pdbPath = arg->getValue(); @@ -1381,8 +1543,18 @@ void LinkerDriver::link(ArrayRef argsArr) { // Handle /subsystem if (auto *arg = args.getLastArg(OPT_subsystem)) - parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion, - &config->minorOSVersion); + parseSubsystem(arg->getValue(), &config->subsystem, + &config->majorSubsystemVersion, + &config->minorSubsystemVersion); + + // Handle /osversion + if (auto *arg = args.getLastArg(OPT_osversion)) { + parseVersion(arg->getValue(), &config->majorOSVersion, + &config->minorOSVersion); + } else { + config->majorOSVersion = config->majorSubsystemVersion; + config->minorOSVersion = config->minorSubsystemVersion; + } // Handle /timestamp if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) { @@ -1415,9 +1587,12 @@ void LinkerDriver::link(ArrayRef argsArr) { // Handle /opt. bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); - unsigned icfLevel = - args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on + Optional icfLevel = None; + 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(); SmallVector vec; @@ -1428,13 +1603,23 @@ void LinkerDriver::link(ArrayRef argsArr) { } else if (s == "noref") { doGC = false; } else if (s == "icf" || s.startswith("icf=")) { - icfLevel = 2; + icfLevel = ICFLevel::All; + } else if (s == "safeicf") { + icfLevel = ICFLevel::Safe; } else if (s == "noicf") { - icfLevel = 0; + icfLevel = ICFLevel::None; } else if (s == "lldtailmerge") { tailMerge = 2; } else if (s == "nolldtailmerge") { tailMerge = 0; + } else if (s == "ltonewpassmanager") { + ltoNewPM = true; + } else if (s == "noltonewpassmanager") { + ltoNewPM = false; + } else if (s == "ltodebugpassmanager") { + ltoDebugPM = true; + } else if (s == "noltodebugpassmanager") { + ltoDebugPM = false; } else if (s.startswith("lldlto=")) { StringRef optLevel = s.substr(7); if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3) @@ -1454,16 +1639,14 @@ void LinkerDriver::link(ArrayRef argsArr) { } } - // Limited ICF is enabled if GC is enabled and ICF was never mentioned - // explicitly. - // FIXME: LLD only implements "limited" ICF, i.e. it only merges identical - // code. If the user passes /OPT:ICF explicitly, LLD should merge identical - // comdat readonly data. - if (icfLevel == 1 && !doGC) - icfLevel = 0; + if (!icfLevel) + icfLevel = doGC ? ICFLevel::All : ICFLevel::None; config->doGC = doGC; - config->doICF = icfLevel > 0; - config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2; + config->doICF = icfLevel.getValue(); + config->tailMerge = + (tailMerge == 1 && config->doICF != ICFLevel::None) || tailMerge == 2; + config->ltoNewPassManager = ltoNewPM; + config->ltoDebugPassManager = ltoDebugPM; // Handle /lldsavetemps if (args.hasArg(OPT_lldsavetemps)) @@ -1564,14 +1747,16 @@ void LinkerDriver::link(ArrayRef argsArr) { config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace); config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path); + config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); + config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); // Handle miscellaneous boolean flags. config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); config->allowIsolation = args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); config->incremental = args.hasFlag(OPT_incremental, OPT_incremental_no, - !config->doGC && !config->doICF && !args.hasArg(OPT_order) && - !args.hasArg(OPT_profile)); + !config->doGC && config->doICF == ICFLevel::None && + !args.hasArg(OPT_order) && !args.hasArg(OPT_profile)); config->integrityCheck = args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false); @@ -1581,15 +1766,20 @@ void LinkerDriver::link(ArrayRef argsArr) { config->terminalServerAware = !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); config->debugDwarf = debug == DebugKind::Dwarf; - config->debugGHashes = debug == DebugKind::GHash; + config->debugGHashes = debug == DebugKind::GHash || debug == DebugKind::Full; config->debugSymtab = debug == DebugKind::Symtab; config->autoImport = args.hasFlag(OPT_auto_import, OPT_auto_import_no, config->mingw); config->pseudoRelocs = args.hasFlag( OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw); - - // Don't warn about long section names, such as .debug_info, for mingw or when - // -debug:dwarf is requested. + config->callGraphProfileSort = args.hasFlag( + OPT_call_graph_profile_sort, OPT_call_graph_profile_sort_no, true); + config->stdcallFixup = + args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, config->mingw); + config->warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup); + + // 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; @@ -1618,7 +1808,7 @@ void LinkerDriver::link(ArrayRef argsArr) { config->incremental = false; } - if (config->incremental && config->doICF) { + if (config->incremental && config->doICF != ICFLevel::None) { warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to " "disable"); config->incremental = false; @@ -1848,6 +2038,9 @@ void LinkerDriver::link(ArrayRef argsArr) { symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); // Needed for MSVC 2017 15.5 CRT. 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); if (config->pseudoRelocs) { symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); @@ -1911,10 +2104,16 @@ void LinkerDriver::link(ArrayRef argsArr) { while (run()); } - if (config->autoImport) { + // Create wrapped symbols for -wrap option. + std::vector wrapped = addWrappedSymbols(args); + // Load more object files that might be needed for wrapped symbols. + if (!wrapped.empty()) + while (run()); + + if (config->autoImport || config->stdcallFixup) { // MinGW specific. // Load any further object files that might be needed for doing automatic - // imports. + // imports, and do stdcall fixups. // // For cases with no automatically imported symbols, this iterates once // over the symbol table and doesn't do anything. @@ -1926,7 +2125,13 @@ void LinkerDriver::link(ArrayRef argsArr) { // normal object file as well (although that won't be used for the // actual autoimport later on). If this pass adds new undefined references, // we won't iterate further to resolve them. - symtab->loadMinGWAutomaticImports(); + // + // If stdcall fixups only are needed for loading import entries from + // a DLL without import library, this also just needs running once. + // 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(); run(); } @@ -1939,6 +2144,13 @@ void LinkerDriver::link(ArrayRef argsArr) { if (errorCount()) return; + config->hadExplicitExports = !config->exports.empty(); + if (config->mingw) { + // In MinGW, all symbols are automatically exported if no symbols + // are chosen to be exported. + maybeExportMinGWSymbols(args); + } + // 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). @@ -1954,17 +2166,16 @@ void LinkerDriver::link(ArrayRef argsArr) { // references to the symbols we use from them. run(); + // Apply symbol renames for -wrap. + if (!wrapped.empty()) + wrapSymbols(wrapped); + // Resolve remaining undefined symbols and warn about imported locals. symtab->resolveRemainingUndefines(); if (errorCount()) return; - config->hadExplicitExports = !config->exports.empty(); if (config->mingw) { - // In MinGW, all symbols are automatically exported if no symbols - // are chosen to be exported. - maybeExportMinGWSymbols(args); - // Make sure the crtend.o object is the last object file. This object // file can contain terminating section chunks that need to be placed // last. GNU ld processes files and static libraries explicitly in the @@ -2024,20 +2235,53 @@ void LinkerDriver::link(ArrayRef argsArr) { // Handle /order. We want to do this at this moment because we // need a complete list of comdat sections to warn on nonexistent // functions. - if (auto *arg = args.getLastArg(OPT_order)) + if (auto *arg = args.getLastArg(OPT_order)) { + if (args.hasArg(OPT_call_graph_ordering_file)) + error("/order and /call-graph-order-file may not be used together"); parseOrderFile(arg->getValue()); + config->callGraphProfileSort = false; + } + + // Handle /call-graph-ordering-file and /call-graph-profile-sort (default on). + if (config->callGraphProfileSort) { + if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) { + parseCallGraphFile(arg->getValue()); + } + readCallGraphsFromObjectFiles(); + } + + // Handle /print-symbol-order. + if (auto *arg = args.getLastArg(OPT_print_symbol_order)) + config->printSymbolOrder = arg->getValue(); // Identify unreferenced COMDAT sections. - if (config->doGC) + if (config->doGC) { + if (config->mingw) { + // markLive doesn't traverse .eh_frame, but the personality function is + // only reached that way. The proper solution would be to parse and + // traverse the .eh_frame section, like the ELF linker does. + // 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)); + if (d && !d->isGCRoot) { + d->isGCRoot = true; + config->gcroot.push_back(d); + } + } + } + markLive(symtab->getChunks()); + } // Needs to happen after the last call to addFile(). convertResources(); // Identify identical COMDAT sections to merge them. - if (config->doICF) { + if (config->doICF != ICFLevel::None) { findKeepUniqueSections(); - doICF(symtab->getChunks()); + doICF(symtab->getChunks(), config->doICF); } // Write the result. diff --git a/gnu/llvm/lld/COFF/Driver.h b/gnu/llvm/lld/COFF/Driver.h index 3fee9b1fe50..5729bed6952 100644 --- a/gnu/llvm/lld/COFF/Driver.h +++ b/gnu/llvm/lld/COFF/Driver.h @@ -78,7 +78,7 @@ private: class LinkerDriver { public: - void link(llvm::ArrayRef args); + void linkerMain(llvm::ArrayRef args); // Used by the resolver to parse .drectve section contents. void parseDirectives(InputFile *file); @@ -93,12 +93,9 @@ public: void enqueuePath(StringRef path, bool wholeArchive, bool lazy); -private: std::unique_ptr tar; // for /linkrepro - // Opens a file. Path has to be resolved already. - MemoryBufferRef openFile(StringRef path); - +private: // Searches a file from search paths. Optional findFile(StringRef filename); Optional findLib(StringRef filename); @@ -168,7 +165,7 @@ void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); // Parses a string in the form of "[,[.]]". void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, - uint32_t *minor); + uint32_t *minor, bool *gotVersion = nullptr); void parseAlternateName(StringRef); void parseMerge(StringRef); @@ -206,8 +203,6 @@ void checkFailIfMismatch(StringRef arg, InputFile *source); MemoryBufferRef convertResToCOFF(ArrayRef mbs, ArrayRef objs); -void runMSVCLinker(std::string rsp, ArrayRef objects); - // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, diff --git a/gnu/llvm/lld/COFF/DriverUtils.cpp b/gnu/llvm/lld/COFF/DriverUtils.cpp index 6cb761abea4..b5abe8b1196 100644 --- a/gnu/llvm/lld/COFF/DriverUtils.cpp +++ b/gnu/llvm/lld/COFF/DriverUtils.cpp @@ -32,6 +32,7 @@ #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include "llvm/WindowsManifest/WindowsManifestMerger.h" +#include #include using namespace llvm::COFF; @@ -87,10 +88,10 @@ void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { StringRef s1, s2; std::tie(s1, s2) = arg.split('.'); - if (s1.getAsInteger(0, *major)) + if (s1.getAsInteger(10, *major)) fatal("invalid number: " + s1); *minor = 0; - if (!s2.empty() && s2.getAsInteger(0, *minor)) + if (!s2.empty() && s2.getAsInteger(10, *minor)) fatal("invalid number: " + s2); } @@ -98,12 +99,18 @@ void parseGuard(StringRef fullArg) { SmallVector splitArgs; fullArg.split(splitArgs, ","); for (StringRef arg : splitArgs) { - if (arg.equals_lower("no")) + if (arg.equals_insensitive("no")) config->guardCF = GuardCFLevel::Off; - else if (arg.equals_lower("nolongjmp")) - config->guardCF = GuardCFLevel::NoLongJmp; - else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) - config->guardCF = GuardCFLevel::Full; + else if (arg.equals_insensitive("nolongjmp")) + 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; + else if (arg.equals_insensitive("ehcont")) + config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; else fatal("invalid argument to /guard: " + arg); } @@ -111,7 +118,7 @@ void parseGuard(StringRef fullArg) { // Parses a string in the form of "[,[.]]". void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, - uint32_t *minor) { + uint32_t *minor, bool *gotVersion) { StringRef sysStr, ver; std::tie(sysStr, ver) = arg.split(','); std::string sysStrLower = sysStr.lower(); @@ -131,6 +138,8 @@ void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, fatal("unknown subsystem: " + sysStr); if (!ver.empty()) parseVersion(ver, major, minor); + if (gotVersion) + *gotVersion = !ver.empty(); } // Parse a string of the form of "=". @@ -248,17 +257,17 @@ void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to Config. void parseManifest(StringRef arg) { - if (arg.equals_lower("no")) { + if (arg.equals_insensitive("no")) { config->manifest = Configuration::No; return; } - if (!arg.startswith_lower("embed")) + if (!arg.startswith_insensitive("embed")) fatal("invalid option " + arg); config->manifest = Configuration::Embed; arg = arg.substr(strlen("embed")); if (arg.empty()) return; - if (!arg.startswith_lower(",id=")) + if (!arg.startswith_insensitive(",id=")) fatal("invalid option " + arg); arg = arg.substr(strlen(",id=")); if (arg.getAsInteger(0, config->manifestID)) @@ -268,7 +277,7 @@ void parseManifest(StringRef arg) { // Parses a string in the form of "level=|uiAccess=|NO". // Results are directly written to Config. void parseManifestUAC(StringRef arg) { - if (arg.equals_lower("no")) { + if (arg.equals_insensitive("no")) { config->manifestUAC = false; return; } @@ -276,12 +285,12 @@ void parseManifestUAC(StringRef arg) { arg = arg.ltrim(); if (arg.empty()) return; - if (arg.startswith_lower("level=")) { + if (arg.startswith_insensitive("level=")) { arg = arg.substr(strlen("level=")); std::tie(config->manifestLevel, arg) = arg.split(" "); continue; } - if (arg.startswith_lower("uiaccess=")) { + if (arg.startswith_insensitive("uiaccess=")) { arg = arg.substr(strlen("uiaccess=")); std::tie(config->manifestUIAccess, arg) = arg.split(" "); continue; @@ -296,9 +305,9 @@ void parseSwaprun(StringRef arg) { do { StringRef swaprun, newArg; std::tie(swaprun, newArg) = arg.split(','); - if (swaprun.equals_lower("cd")) + if (swaprun.equals_insensitive("cd")) config->swaprunCD = true; - else if (swaprun.equals_lower("net")) + else if (swaprun.equals_insensitive("net")) config->swaprunNet = true; else if (swaprun.empty()) error("/swaprun: missing argument"); @@ -347,7 +356,7 @@ public: // is called (you cannot remove an opened file on Windows.) std::unique_ptr getMemoryBuffer() { // IsVolatile=true forces MemoryBuffer to not use mmap(). - return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1, + return CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, /*RequiresNullTerminator=*/false, /*IsVolatile=*/true), "could not open " + path); @@ -411,7 +420,7 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { // Create the default manifest file as a temporary file. TemporaryFile Default("defaultxml", "manifest"); std::error_code ec; - raw_fd_ostream os(Default.path, ec, sys::fs::OF_Text); + raw_fd_ostream os(Default.path, ec, sys::fs::OF_TextWithCRLF); if (ec) fatal("failed to open " + Default.path + ": " + ec.message()); os << defaultXml; @@ -513,7 +522,7 @@ void createSideBySideManifest() { if (path == "") path = config->outputFile + ".manifest"; std::error_code ec; - raw_fd_ostream out(path, ec, sys::fs::OF_Text); + raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF); if (ec) fatal("failed to create manifest: " + ec.message()); out << createManifestXml(); @@ -551,21 +560,21 @@ Export parseExport(StringRef arg) { while (!rest.empty()) { StringRef tok; std::tie(tok, rest) = rest.split(","); - if (tok.equals_lower("noname")) { + if (tok.equals_insensitive("noname")) { if (e.ordinal == 0) goto err; e.noname = true; continue; } - if (tok.equals_lower("data")) { + if (tok.equals_insensitive("data")) { e.data = true; continue; } - if (tok.equals_lower("constant")) { + if (tok.equals_insensitive("constant")) { e.constant = true; continue; } - if (tok.equals_lower("private")) { + if (tok.equals_insensitive("private")) { e.isPrivate = true; continue; } @@ -673,12 +682,15 @@ void fixupExports() { void assignExportOrdinals() { // Assign unique ordinals if default (= 0). - uint16_t max = 0; + uint32_t max = 0; for (Export &e : config->exports) - max = std::max(max, e.ordinal); + max = std::max(max, (uint32_t)e.ordinal); for (Export &e : config->exports) if (e.ordinal == 0) e.ordinal = ++max; + if (max > std::numeric_limits::max()) + fatal("too many exported symbols (max " + + Twine(std::numeric_limits::max()) + ")"); } // Parses a string in the form of "key=value" and check @@ -846,7 +858,7 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { handleColorDiagnostics(args); - for (auto *arg : args.filtered(OPT_UNKNOWN)) { + for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) { std::string nearest; if (optTable.findNearest(arg->getAsString(args), nearest) > 1) warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); @@ -871,14 +883,17 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) { SmallVector tokens; cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens); for (StringRef tok : tokens) { - if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:")) + if (tok.startswith_insensitive("/export:") || + tok.startswith_insensitive("-export:")) result.exports.push_back(tok.substr(strlen("/export:"))); - else if (tok.startswith_lower("/include:") || - tok.startswith_lower("-include:")) + else if (tok.startswith_insensitive("/include:") || + tok.startswith_insensitive("-include:")) result.includes.push_back(tok.substr(strlen("/include:"))); else { - // Save non-null-terminated strings to make proper C strings. - bool HasNul = tok.data()[tok.size()] == '\0'; + // 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()); } } @@ -918,7 +933,7 @@ std::vector ArgParser::tokenize(StringRef s) { } void printHelp(const char *argv0) { - optTable.PrintHelp(lld::outs(), + optTable.printHelp(lld::outs(), (std::string(argv0) + " [options] file...").c_str(), "LLVM Linker", false); } diff --git a/gnu/llvm/lld/COFF/ICF.cpp b/gnu/llvm/lld/COFF/ICF.cpp index 1b33634b63d..73264696729 100644 --- a/gnu/llvm/lld/COFF/ICF.cpp +++ b/gnu/llvm/lld/COFF/ICF.cpp @@ -40,6 +40,7 @@ static Timer icfTimer("ICF", Timer::root()); class ICF { public: + ICF(ICFLevel icfLevel) : icfLevel(icfLevel){}; void run(ArrayRef v); private: @@ -62,6 +63,7 @@ private: std::vector chunks; int cnt = 0; std::atomic repeat = {false}; + ICFLevel icfLevel = ICFLevel::All; }; // Returns true if section S is subject of ICF. @@ -81,8 +83,9 @@ bool ICF::isEligible(SectionChunk *c) { if (!c->isCOMDAT() || !c->live || writable) return false; - // Code sections are eligible. - if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) + // Under regular (not safe) ICF, all code sections are eligible. + if ((icfLevel == ICFLevel::All) && + c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; // .pdata and .xdata unwind info sections are eligible. @@ -131,7 +134,7 @@ bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { auto considerForICF = [](const SectionChunk &assoc) { StringRef Name = assoc.getSectionName(); return !(Name.startswith(".debug") || Name == ".gfids$y" || - Name == ".gljmp$y"); + Name == ".giats$y" || Name == ".gljmp$y"); }; auto ra = make_filter_range(a->children(), considerForICF); auto rb = make_filter_range(b->children(), considerForICF); @@ -314,7 +317,9 @@ void ICF::run(ArrayRef vec) { } // Entry point to ICF. -void doICF(ArrayRef chunks) { ICF().run(chunks); } +void doICF(ArrayRef chunks, ICFLevel icfLevel) { + ICF(icfLevel).run(chunks); +} } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/ICF.h b/gnu/llvm/lld/COFF/ICF.h index 0b3c8fa2ff2..f8cc8071f9e 100644 --- a/gnu/llvm/lld/COFF/ICF.h +++ b/gnu/llvm/lld/COFF/ICF.h @@ -9,6 +9,7 @@ #ifndef LLD_COFF_ICF_H #define LLD_COFF_ICF_H +#include "Config.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" @@ -17,7 +18,7 @@ namespace coff { class Chunk; -void doICF(ArrayRef chunks); +void doICF(ArrayRef chunks, ICFLevel); } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/InputFiles.cpp b/gnu/llvm/lld/COFF/InputFiles.cpp index 4346b3a2ffa..f32353ca4f9 100644 --- a/gnu/llvm/lld/COFF/InputFiles.cpp +++ b/gnu/llvm/lld/COFF/InputFiles.cpp @@ -249,6 +249,11 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, return nullptr; } + if (name == ".llvm.call-graph-profile") { + callgraphSec = sec; + return nullptr; + } + // Object files may have DWARF debug info or MS CodeView debug info // (or both). // @@ -275,8 +280,12 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, debugChunks.push_back(c); else if (name == ".gfids$y") guardFidChunks.push_back(c); + else if (name == ".giats$y") + guardIATChunks.push_back(c); else if (name == ".gljmp$y") guardLJmpChunks.push_back(c); + else if (name == ".gehcont$y") + guardEHContChunks.push_back(c); else if (name == ".sxdata") sxDataChunks.push_back(c); else if (config->tailMerge && sec->NumberOfRelocations == 0 && @@ -467,8 +476,23 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { return symtab->addUndefined(name, this, sym.isWeakExternal()); } -void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, - bool &prevailing, DefinedRegular *leader) { +static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj, + int32_t section) { + uint32_t numSymbols = obj->getNumberOfSymbols(); + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef sym = check(obj->getSymbol(i)); + if (sym.getSectionNumber() != section) + continue; + if (const coff_aux_section_definition *def = sym.getSectionDefinition()) + return def; + } + return nullptr; +} + +void ObjFile::handleComdatSelection( + COFFSymbolRef sym, COMDATType &selection, bool &prevailing, + DefinedRegular *leader, + const llvm::object::coff_aux_section_definition *def) { if (prevailing) return; // There's already an existing comdat for this symbol: `Leader`. @@ -476,16 +500,14 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, // symbol in `Sym` should be discarded, produce a duplicate symbol // error, etc. - SectionChunk *leaderChunk = nullptr; - COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY; + SectionChunk *leaderChunk = leader->getChunk(); + COMDATType leaderSelection = leaderChunk->selection; - if (leader->data) { - leaderChunk = leader->getChunk(); - leaderSelection = leaderChunk->selection; - } else { - // FIXME: comdats from LTO files don't know their selection; treat them - // as "any". - selection = leaderSelection; + assert(leader->data && "Comdat leader without SectionChunk?"); + if (isa(leader->file)) { + // If the leader is only a LTO symbol, we don't know e.g. its final size + // yet, so we can't do the full strict comdat selection checking yet. + selection = leaderSelection = IMAGE_COMDAT_SELECT_ANY; } if ((selection == IMAGE_COMDAT_SELECT_ANY && @@ -535,8 +557,18 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, break; case IMAGE_COMDAT_SELECT_SAME_SIZE: - if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) - symtab->reportDuplicate(leader, this); + if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) { + if (!config->mingw) { + 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); + } + } break; case IMAGE_COMDAT_SELECT_EXACT_MATCH: { @@ -652,7 +684,7 @@ Optional ObjFile::createDefined( COMDATType selection = (COMDATType)def->Selection; if (leader->isCOMDAT) - handleComdatSelection(sym, selection, prevailing, leader); + handleComdatSelection(sym, selection, prevailing, leader, def); if (prevailing) { SectionChunk *c = readSection(sectionNumber, def, getName()); @@ -757,8 +789,14 @@ void ObjFile::initializeDependencies() { else data = getDebugSection(".debug$T"); - if (data.empty()) + // 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); return; + } // Get the first type record. It will indicate if this object uses a type // server (/Zi) or a PCH file (/Yu). @@ -793,6 +831,8 @@ void ObjFile::initializeDependencies() { PrecompRecord precomp = cantFail( TypeDeserializer::deserializeAs(firstType->data())); debugTypesObj = makeUsePrecompSource(this, precomp); + // Drop the LF_PRECOMP record from the input stream. + debugTypes = debugTypes.drop_front(firstType->RecordData.size()); return; } @@ -917,12 +957,6 @@ Optional ObjFile::getDILineInfo(uint32_t offset, return dwarf->getDILineInfo(offset, sectionIndex); } -static StringRef ltrim1(StringRef s, const char *chars) { - if (!s.empty() && strchr(chars, s[0])) - return s.substr(1); - return s; -} - void ImportFile::parse() { const char *buf = mb.getBufferStart(); const auto *hdr = reinterpret_cast(buf); @@ -1002,16 +1036,49 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, 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() { std::vector> comdat(obj->getComdatTable().size()); for (size_t i = 0; i != obj->getComdatTable().size(); ++i) - // FIXME: lto::InputFile doesn't keep enough data to do correct comdat - // selection handling. - comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i])); + // FIXME: Check nodeduplicate + comdat[i] = + 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; + else + fakeSC = <oDataSectionChunk.chunk; if (objSym.isUndefined()) { sym = symtab->addUndefined(symName, this, false); } else if (objSym.isCommon()) { @@ -1023,14 +1090,17 @@ void BitcodeFile::parse() { Symbol *alias = symtab->addUndefined(saver.save(fallback)); checkAndSetWeakAlias(symtab, this, sym, alias); } else if (comdatIndex != -1) { - if (symName == obj->getComdatTable()[comdatIndex]) + if (symName == obj->getComdatTable()[comdatIndex].first) { sym = comdat[comdatIndex].first; - else if (comdat[comdatIndex].second) - sym = symtab->addRegular(this, symName); - else + if (cast(sym)->data == nullptr) + cast(sym)->data = &fakeSC->repl; + } else if (comdat[comdatIndex].second) { + sym = symtab->addRegular(this, symName, nullptr, fakeSC); + } else { sym = symtab->addUndefined(symName, this, false); + } } else { - sym = symtab->addRegular(this, symName); + sym = symtab->addRegular(this, symName, nullptr, fakeSC); } symbols.push_back(sym); if (objSym.isUsed()) @@ -1062,3 +1132,93 @@ std::string lld::coff::replaceThinLTOSuffix(StringRef path) { return (path + repl).str(); return std::string(path); } + +static bool isRVACode(COFFObjectFile *coffObj, uint64_t rva, InputFile *file) { + for (size_t i = 1, e = coffObj->getNumberOfSections(); i <= e; i++) { + const coff_section *sec = CHECK(coffObj->getSection(i), file); + if (rva >= sec->VirtualAddress && + rva <= sec->VirtualAddress + sec->VirtualSize) { + return (sec->Characteristics & COFF::IMAGE_SCN_CNT_CODE) != 0; + } + } + return false; +} + +void DLLFile::parse() { + // Parse a memory buffer as a PE-COFF executable. + std::unique_ptr bin = CHECK(createBinary(mb), this); + + if (auto *obj = dyn_cast(bin.get())) { + bin.release(); + coffObj.reset(obj); + } else { + error(toString(this) + " is not a COFF file"); + return; + } + + if (!coffObj->getPE32Header() && !coffObj->getPE32PlusHeader()) { + error(toString(this) + " is not a PE-COFF executable"); + return; + } + + for (const auto &exp : coffObj->export_directories()) { + StringRef dllName, symbolName; + uint32_t exportRVA; + checkError(exp.getDllName(dllName)); + checkError(exp.getSymbolName(symbolName)); + checkError(exp.getExportRVA(exportRVA)); + + if (symbolName.empty()) + continue; + + bool code = isRVACode(coffObj.get(), exportRVA, this); + + Symbol *s = make(); + s->dllName = dllName; + s->symbolName = symbolName; + s->importType = code ? ImportType::IMPORT_CODE : ImportType::IMPORT_DATA; + s->nameType = ImportNameType::IMPORT_NAME; + + if (coffObj->getMachine() == I386) { + s->symbolName = symbolName = saver.save("_" + symbolName); + s->nameType = ImportNameType::IMPORT_NAME_NOPREFIX; + } + + StringRef impName = saver.save("__imp_" + symbolName); + symtab->addLazyDLLSymbol(this, s, impName); + if (code) + symtab->addLazyDLLSymbol(this, s, symbolName); + } +} + +MachineTypes DLLFile::getMachineType() { + if (coffObj) + return static_cast(coffObj->getMachine()); + return IMAGE_FILE_MACHINE_UNKNOWN; +} + +void DLLFile::makeImport(DLLFile::Symbol *s) { + if (!seen.insert(s->symbolName).second) + return; + + 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); + memset(buf, 0, size); + char *p = buf; + auto *imp = reinterpret_cast(p); + p += sizeof(*imp); + imp->Sig2 = 0xFFFF; + imp->Machine = coffObj->getMachine(); + imp->SizeOfData = impSize; + imp->OrdinalHint = 0; // Only linking by name + imp->TypeInfo = (s->nameType << 2) | s->importType; + + // Write symbol name and DLL name. + memcpy(p, s->symbolName.data(), s->symbolName.size()); + 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); +} diff --git a/gnu/llvm/lld/COFF/InputFiles.h b/gnu/llvm/lld/COFF/InputFiles.h index 50323f596e2..47b5c588c5a 100644 --- a/gnu/llvm/lld/COFF/InputFiles.h +++ b/gnu/llvm/lld/COFF/InputFiles.h @@ -14,6 +14,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" @@ -67,7 +68,8 @@ public: LazyObjectKind, PDBKind, ImportKind, - BitcodeKind + BitcodeKind, + DLLKind }; Kind kind() const { return fileKind; } virtual ~InputFile() {} @@ -144,9 +146,13 @@ public: ArrayRef getDebugChunks() { return debugChunks; } ArrayRef getSXDataChunks() { return sxDataChunks; } ArrayRef getGuardFidChunks() { return guardFidChunks; } + ArrayRef getGuardIATChunks() { return guardIATChunks; } ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } + ArrayRef getGuardEHContChunks() { return guardEHContChunks; } ArrayRef getSymbols() { return symbols; } + MutableArrayRef getMutableSymbols() { return symbols; } + ArrayRef getDebugSection(StringRef secName); // Returns a Symbol object for the symbolIndex'th symbol in the @@ -181,7 +187,7 @@ public: bool hasSafeSEH() { return feat00Flags & 0x1; } // True if this file was compiled with /guard:cf. - bool hasGuardCF() { return feat00Flags & 0x800; } + bool hasGuardCF() { return feat00Flags & 0x4800; } // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like @@ -191,6 +197,8 @@ public: const coff_section *addrsigSec = nullptr; + const coff_section *callgraphSec = nullptr; + // When using Microsoft precompiled headers, this is the PCH's key. // The same key is used by both the precompiled object, and objects using the // precompiled object. Any difference indicates out-of-date objects. @@ -253,9 +261,10 @@ private: // match the existing symbol and its selection. If either old or new // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace // the existing leader. In that case, Prevailing is set to true. - void handleComdatSelection(COFFSymbolRef sym, - llvm::COFF::COMDATType &selection, - bool &prevailing, DefinedRegular *leader); + void + handleComdatSelection(COFFSymbolRef sym, llvm::COFF::COMDATType &selection, + bool &prevailing, DefinedRegular *leader, + const llvm::object::coff_aux_section_definition *def); llvm::Optional createDefined(COFFSymbolRef sym, @@ -280,10 +289,13 @@ private: // 32-bit x86. std::vector sxDataChunks; - // Chunks containing symbol table indices of address taken symbols and longjmp - // targets. These are not linked into the final binary when /guard:cf is set. + // Chunks containing symbol table indices of address taken symbols, address + // taken IAT entries, longjmp and ehcont targets. These are not linked into + // the final binary when /guard:cf is set. std::vector guardFidChunks; + std::vector guardIATChunks; std::vector guardLJmpChunks; + std::vector guardEHContChunks; // This vector contains a list of all symbols defined or referenced by this // file. They are indexed such that you can get a Symbol by symbol @@ -350,7 +362,7 @@ public: const coff_import_header *hdr; Chunk *location = nullptr; - // We want to eliminate dllimported symbols if no one actually refers them. + // We want to eliminate dllimported symbols if no one actually refers to them. // These "Live" bits are used to keep track of which import library members // are actually in use. // @@ -383,6 +395,28 @@ private: std::vector symbols; }; +// .dll file. MinGW only. +class DLLFile : public InputFile { +public: + explicit DLLFile(MemoryBufferRef m) : InputFile(DLLKind, m) {} + static bool classof(const InputFile *f) { return f->kind() == DLLKind; } + void parse() override; + MachineTypes getMachineType() override; + + struct Symbol { + StringRef dllName; + StringRef symbolName; + llvm::COFF::ImportNameType nameType; + llvm::COFF::ImportType importType; + }; + + void makeImport(Symbol *s); + +private: + std::unique_ptr coffObj; + llvm::StringSet<> seen; +}; + inline bool isBitcode(MemoryBufferRef mb) { return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; } diff --git a/gnu/llvm/lld/COFF/LTO.cpp b/gnu/llvm/lld/COFF/LTO.cpp index bb44819e60f..d117abf86f7 100644 --- a/gnu/llvm/lld/COFF/LTO.cpp +++ b/gnu/llvm/lld/COFF/LTO.cpp @@ -62,6 +62,7 @@ static std::string getThinLTOOutputFile(StringRef path) { static lto::Config createConfig() { lto::Config c; c.Options = initTargetOptionsFromCodeGenFlags(); + c.Options.EmitAddrsig = true; // 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. @@ -82,6 +83,10 @@ static lto::Config createConfig() { 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) + ".", @@ -139,6 +144,11 @@ void BitcodeCompiler::add(BitcodeFile &f) { r.VisibleToRegularObj = sym->isUsedInRegularObj; if (r.Prevailing) undefine(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; } checkError(ltoObj->add(std::move(f.obj), resols)); } diff --git a/gnu/llvm/lld/COFF/MinGW.cpp b/gnu/llvm/lld/COFF/MinGW.cpp index e24cdca6ee3..7c1891e67d4 100644 --- a/gnu/llvm/lld/COFF/MinGW.cpp +++ b/gnu/llvm/lld/COFF/MinGW.cpp @@ -7,9 +7,14 @@ //===----------------------------------------------------------------------===// #include "MinGW.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" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -118,7 +123,7 @@ void AutoExporter::addWholeArchive(StringRef path) { } bool AutoExporter::shouldExport(Defined *sym) const { - if (!sym || !sym->isLive() || !sym->getChunk()) + if (!sym || !sym->getChunk()) return false; // Only allow the symbol kinds that make sense to export; in particular, @@ -173,3 +178,86 @@ void lld::coff::writeDefFile(StringRef name) { os << "\n"; } } + +static StringRef mangle(Twine sym) { + assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (config->machine == I386) + return saver.save("_" + sym); + return saver.save(sym); +} + +// Handles -wrap option. +// +// This function instantiates wrapper symbols. At this point, they seem +// 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) { + std::vector v; + DenseSet seen; + + for (auto *arg : args.filtered(OPT_wrap)) { + StringRef name = arg->getValue(); + if (!seen.insert(name).second) + continue; + + Symbol *sym = symtab->findUnderscore(name); + if (!sym) + continue; + + Symbol *real = symtab->addUndefined(mangle("__real_" + name)); + Symbol *wrap = symtab->addUndefined(mangle("__wrap_" + name)); + 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 + // below sort things out before checking finally with + // symtab->resolveRemainingUndefines(). + sym->deferUndefined = true; + real->deferUndefined = true; + // We want to tell LTO not to inline symbols to be overwritten + // because LTO doesn't know the final symbol contents after renaming. + real->canInline = false; + sym->canInline = false; + + // Tell LTO not to eliminate these symbols. + sym->isUsedInRegularObj = true; + if (!isa(wrap)) + wrap->isUsedInRegularObj = true; + } + return v; +} + +// Do renaming for -wrap by updating pointers to symbols. +// +// 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) { + 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()); + // 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()); + map[imp] = wrapimp; + } + } + } + + // Update pointers in input files. + parallelForEach(ObjFile::instances, [&](ObjFile *file) { + MutableArrayRef syms = file->getMutableSymbols(); + for (size_t i = 0, e = syms.size(); i != e; ++i) + if (Symbol *s = map.lookup(syms[i])) + syms[i] = s; + }); +} diff --git a/gnu/llvm/lld/COFF/MinGW.h b/gnu/llvm/lld/COFF/MinGW.h index 3d7a186aa19..2f2bd119c33 100644 --- a/gnu/llvm/lld/COFF/MinGW.h +++ b/gnu/llvm/lld/COFF/MinGW.h @@ -12,7 +12,10 @@ #include "Config.h" #include "Symbols.h" #include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Option/ArgList.h" +#include namespace lld { namespace coff { @@ -36,6 +39,24 @@ public: void writeDefFile(StringRef name); +// The -wrap option is a feature to rename symbols so that you can write +// wrappers for existing functions. If you pass `-wrap:foo`, all +// occurrences of symbol `foo` are resolved to `__wrap_foo` (so, you are +// expected to write `__wrap_foo` function as a wrapper). The original +// symbol becomes accessible as `__real_foo`, so you can call that from your +// wrapper. +// +// This data structure is instantiated for each -wrap option. +struct WrappedSymbol { + Symbol *sym; + Symbol *real; + Symbol *wrap; +}; + +std::vector addWrappedSymbols(llvm::opt::InputArgList &args); + +void wrapSymbols(ArrayRef wrapped); + } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/Options.td b/gnu/llvm/lld/COFF/Options.td index 087d53b5d2d..2ce145520ea 100644 --- a/gnu/llvm/lld/COFF/Options.td +++ b/gnu/llvm/lld/COFF/Options.td @@ -9,6 +9,11 @@ class F : Flag<["/", "-", "/?", "-?"], name>; class P : Joined<["/", "-", "/?", "-?"], name#":">, HelpText; +// Same as P<> above, but without help texts, for private undocumented +// options. +class P_priv : + Joined<["/", "-", "/?", "-?"], name#":">; + // Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the // flag on and using it suffixed by ":no" turns it off. multiclass B { @@ -28,9 +33,12 @@ def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; def base : P<"base", "Base address of the program">; def color_diagnostics: Flag<["--"], "color-diagnostics">, - HelpText<"Use colors in diagnostics">; + HelpText<"Alias for --color-diagnostics=always">; +def no_color_diagnostics: Flag<["--"], "no-color-diagnostics">, + HelpText<"Alias for --color-diagnostics=never">; def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, - HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; + HelpText<"Use colors in diagnostics (default: auto)">, + 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 entry : P<"entry", "Name of entry point symbol">; @@ -50,8 +58,9 @@ def implib : P<"implib", "Import library name">; def lib : F<"lib">, HelpText<"Act like lib.exe; must be first argument if present">; def libpath : P<"libpath", "Additional library search path">; -def linkrepro : P<"linkrepro", - "Dump linker invocation and input files for debugging">; +def linkrepro : Joined<["/", "-", "/?", "-?"], "linkrepro:">, + MetaVarName<"directory">, + HelpText<"Write repro.tar containing inputs and command to reproduce link">; def lldignoreenv : F<"lldignoreenv">, HelpText<"Ignore environment variables like %LIB%">; def lldltocache : P<"lldltocache", @@ -59,7 +68,7 @@ def lldltocache : P<"lldltocache", def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">; def lldsavetemps : F<"lldsavetemps">, - HelpText<"Save temporary files instead of deleting them">; + HelpText<"Save intermediate LTO compilation results">; def machine : P<"machine", "Specify target platform">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; @@ -68,8 +77,6 @@ def opt : P<"opt", "Control optimizations">; def order : P<"order", "Put functions in order">; def out : P<"out", "Path to file to write output">; def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; -def no_color_diagnostics: F<"no-color-diagnostics">, - HelpText<"Do not use colors in diagnostics">; 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">; @@ -129,8 +136,9 @@ def noentry : F<"noentry">, def profile : F<"profile">; def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; -def reproduce : P<"reproduce", - "Dump linker invocation and input files for debugging">; +def reproduce : Joined<["/", "-", "/?", "-?"], "reproduce:">, + MetaVarName<"filename">, + HelpText<"Write tar file containing inputs and command to reproduce link">; def swaprun : P<"swaprun", "Comma-separated list of 'cd' or 'net'">; def swaprun_cd : F<"swaprun:cd">, Alias, AliasArgs<["cd"]>, @@ -194,7 +202,7 @@ def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; defm auto_import : B_priv<"auto-import">; defm runtime_pseudo_reloc : B_priv<"runtime-pseudo-reloc">; def end_lib : F<"end-lib">, - HelpText<"Ends group of objects treated as if they were in a library">; + HelpText<"End group of objects treated as if they were in a library">; def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; defm demangle : B<"demangle", @@ -205,13 +213,15 @@ def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">, def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; def noseh : F<"noseh">; +def osversion : P_priv<"osversion">; def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def pdb_source_path : P<"pdbsourcepath", "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; def start_lib : F<"start-lib">, - HelpText<"Starts group of objects treated as if they were in a library">; + HelpText<"Start group of objects treated as if they were in a library">; +defm stdcall_fixup : B_priv<"stdcall-fixup">; def thinlto_emit_imports_files : F<"thinlto-emit-imports-files">, HelpText<"Emit .imports files with -thinlto-index-only">; @@ -230,11 +240,27 @@ def thinlto_prefix_replace: P< def lto_obj_path : P< "lto-obj-path", "output native object for merged LTO unit to this path">; +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">; def dash_dash_version : Flag<["--"], "version">, - HelpText<"Print version information">; + 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", + "Layout sections to optimize the given callgraph">; +defm call_graph_profile_sort: B< + "call-graph-profile-sort", + "Reorder sections with call graph profile (default)", + "Do not reorder sections with call graph profile">; +def print_symbol_order: P< + "print-symbol-order", + "Print a symbol order specified by /call-graph-ordering-file and " + "/call-graph-profile-sort into the specified file">; +def wrap : P_priv<"wrap">; // Flags for debugging def lldmap : F<"lldmap">; diff --git a/gnu/llvm/lld/COFF/PDB.cpp b/gnu/llvm/lld/COFF/PDB.cpp index 49d04add5be..e355857dd93 100644 --- a/gnu/llvm/lld/COFF/PDB.cpp +++ b/gnu/llvm/lld/COFF/PDB.cpp @@ -62,12 +62,14 @@ using namespace lld; 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); @@ -107,18 +109,39 @@ public: /// Link info for each import file in the symbol table into the PDB. void addImportFilesToPDB(ArrayRef outputSections); + void createModuleDBI(ObjFile *file); + /// Link CodeView from a single object file into the target (output) PDB. /// When a precompiled headers object is linked, its TPI map might be provided /// externally. void addDebug(TpiSource *source); - const CVIndexMap *mergeTypeRecords(TpiSource *source, CVIndexMap *localMap); - - void addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap); - - void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, - std::vector &stringTableRefs, - BinaryStreamRef symData); + void addDebugSymbols(TpiSource *source); + + // Analyze the symbol records to separate module symbols from global symbols, + // find string references, and calculate how large the symbol stream will be + // in the PDB. + void analyzeSymbolSubsection(SectionChunk *debugChunk, + uint32_t &moduleSymOffset, + uint32_t &nextRelocIndex, + std::vector &stringTableFixups, + BinaryStreamRef symData); + + // Write all module symbols from all all live debug symbol subsections of the + // given object file into the given stream writer. + Error writeAllModuleSymbolRecords(ObjFile *file, BinaryStreamWriter &writer); + + // Callback to copy and relocate debug symbols during PDB file writing. + static Error commitSymbolsForObject(void *ctx, void *obj, + BinaryStreamWriter &writer); + + // Copy the symbol record, relocate it, and fix the alignment if necessary. + // Rewrite type indices in the record. Replace unrecognized symbol records + // with S_SKIP records. + void writeSymbolRecord(SectionChunk *debugChunk, + ArrayRef sectionContents, CVSymbol sym, + size_t alignedSize, uint32_t &nextRelocIndex, + std::vector &storage); /// Add the section map and section contributions to the PDB. void addSections(ArrayRef outputSections, @@ -147,8 +170,20 @@ private: uint64_t globalSymbols = 0; uint64_t moduleSymbols = 0; uint64_t publicSymbols = 0; + uint64_t nbTypeRecords = 0; + uint64_t nbTypeRecordsBytes = 0; +}; + +/// Represents an unrelocated DEBUG_S_FRAMEDATA subsection. +struct UnrelocatedFpoData { + SectionChunk *debugChunk = nullptr; + ArrayRef subsecData; + uint32_t relocIndex = 0; }; +/// The size of the magic bytes at the beginning of a symbol section or stream. +enum : uint32_t { kSymbolStreamMagicSize = 4 }; + class DebugSHandler { PDBLinker &linker; @@ -156,7 +191,7 @@ class DebugSHandler { ObjFile &file; /// The result of merging type indices. - const CVIndexMap *indexMap; + TpiSource *source; /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by /// index from other records in the .debug$S section. All of these strings @@ -175,23 +210,36 @@ class DebugSHandler { /// contain string table references which need to be re-written, so we /// collect them all here and re-write them after all subsections have been /// discovered and processed. - std::vector newFpoFrames; + std::vector frameDataSubsecs; + + /// List of string table references in symbol records. Later they will be + /// applied to the symbols during PDB writing. + std::vector stringTableFixups; + + /// Sum of the size of all module symbol records across all .debug$S sections. + /// Includes record realignment and the size of the symbol stream magic + /// prefix. + uint32_t moduleStreamSize = kSymbolStreamMagicSize; + + /// Next relocation index in the current .debug$S section. Resets every + /// handleDebugS call. + uint32_t nextRelocIndex = 0; - /// Pointers to raw memory that we determine have string table references - /// that need to be re-written. We first process all .debug$S subsections - /// to ensure that we can handle subsections written in any order, building - /// up this list as we go. At the end, we use the string table (which must - /// have been discovered by now else it is an error) to re-write these - /// references. - std::vector stringTableReferences; + void advanceRelocIndex(SectionChunk *debugChunk, ArrayRef subsec); - void mergeInlineeLines(const DebugSubsectionRecord &inlineeLines); + void addUnrelocatedSubsection(SectionChunk *debugChunk, + const DebugSubsectionRecord &ss); + + void addFrameDataSubsection(SectionChunk *debugChunk, + const DebugSubsectionRecord &ss); + + void recordStringTableReferences(CVSymbol sym, uint32_t symOffset); public: - DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap *indexMap) - : linker(linker), file(file), indexMap(indexMap) {} + DebugSHandler(PDBLinker &linker, ObjFile &file, TpiSource *source) + : linker(linker), file(file), source(source) {} - void handleDebugS(ArrayRef relocatedDebugContents); + void handleDebugS(SectionChunk *debugChunk); void finish(); }; @@ -250,68 +298,34 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, }); } -static bool remapTypeIndex(TypeIndex &ti, ArrayRef typeIndexMap) { - if (ti.isSimple()) - return true; - if (ti.toArrayIndex() >= typeIndexMap.size()) - return false; - ti = typeIndexMap[ti.toArrayIndex()]; - return true; -} - -static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind, - MutableArrayRef recordBytes, - const CVIndexMap &indexMap, - ArrayRef typeRefs) { - MutableArrayRef contents = - recordBytes.drop_front(sizeof(RecordPrefix)); - for (const TiReference &ref : typeRefs) { - unsigned byteSize = ref.Count * sizeof(TypeIndex); - if (contents.size() < ref.Offset + byteSize) - fatal("symbol record too short"); - - // This can be an item index or a type index. Choose the appropriate map. - ArrayRef typeOrItemMap = indexMap.tpiMap; - bool isItemIndex = ref.Kind == TiRefKind::IndexRef; - if (isItemIndex && indexMap.isTypeServerMap) - typeOrItemMap = indexMap.ipiMap; - - MutableArrayRef tIs( - reinterpret_cast(contents.data() + ref.Offset), ref.Count); - for (TypeIndex &ti : tIs) { - if (!remapTypeIndex(ti, typeOrItemMap)) { - log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " + - file->getName() + " with bad " + (isItemIndex ? "item" : "type") + - " index 0x" + utohexstr(ti.getIndex())); - ti = TypeIndex(SimpleTypeKind::NotTranslated); - continue; - } - } - } -} - -static void -recordStringTableReferenceAtOffset(MutableArrayRef contents, - uint32_t offset, - std::vector &strTableRefs) { - contents = - contents.drop_front(offset).take_front(sizeof(support::ulittle32_t)); - ulittle32_t *index = reinterpret_cast(contents.data()); - strTableRefs.push_back(index); +static void addGHashTypeInfo(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) { + 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 -recordStringTableReferences(SymbolKind kind, MutableArrayRef contents, - std::vector &strTableRefs) { +recordStringTableReferences(CVSymbol sym, uint32_t symOffset, + std::vector &stringTableFixups) { // For now we only handle S_FILESTATIC, but we may need the same logic for // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any // PDBs that contain these types of records, so because of the uncertainty // they are omitted here until we can prove that it's necessary. - switch (kind) { - case SymbolKind::S_FILESTATIC: + switch (sym.kind()) { + case SymbolKind::S_FILESTATIC: { // FileStaticSym::ModFileOffset - recordStringTableReferenceAtOffset(contents, 8, strTableRefs); + uint32_t ref = *reinterpret_cast(&sym.data()[8]); + stringTableFixups.push_back({ref, symOffset + 8}); break; + } case SymbolKind::S_DEFRANGE: case SymbolKind::S_DEFRANGE_SUBFIELD: log("Not fixing up string table reference in S_DEFRANGE / " @@ -330,7 +344,7 @@ 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, - TypeCollection &idTable) { + TypeMerger &tMerger, TpiSource *source) { RecordPrefix *prefix = reinterpret_cast(recordData.data()); SymbolKind kind = symbolKind(recordData); @@ -357,13 +371,30 @@ static void translateIdSymbols(MutableArrayRef &recordData, reinterpret_cast(content.data() + refs[0].Offset); // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in // the IPI stream, whose `FunctionType` member refers to the TPI stream. - // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and + // Note that LF_FUNC_ID and LF_MFUNC_ID have the same record layout, and // in both cases we just need the second type index. if (!ti->isSimple() && !ti->isNoneType()) { - CVType funcIdData = idTable.getType(*ti); - ArrayRef tiBuf = funcIdData.data().slice(8, 4); - assert(tiBuf.size() == 4 && "corrupt LF_[MEM]FUNC_ID record"); - *ti = *reinterpret_cast(tiBuf.data()); + TypeIndex newType = TypeIndex(SimpleTypeKind::NotTranslated); + if (config->debugGHashes) { + auto idToType = tMerger.funcIdToType.find(*ti); + if (idToType != tMerger.funcIdToType.end()) + newType = idToType->second; + } else { + if (tMerger.getIDTable().contains(*ti)) { + CVType funcIdData = tMerger.getIDTable().getType(*ti); + if (funcIdData.length() >= 8 && (funcIdData.kind() == LF_FUNC_ID || + funcIdData.kind() == LF_MFUNC_ID)) { + newType = *reinterpret_cast(&funcIdData.data()[8]); + } + } + } + if (newType == TypeIndex(SimpleTypeKind::NotTranslated)) { + warn(formatv("procedure symbol record for `{0}` in {1} refers to PDB " + "item index {2:X} which is not a valid function ID record", + getSymbolName(CVSymbol(recordData)), + source->file->getName(), ti->getIndex())); + } + *ti = newType; } kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 @@ -372,60 +403,48 @@ static void translateIdSymbols(MutableArrayRef &recordData, } } -/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. -/// The object file may not be aligned. -static MutableArrayRef -copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef &alignedMem) { - size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); - assert(size >= 4 && "record too short"); - assert(size <= MaxRecordLength && "record too long"); - assert(alignedMem.size() >= size && "didn't preallocate enough"); - - // Copy the symbol record and zero out any padding bytes. - MutableArrayRef newData = alignedMem.take_front(size); - alignedMem = alignedMem.drop_front(size); - memcpy(newData.data(), sym.data().data(), sym.length()); - memset(newData.data() + sym.length(), 0, size - sym.length()); - - // Update the record prefix length. It should point to the beginning of the - // next record. - auto *prefix = reinterpret_cast(newData.data()); - prefix->RecordLen = size - 2; - return newData; -} - +namespace { struct ScopeRecord { ulittle32_t ptrParent; ulittle32_t ptrEnd; }; +} // namespace -struct SymbolScope { - ScopeRecord *openingRecord; - uint32_t scopeOffset; -}; +/// Given a pointer to a symbol record that opens a scope, return a pointer to +/// the scope fields. +static ScopeRecord *getSymbolScopeFields(void *sym) { + return reinterpret_cast(reinterpret_cast(sym) + + sizeof(RecordPrefix)); +} -static void scopeStackOpen(SmallVectorImpl &stack, - uint32_t curOffset, CVSymbol &sym) { - assert(symbolOpensScope(sym.kind())); - SymbolScope s; - s.scopeOffset = curOffset; - s.openingRecord = const_cast( - reinterpret_cast(sym.content().data())); - s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset; - stack.push_back(s); +// To open a scope, push the offset of the current symbol record onto the +// stack. +static void scopeStackOpen(SmallVectorImpl &stack, + std::vector &storage) { + stack.push_back(storage.size()); } -static void scopeStackClose(SmallVectorImpl &stack, - uint32_t curOffset, InputFile *file) { +// To close a scope, update the record that opened the scope. +static void scopeStackClose(SmallVectorImpl &stack, + std::vector &storage, + uint32_t storageBaseOffset, ObjFile *file) { if (stack.empty()) { warn("symbol scopes are not balanced in " + file->getName()); return; } - SymbolScope s = stack.pop_back_val(); - s.openingRecord->ptrEnd = curOffset; + + // Update ptrEnd of the record that opened the scope to point to the + // current record, if we are writing into the module symbol stream. + uint32_t offOpen = stack.pop_back_val(); + uint32_t offEnd = storageBaseOffset + storage.size(); + uint32_t offParent = stack.empty() ? 0 : (stack.back() + storageBaseOffset); + ScopeRecord *scopeRec = getSymbolScopeFields(&(storage)[offOpen]); + scopeRec->ptrParent = offParent; + scopeRec->ptrEnd = offEnd; } -static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { +static bool symbolGoesInModuleStream(const CVSymbol &sym, + unsigned symbolScopeDepth) { switch (sym.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_CONSTANT: @@ -439,7 +458,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { return false; // S_UDT records go in the module stream if it is not a global S_UDT. case SymbolKind::S_UDT: - return !isGlobalScope; + return symbolScopeDepth > 0; // S_GDATA32 does not go in the module stream, but S_LDATA32 does. case SymbolKind::S_LDATA32: case SymbolKind::S_LTHREAD32: @@ -449,13 +468,15 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { } static bool symbolGoesInGlobalsStream(const CVSymbol &sym, - bool isFunctionScope) { + unsigned symbolScopeDepth) { switch (sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_GDATA32: case SymbolKind::S_GTHREAD32: case SymbolKind::S_GPROC32: case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_LPROC32_ID: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place // since they are synthesized by the linker in response to S_GPROC32 and // S_LPROC32, but if we do see them, copy them straight through. @@ -466,14 +487,16 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym, case SymbolKind::S_UDT: case SymbolKind::S_LDATA32: case SymbolKind::S_LTHREAD32: - return !isFunctionScope; + return symbolScopeDepth == 0; default: return false; } } static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, - unsigned symOffset, const CVSymbol &sym) { + unsigned symOffset, + std::vector &symStorage) { + CVSymbol sym(makeArrayRef(symStorage)); switch (sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_UDT: @@ -482,9 +505,14 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, case SymbolKind::S_LTHREAD32: case SymbolKind::S_LDATA32: case SymbolKind::S_PROCREF: - case SymbolKind::S_LPROCREF: - builder.addGlobalSymbol(sym); + 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()); + memcpy(mem, sym.data().data(), sym.length()); + builder.addGlobalSymbol(CVSymbol(makeArrayRef(mem, sym.length()))); break; + } case SymbolKind::S_GPROC32: case SymbolKind::S_LPROC32: { SymbolRecordKind k = SymbolRecordKind::ProcRefSym; @@ -505,119 +533,189 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, } } -void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, - std::vector &stringTableRefs, - BinaryStreamRef symData) { - ArrayRef symsBuffer; - cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); - SmallVector scopes; - - // Iterate every symbol to check if any need to be realigned, and if so, how - // much space we need to allocate for them. - bool needsRealignment = false; - unsigned totalRealignedSize = 0; - auto ec = forEachCodeViewRecord( - symsBuffer, [&](CVSymbol sym) -> llvm::Error { - unsigned realignedSize = - alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); - needsRealignment |= realignedSize != sym.length(); - totalRealignedSize += realignedSize; - return Error::success(); - }); - - // If any of the symbol record lengths was corrupt, ignore them all, warn - // about it, and move on. - if (ec) { - warn("corrupt symbol records in " + file->getName()); - consumeError(std::move(ec)); +// Check if the given symbol record was padded for alignment. If so, zero out +// the padding bytes and update the record prefix with the new size. +static void fixRecordAlignment(MutableArrayRef recordBytes, + size_t oldSize) { + size_t alignedSize = recordBytes.size(); + if (oldSize == alignedSize) return; - } + reinterpret_cast(recordBytes.data())->RecordLen = + alignedSize - 2; + memset(recordBytes.data() + oldSize, 0, alignedSize - oldSize); +} + +// Replace any record with a skip record of the same size. This is useful when +// we have reserved size for a symbol record, but type index remapping fails. +static void replaceWithSkipRecord(MutableArrayRef recordBytes) { + memset(recordBytes.data(), 0, recordBytes.size()); + auto *prefix = reinterpret_cast(recordBytes.data()); + prefix->RecordKind = SymbolKind::S_SKIP; + prefix->RecordLen = recordBytes.size() - 2; +} - // If any symbol needed realignment, allocate enough contiguous memory for - // them all. Typically symbol subsections are small enough that this will not - // cause fragmentation. - MutableArrayRef alignedSymbolMem; - if (needsRealignment) { - void *alignedData = - bAlloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); - alignedSymbolMem = makeMutableArrayRef( - reinterpret_cast(alignedData), totalRealignedSize); +// Copy the symbol record, relocate it, and fix the alignment if necessary. +// Rewrite type indices in the record. Replace unrecognized symbol records with +// S_SKIP records. +void PDBLinker::writeSymbolRecord(SectionChunk *debugChunk, + ArrayRef sectionContents, + CVSymbol sym, size_t alignedSize, + uint32_t &nextRelocIndex, + std::vector &storage) { + // Allocate space for the new record at the end of the storage. + storage.resize(storage.size() + alignedSize); + auto recordBytes = MutableArrayRef(storage).take_back(alignedSize); + + // Copy the symbol record and relocate it. + debugChunk->writeAndRelocateSubsection(sectionContents, sym.data(), + nextRelocIndex, recordBytes.data()); + fixRecordAlignment(recordBytes, sym.length()); + + // Re-map all the type index references. + TpiSource *source = debugChunk->file->debugTypesObj; + if (!source->remapTypesInSymbolRecord(recordBytes)) { + log("ignoring unknown symbol record with kind 0x" + utohexstr(sym.kind())); + replaceWithSkipRecord(recordBytes); } - // Iterate again, this time doing the real work. - unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset(); - ArrayRef bulkSymbols; - cantFail(forEachCodeViewRecord( - symsBuffer, [&](CVSymbol sym) -> llvm::Error { - // Align the record if required. - MutableArrayRef recordBytes; - if (needsRealignment) { - recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem); - sym = CVSymbol(recordBytes); - } else { - // Otherwise, we can actually mutate the symbol directly, since we - // copied it to apply relocations. - recordBytes = makeMutableArrayRef( - const_cast(sym.data().data()), sym.length()); - } + // An object file may have S_xxx_ID symbols, but these get converted to + // "real" symbols in a PDB. + translateIdSymbols(recordBytes, tMerger, source); +} - // Discover type index references in the record. Skip it if we don't - // know where they are. - SmallVector typeRefs; - if (!discoverTypeIndicesInSymbol(sym, typeRefs)) { - log("ignoring unknown symbol record with kind 0x" + - utohexstr(sym.kind())); - return Error::success(); - } +void PDBLinker::analyzeSymbolSubsection( + SectionChunk *debugChunk, uint32_t &moduleSymOffset, + uint32_t &nextRelocIndex, std::vector &stringTableFixups, + BinaryStreamRef symData) { + ObjFile *file = debugChunk->file; + uint32_t moduleSymStart = moduleSymOffset; - // Re-map all the type index references. - remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap, - typeRefs); + uint32_t scopeLevel = 0; + std::vector storage; + ArrayRef sectionContents = debugChunk->getContents(); - // An object file may have S_xxx_ID symbols, but these get converted to - // "real" symbols in a PDB. - translateIdSymbols(recordBytes, tMerger.getIDTable()); - sym = CVSymbol(recordBytes); + ArrayRef symsBuffer; + cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); - // If this record refers to an offset in the object file's string table, - // add that item to the global PDB string table and re-write the index. - recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs); + if (symsBuffer.empty()) + warn("empty symbols subsection in " + file->getName()); - // Fill in "Parent" and "End" fields by maintaining a stack of scopes. + Error ec = forEachCodeViewRecord( + symsBuffer, [&](CVSymbol sym) -> llvm::Error { + // Track the current scope. if (symbolOpensScope(sym.kind())) - scopeStackOpen(scopes, curSymOffset, sym); + ++scopeLevel; else if (symbolEndsScope(sym.kind())) - scopeStackClose(scopes, curSymOffset, file); + --scopeLevel; + + uint32_t alignedSize = + alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); - // Add the symbol to the globals stream if necessary. Do this before - // adding the symbol to the module since we may need to get the next - // symbol offset, and writing to the module's symbol stream will update - // that offset. - if (symbolGoesInGlobalsStream(sym, !scopes.empty())) { + // Copy global records. Some global records (mainly procedures) + // reference the current offset into the module stream. + if (symbolGoesInGlobalsStream(sym, scopeLevel)) { + storage.clear(); + writeSymbolRecord(debugChunk, sectionContents, sym, alignedSize, + nextRelocIndex, storage); addGlobalSymbol(builder.getGsiBuilder(), - file->moduleDBI->getModuleIndex(), curSymOffset, sym); + file->moduleDBI->getModuleIndex(), moduleSymOffset, + storage); ++globalSymbols; } - if (symbolGoesInModuleStream(sym, scopes.empty())) { - // Add symbols to the module in bulk. If this symbol is contiguous - // with the previous run of symbols to add, combine the ranges. If - // not, close the previous range of symbols and start a new one. - if (sym.data().data() == bulkSymbols.end()) { - bulkSymbols = makeArrayRef(bulkSymbols.data(), - bulkSymbols.size() + sym.length()); - } else { - file->moduleDBI->addSymbolsInBulk(bulkSymbols); - bulkSymbols = recordBytes; - } - curSymOffset += sym.length(); + // Update the module stream offset and record any string table index + // references. There are very few of these and they will be rewritten + // later during PDB writing. + if (symbolGoesInModuleStream(sym, scopeLevel)) { + recordStringTableReferences(sym, moduleSymOffset, stringTableFixups); + moduleSymOffset += alignedSize; ++moduleSymbols; } + return Error::success(); - })); + }); - // Add any remaining symbols we've accumulated. - file->moduleDBI->addSymbolsInBulk(bulkSymbols); + // If we encountered corrupt records, ignore the whole subsection. If we wrote + // any partial records, undo that. For globals, we just keep what we have and + // continue. + if (ec) { + warn("corrupt symbol records in " + file->getName()); + moduleSymOffset = moduleSymStart; + consumeError(std::move(ec)); + } +} + +Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file, + BinaryStreamWriter &writer) { + std::vector storage; + SmallVector scopes; + + // Visit all live .debug$S sections a second time, and write them to the PDB. + for (SectionChunk *debugChunk : file->getDebugChunks()) { + if (!debugChunk->live || debugChunk->getSize() == 0 || + debugChunk->getSectionName() != ".debug$S") + continue; + + ArrayRef sectionContents = debugChunk->getContents(); + auto contents = + SectionChunk::consumeDebugMagic(sectionContents, ".debug$S"); + DebugSubsectionArray subsections; + BinaryStreamReader reader(contents, support::little); + exitOnErr(reader.readArray(subsections, contents.size())); + + uint32_t nextRelocIndex = 0; + for (const DebugSubsectionRecord &ss : subsections) { + if (ss.kind() != DebugSubsectionKind::Symbols) + continue; + + uint32_t moduleSymStart = writer.getOffset(); + scopes.clear(); + storage.clear(); + ArrayRef symsBuffer; + BinaryStreamRef sr = ss.getRecordData(); + cantFail(sr.readBytes(0, sr.getLength(), symsBuffer)); + auto ec = forEachCodeViewRecord( + symsBuffer, [&](CVSymbol sym) -> llvm::Error { + // Track the current scope. Only update records in the postmerge + // pass. + if (symbolOpensScope(sym.kind())) + scopeStackOpen(scopes, storage); + else if (symbolEndsScope(sym.kind())) + scopeStackClose(scopes, storage, moduleSymStart, file); + + // Copy, relocate, and rewrite each module symbol. + if (symbolGoesInModuleStream(sym, scopes.size())) { + uint32_t alignedSize = + alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + writeSymbolRecord(debugChunk, sectionContents, sym, alignedSize, + nextRelocIndex, storage); + } + return Error::success(); + }); + + // If we encounter corrupt records in the second pass, ignore them. We + // already warned about them in the first analysis pass. + if (ec) { + consumeError(std::move(ec)); + storage.clear(); + } + + // Writing bytes has a very high overhead, so write the entire subsection + // at once. + // TODO: Consider buffering symbols for the entire object file to reduce + // overhead even further. + if (Error e = writer.writeBytes(storage)) + return e; + } + } + + return Error::success(); +} + +Error PDBLinker::commitSymbolsForObject(void *ctx, void *obj, + BinaryStreamWriter &writer) { + return static_cast(ctx)->writeAllModuleSymbolRecords( + static_cast(obj), writer); } static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { @@ -657,18 +755,18 @@ translateStringTableIndex(uint32_t objIndex, return pdbStrTable.insert(*expectedString); } -void DebugSHandler::handleDebugS(ArrayRef relocatedDebugContents) { - relocatedDebugContents = - SectionChunk::consumeDebugMagic(relocatedDebugContents, ".debug$S"); - +void DebugSHandler::handleDebugS(SectionChunk *debugChunk) { + // Note that we are processing the *unrelocated* section contents. They will + // be relocated later during PDB writing. + ArrayRef contents = debugChunk->getContents(); + contents = SectionChunk::consumeDebugMagic(contents, ".debug$S"); DebugSubsectionArray subsections; - BinaryStreamReader reader(relocatedDebugContents, support::little); - exitOnErr(reader.readArray(subsections, relocatedDebugContents.size())); + BinaryStreamReader reader(contents, support::little); + exitOnErr(reader.readArray(subsections, contents.size())); + debugChunk->sortRelocations(); - // If there is no index map, use an empty one. - CVIndexMap tempIndexMap; - if (!indexMap) - indexMap = &tempIndexMap; + // Reset the relocation index, since this is a new section. + nextRelocIndex = 0; for (const DebugSubsectionRecord &ss : subsections) { // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++ @@ -689,30 +787,17 @@ void DebugSHandler::handleDebugS(ArrayRef relocatedDebugContents) { exitOnErr(checksums.initialize(ss.getRecordData())); break; case DebugSubsectionKind::Lines: - // We can add the relocated line table directly to the PDB without - // modification because the file checksum offsets will stay the same. - file.moduleDBI->addDebugSubsection(ss); - break; case DebugSubsectionKind::InlineeLines: - // The inlinee lines subsection also has file checksum table references - // that can be used directly, but it contains function id references that - // must be remapped. - mergeInlineeLines(ss); + addUnrelocatedSubsection(debugChunk, ss); break; - case DebugSubsectionKind::FrameData: { - // We need to re-write string table indices here, so save off all - // frame data subsections until we've processed the entire list of - // subsections so that we can be sure we have the string table. - DebugFrameDataSubsectionRef fds; - exitOnErr(fds.initialize(ss.getRecordData())); - newFpoFrames.push_back(std::move(fds)); + case DebugSubsectionKind::FrameData: + addFrameDataSubsection(debugChunk, ss); break; - } - case DebugSubsectionKind::Symbols: { - linker.mergeSymbolRecords(&file, *indexMap, stringTableReferences, - ss.getRecordData()); + case DebugSubsectionKind::Symbols: + linker.analyzeSymbolSubsection(debugChunk, moduleStreamSize, + nextRelocIndex, stringTableFixups, + ss.getRecordData()); break; - } case DebugSubsectionKind::CrossScopeImports: case DebugSubsectionKind::CrossScopeExports: @@ -739,6 +824,85 @@ void DebugSHandler::handleDebugS(ArrayRef relocatedDebugContents) { } } +void DebugSHandler::advanceRelocIndex(SectionChunk *sc, + ArrayRef subsec) { + ptrdiff_t vaBegin = subsec.data() - sc->getContents().data(); + assert(vaBegin > 0); + auto relocs = sc->getRelocs(); + for (; nextRelocIndex < relocs.size(); ++nextRelocIndex) { + if (relocs[nextRelocIndex].VirtualAddress >= vaBegin) + break; + } +} + +namespace { +/// Wrapper class for unrelocated line and inlinee line subsections, which +/// require only relocation and type index remapping to add to the PDB. +class UnrelocatedDebugSubsection : public DebugSubsection { +public: + UnrelocatedDebugSubsection(DebugSubsectionKind k, SectionChunk *debugChunk, + ArrayRef subsec, uint32_t relocIndex) + : DebugSubsection(k), debugChunk(debugChunk), subsec(subsec), + relocIndex(relocIndex) {} + + Error commit(BinaryStreamWriter &writer) const override; + uint32_t calculateSerializedSize() const override { return subsec.size(); } + + SectionChunk *debugChunk; + ArrayRef subsec; + uint32_t relocIndex; +}; +} // namespace + +Error UnrelocatedDebugSubsection::commit(BinaryStreamWriter &writer) const { + std::vector relocatedBytes(subsec.size()); + uint32_t tmpRelocIndex = relocIndex; + debugChunk->writeAndRelocateSubsection(debugChunk->getContents(), subsec, + tmpRelocIndex, relocatedBytes.data()); + + // Remap type indices in inlinee line records in place. Skip the remapping if + // there is no type source info. + if (kind() == DebugSubsectionKind::InlineeLines && + debugChunk->file->debugTypesObj) { + TpiSource *source = debugChunk->file->debugTypesObj; + DebugInlineeLinesSubsectionRef inlineeLines; + BinaryStreamReader storageReader(relocatedBytes, support::little); + exitOnErr(inlineeLines.initialize(storageReader)); + for (const InlineeSourceLine &line : inlineeLines) { + TypeIndex &inlinee = *const_cast(&line.Header->Inlinee); + if (!source->remapTypeIndex(inlinee, TiRefKind::IndexRef)) { + log("bad inlinee line record in " + debugChunk->file->getName() + + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); + } + } + } + + return writer.writeBytes(relocatedBytes); +} + +void DebugSHandler::addUnrelocatedSubsection(SectionChunk *debugChunk, + const DebugSubsectionRecord &ss) { + ArrayRef subsec; + BinaryStreamRef sr = ss.getRecordData(); + cantFail(sr.readBytes(0, sr.getLength(), subsec)); + advanceRelocIndex(debugChunk, subsec); + file.moduleDBI->addDebugSubsection( + std::make_shared(ss.kind(), debugChunk, + subsec, nextRelocIndex)); +} + +void DebugSHandler::addFrameDataSubsection(SectionChunk *debugChunk, + const DebugSubsectionRecord &ss) { + // We need to re-write string table indices here, so save off all + // frame data subsections until we've processed the entire list of + // subsections so that we can be sure we have the string table. + ArrayRef subsec; + BinaryStreamRef sr = ss.getRecordData(); + cantFail(sr.readBytes(0, sr.getLength(), subsec)); + advanceRelocIndex(debugChunk, subsec); + frameDataSubsecs.push_back({debugChunk, subsec, nextRelocIndex}); +} + static Expected getFileName(const DebugStringTableSubsectionRef &strings, const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) { @@ -749,29 +913,14 @@ getFileName(const DebugStringTableSubsectionRef &strings, return strings.getString(offset); } -void DebugSHandler::mergeInlineeLines( - const DebugSubsectionRecord &inlineeSubsection) { - DebugInlineeLinesSubsectionRef inlineeLines; - exitOnErr(inlineeLines.initialize(inlineeSubsection.getRecordData())); - - // Remap type indices in inlinee line records in place. - for (const InlineeSourceLine &line : inlineeLines) { - TypeIndex &inlinee = *const_cast(&line.Header->Inlinee); - ArrayRef typeOrItemMap = - indexMap->isTypeServerMap ? indexMap->ipiMap : indexMap->tpiMap; - if (!remapTypeIndex(inlinee, typeOrItemMap)) { - log("bad inlinee line record in " + file.getName() + - " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); - } - } - - // Add the modified inlinee line subsection directly. - file.moduleDBI->addDebugSubsection(inlineeSubsection); -} - void DebugSHandler::finish() { pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder(); + // If we found any symbol records for the module symbol stream, defer them. + if (moduleStreamSize > kSymbolStreamMagicSize) + file.moduleDBI->addUnmergedSymbols(&file, moduleStreamSize - + kSymbolStreamMagicSize); + // We should have seen all debug subsections across the entire object file now // which means that if a StringTable subsection and Checksums subsection were // present, now is the time to handle them. @@ -780,26 +929,50 @@ void DebugSHandler::finish() { fatal(".debug$S sections with a checksums subsection must also contain a " "string table subsection"); - if (!stringTableReferences.empty()) + if (!stringTableFixups.empty()) warn("No StringTable subsection was encountered, but there are string " "table references"); return; } - // Rewrite string table indices in the Fpo Data and symbol records to refer to - // the global PDB string table instead of the object file string table. - for (DebugFrameDataSubsectionRef &fds : newFpoFrames) { - const ulittle32_t *reloc = fds.getRelocPtr(); + // 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 + // must also be rewritten to use the PDB string table. + for (const UnrelocatedFpoData &subsec : frameDataSubsecs) { + // Relocate the first four bytes of the subection and reinterpret them as a + // 32 bit integer. + SectionChunk *debugChunk = subsec.debugChunk; + ArrayRef subsecData = subsec.subsecData; + uint32_t relocIndex = subsec.relocIndex; + auto unrelocatedRvaStart = subsecData.take_front(sizeof(uint32_t)); + uint8_t relocatedRvaStart[sizeof(uint32_t)]; + debugChunk->writeAndRelocateSubsection(debugChunk->getContents(), + unrelocatedRvaStart, relocIndex, + &relocatedRvaStart[0]); + uint32_t rvaStart; + memcpy(&rvaStart, &relocatedRvaStart[0], sizeof(uint32_t)); + + // Copy each frame data record, add in rvaStart, translate string table + // indices, and add the record to the PDB. + DebugFrameDataSubsectionRef fds; + BinaryStreamReader reader(subsecData, support::little); + exitOnErr(fds.initialize(reader)); for (codeview::FrameData fd : fds) { - fd.RvaStart += *reloc; + fd.RvaStart += rvaStart; fd.FrameFunc = translateStringTableIndex(fd.FrameFunc, cvStrTab, linker.pdbStrTab); dbiBuilder.addNewFpoData(fd); } } - for (ulittle32_t *ref : stringTableReferences) - *ref = translateStringTableIndex(*ref, cvStrTab, linker.pdbStrTab); + // Translate the fixups and pass them off to the module builder so they will + // be applied during writing. + for (StringTableFixup &ref : stringTableFixups) { + ref.StrTabOffset = + translateStringTableIndex(ref.StrTabOffset, cvStrTab, linker.pdbStrTab); + } + file.moduleDBI->setStringTableFixups(std::move(stringTableFixups)); // Make a new file checksum table that refers to offsets in the PDB-wide // string table. Generally the string table subsection appears after the @@ -834,23 +1007,6 @@ static void warnUnusable(InputFile *f, Error e) { warn(msg); } -const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source, - CVIndexMap *localMap) { - ScopedTimer t(typeMergingTimer); - // Before we can process symbol substreams from .debug$S, we need to process - // type information, file checksums, and the string table. Add type info to - // the PDB first, so that we can get the map from object file type and item - // indices to PDB type and item indices. - Expected r = source->mergeDebugT(&tMerger, localMap); - - // If the .debug$T sections fail to merge, assume there is no debug info. - if (!r) { - warnUnusable(source->file, r.takeError()); - return nullptr; - } - return *r; -} - // Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef relocateDebugChunk(SectionChunk &debugChunk) { uint8_t *buffer = bAlloc.Allocate(debugChunk.getSize()); @@ -860,12 +1016,17 @@ static ArrayRef relocateDebugChunk(SectionChunk &debugChunk) { return makeArrayRef(buffer, debugChunk.getSize()); } -void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) { +void PDBLinker::addDebugSymbols(TpiSource *source) { + // If this TpiSource doesn't have an object file, it must be from a type + // server PDB. Type server PDBs do not contain symbols, so stop here. + if (!source->file) + return; + ScopedTimer t(symbolMergingTimer); pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); - DebugSHandler dsh(*this, *file, indexMap); + DebugSHandler dsh(*this, *source->file, source); // Now do all live .debug$S and .debug$F sections. - for (SectionChunk *debugChunk : file->getDebugChunks()) { + for (SectionChunk *debugChunk : source->file->getDebugChunks()) { if (!debugChunk->live || debugChunk->getSize() == 0) continue; @@ -874,11 +1035,12 @@ void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) { if (!isDebugS && !isDebugF) continue; - ArrayRef relocatedDebugContents = relocateDebugChunk(*debugChunk); - if (isDebugS) { - dsh.handleDebugS(relocatedDebugContents); + dsh.handleDebugS(debugChunk); } else if (isDebugF) { + // Handle old FPO data .debug$F sections. These are relatively rare. + ArrayRef relocatedDebugContents = + relocateDebugChunk(*debugChunk); FixedStreamArray fpoRecords; BinaryStreamReader reader(relocatedDebugContents, support::little); uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData); @@ -899,17 +1061,18 @@ void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) { // path to the object into the PDB. If this is a plain object, we make its // path absolute. If it's an object in an archive, we make the archive path // absolute. -static void createModuleDBI(pdb::PDBFileBuilder &builder, ObjFile *file) { +void PDBLinker::createModuleDBI(ObjFile *file) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); SmallString<128> objName; bool inArchive = !file->parentName.empty(); objName = inArchive ? file->parentName : file->getName(); pdbMakeAbsolute(objName); - StringRef modName = inArchive ? file->getName() : StringRef(objName); + StringRef modName = inArchive ? file->getName() : objName.str(); file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); file->moduleDBI->setObjFileName(objName); + file->moduleDBI->setMergeSymbolsCallback(this, &commitSymbolsForObject); ArrayRef chunks = file->getChunks(); uint32_t modi = file->moduleDBI->getModuleIndex(); @@ -925,13 +1088,28 @@ static void createModuleDBI(pdb::PDBFileBuilder &builder, ObjFile *file) { } void PDBLinker::addDebug(TpiSource *source) { - CVIndexMap localMap; - const CVIndexMap *indexMap = mergeTypeRecords(source, &localMap); + // Before we can process symbol substreams from .debug$S, we need to process + // type information, file checksums, and the string table. Add type info to + // the PDB first, so that we can get the map from object file type and item + // indices to PDB type and item indices. If we are using ghashes, types have + // already been merged. + if (!config->debugGHashes) { + ScopedTimer t(typeMergingTimer); + if (Error e = source->mergeDebugT(&tMerger)) { + // If type merging failed, ignore the symbols. + warnUnusable(source->file, std::move(e)); + return; + } + } - if (source->kind == TpiSource::PDB) - return; // No symbols in TypeServer PDBs + // If type merging failed, ignore the symbols. + Error typeError = std::move(source->typeMergingError); + if (typeError) { + warnUnusable(source->file, std::move(typeError)); + return; + } - addDebugSymbols(source->file, indexMap); + addDebugSymbols(source); } static pdb::BulkPublic createPublic(Defined *def) { @@ -961,38 +1139,41 @@ void PDBLinker::addObjectsToPDB() { ScopedTimer t1(addObjectsTimer); // Create module descriptors - for_each(ObjFile::instances, - [&](ObjFile *obj) { createModuleDBI(builder, obj); }); + for_each(ObjFile::instances, [&](ObjFile *obj) { createModuleDBI(obj); }); - // Merge OBJs that do not have debug types - for_each(ObjFile::instances, [&](ObjFile *obj) { - if (obj->debugTypesObj) - return; - // Even if there're no types, still merge non-symbol .Debug$S and .Debug$F - // sections - addDebugSymbols(obj, nullptr); - }); + // Reorder dependency type sources to come first. + TpiSource::sortDependencies(); - // Merge dependencies - TpiSource::forEachSource([&](TpiSource *source) { - if (source->isDependency()) - addDebug(source); - }); + // Merge type information from input files using global type hashing. + if (config->debugGHashes) + tMerger.mergeTypesWithGHash(); - // Merge regular and dependent OBJs - TpiSource::forEachSource([&](TpiSource *source) { - if (!source->isDependency()) - addDebug(source); - }); + // Merge dependencies and then regular objects. + for_each(TpiSource::dependencySources, + [&](TpiSource *source) { addDebug(source); }); + for_each(TpiSource::objectSources, + [&](TpiSource *source) { addDebug(source); }); builder.getStringTableBuilder().setStrings(pdbStrTab); t1.stop(); // Construct TPI and IPI stream contents. ScopedTimer t2(tpiStreamLayoutTimer); - addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); - addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); + // Collect all the merged types. + if (config->debugGHashes) { + addGHashTypeInfo(builder); + } else { + addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); + addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); + } t2.stop(); + + if (config->showSummary) { + for_each(TpiSource::instances, [&](TpiSource *source) { + nbTypeRecords += source->nbTypeRecords; + nbTypeRecordsBytes += source->nbTypeRecordsBytes; + }); + } } void PDBLinker::addPublicsToPDB() { @@ -1004,8 +1185,25 @@ void PDBLinker::addPublicsToPDB() { // 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); - if (def && def->isLive() && def->getChunk()) + if (def && def->isLive() && def->getChunk()) { + // Don't emit a public symbol for coverage data symbols. LLVM code + // coverage (and PGO) create a __profd_ and __profc_ symbol for every + // function. C++ mangled names are long, and tend to dominate symbol size. + // Including these names triples the size of the public stream, which + // results in bloated PDB files. These symbols generally are not helpful + // for debugging, so suppress them. + StringRef name = def->getName(); + if (name.data()[0] == '_' && name.data()[1] == '_') { + // Drop the '_' prefix for x86. + if (config->machine == I386) + name = name.drop_front(1); + if (name.startswith("__profd_") || name.startswith("__profc_") || + name.startswith("__covrec_")) { + return; + } + } publics.push_back(createPublic(def)); + } }); if (!publics.empty()) { @@ -1032,8 +1230,10 @@ void PDBLinker::printStats() { "Input OBJ files (expanded from all cmd-line inputs)"); print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies"); print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies"); - print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(), - "Merged TPI records"); + print(nbTypeRecords, "Input type records"); + print(nbTypeRecordsBytes, "Input type records bytes"); + print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records"); + print(builder.getIpiBuilder().getRecordCount(), "Merged IPI records"); print(pdbStrTab.size(), "Output PDB strings"); print(globalSymbols, "Global symbol records"); print(moduleSymbols, "Module symbol records"); @@ -1085,8 +1285,11 @@ void PDBLinker::printStats() { } }; - printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable()); - printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable()); + if (!config->debugGHashes) { + // FIXME: Reimplement for ghash. + printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable()); + printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable()); + } message(buffer); } @@ -1099,7 +1302,14 @@ void PDBLinker::addNatvisFiles() { warn("Cannot open input file: " + file); continue; } - builder.addInjectedSource(file, std::move(*dataOrErr)); + 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()); + + builder.addInjectedSource(file, std::move(data)); } } @@ -1112,7 +1322,9 @@ void PDBLinker::addNamedStreams() { warn("Cannot open input file: " + file); continue; } - exitOnErr(builder.addNamedStream(stream, (*dataOrErr)->getBuffer())); + std::unique_ptr data = std::move(*dataOrErr); + exitOnErr(builder.addNamedStream(stream, data->getBuffer())); + driver->takeBuffer(std::move(data)); } } @@ -1336,16 +1548,18 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( cs, bAlloc, CodeViewContainer::Pdb)); - SmallVector scopes; CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol( ts, bAlloc, CodeViewContainer::Pdb); - scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym); + + // Write ptrEnd for the S_THUNK32. + ScopeRecord *thunkSymScope = + getSymbolScopeFields(const_cast(newSym.data().data())); mod->addSymbol(newSym); newSym = codeview::SymbolSerializer::writeOneSymbol(es, bAlloc, CodeViewContainer::Pdb); - scopeStackClose(scopes, mod->getNextSymbolOffset(), file); + thunkSymScope->ptrEnd = mod->getNextSymbolOffset(); mod->addSymbol(newSym); @@ -1451,9 +1665,13 @@ void PDBLinker::addSections(ArrayRef outputSections, } void PDBLinker::commit(codeview::GUID *guid) { - ExitOnError exitOnErr((config->pdbPath + ": ").str()); - // Write to a file. - exitOnErr(builder.commit(config->pdbPath, 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)) { + checkError(std::move(e)); + error("failed to write PDB file " + Twine(config->pdbPath)); + } } static uint32_t getSecrelReloc() { diff --git a/gnu/llvm/lld/COFF/PDB.h b/gnu/llvm/lld/COFF/PDB.h index 273609ea788..53506d40bae 100644 --- a/gnu/llvm/lld/COFF/PDB.h +++ b/gnu/llvm/lld/COFF/PDB.h @@ -20,6 +20,8 @@ union DebugInfo; } namespace lld { +class Timer; + namespace coff { class OutputSection; class SectionChunk; @@ -32,6 +34,10 @@ void createPDB(SymbolTable *symtab, llvm::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 173e32f628e..536f3435072 100644 --- a/gnu/llvm/lld/COFF/SymbolTable.cpp +++ b/gnu/llvm/lld/COFF/SymbolTable.cpp @@ -28,6 +28,12 @@ using namespace llvm; namespace lld { namespace coff { +StringRef ltrim1(StringRef s, const char *chars) { + if (!s.empty() && strchr(chars, s[0])) + return s.substr(1); + return s; +} + static Timer ltoTimer("LTO", Timer::root()); SymbolTable *symtab; @@ -75,6 +81,11 @@ static void forceLazy(Symbol *s) { case Symbol::Kind::LazyObjectKind: cast(s)->file->fetch(); break; + case Symbol::Kind::LazyDLLSymbolKind: { + auto *l = cast(s); + l->file->makeImport(l->sym); + break; + } default: llvm_unreachable( "symbol passed to forceLazy is not a LazyArchive or LazyObject"); @@ -244,7 +255,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { errorOrWarn(os.str()); } -void SymbolTable::loadMinGWAutomaticImports() { +void SymbolTable::loadMinGWSymbols() { for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); @@ -255,17 +266,50 @@ void SymbolTable::loadMinGWAutomaticImports() { StringRef name = undef->getName(); - if (name.startswith("__imp_")) - continue; - // If we have an undefined symbol, but we have a lazy symbol we could - // load, load it. - Symbol *l = find(("__imp_" + name).str()); - if (!l || l->pendingArchiveLoad || !l->isLazy()) - continue; + if (config->machine == I386 && config->stdcallFixup) { + // Check if we can resolve an undefined decorated symbol by finding + // the indended target as an undecorated symbol (only with a leading + // underscore). + StringRef origName = name; + StringRef baseName = name; + // Trim down stdcall/fastcall/vectorcall symbols to the base name. + baseName = ltrim1(baseName, "_@"); + baseName = baseName.substr(0, baseName.find('@')); + // Add a leading underscore, as it would be in cdecl form. + std::string newName = ("_" + baseName).str(); + Symbol *l; + if (newName != origName && (l = find(newName)) != nullptr) { + // If we found a symbol and it is lazy; load it. + if (l->isLazy() && !l->pendingArchiveLoad) { + log("Loading lazy " + l->getName() + " from " + + l->getFile()->getName() + " for stdcall fixup"); + forceLazy(l); + } + // If it's lazy or already defined, hook it up as weak alias. + if (l->isLazy() || isa(l)) { + if (config->warnStdcallFixup) + warn("Resolving " + origName + " by linking to " + newName); + else + log("Resolving " + origName + " by linking to " + newName); + undef->weakAlias = l; + continue; + } + } + } + + if (config->autoImport) { + if (name.startswith("__imp_")) + continue; + // If we have an undefined symbol, but we have a lazy symbol we could + // load, load it. + Symbol *l = find(("__imp_" + name).str()); + if (!l || l->pendingArchiveLoad || !l->isLazy()) + continue; - log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + - " for automatic import"); - forceLazy(l); + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + + " for automatic import"); + forceLazy(l); + } } } @@ -390,7 +434,7 @@ void SymbolTable::reportUnresolvable() { for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); - if (!undef) + if (!undef || sym->deferUndefined) continue; if (undef->getWeakAlias()) continue; @@ -402,7 +446,7 @@ void SymbolTable::reportUnresolvable() { } if (name.contains("_PchSym_")) continue; - if (config->mingw && impSymbol(name)) + if (config->autoImport && impSymbol(name)) continue; undefs.insert(sym); } @@ -482,6 +526,7 @@ std::pair SymbolTable::insert(StringRef name) { sym = reinterpret_cast(make()); sym->isUsedInRegularObj = false; sym->pendingArchiveLoad = false; + sym->canInline = true; inserted = true; } return {sym, inserted}; @@ -539,6 +584,22 @@ void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { f->fetch(); } +void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, + StringRef n) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n); + if (wasInserted) { + replaceSymbol(s, f, sym, n); + return; + } + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) + return; + s->pendingArchiveLoad = true; + f->makeImport(sym); +} + static std::string getSourceLocationBitcode(BitcodeFile *file) { std::string res("\n>>> defined at "); StringRef source = file->obj->getSourceFileName(); diff --git a/gnu/llvm/lld/COFF/SymbolTable.h b/gnu/llvm/lld/COFF/SymbolTable.h index 870a7151fa8..e88002c8831 100644 --- a/gnu/llvm/lld/COFF/SymbolTable.h +++ b/gnu/llvm/lld/COFF/SymbolTable.h @@ -57,7 +57,9 @@ public: // symbols and warn about imported local symbols. void resolveRemainingUndefines(); - void loadMinGWAutomaticImports(); + // Load lazy objects that are needed for MinGW automatic import and for + // doing stdcall fixups. + void loadMinGWSymbols(); bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); // Returns a list of chunks of selected symbols. @@ -87,6 +89,7 @@ public: Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); void addLazyObject(LazyObjFile *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, @@ -134,6 +137,8 @@ extern SymbolTable *symtab; std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); +StringRef ltrim1(StringRef s, const char *chars); + } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/Symbols.cpp b/gnu/llvm/lld/COFF/Symbols.cpp index 60ff72aeb52..8a6a9b27d45 100644 --- a/gnu/llvm/lld/COFF/Symbols.cpp +++ b/gnu/llvm/lld/COFF/Symbols.cpp @@ -70,6 +70,8 @@ InputFile *Symbol::getFile() { return sym->file; if (auto *sym = dyn_cast(this)) return sym->file; + if (auto *sym = dyn_cast(this)) + return sym->file; return nullptr; } diff --git a/gnu/llvm/lld/COFF/Symbols.h b/gnu/llvm/lld/COFF/Symbols.h index 1da4df36696..bb911171b1f 100644 --- a/gnu/llvm/lld/COFF/Symbols.h +++ b/gnu/llvm/lld/COFF/Symbols.h @@ -61,6 +61,7 @@ public: UndefinedKind, LazyArchiveKind, LazyObjectKind, + LazyDLLSymbolKind, LastDefinedCOFFKind = DefinedCommonKind, LastDefinedKind = DefinedSyntheticKind, @@ -92,7 +93,8 @@ public: bool isLive() const; bool isLazy() const { - return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind || + symbolKind == LazyDLLSymbolKind; } private: @@ -103,8 +105,8 @@ protected: explicit Symbol(Kind k, StringRef n = "") : symbolKind(k), isExternal(true), isCOMDAT(false), writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false), - isRuntimePseudoReloc(false), nameSize(n.size()), - nameData(n.empty() ? nullptr : n.data()) {} + isRuntimePseudoReloc(false), deferUndefined(false), canInline(true), + nameSize(n.size()), nameData(n.empty() ? nullptr : n.data()) {} const unsigned symbolKind : 8; unsigned isExternal : 1; @@ -130,6 +132,16 @@ public: unsigned isRuntimePseudoReloc : 1; + // True if we want to allow this symbol to be undefined in the early + // undefined check pass in SymbolTable::reportUnresolvable(), as it + // might be fixed up later. + unsigned deferUndefined : 1; + + // False if LTO shouldn't inline whatever this symbol points to. If a symbol + // is overwritten after LTO, LTO shouldn't inline the symbol because it + // doesn't know the final contents of the symbol. + unsigned canInline : 1; + protected: // Symbol name length. Assume symbol lengths fit in a 32-bit integer. uint32_t nameSize; @@ -299,6 +311,19 @@ public: LazyObjFile *file; }; +// MinGW only. +class LazyDLLSymbol : public Symbol { +public: + LazyDLLSymbol(DLLFile *f, DLLFile::Symbol *s, StringRef n) + : Symbol(LazyDLLSymbolKind, n), file(f), sym(s) {} + static bool classof(const Symbol *s) { + return s->kind() == LazyDLLSymbolKind; + } + + DLLFile *file; + DLLFile::Symbol *sym; +}; + // Undefined symbols. class Undefined : public Symbol { public: @@ -343,6 +368,13 @@ public: uint16_t getOrdinal() { return file->hdr->OrdinalHint; } ImportFile *file; + + // This is a pointer to the synthetic symbol associated with the load thunk + // for this symbol that will be called if the DLL is delay-loaded. This is + // needed for Control Flow Guard because if this DefinedImportData symbol is a + // valid call target, the corresponding load thunk must also be marked as a + // valid call target. + DefinedSynthetic *loadThunkSym = nullptr; }; // This class represents a symbol for a jump table entry which jumps @@ -406,6 +438,7 @@ inline uint64_t Defined::getRVA() { return cast(this)->getRVA(); case LazyArchiveKind: case LazyObjectKind: + case LazyDLLSymbolKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); } @@ -430,6 +463,7 @@ inline Chunk *Defined::getChunk() { return cast(this)->getChunk(); case LazyArchiveKind: case LazyObjectKind: + case LazyDLLSymbolKind: case UndefinedKind: llvm_unreachable("Cannot get the chunk of an undefined symbol."); } @@ -450,6 +484,7 @@ union SymbolUnion { alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; alignas(LazyObject) char j[sizeof(LazyObject)]; + alignas(LazyDLLSymbol) char k[sizeof(LazyDLLSymbol)]; }; template @@ -461,7 +496,9 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) { "SymbolUnion not aligned enough"); assert(static_cast(static_cast(nullptr)) == nullptr && "Not a Symbol"); + bool canInline = s->canInline; new (s) T(std::forward(arg)...); + s->canInline = canInline; } } // namespace coff diff --git a/gnu/llvm/lld/COFF/TypeMerger.h b/gnu/llvm/lld/COFF/TypeMerger.h index 858f55b6856..72fd5fc72b0 100644 --- a/gnu/llvm/lld/COFF/TypeMerger.h +++ b/gnu/llvm/lld/COFF/TypeMerger.h @@ -10,60 +10,57 @@ #define LLD_COFF_TYPEMERGER_H #include "Config.h" -#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" #include "llvm/Support/Allocator.h" +#include namespace lld { namespace coff { +using llvm::codeview::GloballyHashedType; +using llvm::codeview::TypeIndex; + +struct GHashState; + class TypeMerger { public: - TypeMerger(llvm::BumpPtrAllocator &alloc) - : typeTable(alloc), idTable(alloc), globalTypeTable(alloc), - globalIDTable(alloc) {} + TypeMerger(llvm::BumpPtrAllocator &alloc); + + ~TypeMerger(); /// Get the type table or the global type table if /DEBUG:GHASH is enabled. inline llvm::codeview::TypeCollection &getTypeTable() { - if (config->debugGHashes) - return globalTypeTable; + assert(!config->debugGHashes); return typeTable; } /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. inline llvm::codeview::TypeCollection &getIDTable() { - if (config->debugGHashes) - return globalIDTable; + assert(!config->debugGHashes); return idTable; } + /// Use global hashes to eliminate duplicate types and identify unique type + /// indices in each TpiSource. + void mergeTypesWithGHash(); + + /// Map from PDB function id type indexes to PDB function type indexes. + /// Populated after mergeTypesWithGHash. + llvm::DenseMap funcIdToType; + /// Type records that will go into the PDB TPI stream. llvm::codeview::MergingTypeTableBuilder typeTable; /// Item records that will go into the PDB IPI stream. llvm::codeview::MergingTypeTableBuilder idTable; - /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH) - llvm::codeview::GlobalTypeTableBuilder globalTypeTable; - - /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH) - llvm::codeview::GlobalTypeTableBuilder globalIDTable; - // When showSummary is enabled, these are histograms of TPI and IPI records // keyed by type index. SmallVector tpiCounts; SmallVector ipiCounts; }; -/// Map from type index and item index in a type server PDB to the -/// corresponding index in the destination PDB. -struct CVIndexMap { - llvm::SmallVector tpiMap; - llvm::SmallVector ipiMap; - bool isTypeServerMap = false; - bool isPrecompiledTypeMap = false; -}; - } // namespace coff } // namespace lld diff --git a/gnu/llvm/lld/COFF/Writer.cpp b/gnu/llvm/lld/COFF/Writer.cpp index 0188f0971a7..37cbe2bb96a 100644 --- a/gnu/llvm/lld/COFF/Writer.cpp +++ b/gnu/llvm/lld/COFF/Writer.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Writer.h" +#include "CallGraphSort.h" #include "Config.h" #include "DLL.h" #include "InputFiles.h" @@ -87,6 +88,8 @@ OutputSection *Chunk::getOutputSection() const { return osidx == 0 ? nullptr : outputSections[osidx - 1]; } +void OutputSection::clear() { outputSections.clear(); } + namespace { class DebugDirectoryChunk : public NonSectionChunk { @@ -224,16 +227,21 @@ private: void markSymbolsForRVATable(ObjFile *file, ArrayRef symIdxChunks, SymbolRVASet &tableSymbols); + void getSymbolsFromSections(ObjFile *file, + ArrayRef symIdxChunks, + std::vector &symbols); void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, - StringRef countSym); + StringRef countSym, bool hasFlag=false); void setSectionPermissions(); void writeSections(); void writeBuildId(); + void sortSections(); void sortExceptionTable(); void sortCRTSectionChunks(std::vector &chunks); void addSyntheticIdata(); 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); @@ -260,6 +268,7 @@ private: DelayLoadContents delayIdata; EdataContents edata; bool setNoSEHCharacteristic = false; + uint32_t tlsAlignment = 0; DebugDirectoryChunk *debugDirectory = nullptr; std::vector> debugRecords; @@ -586,6 +595,7 @@ void Writer::finalizeAddresses() { // If the verification above thought we needed thunks, we should have // added some. assert(addressesChanged); + (void)addressesChanged; // Recalculate the layout for the whole image (and verify the ranges at // the start of the next round). @@ -599,13 +609,11 @@ void Writer::finalizeAddresses() { void Writer::run() { ScopedTimer t1(codeLayoutTimer); - // First, clear the output sections from previous runs - outputSections.clear(); - createImportTables(); createSections(); - createMiscChunks(); appendImportThunks(); + // Import thunks must be added before the Control Flow Guard tables are added. + createMiscChunks(); createExportTable(); mergeSections(); removeUnusedSections(); @@ -628,6 +636,11 @@ void Writer::run() { writeSections(); sortExceptionTable(); + // Fix up the alignment in the TLS Directory's characteristic field, + // if a specific alignment value is needed + if (tlsAlignment) + fixTlsAlignment(); + t1.stop(); if (!config->pdbPath.empty() && config->debug) { @@ -801,6 +814,19 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { name.startswith(".xdata$") || name.startswith(".eh_frame$"); } +void Writer::sortSections() { + if (!config->callGraphProfile.empty()) { + DenseMap order = computeCallGraphProfileOrder(); + for (auto it : order) { + if (DefinedRegular *sym = it.first->sym) + config->order[sym->getName()] = it.second; + } + } + if (!config->order.empty()) + for (auto it : partialSections) + sortBySectionOrder(it.second->chunks); +} + // Create output section objects and add them to OutputSections. void Writer::createSections() { // First, create the builtin sections. @@ -848,6 +874,10 @@ void Writer::createSections() { StringRef name = c->getSectionName(); if (shouldStripSectionSuffix(sc, name)) name = name.split('$').first; + + if (name.startswith(".tls")) + tlsAlignment = std::max(tlsAlignment, c->getAlignment()); + PartialSection *pSec = createPartialSection(name, c->getOutputCharacteristics()); pSec->chunks.push_back(c); @@ -864,10 +894,7 @@ void Writer::createSections() { if (hasIdata) addSyntheticIdata(); - // Process an /order option. - if (!config->order.empty()) - for (auto it : partialSections) - sortBySectionOrder(it.second->chunks); + sortSections(); if (hasIdata) locateImportTables(); @@ -952,16 +979,15 @@ void Writer::createMiscChunks() { } if (config->cetCompat) { - ExtendedDllCharacteristicsChunk *extendedDllChars = - make( - IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT); - debugRecords.push_back( - {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars}); + debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, + make( + IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT)}); } - if (debugRecords.size() > 0) { - for (std::pair r : debugRecords) - debugInfoSec->addChunk(r.second); + // Align and add each chunk referenced by the debug data directory. + for (std::pair r : debugRecords) { + r.second->setAlignment(4); + debugInfoSec->addChunk(r.second); } // Create SEH table. x86-only. @@ -1053,6 +1079,10 @@ void Writer::createExportTable() { edataStart = edataSec->chunks.front(); edataEnd = edataSec->chunks.back(); } + // Warn on exported deleting destructor. + for (auto e : config->exports) + if (e.sym && e.sym->getName().startswith("??_G")) + warn("export of deleting dtor: " + toString(*e.sym)); } void Writer::removeUnusedSections() { @@ -1362,8 +1392,8 @@ template void Writer::writeHeader() { pe->MinorImageVersion = config->minorImageVersion; pe->MajorOperatingSystemVersion = config->majorOSVersion; pe->MinorOperatingSystemVersion = config->minorOSVersion; - pe->MajorSubsystemVersion = config->majorOSVersion; - pe->MinorSubsystemVersion = config->minorOSVersion; + pe->MajorSubsystemVersion = config->majorSubsystemVersion; + pe->MinorSubsystemVersion = config->minorSubsystemVersion; pe->Subsystem = config->subsystem; pe->SizeOfImage = sizeOfImage; pe->SizeOfHeaders = sizeOfHeaders; @@ -1553,6 +1583,7 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, break; case Symbol::LazyArchiveKind: case Symbol::LazyObjectKind: + case Symbol::LazyDLLSymbolKind: case Symbol::UndefinedKind: // Undefined symbols resolve to zero, so they don't have an RVA. Lazy // symbols shouldn't have relocations. @@ -1607,16 +1638,23 @@ static void markSymbolsWithRelocations(ObjFile *file, // table. void Writer::createGuardCFTables() { SymbolRVASet addressTakenSyms; + SymbolRVASet giatsRVASet; + std::vector giatsSymbols; SymbolRVASet longJmpTargets; + SymbolRVASet ehContTargets; for (ObjFile *file : ObjFile::instances) { // If the object was compiled with /guard:cf, the address taken symbols - // are in .gfids$y sections, and the longjmp targets are in .gljmp$y - // sections. If the object was not compiled with /guard:cf, we assume there - // were no setjmp targets, and that all code symbols with relocations are - // possibly address-taken. + // 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 + // compiled with /guard:cf, we assume there were no setjmp and ehcont + // targets, and that all code symbols with relocations are possibly + // address-taken. if (file->hasGuardCF()) { markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); + markSymbolsForRVATable(file, file->getGuardIATChunks(), giatsRVASet); + getSymbolsFromSections(file, file->getGuardIATChunks(), giatsSymbols); markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); + markSymbolsForRVATable(file, file->getGuardEHContChunks(), ehContTargets); } else { markSymbolsWithRelocations(file, addressTakenSyms); } @@ -1630,6 +1668,16 @@ void Writer::createGuardCFTables() { for (Export &e : config->exports) maybeAddAddressTakenFunction(addressTakenSyms, e.sym); + // For each entry in the .giats table, check if it has a corresponding load + // thunk (e.g. because the DLL that defines it will be delay-loaded) and, if + // so, add the load thunk to the address taken (.gfids) table. + for (Symbol *s : giatsSymbols) { + if (auto *di = dyn_cast(s)) { + if (di->loadThunkSym) + addSymbolToRVASet(addressTakenSyms, di->loadThunkSym); + } + } + // Ensure sections referenced in the gfid table are 16-byte aligned. for (const ChunkAndOffset &c : addressTakenSyms) if (c.inputChunk->getAlignment() < 16) @@ -1638,27 +1686,38 @@ void Writer::createGuardCFTables() { maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table", "__guard_fids_count"); + // Add the Guard Address Taken IAT Entry Table (.giats). + maybeAddRVATable(std::move(giatsRVASet), "__guard_iat_table", + "__guard_iat_count"); + // Add the longjmp target table unless the user told us not to. - if (config->guardCF == GuardCFLevel::Full) + if (config->guardCF & GuardCFLevel::LongJmp) maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); + // Add the ehcont target table unless the user told us not to. + if (config->guardCF & GuardCFLevel::EHCont) + maybeAddRVATable(std::move(ehContTargets), "__guard_eh_cont_table", + "__guard_eh_cont_count", true); + // Set __guard_flags, which will be used in the load config to indicate that // /guard:cf was enabled. uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | uint32_t(coff_guard_flags::HasFidTable); - if (config->guardCF == GuardCFLevel::Full) + if (config->guardCF & GuardCFLevel::LongJmp) guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + if (config->guardCF & GuardCFLevel::EHCont) + guardFlags |= uint32_t(coff_guard_flags::HasEHContTable); Symbol *flagSym = symtab->findUnderscore("__guard_flags"); cast(flagSym)->setVA(guardFlags); } // Take a list of input sections containing symbol table indices and add those -// symbols to an RVA table. The challenge is that symbol RVAs are not known and +// symbols to a vector. The challenge is that symbol RVAs are not known and // depend on the table size, so we can't directly build a set of integers. -void Writer::markSymbolsForRVATable(ObjFile *file, +void Writer::getSymbolsFromSections(ObjFile *file, ArrayRef symIdxChunks, - SymbolRVASet &tableSymbols) { + std::vector &symbols) { for (SectionChunk *c : symIdxChunks) { // Skip sections discarded by linker GC. This comes up when a .gfids section // is associated with something like a vtable and the vtable is discarded. @@ -1676,7 +1735,7 @@ void Writer::markSymbolsForRVATable(ObjFile *file, } // Read each symbol table index and check if that symbol was included in the - // final link. If so, add it to the table symbol set. + // final link. If so, add it to the vector of symbols. ArrayRef symIndices( reinterpret_cast(data.data()), data.size() / 4); ArrayRef objSymbols = file->getSymbols(); @@ -1688,27 +1747,43 @@ void Writer::markSymbolsForRVATable(ObjFile *file, } if (Symbol *s = objSymbols[symIndex]) { if (s->isLive()) - addSymbolToRVASet(tableSymbols, cast(s)); + symbols.push_back(cast(s)); } } } } +// Take a list of input sections containing symbol table indices and add those +// symbols to an RVA table. +void Writer::markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols) { + std::vector syms; + getSymbolsFromSections(file, symIdxChunks, syms); + + for (Symbol *s : syms) + addSymbolToRVASet(tableSymbols, cast(s)); +} + // Replace the absolute table symbol with a synthetic symbol pointing to // tableChunk so that we can emit base relocations for it and resolve section // relative relocations. void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, - StringRef countSym) { + StringRef countSym, bool hasFlag) { if (tableSymbols.empty()) return; - RVATableChunk *tableChunk = make(std::move(tableSymbols)); + NonSectionChunk *tableChunk; + if (hasFlag) + tableChunk = make(std::move(tableSymbols)); + else + tableChunk = make(std::move(tableSymbols)); rdataSec->addChunk(tableChunk); Symbol *t = symtab->findUnderscore(tableSym); Symbol *c = symtab->findUnderscore(countSym); replaceSymbol(t, t->getName(), tableChunk); - cast(c)->setVA(tableChunk->getSize() / 4); + cast(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4)); } // MinGW specific. Gather all relocations that are imported from a DLL even @@ -1993,3 +2068,33 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { return it->second; return nullptr; } + +void Writer::fixTlsAlignment() { + Defined *tlsSym = + dyn_cast_or_null(symtab->findUnderscore("_tls_used")); + if (!tlsSym) + return; + + OutputSection *sec = tlsSym->getChunk()->getOutputSection(); + 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() + ? sizeof(object::coff_tls_directory64) + : sizeof(object::coff_tls_directory32); + + if (tlsOffset + directorySize > sec->getRawSize()) + fatal("_tls_used sym is malformed"); + + if (config->is64()) { + object::coff_tls_directory64 *tlsDir = + reinterpret_cast(&secBuf[tlsOffset]); + tlsDir->setAlignment(tlsAlignment); + } else { + object::coff_tls_directory32 *tlsDir = + reinterpret_cast(&secBuf[tlsOffset]); + tlsDir->setAlignment(tlsAlignment); + } +} diff --git a/gnu/llvm/lld/COFF/Writer.h b/gnu/llvm/lld/COFF/Writer.h index 96389df2ac0..2bb26da7d42 100644 --- a/gnu/llvm/lld/COFF/Writer.h +++ b/gnu/llvm/lld/COFF/Writer.h @@ -50,6 +50,9 @@ public: void writeHeaderTo(uint8_t *buf); 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 diff --git a/gnu/llvm/lld/Common/Args.cpp b/gnu/llvm/lld/Common/Args.cpp index 4ea3a435c7a..fe7300dd597 100644 --- a/gnu/llvm/lld/Common/Args.cpp +++ b/gnu/llvm/lld/Common/Args.cpp @@ -26,14 +26,17 @@ CodeGenOpt::Level lld::args::getCGOptLevel(int optLevelLTO) { return CodeGenOpt::Default; } -int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key, - int64_t Default) { +static int64_t getInteger(opt::InputArgList &args, unsigned key, + int64_t Default, unsigned base) { auto *a = args.getLastArg(key); if (!a) return Default; int64_t v; - if (to_integer(a->getValue(), v, 10)) + StringRef s = a->getValue(); + if (base == 16 && (s.startswith("0x") || s.startswith("0X"))) + s = s.drop_front(2); + if (to_integer(s, v, base)) return v; StringRef spelling = args.getArgString(a->getIndex()); @@ -41,6 +44,16 @@ int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key, return 0; } +int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key, + int64_t Default) { + return ::getInteger(args, key, Default, 10); +} + +int64_t lld::args::getHex(opt::InputArgList &args, unsigned key, + int64_t Default) { + return ::getInteger(args, key, Default, 16); +} + std::vector lld::args::getStrings(opt::InputArgList &args, int id) { std::vector v; for (auto *arg : args.filtered(id)) @@ -76,7 +89,7 @@ std::vector lld::args::getLines(MemoryBufferRef mb) { } StringRef lld::args::getFilenameWithoutExe(StringRef path) { - if (path.endswith_lower(".exe")) + if (path.endswith_insensitive(".exe")) return sys::path::stem(path); return sys::path::filename(path); } diff --git a/gnu/llvm/lld/Common/CMakeLists.txt b/gnu/llvm/lld/Common/CMakeLists.txt index 41eb58c1519..0437d5afb8e 100644 --- a/gnu/llvm/lld/Common/CMakeLists.txt +++ b/gnu/llvm/lld/Common/CMakeLists.txt @@ -1,7 +1,3 @@ -if(NOT LLD_BUILT_STANDALONE) - set(tablegen_deps intrinsics_gen) -endif() - set(LLD_SYSTEM_LIBS ${LLVM_PTHREAD_LIB}) if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) @@ -30,9 +26,6 @@ set_source_files_properties("${version_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) -set_property(SOURCE Version.cpp APPEND PROPERTY - COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC") - add_lld_library(lldCommon Args.cpp DWARF.cpp @@ -63,5 +56,5 @@ add_lld_library(lldCommon ${LLD_SYSTEM_LIBS} DEPENDS - ${tablegen_deps} + intrinsics_gen ) diff --git a/gnu/llvm/lld/Common/ErrorHandler.cpp b/gnu/llvm/lld/Common/ErrorHandler.cpp index 94ff23173d6..269a0f62ec6 100644 --- a/gnu/llvm/lld/Common/ErrorHandler.cpp +++ b/gnu/llvm/lld/Common/ErrorHandler.cpp @@ -13,15 +13,14 @@ #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include #include -#if !defined(_MSC_VER) && !defined(__MINGW32__) -#include -#endif - using namespace llvm; using namespace lld; @@ -43,31 +42,48 @@ static StringRef getSeparator(const Twine &msg) { raw_ostream *lld::stdoutOS; raw_ostream *lld::stderrOS; -raw_ostream &lld::outs() { return stdoutOS ? *stdoutOS : llvm::outs(); } -raw_ostream &lld::errs() { return stderrOS ? *stderrOS : llvm::errs(); } - ErrorHandler &lld::errorHandler() { static ErrorHandler handler; return handler; } +raw_ostream &lld::outs() { + if (errorHandler().disableOutput) + return llvm::nulls(); + return stdoutOS ? *stdoutOS : llvm::outs(); +} + +raw_ostream &lld::errs() { + if (errorHandler().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(); + // Re-throw a possible signal or exception once/if it was catched by + // safeLldMain(). + CrashRecoveryContext::throwIfCrash(val); + // Dealloc/destroy ManagedStatic variables before calling _exit(). // In an LTO build, allows us to get the output of -time-passes. // Ensures that the thread pool for the parallel algorithms is stopped to // avoid intermittent crashes on Windows when exiting. - llvm_shutdown(); + if (!CrashRecoveryContext::GetCurrent()) + llvm_shutdown(); { std::lock_guard lock(mu); lld::outs().flush(); lld::errs().flush(); } - _exit(val); + // 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. + llvm::sys::Process::Exit(val, /*NoCleanup=*/true); } void lld::diagnosticHandler(const DiagnosticInfo &di) { @@ -153,13 +169,15 @@ std::string ErrorHandler::getLocation(const Twine &msg) { } void ErrorHandler::log(const Twine &msg) { - if (!verbose) + if (!verbose || disableOutput) return; std::lock_guard lock(mu); lld::errs() << logName << ": " << msg << "\n"; } void ErrorHandler::message(const Twine &msg) { + if (disableOutput) + return; std::lock_guard lock(mu); lld::outs() << msg << "\n"; lld::outs().flush(); @@ -216,6 +234,51 @@ void ErrorHandler::error(const Twine &msg) { exitLld(1); } +void ErrorHandler::error(const Twine &msg, ErrorTag tag, + ArrayRef args) { + if (errorHandlingScript.empty()) { + error(msg); + return; + } + SmallVector scriptArgs; + scriptArgs.push_back(errorHandlingScript); + switch (tag) { + case ErrorTag::LibNotFound: + scriptArgs.push_back("missing-lib"); + break; + case ErrorTag::SymbolNotFound: + scriptArgs.push_back("undefined-symbol"); + break; + } + scriptArgs.insert(scriptArgs.end(), args.begin(), args.end()); + int res = llvm::sys::ExecuteAndWait(errorHandlingScript, scriptArgs); + if (res == 0) { + return error(msg); + } else { + // Temporarily disable error limit to make sure the two calls to error(...) + // only count as one. + uint64_t currentErrorLimit = errorLimit; + errorLimit = 0; + error(msg); + errorLimit = currentErrorLimit; + --errorCount; + + switch (res) { + case -1: + error("error handling script '" + errorHandlingScript + + "' failed to execute"); + break; + case -2: + error("error handling script '" + errorHandlingScript + + "' crashed or timeout"); + break; + default: + error("error handling script '" + errorHandlingScript + + "' exited with code " + Twine(res)); + } + } +} + void ErrorHandler::fatal(const Twine &msg) { error(msg); exitLld(1); diff --git a/gnu/llvm/lld/Common/Reproduce.cpp b/gnu/llvm/lld/Common/Reproduce.cpp index 00309f58b93..017f53df05a 100644 --- a/gnu/llvm/lld/Common/Reproduce.cpp +++ b/gnu/llvm/lld/Common/Reproduce.cpp @@ -54,7 +54,12 @@ std::string lld::toString(const opt::Arg &arg) { std::string k = std::string(arg.getSpelling()); if (arg.getNumValues() == 0) return k; - std::string v = quote(arg.getValue()); + std::string v; + for (size_t i = 0; i < arg.getNumValues(); ++i) { + if (i > 0) + v.push_back(' '); + v += quote(arg.getValue(i)); + } if (arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle) return k + v; return k + " " + v; diff --git a/gnu/llvm/lld/Common/Strings.cpp b/gnu/llvm/lld/Common/Strings.cpp index 17c2c207491..7bf336490da 100644 --- a/gnu/llvm/lld/Common/Strings.cpp +++ b/gnu/llvm/lld/Common/Strings.cpp @@ -21,12 +21,11 @@ using namespace lld; // Returns the demangled C++ symbol name for name. std::string lld::demangleItanium(StringRef name) { - // itaniumDemangle can be used to demangle strings other than symbol - // names which do not necessarily start with "_Z". Name can be - // either a C or C++ symbol. Don't call demangle if the name - // does not look like a C++ symbol name to avoid getting unexpected - // result for a C symbol that happens to match a mangled type name. - if (!name.startswith("_Z")) + // 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)); @@ -77,9 +76,8 @@ std::vector lld::parseHex(StringRef s) { // Returns true if S is valid as a C language identifier. bool lld::isValidCIdentifier(StringRef s) { - return !s.empty() && (isAlpha(s[0]) || s[0] == '_') && - std::all_of(s.begin() + 1, s.end(), - [](char c) { return c == '_' || isAlnum(c); }); + return !s.empty() && !isDigit(s[0]) && + llvm::all_of(s, [](char c) { return isAlnum(c) || c == '_'; }); } // Write the contents of the a buffer to a file diff --git a/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp b/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp index 9b166a3e130..d39477ed89a 100644 --- a/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp +++ b/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp @@ -14,7 +14,7 @@ static llvm::codegen::RegisterCodeGenFlags CGF; llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { - return llvm::codegen::InitTargetOptionsFromCodeGenFlags(); + return llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); } llvm::Optional lld::getRelocModelFromCMModel() { diff --git a/gnu/llvm/lld/Common/Timer.cpp b/gnu/llvm/lld/Common/Timer.cpp index ea221fd86f3..16c518e4bf8 100644 --- a/gnu/llvm/lld/Common/Timer.cpp +++ b/gnu/llvm/lld/Common/Timer.cpp @@ -45,7 +45,7 @@ void Timer::print() { if (child->total > 0) child->print(1, totalDuration); - message(std::string(49, '-')); + message(std::string(50, '-')); root().print(0, root().millis(), false); } @@ -62,7 +62,7 @@ void Timer::print(int depth, double totalDuration, bool recurse) const { SmallString<32> str; llvm::raw_svector_ostream stream(str); std::string s = std::string(depth * 2, ' ') + name + std::string(":"); - stream << format("%-30s%5d ms (%5.1f%%)", s.c_str(), (int)millis(), p); + stream << format("%-30s%7d ms (%5.1f%%)", s.c_str(), (int)millis(), p); message(str); diff --git a/gnu/llvm/lld/Common/Version.cpp b/gnu/llvm/lld/Common/Version.cpp index cd9fcd4f405..f3768091cd0 100644 --- a/gnu/llvm/lld/Common/Version.cpp +++ b/gnu/llvm/lld/Common/Version.cpp @@ -12,9 +12,7 @@ #include "lld/Common/Version.h" -#ifdef HAVE_VCS_VERSION_INC #include "VCSVersion.inc" -#endif // Returns a version string, e.g.: // lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef) diff --git a/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp b/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp index 724d668449b..b9fd4cdbad6 100644 --- a/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp +++ b/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp @@ -413,9 +413,7 @@ void Patch843419Section::writeTo(uint8_t *buf) { write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); // Apply any relocation transferred from the original patchee section. - // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc - // also adds outSecOff so we need to subtract to avoid double counting. - this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); + relocateAlloc(buf, buf + getSize()); // Return address is the next instruction after the one we have just copied. uint64_t s = getLDSTAddr() + 4; diff --git a/gnu/llvm/lld/ELF/AArch64ErrataFix.h b/gnu/llvm/lld/ELF/AArch64ErrataFix.h index 0548b58751f..dfe57b95dd9 100644 --- a/gnu/llvm/lld/ELF/AArch64ErrataFix.h +++ b/gnu/llvm/lld/ELF/AArch64ErrataFix.h @@ -18,7 +18,7 @@ namespace elf { class Defined; class InputSection; -struct InputSectionDescription; +class InputSectionDescription; class OutputSection; class Patch843419Section; diff --git a/gnu/llvm/lld/ELF/ARMErrataFix.cpp b/gnu/llvm/lld/ELF/ARMErrataFix.cpp index bd6f689b584..77623780ffa 100644 --- a/gnu/llvm/lld/ELF/ARMErrataFix.cpp +++ b/gnu/llvm/lld/ELF/ARMErrataFix.cpp @@ -164,6 +164,15 @@ static uint64_t getThumbDestAddr(uint64_t sourceAddr, uint32_t instr) { offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP24); else offset = target->getImplicitAddend(buf, R_ARM_THM_CALL); + // A BLX instruction from Thumb to Arm may have an address that is + // not 4-byte aligned. As Arm instructions are always 4-byte aligned + // the instruction is calculated (from Arm ARM): + // targetAddress = Align(PC, 4) + imm32 + // where + // Align(x, y) = y * (x Div y) + // which corresponds to alignDown. + if (isBLX(instr)) + sourceAddr = alignDown(sourceAddr, 4); return sourceAddr + offset + 4; } @@ -173,11 +182,9 @@ void Patch657417Section::writeTo(uint8_t *buf) { write32le(buf, 0xea000000); else write32le(buf, 0x9000f000); - // If we have a relocation then apply it. For a SyntheticSection buf already - // has outSecOff added, but relocateAlloc also adds outSecOff so we need to - // subtract to avoid double counting. + // If we have a relocation then apply it. if (!relocations.empty()) { - relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); + relocateAlloc(buf, buf + getSize()); return; } @@ -187,7 +194,11 @@ void Patch657417Section::writeTo(uint8_t *buf) { // We cannot use the instruction in the patchee section as this will have // been altered to point to us! uint64_t s = getThumbDestAddr(getBranchAddr(), instr); - uint64_t p = getVA(4); + // A BLX changes the state of the branch in the patch to Arm state, which + // has a PC Bias of 8, whereas in all other cases the branch is in Thumb + // state with a PC Bias of 4. + uint64_t pcBias = isBLX(instr) ? 8 : 4; + uint64_t p = getVA(pcBias); target->relocateNoSym(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p); } diff --git a/gnu/llvm/lld/ELF/ARMErrataFix.h b/gnu/llvm/lld/ELF/ARMErrataFix.h index 5a39bcc75cd..a93609b35ba 100644 --- a/gnu/llvm/lld/ELF/ARMErrataFix.h +++ b/gnu/llvm/lld/ELF/ARMErrataFix.h @@ -19,7 +19,7 @@ namespace elf { class Defined; class InputSection; -struct InputSectionDescription; +class InputSectionDescription; class OutputSection; class Patch657417Section; diff --git a/gnu/llvm/lld/ELF/Arch/AArch64.cpp b/gnu/llvm/lld/ELF/Arch/AArch64.cpp index 637046e90bb..c1ab0e97efe 100644 --- a/gnu/llvm/lld/ELF/Arch/AArch64.cpp +++ b/gnu/llvm/lld/ELF/Arch/AArch64.cpp @@ -34,6 +34,7 @@ public: RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, @@ -46,8 +47,7 @@ public: bool usesOnlyLowPageBits(RelType type) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; - RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const override; + RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const override; void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, @@ -71,6 +71,7 @@ AArch64::AArch64() { pltEntrySize = 16; ipltEntrySize = 16; defaultMaxPageSize = 65536; + gotBaseSymInGotPlt = false; // Align to the 2 MiB page size (known as a superpage or huge page). // FreeBSD automatically promotes 2 MiB-aligned allocations. @@ -121,7 +122,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_TLSLE_MOVW_TPREL_G1: case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: case R_AARCH64_TLSLE_MOVW_TPREL_G2: - return R_TLS; + return R_TPREL; case R_AARCH64_CALL26: case R_AARCH64_CONDBR19: case R_AARCH64_JUMP26: @@ -147,6 +148,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; + case R_AARCH64_LD64_GOTPAGE_LO15: + return R_AARCH64_GOT_PAGE; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return R_AARCH64_GOT_PAGE_PC; @@ -159,8 +162,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, } } -RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const { +RelExpr AArch64::adjustTlsExpr(RelType type, RelExpr expr) const { if (expr == R_RELAX_TLS_GD_TO_IE) { if (type == R_AARCH64_TLSDESC_ADR_PAGE21) return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; @@ -193,8 +195,19 @@ RelType AArch64::getDynRel(RelType type) const { return R_AARCH64_NONE; } +int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_AARCH64_TLSDESC: + return read64(buf + 8); + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { - write64le(buf, in.plt->getVA()); + write64(buf, in.plt->getVA()); } void AArch64::writePltHeader(uint8_t *buf) const { @@ -322,20 +335,20 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, case R_AARCH64_ABS16: case R_AARCH64_PREL16: checkIntUInt(loc, val, 16, rel); - write16le(loc, val); + write16(loc, val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: checkIntUInt(loc, val, 32, rel); - write32le(loc, val); + write32(loc, val); break; case R_AARCH64_PLT32: checkInt(loc, val, 32, rel); - write32le(loc, val); + write32(loc, val); break; case R_AARCH64_ABS64: case R_AARCH64_PREL64: - write64le(loc, val); + write64(loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: or32AArch64Imm(loc, val); @@ -400,6 +413,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, checkAlignment(loc, val, 16, rel); or32AArch64Imm(loc, getBits(val, 4, 11)); break; + case R_AARCH64_LD64_GOTPAGE_LO15: + checkAlignment(loc, val, 8, rel); + or32AArch64Imm(loc, getBits(val, 3, 14)); + break; case R_AARCH64_MOVW_UABS_G0: checkUInt(loc, val, 16, rel); LLVM_FALLTHROUGH; @@ -462,6 +479,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, case R_AARCH64_TLSDESC_ADD_LO12: or32AArch64Imm(loc, val); break; + case R_AARCH64_TLSDESC: + // For R_AARCH64_TLSDESC the addend is stored in the second 64-bit word. + write64(loc + 8, val); + break; default: llvm_unreachable("unknown relocation"); } diff --git a/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp b/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp index 3610a38692d..466ad81922d 100644 --- a/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp +++ b/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp @@ -22,6 +22,10 @@ using namespace lld::elf; namespace { class AMDGPU final : public TargetInfo { +private: + uint32_t calcEFlagsV3() const; + uint32_t calcEFlagsV4() const; + public: AMDGPU(); uint32_t calcEFlags() const override; @@ -41,11 +45,10 @@ AMDGPU::AMDGPU() { } static uint32_t getEFlags(InputFile *file) { - return cast>(file)->getObj().getHeader()->e_flags; + return cast>(file)->getObj().getHeader().e_flags; } -uint32_t AMDGPU::calcEFlags() const { - assert(!objectFiles.empty()); +uint32_t AMDGPU::calcEFlagsV3() const { uint32_t ret = getEFlags(objectFiles[0]); // Verify that all input files have the same e_flags. @@ -58,6 +61,67 @@ uint32_t AMDGPU::calcEFlags() const { return ret; } +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 retSramEcc = + getEFlags(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)) { + if (retMach != (getEFlags(f) & EF_AMDGPU_MACH)) { + error("incompatible mach: " + toString(f)); + return 0; + } + + if (retXnack == EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4 || + (retXnack != EF_AMDGPU_FEATURE_XNACK_ANY_V4 && + (getEFlags(f) & EF_AMDGPU_FEATURE_XNACK_V4) + != EF_AMDGPU_FEATURE_XNACK_ANY_V4)) { + if (retXnack != (getEFlags(f) & EF_AMDGPU_FEATURE_XNACK_V4)) { + error("incompatible xnack: " + toString(f)); + return 0; + } + } else { + if (retXnack == EF_AMDGPU_FEATURE_XNACK_ANY_V4) + retXnack = getEFlags(f) & EF_AMDGPU_FEATURE_XNACK_V4; + } + + if (retSramEcc == EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4 || + (retSramEcc != EF_AMDGPU_FEATURE_SRAMECC_ANY_V4 && + (getEFlags(f) & EF_AMDGPU_FEATURE_SRAMECC_V4) != + EF_AMDGPU_FEATURE_SRAMECC_ANY_V4)) { + if (retSramEcc != (getEFlags(f) & EF_AMDGPU_FEATURE_SRAMECC_V4)) { + error("incompatible sramecc: " + toString(f)); + return 0; + } + } else { + if (retSramEcc == EF_AMDGPU_FEATURE_SRAMECC_ANY_V4) + retSramEcc = getEFlags(f) & EF_AMDGPU_FEATURE_SRAMECC_V4; + } + } + + return retMach | retXnack | retSramEcc; +} + +uint32_t AMDGPU::calcEFlags() const { + assert(!objectFiles.empty()); + + uint8_t abiVersion = cast>(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: + return calcEFlagsV4(); + default: + error("unknown abi version: " + Twine(abiVersion)); + return 0; + } +} + void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { switch (rel.type) { case R_AMDGPU_ABS32: @@ -75,6 +139,12 @@ void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_AMDGPU_REL32_HI: write32le(loc, val >> 32); break; + case R_AMDGPU_REL16: { + int64_t simm = (static_cast(val) - 4) / 4; + checkInt(loc, simm, 16, rel); + write16le(loc, simm); + break; + } default: llvm_unreachable("unknown relocation"); } @@ -90,6 +160,7 @@ RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, case R_AMDGPU_REL32_LO: case R_AMDGPU_REL32_HI: case R_AMDGPU_REL64: + case R_AMDGPU_REL16: return R_PC; case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: diff --git a/gnu/llvm/lld/ELF/Arch/ARM.cpp b/gnu/llvm/lld/ELF/Arch/ARM.cpp index fd90557cc4f..d909a3234c1 100644 --- a/gnu/llvm/lld/ELF/Arch/ARM.cpp +++ b/gnu/llvm/lld/ELF/Arch/ARM.cpp @@ -150,7 +150,7 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s, case R_ARM_NONE: return R_NONE; case R_ARM_TLS_LE32: - return R_TLS; + return R_TPREL; case R_ARM_V4BX: // V4BX is just a marker to indicate there's a "bx rN" instruction at the // given address. It can be used to implement a special linker mode which @@ -279,7 +279,7 @@ 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 { + 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()) @@ -298,7 +298,7 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, LLVM_FALLTHROUGH; case R_ARM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); - return !inBranchRange(type, branchAddr, dst); + return !inBranchRange(type, branchAddr, dst + a); } case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: @@ -309,7 +309,7 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, LLVM_FALLTHROUGH; case R_ARM_THM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); - return !inBranchRange(type, branchAddr, dst); + return !inBranchRange(type, branchAddr, dst + a); } } return false; @@ -350,46 +350,31 @@ uint32_t ARM::getThunkSectionSpacing() const { } bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { - uint64_t range; - uint64_t instrSize; + if ((dst & 0x1) == 0) + // Destination is ARM, if ARM caller then Src is already 4-byte aligned. + // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure + // destination will be 4 byte aligned. + src &= ~0x3; + else + // Bit 0 == 1 denotes Thumb state, it is not part of the range. + dst &= ~0x1; + int64_t offset = dst - src; switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: case R_ARM_CALL: - range = 0x2000000; - instrSize = 4; - break; + return llvm::isInt<26>(offset); case R_ARM_THM_JUMP19: - range = 0x100000; - instrSize = 2; - break; + return llvm::isInt<21>(offset); case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; - instrSize = 2; - break; + return config->armJ1J2BranchEncoding ? llvm::isInt<25>(offset) + : llvm::isInt<23>(offset); default: return true; } - // PC at Src is 2 instructions ahead, immediate of branch is signed - if (src > dst) - range -= 2 * instrSize; - else - range += instrSize; - - if ((dst & 0x1) == 0) - // Destination is ARM, if ARM caller then Src is already 4-byte aligned. - // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure - // destination will be 4 byte aligned. - src &= ~0x3; - else - // Bit 0 == 1 denotes Thumb state, it is not part of the range - dst &= ~0x1; - - uint64_t distance = (src > dst) ? src - dst : dst - src; - return distance <= range; } // Helper to produce message text when LLD detects that a CALL relocation to @@ -722,20 +707,29 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; case R_ARM_ABS32: case R_ARM_BASE_PREL: + case R_ARM_GLOB_DAT: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: + case R_ARM_IRELATIVE: case R_ARM_REL32: + case R_ARM_RELATIVE: + case R_ARM_SBREL32: case R_ARM_TARGET1: case R_ARM_TARGET2: + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DTPOFF32: case R_ARM_TLS_GD32: - case R_ARM_TLS_LDM32: - case R_ARM_TLS_LDO32: case R_ARM_TLS_IE32: + case R_ARM_TLS_LDM32: case R_ARM_TLS_LE32: + case R_ARM_TLS_LDO32: + case R_ARM_TLS_TPOFF32: return SignExtend64<32>(read32le(buf)); case R_ARM_PREL31: return SignExtend64<31>(read32le(buf)); @@ -843,6 +837,10 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { uint64_t imm12 = read16le(buf + 2) & 0x0fff; return u ? imm12 : -imm12; } + case R_ARM_NONE: + 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 4513a970b32..d0d24722570 100644 --- a/gnu/llvm/lld/ELF/Arch/AVR.cpp +++ b/gnu/llvm/lld/ELF/Arch/AVR.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// AVR is a Harvard-architecture 8-bit micrcontroller designed for small +// AVR is a Harvard-architecture 8-bit microcontroller designed for small // baremetal programs. All AVR-family processors have 32 8-bit registers. // The tiniest AVR has 32 byte RAM and 1 KiB program memory, and the largest // one supports up to 2^24 data address space and 2^22 code address space. @@ -43,6 +43,7 @@ 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; void relocate(uint8_t *loc, const Relocation &rel, @@ -55,11 +56,38 @@ AVR::AVR() { noneRel = R_AVR_NONE; } RelExpr AVR::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_AVR_6: + case R_AVR_6_ADIW: + case R_AVR_8: + case R_AVR_16: + case R_AVR_16_PM: + case R_AVR_32: + case R_AVR_LDI: + case R_AVR_LO8_LDI: + case R_AVR_LO8_LDI_NEG: + case R_AVR_HI8_LDI: + case R_AVR_HI8_LDI_NEG: + case R_AVR_HH8_LDI_NEG: + case R_AVR_HH8_LDI: + case R_AVR_MS8_LDI_NEG: + case R_AVR_MS8_LDI: + case R_AVR_LO8_LDI_PM: + case R_AVR_LO8_LDI_PM_NEG: + case R_AVR_HI8_LDI_PM: + case R_AVR_HI8_LDI_PM_NEG: + case R_AVR_HH8_LDI_PM: + case R_AVR_HH8_LDI_PM_NEG: + case R_AVR_PORT5: + case R_AVR_PORT6: + case R_AVR_CALL: + return R_ABS; case R_AVR_7_PCREL: case R_AVR_13_PCREL: return R_PC; default: - return R_ABS; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } @@ -187,8 +215,7 @@ void AVR::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"); } } @@ -196,3 +223,28 @@ TargetInfo *elf::getAVRTargetInfo() { static AVR target; return ⌖ } + +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader().e_flags; +} + +uint32_t AVR::calcEFlags() const { + assert(!objectFiles.empty()); + + uint32_t flags = getEFlags(objectFiles[0]); + bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED; + + for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + uint32_t objFlags = getEFlags(f); + if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK)) + error(toString(f) + + ": cannot link object files with incompatible target ISA"); + if (!(objFlags & EF_AVR_LINKRELAX_PREPARED)) + hasLinkRelaxFlag = false; + } + + if (!hasLinkRelaxFlag) + flags &= ~EF_AVR_LINKRELAX_PREPARED; + + return flags; +} diff --git a/gnu/llvm/lld/ELF/Arch/Hexagon.cpp b/gnu/llvm/lld/ELF/Arch/Hexagon.cpp index 7740ce9a71e..02d872d58ca 100644 --- a/gnu/llvm/lld/ELF/Arch/Hexagon.cpp +++ b/gnu/llvm/lld/ELF/Arch/Hexagon.cpp @@ -66,7 +66,7 @@ uint32_t Hexagon::calcEFlags() const { // greatest revision in the list of inputs. uint32_t ret = 0; for (InputFile *f : objectFiles) { - uint32_t eflags = cast>(f)->getObj().getHeader()->e_flags; + uint32_t eflags = cast>(f)->getObj().getHeader().e_flags; if (eflags > ret) ret = eflags; } @@ -154,7 +154,7 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, case R_HEX_TPREL_32_6_X: case R_HEX_TPREL_HI16: case R_HEX_TPREL_LO16: - return R_TLS; + return R_TPREL; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); diff --git a/gnu/llvm/lld/ELF/Arch/Mips.cpp b/gnu/llvm/lld/ELF/Arch/Mips.cpp index fd1c5f50773..a233a01d5bb 100644 --- a/gnu/llvm/lld/ELF/Arch/Mips.cpp +++ b/gnu/llvm/lld/ELF/Arch/Mips.cpp @@ -146,7 +146,7 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, case R_MIPS_TLS_TPREL64: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: - return R_TLS; + return R_TPREL; case R_MIPS_PC32: case R_MIPS_PC16: case R_MIPS_PC19_S2: @@ -372,7 +372,7 @@ bool MIPS::needsThunk(RelExpr expr, RelType type, const InputFile *file, if (!f) return false; // If current file has PIC code, LA25 stub is not required. - if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC) + if (f->getObj().getHeader().e_flags & EF_MIPS_PIC) return false; auto *d = dyn_cast(&s); // LA25 is required if target file has PIC code @@ -385,8 +385,10 @@ int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { const endianness e = ELFT::TargetEndianness; switch (type) { case R_MIPS_32: + case R_MIPS_REL32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_DTPMOD32: case R_MIPS_TLS_TPREL32: return SignExtend64<32>(read32(buf)); case R_MIPS_26: @@ -394,25 +396,37 @@ int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { // we should use another expression for calculation: // ((A << 2) | (P & 0xf0000000)) >> 2 return SignExtend64<28>(read32(buf) << 2); + case R_MIPS_CALL_HI16: case R_MIPS_GOT16: + case R_MIPS_GOT_HI16: case R_MIPS_HI16: case R_MIPS_PCHI16: return SignExtend64<16>(read32(buf)) << 16; + case R_MIPS_CALL16: + case R_MIPS_CALL_LO16: + case R_MIPS_GOT_LO16: case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_GOTTPREL: + case R_MIPS_TLS_LDM: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: return SignExtend64<16>(read32(buf)); case R_MICROMIPS_GOT16: case R_MICROMIPS_HI16: return SignExtend64<16>(readShuffle(buf)) << 16; + case R_MICROMIPS_CALL16: case R_MICROMIPS_GPREL16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_LDM: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: return SignExtend64<16>(readShuffle(buf)); @@ -446,7 +460,22 @@ int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { return SignExtend64<25>(readShuffle(buf) << 2); case R_MICROMIPS_PC26_S1: return SignExtend64<27>(readShuffle(buf) << 1); + case R_MIPS_64: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + case (R_MIPS_64 << 8) | R_MIPS_REL32: + return read64(buf); + case R_MIPS_COPY: + return config->is64 ? read64(buf) : read32(buf); + case R_MIPS_NONE: + case R_MIPS_JUMP_SLOT: + case R_MIPS_JALR: + // These relocations are defined as not having an implicit addend. + return 0; default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; } } @@ -749,7 +778,7 @@ template bool elf::isMipsPIC(const Defined *sym) { if (!file) return false; - return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; + return file->getObj().getHeader().e_flags & EF_MIPS_PIC; } template TargetInfo *elf::getMipsTargetInfo() { diff --git a/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp b/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp index 85329c3bef5..77c05a818a5 100644 --- a/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp +++ b/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp @@ -297,7 +297,7 @@ static uint32_t getArchFlags(ArrayRef files) { template uint32_t elf::calcMipsEFlags() { std::vector v; for (InputFile *f : objectFiles) - v.push_back({f, cast>(f)->getObj().getHeader()->e_flags}); + 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 // we can derive from emulation information, since this at least gets us @@ -363,7 +363,7 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, template static bool isN32Abi(const InputFile *f) { if (auto *ef = dyn_cast(f)) - return ef->template getObj().getHeader()->e_flags & EF_MIPS_ABI2; + return ef->template getObj().getHeader().e_flags & EF_MIPS_ABI2; return false; } diff --git a/gnu/llvm/lld/ELF/Arch/PPC64.cpp b/gnu/llvm/lld/ELF/Arch/PPC64.cpp index 71c568088fb..a0c2d1617ca 100644 --- a/gnu/llvm/lld/ELF/Arch/PPC64.cpp +++ b/gnu/llvm/lld/ELF/Arch/PPC64.cpp @@ -22,8 +22,8 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -static uint64_t ppc64TocOffset = 0x8000; -static uint64_t dynamicThreadPointerOffset = 0x8000; +constexpr uint64_t ppc64TocOffset = 0x8000; +constexpr uint64_t dynamicThreadPointerOffset = 0x8000; // The instruction encoding of bits 21-30 from the ISA for the Xform and Dform // instructions that can be used as part of the initial exec TLS sequence. @@ -62,6 +62,92 @@ enum DFormOpcd { ADDI = 14 }; +constexpr uint32_t NOP = 0x60000000; + +enum class PPCLegacyInsn : uint32_t { + NOINSN = 0, + // Loads. + LBZ = 0x88000000, + LHZ = 0xa0000000, + LWZ = 0x80000000, + LHA = 0xa8000000, + LWA = 0xe8000002, + LD = 0xe8000000, + LFS = 0xC0000000, + LXSSP = 0xe4000003, + LFD = 0xc8000000, + LXSD = 0xe4000002, + LXV = 0xf4000001, + LXVP = 0x18000000, + + // Stores. + STB = 0x98000000, + STH = 0xb0000000, + STW = 0x90000000, + STD = 0xf8000000, + STFS = 0xd0000000, + STXSSP = 0xf4000003, + STFD = 0xd8000000, + STXSD = 0xf4000002, + STXV = 0xf4000005, + STXVP = 0x18000001 +}; +enum class PPCPrefixedInsn : uint64_t { + NOINSN = 0, + PREFIX_MLS = 0x0610000000000000, + PREFIX_8LS = 0x0410000000000000, + + // Loads. + PLBZ = PREFIX_MLS, + PLHZ = PREFIX_MLS, + PLWZ = PREFIX_MLS, + PLHA = PREFIX_MLS, + PLWA = PREFIX_8LS | 0xa4000000, + PLD = PREFIX_8LS | 0xe4000000, + PLFS = PREFIX_MLS, + PLXSSP = PREFIX_8LS | 0xac000000, + PLFD = PREFIX_MLS, + PLXSD = PREFIX_8LS | 0xa8000000, + PLXV = PREFIX_8LS | 0xc8000000, + PLXVP = PREFIX_8LS | 0xe8000000, + + // Stores. + PSTB = PREFIX_MLS, + PSTH = PREFIX_MLS, + PSTW = PREFIX_MLS, + PSTD = PREFIX_8LS | 0xf4000000, + PSTFS = PREFIX_MLS, + PSTXSSP = PREFIX_8LS | 0xbc000000, + PSTFD = PREFIX_MLS, + PSTXSD = PREFIX_8LS | 0xb8000000, + PSTXV = PREFIX_8LS | 0xd8000000, + PSTXVP = PREFIX_8LS | 0xf8000000 +}; +static bool checkPPCLegacyInsn(uint32_t encoding) { + PPCLegacyInsn insn = static_cast(encoding); + if (insn == PPCLegacyInsn::NOINSN) + return false; +#define PCREL_OPT(Legacy, PCRel, InsnMask) \ + if (insn == PPCLegacyInsn::Legacy) \ + return true; +#include "PPCInsns.def" +#undef PCREL_OPT + return false; +} + +// Masks to apply to legacy instructions when converting them to prefixed, +// pc-relative versions. For the most part, the primary opcode is shared +// between the legacy instruction and the suffix of its prefixed version. +// However, there are some instances where that isn't the case (DS-Form and +// DQ-form instructions). +enum class LegacyToPrefixMask : uint64_t { + NOMASK = 0x0, + OPC_AND_RST = 0xffe00000, // Primary opc (0-5) and R[ST] (6-10). + ONLY_RST = 0x3e00000, // [RS]T (6-10). + ST_STX28_TO5 = + 0x8000000003e00000, // S/T (6-10) - The [S/T]X bit moves from 28 to 5. +}; + uint64_t elf::getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a @@ -106,6 +192,11 @@ bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; } +void elf::writePrefixedInstruction(uint8_t *loc, uint64_t insn) { + insn = config->isLE ? insn << 32 | insn >> 32 : insn; + write64(loc, insn); +} + static bool addOptional(StringRef name, uint64_t value, std::vector &defined) { Symbol *sym = symtab->find(name); @@ -291,8 +382,9 @@ public: int64_t a) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; - RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const override; + RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; + RelExpr adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const override; void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const override; void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, @@ -315,7 +407,7 @@ public: // document. static uint16_t lo(uint64_t v) { return v; } static uint16_t hi(uint64_t v) { return v >> 16; } -static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; } +static uint64_t ha(uint64_t v) { return (v + 0x8000) >> 16; } static uint16_t higher(uint64_t v) { return v >> 32; } static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; } static uint16_t highest(uint64_t v) { return v >> 48; } @@ -328,6 +420,7 @@ static bool isDQFormInstruction(uint32_t encoding) { switch (getPrimaryOpCode(encoding)) { default: return false; + case 6: // Power10 paired loads/stores (lxvp, stxvp). case 56: // The only instruction with a primary opcode of 56 is `lq`. return true; @@ -339,6 +432,78 @@ static bool isDQFormInstruction(uint32_t encoding) { } } +static bool isDSFormInstruction(PPCLegacyInsn insn) { + switch (insn) { + default: + return false; + case PPCLegacyInsn::LWA: + case PPCLegacyInsn::LD: + case PPCLegacyInsn::LXSD: + case PPCLegacyInsn::LXSSP: + case PPCLegacyInsn::STD: + case PPCLegacyInsn::STXSD: + case PPCLegacyInsn::STXSSP: + return true; + } +} + +static PPCLegacyInsn getPPCLegacyInsn(uint32_t encoding) { + uint32_t opc = encoding & 0xfc000000; + + // If the primary opcode is shared between multiple instructions, we need to + // fix it up to match the actual instruction we are after. + if ((opc == 0xe4000000 || opc == 0xe8000000 || opc == 0xf4000000 || + opc == 0xf8000000) && + !isDQFormInstruction(encoding)) + opc = encoding & 0xfc000003; + else if (opc == 0xf4000000) + opc = encoding & 0xfc000007; + else if (opc == 0x18000000) + opc = encoding & 0xfc00000f; + + // If the value is not one of the enumerators in PPCLegacyInsn, we want to + // return PPCLegacyInsn::NOINSN. + if (!checkPPCLegacyInsn(opc)) + return PPCLegacyInsn::NOINSN; + return static_cast(opc); +} + +static PPCPrefixedInsn getPCRelativeForm(PPCLegacyInsn insn) { + switch (insn) { +#define PCREL_OPT(Legacy, PCRel, InsnMask) \ + case PPCLegacyInsn::Legacy: \ + return PPCPrefixedInsn::PCRel +#include "PPCInsns.def" +#undef PCREL_OPT + } + return PPCPrefixedInsn::NOINSN; +} + +static LegacyToPrefixMask getInsnMask(PPCLegacyInsn insn) { + switch (insn) { +#define PCREL_OPT(Legacy, PCRel, InsnMask) \ + case PPCLegacyInsn::Legacy: \ + return LegacyToPrefixMask::InsnMask +#include "PPCInsns.def" +#undef PCREL_OPT + } + return LegacyToPrefixMask::NOMASK; +} +static uint64_t getPCRelativeForm(uint32_t encoding) { + PPCLegacyInsn origInsn = getPPCLegacyInsn(encoding); + PPCPrefixedInsn pcrelInsn = getPCRelativeForm(origInsn); + if (pcrelInsn == PPCPrefixedInsn::NOINSN) + return UINT64_C(-1); + LegacyToPrefixMask origInsnMask = getInsnMask(origInsn); + uint64_t pcrelEncoding = + (uint64_t)pcrelInsn | (encoding & (uint64_t)origInsnMask); + + // If the mask requires moving bit 28 to bit 5, do that now. + if (origInsnMask == LegacyToPrefixMask::ST_STX28_TO5) + pcrelEncoding |= (encoding & 0x8) << 23; + return pcrelEncoding; +} + static bool isInstructionUpdateForm(uint32_t encoding) { switch (getPrimaryOpCode(encoding)) { default: @@ -363,6 +528,25 @@ static bool isInstructionUpdateForm(uint32_t encoding) { } } +// Compute the total displacement between the prefixed instruction that gets +// to the start of the data and the load/store instruction that has the offset +// into the data structure. +// For example: +// paddi 3, 0, 1000, 1 +// lwz 3, 20(3) +// Should add up to 1020 for total displacement. +static int64_t getTotalDisp(uint64_t prefixedInsn, uint32_t accessInsn) { + int64_t disp34 = llvm::SignExtend64( + ((prefixedInsn & 0x3ffff00000000) >> 16) | (prefixedInsn & 0xffff), 34); + int32_t disp16 = llvm::SignExtend32(accessInsn & 0xffff, 16); + // For DS and DQ form instructions, we need to mask out the XO bits. + if (isDQFormInstruction(accessInsn)) + disp16 &= ~0xf; + else if (isDSFormInstruction(getPPCLegacyInsn(accessInsn))) + disp16 &= ~0x3; + return disp34 + disp16; +} + // There are a number of places when we either want to read or write an // instruction when handling a half16 relocation type. On big-endian the buffer // pointer is pointing into the middle of the word we want to extract, and on @@ -376,15 +560,6 @@ static uint32_t readFromHalf16(const uint8_t *loc) { return read32(config->isLE ? loc : loc - 2); } -// The prefixed instruction is always a 4 byte prefix followed by a 4 byte -// instruction. Therefore, the prefix is always in lower memory than the -// instruction (regardless of endianness). -// As a result, we need to shift the pieces around on little endian machines. -static void writePrefixedInstruction(uint8_t *loc, uint64_t insn) { - insn = config->isLE ? insn << 32 | insn >> 32 : insn; - write64(loc, insn); -} - static uint64_t readPrefixedInstruction(const uint8_t *loc) { uint64_t fullInstr = read64(loc); return config->isLE ? (fullInstr << 32 | fullInstr >> 32) : fullInstr; @@ -446,8 +621,8 @@ int PPC64::getTlsGdRelaxSkip(RelType type) const { static uint32_t getEFlags(InputFile *file) { if (config->ekind == ELF64BEKind) - return cast>(file)->getObj().getHeader()->e_flags; - return cast>(file)->getObj().getHeader()->e_flags; + return cast>(file)->getObj().getHeader().e_flags; + return cast>(file)->getObj().getHeader().e_flags; } // This file implements v2 ABI. This function makes sure that all @@ -479,6 +654,49 @@ void PPC64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { relocateNoSym(loc, R_PPC64_TOC16_LO, val); break; } + case R_PPC64_GOT_PCREL34: { + // Clear the first 8 bits of the prefix and the first 6 bits of the + // instruction (the primary opcode). + uint64_t insn = readPrefixedInstruction(loc); + if ((insn & 0xfc000000) != 0xe4000000) + error("expected a 'pld' for got-indirect to pc-relative relaxing"); + insn &= ~0xff000000fc000000; + + // Replace the cleared bits with the values for PADDI (0x600000038000000); + insn |= 0x600000038000000; + writePrefixedInstruction(loc, insn); + relocate(loc, rel, val); + break; + } + case R_PPC64_PCREL_OPT: { + // We can only relax this if the R_PPC64_GOT_PCREL34 at this offset can + // be relaxed. The eligibility for the relaxation needs to be determined + // on that relocation since this one does not relocate a symbol. + uint64_t insn = readPrefixedInstruction(loc); + uint32_t accessInsn = read32(loc + rel.addend); + uint64_t pcRelInsn = getPCRelativeForm(accessInsn); + + // This error is not necessary for correctness but is emitted for now + // to ensure we don't miss these opportunities in real code. It can be + // removed at a later date. + if (pcRelInsn == UINT64_C(-1)) { + errorOrWarn( + "unrecognized instruction for R_PPC64_PCREL_OPT relaxation: 0x" + + Twine::utohexstr(accessInsn)); + break; + } + + int64_t totalDisp = getTotalDisp(insn, accessInsn); + if (!isInt<34>(totalDisp)) + break; // Displacement doesn't fit. + // Convert the PADDI to the prefixed version of accessInsn and convert + // accessInsn to a nop. + writePrefixedInstruction(loc, pcRelInsn | + ((totalDisp & 0x3ffff0000) << 16) | + (totalDisp & 0xffff)); + write32(loc + rel.addend, NOP); // nop accessInsn. + break; + } default: llvm_unreachable("unexpected relocation type"); } @@ -503,22 +721,45 @@ void PPC64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, switch (rel.type) { case R_PPC64_GOT_TLSGD16_HA: - writeFromHalf16(loc, 0x60000000); // nop + writeFromHalf16(loc, NOP); break; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 relocateNoSym(loc, R_PPC64_TPREL16_HA, val); break; - case R_PPC64_TLSGD: - write32(loc, 0x60000000); // nop - write32(loc + 4, 0x38630000); // addi r3, r3 - // Since we are relocating a half16 type relocation and Loc + 4 points to - // the start of an instruction we need to advance the buffer by an extra - // 2 bytes on BE. - relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, val); + case R_PPC64_GOT_TLSGD_PCREL34: + // Relax from paddi r3, 0, x@got@tlsgd@pcrel, 1 to + // paddi r3, r13, x@tprel, 0 + writePrefixedInstruction(loc, 0x06000000386d0000); + relocateNoSym(loc, R_PPC64_TPREL34, val); break; + case R_PPC64_TLSGD: { + // PC Relative Relaxation: + // Relax from bl __tls_get_addr@notoc(x@tlsgd) to + // nop + // TOC Relaxation: + // Relax from bl __tls_get_addr(x@tlsgd) + // nop + // to + // nop + // addi r3, r3, x@tprel@l + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + write32(loc, NOP); // nop + write32(loc + 4, 0x38630000); // addi r3, r3 + // Since we are relocating a half16 type relocation and Loc + 4 points to + // the start of an instruction we need to advance the buffer by an extra + // 2 bytes on BE. + relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); + } else if (locAsInt % 4 == 1) { + write32(loc - 1, NOP); + } else { + errorOrWarn("R_PPC64_TLSGD has unexpected byte alignment"); + } + break; + } default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } @@ -543,21 +784,45 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, switch (rel.type) { case R_PPC64_GOT_TLSLD16_HA: - writeFromHalf16(loc, 0x60000000); // nop + writeFromHalf16(loc, NOP); break; case R_PPC64_GOT_TLSLD16_LO: writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0 break; - case R_PPC64_TLSLD: - write32(loc, 0x60000000); // nop - write32(loc + 4, 0x38631000); // addi r3, r3, 4096 + case R_PPC64_GOT_TLSLD_PCREL34: + // Relax from paddi r3, 0, x1@got@tlsld@pcrel, 1 to + // paddi r3, r13, 0x1000, 0 + writePrefixedInstruction(loc, 0x06000000386d1000); break; + case R_PPC64_TLSLD: { + // PC Relative Relaxation: + // Relax from bl __tls_get_addr@notoc(x@tlsld) + // to + // nop + // TOC Relaxation: + // Relax from bl __tls_get_addr(x@tlsld) + // nop + // to + // nop + // addi r3, r3, 4096 + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + write32(loc, NOP); + write32(loc + 4, 0x38631000); // addi r3, r3, 4096 + } else if (locAsInt % 4 == 1) { + write32(loc - 1, NOP); + } else { + errorOrWarn("R_PPC64_TLSLD has unexpected byte alignment"); + } + break; + } case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_HA: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: + case R_PPC64_DTPREL34: relocate(loc, rel, val); break; default: @@ -614,7 +879,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; switch (rel.type) { case R_PPC64_GOT_TPREL16_HA: - write32(loc - offset, 0x60000000); // nop + write32(loc - offset, NOP); break; case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: { @@ -623,16 +888,57 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, relocateNoSym(loc, R_PPC64_TPREL16_HA, val); break; } + case R_PPC64_GOT_TPREL_PCREL34: { + const uint64_t pldRT = readPrefixedInstruction(loc) & 0x0000000003e00000; + // paddi RT(from pld), r13, symbol@tprel, 0 + writePrefixedInstruction(loc, 0x06000000380d0000 | pldRT); + relocateNoSym(loc, R_PPC64_TPREL34, val); + break; + } case R_PPC64_TLS: { - uint32_t primaryOp = getPrimaryOpCode(read32(loc)); - if (primaryOp != 31) - error("unrecognized instruction for IE to LE R_PPC64_TLS"); - uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 - uint32_t dFormOp = getPPCDFormOp(secondaryOp); - if (dFormOp == 0) - error("unrecognized instruction for IE to LE R_PPC64_TLS"); - write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); - relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val); + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + uint32_t primaryOp = getPrimaryOpCode(read32(loc)); + if (primaryOp != 31) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); + relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val); + } else if (locAsInt % 4 == 1) { + // If the offset is not 4 byte aligned then we have a PCRel type reloc. + // This version of the relocation is offset by one byte from the + // instruction it references. + uint32_t tlsInstr = read32(loc - 1); + uint32_t primaryOp = getPrimaryOpCode(tlsInstr); + if (primaryOp != 31) + errorOrWarn("unrecognized instruction for IE to LE R_PPC64_TLS"); + uint32_t secondaryOp = (tlsInstr & 0x000007FE) >> 1; // bits 21-30 + // The add is a special case and should be turned into a nop. The paddi + // that comes before it will already have computed the address of the + // symbol. + if (secondaryOp == 266) { + // Check if the add uses the same result register as the input register. + uint32_t rt = (tlsInstr & 0x03E00000) >> 21; // bits 6-10 + uint32_t ra = (tlsInstr & 0x001F0000) >> 16; // bits 11-15 + if (ra == rt) { + write32(loc - 1, NOP); + } else { + // mr rt, ra + write32(loc - 1, 0x7C000378 | (rt << 16) | (ra << 21) | (ra << 11)); + } + } else { + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + errorOrWarn("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc - 1, ((dFormOp << 26) | (tlsInstr & 0x03FF0000))); + } + } else { + errorOrWarn("R_PPC64_TLS must be either 4 byte aligned or one byte " + "offset from 4 byte aligned"); + } break; } default: @@ -650,6 +956,7 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_ADDR16_DS: case R_PPC64_ADDR16_HA: case R_PPC64_ADDR16_HI: + case R_PPC64_ADDR16_HIGH: case R_PPC64_ADDR16_HIGHER: case R_PPC64_ADDR16_HIGHERA: case R_PPC64_ADDR16_HIGHEST: @@ -672,6 +979,8 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_TOC16_LO: return R_GOTREL; case R_PPC64_GOT_PCREL34: + case R_PPC64_GOT_TPREL_PCREL34: + case R_PPC64_PCREL_OPT: return R_GOT_PC; case R_PPC64_TOC16_HA: case R_PPC64_TOC16_LO_DS: @@ -695,11 +1004,15 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_LO: return R_TLSGD_GOT; + case R_PPC64_GOT_TLSGD_PCREL34: + return R_TLSGD_PC; case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TLSLD16_LO: return R_TLSLD_GOT; + case R_PPC64_GOT_TLSLD_PCREL34: + return R_TLSLD_PC; case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: @@ -720,7 +1033,8 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: - return R_TLS; + case R_PPC64_TPREL34: + return R_TPREL; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_HA: @@ -732,6 +1046,7 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: case R_PPC64_DTPREL64: + case R_PPC64_DTPREL34: return R_DTPREL; case R_PPC64_TLSGD: return R_TLSDESC_CALL; @@ -912,13 +1227,19 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_PPC64_REL16_HA: case R_PPC64_TPREL16_HA: if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) - writeFromHalf16(loc, 0x60000000); - else + writeFromHalf16(loc, NOP); + else { + checkInt(loc, val + 0x8000, 32, rel); write16(loc, ha(val)); + } break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: case R_PPC64_TPREL16_HI: + checkInt(loc, val, 32, rel); + write16(loc, hi(val)); + break; + case R_PPC64_ADDR16_HIGH: write16(loc, hi(val)); break; case R_PPC64_ADDR16_HIGHER: @@ -1006,7 +1327,18 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_PPC64_DTPREL64: write64(loc, val - dynamicThreadPointerOffset); break; - case R_PPC64_PCREL34: { + case R_PPC64_DTPREL34: + // The Dynamic Thread Vector actually points 0x8000 bytes past the start + // of the TLS block. Therefore, in the case of R_PPC64_DTPREL34 we first + // need to subtract that value then fallthrough to the general case. + val -= dynamicThreadPointerOffset; + LLVM_FALLTHROUGH; + case R_PPC64_PCREL34: + case R_PPC64_GOT_PCREL34: + case R_PPC64_GOT_TLSGD_PCREL34: + case R_PPC64_GOT_TLSLD_PCREL34: + case R_PPC64_GOT_TPREL_PCREL34: + case R_PPC64_TPREL34: { const uint64_t si0Mask = 0x00000003ffff0000; const uint64_t si1Mask = 0x000000000000ffff; const uint64_t fullMask = 0x0003ffff0000ffff; @@ -1017,17 +1349,9 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { (val & si1Mask)); break; } - case R_PPC64_GOT_PCREL34: { - const uint64_t si0Mask = 0x00000003ffff0000; - const uint64_t si1Mask = 0x000000000000ffff; - const uint64_t fullMask = 0x0003ffff0000ffff; - checkInt(loc, val, 34, rel); - - uint64_t instr = readPrefixedInstruction(loc) & ~fullMask; - writePrefixedInstruction(loc, instr | ((val & si0Mask) << 16) | - (val & si1Mask)); + // If we encounter a PCREL_OPT relocation that we won't optimize. + case R_PPC64_PCREL_OPT: break; - } default: llvm_unreachable("unknown relocation"); } @@ -1039,26 +1363,19 @@ bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, type != R_PPC64_REL24_NOTOC) return false; - // FIXME: Remove the fatal error once the call protocol is implemented. - if (type == R_PPC64_REL24_NOTOC && s.isInPlt()) - fatal("unimplemented feature: external function call with the reltype" - " R_PPC64_REL24_NOTOC"); - // If a function is in the Plt it needs to be called with a call-stub. if (s.isInPlt()) return true; - // FIXME: Remove the fatal error once the call protocol is implemented. - if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1) - fatal("unimplemented feature: local function call with the reltype" - " R_PPC64_REL24_NOTOC and the callee needs toc-pointer setup"); - // This check looks at the st_other bits of the callee with relocation // R_PPC64_REL14 or R_PPC64_REL24. If the value is 1, then the callee // clobbers the TOC and we need an R2 save stub. if (type != R_PPC64_REL24_NOTOC && (s.stOther >> 5) == 1) return true; + if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1) + return true; + // If a symbol is a weak undefined and we are compiling an executable // it doesn't need a range-extending thunk since it can't be called. if (s.isUndefWeak() && !config->shared) @@ -1089,15 +1406,26 @@ bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { llvm_unreachable("unsupported relocation type used in branch"); } -RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const { - if (expr == R_RELAX_TLS_GD_TO_IE) +RelExpr PPC64::adjustTlsExpr(RelType type, RelExpr expr) const { + if (type != R_PPC64_GOT_TLSGD_PCREL34 && expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; return expr; } +RelExpr PPC64::adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const { + if ((type == R_PPC64_GOT_PCREL34 || type == R_PPC64_PCREL_OPT) && + config->pcRelOptimize) { + // It only makes sense to optimize pld since paddi means that the address + // of the object in the GOT is required rather than the object itself. + if ((readPrefixedInstruction(loc) & 0xfc000000) == 0xe4000000) + return R_PPC64_RELAX_GOT_PC; + } + return R_GOT_PC; +} + // Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` uses 4 instructions. // Instruction Relocation Symbol @@ -1132,10 +1460,35 @@ void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, relocateNoSym(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } - case R_PPC64_TLSGD: - write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop - write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + case R_PPC64_GOT_TLSGD_PCREL34: { + // Relax from paddi r3, 0, sym@got@tlsgd@pcrel, 1 to + // pld r3, sym@got@tprel@pcrel + writePrefixedInstruction(loc, 0x04100000e4600000); + relocateNoSym(loc, R_PPC64_GOT_TPREL_PCREL34, val); return; + } + case R_PPC64_TLSGD: { + // PC Relative Relaxation: + // Relax from bl __tls_get_addr@notoc(x@tlsgd) to + // nop + // TOC Relaxation: + // Relax from bl __tls_get_addr(x@tlsgd) + // nop + // to + // nop + // add r3, r3, r13 + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + write32(loc, NOP); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + } else if (locAsInt % 4 == 1) { + // bl __tls_get_addr(sym@tlsgd) --> add r3, r3, r13 + write32(loc - 1, 0x7c636a14); + } else { + errorOrWarn("R_PPC64_TLSGD has unexpected byte alignment"); + } + return; + } default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); } @@ -1204,7 +1557,7 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint32_t secondInstr = read32(loc + 8); if (!loImm && getPrimaryOpCode(secondInstr) == 14) { loImm = secondInstr & 0xFFFF; - } else if (secondInstr != 0x60000000) { + } else if (secondInstr != NOP) { return false; } @@ -1218,7 +1571,7 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, }; if (!checkRegOperands(firstInstr, 12, 1)) return false; - if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12)) + if (secondInstr != NOP && !checkRegOperands(secondInstr, 12, 12)) return false; int32_t stackFrameSize = (hiImm * 65536) + loImm; @@ -1237,12 +1590,12 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, if (hiImm) { write32(loc + 4, 0x3D810000 | (uint16_t)hiImm); // If the low immediate is zero the second instruction will be a nop. - secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000; + secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : NOP; write32(loc + 8, secondInstr); } else { // addi r12, r1, imm write32(loc + 4, (0x39810000) | (uint16_t)loImm); - write32(loc + 8, 0x60000000); + write32(loc + 8, NOP); } return true; diff --git a/gnu/llvm/lld/ELF/Arch/PPCInsns.def b/gnu/llvm/lld/ELF/Arch/PPCInsns.def new file mode 100644 index 00000000000..1baa8fd6c2a --- /dev/null +++ b/gnu/llvm/lld/ELF/Arch/PPCInsns.def @@ -0,0 +1,27 @@ +#ifndef PCREL_OPT +#error "Need to define function-style macro PCREL_OPT" +#endif +PCREL_OPT(NOINSN, NOINSN, NOMASK); +PCREL_OPT(LBZ, PLBZ, OPC_AND_RST); +PCREL_OPT(LHZ, PLHZ, OPC_AND_RST); +PCREL_OPT(LWZ, PLWZ, OPC_AND_RST); +PCREL_OPT(LHA, PLHA, OPC_AND_RST); +PCREL_OPT(LWA, PLWA, ONLY_RST); +PCREL_OPT(LD, PLD , ONLY_RST); +PCREL_OPT(LFS, PLFS, OPC_AND_RST); +PCREL_OPT(LXSSP, PLXSSP, ONLY_RST); +PCREL_OPT(LFD, PLFD, OPC_AND_RST); +PCREL_OPT(LXSD, PLXSD, ONLY_RST); +PCREL_OPT(LXV, PLXV, ST_STX28_TO5); +PCREL_OPT(LXVP, PLXVP, OPC_AND_RST); + +PCREL_OPT(STB, PSTB, OPC_AND_RST); +PCREL_OPT(STH, PSTH, OPC_AND_RST); +PCREL_OPT(STW, PSTW, OPC_AND_RST); +PCREL_OPT(STD, PSTD, ONLY_RST); +PCREL_OPT(STFS, PSTFS, OPC_AND_RST); +PCREL_OPT(STXSSP, PSTXSSP, ONLY_RST); +PCREL_OPT(STFD, PSTFD, OPC_AND_RST); +PCREL_OPT(STXSD, PSTXSD, ONLY_RST); +PCREL_OPT(STXV, PSTXV, ST_STX28_TO5); +PCREL_OPT(STXVP, PSTXVP, OPC_AND_RST); diff --git a/gnu/llvm/lld/ELF/Arch/RISCV.cpp b/gnu/llvm/lld/ELF/Arch/RISCV.cpp index b340fd00dee..dad23fff91c 100644 --- a/gnu/llvm/lld/ELF/Arch/RISCV.cpp +++ b/gnu/llvm/lld/ELF/Arch/RISCV.cpp @@ -24,8 +24,10 @@ class RISCV final : public TargetInfo { public: RISCV(); uint32_t calcEFlags() const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void writeGotHeader(uint8_t *buf) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; @@ -104,8 +106,8 @@ RISCV::RISCV() { static uint32_t getEFlags(InputFile *f) { if (config->is64) - return cast>(f)->getObj().getHeader()->e_flags; - return cast>(f)->getObj().getHeader()->e_flags; + return cast>(f)->getObj().getHeader().e_flags; + return cast>(f)->getObj().getHeader().e_flags; } uint32_t RISCV::calcEFlags() const { @@ -133,6 +135,28 @@ uint32_t RISCV::calcEFlags() const { return target; } +int64_t RISCV::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + case R_RISCV_32: + case R_RISCV_TLS_DTPMOD32: + case R_RISCV_TLS_DTPREL32: + return SignExtend64<32>(read32le(buf)); + case R_RISCV_64: + return read64le(buf); + case R_RISCV_RELATIVE: + case R_RISCV_IRELATIVE: + return config->is64 ? read64le(buf) : read32le(buf); + case R_RISCV_NONE: + case R_RISCV_JUMP_SLOT: + // These relocations are defined as not having an implicit addend. + return 0; + } +} + void RISCV::writeGotHeader(uint8_t *buf) const { if (config->is64) write64le(buf, mainPart->dynamic->getVA()); @@ -147,6 +171,15 @@ void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const { write32le(buf, in.plt->getVA()); } +void RISCV::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + if (config->writeAddends) { + if (config->is64) + write64le(buf, s.getVA()); + else + write32le(buf, s.getVA()); + } +} + void RISCV::writePltHeader(uint8_t *buf) const { // 1: auipc t2, %pcrel_hi(.got.plt) // sub t1, t1, t3 @@ -235,7 +268,7 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, case R_RISCV_TPREL_HI20: case R_RISCV_TPREL_LO12_I: case R_RISCV_TPREL_LO12_S: - return R_TLS; + return R_TPREL; case R_RISCV_RELAX: case R_RISCV_TPREL_ADD: return R_NONE; diff --git a/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp b/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp index f137c21fc89..9e18ae4753b 100644 --- a/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp +++ b/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp @@ -78,7 +78,7 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, return R_NONE; case R_SPARC_TLS_LE_HIX22: case R_SPARC_TLS_LE_LOX10: - return R_TLS; + return R_TPREL; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); diff --git a/gnu/llvm/lld/ELF/Arch/X86.cpp b/gnu/llvm/lld/ELF/Arch/X86.cpp index 8c8824d53cc..df769f0a1c8 100644 --- a/gnu/llvm/lld/ELF/Arch/X86.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86.cpp @@ -37,8 +37,7 @@ public: void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; - RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) 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, @@ -149,9 +148,9 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s, case R_386_GOTOFF: return R_GOTPLTREL; case R_386_TLS_LE: - return R_TLS; + return R_TPREL; case R_386_TLS_LE_32: - return R_NEG_TLS; + return R_TPREL_NEG; case R_386_NONE: return R_NONE; default: @@ -161,8 +160,7 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s, } } -RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const { +RelExpr X86::adjustTlsExpr(RelType type, RelExpr expr) const { switch (expr) { default: return expr; @@ -252,16 +250,36 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_386_PC16: return SignExtend64<16>(read16le(buf)); case R_386_32: + case R_386_GLOB_DAT: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: case R_386_GOTPC: + case R_386_IRELATIVE: case R_386_PC32: case R_386_PLT32: + case R_386_RELATIVE: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: case R_386_TLS_LDO_32: + case R_386_TLS_LDM: + case R_386_TLS_IE: + case R_386_TLS_IE_32: case R_386_TLS_LE: + case R_386_TLS_LE_32: + case R_386_TLS_GD: + case R_386_TLS_GD_32: + case R_386_TLS_GOTIE: + case R_386_TLS_TPOFF: + case R_386_TLS_TPOFF32: return SignExtend64<32>(read32le(buf)); + case R_386_NONE: + case R_386_JUMP_SLOT: + // These relocations are defined as not having an implicit addend. + return 0; default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; } } diff --git a/gnu/llvm/lld/ELF/Arch/X86_64.cpp b/gnu/llvm/lld/ELF/Arch/X86_64.cpp index 24711ec210a..514ddc5ec8b 100644 --- a/gnu/llvm/lld/ELF/Arch/X86_64.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86_64.cpp @@ -32,16 +32,18 @@ public: RelType getDynRel(RelType type) const override; void writeGotPltHeader(uint8_t *buf) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void applyJumpInstrMod(uint8_t *loc, JumpModType type, unsigned size) const override; - RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const override; + RelExpr adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const override; void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const override; void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, @@ -85,6 +87,7 @@ X86_64::X86_64() { tlsGotRel = R_X86_64_TPOFF64; tlsModuleIndexRel = R_X86_64_DTPMOD64; tlsOffsetRel = R_X86_64_DTPOFF64; + gotEntrySize = 8; pltHeaderSize = 16; pltEntrySize = 16; ipltEntrySize = 16; @@ -324,7 +327,7 @@ RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, case R_X86_64_DTPOFF64: return R_DTPREL; case R_X86_64_TPOFF32: - return R_TLS; + return R_TPREL; case R_X86_64_TLSDESC_CALL: return R_TLSDESC_CALL; case R_X86_64_TLSLD: @@ -378,6 +381,12 @@ void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { write64le(buf, s.getPltVA() + 6); } +void X86_64::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + // An x86 entry is the address of the ifunc resolver function (for -z rel). + if (config->writeAddends) + write64le(buf, s.getVA()); +} + void X86_64::writePltHeader(uint8_t *buf) const { const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) @@ -674,6 +683,55 @@ void X86_64::applyJumpInstrMod(uint8_t *loc, JumpModType type, } } +int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_X86_64_8: + case R_X86_64_PC8: + return SignExtend64<8>(*buf); + case R_X86_64_16: + case R_X86_64_PC16: + return SignExtend64<16>(read16le(buf)); + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_TPOFF32: + case R_X86_64_GOT32: + case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: + case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + case R_X86_64_PC32: + case R_X86_64_GOTTPOFF: + case R_X86_64_PLT32: + case R_X86_64_TLSGD: + case R_X86_64_TLSLD: + case R_X86_64_DTPOFF32: + case R_X86_64_SIZE32: + return SignExtend64<32>(read32le(buf)); + case R_X86_64_64: + case R_X86_64_TPOFF64: + case R_X86_64_DTPOFF64: + case R_X86_64_DTPMOD64: + case R_X86_64_PC64: + case R_X86_64_SIZE64: + case R_X86_64_GLOB_DAT: + case R_X86_64_GOT64: + case R_X86_64_GOTOFF64: + case R_X86_64_GOTPC64: + case R_X86_64_IRELATIVE: + case R_X86_64_RELATIVE: + return read64le(buf); + case R_X86_64_JUMP_SLOT: + case R_X86_64_NONE: + // These relocations are defined as not having an implicit addend. + return 0; + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { switch (rel.type) { case R_X86_64_8: @@ -728,12 +786,17 @@ void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } } -RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr relExpr) const { - if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) - return relExpr; - const uint8_t op = data[-2]; - const uint8_t modRm = data[-1]; +RelExpr X86_64::adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const { + // Only R_X86_64_[REX_]GOTPCRELX can be relaxed. GNU as may emit GOTPCRELX + // with addend != -4. Such an instruction does not load the full GOT entry, so + // we cannot relax the relocation. E.g. movl x@GOTPCREL+4(%rip), %rax + // (addend=0) loads the high 32 bits of the GOT entry. + if ((type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) || + addend != -4) + return R_GOT_PC; + const uint8_t op = loc[-2]; + const uint8_t modRm = loc[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into @@ -746,12 +809,13 @@ RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) return R_RELAX_GOT_PC; + // We don't support test/binop instructions without a REX prefix. + if (type == R_X86_64_GOTPCRELX) + return R_GOT_PC; + // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. - // We also don't relax test/binop instructions without REX byte, - // they are 32bit operations and not common to have. - assert(type == R_X86_64_REX_GOTPCRELX); - return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; + return config->isPic ? R_GOT_PC : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method @@ -822,7 +886,8 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, write32le(loc, val); } -void X86_64::relaxGot(uint8_t *loc, const Relocation &, uint64_t val) const { +void X86_64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { + checkInt(loc, val, 32, rel); const uint8_t op = loc[-2]; const uint8_t modRm = loc[-1]; diff --git a/gnu/llvm/lld/ELF/CMakeLists.txt b/gnu/llvm/lld/ELF/CMakeLists.txt index b89f4436288..f85d0fb9f55 100644 --- a/gnu/llvm/lld/ELF/CMakeLists.txt +++ b/gnu/llvm/lld/ELF/CMakeLists.txt @@ -2,10 +2,6 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(ELFOptionsTableGen) -if(NOT LLD_BUILT_STANDALONE) - set(tablegen_deps intrinsics_gen) -endif() - add_lld_library(lldELF AArch64ErrataFix.cpp Arch/AArch64.cpp @@ -66,5 +62,5 @@ add_lld_library(lldELF DEPENDS ELFOptionsTableGen - ${tablegen_deps} + intrinsics_gen ) diff --git a/gnu/llvm/lld/ELF/CallGraphSort.cpp b/gnu/llvm/lld/ELF/CallGraphSort.cpp index 21c641b5161..15da4d2414a 100644 --- a/gnu/llvm/lld/ELF/CallGraphSort.cpp +++ b/gnu/llvm/lld/ELF/CallGraphSort.cpp @@ -68,7 +68,7 @@ struct Cluster { int next; int prev; - size_t size = 0; + uint64_t size; uint64_t weight = 0; uint64_t initialWeight = 0; Edge bestPred = {-1, 0}; @@ -223,14 +223,14 @@ DenseMap CallGraphSort::run() { DenseMap orderMap; int curOrder = 1; - for (int leader : sorted) + 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); diff --git a/gnu/llvm/lld/ELF/DWARF.cpp b/gnu/llvm/lld/ELF/DWARF.cpp index 5767f6020f9..707a6ebd169 100644 --- a/gnu/llvm/lld/ELF/DWARF.cpp +++ b/gnu/llvm/lld/ELF/DWARF.cpp @@ -77,18 +77,23 @@ template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { namespace { template struct LLDRelocationResolver { // In the ELF ABIs, S sepresents the value of the symbol in the relocation - // entry. For Rela, the addend is stored as part of the relocation entry. - static uint64_t resolve(object::RelocationRef ref, uint64_t s, - uint64_t /* A */) { - return s + ref.getRawDataRefImpl().p; + // entry. For Rela, the addend is stored as part of the relocation entry and + // is provided by the `findAux` method. + // In resolve() methods, the `type` and `offset` arguments would always be 0, + // because we don't set an owning object for the `RelocationRef` instance that + // we create in `findAux()`. + static uint64_t resolve(uint64_t /*type*/, uint64_t /*offset*/, uint64_t s, + uint64_t /*locData*/, int64_t addend) { + return s + addend; } }; template struct LLDRelocationResolver> { - // For Rel, the addend A is supplied by the caller. - static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s, - uint64_t a) { - return s + a; + // For Rel, the addend is extracted from the relocated location and is + // supplied by the caller. + static uint64_t resolve(uint64_t /*type*/, uint64_t /*offset*/, uint64_t s, + uint64_t locData, int64_t /*addend*/) { + return s + locData; } }; } // namespace diff --git a/gnu/llvm/lld/ELF/Driver.h b/gnu/llvm/lld/ELF/Driver.h index 3115e28d166..96d040041c5 100644 --- a/gnu/llvm/lld/ELF/Driver.h +++ b/gnu/llvm/lld/ELF/Driver.h @@ -26,7 +26,7 @@ extern class LinkerDriver *driver; class LinkerDriver { public: - void main(ArrayRef args); + void linkerMain(ArrayRef args); void addFile(StringRef path, bool withLOption); void addLibrary(StringRef name); diff --git a/gnu/llvm/lld/ELF/EhFrame.cpp b/gnu/llvm/lld/ELF/EhFrame.cpp index f97e3b604eb..578c640f021 100644 --- a/gnu/llvm/lld/ELF/EhFrame.cpp +++ b/gnu/llvm/lld/ELF/EhFrame.cpp @@ -38,6 +38,7 @@ 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) { @@ -50,6 +51,7 @@ private: StringRef readString(); void skipLeb128(); void skipAugP(); + StringRef getAugmentation(); InputSectionBase *isec; ArrayRef d; @@ -152,7 +154,11 @@ uint8_t elf::getFdeEncoding(EhSectionPiece *p) { return EhReader(p->sec, p->data()).getFdeEncoding(); } -uint8_t EhReader::getFdeEncoding() { +bool elf::hasLSDA(const EhSectionPiece &p) { + return EhReader(p.sec, p.data()).hasLSDA(); +} + +StringRef EhReader::getAugmentation() { skipBytes(8); int version = readByte(); if (version != 1 && version != 3) @@ -171,26 +177,42 @@ uint8_t EhReader::getFdeEncoding() { readByte(); else skipLeb128(); + return aug; +} +uint8_t EhReader::getFdeEncoding() { // We only care about an 'R' value, but other records may precede an 'R' // record. Unfortunately records are not in TLV (type-length-value) format, // so we need to teach the linker how to skip records for each type. + StringRef aug = getAugmentation(); for (char c : aug) { if (c == 'R') return readByte(); - if (c == 'z') { + if (c == 'z') skipLeb128(); - continue; - } - if (c == 'P') { - skipAugP(); - continue; - } - if (c == 'L') { + else if (c == 'L') readByte(); - continue; - } - failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); + else if (c == 'P') + skipAugP(); + else if (c != 'B' && c != 'S') + failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); } return DW_EH_PE_absptr; } + +bool EhReader::hasLSDA() { + StringRef aug = getAugmentation(); + for (char c : aug) { + if (c == 'L') + return true; + if (c == 'z') + skipLeb128(); + else if (c == 'P') + skipAugP(); + else if (c == 'R') + readByte(); + else if (c != 'B' && c != 'S') + 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 20dd6126ec8..e44832317b3 100644 --- a/gnu/llvm/lld/ELF/EhFrame.h +++ b/gnu/llvm/lld/ELF/EhFrame.h @@ -18,6 +18,7 @@ struct EhSectionPiece; size_t readEhRecordSize(InputSectionBase *s, size_t off); uint8_t getFdeEncoding(EhSectionPiece *p); +bool hasLSDA(const EhSectionPiece &p); } // namespace elf } // namespace lld diff --git a/gnu/llvm/lld/ELF/ICF.cpp b/gnu/llvm/lld/ELF/ICF.cpp index ecf0a282420..5cf944d39c8 100644 --- a/gnu/llvm/lld/ELF/ICF.cpp +++ b/gnu/llvm/lld/ELF/ICF.cpp @@ -74,6 +74,7 @@ #include "ICF.h" #include "Config.h" +#include "EhFrame.h" #include "LinkerScript.h" #include "OutputSections.h" #include "SymbolTable.h" @@ -101,7 +102,7 @@ public: void run(); private: - void segregate(size_t begin, size_t end, bool constant); + void segregate(size_t begin, size_t end, uint32_t eqClassBase, bool constant); template bool constantEq(const InputSection *a, ArrayRef relsA, @@ -196,7 +197,8 @@ static bool isEligible(InputSection *s) { // Split an equivalence class into smaller classes. template -void ICF::segregate(size_t begin, size_t end, bool constant) { +void ICF::segregate(size_t begin, size_t end, uint32_t eqClassBase, + bool constant) { // This loop rearranges sections in [Begin, End) so that all sections // that are equal in terms of equals{Constant,Variable} are contiguous // in [Begin, End). @@ -218,10 +220,11 @@ void ICF::segregate(size_t begin, size_t end, bool constant) { size_t mid = bound - sections.begin(); // Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by - // updating the sections in [Begin, Mid). We use Mid as an equivalence - // class ID because every group ends with a unique index. + // updating the sections in [Begin, Mid). We use Mid as the basis for + // the equivalence class ID because every group ends with a unique index. + // Add this to eqClassBase to avoid equality with unique IDs. for (size_t i = begin; i < mid; ++i) - sections[i]->eqClass[next] = mid; + sections[i]->eqClass[next] = eqClassBase + mid; // If we created a group, we need to iterate the main loop again. if (mid != end) @@ -352,8 +355,8 @@ bool ICF::variableEq(const InputSection *secA, ArrayRef ra, continue; auto *y = cast(db->section); - // Ineligible sections are in the special equivalence class 0. - // They can never be the same in terms of the equivalence class. + // Sections that are in the special equivalence class 0, can never be the + // same in terms of the equivalence class. if (x->eqClass[current] == 0) return false; if (x->eqClass[current] != y->eqClass[current]) @@ -442,7 +445,7 @@ static void combineRelocHashes(unsigned cnt, InputSection *isec, if (auto *relSec = dyn_cast_or_null(d->section)) hash += relSec->eqClass[cnt % 2]; } - // Set MSB to 1 to avoid collisions with non-hash IDs. + // Set MSB to 1 to avoid collisions with unique IDs. isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31); } @@ -459,17 +462,41 @@ template void ICF::run() { for (Symbol *sym : symtab->symbols()) 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 + // text section is referenced by a .eh_frame FDE with LSDA, it is not + // eligible. This is implemented by iterating over CIE/FDE and setting + // eqClass[0] to the referenced text section from a live FDE. + // + // If two .gcc_except_table have identical semantics (usually identical + // content with PC-relative encoding), we will lose folding opportunity. + uint32_t uniqueId = 0; + for (Partition &part : partitions) + part.ehFrame->iterateFDEWithLSDA( + [&](InputSection &s) { s.eqClass[0] = s.eqClass[1] = ++uniqueId; }); + // Collect sections to merge. for (InputSectionBase *sec : inputSections) { auto *s = cast(sec); - if (isEligible(s)) - sections.push_back(s); + if (s->eqClass[0] == 0) { + if (isEligible(s)) + sections.push_back(s); + else + // Ineligible sections are assigned unique IDs, i.e. each section + // belongs to an equivalence class of its own. + s->eqClass[0] = s->eqClass[1] = ++uniqueId; + } } // Initially, we use hash values to partition sections. - parallelForEach( - sections, [&](InputSection *s) { s->eqClass[0] = xxHash64(s->data()); }); + parallelForEach(sections, [&](InputSection *s) { + // Set MSB to 1 to avoid collisions with unique IDs. + s->eqClass[0] = xxHash64(s->data()) | (1U << 31); + }); + // Perform 2 rounds of relocation hash propagation. 2 is an empirical value to + // reduce the average sizes of equivalence classes, i.e. segregate() which has + // a large time complexity will have less work to do. for (unsigned cnt = 0; cnt != 2; ++cnt) { parallelForEach(sections, [&](InputSection *s) { if (s->areRelocsRela) @@ -485,14 +512,20 @@ template void ICF::run() { return a->eqClass[0] < b->eqClass[0]; }); - // Compare static contents and assign unique IDs for each static content. - forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); }); + // Compare static contents and assign unique equivalence class IDs for each + // static content. Use a base offset for these IDs to ensure no overlap with + // the unique IDs already assigned. + uint32_t eqClassBase = ++uniqueId; + forEachClass([&](size_t begin, size_t end) { + segregate(begin, end, eqClassBase, true); + }); // Split groups by comparing relocations until convergence is obtained. do { repeat = false; - forEachClass( - [&](size_t begin, size_t end) { segregate(begin, end, false); }); + forEachClass([&](size_t begin, size_t end) { + segregate(begin, end, eqClassBase, false); + }); } while (repeat); log("ICF needed " + Twine(cnt) + " iterations"); diff --git a/gnu/llvm/lld/ELF/InputFiles.cpp b/gnu/llvm/lld/ELF/InputFiles.cpp index c2f1830a981..d5b9efbe18f 100644 --- a/gnu/llvm/lld/ELF/InputFiles.cpp +++ b/gnu/llvm/lld/ELF/InputFiles.cpp @@ -27,6 +27,7 @@ #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" +#include "llvm/Support/RISCVAttributeParser.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -104,14 +105,18 @@ InputFile::InputFile(Kind k, MemoryBufferRef m) } Optional elf::readFile(StringRef path) { + llvm::TimeTraceScope timeScope("Load input files", path); + // The --chroot option changes our virtual root directory. // This is useful when you are dealing with files created by --reproduce. if (!config->chroot.empty() && path.startswith("/")) path = saver.save(config->chroot + path); log(path); + config->dependencyFiles.insert(llvm::CachedHashString(path)); - auto mbOrErr = MemoryBuffer::getFile(path, -1, false); + auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (auto ec = mbOrErr.getError()) { error("cannot open " + path + ": " + ec.message()); return None; @@ -272,6 +277,16 @@ std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec, } } +StringRef InputFile::getNameForScript() const { + if (archiveName.empty()) + return getName(); + + if (nameForScriptCache.empty()) + nameForScriptCache = (archiveName + Twine(':') + getName()).str(); + + return nameForScriptCache; +} + template DWARFCache *ObjFile::getDwarf() { llvm::call_once(initDwarf, [this]() { dwarf = std::make_unique(std::make_unique( @@ -346,9 +361,9 @@ template void ELFFileBase::init() { // Initialize trivial attributes. const ELFFile &obj = getObj(); - emachine = obj.getHeader()->e_machine; - osabi = obj.getHeader()->e_ident[llvm::ELF::EI_OSABI]; - abiVersion = obj.getHeader()->e_ident[llvm::ELF::EI_ABIVERSION]; + emachine = obj.getHeader().e_machine; + osabi = obj.getHeader().e_ident[llvm::ELF::EI_OSABI]; + abiVersion = obj.getHeader().e_ident[llvm::ELF::EI_ABIVERSION]; ArrayRef sections = CHECK(obj.sections(), this); @@ -376,7 +391,7 @@ template void ELFFileBase::init() { template uint32_t ObjFile::getSectionIndex(const Elf_Sym &sym) const { return CHECK( - this->getObj().getSectionIndex(&sym, getELFSyms(), shndxTable), + this->getObj().getSectionIndex(sym, getELFSyms(), shndxTable), this); } @@ -563,8 +578,7 @@ void ObjFile::initializeSections(bool ignoreComdats) { const Elf_Shdr &sec = objSections[i]; if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) - cgProfile = - check(obj.template getSectionContentsAsArray(&sec)); + cgProfileSectionIndex = i; // SHF_EXCLUDE'ed sections are discarded by the linker. However, // if -r is given, we'll let the final link discard such sections. @@ -578,8 +592,12 @@ void ObjFile::initializeSections(bool ignoreComdats) { if (sec.sh_link != 0) this->addrsigSec = &sec; else if (config->icf == ICFLevel::Safe) - warn(toString(this) + ": --icf=safe is incompatible with object " - "files created using objcopy or ld -r"); + warn(toString(this) + + ": --icf=safe conservatively ignores " + "SHT_LLVM_ADDRSIG [index " + + Twine(i) + + "] with sh_link=0 " + "(likely created using objcopy or ld -r)"); } this->sections[i] = &InputSection::discarded; continue; @@ -591,27 +609,20 @@ void ObjFile::initializeSections(bool ignoreComdats) { StringRef signature = getShtGroupSignature(objSections, sec); this->sections[i] = &InputSection::discarded; - ArrayRef entries = - CHECK(obj.template getSectionContentsAsArray(&sec), this); + CHECK(obj.template getSectionContentsAsArray(sec), this); if (entries.empty()) fatal(toString(this) + ": empty SHT_GROUP"); - // The first word of a SHT_GROUP section contains flags. Currently, - // the standard defines only "GRP_COMDAT" flag for the COMDAT group. - // An group with the empty flag doesn't define anything; such sections - // are just skipped. - if (entries[0] == 0) - continue; - - if (entries[0] != GRP_COMDAT) + Elf_Word flag = entries[0]; + if (flag && flag != GRP_COMDAT) fatal(toString(this) + ": unsupported SHT_GROUP format"); - bool isNew = - ignoreComdats || + bool keepGroup = + (flag & GRP_COMDAT) == 0 || ignoreComdats || symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this) .second; - if (isNew) { + if (keepGroup) { if (config->relocatable) this->sections[i] = createInputSection(sec); selectedGroups.push_back(entries); @@ -656,17 +667,19 @@ void ObjFile::initializeSections(bool ignoreComdats) { if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA) this->sections[i] = createInputSection(sec); - if (!(sec.sh_flags & SHF_LINK_ORDER)) + // A SHF_LINK_ORDER section with sh_link=0 is handled as if it did not have + // the flag. + if (!(sec.sh_flags & SHF_LINK_ORDER) || !sec.sh_link) continue; - // .ARM.exidx sections have a reverse dependency on the InputSection they - // have a SHF_LINK_ORDER dependency, this is identified by the sh_link. InputSectionBase *linkSec = nullptr; if (sec.sh_link < this->sections.size()) linkSec = this->sections[sec.sh_link]; if (!linkSec) fatal(toString(this) + ": invalid sh_link index: " + Twine(sec.sh_link)); + // A SHF_LINK_ORDER section is discarded if its linked-to section is + // discarded. InputSection *isec = cast(this->sections[i]); linkSec->dependentSections.push_back(isec); if (!isa(linkSec)) @@ -777,20 +790,21 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) { // of zero or more type-length-value fields. We want to find a field of a // certain type. It seems a bit too much to just store a 32-bit value, perhaps // the ABI is unnecessarily complicated. -template -static uint32_t readAndFeatures(ObjFile *obj, ArrayRef data) { +template static uint32_t readAndFeatures(const InputSection &sec) { using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; uint32_t featuresSet = 0; + ArrayRef data = sec.data(); + auto reportFatal = [&](const uint8_t *place, const char *msg) { + fatal(toString(sec.file) + ":(" + sec.name + "+0x" + + Twine::utohexstr(place - sec.data().data()) + "): " + msg); + }; while (!data.empty()) { // Read one NOTE record. - if (data.size() < sizeof(Elf_Nhdr)) - fatal(toString(obj) + ": .note.gnu.property: section too short"); - auto *nhdr = reinterpret_cast(data.data()); - if (data.size() < nhdr->getSize()) - fatal(toString(obj) + ": .note.gnu.property: section too short"); + if (data.size() < sizeof(Elf_Nhdr) || data.size() < nhdr->getSize()) + reportFatal(data.data(), "data is too short"); Elf_Note note(*nhdr); if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") { @@ -805,25 +819,26 @@ static uint32_t readAndFeatures(ObjFile *obj, ArrayRef data) { // Read a body of a NOTE record, which consists of type-length-value fields. ArrayRef desc = note.getDesc(); while (!desc.empty()) { + const uint8_t *place = desc.data(); if (desc.size() < 8) - fatal(toString(obj) + ": .note.gnu.property: section too short"); - - uint32_t type = read32le(desc.data()); - uint32_t size = read32le(desc.data() + 4); + reportFatal(place, "program property is too short"); + uint32_t type = read32(desc.data()); + uint32_t size = read32(desc.data() + 4); + desc = desc.slice(8); + if (desc.size() < size) + reportFatal(place, "program property is too short"); if (type == featureAndType) { // We found a FEATURE_1_AND field. There may be more than one of these // in a .note.gnu.property section, for a relocatable object we // accumulate the bits set. - featuresSet |= read32le(desc.data() + 8); + if (size < 4) + reportFatal(place, "FEATURE_1_AND entry is too short"); + featuresSet |= read32(desc.data()); } - // On 64-bit, a payload may be followed by a 4-byte padding to make its - // size a multiple of 8. - if (ELFT::Is64Bits) - size = alignTo(size, 8); - - desc = desc.slice(size + 8); // +8 for Type and Size + // Padding is present in the note descriptor, if necessary. + desc = desc.slice(alignTo<(ELFT::Is64Bits ? 8 : 4)>(size)); } // Go to next NOTE record to look for more FEATURE_1_AND descriptions. @@ -862,36 +877,58 @@ template InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { StringRef name = getSectionName(sec); - switch (sec.sh_type) { - case SHT_ARM_ATTRIBUTES: { - if (config->emachine != EM_ARM) - break; + if (config->emachine == EM_ARM && sec.sh_type == SHT_ARM_ATTRIBUTES) { ARMAttributeParser attributes; - ArrayRef contents = check(this->getObj().getSectionContents(&sec)); + ArrayRef contents = check(this->getObj().getSectionContents(sec)); if (Error e = attributes.parse(contents, config->ekind == ELF32LEKind ? support::little : support::big)) { auto *isec = make(*this, sec, name); warn(toString(isec) + ": " + llvm::toString(std::move(e))); - break; + } else { + updateSupportedARMFeatures(attributes); + updateARMVFPArgs(attributes, this); + + // FIXME: Retain the first attribute section we see. The eglibc ARM + // dynamic loaders require the presence of an attribute section for dlopen + // to work. In a full implementation we would merge all attribute + // sections. + if (in.attributes == nullptr) { + in.attributes = make(*this, sec, name); + return in.attributes; + } + return &InputSection::discarded; } - updateSupportedARMFeatures(attributes); - updateARMVFPArgs(attributes, this); - - // FIXME: Retain the first attribute section we see. The eglibc ARM - // dynamic loaders require the presence of an attribute section for dlopen - // to work. In a full implementation we would merge all attribute sections. - if (in.armAttributes == nullptr) { - in.armAttributes = make(*this, sec, name); - return in.armAttributes; + } + + if (config->emachine == EM_RISCV && sec.sh_type == SHT_RISCV_ATTRIBUTES) { + RISCVAttributeParser attributes; + ArrayRef contents = check(this->getObj().getSectionContents(sec)); + if (Error e = attributes.parse(contents, support::little)) { + auto *isec = make(*this, sec, name); + warn(toString(isec) + ": " + llvm::toString(std::move(e))); + } else { + // FIXME: Validate arch tag contains C if and only if EF_RISCV_RVC is + // present. + + // FIXME: Retain the first attribute section we see. Tools such as + // llvm-objdump make use of the attribute section to determine which + // standard extensions to enable. In a full implementation we would merge + // all attribute sections. + if (in.attributes == nullptr) { + in.attributes = make(*this, sec, name); + return in.attributes; + } + return &InputSection::discarded; } - return &InputSection::discarded; } + + switch (sec.sh_type) { case SHT_LLVM_DEPENDENT_LIBRARIES: { if (config->relocatable) break; ArrayRef data = - CHECK(this->getObj().template getSectionContentsAsArray(&sec), this); + CHECK(this->getObj().template getSectionContentsAsArray(sec), this); if (!data.empty() && data.back() != '\0') { error(toString(this) + ": corrupted dependent libraries section (unterminated string): " + @@ -926,48 +963,34 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { this->sections[sec.sh_info] = target; } - // This section contains relocation information. - // If -r is given, we do not interpret or apply relocation - // but just copy relocation sections to output. - if (config->relocatable) { - InputSection *relocSec = make(*this, sec, name); - // We want to add a dependency to target, similar like we do for - // -emit-relocs below. This is useful for the case when linker script - // contains the "/DISCARD/". It is perhaps uncommon to use a script with - // -r, but we faced it in the Linux kernel and have to handle such case - // and not to crash. - target->dependentSections.push_back(relocSec); - return relocSec; - } - if (target->firstRelocation) fatal(toString(this) + ": multiple relocation sections to one section are not supported"); if (sec.sh_type == SHT_RELA) { - ArrayRef rels = CHECK(getObj().relas(&sec), this); + ArrayRef rels = CHECK(getObj().relas(sec), this); target->firstRelocation = rels.begin(); target->numRelocations = rels.size(); target->areRelocsRela = true; } else { - ArrayRef rels = CHECK(getObj().rels(&sec), this); + ArrayRef rels = CHECK(getObj().rels(sec), this); target->firstRelocation = rels.begin(); target->numRelocations = rels.size(); target->areRelocsRela = false; } assert(isUInt<31>(target->numRelocations)); - // Relocation sections processed by the linker are usually removed - // from the output, so returning `nullptr` for the normal case. - // However, if -emit-relocs is given, we need to leave them in the output. - // (Some post link analysis tools need this information.) - if (config->emitRelocs) { - InputSection *relocSec = make(*this, sec, name); - // We will not emit relocation section if target was discarded. - target->dependentSections.push_back(relocSec); - return relocSec; - } - return nullptr; + // Relocation sections are usually removed from the output, so return + // `nullptr` for the normal case. However, if -r or --emit-relocs is + // specified, we need to copy them to the output. (Some post link analysis + // tools specify --emit-relocs to obtain the information.) + if (!config->relocatable && !config->emitRelocs) + return nullptr; + InputSection *relocSec = make(*this, sec, name); + // If the relocated section is discarded (due to /DISCARD/ or + // --gc-sections), the relocation section should be discarded as well. + target->dependentSections.push_back(relocSec); + return relocSec; } } @@ -996,8 +1019,7 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { // .note.gnu.property containing a single AND'ed bitmap, we discard an input // file's .note.gnu.property section. if (name == ".note.gnu.property") { - ArrayRef contents = check(this->getObj().getSectionContents(&sec)); - this->andFeatures = readAndFeatures(this, contents); + this->andFeatures = readAndFeatures(InputSection(*this, sec, name)); return &InputSection::discarded; } @@ -1052,7 +1074,7 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { template StringRef ObjFile::getSectionName(const Elf_Shdr &sec) { - return CHECK(getObj().getSectionName(&sec, sectionStringTable), this); + return CHECK(getObj().getSectionName(sec, sectionStringTable), this); } // Initialize this->Symbols. this->Symbols is a parallel array as @@ -1109,6 +1131,7 @@ template void ObjFile::initializeSymbols() { } // Symbol resolution of non-local symbols. + SmallVector undefineds; for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; uint8_t binding = eSym.getBinding(); @@ -1125,8 +1148,7 @@ template void ObjFile::initializeSymbols() { // Handle global undefined symbols. if (eSym.st_shndx == SHN_UNDEF) { - this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type}); - this->symbols[i]->referenced = true; + undefineds.push_back(i); continue; } @@ -1173,6 +1195,20 @@ template void ObjFile::initializeSymbols() { fatal(toString(this) + ": unexpected binding: " + Twine((int)binding)); } + + // Undefined symbols (excluding those defined relative to non-prevailing + // sections) can trigger recursive fetch. Process defined symbols first so + // that the relative order between a defined symbol and an undefined symbol + // does not change the 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 Elf_Sym &eSym = eSyms[i]; + StringRefZ name = this->stringTable.data() + eSym.st_name; + this->symbols[i]->resolve(Undefined{this, name, eSym.getBinding(), + eSym.st_other, eSym.getType()}); + this->symbols[i]->referenced = true; + } } ArchiveFile::ArchiveFile(std::unique_ptr &&file) @@ -1212,6 +1248,89 @@ void ArchiveFile::fetch(const Archive::Symbol &sym) { parseFile(file); } +// The handling of tentative definitions (COMMON symbols) in archives is murky. +// A tentative definition will be promoted to a global definition if there are +// no non-tentative definitions to dominate it. When we hold a tentative +// definition to a symbol and are inspecting archive members for inclusion +// there are 2 ways we can proceed: +// +// 1) Consider the tentative definition a 'real' definition (ie promotion from +// tentative to real definition has already happened) and not inspect +// archive members for Global/Weak definitions to replace the tentative +// definition. An archive member would only be included if it satisfies some +// other undefined symbol. This is the behavior Gold uses. +// +// 2) Consider the tentative definition as still undefined (ie the promotion to +// a real definition happens only after all symbol resolution is done). +// The linker searches archive members for STB_GLOBAL definitions to +// replace the tentative definition with. This is the behavior used by +// GNU ld. +// +// The second behavior is inherited from SysVR4, which based it on the FORTRAN +// COMMON BLOCK model. This behavior is needed for proper initialization in old +// (pre F90) FORTRAN code that is packaged into an archive. +// +// The following functions search archive members for definitions to replace +// tentative definitions (implementing behavior 2). +static bool isBitcodeNonCommonDef(MemoryBufferRef mb, StringRef symName, + StringRef archiveName) { + IRSymtabFile symtabFile = check(readIRSymtab(mb)); + for (const irsymtab::Reader::SymbolRef &sym : + symtabFile.TheReader.symbols()) { + if (sym.isGlobal() && sym.getName() == symName) + return !sym.isUndefined() && !sym.isWeak() && !sym.isCommon(); + } + return false; +} + +template +static bool isNonCommonDef(MemoryBufferRef mb, StringRef symName, + StringRef archiveName) { + ObjFile *obj = make>(mb, archiveName); + StringRef stringtable = obj->getStringTable(); + + for (auto sym : obj->template getGlobalELFSyms()) { + Expected name = sym.getName(stringtable); + if (name && name.get() == symName) + return sym.isDefined() && sym.getBinding() == STB_GLOBAL && + !sym.isCommon(); + } + return false; +} + +static bool isNonCommonDef(MemoryBufferRef mb, StringRef symName, + StringRef archiveName) { + switch (getELFKind(mb, archiveName)) { + case ELF32LEKind: + return isNonCommonDef(mb, symName, archiveName); + case ELF32BEKind: + return isNonCommonDef(mb, symName, archiveName); + case ELF64LEKind: + return isNonCommonDef(mb, symName, archiveName); + case ELF64BEKind: + return isNonCommonDef(mb, symName, archiveName); + default: + llvm_unreachable("getELFKind"); + } +} + +bool ArchiveFile::shouldFetchForCommon(const Archive::Symbol &sym) { + Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member for symbol " + + toELFString(sym)); + MemoryBufferRef mb = + CHECK(c.getMemoryBufferRef(), + toString(this) + + ": could not get the buffer for the member defining symbol " + + toELFString(sym)); + + if (isBitcode(mb)) + return isBitcodeNonCommonDef(mb, sym.getName(), getName()); + + return isNonCommonDef(mb, sym.getName(), getName()); +} + size_t ArchiveFile::getMemberCount() const { size_t count = 0; Error err = Error::success(); @@ -1266,7 +1385,7 @@ std::vector SharedFile::parseVerneed(const ELFFile &obj, if (!sec) return {}; std::vector verneeds; - ArrayRef data = CHECK(obj.getSectionContents(sec), this); + ArrayRef data = CHECK(obj.getSectionContents(*sec), this); const uint8_t *verneedBuf = data.begin(); for (unsigned i = 0; i != sec->sh_info; ++i) { if (verneedBuf + sizeof(typename ELFT::Verneed) > data.end()) @@ -1342,7 +1461,7 @@ template void SharedFile::parse() { continue; case SHT_DYNAMIC: dynamicTags = - CHECK(obj.template getSectionContentsAsArray(&sec), this); + CHECK(obj.template getSectionContentsAsArray(sec), this); break; case SHT_GNU_versym: versymSec = &sec; @@ -1401,7 +1520,7 @@ template void SharedFile::parse() { std::vector versyms(size, VER_NDX_GLOBAL); if (versymSec) { ArrayRef versym = - CHECK(obj.template getSectionContentsAsArray(versymSec), + CHECK(obj.template getSectionContentsAsArray(*versymSec), this) .slice(firstGlobal); for (size_t i = 0; i < size; ++i) @@ -1448,6 +1567,9 @@ template void SharedFile::parse() { Symbol *s = symtab->addSymbol( Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); s->exportDynamic = true; + if (s->isUndefined() && !s->isWeak() && + config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore) + requiredSymbols.push_back(s); continue; } @@ -1494,9 +1616,10 @@ static ELFKind getBitcodeELFKind(const Triple &t) { return t.isArch64Bit() ? ELF64BEKind : ELF32BEKind; } -static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { +static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { switch (t.getArch()) { case Triple::aarch64: + case Triple::aarch64_be: return EM_AARCH64; case Triple::amdgcn: case Triple::r600: @@ -1514,6 +1637,7 @@ static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { case Triple::msp430: return EM_MSP430; case Triple::ppc: + case Triple::ppcle: return EM_PPC; case Triple::ppc64: case Triple::ppc64le: @@ -1532,6 +1656,19 @@ static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { } } +static uint8_t getOsAbi(const Triple &t) { + switch (t.getOS()) { + case Triple::AMDHSA: + return ELF::ELFOSABI_AMDGPU_HSA; + case Triple::AMDPAL: + return ELF::ELFOSABI_AMDGPU_PAL; + case Triple::Mesa3D: + return ELF::ELFOSABI_AMDGPU_MESA3D; + default: + return ELF::ELFOSABI_NONE; + } +} + BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, uint64_t offsetInArchive) : InputFile(BitcodeKind, mb) { @@ -1559,6 +1696,7 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, Triple t(obj->getTargetTriple()); ekind = getBitcodeELFKind(t); emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t); + osabi = getOsAbi(t); } static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { @@ -1606,9 +1744,12 @@ static Symbol *createBitcodeSymbol(const std::vector &keptComdats, template void BitcodeFile::parse() { std::vector keptComdats; - for (StringRef s : obj->getComdatTable()) + for (std::pair s : obj->getComdatTable()) { keptComdats.push_back( - symtab->comdatGroups.try_emplace(CachedHashStringRef(s), this).second); + s.second == Comdat::NoDeduplicate || + symtab->comdatGroups.try_emplace(CachedHashStringRef(s.first), this) + .second); + } for (const lto::InputFile::Symbol &objSym : obj->symbols()) symbols.push_back(createBitcodeSymbol(keptComdats, objSym, *this)); @@ -1732,6 +1873,13 @@ template void LazyObjFile::parse() { } } +bool LazyObjFile::shouldFetchForCommon(const StringRef &name) { + if (isBitcode(mb)) + return isBitcodeNonCommonDef(mb, name, archiveName); + + return isNonCommonDef(mb, name, archiveName); +} + std::string elf::replaceThinLTOSuffix(StringRef path) { StringRef suffix = config->thinLTOObjectSuffixReplace.first; StringRef repl = config->thinLTOObjectSuffixReplace.second; diff --git a/gnu/llvm/lld/ELF/InputFiles.h b/gnu/llvm/lld/ELF/InputFiles.h index 7af85e417ca..bd72cfcdd05 100644 --- a/gnu/llvm/lld/ELF/InputFiles.h +++ b/gnu/llvm/lld/ELF/InputFiles.h @@ -92,9 +92,11 @@ public: return symbols; } - // Filename of .a which contained this file. If this file was - // not in an archive file, it is the empty string. We use this - // string for creating error messages. + // Get filename to use for linker script processing. + StringRef getNameForScript() const; + + // If not empty, this stores the name of the archive containing this file. + // We use this string for creating error messages. std::string archiveName; // If this is an architecture-specific file, the following members @@ -128,6 +130,10 @@ public: // [.got, .got + 0xFFFC]. bool ppc64SmallCodeModelTocRelocs = false; + // True if the file has TLSGD/TLSLD GOT relocations without R_PPC64_TLSGD or + // R_PPC64_TLSLD. Disable TLS relaxation to avoid bad code generation. + bool ppc64DisableTLSRelax = false; + // groupId is used for --warn-backrefs which is an optional error // checking feature. All files within the same --{start,end}-group or // --{start,end}-lib get the same group ID. Otherwise, each file gets a new @@ -147,6 +153,9 @@ protected: private: const Kind fileKind; + + // Cache for getNameForScript(). + mutable std::string nameForScriptCache; }; class ELFFileBase : public InputFile { @@ -180,12 +189,7 @@ protected: // .o file. template class ObjFile : public ELFFileBase { - using Elf_Rel = typename ELFT::Rel; - using Elf_Rela = typename ELFT::Rela; - using Elf_Sym = typename ELFT::Sym; - using Elf_Shdr = typename ELFT::Shdr; - using Elf_Word = typename ELFT::Word; - using Elf_CGProfile = typename ELFT::CGProfile; + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) public: static bool classof(const InputFile *f) { return f->kind() == ObjKind; } @@ -245,8 +249,8 @@ public: // Pointer to this input file's .llvm_addrsig section, if it has one. const Elf_Shdr *addrsigSec = nullptr; - // SHT_LLVM_CALL_GRAPH_PROFILE table - ArrayRef cgProfile; + // SHT_LLVM_CALL_GRAPH_PROFILE section index. + uint32_t cgProfileSectionIndex = 0; // Get cached DWARF information. DWARFCache *getDwarf(); @@ -307,6 +311,10 @@ public: template void parse(); void fetch(); + // Check if a non-common symbol should be fetched to override a common + // definition. + bool shouldFetchForCommon(const StringRef &name); + bool fetched = false; private: @@ -326,6 +334,10 @@ public: // more than once.) void fetch(const Archive::Symbol &sym); + // Check if a non-common symbol should be fetched to override a common + // definition. + bool shouldFetchForCommon(const Archive::Symbol &sym); + size_t getMemberCount() const; size_t getFetchedMemberCount() const { return seen.size(); } @@ -369,12 +381,13 @@ public: template void parse(); - // Used for --no-allow-shlib-undefined. - bool allNeededIsKnown; - // Used for --as-needed bool isNeeded; + // Non-weak undefined symbols which are not yet resolved when the SO is + // parsed. Only filled for `--no-allow-shlib-undefined`. + std::vector requiredSymbols; + private: template std::vector parseVerneed(const llvm::object::ELFFile &obj, diff --git a/gnu/llvm/lld/ELF/InputSection.h b/gnu/llvm/lld/ELF/InputSection.h index 112c6ab49a3..c914d0b4215 100644 --- a/gnu/llvm/lld/ELF/InputSection.h +++ b/gnu/llvm/lld/ELF/InputSection.h @@ -52,15 +52,15 @@ public: // this but instead this->Repl. SectionBase *repl; - unsigned sectionKind : 3; + uint8_t sectionKind : 3; // The next two bit fields are only used by InputSectionBase, but we // put them here so the struct packs better. - unsigned bss : 1; + uint8_t bss : 1; // Set for sections that should not be folded by ICF. - unsigned keepUnique : 1; + uint8_t keepUnique : 1; // 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 @@ -134,6 +134,10 @@ public: // and shrinking a section. unsigned bytesDropped = 0; + // 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 push_back(uint64_t num) { @@ -210,17 +214,13 @@ public: // The native ELF reloc data type is not very convenient to handle. // So we convert ELF reloc records to our own records in Relocations.cpp. // This vector contains such "cooked" relocations. - std::vector relocations; - - // Indicates that this section needs to be padded with a NOP filler if set to - // true. - bool nopFiller = false; + 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. - std::vector jumpInstrMods; + SmallVector jumpInstrMods; // A function compiled with -fsplit-stack calling a function // compiled without -fsplit-stack needs its prologue adjusted. Find @@ -238,6 +238,7 @@ public: } protected: + template void parseCompressedHeader(); void uncompress() const; @@ -314,7 +315,7 @@ struct EhSectionPiece { unsigned firstRelocation) : inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {} - ArrayRef data() { + ArrayRef data() const { return {sec->data().data() + this->inputOff, size}; } @@ -390,8 +391,15 @@ 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 + inline bool isDebugSection(const InputSectionBase &sec) { - return sec.name.startswith(".debug") || sec.name.startswith(".zdebug"); + return (sec.flags & llvm::ELF::SHF_ALLOC) == 0 && + (sec.name.startswith(".debug") || sec.name.startswith(".zdebug")); } // The list of all input sections. diff --git a/gnu/llvm/lld/ELF/LTO.cpp b/gnu/llvm/lld/ELF/LTO.cpp index b8041afed6c..e8710e3bdb4 100644 --- a/gnu/llvm/lld/ELF/LTO.cpp +++ b/gnu/llvm/lld/ELF/LTO.cpp @@ -57,6 +57,19 @@ static std::unique_ptr openFile(StringRef file) { return ret; } +// The merged bitcode after LTO is large. Try opening a file stream that +// supports reading, seeking and writing. Such a file allows BitcodeWriter to +// flush buffered data to reduce memory consumption. If this fails, open a file +// stream that supports only write. +static std::unique_ptr openLTOOutputFile(StringRef file) { + std::error_code ec; + std::unique_ptr fs = + std::make_unique(file, ec); + if (!ec) + return fs; + return openFile(file); +} + static std::string getThinLTOOutputFile(StringRef modulePath) { return lto::getThinLTOOutputFile( std::string(modulePath), std::string(config->thinLTOPrefixReplace.first), @@ -76,7 +89,7 @@ static lto::Config createConfig() { c.Options.DataSections = true; // Check if basic block sections must be used. - // Allowed values for --lto-basicblock-sections are "all", "labels", + // Allowed values for --lto-basic-block-sections are "all", "labels", // "", or none. This is the equivalent // of -fbasic-block-sections= flag in clang. if (!config->ltoBasicBlockSections.empty()) { @@ -99,6 +112,7 @@ static lto::Config createConfig() { } } + c.Options.PseudoProbeForProfiling = config->ltoPseudoProbeForProfiling; c.Options.UniqueBasicBlockSectionNames = config->ltoUniqueBasicBlockSectionNames; @@ -130,6 +144,7 @@ static lto::Config createConfig() { c.RemarksFilename = std::string(config->optRemarksFilename); c.RemarksPasses = std::string(config->optRemarksPasses); c.RemarksWithHotness = config->optRemarksWithHotness; + c.RemarksHotnessThreshold = config->optRemarksHotnessThreshold; c.RemarksFormat = std::string(config->optRemarksFormat); c.SampleProfile = std::string(config->ltoSampleProfile); @@ -151,7 +166,8 @@ static lto::Config createConfig() { if (config->emitLLVM) { c.PostInternalizeModuleHook = [](size_t task, const Module &m) { - if (std::unique_ptr os = openFile(config->outputFile)) + if (std::unique_ptr os = + openLTOOutputFile(config->outputFile)) WriteBitcodeToFile(m, *os, false); return false; }; @@ -231,6 +247,11 @@ void BitcodeCompiler::add(BitcodeFile &f) { r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj || (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); const auto *dr = dyn_cast(sym); r.FinalDefinitionInLinkageUnit = (isExec || sym->visibility != STV_DEFAULT) && dr && diff --git a/gnu/llvm/lld/ELF/LinkerScript.h b/gnu/llvm/lld/ELF/LinkerScript.h index 4a1a5fd71b6..d2487ae0f9d 100644 --- a/gnu/llvm/lld/ELF/LinkerScript.h +++ b/gnu/llvm/lld/ELF/LinkerScript.h @@ -29,6 +29,7 @@ namespace lld { namespace elf { class Defined; +class InputFile; class InputSection; class InputSectionBase; class OutputSection; @@ -146,19 +147,32 @@ struct MemoryRegion { // This struct represents one section match pattern in SECTIONS() command. // It can optionally have negative match pattern for EXCLUDED_FILE command. // Also it may be surrounded with SORT() command, so contains sorting rules. -struct SectionPattern { +class SectionPattern { + StringMatcher excludedFilePat; + + // Cache of the most recent input argument and result of excludesFile(). + mutable llvm::Optional> excludesFileCache; + +public: SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2) : excludedFilePat(pat1), sectionPat(pat2), sortOuter(SortSectionPolicy::Default), sortInner(SortSectionPolicy::Default) {} - StringMatcher excludedFilePat; + bool excludesFile(const InputFile *file) const; + StringMatcher sectionPat; SortSectionPolicy sortOuter; SortSectionPolicy sortInner; }; -struct InputSectionDescription : BaseCommand { +class InputSectionDescription : public BaseCommand { + SingleStringMatcher filePat; + + // Cache of the most recent input argument and result of matchesFile(). + mutable llvm::Optional> matchesFileCache; + +public: InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0, uint64_t withoutFlags = 0) : BaseCommand(InputSectionKind), filePat(filePattern), @@ -168,7 +182,7 @@ struct InputSectionDescription : BaseCommand { return c->kind == InputSectionKind; } - SingleStringMatcher filePat; + bool matchesFile(const InputFile *file) const; // Input sections that matches at least one of SectionPatterns // will be associated with this InputSectionDescription. @@ -213,7 +227,7 @@ struct ByteCommand : BaseCommand { }; struct InsertCommand { - OutputSection *os; + std::vector names; bool isAfter; StringRef where; }; @@ -233,11 +247,11 @@ class LinkerScript final { // not be used outside of the scope of a call to the above functions. struct AddressState { AddressState(); - uint64_t threadBssOffset = 0; OutputSection *outSec = nullptr; MemoryRegion *memRegion = nullptr; MemoryRegion *lmaRegion = nullptr; uint64_t lmaOffset = 0; + uint64_t tbssAddr = 0; }; llvm::DenseMap nameToOutputSection; @@ -329,6 +343,9 @@ public: // to be reordered. std::vector insertCommands; + // OutputSections specified by OVERWRITE_SECTIONS. + std::vector overwriteSections; + // Sections that will be warned/errored by --orphan-handling. std::vector orphanSections; }; diff --git a/gnu/llvm/lld/ELF/MapFile.cpp b/gnu/llvm/lld/ELF/MapFile.cpp index 12cffead1f8..239c6c39484 100644 --- a/gnu/llvm/lld/ELF/MapFile.cpp +++ b/gnu/llvm/lld/ELF/MapFile.cpp @@ -29,6 +29,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -142,6 +143,8 @@ 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); diff --git a/gnu/llvm/lld/ELF/MarkLive.cpp b/gnu/llvm/lld/ELF/MarkLive.cpp index 28e13e8c123..e828429b421 100644 --- a/gnu/llvm/lld/ELF/MarkLive.cpp +++ b/gnu/llvm/lld/ELF/MarkLive.cpp @@ -56,7 +56,7 @@ private: void mark(); template - void resolveReloc(InputSectionBase &sec, RelTy &rel, bool isLSDA); + void resolveReloc(InputSectionBase &sec, RelTy &rel, bool fromFDE); template void scanEhFrameSection(EhInputSection &eh, ArrayRef rels); @@ -65,7 +65,7 @@ private: unsigned partition; // A list of sections to visit. - SmallVector queue; + 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. @@ -89,7 +89,7 @@ static uint64_t getAddend(InputSectionBase &sec, template template void MarkLive::resolveReloc(InputSectionBase &sec, RelTy &rel, - bool isLSDA) { + bool fromFDE) { Symbol &sym = sec.getFile()->getRelocTargetSym(rel); // If a symbol is referenced in a live section, it is used. @@ -104,7 +104,16 @@ void MarkLive::resolveReloc(InputSectionBase &sec, RelTy &rel, if (d->isSection()) offset += getAddend(sec, rel); - if (!isLSDA || !(relSec->flags & SHF_EXECINSTR)) + // fromFDE being true means this is referenced by a FDE in a .eh_frame + // piece. The relocation points to the described function or to a LSDA. We + // only need to keep the LSDA live, so ignore anything that points to + // executable sections. If the LSDA is in a section group or has the + // SHF_LINK_ORDER flag, we ignore the relocation as well because (a) if the + // associated text section is live, the LSDA will be retained due to section + // group/SHF_LINK_ORDER rules (b) if the associated text section should be + // discarded, marking the LSDA will unnecessarily retain the text section. + if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) || + relSec->nextInSectionGroup))) enqueue(relSec, offset); return; } @@ -148,13 +157,10 @@ void MarkLive::scanEhFrameSection(EhInputSection &eh, continue; } - // This is a FDE. The relocations point to the described function or to - // a LSDA. We only need to keep the LSDA alive, so ignore anything that - // points to executable sections. uint64_t pieceEnd = piece.inputOff + piece.size; - for (size_t j = firstRelI, end2 = rels.size(); j < end2; ++j) - if (rels[j].r_offset < pieceEnd) - resolveReloc(eh, rels[j], true); + for (size_t j = firstRelI, end2 = rels.size(); + j < end2 && rels[j].r_offset < pieceEnd; ++j) + resolveReloc(eh, rels[j], true); } } @@ -255,12 +261,20 @@ template void MarkLive::run() { scanEhFrameSection(*eh, eh->template rels()); } + if (sec->flags & SHF_GNU_RETAIN) { + enqueue(sec, 0); + continue; + } if (sec->flags & SHF_LINK_ORDER) continue; if (isReserved(sec) || script->shouldKeep(sec)) { enqueue(sec, 0); - } else if (isValidCIdentifier(sec->name)) { + } else if ((!config->zStartStopGC || sec->name.startswith("__libc_")) && + isValidCIdentifier(sec->name)) { + // 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); } @@ -339,16 +353,16 @@ template void elf::markLive() { // 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 and SHT_REL/SHT_RELA sections. + // 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.). + // 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. @@ -370,8 +384,11 @@ template void elf::markLive() { bool isLinkOrder = (sec->flags & SHF_LINK_ORDER); bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA); - if (!isAlloc && !isLinkOrder && !isRel && !sec->nextInSectionGroup) + if (!isAlloc && !isLinkOrder && !isRel && !sec->nextInSectionGroup) { sec->markLive(); + for (InputSection *isec : sec->dependentSections) + isec->markLive(); + } } // Follow the graph to mark all live sections. diff --git a/gnu/llvm/lld/ELF/OutputSections.cpp b/gnu/llvm/lld/ELF/OutputSections.cpp index 881c375a115..088d1cdc65e 100644 --- a/gnu/llvm/lld/ELF/OutputSections.cpp +++ b/gnu/llvm/lld/ELF/OutputSections.cpp @@ -20,7 +20,9 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/SHA1.h" +#include "llvm/Support/TimeProfiler.h" #include +#include using namespace llvm; using namespace llvm::dwarf; @@ -284,6 +286,8 @@ template void OutputSection::maybeCompress() { !name.startswith(".debug_")) return; + llvm::TimeTraceScope timeScope("Compress debug sections"); + // Create a section header. zDebugHeader.resize(sizeof(Elf_Chdr)); auto *hdr = reinterpret_cast(zDebugHeader.data()); @@ -380,6 +384,15 @@ static void finalizeShtGroup(OutputSection *os, // provides signature of the section group. ArrayRef symbols = section->file->getSymbols(); os->info = in.symTab->getSymbolIndex(symbols[section->info]); + + // 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; + ArrayRef sections = section->file->getSections(); + for (const uint32_t &idx : section->getDataAs().slice(1)) + if (OutputSection *osec = sections[read32(&idx)]->getOutputSection()) + seen.insert(osec->sectionIndex); + os->size = (1 + seen.size()) * sizeof(uint32_t); } void OutputSection::finalize() { @@ -405,7 +418,11 @@ void OutputSection::finalize() { if (!config->copyRelocs || (type != SHT_RELA && type != SHT_REL)) return; - if (isa(first)) + // Skip if 'first' is synthetic, i.e. not a section created by --emit-relocs. + // Normally 'type' was changed by 'first' so 'first' should be non-null. + // However, if the output section is .rela.dyn, 'type' can be set by the empty + // synthetic .rela.plt and first can be null. + if (!first || isa(first)) return; link = in.symTab->getParent()->sectionIndex; @@ -434,19 +451,19 @@ static bool isCrtend(StringRef s) { return std::regex_match(s.begin(), s.end(), re); } -// .ctors and .dtors are sorted by this priority from highest to lowest. -// -// 1. The section was contained in crtbegin (crtbegin contains -// some sentinel value in its .ctors and .dtors so that the runtime -// can find the beginning of the sections.) +// .ctors and .dtors are sorted by this order: // -// 2. The section has an optional priority value in the form of ".ctors.N" -// or ".dtors.N" where N is a number. Unlike .{init,fini}_array, -// they are compared as string rather than number. +// 1. .ctors/.dtors in crtbegin (which contains a sentinel value -1). +// 2. The section is named ".ctors" or ".dtors" (priority: 65536). +// 3. The section has an optional priority value in the form of ".ctors.N" or +// ".dtors.N" where N is a number in the form of %05u (priority: 65535-N). +// 4. .ctors/.dtors in crtend (which contains a sentinel value 0). // -// 3. The section is just ".ctors" or ".dtors". -// -// 4. The section was contained in crtend, which contains an end marker. +// For 2 and 3, the sections are sorted by priority from high to low, e.g. +// .ctors (65536), .ctors.00100 (65436), .ctors.00200 (65336). In GNU ld's +// internal linker scripts, the sorting is by string comparison which can +// achieve the same goal given the optional priority values are of the same +// length. // // In an ideal world, we don't need this function because .init_array and // .ctors are duplicate features (and .init_array is newer.) However, there @@ -461,13 +478,7 @@ static bool compCtors(const InputSection *a, const InputSection *b) { bool endB = isCrtend(b->file->getName()); if (endA != endB) return endB; - StringRef x = a->name; - StringRef y = b->name; - assert(x.startswith(".ctors") || x.startswith(".dtors")); - assert(y.startswith(".ctors") || y.startswith(".dtors")); - x = x.substr(6); - y = y.substr(6); - return x < y; + return getPriority(a->name) > getPriority(b->name); } // Sorts input sections by the special rules for .ctors and .dtors. @@ -479,16 +490,17 @@ void OutputSection::sortCtorsDtors() { llvm::stable_sort(isd->sections, compCtors); } -// If an input string is in the form of "foo.N" where N is a number, -// return N. Otherwise, returns 65536, which is one greater than the -// lowest priority. +// If an input string is in the form of "foo.N" where N is a number, return N +// (65535-N if .ctors.N or .dtors.N). Otherwise, returns 65536, which is one +// greater than the lowest priority. int elf::getPriority(StringRef s) { size_t pos = s.rfind('.'); if (pos == StringRef::npos) return 65536; - int v; - if (!to_integer(s.substr(pos + 1), v, 10)) - return 65536; + int v = 65536; + if (to_integer(s.substr(pos + 1), v, 10) && + (pos == 6 && (s.startswith(".ctors") || s.startswith(".dtors")))) + v = 65535 - v; return v; } @@ -527,6 +539,41 @@ std::array OutputSection::getFiller() { return {0, 0, 0, 0}; } +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) { + // 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 + // sections created during linking. + const auto *sec = dyn_cast(sections[i]); + if (!sec) + return; + for (const DynamicReloc &rel : sec->relocs) { + int64_t addend = rel.computeAddend(); + const OutputSection *relOsec = rel.inputSec->getOutputSection(); + assert(relOsec != nullptr && "missing output section for relocation"); + const uint8_t *relocTarget = + bufStart + relOsec->offset + rel.inputSec->getOffset(rel.offsetInSec); + // For SHT_NOBITS the written addend is always zero. + int64_t writtenAddend = + relOsec->type == SHT_NOBITS + ? 0 + : target->getImplicitAddend(relocTarget, rel.type); + if (addend != writtenAddend) + internalLinkerError( + getErrorLocation(relocTarget), + "wrote incorrect addend value 0x" + utohexstr(writtenAddend) + + " instead of 0x" + utohexstr(addend) + + " for dynamic relocation " + toString(rel.type) + + " at offset 0x" + utohexstr(rel.getOffset()) + + (rel.sym ? " against symbol " + toString(*rel.sym) : "")); + } + }); +} + template void OutputSection::writeHeaderTo(ELF32LE::Shdr *Shdr); template void OutputSection::writeHeaderTo(ELF32BE::Shdr *Shdr); template void OutputSection::writeHeaderTo(ELF64LE::Shdr *Shdr); diff --git a/gnu/llvm/lld/ELF/OutputSections.h b/gnu/llvm/lld/ELF/OutputSections.h index d5686f11ec8..a0f80661438 100644 --- a/gnu/llvm/lld/ELF/OutputSections.h +++ b/gnu/llvm/lld/ELF/OutputSections.h @@ -102,6 +102,8 @@ public: void finalize(); template void writeTo(uint8_t *buf); + // Check that the addends for dynamic relocations were written correctly. + void checkDynRelAddends(const uint8_t *bufStart); template void maybeCompress(); void sort(llvm::function_ref order); @@ -111,7 +113,7 @@ public: private: // Used for implementation of --compress-debug-sections option. std::vector zDebugHeader; - llvm::SmallVector compressedData; + llvm::SmallVector compressedData; std::array getFiller(); }; @@ -135,12 +137,6 @@ struct Out { static OutputSection *finiArray; }; -} // namespace elf -} // namespace lld - -namespace lld { -namespace elf { - uint64_t getHeaderSize(); extern std::vector outputSections; diff --git a/gnu/llvm/lld/ELF/Relocations.h b/gnu/llvm/lld/ELF/Relocations.h index ec59c63410d..a702aac183a 100644 --- a/gnu/llvm/lld/ELF/Relocations.h +++ b/gnu/llvm/lld/ELF/Relocations.h @@ -41,7 +41,6 @@ enum RelExpr { R_GOTPLT, R_GOTPLTREL, R_GOTREL, - R_NEG_TLS, R_NONE, R_PC, R_PLT, @@ -58,7 +57,8 @@ enum RelExpr { R_RELAX_TLS_LD_TO_LE, R_RELAX_TLS_LD_TO_LE_ABS, R_SIZE, - R_TLS, + R_TPREL, + R_TPREL_NEG, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC, @@ -78,6 +78,7 @@ enum RelExpr { // of a relocation type, there are some relocations whose semantics are // unique to a target. Such relocation are marked with R_. R_AARCH64_GOT_PAGE_PC, + R_AARCH64_GOT_PAGE, R_AARCH64_PAGE_PC, R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, R_AARCH64_TLSDESC_PAGE, @@ -96,6 +97,7 @@ enum RelExpr { R_PPC64_CALL_PLT, R_PPC64_RELAX_TOC, R_PPC64_TOCBASE, + R_PPC64_RELAX_GOT_PC, R_RISCV_ADD, R_RISCV_PC_INDIRECT, }; @@ -130,7 +132,7 @@ bool hexagonNeedsTLSSymbol(ArrayRef outputSections); class ThunkSection; class Thunk; -struct InputSectionDescription; +class InputSectionDescription; class ThunkCreator { public: @@ -146,8 +148,8 @@ private: void mergeThunks(ArrayRef outputSections); ThunkSection *getISDThunkSec(OutputSection *os, InputSection *isec, - InputSectionDescription *isd, uint32_t type, - uint64_t src); + InputSectionDescription *isd, + const Relocation &rel, uint64_t src); ThunkSection *getISThunkSec(InputSection *isec); @@ -194,6 +196,19 @@ template static inline int64_t getAddend(const typename ELFT::Rela &rel) { return rel.r_addend; } + +template +ArrayRef sortRels(ArrayRef rels, SmallVector &storage) { + auto cmp = [](const RelTy &a, const RelTy &b) { + return a.r_offset < b.r_offset; + }; + if (!llvm::is_sorted(rels, cmp)) { + storage.assign(rels.begin(), rels.end()); + llvm::stable_sort(storage, cmp); + rels = storage; + } + return rels; +} } // namespace elf } // namespace lld diff --git a/gnu/llvm/lld/ELF/ScriptLexer.cpp b/gnu/llvm/lld/ELF/ScriptLexer.cpp index 9ac8447eef0..236a188324c 100644 --- a/gnu/llvm/lld/ELF/ScriptLexer.cpp +++ b/gnu/llvm/lld/ELF/ScriptLexer.cpp @@ -56,7 +56,25 @@ size_t ScriptLexer::getLineNumber() { return 1; StringRef s = getCurrentMB().getBuffer(); StringRef tok = tokens[pos - 1]; - return s.substr(0, tok.data() - s.data()).count('\n') + 1; + const size_t tokOffset = tok.data() - s.data(); + + // For the first token, or when going backwards, start from the beginning of + // the buffer. If this token is after the previous token, start from the + // previous token. + size_t line = 1; + size_t start = 0; + if (lastLineNumberOffset > 0 && tokOffset >= lastLineNumberOffset) { + start = lastLineNumberOffset; + line = lastLineNumber; + } + + line += s.substr(start, tokOffset - start).count('\n'); + + // Store the line number of this token for reuse. + lastLineNumberOffset = tokOffset; + lastLineNumber = line; + + return line; } // Returns 0-based column number of the current token. @@ -146,7 +164,7 @@ StringRef ScriptLexer::skipSpace(StringRef s) { if (s.startswith("/*")) { size_t e = s.find("*/", 2); if (e == StringRef::npos) { - error("unclosed comment in a linker script"); + setError("unclosed comment in a linker script"); return ""; } s = s.substr(e + 2); diff --git a/gnu/llvm/lld/ELF/ScriptLexer.h b/gnu/llvm/lld/ELF/ScriptLexer.h index 306d428e98f..405fc735cbe 100644 --- a/gnu/llvm/lld/ELF/ScriptLexer.h +++ b/gnu/llvm/lld/ELF/ScriptLexer.h @@ -24,7 +24,7 @@ public: void setError(const Twine &msg); void tokenize(MemoryBufferRef mb); - static StringRef skipSpace(StringRef s); + StringRef skipSpace(StringRef s); bool atEOF(); StringRef next(); StringRef peek(); @@ -40,6 +40,9 @@ public: bool inExpr = false; size_t pos = 0; + size_t lastLineNumber = 0; + size_t lastLineNumberOffset = 0; + protected: MemoryBufferRef getCurrentMB(); diff --git a/gnu/llvm/lld/ELF/ScriptParser.cpp b/gnu/llvm/lld/ELF/ScriptParser.cpp index fea6b7a274e..1c743fd4774 100644 --- a/gnu/llvm/lld/ELF/ScriptParser.cpp +++ b/gnu/llvm/lld/ELF/ScriptParser.cpp @@ -29,8 +29,10 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TimeProfiler.h" #include #include #include @@ -75,6 +77,7 @@ private: void readOutput(); void readOutputArch(); void readOutputFormat(); + void readOverwriteSections(); void readPhdrs(); void readRegionAlias(); void readSearchDir(); @@ -100,6 +103,7 @@ private: uint64_t withFlags, uint64_t withoutFlags); unsigned readPhdrType(); + SortSectionPolicy peekSortKind(); SortSectionPolicy readSortKind(); SymbolAssignment *readProvideHidden(bool provide, bool hidden); SymbolAssignment *readAssignment(StringRef tok); @@ -248,6 +252,8 @@ void ScriptParser::readLinkerScript() { readOutputArch(); } else if (tok == "OUTPUT_FORMAT") { readOutputFormat(); + } else if (tok == "OVERWRITE_SECTIONS") { + readOverwriteSections(); } else if (tok == "PHDRS") { readPhdrs(); } else if (tok == "REGION_ALIAS") { @@ -282,10 +288,11 @@ void ScriptParser::addFile(StringRef s) { if (isUnderSysroot && s.startswith("/")) { SmallString<128> pathData; StringRef path = (config->sysroot + s).toStringRef(pathData); - if (sys::fs::exists(path)) { + if (sys::fs::exists(path)) driver->addFile(saver.save(path), /*withLOption=*/false); - return; - } + else + setError("cannot find " + s + " inside " + config->sysroot); + return; } if (s.startswith("/")) { @@ -409,7 +416,9 @@ static std::pair parseBfdName(StringRef s) { .Case("elf32-x86-64", {ELF32LEKind, EM_X86_64}) .Case("elf64-aarch64", {ELF64LEKind, EM_AARCH64}) .Case("elf64-littleaarch64", {ELF64LEKind, EM_AARCH64}) + .Case("elf64-bigaarch64", {ELF64BEKind, EM_AARCH64}) .Case("elf32-powerpc", {ELF32BEKind, EM_PPC}) + .Case("elf32-powerpcle", {ELF32LEKind, EM_PPC}) .Case("elf64-powerpc", {ELF64BEKind, EM_PPC64}) .Case("elf64-powerpcle", {ELF64LEKind, EM_PPC64}) .Case("elf64-x86-64", {ELF64LEKind, EM_X86_64}) @@ -422,16 +431,30 @@ static std::pair parseBfdName(StringRef s) { .Case("elf32-littleriscv", {ELF32LEKind, EM_RISCV}) .Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV}) .Case("elf64-sparc", {ELF64BEKind, EM_SPARCV9}) + .Case("elf32-msp430", {ELF32LEKind, EM_MSP430}) .Default({ELFNoneKind, EM_NONE}); } -// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little). -// Currently we ignore big and little parameters. +// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(default, big, little). Choose +// big if -EB is specified, little if -EL is specified, or default if neither is +// specified. void ScriptParser::readOutputFormat() { expect("("); + StringRef s; config->bfdname = unquote(next()); - StringRef s = config->bfdname; + if (!consume(")")) { + expect(","); + s = unquote(next()); + if (config->optEB) + config->bfdname = s; + expect(","); + s = unquote(next()); + if (config->optEL) + config->bfdname = s; + consume(")"); + } + s = config->bfdname; if (s.consume_back("-freebsd")) config->osabi = ELFOSABI_FREEBSD; @@ -440,14 +463,8 @@ void ScriptParser::readOutputFormat() { setError("unknown output format name: " + config->bfdname); if (s == "elf32-ntradlittlemips" || s == "elf32-ntradbigmips") config->mipsN32Abi = true; - - if (consume(")")) - return; - expect(","); - skip(); - expect(","); - skip(); - expect(")"); + if (config->emachine == EM_MSP430) + config->osabi = ELFOSABI_STANDALONE; } void ScriptParser::readPhdrs() { @@ -540,6 +557,12 @@ std::vector ScriptParser::readOverlay() { return v; } +void ScriptParser::readOverwriteSections() { + expect("{"); + while (!errorCount() && !consume("}")) + script->overwriteSections.push_back(readOutputSectionDescription(next())); +} + void ScriptParser::readSections() { expect("{"); std::vector v; @@ -573,9 +596,12 @@ void ScriptParser::readSections() { else if (!consume("BEFORE")) setError("expected AFTER/BEFORE, but got '" + next() + "'"); StringRef where = next(); + std::vector names; for (BaseCommand *cmd : v) if (auto *os = dyn_cast(cmd)) - script->insertCommands.push_back({os, isAfter, where}); + names.push_back(os->name); + if (!names.empty()) + script->insertCommands.push_back({std::move(names), isAfter, where}); } void ScriptParser::readTarget() { @@ -616,16 +642,20 @@ StringMatcher ScriptParser::readFilePatterns() { return Matcher; } +SortSectionPolicy ScriptParser::peekSortKind() { + return StringSwitch(peek()) + .Cases("SORT", "SORT_BY_NAME", SortSectionPolicy::Name) + .Case("SORT_BY_ALIGNMENT", SortSectionPolicy::Alignment) + .Case("SORT_BY_INIT_PRIORITY", SortSectionPolicy::Priority) + .Case("SORT_NONE", SortSectionPolicy::None) + .Default(SortSectionPolicy::Default); +} + SortSectionPolicy ScriptParser::readSortKind() { - if (consume("SORT") || consume("SORT_BY_NAME")) - return SortSectionPolicy::Name; - if (consume("SORT_BY_ALIGNMENT")) - return SortSectionPolicy::Alignment; - if (consume("SORT_BY_INIT_PRIORITY")) - return SortSectionPolicy::Priority; - if (consume("SORT_NONE")) - return SortSectionPolicy::None; - return SortSectionPolicy::Default; + SortSectionPolicy ret = peekSortKind(); + if (ret != SortSectionPolicy::Default) + skip(); + return ret; } // Reads SECTIONS command contents in the following form: @@ -651,11 +681,15 @@ std::vector ScriptParser::readInputSectionsList() { } StringMatcher SectionMatcher; - while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE") + // Break if the next token is ), EXCLUDE_FILE, or SORT*. + while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE" && + peekSortKind() == SortSectionPolicy::Default) SectionMatcher.addPattern(unquote(next())); if (!SectionMatcher.empty()) ret.push_back({std::move(excludeFilePat), std::move(SectionMatcher)}); + else if (excludeFilePat.empty()) + break; else setError("section pattern is expected"); } @@ -967,6 +1001,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) { } SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) { + name = unquote(name); StringRef op = next(); assert(op == "=" || op == "+="); Expr e = readExpr(); @@ -1096,24 +1131,24 @@ Expr ScriptParser::readConstant() { static Optional parseInt(StringRef tok) { // Hexadecimal uint64_t val; - if (tok.startswith_lower("0x")) { + if (tok.startswith_insensitive("0x")) { if (!to_integer(tok.substr(2), val, 16)) return None; return val; } - if (tok.endswith_lower("H")) { + if (tok.endswith_insensitive("H")) { if (!to_integer(tok.drop_back(), val, 16)) return None; return val; } // Decimal - if (tok.endswith_lower("K")) { + if (tok.endswith_insensitive("K")) { if (!to_integer(tok.drop_back(), val, 10)) return None; return val * 1024; } - if (tok.endswith_lower("M")) { + if (tok.endswith_insensitive("M")) { if (!to_integer(tok.drop_back(), val, 10)) return None; return val * 1024 * 1024; @@ -1213,6 +1248,13 @@ static void checkIfExists(OutputSection *cmd, StringRef location) { error(location + ": undefined section " + cmd->name); } +static bool isValidSymbolName(StringRef s) { + auto valid = [](char c) { + return isAlnum(c) || c == '$' || c == '.' || c == '_'; + }; + return !s.empty() && !isDigit(s[0]) && llvm::all_of(s, valid); +} + Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); @@ -1309,8 +1351,11 @@ Expr ScriptParser::readPrimary() { return [=] { return alignTo(script->getDot(), e().getValue()); }; } if (tok == "DEFINED") { - StringRef name = readParenLiteral(); - return [=] { return symtab->find(name) ? 1 : 0; }; + StringRef name = unquote(readParenLiteral()); + return [=] { + Symbol *b = symtab->find(name); + return (b && b->isDefined()) ? 1 : 0; + }; } if (tok == "LENGTH") { StringRef name = readParenLiteral(); @@ -1329,6 +1374,15 @@ Expr ScriptParser::readPrimary() { return cmd->getLMA(); }; } + if (tok == "LOG2CEIL") { + expect("("); + Expr a = readExpr(); + expect(")"); + return [=] { + // LOG2CEIL(0) is defined to be 0. + return llvm::Log2_64_Ceil(std::max(a().getValue(), UINT64_C(1))); + }; + } if (tok == "MAX" || tok == "MIN") { expect("("); Expr a = readExpr(); @@ -1375,7 +1429,8 @@ Expr ScriptParser::readPrimary() { return [=] { return *val; }; // Tok is a symbol name. - if (!isValidCIdentifier(tok)) + tok = unquote(tok); + if (!isValidSymbolName(tok)) setError("malformed number: " + tok); script->referencedSymbols.push_back(tok); return [=] { return script->getSymbolValue(tok, location); }; @@ -1441,9 +1496,9 @@ void ScriptParser::readAnonymousDeclaration() { std::vector globals; std::tie(locals, globals) = readSymbols(); for (const SymbolVersion &pat : locals) - config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat); + config->versionDefinitions[VER_NDX_LOCAL].localPatterns.push_back(pat); for (const SymbolVersion &pat : globals) - config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(pat); + config->versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back(pat); expect(";"); } @@ -1455,13 +1510,12 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) { std::vector locals; std::vector globals; std::tie(locals, globals) = readSymbols(); - for (const SymbolVersion &pat : locals) - config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat); // Create a new version definition and add that to the global symbols. VersionDefinition ver; ver.name = verStr; - ver.patterns = globals; + ver.nonLocalPatterns = std::move(globals); + ver.localPatterns = std::move(locals); ver.id = config->versionDefinitions.size(); config->versionDefinitions.push_back(ver); @@ -1607,17 +1661,23 @@ std::pair ScriptParser::readMemoryAttributes() { } void elf::readLinkerScript(MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read linker script", + mb.getBufferIdentifier()); ScriptParser(mb).readLinkerScript(); } void elf::readVersionScript(MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read version script", + mb.getBufferIdentifier()); ScriptParser(mb).readVersionScript(); } void elf::readDynamicList(MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read dynamic list", mb.getBufferIdentifier()); ScriptParser(mb).readDynamicList(); } void elf::readDefsym(StringRef name, MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read defsym input", name); ScriptParser(mb).readDefsym(name); } diff --git a/gnu/llvm/lld/ELF/SymbolTable.cpp b/gnu/llvm/lld/ELF/SymbolTable.cpp index afc8b05f876..22e6b4f9289 100644 --- a/gnu/llvm/lld/ELF/SymbolTable.cpp +++ b/gnu/llvm/lld/ELF/SymbolTable.cpp @@ -42,6 +42,8 @@ void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { if (real->exportDynamic) sym->exportDynamic = true; + if (!real->isUsedInRegularObj && sym->isUndefined()) + sym->isUsedInRegularObj = false; // Now renaming is complete, and no one refers to real. We drop real from // .symtab and .dynsym. If real is undefined, it is important that we don't @@ -132,9 +134,20 @@ static bool canBeVersioned(const Symbol &sym) { StringMap> &SymbolTable::getDemangledSyms() { if (!demangledSyms) { demangledSyms.emplace(); + std::string demangled; for (Symbol *sym : symVector) - if (canBeVersioned(*sym)) - (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym); + if (canBeVersioned(*sym)) { + StringRef name = sym->getName(); + size_t pos = name.find('@'); + if (pos == std::string::npos) + demangled = demangleItanium(name); + else if (pos + 1 == name.size() || name[pos + 1] == '@') + demangled = demangleItanium(name.substr(0, pos)); + else + demangled = + (demangleItanium(name.substr(0, pos)) + name.substr(pos)).str(); + (*demangledSyms)[demangled].push_back(sym); + } } return *demangledSyms; } @@ -148,19 +161,29 @@ std::vector SymbolTable::findByVersion(SymbolVersion ver) { return {}; } -std::vector SymbolTable::findAllByVersion(SymbolVersion ver) { +std::vector SymbolTable::findAllByVersion(SymbolVersion ver, + bool includeNonDefault) { std::vector res; SingleStringMatcher m(ver.name); + auto check = [&](StringRef name) { + size_t pos = name.find('@'); + if (!includeNonDefault) + return pos == StringRef::npos; + return !(pos + 1 < name.size() && name[pos + 1] == '@'); + }; if (ver.isExternCpp) { for (auto &p : getDemangledSyms()) if (m.match(p.first())) - res.insert(res.end(), p.second.begin(), p.second.end()); + for (Symbol *sym : p.second) + if (check(sym->getName())) + res.push_back(sym); return res; } for (Symbol *sym : symVector) - if (canBeVersioned(*sym) && m.match(sym->getName())) + if (canBeVersioned(*sym) && check(sym->getName()) && + m.match(sym->getName())) res.push_back(sym); return res; } @@ -170,7 +193,7 @@ void SymbolTable::handleDynamicList() { for (SymbolVersion &ver : config->dynamicList) { std::vector syms; if (ver.hasWildcard) - syms = findAllByVersion(ver); + syms = findAllByVersion(ver, /*includeNonDefault=*/true); else syms = findByVersion(ver); @@ -179,21 +202,13 @@ void SymbolTable::handleDynamicList() { } } -// Set symbol versions to symbols. This function handles patterns -// containing no wildcard characters. -void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, - StringRef versionName) { - if (ver.hasWildcard) - return; - +// Set symbol versions to symbols. This function handles patterns containing no +// wildcard characters. Return false if no symbol definition matches ver. +bool SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, + StringRef versionName, + bool includeNonDefault) { // Get a list of symbols which we need to assign the version to. std::vector syms = findByVersion(ver); - if (syms.empty()) { - if (!config->undefinedVersion) - error("version script assignment of '" + versionName + "' to symbol '" + - ver.name + "' failed: symbol not defined"); - return; - } auto getName = [](uint16_t ver) -> std::string { if (ver == VER_NDX_LOCAL) @@ -205,10 +220,11 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, // Assign the version. for (Symbol *sym : syms) { - // Skip symbols containing version info because symbol versions - // specified by symbol names take precedence over version scripts. - // See parseSymbolVersion(). - if (sym->getName().contains('@')) + // For a non-local versionId, skip symbols containing version info because + // symbol versions specified by symbol names take precedence over version + // scripts. See parseSymbolVersion(). + if (!includeNonDefault && versionId != VER_NDX_LOCAL && + sym->getName().contains('@')) continue; // If the version has not been assigned, verdefIndex is -1. Use an arbitrary @@ -223,13 +239,15 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, warn("attempt to reassign symbol '" + ver.name + "' of " + getName(sym->versionId) + " to " + getName(versionId)); } + return !syms.empty(); } -void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) { +void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId, + bool includeNonDefault) { // Exact matching takes precedence over fuzzy matching, // so we set a version to a symbol only if no version has been assigned // to the symbol. This behavior is compatible with GNU. - for (Symbol *sym : findAllByVersion(ver)) + for (Symbol *sym : findAllByVersion(ver, includeNonDefault)) if (sym->verdefIndex == UINT32_C(-1)) { sym->verdefIndex = 0; sym->versionId = versionId; @@ -242,26 +260,60 @@ void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) { // script file, the script does not actually define any symbol version, // but just specifies symbols visibilities. void SymbolTable::scanVersionScript() { + SmallString<128> buf; // First, we assign versions to exact matching symbols, // i.e. version definitions not containing any glob meta-characters. - for (VersionDefinition &v : config->versionDefinitions) - for (SymbolVersion &pat : v.patterns) - assignExactVersion(pat, v.id, v.name); + std::vector syms; + for (VersionDefinition &v : config->versionDefinitions) { + auto assignExact = [&](SymbolVersion pat, uint16_t id, StringRef ver) { + bool found = + assignExactVersion(pat, id, ver, /*includeNonDefault=*/false); + buf.clear(); + found |= assignExactVersion({(pat.name + "@" + v.name).toStringRef(buf), + pat.isExternCpp, /*hasWildCard=*/false}, + id, ver, /*includeNonDefault=*/true); + if (!found && !config->undefinedVersion) + errorOrWarn("version script assignment of '" + ver + "' to symbol '" + + pat.name + "' failed: symbol not defined"); + }; + for (SymbolVersion &pat : v.nonLocalPatterns) + if (!pat.hasWildcard) + assignExact(pat, v.id, v.name); + for (SymbolVersion pat : v.localPatterns) + if (!pat.hasWildcard) + assignExact(pat, VER_NDX_LOCAL, "local"); + } // Next, assign versions to wildcards that are not "*". Note that because the // last match takes precedence over previous matches, we iterate over the // definitions in the reverse order. - for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) - for (SymbolVersion &pat : v.patterns) + auto assignWildcard = [&](SymbolVersion pat, uint16_t id, StringRef ver) { + assignWildcardVersion(pat, id, /*includeNonDefault=*/false); + buf.clear(); + assignWildcardVersion({(pat.name + "@" + ver).toStringRef(buf), + pat.isExternCpp, /*hasWildCard=*/true}, + id, + /*includeNonDefault=*/true); + }; + for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) { + for (SymbolVersion &pat : v.nonLocalPatterns) if (pat.hasWildcard && pat.name != "*") - assignWildcardVersion(pat, v.id); + assignWildcard(pat, v.id, v.name); + for (SymbolVersion &pat : v.localPatterns) + if (pat.hasWildcard && pat.name != "*") + assignWildcard(pat, VER_NDX_LOCAL, v.name); + } // Then, assign versions to "*". In GNU linkers they have lower priority than // other wildcards. - for (VersionDefinition &v : config->versionDefinitions) - for (SymbolVersion &pat : v.patterns) + for (VersionDefinition &v : config->versionDefinitions) { + for (SymbolVersion &pat : v.nonLocalPatterns) if (pat.hasWildcard && pat.name == "*") - assignWildcardVersion(pat, v.id); + assignWildcard(pat, v.id, v.name); + for (SymbolVersion &pat : v.localPatterns) + if (pat.hasWildcard && pat.name == "*") + assignWildcard(pat, VER_NDX_LOCAL, v.name); + } // Symbol themselves might know their versions because symbols // can contain versions in the form of @. diff --git a/gnu/llvm/lld/ELF/SymbolTable.h b/gnu/llvm/lld/ELF/SymbolTable.h index 507af8d2be7..54c4b1169ed 100644 --- a/gnu/llvm/lld/ELF/SymbolTable.h +++ b/gnu/llvm/lld/ELF/SymbolTable.h @@ -65,12 +65,14 @@ public: private: std::vector findByVersion(SymbolVersion ver); - std::vector findAllByVersion(SymbolVersion ver); + std::vector findAllByVersion(SymbolVersion ver, + bool includeNonDefault); llvm::StringMap> &getDemangledSyms(); - void assignExactVersion(SymbolVersion ver, uint16_t versionId, - StringRef versionName); - void assignWildcardVersion(SymbolVersion ver, uint16_t versionId); + bool assignExactVersion(SymbolVersion ver, uint16_t versionId, + StringRef versionName, bool includeNonDefault); + void assignWildcardVersion(SymbolVersion ver, uint16_t versionId, + bool includeNonDefault); // The order the global symbols are in is not defined. We can use an arbitrary // order, but it has to be reproducible. That is true even when cross linking. diff --git a/gnu/llvm/lld/ELF/SyntheticSections.h b/gnu/llvm/lld/ELF/SyntheticSections.h index 8ed82ba64a6..bc24922598f 100644 --- a/gnu/llvm/lld/ELF/SyntheticSections.h +++ b/gnu/llvm/lld/ELF/SyntheticSections.h @@ -23,6 +23,7 @@ #include "DWARF.h" #include "EhFrame.h" #include "InputSection.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Endian.h" @@ -88,6 +89,8 @@ public: std::vector getFdeData() const; ArrayRef getCieRecords() const { return cieRecords; } + template + void iterateFDEWithLSDA(llvm::function_ref fn); private: // This is used only when parsing EhInputSection. We keep it here to avoid @@ -98,14 +101,17 @@ private: template void addRecords(EhInputSection *s, llvm::ArrayRef rels); - template - void addSectionAux(EhInputSection *s); + template void addSectionAux(EhInputSection *s); + template + void iterateFDEWithLSDAAux(EhInputSection &sec, ArrayRef rels, + llvm::DenseSet &ciesWithLSDA, + llvm::function_ref fn); template CieRecord *addCie(EhSectionPiece &piece, ArrayRef rels); template - bool isFdeLive(EhSectionPiece &piece, ArrayRef rels); + Defined *isFdeLive(EhSectionPiece &piece, ArrayRef rels); uint64_t getFdePc(uint8_t *buf, size_t off, uint8_t enc) const; @@ -419,48 +425,73 @@ private: class DynamicReloc { public: + enum Kind { + /// The resulting dynamic relocation does not reference a symbol (#sym must + /// be nullptr) and uses #addend as the result of computeAddend(). + AddendOnly, + /// The resulting dynamic relocation will not reference a symbol: #sym is + /// only used to compute the addend with InputSection::getRelocTargetVA(). + /// Useful for various relative and TLS relocations (e.g. R_X86_64_TPOFF64). + AddendOnlyWithTargetVA, + /// The resulting dynamic relocation references symbol #sym from the dynamic + /// symbol table and uses #addend as the value of computeAddend(). + AgainstSymbol, + /// The resulting dynamic relocation references symbol #sym from the dynamic + /// symbol table and uses InputSection::getRelocTargetVA() + #addend for the + /// final addend. It can be used for relocations that write the symbol VA as + // the addend (e.g. R_MIPS_TLS_TPREL64) but still reference the symbol. + AgainstSymbolWithTargetVA, + /// This is used by the MIPS multi-GOT implementation. It relocates + /// addresses of 64kb pages that lie inside the output section. + MipsMultiGotPage, + }; + /// This constructor records a relocation against a symbol. DynamicReloc(RelType type, const InputSectionBase *inputSec, - uint64_t offsetInSec, bool useSymVA, Symbol *sym, int64_t addend) - : type(type), sym(sym), inputSec(inputSec), offsetInSec(offsetInSec), - useSymVA(useSymVA), addend(addend), outputSec(nullptr) {} - // This constructor records dynamic relocation settings used by MIPS - // multi-GOT implementation. It's to relocate addresses of 64kb pages - // lie inside the output section. + uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend, + RelExpr expr) + : type(type), sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec), + kind(kind), expr(expr), addend(addend) {} + /// This constructor records a relative relocation with no symbol. + DynamicReloc(RelType type, const InputSectionBase *inputSec, + uint64_t offsetInSec, int64_t addend = 0) + : type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec), + kind(AddendOnly), expr(R_ADDEND), addend(addend) {} + /// This constructor records dynamic relocation settings used by the MIPS + /// multi-GOT implementation. DynamicReloc(RelType type, const InputSectionBase *inputSec, uint64_t offsetInSec, const OutputSection *outputSec, int64_t addend) : type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec), - useSymVA(false), addend(addend), outputSec(outputSec) {} + kind(MipsMultiGotPage), expr(R_ADDEND), addend(addend), + outputSec(outputSec) {} uint64_t getOffset() const; uint32_t getSymIndex(SymbolTableBaseSection *symTab) const; + bool needsDynSymIndex() const { + return kind == AgainstSymbol || kind == AgainstSymbolWithTargetVA; + } - // Computes the addend of the dynamic relocation. Note that this is not the - // same as the addend member variable as it also includes the symbol address - // if useSymVA is true. + /// Computes the addend of the dynamic relocation. Note that this is not the + /// same as the #addend member variable as it may also include the symbol + /// address/the address of the corresponding GOT entry/etc. int64_t computeAddend() const; RelType type; - Symbol *sym; - const InputSectionBase *inputSec = nullptr; + const InputSectionBase *inputSec; uint64_t offsetInSec; - // If this member is true, the dynamic relocation will not be against the - // symbol but will instead be a relative relocation that simply adds the - // load address. This means we need to write the symbol virtual address - // plus the original addend as the final relocation addend. - bool useSymVA; + +private: + Kind kind; + // The kind of expression used to calculate the added (required e.g. for + // relative GOT relocations). + RelExpr expr; int64_t addend; - const OutputSection *outputSec; + const OutputSection *outputSec = nullptr; }; template class DynamicSection final : public SyntheticSection { - using Elf_Dyn = typename ELFT::Dyn; - using Elf_Rel = typename ELFT::Rel; - using Elf_Rela = typename ELFT::Rela; - using Elf_Relr = typename ELFT::Relr; - using Elf_Shdr = typename ELFT::Shdr; - using Elf_Sym = typename ELFT::Sym; + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) // finalizeContents() fills this vector with the section contents. std::vector>> entries; @@ -487,18 +518,37 @@ class RelocationBaseSection : public SyntheticSection { public: RelocationBaseSection(StringRef name, uint32_t type, int32_t dynamicTag, int32_t sizeDynamicTag); - void addReloc(RelType dynType, InputSectionBase *isec, uint64_t offsetInSec, - Symbol *sym); - // Add a dynamic relocation that might need an addend. This takes care of - // writing the addend to the output section if needed. - void addReloc(RelType dynType, InputSectionBase *inputSec, - uint64_t offsetInSec, Symbol *sym, int64_t addend, RelExpr expr, - RelType type); + /// Add a dynamic relocation without writing an addend to the output section. + /// This overload can be used if the addends are written directly instead of + /// using relocations on the input section (e.g. MipsGotSection::writeTo()). void addReloc(const DynamicReloc &reloc); + /// Add a dynamic relocation against \p sym with an optional addend. + void addSymbolReloc(RelType dynType, InputSectionBase *isec, + uint64_t offsetInSec, Symbol &sym, int64_t addend = 0, + llvm::Optional addendRelType = llvm::None); + /// Add a relative dynamic relocation that uses the target address of \p sym + /// (i.e. InputSection::getRelocTargetVA()) + \p addend as the addend. + void addRelativeReloc(RelType dynType, InputSectionBase *isec, + uint64_t offsetInSec, Symbol &sym, int64_t addend, + RelType addendRelType, RelExpr expr); + /// Add a dynamic relocation using the target address of \p sym as the addend + /// if \p sym is non-preemptible. Otherwise add a relocation against \p sym. + void addAddendOnlyRelocIfNonPreemptible(RelType dynType, + InputSectionBase *isec, + uint64_t offsetInSec, Symbol &sym, + RelType addendRelType); + void addReloc(DynamicReloc::Kind kind, RelType dynType, + InputSectionBase *inputSec, uint64_t offsetInSec, Symbol &sym, + int64_t addend, RelExpr expr, RelType addendRelType); bool isNeeded() const override { return !relocs.empty(); } size_t getSize() const override { return relocs.size() * this->entsize; } size_t getRelativeRelocCount() const { return numRelativeRelocs; } void finalizeContents() override; + static bool classof(const SectionBase *d) { + return SyntheticSection::classof(d) && + (d->type == llvm::ELF::SHT_RELA || d->type == llvm::ELF::SHT_REL || + d->type == llvm::ELF::SHT_RELR); + } int32_t dynamicTag, sizeDynamicTag; std::vector relocs; @@ -1194,7 +1244,7 @@ inline Partition &SectionBase::getPartition() const { // Linker generated sections which can be used as inputs and are not specific to // a partition. struct InStruct { - InputSection *armAttributes; + InputSection *attributes; BssSection *bss; BssSection *bssRelRo; GotSection *got; diff --git a/gnu/llvm/lld/ELF/Target.cpp b/gnu/llvm/lld/ELF/Target.cpp index 6abd8b452e2..d3e54f7387d 100644 --- a/gnu/llvm/lld/ELF/Target.cpp +++ b/gnu/llvm/lld/ELF/Target.cpp @@ -130,6 +130,8 @@ ErrorPlace elf::getErrorPlace(const uint8_t *loc) { TargetInfo::~TargetInfo() {} int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const { + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; } @@ -150,11 +152,15 @@ bool TargetInfo::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { return true; } -RelExpr TargetInfo::adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const { +RelExpr TargetInfo::adjustTlsExpr(RelType type, RelExpr expr) const { return expr; } +RelExpr TargetInfo::adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *data) const { + 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"); diff --git a/gnu/llvm/lld/ELF/Target.h b/gnu/llvm/lld/ELF/Target.h index 47905ae64a4..1fe3217c6d1 100644 --- a/gnu/llvm/lld/ELF/Target.h +++ b/gnu/llvm/lld/ELF/Target.h @@ -122,6 +122,7 @@ public: RelType tlsGotRel; RelType tlsModuleIndexRel; RelType tlsOffsetRel; + unsigned gotEntrySize = config->wordsize; unsigned pltEntrySize; unsigned pltHeaderSize; unsigned ipltEntrySize; @@ -148,8 +149,9 @@ public: // non-split-stack callee this will return true. Otherwise returns false. bool needsMoreStackNonSplit = true; - virtual RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const; + 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, @@ -213,6 +215,11 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther); // 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. +void writePrefixedInstruction(uint8_t *loc, uint64_t insn); + void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); @@ -224,6 +231,8 @@ template bool isMipsPIC(const Defined *sym); void reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v, int64_t min, uint64_t max); +void reportRangeError(uint8_t *loc, int64_t v, int n, const Symbol &sym, + const Twine &msg); // Make sure that V can be represented as an N bit signed integer. inline void checkInt(uint8_t *loc, int64_t v, int n, const Relocation &rel) { diff --git a/gnu/llvm/lld/ELF/Thunks.cpp b/gnu/llvm/lld/ELF/Thunks.cpp index ea74d343ebb..dbc476ffeeb 100644 --- a/gnu/llvm/lld/ELF/Thunks.cpp +++ b/gnu/llvm/lld/ELF/Thunks.cpp @@ -72,7 +72,7 @@ public: // if the target is in range, otherwise it creates a long thunk. class ARMThunk : public Thunk { public: - ARMThunk(Symbol &dest) : Thunk(dest, 0) {} + ARMThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} bool getMayUseShortThunk(); uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); } @@ -102,7 +102,9 @@ private: // which has a range of 16MB. class ThumbThunk : public Thunk { public: - ThumbThunk(Symbol &dest) : Thunk(dest, 0) { alignment = 2; } + ThumbThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) { + alignment = 2; + } bool getMayUseShortThunk(); uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); } @@ -125,7 +127,7 @@ private: // Source State, TargetState, Target Requirement, ABS or PI, Range class ARMV7ABSLongThunk final : public ARMThunk { public: - ARMV7ABSLongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV7ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } void writeLong(uint8_t *buf) override; @@ -134,7 +136,7 @@ public: class ARMV7PILongThunk final : public ARMThunk { public: - ARMV7PILongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV7PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; @@ -143,7 +145,8 @@ public: class ThumbV7ABSLongThunk final : public ThumbThunk { public: - ThumbV7ABSLongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV7ABSLongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 10; } void writeLong(uint8_t *buf) override; @@ -152,7 +155,7 @@ public: class ThumbV7PILongThunk final : public ThumbThunk { public: - ThumbV7PILongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV7PILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } void writeLong(uint8_t *buf) override; @@ -166,7 +169,7 @@ public: // can result in a thunk class ARMV5ABSLongThunk final : public ARMThunk { public: - ARMV5ABSLongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV5ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 8; } void writeLong(uint8_t *buf) override; @@ -177,7 +180,7 @@ public: class ARMV5PILongThunk final : public ARMThunk { public: - ARMV5PILongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV5PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; @@ -189,7 +192,8 @@ public: // Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted class ThumbV6MABSLongThunk final : public ThumbThunk { public: - ThumbV6MABSLongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV6MABSLongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } void writeLong(uint8_t *buf) override; @@ -198,7 +202,8 @@ public: class ThumbV6MPILongThunk final : public ThumbThunk { public: - ThumbV6MPILongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV6MPILongThunk(Symbol &dest, int64_t addend) + : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; @@ -277,6 +282,8 @@ public: uint32_t size() override { return 20; } void writeTo(uint8_t *buf) override; void addSymbols(ThunkSection &isec) override; + bool isCompatibleWith(const InputSection &isec, + const Relocation &rel) const override; }; // PPC64 R2 Save Stub @@ -287,15 +294,71 @@ public: // 2) Tail calls the callee. class PPC64R2SaveStub final : public Thunk { public: - PPC64R2SaveStub(Symbol &dest) : Thunk(dest, 0) {} - uint32_t size() override { return 8; } + PPC64R2SaveStub(Symbol &dest, int64_t addend) : Thunk(dest, addend) { + alignment = 16; + } + + // To prevent oscillations in layout when moving from short to long thunks + // we make sure that once a thunk has been set to long it cannot go back. + bool getMayUseShortThunk() { + if (!mayUseShortThunk) + return false; + if (!isInt<26>(computeOffset())) { + mayUseShortThunk = false; + return false; + } + return true; + } + uint32_t size() override { return getMayUseShortThunk() ? 8 : 32; } + void writeTo(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; + bool isCompatibleWith(const InputSection &isec, + const Relocation &rel) const override; + +private: + // Transitioning from long to short can create layout oscillations in + // certain corner cases which would prevent the layout from converging. + // This is similar to the handling for ARMThunk. + bool mayUseShortThunk = true; + int64_t computeOffset() const { + return destination.getVA() - (getThunkTargetSym()->getVA() + 4); + } +}; + +// PPC64 R12 Setup Stub +// When a caller that does not maintain a toc-pointer performs a local call to +// a callee which requires a toc-pointer then we need this stub to place the +// callee's global entry point into r12 without a save of R2. +class PPC64R12SetupStub final : public Thunk { +public: + PPC64R12SetupStub(Symbol &dest) : Thunk(dest, 0) { 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; +}; + +// PPC64 PC-relative PLT Stub +// When a caller that does not maintain a toc-pointer performs an extern call +// then this stub is needed for: +// 1) Loading the target functions address from the procedure linkage table into +// r12 for use by the target functions global entry point, and into the count +// register with pc-relative instructions. +// 2) Transferring control to the target function through an indirect branch. +class PPC64PCRelPLTStub final : public Thunk { +public: + PPC64PCRelPLTStub(Symbol &dest) : Thunk(dest, 0) { 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; }; // 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 call offset is -// larger then that we need to emit a long-branch thunk. The target address +// larger than that we need to emit a long-branch thunk. The target address // of the callee is stored in a table to be accessed TOC-relative. Since the // call must be local (a non-local call will have a PltCallStub instead) the // table stores the address of the callee's local entry point. For @@ -303,9 +366,11 @@ public: // used. class PPC64LongBranchThunk : public Thunk { public: - uint32_t size() override { return 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; protected: PPC64LongBranchThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} @@ -318,10 +383,10 @@ public: assert(!dest.isPreemptible); if (Optional index = in.ppc64LongBranchTarget->addEntry(&dest, addend)) { - mainPart->relaDyn->addReloc( - {target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8), - true, &dest, - addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther)}); + mainPart->relaDyn->addRelativeReloc( + target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8), + dest, addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther), + target->symbolicRel, R_ABS); } } }; @@ -334,6 +399,24 @@ 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, @@ -836,13 +919,48 @@ void PPC64PltCallStub::addSymbols(ThunkSection &isec) { s->file = destination.file; } +bool PPC64PltCallStub::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; +} + void PPC64R2SaveStub::writeTo(uint8_t *buf) { - int64_t offset = destination.getVA() - (getThunkTargetSym()->getVA() + 4); + const int64_t offset = computeOffset(); + write32(buf + 0, 0xf8410018); // std r2,24(r1) // The branch offset needs to fit in 26 bits. - if (!isInt<26>(offset)) - fatal("R2 save stub branch offset is too large: " + Twine(offset)); - write32(buf + 0, 0xf8410018); // std r2,24(r1) - write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b + if (getMayUseShortThunk()) { + 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 + nextInstOffset = 12; + } + write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12 + write32(buf + nextInstOffset + 4, BCTR); // bctr + } else { + in.ppc64LongBranchTarget->addEntry(&destination, addend); + const int64_t offsetFromTOC = + in.ppc64LongBranchTarget->getEntryVA(&destination, addend) - + getPPC64TocBase(); + writePPC64LoadAndBranch(buf + 4, offsetFromTOC); + } } void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { @@ -851,6 +969,82 @@ void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { s->needsTocRestore = true; } +bool PPC64R2SaveStub::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; +} + +void PPC64R12SetupStub::writeTo(uint8_t *buf) { + int64_t offset = destination.getVA() - getThunkTargetSym()->getVA(); + if (!isInt<34>(offset)) + reportRangeError(buf, offset, 34, destination, "R12 setup 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 PPC64R12SetupStub::addSymbols(ThunkSection &isec) { + addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0, + isec); +} + +bool PPC64R12SetupStub::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + return rel.type == R_PPC64_REL24_NOTOC; +} + +void PPC64PCRelPLTStub::writeTo(uint8_t *buf) { + int nextInstOffset = 0; + int64_t offset = destination.getGotPltVA() - getThunkTargetSym()->getVA(); + + if (config->Power10Stub) { + if (!isInt<34>(offset)) + reportRangeError(buf, offset, 34, destination, + "PC-relative PLT stub offset"); + const uint64_t pld = PLD_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) | + (offset & 0xffff); + writePrefixedInstruction(buf + 0, pld); // pld r12, func@plt@pcrel + nextInstOffset = 8; + } else { + 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; + } + write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12 + write32(buf + nextInstOffset + 4, BCTR); // bctr +} + +void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) { + addSymbol(saver.save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, + isec); +} + +bool PPC64PCRelPLTStub::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + return rel.type == R_PPC64_REL24_NOTOC; +} + void PPC64LongBranchThunk::writeTo(uint8_t *buf) { int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) - getPPC64TocBase(); @@ -862,6 +1056,47 @@ void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { isec); } +bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + 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; @@ -880,7 +1115,7 @@ static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) { // - 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) { +static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) { switch (reloc) { case R_ARM_PC24: case R_ARM_PLT32: @@ -888,8 +1123,8 @@ static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s) { case R_ARM_CALL: case R_ARM_THM_CALL: if (config->picThunk) - return make(s); - return make(s); + return make(s, a); + return make(s, a); } fatal("relocation " + toString(reloc) + " to " + toString(s) + " not supported for Armv5 or Armv6 targets"); @@ -900,21 +1135,21 @@ static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s) { // - MOVT and MOVW instructions cannot be used. // - Only a limited number of instructions can access registers r8 and above // - No interworking support is needed (all Thumb). -static Thunk *addThunkV6M(RelType reloc, Symbol &s) { +static Thunk *addThunkV6M(RelType reloc, Symbol &s, int64_t a) { switch (reloc) { case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: if (config->isPic) - return make(s); - return make(s); + return make(s, a); + return make(s, a); } fatal("relocation " + toString(reloc) + " to " + toString(s) + " not supported for Armv6-M targets"); } // Creates a thunk for Thumb-ARM interworking or branch range extension. -static Thunk *addThunkArm(RelType reloc, Symbol &s) { +static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) { // Decide which Thunk is needed based on: // Available instruction set // - An Arm Thunk can only be used if Arm state is available. @@ -933,8 +1168,8 @@ static Thunk *addThunkArm(RelType reloc, Symbol &s) { // architecture to flag. if (!config->armHasMovtMovw) { if (!config->armJ1J2BranchEncoding) - return addThunkPreArmv7(reloc, s); - return addThunkV6M(reloc, s); + return addThunkPreArmv7(reloc, s, a); + return addThunkV6M(reloc, s, a); } switch (reloc) { @@ -943,14 +1178,14 @@ static Thunk *addThunkArm(RelType reloc, Symbol &s) { case R_ARM_JUMP24: case R_ARM_CALL: if (config->picThunk) - return make(s); - return make(s); + return make(s, a); + return make(s, a); case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: if (config->picThunk) - return make(s); - return make(s); + return make(s, a); + return make(s, a); } fatal("unrecognized relocation type"); } @@ -974,15 +1209,23 @@ static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel, } static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) { - assert((type == R_PPC64_REL14 || type == R_PPC64_REL24) && + assert((type == R_PPC64_REL14 || type == R_PPC64_REL24 || + type == R_PPC64_REL24_NOTOC) && "unexpected relocation type for thunk"); if (s.isInPlt()) - return make(s); + return type == R_PPC64_REL24_NOTOC ? (Thunk *)make(s) + : (Thunk *)make(s); // This check looks at the st_other bits of the callee. If the value is 1 - // then the callee clobbers the TOC and we need an R2 save stub. - if ((s.stOther >> 5) == 1) - return make(s); + // then the callee clobbers the TOC and we need an R2 save stub when RelType + // is R_PPC64_REL14 or R_PPC64_REL24. + if ((type == R_PPC64_REL14 || type == R_PPC64_REL24) && (s.stOther >> 5) == 1) + return make(s, a); + + if (type == R_PPC64_REL24_NOTOC) + return (s.stOther >> 5) > 1 + ? (Thunk *)make(s) + : (Thunk *)make(s, a); if (config->picThunk) return make(s, a); @@ -998,7 +1241,7 @@ Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) { return addThunkAArch64(rel.type, s, a); if (config->emachine == EM_ARM) - return addThunkArm(rel.type, s); + return addThunkArm(rel.type, s, a); if (config->emachine == EM_MIPS) return addThunkMips(rel.type, s); diff --git a/gnu/llvm/lld/ELF/Thunks.h b/gnu/llvm/lld/ELF/Thunks.h index a8575b4cdb5..5558da1a2c7 100644 --- a/gnu/llvm/lld/ELF/Thunks.h +++ b/gnu/llvm/lld/ELF/Thunks.h @@ -9,6 +9,7 @@ #ifndef LLD_ELF_THUNKS_H #define LLD_ELF_THUNKS_H +#include "llvm/ADT/SmallVector.h" #include "Relocations.h" namespace lld { @@ -73,6 +74,10 @@ void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, const InputFile *file, int64_t addend); void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset); +static inline uint16_t computeHiBits(uint32_t toCompute) { + return (toCompute + 0x8000) >> 16; +} + } // namespace elf } // namespace lld diff --git a/gnu/llvm/lld/MachO/Arch/ARM.cpp b/gnu/llvm/lld/MachO/Arch/ARM.cpp new file mode 100644 index 00000000000..42c7b893086 --- /dev/null +++ b/gnu/llvm/lld/MachO/Arch/ARM.cpp @@ -0,0 +1,172 @@ +//===- ARM.cpp ------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/Bitfields.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +namespace { + +struct ARM : TargetInfo { + ARM(uint32_t cpuSubtype); + + int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, + const relocation_info) const override; + 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 writeStubHelperHeader(uint8_t *buf) const override; + void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) 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; } +}; + +} // namespace + +const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const { + static const 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)}, +#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 { + // FIXME: implement this + return 0; +} + +template using BitfieldFlag = Bitfield::Element; + +// ARM BL encoding: +// +// 30 28 24 0 +// +---------+---------+----------------------------------------------+ +// | cond | 1 0 1 1 | imm24 | +// +---------+---------+----------------------------------------------+ +// +// `cond` here varies depending on whether we have bleq, blne, etc. +// `imm24` encodes a 26-bit pcrel offset -- last 2 bits are zero as ARM +// functions are 4-byte-aligned. +// +// ARM BLX encoding: +// +// 30 28 24 0 +// +---------+---------+----------------------------------------------+ +// | 1 1 1 1 | 1 0 1 H | imm24 | +// +---------+---------+----------------------------------------------+ +// +// Since Thumb functions are 2-byte-aligned, we need one extra bit to encode +// the offset -- that is the H bit. +// +// BLX is always unconditional, so while we can convert directly from BLX to BL, +// we need to insert a shim if a BL's target is a Thumb function. +// +// Helper aliases for decoding BL / BLX: +using Cond = Bitfield::Element; +using Imm24 = Bitfield::Element; + +void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, + uint64_t pc) const { + switch (r.type) { + case ARM_RELOC_BR24: { + uint32_t base = read32le(loc); + bool isBlx = Bitfield::get(base) == 0xf; + const Symbol *sym = r.referent.get(); + int32_t offset = value - (pc + 8); + + if (auto *defined = dyn_cast(sym)) { + if (!isBlx && defined->thumb) { + error("TODO: implement interworking shim"); + return; + } else if (isBlx && !defined->thumb) { + Bitfield::set(base, 0xe); // unconditional BL + Bitfield::set>(base, 1); + isBlx = false; + } + } else { + error("TODO: Implement ARM_RELOC_BR24 for dylib symbols"); + return; + } + + if (isBlx) { + assert((0x1 & value) == 0); + Bitfield::set(base, offset >> 2); + Bitfield::set>(base, (offset >> 1) & 1); // H bit + } else { + assert((0x3 & value) == 0); + Bitfield::set(base, offset >> 2); + } + write32le(loc, base); + break; + } + default: + fatal("unhandled relocation type"); + } +} + +void ARM::writeStub(uint8_t *buf, const Symbol &sym) const { + fatal("TODO: implement this"); +} + +void ARM::writeStubHelperHeader(uint8_t *buf) const { + fatal("TODO: implement this"); +} + +void ARM::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, + uint64_t entryAddr) const { + fatal("TODO: implement this"); +} + +void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const { + fatal("TODO: implement this"); +} + +ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) { + cpuType = CPU_TYPE_ARM; + this->cpuSubtype = cpuSubtype; + + stubSize = 0 /* FIXME */; + stubHelperHeaderSize = 0 /* FIXME */; + stubHelperEntrySize = 0 /* FIXME */; +} + +TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) { + static ARM t(cpuSubtype); + return &t; +} diff --git a/gnu/llvm/lld/MachO/Arch/ARM64.cpp b/gnu/llvm/lld/MachO/Arch/ARM64.cpp new file mode 100644 index 00000000000..36c3cc63928 --- /dev/null +++ b/gnu/llvm/lld/MachO/Arch/ARM64.cpp @@ -0,0 +1,145 @@ +//===- ARM64.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 "Arch/ARM64Common.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +namespace { + +struct ARM64 : ARM64Common { + ARM64(); + void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStubHelperHeader(uint8_t *buf) const override; + void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) const override; + const RelocAttrs &getRelocAttrs(uint8_t type) const override; + void populateThunk(InputSection *thunk, Symbol *funcSym) override; +}; + +} // namespace + +// Random notes on reloc types: +// ADDEND always pairs with BRANCH26, PAGE21, or PAGEOFF12 +// POINTER_TO_GOT: ld64 supports a 4-byte pc-relative form as well as an 8-byte +// 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{{ +#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)}, +#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 + 0xf9400210, // 04: ldr x16, [x16, __la_symbol_ptr@pageoff] + 0xd61f0200, // 08: br x16 +}; + +void ARM64::writeStub(uint8_t *buf8, const Symbol &sym) const { + ::writeStub(buf8, stubCode, sym); +} + +static constexpr uint32_t stubHelperHeaderCode[] = { + 0x90000011, // 00: adrp x17, _dyld_private@page + 0x91000231, // 04: add x17, x17, _dyld_private@pageoff + 0xa9bf47f0, // 08: stp x16/x17, [sp, #-16]! + 0x90000010, // 0c: adrp x16, dyld_stub_binder@page + 0xf9400210, // 10: ldr x16, [x16, dyld_stub_binder@pageoff] + 0xd61f0200, // 14: br x16 +}; + +void ARM64::writeStubHelperHeader(uint8_t *buf8) const { + ::writeStubHelperHeader(buf8, stubHelperHeaderCode); +} + +static constexpr uint32_t stubHelperEntryCode[] = { + 0x18000050, // 00: ldr w16, l0 + 0x14000000, // 04: b stubHelperHeader + 0x00000000, // 08: l0: .long 0 +}; + +void ARM64::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, + uint64_t entryVA) const { + ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA); +} + +// 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. +static constexpr uint32_t thunkCode[] = { + 0x90000010, // 00: adrp x16, @page + 0x91000210, // 04: add x16, [x16,@pageoff] + 0xd61f0200, // 08: br x16 +}; + +void ARM64::populateThunk(InputSection *thunk, Symbol *funcSym) { + thunk->align = 4; + thunk->data = {reinterpret_cast(thunkCode), + sizeof(thunkCode)}; + thunk->relocs.push_back({/*type=*/ARM64_RELOC_PAGEOFF12, + /*pcrel=*/false, /*length=*/2, + /*offset=*/4, /*addend=*/0, + /*referent=*/funcSym}); + thunk->relocs.push_back({/*type=*/ARM64_RELOC_PAGE21, + /*pcrel=*/true, /*length=*/2, + /*offset=*/0, /*addend=*/0, + /*referent=*/funcSym}); +} + +ARM64::ARM64() : ARM64Common(LP64()) { + cpuType = CPU_TYPE_ARM64; + cpuSubtype = CPU_SUBTYPE_ARM64_ALL; + + stubSize = sizeof(stubCode); + thunkSize = sizeof(thunkCode); + branchRange = maxIntN(28) - thunkSize; + stubHelperHeaderSize = sizeof(stubHelperHeaderCode); + stubHelperEntrySize = sizeof(stubHelperEntryCode); +} + +TargetInfo *macho::createARM64TargetInfo() { + static ARM64 t; + return &t; +} diff --git a/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp b/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp new file mode 100644 index 00000000000..67e7292fd6f --- /dev/null +++ b/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp @@ -0,0 +1,110 @@ +//===- ARM64Common.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 "Arch/ARM64Common.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Endian.h" + +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, + const relocation_info rel) const { + if (rel.r_type != ARM64_RELOC_UNSIGNED && + rel.r_type != ARM64_RELOC_SUBTRACTOR) { + // All other reloc types should use the ADDEND relocation to store their + // addends. + // TODO(gkm): extract embedded addend just so we can assert that it is 0 + return 0; + } + + const auto *buf = reinterpret_cast(mb.getBufferStart()); + const uint8_t *loc = buf + offset + rel.r_address; + switch (rel.r_length) { + case 2: + return static_cast(read32le(loc)); + case 3: + return read64le(loc); + 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 { + uint32_t base = ((r.length == 2) ? read32le(loc) : 0); + switch (r.type) { + case ARM64_RELOC_BRANCH26: + value = encodeBranch26(r, base, value - pc); + break; + case ARM64_RELOC_SUBTRACTOR: + case ARM64_RELOC_UNSIGNED: + if (r.length == 2) + checkInt(r, value, 32); + break; + case ARM64_RELOC_POINTER_TO_GOT: + if (r.pcrel) + value -= pc; + checkInt(r, value, 32); + break; + case ARM64_RELOC_PAGE21: + case ARM64_RELOC_GOT_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGE21: { + assert(r.pcrel); + value = encodePage21(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); + 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 { + // The instruction format comments below are quoted from + // Arm® Architecture Reference Manual + // Armv8, for Armv8-A architecture profile + // ARM DDI 0487G.a (ID011921) + uint32_t instruction = read32le(loc); + // C6.2.132 LDR (immediate) + // This matches both the 64- and 32-bit variants: + // LDR <(X|W)t>, [{, #}] + if ((instruction & 0xbfc00000) != 0xb9400000) + error(getRelocAttrs(type).name + " reloc requires LDR instruction"); + assert(((instruction >> 10) & 0xfff) == 0 && + "non-zero embedded LDR immediate"); + // C6.2.4 ADD (immediate) + // ADD , , #{, } + instruction = ((instruction & 0x001fffff) | 0x91000000); + write32le(loc, instruction); +} diff --git a/gnu/llvm/lld/MachO/Arch/ARM64Common.h b/gnu/llvm/lld/MachO/Arch/ARM64Common.h new file mode 100644 index 00000000000..934101caefb --- /dev/null +++ b/gnu/llvm/lld/MachO/Arch/ARM64Common.h @@ -0,0 +1,144 @@ +//===- ARM64Common.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_ARCH_ARM64COMMON_H +#define LLD_MACHO_ARCH_ARM64COMMON_H + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "llvm/BinaryFormat/MachO.h" + +namespace lld { +namespace macho { + +struct ARM64Common : TargetInfo { + template ARM64Common(LP lp) : TargetInfo(lp) {} + + int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, + const llvm::MachO::relocation_info) const override; + void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, + uint64_t pc) const override; + + void relaxGotLoad(uint8_t *loc, uint8_t type) const override; + uint64_t getPageSize() const override { return 16 * 1024; } +}; + +inline uint64_t bitField(uint64_t value, int right, int width, int left) { + return ((value >> right) & ((1 << width) - 1)) << left; +} + +// 25 0 +// +-----------+---------------------------------------------------+ +// | | imm26 | +// +-----------+---------------------------------------------------+ + +inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) { + checkInt(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)); +} + +inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) { + checkInt(d, va, 28); + return (base | bitField(va, 2, 26, 0)); +} + +// 30 29 23 5 +// +-+---+---------+-------------------------------------+---------+ +// | |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 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)); +} + +// 21 10 +// +-------------------+-----------------------+-------------------+ +// | | imm12 | | +// +-------------------+-----------------------+-------------------+ + +inline uint64_t encodePageOff12(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; + } + + // TODO(gkm): extract embedded addend and warn if != 0 + // uint64_t addend = ((base & 0x003FFC00) >> 10); + return (base | bitField(va, scale, 12 - scale, 10)); +} + +inline uint64_t pageBits(uint64_t address) { + const uint64_t pageMask = ~0xfffull; + return address & pageMask; +} + +template +inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3], + const macho::Symbol &sym) { + auto *buf32 = reinterpret_cast(buf8); + constexpr size_t stubCodeSize = 3 * sizeof(uint32_t); + 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); + buf32[2] = stubCode[2]; +} + +template +inline void writeStubHelperHeader(uint8_t *buf8, + const uint32_t stubHelperHeaderCode[6]) { + auto *buf32 = reinterpret_cast(buf8); + auto pcPageBits = [](int i) { + return pageBits(in.stubHelper->addr + i * sizeof(uint32_t)); + }; + 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); + 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); + buf32[5] = stubHelperHeaderCode[5]; +} + +inline void writeStubHelperEntry(uint8_t *buf8, + const uint32_t stubHelperEntryCode[3], + const DylibSymbol &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)); + buf32[2] = sym.lazyBindOffset; +} + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp b/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp new file mode 100644 index 00000000000..f7b1439b893 --- /dev/null +++ b/gnu/llvm/lld/MachO/Arch/ARM64_32.cpp @@ -0,0 +1,116 @@ +//===- ARM64_32.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 "Arch/ARM64Common.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +namespace { + +struct ARM64_32 : ARM64Common { + ARM64_32(); + void writeStub(uint8_t *buf, const Symbol &) const override; + void writeStubHelperHeader(uint8_t *buf) const override; + void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) const override; + const RelocAttrs &getRelocAttrs(uint8_t type) 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{{ +#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)}, +#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. + +static constexpr uint32_t stubCode[] = { + 0x90000010, // 00: adrp x16, __la_symbol_ptr@page + 0xb9400210, // 04: ldr w16, [x16, __la_symbol_ptr@pageoff] + 0xd61f0200, // 08: br x16 +}; + +void ARM64_32::writeStub(uint8_t *buf8, const Symbol &sym) const { + ::writeStub(buf8, stubCode, sym); +} + +static constexpr uint32_t stubHelperHeaderCode[] = { + 0x90000011, // 00: adrp x17, _dyld_private@page + 0x91000231, // 04: add x17, x17, _dyld_private@pageoff + 0xa9bf47f0, // 08: stp x16/x17, [sp, #-16]! + 0x90000010, // 0c: adrp x16, dyld_stub_binder@page + 0xb9400210, // 10: ldr w16, [x16, dyld_stub_binder@pageoff] + 0xd61f0200, // 14: br x16 +}; + +void ARM64_32::writeStubHelperHeader(uint8_t *buf8) const { + ::writeStubHelperHeader(buf8, stubHelperHeaderCode); +} + +static constexpr uint32_t stubHelperEntryCode[] = { + 0x18000050, // 00: ldr w16, l0 + 0x14000000, // 04: b stubHelperHeader + 0x00000000, // 08: l0: .long 0 +}; + +void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym, + uint64_t entryVA) const { + ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA); +} + +ARM64_32::ARM64_32() : ARM64Common(ILP32()) { + cpuType = CPU_TYPE_ARM64_32; + cpuSubtype = CPU_SUBTYPE_ARM64_V8; + + stubSize = sizeof(stubCode); + stubHelperHeaderSize = sizeof(stubHelperHeaderCode); + stubHelperEntrySize = sizeof(stubHelperEntryCode); +} + +TargetInfo *macho::createARM64_32TargetInfo() { + static ARM64_32 t; + return &t; +} diff --git a/gnu/llvm/lld/MachO/Arch/X86_64.cpp b/gnu/llvm/lld/MachO/Arch/X86_64.cpp index 36f686ca2f1..7e2155801fc 100644 --- a/gnu/llvm/lld/MachO/Arch/X86_64.cpp +++ b/gnu/llvm/lld/MachO/Arch/X86_64.cpp @@ -25,128 +25,90 @@ namespace { struct X86_64 : TargetInfo { X86_64(); - uint64_t getImplicitAddend(MemoryBufferRef, const section_64 &, - const relocation_info &) const override; - void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const override; + int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, + const relocation_info) const override; + void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, + uint64_t relocVA) const override; - void writeStub(uint8_t *buf, const DylibSymbol &) const override; + void writeStub(uint8_t *buf, const Symbol &) const override; void writeStubHelperHeader(uint8_t *buf) const override; void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, uint64_t entryAddr) const override; - void prepareSymbolRelocation(lld::macho::Symbol &, const InputSection *, - const Reloc &) override; - uint64_t getSymbolVA(const lld::macho::Symbol &, uint8_t type) 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; } }; } // namespace -static std::string getErrorLocation(MemoryBufferRef mb, const section_64 &sec, - const relocation_info &rel) { - return ("invalid relocation at offset " + std::to_string(rel.r_address) + - " of " + sec.segname + "," + sec.sectname + " in " + - mb.getBufferIdentifier()) - .str(); +const RelocAttrs &X86_64::getRelocAttrs(uint8_t type) const { + static const 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)}, +#undef B + }}; + assert(type < relocAttrsArray.size() && "invalid relocation type"); + if (type >= relocAttrsArray.size()) + return invalidRelocAttrs; + return relocAttrsArray[type]; } -static void validateLength(MemoryBufferRef mb, const section_64 &sec, - const relocation_info &rel, - const std::vector &validLengths) { - if (std::find(validLengths.begin(), validLengths.end(), rel.r_length) != - validLengths.end()) - return; - - std::string msg = getErrorLocation(mb, sec, rel) + ": relocations of type " + - std::to_string(rel.r_type) + " must have r_length of "; - bool first = true; - for (uint8_t length : validLengths) { - if (!first) - msg += " or "; - first = false; - msg += std::to_string(length); - } - fatal(msg); -} - -uint64_t X86_64::getImplicitAddend(MemoryBufferRef mb, const section_64 &sec, - const relocation_info &rel) const { - auto *buf = reinterpret_cast(mb.getBufferStart()); - const uint8_t *loc = buf + sec.offset + rel.r_address; - switch (rel.r_type) { - case X86_64_RELOC_BRANCH: - // XXX: ld64 also supports r_length = 0 here but I'm not sure when such a - // relocation will actually be generated. - validateLength(mb, sec, rel, {2}); - break; - case X86_64_RELOC_SIGNED: +static int pcrelOffset(uint8_t type) { + switch (type) { case X86_64_RELOC_SIGNED_1: + return 1; case X86_64_RELOC_SIGNED_2: + return 2; case X86_64_RELOC_SIGNED_4: - case X86_64_RELOC_GOT_LOAD: - case X86_64_RELOC_GOT: - if (!rel.r_pcrel) - fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " + - std::to_string(rel.r_type) + " must be pcrel"); - validateLength(mb, sec, rel, {2}); - break; - case X86_64_RELOC_UNSIGNED: - if (rel.r_pcrel) - fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " + - std::to_string(rel.r_type) + " must not be pcrel"); - validateLength(mb, sec, rel, {2, 3}); - break; + return 4; default: - error("TODO: Unhandled relocation type " + std::to_string(rel.r_type)); return 0; } +} + +int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, + relocation_info rel) const { + auto *buf = reinterpret_cast(mb.getBufferStart()); + const uint8_t *loc = buf + offset + rel.r_address; switch (rel.r_length) { - case 0: - return *loc; - case 1: - return read16le(loc); case 2: - return read32le(loc); + return static_cast(read32le(loc)) + pcrelOffset(rel.r_type); case 3: - return read64le(loc); + return read64le(loc) + pcrelOffset(rel.r_type); default: llvm_unreachable("invalid r_length"); } } -void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const { - switch (r.type) { - case X86_64_RELOC_BRANCH: - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - case X86_64_RELOC_GOT_LOAD: - case X86_64_RELOC_GOT: - // These types are only used for pc-relative relocations, so offset by 4 - // since the RIP has advanced by 4 at this point. This is only valid when - // r_length = 2, which is enforced by validateLength(). - val -= 4; - break; - case X86_64_RELOC_UNSIGNED: - break; - default: - llvm_unreachable( - "getImplicitAddend should have flagged all unhandled relocation types"); +void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, + uint64_t relocVA) const { + if (r.pcrel) { + uint64_t pc = relocVA + 4 + pcrelOffset(r.type); + value -= pc; } switch (r.length) { - case 0: - *loc = val; - break; - case 1: - write16le(loc, val); - break; case 2: - write32le(loc, val); + if (r.type == X86_64_RELOC_UNSIGNED) + checkUInt(r, value, 32); + else + checkInt(r, value, 32); + write32le(loc, value); break; case 3: - write64le(loc, val); + write64le(loc, value); break; default: llvm_unreachable("invalid r_length"); @@ -162,9 +124,10 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const { // bufAddr: The virtual address corresponding to buf[0]. // bufOff: The offset within buf of the next instruction. // destAddr: The destination address that the current instruction references. -static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff, - uint64_t destAddr) { +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); // 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); @@ -174,11 +137,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 DylibSymbol &sym) const { +void X86_64::writeStub(uint8_t *buf, const Symbol &sym) const { memcpy(buf, stub, 2); // just copy the two nonzero bytes uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub); - writeRipRelative(buf, stubAddr, sizeof(stub), - in.lazyPointers->addr + sym.stubsIndex * WordSize); + writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), + in.lazyPointers->addr + sym.stubsIndex * LP64::wordSize); } static constexpr uint8_t stubHelperHeader[] = { @@ -188,90 +151,37 @@ static constexpr uint8_t stubHelperHeader[] = { 0x90, // 0xf: nop }; -static constexpr uint8_t stubHelperEntry[] = { - 0x68, 0, 0, 0, 0, // 0x0: pushq - 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> -}; - void X86_64::writeStubHelperHeader(uint8_t *buf) const { memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader)); - writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA()); - writeRipRelative(buf, in.stubHelper->addr, 0xf, + SymbolDiagnostic d = {nullptr, "stub helper header"}; + writeRipRelative(d, buf, in.stubHelper->addr, 7, + in.imageLoaderCache->getVA()); + writeRipRelative(d, buf, in.stubHelper->addr, 0xf, in.got->addr + - in.stubHelper->stubBinder->gotIndex * WordSize); + in.stubHelper->stubBinder->gotIndex * LP64::wordSize); } +static constexpr uint8_t stubHelperEntry[] = { + 0x68, 0, 0, 0, 0, // 0x0: pushq + 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> +}; + void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, uint64_t entryAddr) const { memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); write32le(buf + 1, sym.lazyBindOffset); - writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry), - in.stubHelper->addr); + writeRipRelative({&sym, "stub helper"}, buf, entryAddr, + sizeof(stubHelperEntry), in.stubHelper->addr); } -void X86_64::prepareSymbolRelocation(lld::macho::Symbol &sym, - const InputSection *isec, const Reloc &r) { - switch (r.type) { - case X86_64_RELOC_GOT_LOAD: - // TODO: implement mov -> lea relaxation for non-dynamic symbols - case X86_64_RELOC_GOT: - in.got->addEntry(sym); - break; - case X86_64_RELOC_BRANCH: { - if (auto *dysym = dyn_cast(&sym)) - in.stubs->addEntry(*dysym); - break; - } - case X86_64_RELOC_UNSIGNED: { - if (auto *dysym = dyn_cast(&sym)) { - if (r.length != 3) { - error("X86_64_RELOC_UNSIGNED referencing the dynamic symbol " + - dysym->getName() + " must have r_length = 3"); - return; - } - in.binding->addEntry(dysym, isec, r.offset, r.addend); - } - break; - } - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - break; - case X86_64_RELOC_SUBTRACTOR: - case X86_64_RELOC_TLV: - fatal("TODO: handle relocation type " + std::to_string(r.type)); - break; - default: - llvm_unreachable("unexpected relocation type"); - } -} - -uint64_t X86_64::getSymbolVA(const lld::macho::Symbol &sym, - uint8_t type) const { - switch (type) { - case X86_64_RELOC_GOT_LOAD: - case X86_64_RELOC_GOT: - return in.got->addr + sym.gotIndex * WordSize; - case X86_64_RELOC_BRANCH: - if (auto *dysym = dyn_cast(&sym)) - return in.stubs->addr + dysym->stubsIndex * sizeof(stub); - return sym.getVA(); - case X86_64_RELOC_UNSIGNED: - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - return sym.getVA(); - case X86_64_RELOC_SUBTRACTOR: - case X86_64_RELOC_TLV: - fatal("TODO: handle relocation type " + std::to_string(type)); - default: - llvm_unreachable("Unexpected relocation type"); - } +void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const { + // Convert MOVQ to LEAQ + if (loc[-2] != 0x8b) + error(getRelocAttrs(type).name + " reloc requires MOVQ instruction"); + loc[-2] = 0x8d; } -X86_64::X86_64() { +X86_64::X86_64() : TargetInfo(LP64()) { cpuType = CPU_TYPE_X86_64; cpuSubtype = CPU_SUBTYPE_X86_64_ALL; diff --git a/gnu/llvm/lld/MachO/CMakeLists.txt b/gnu/llvm/lld/MachO/CMakeLists.txt index 6fe356f5158..eff1812a6bb 100644 --- a/gnu/llvm/lld/MachO/CMakeLists.txt +++ b/gnu/llvm/lld/MachO/CMakeLists.txt @@ -2,35 +2,61 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(MachOOptionsTableGen) +include_directories(${LLVM_MAIN_SRC_DIR}/../libunwind/include) + add_lld_library(lldMachO2 + Arch/ARM.cpp + Arch/ARM64.cpp + Arch/ARM64Common.cpp + Arch/ARM64_32.cpp Arch/X86_64.cpp + ConcatOutputSection.cpp Driver.cpp + DriverUtils.cpp + Dwarf.cpp ExportTrie.cpp + ICF.cpp InputFiles.cpp InputSection.cpp - MergedOutputSection.cpp + LTO.cpp + MapFile.cpp + MarkLive.cpp + ObjC.cpp OutputSection.cpp OutputSegment.cpp + Relocations.cpp SymbolTable.cpp Symbols.cpp SyntheticSections.cpp Target.cpp + UnwindInfoSection.cpp Writer.cpp LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat + BitReader Core + DebugInfoDWARF + LTO + MC + ObjCARCOpts Object Option + Passes Support TextAPI LINK_LIBS lldCommon ${LLVM_PTHREAD_LIB} + ${XAR_LIB} DEPENDS MachOOptionsTableGen ${tablegen_deps} ) + +if(LLVM_HAVE_LIBXAR) + target_link_libraries(lldMachO2 PRIVATE ${XAR_LIB}) +endif() diff --git a/gnu/llvm/lld/MachO/ConcatOutputSection.cpp b/gnu/llvm/lld/MachO/ConcatOutputSection.cpp new file mode 100644 index 00000000000..78590cff2ee --- /dev/null +++ b/gnu/llvm/lld/MachO/ConcatOutputSection.cpp @@ -0,0 +1,377 @@ +//===- ConcatOutputSection.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 "ConcatOutputSection.h" +#include "Config.h" +#include "OutputSegment.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TimeProfiler.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace lld; +using namespace lld::macho; + +MapVector macho::concatOutputSections; + +void ConcatOutputSection::addInput(ConcatInputSection *input) { + assert(input->parent == this); + if (inputs.empty()) { + align = input->align; + flags = input->getFlags(); + } else { + align = std::max(align, input->align); + finalizeFlags(input); + } + inputs.push_back(input); +} + +// Branch-range extension can be implemented in two ways, either through ... +// +// (1) Branch islands: Single branch instructions (also of limited range), +// that might be chained in multiple hops to reach the desired +// destination. On ARM64, as 16 branch islands are needed to hop between +// opposite ends of a 2 GiB program. LD64 uses branch islands exclusively, +// even when it needs excessive hops. +// +// (2) Thunks: Instruction(s) to load the destination address into a scratch +// register, followed by a register-indirect branch. Thunks are +// constructed to reach any arbitrary address, so need not be +// chained. Although thunks need not be chained, a program might need +// 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, +// 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 ... +// +// (1) new Defined privateExtern 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.2) existing DylibSymbol for the real function in a dylib +// +// Nearly-optimal thunk-placement algorithm features: +// +// * Single pass: O(n) on the number of call sites. +// +// * Accounts for the exact space overhead of thunks - no heuristics +// +// * Exploits the full range of call instructions - forward & backward +// +// Data: +// +// * DenseMap thunkMap: Maps the function symbol +// to its thunk bookkeeper. +// +// * struct ThunkInfo (bookkeeper): Call instructions have limited range, and +// 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.. +// +// * 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. +// +// * std::vector MergedInputSection::thunks: A vector parallel +// to the inputs vector. We store new thunks via cheap vector append, rather +// than costly insertion into the inputs vector. +// +// Control Flow: +// +// * During address assignment, MergedInputSection::finalize() examines call +// sites by ascending address and creates thunks. When a function is beyond +// the range of a call site, we need a thunk. Place it at the largest +// available forward address from the call site. Call sites increase +// monotonically and thunks are always placed as far forward as possible; +// 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 +// the inputs and thunks vectors (both ordered by ascending address), which +// is simple and cheap. + +DenseMap lld::macho::thunkMap; + +// Determine whether we need thunks, which depends on the target arch -- RISC +// (i.e., ARM) generally does because it has limited-range branch/call +// 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 { + if (!target->usesThunks()) + return false; + uint64_t isecAddr = addr; + for (InputSection *isec : inputs) + isecAddr = alignTo(isecAddr, isec->align) + isec->getSize(); + if (isecAddr - addr + in.stubs->getSize() <= target->branchRange) + return false; + // Yes, this program is large enough to need thunks. + for (InputSection *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() + 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 . + ++thunkInfo.callSiteCount; + // Knowing InputSection call site count will help us avoid work on those + // that have no BRANCH relocs. + ++isec->callSiteCount; + } + } + return true; +} + +// 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. + size_t maxPotentialThunks = 0; + for (auto &tp : thunkMap) { + ThunkInfo &ti = tp.second; + maxPotentialThunks += + !tp.first->isInStubs() && ti.callSitesUsed < ti.callSiteCount; + } + // Tally the total size of input sections remaining to process. + uint64_t isecEnd = isec->getVA(); + for (size_t i = callIdx; i < endIdx; 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 stubsInRangeVA = isecEnd + maxPotentialThunks * target->thunkSize + + in.stubs->getSize() - branchRange; + 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))); + 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(); + }; + + if (!needsThunks()) { + for (ConcatInputSection *isec : inputs) + finalizeOne(isec); + size = isecAddr - addr; + fileSize = isecFileOff - fileOff; + return; + } + + uint64_t branchRange = target->branchRange; + uint64_t stubsInRangeVA = TargetInfo::outOfRangeVA; + size_t thunkSize = target->thunkSize; + size_t relocCount = 0; + size_t callSiteCount = 0; + size_t thunkCallCount = 0; + size_t thunkCount = 0; + + // inputs[finalIdx] is for finalization (address-assignment) + size_t finalIdx = 0; + // Kick-off by ensuring that the first input section has an address + for (size_t callIdx = 0, endIdx = inputs.size(); callIdx < endIdx; + ++callIdx) { + if (finalIdx == callIdx) + finalizeOne(inputs[finalIdx++]); + 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) + finalizeOne(inputs[finalIdx++]); + if (isec->callSiteCount == 0) + 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 + // we can estimate the threshold address after which we can + // reach any stub with a forward branch. Note that although it + // sits in the middle of a loop, this code executes only once. + // It is in the loop because we need to call it at the proper + // time: the earliest call site from which the end of __text + // (and start of __stubs) comes within range of a forward branch. + stubsInRangeVA = estimateStubsInRangeVA(callIdx); + } + // Process relocs by ascending address, i.e., ascending offset within isec + std::vector &relocs = isec->relocs; + // FIXME: This property does not hold for object files produced by ld64's + // `-r` mode. + assert(is_sorted(relocs, + [](Reloc &a, Reloc &b) { return a.offset > b.offset; })); + for (Reloc &r : reverse(relocs)) { + ++relocCount; + if (!target->hasAttr(r.type, RelocAttrBits::BRANCH)) + continue; + ++callSiteCount; + // Calculate branch reachability boundaries + uint64_t callVA = isecVA + r.offset; + uint64_t lowVA = branchRange < callVA ? callVA - branchRange : 0; + uint64_t highVA = callVA + branchRange; + // 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) { + // ... 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) { + // The referent is reachable with a simple call instruction. + continue; + } + ++thunkInfo.thunkCallCount; + ++thunkCallCount; + // If an existing thunk is reachable, use it ... + if (thunkInfo.sym) { + uint64_t thunkVA = thunkInfo.isec->getVA(); + 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]. + fatal(Twine(__FUNCTION__) + ": FIXME: thunk range overrun"); + } + thunkInfo.isec = + make(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); + 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()) + + ", relocs = " + std::to_string(relocCount) + + ", all calls = " + std::to_string(callSiteCount) + + ", thunk calls = " + std::to_string(thunkCallCount) + + ", thunks = " + std::to_string(thunkCount)); +} + +void ConcatOutputSection::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 || + inputs[i]->outSecOff < thunks[t]->outSecOff)) { + inputs[i]->writeTo(buf + inputs[i]->outSecOff); + ++i; + } + while (t < te && (i == ie || thunks[t]->outSecOff < inputs[i]->outSecOff)) { + thunks[t]->writeTo(buf + thunks[t]->outSecOff); + ++t; + } + } +} + +void ConcatOutputSection::finalizeFlags(InputSection *input) { + switch (sectionType(input->getFlags())) { + default /*type-unspec'ed*/: + // FIXME: Add additional logic here when supporting emitting obj files. + break; + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_16BYTE_LITERALS: + case S_CSTRING_LITERALS: + case S_ZEROFILL: + case S_LAZY_SYMBOL_POINTERS: + case S_MOD_TERM_FUNC_POINTERS: + case S_THREAD_LOCAL_REGULAR: + case S_THREAD_LOCAL_ZEROFILL: + case S_THREAD_LOCAL_VARIABLES: + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + case S_THREAD_LOCAL_VARIABLE_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + case S_SYMBOL_STUBS: + flags |= input->getFlags(); + break; + } +} + +ConcatOutputSection * +ConcatOutputSection::getOrCreateForInput(const InputSection *isec) { + NamePair names = maybeRenameSection({isec->getSegName(), isec->getName()}); + ConcatOutputSection *&osec = concatOutputSections[names]; + if (!osec) + osec = make(names.second); + return osec; +} + +NamePair macho::maybeRenameSection(NamePair key) { + auto newNames = config->sectionRenameMap.find(key); + if (newNames != config->sectionRenameMap.end()) + return newNames->second; + return key; +} diff --git a/gnu/llvm/lld/MachO/ConcatOutputSection.h b/gnu/llvm/lld/MachO/ConcatOutputSection.h new file mode 100644 index 00000000000..ec3d6bfbc34 --- /dev/null +++ b/gnu/llvm/lld/MachO/ConcatOutputSection.h @@ -0,0 +1,96 @@ +//===- ConcatOutputSection.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_MERGED_OUTPUT_SECTION_H +#define LLD_MACHO_MERGED_OUTPUT_SECTION_H + +#include "InputSection.h" +#include "OutputSection.h" +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" + +namespace lld { +namespace macho { + +class Defined; + +// Linking multiple files will inevitably mean resolving sections in different +// 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 { +public: + explicit ConcatOutputSection(StringRef name) + : OutputSection(ConcatKind, name) {} + + const ConcatInputSection *firstSection() const { return inputs.front(); } + const ConcatInputSection *lastSection() const { return inputs.back(); } + bool isNeeded() const override { return !inputs.empty(); } + + // These accessors will only be valid after finalizing the section + 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; + + 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); + + size_t size = 0; + uint64_t fileSize = 0; +}; + +// We maintain one ThunkInfo per real function. +// +// The "active thunk" is represented by the sym/isec pair that +// turns-over during finalize(): as the call-site address advances, +// the active thunk goes out of branch-range, and we create a new +// thunk to take its place. +// +// The remaining members -- bools and counters -- apply to the +// collection of thunks associated with the real function. + +struct ThunkInfo { + // These denote the active thunk: + Defined *sym = nullptr; // private-extern symbol for active thunk + ConcatInputSection *isec = nullptr; // input section for active thunk + + // The following values are cumulative across all thunks on this function + uint32_t callSiteCount = 0; // how many calls to the real function? + uint32_t callSitesUsed = 0; // how many call sites processed so-far? + uint32_t thunkCallCount = 0; // how many call sites went to thunk? + uint8_t sequence = 0; // how many thunks created so-far? +}; + +NamePair maybeRenameSection(NamePair key); + +// Output sections are added to output segments in iteration order +// of ConcatOutputSection, so must have deterministic iteration order. +extern llvm::MapVector concatOutputSections; + +extern llvm::DenseMap thunkMap; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/Config.h b/gnu/llvm/lld/MachO/Config.h index 79812a43356..0f47015b760 100644 --- a/gnu/llvm/lld/MachO/Config.h +++ b/gnu/llvm/lld/MachO/Config.h @@ -9,10 +9,18 @@ #ifndef LLD_MACHO_CONFIG_H #define LLD_MACHO_CONFIG_H +#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/MachO.h" -#include "llvm/TextAPI/MachO/Architecture.h" +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/GlobPattern.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/TextAPI/Architecture.h" +#include "llvm/TextAPI/Platform.h" +#include "llvm/TextAPI/Target.h" #include @@ -22,17 +30,155 @@ namespace macho { class Symbol; struct SymbolPriorityEntry; +using NamePair = std::pair; +using SectionRenameMap = llvm::DenseMap; +using SegmentRenameMap = llvm::DenseMap; + +struct PlatformInfo { + llvm::MachO::Target target; + llvm::VersionTuple minimum; + llvm::VersionTuple sdk; +}; + +inline uint32_t encodeVersion(const llvm::VersionTuple &version) { + return ((version.getMajor() << 020) | + (version.getMinor().getValueOr(0) << 010) | + version.getSubminor().getValueOr(0)); +} + +enum class NamespaceKind { + twolevel, + flat, +}; + +enum class UndefinedSymbolTreatment { + unknown, + error, + warning, + suppress, + dynamic_lookup, +}; + +enum class ICFLevel { + unknown, + none, + safe, + all, +}; + +struct SectionAlign { + llvm::StringRef segName; + llvm::StringRef sectName; + uint32_t align; +}; + +struct SegmentProtection { + llvm::StringRef name; + uint32_t maxProt; + uint32_t initProt; +}; + +class SymbolPatterns { +public: + // GlobPattern can also match literals, + // but we prefer the O(1) lookup of DenseSet. + llvm::DenseSet literals; + std::vector globs; + + bool empty() const { return literals.empty() && globs.empty(); } + void clear(); + void insert(llvm::StringRef symbolName); + bool matchLiteral(llvm::StringRef symbolName) const; + bool matchGlob(llvm::StringRef symbolName) const; + bool match(llvm::StringRef symbolName) const; +}; + struct Configuration { - Symbol *entry; + Symbol *entry = nullptr; bool hasReexports = false; - llvm::StringRef installName; + bool allLoad = false; + bool applicationExtension = false; + bool archMultiple = false; + bool exportDynamic = false; + bool forceLoadObjC = false; + bool forceLoadSwift = false; + 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; + bool printWhyLoad = false; + bool searchDylibsFirst = false; + bool saveTemps = false; + bool adhocCodesign = false; + bool emitFunctionStarts = false; + bool emitBitcodeBundle = false; + bool emitDataInCodeInfo = false; + bool emitEncryptionInfo = false; + bool timeTraceEnabled = false; + bool dataConst = false; + bool dedupLiterals = true; + uint32_t headerPad; + uint32_t dylibCompatibilityVersion = 0; + uint32_t dylibCurrentVersion = 0; + uint32_t timeTraceGranularity = 500; + unsigned optimize; + std::string progName; + + // For `clang -arch arm64 -arch x86_64`, clang will: + // 1. invoke the linker twice, to write one temporary output per arch + // 2. invoke `lipo` to merge the two outputs into a single file + // `outputFile` is the name of the temporary file the linker writes to. + // `finalOutput `is the name of the file lipo writes to after the link. llvm::StringRef outputFile; - llvm::MachO::Architecture arch; + llvm::StringRef finalOutput; + + llvm::StringRef installName; + llvm::StringRef mapFile; + llvm::StringRef ltoObjPath; + llvm::StringRef thinLTOJobs; + llvm::StringRef umbrella; + uint32_t ltoo = 2; + llvm::CachePruningPolicy thinLTOCachePolicy; + llvm::StringRef thinLTOCacheDir; + bool deadStripDylibs = false; + bool demangle = false; + bool deadStrip = false; + PlatformInfo platformInfo; + NamespaceKind namespaceKind = NamespaceKind::twolevel; + UndefinedSymbolTreatment undefinedSymbolTreatment = + UndefinedSymbolTreatment::error; + ICFLevel icfLevel = ICFLevel::none; llvm::MachO::HeaderFileType outputType; + std::vector systemLibraryRoots; std::vector librarySearchPaths; - // TODO: use the framework search paths std::vector frameworkSearchPaths; + std::vector runtimePaths; + std::vector astPaths; + std::vector explicitUndefineds; + llvm::StringSet<> explicitDynamicLookups; + // There are typically few custom sectionAlignments or segmentProtections, + // so use a vector instead of a map. + std::vector sectionAlignments; + std::vector segmentProtections; + llvm::DenseMap priorities; + SectionRenameMap sectionRenameMap; + SegmentRenameMap segmentRenameMap; + + SymbolPatterns exportedSymbols; + SymbolPatterns unexportedSymbols; + + bool zeroModTime = false; + + llvm::MachO::Architecture arch() const { return platformInfo.target.Arch; } + + llvm::MachO::PlatformKind platform() const { + return platformInfo.target.Platform; + } }; // The symbol with the highest priority should be ordered first in the output diff --git a/gnu/llvm/lld/MachO/Driver.cpp b/gnu/llvm/lld/MachO/Driver.cpp index 2a3b0042162..a8c11b6994b 100644 --- a/gnu/llvm/lld/MachO/Driver.cpp +++ b/gnu/llvm/lld/MachO/Driver.cpp @@ -8,12 +8,18 @@ #include "Driver.h" #include "Config.h" +#include "ICF.h" #include "InputFiles.h" +#include "LTO.h" +#include "MarkLive.h" +#include "ObjC.h" #include "OutputSection.h" #include "OutputSegment.h" #include "SymbolTable.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" +#include "UnwindInfoSection.h" #include "Writer.h" #include "lld/Common/Args.h" @@ -21,97 +27,99 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "lld/Common/Memory.h" +#include "lld/Common/Reproduce.h" #include "lld/Common/Version.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Option/ArgList.h" -#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" +#include "llvm/Support/TarWriter.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/TimeProfiler.h" +#include "llvm/TextAPI/PackedVersion.h" + +#include using namespace llvm; using namespace llvm::MachO; -using namespace llvm::sys; +using namespace llvm::object; using namespace llvm::opt; +using namespace llvm::sys; using namespace lld; using namespace lld::macho; -Configuration *lld::macho::config; - -// Create prefix string literals used in Options.td -#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE; -#include "Options.inc" -#undef PREFIX - -// Create table mapping all options defined in Options.td -static const 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}, -#include "Options.inc" -#undef OPTION -}; - -MachOOptTable::MachOOptTable() : OptTable(optInfo) {} - -opt::InputArgList MachOOptTable::parse(ArrayRef argv) { - // Make InputArgList from string vectors. - unsigned missingIndex; - unsigned missingCount; - SmallVector vec(argv.data(), argv.data() + argv.size()); +Configuration *macho::config; +DependencyTracker *macho::depTracker; - opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount); - - if (missingCount) - error(Twine(args.getArgString(missingIndex)) + ": missing argument"); +static HeaderFileType getOutputType(const InputArgList &args) { + // TODO: -r, -dylinker, -preload... + Arg *outputArg = args.getLastArg(OPT_bundle, OPT_dylib, OPT_execute); + if (outputArg == nullptr) + return MH_EXECUTE; - for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) - error("unknown argument: " + arg->getSpelling()); - return args; + switch (outputArg->getOption().getID()) { + case OPT_bundle: + return MH_BUNDLE; + case OPT_dylib: + return MH_DYLIB; + case OPT_execute: + return MH_EXECUTE; + default: + llvm_unreachable("internal error"); + } } -void MachOOptTable::printHelp(const char *argv0, bool showHidden) const { - PrintHelp(lld::outs(), (std::string(argv0) + " [options] file...").c_str(), - "LLVM Linker", showHidden); - lld::outs() << "\n"; +static Optional findLibrary(StringRef name) { + if (config->searchDylibsFirst) { + if (Optional path = findPathCombination( + "lib" + name, config->librarySearchPaths, {".tbd", ".dylib"})) + return path; + return findPathCombination("lib" + name, config->librarySearchPaths, + {".a"}); + } + return findPathCombination("lib" + name, config->librarySearchPaths, + {".tbd", ".dylib", ".a"}); } -static Optional findLibrary(StringRef name) { - std::string stub = (llvm::Twine("lib") + name + ".tbd").str(); - std::string shared = (llvm::Twine("lib") + name + ".dylib").str(); - std::string archive = (llvm::Twine("lib") + name + ".a").str(); - llvm::SmallString<260> location; +static Optional findFramework(StringRef name) { + SmallString<260> symlink; + StringRef suffix; + std::tie(name, suffix) = name.split(","); + for (StringRef dir : config->frameworkSearchPaths) { + symlink = dir; + path::append(symlink, name + ".framework", name); - for (StringRef dir : config->librarySearchPaths) { - for (StringRef library : {stub, shared, archive}) { - location = dir; - llvm::sys::path::append(location, library); - if (fs::exists(location)) - return location.str().str(); + if (!suffix.empty()) { + // NOTE: we must resolve the symlink before trying the suffixes, because + // there are no symlinks for the suffixed paths. + SmallString<260> location; + if (!fs::real_path(symlink, location)) { + // only append suffix if realpath() succeeds + Twine suffixed = location + suffix; + if (fs::exists(suffixed)) + return suffixed.str(); + } + // Suffix lookup failed, fall through to the no-suffix case. } - } - return {}; -} -static TargetInfo *createTargetInfo(opt::InputArgList &args) { - StringRef arch = args.getLastArgValue(OPT_arch, "x86_64"); - config->arch = llvm::MachO::getArchitectureFromName( - args.getLastArgValue(OPT_arch, arch)); - switch (config->arch) { - case llvm::MachO::AK_x86_64: - case llvm::MachO::AK_x86_64h: - return createX86_64TargetInfo(); - default: - fatal("missing or unsupported -arch " + arch); + if (Optional path = resolveDylibPath(symlink)) + return path; } + return {}; } -static bool isDirectory(StringRef option, StringRef path) { +static bool warnIfNotDirectory(StringRef option, StringRef path) { if (!fs::exists(path)) { warn("directory not found for option -" + option + path); return false; @@ -122,89 +130,348 @@ static bool isDirectory(StringRef option, StringRef path) { return true; } -static void getSearchPaths(std::vector &paths, unsigned optionCode, - opt::InputArgList &args, - const SmallVector &systemPaths) { - StringRef optionLetter{(optionCode == OPT_F ? "F" : "L")}; - for (auto const &path : args::getStrings(args, optionCode)) { - if (isDirectory(optionLetter, path)) +static std::vector +getSearchPaths(unsigned optionCode, InputArgList &args, + const std::vector &roots, + const SmallVector &systemPaths) { + std::vector paths; + StringRef optionLetter{optionCode == OPT_F ? "F" : "L"}; + for (StringRef path : args::getStrings(args, optionCode)) { + // NOTE: only absolute paths are re-rooted to syslibroot(s) + bool found = false; + if (path::is_absolute(path, path::Style::posix)) { + for (StringRef root : roots) { + SmallString<261> buffer(root); + 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())); + found = true; + } + } + } + if (!found && warnIfNotDirectory(optionLetter, path)) paths.push_back(path); } - if (!args.hasArg(OPT_Z) && Triple(sys::getProcessTriple()).isOSDarwin()) { - for (auto const &path : systemPaths) { - if (isDirectory(optionLetter, path)) - paths.push_back(path); + + // `-Z` suppresses the standard "system" search paths. + if (args.hasArg(OPT_Z)) + return paths; + + for (const StringRef &path : systemPaths) { + for (const StringRef &root : roots) { + SmallString<261> buffer(root); + path::append(buffer, path); + if (fs::is_directory(buffer)) + paths.push_back(saver.save(buffer.str())); } } + return paths; +} + +static std::vector getSystemLibraryRoots(InputArgList &args) { + std::vector roots; + 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() == "/") + roots.clear(); + // NOTE: roots can never be empty - add an empty root to simplify the library + // and framework search path computation. + if (roots.empty()) + roots.emplace_back(""); + return roots; +} + +static std::vector +getLibrarySearchPaths(InputArgList &args, const std::vector &roots) { + return getSearchPaths(OPT_L, args, roots, {"/usr/lib", "/usr/local/lib"}); } -static void getLibrarySearchPaths(std::vector &paths, - opt::InputArgList &args) { - getSearchPaths(paths, OPT_L, args, {"/usr/lib", "/usr/local/lib"}); +static std::vector +getFrameworkSearchPaths(InputArgList &args, + const std::vector &roots) { + return getSearchPaths(OPT_F, args, roots, + {"/Library/Frameworks", "/System/Library/Frameworks"}); } -static void getFrameworkSearchPaths(std::vector &paths, - opt::InputArgList &args) { - getSearchPaths(paths, OPT_F, args, - {"/Library/Frameworks", "/System/Library/Frameworks"}); +static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) { + SmallString<128> ltoPolicy; + auto add = [<oPolicy](Twine val) { + if (!ltoPolicy.empty()) + ltoPolicy += ":"; + val.toVector(ltoPolicy); + }; + for (const Arg *arg : + args.filtered(OPT_thinlto_cache_policy, 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_prune_interval_lto: + if (!strcmp("-1", arg->getValue())) + add("prune_interval=87600h"); // 10 years + else + add(Twine("prune_interval=") + arg->getValue() + "s"); + break; + case OPT_prune_after_lto: + add(Twine("prune_after=") + arg->getValue() + "s"); + break; + case OPT_max_relative_cache_size_lto: + add(Twine("cache_size=") + arg->getValue() + "%"); + break; + } + } + return CHECK(parseCachePruningPolicy(ltoPolicy), "invalid LTO cache policy"); +} + +namespace { +struct ArchiveMember { + MemoryBufferRef mbref; + uint32_t modTime; + uint64_t offsetInArchive; +}; +} // 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 void addFile(StringRef path) { +static DenseMap loadedArchives; + +static InputFile *addFile(StringRef path, bool forceLoadArchive, + bool isExplicit = true, bool isBundleLoader = false) { Optional buffer = readFile(path); if (!buffer) - return; + return nullptr; MemoryBufferRef mbref = *buffer; + InputFile *newFile = nullptr; - switch (identify_magic(mbref.getBuffer())) { + file_magic magic = identify_magic(mbref.getBuffer()); + switch (magic) { case file_magic::archive: { + // 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"); - inputFiles.push_back(make(std::move(file))); + 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()); + } + } + } + } else if (config->forceLoadObjC) { + for (const object::Archive::Symbol &sym : file->symbols()) + if (sym.getName().startswith(objc::klass)) + symtab->addUndefined(sym.getName(), /*file=*/nullptr, + /*isWeakRef=*/false); + + // 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()); + } + } + } + } + + newFile = loadedArchives[path] = make(std::move(file)); break; } case file_magic::macho_object: - inputFiles.push_back(make(mbref)); + newFile = make(mbref, getModTime(path), ""); break; case file_magic::macho_dynamically_linked_shared_lib: - inputFiles.push_back(make(mbref)); + case file_magic::macho_dynamically_linked_shared_lib_stub: + case file_magic::tapi_file: + if (DylibFile *dylibFile = loadDylib(mbref)) { + if (isExplicit) + dylibFile->explicitlyLinked = true; + newFile = dylibFile; + } break; - case file_magic::tapi_file: { - llvm::Expected> result = - TextAPIReader::get(mbref); - if (!result) - return; - - inputFiles.push_back(make(std::move(*result))); + case file_magic::bitcode: + newFile = make(mbref, "", 0); + break; + case file_magic::macho_executable: + case file_magic::macho_bundle: + // We only allow executable and bundle type here if it is used + // as a bundle loader. + if (!isBundleLoader) + error(path + ": unhandled file type"); + if (DylibFile *dylibFile = loadDylib(mbref, nullptr, isBundleLoader)) + newFile = dylibFile; break; - } default: error(path + ": unhandled file type"); } + if (newFile && !isa(newFile)) { + // printArchiveMemberLoad() prints both .a and .o names, so no need to + // print the .a name here. + if (config->printEachFile && magic != file_magic::archive) + message(toString(newFile)); + inputFiles.insert(newFile); + } + return newFile; +} + +static void addLibrary(StringRef name, bool isNeeded, bool isWeak, + bool isReexport, bool isExplicit, bool forceLoad) { + if (Optional path = findLibrary(name)) { + if (auto *dylibFile = dyn_cast_or_null( + addFile(*path, forceLoad, isExplicit))) { + if (isNeeded) + dylibFile->forceNeeded = true; + if (isWeak) + dylibFile->forceWeakImport = true; + if (isReexport) { + config->hasReexports = true; + dylibFile->reexport = true; + } + } + return; + } + error("library not found for -l" + name); +} + +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))) { + if (isNeeded) + dylibFile->forceNeeded = true; + if (isWeak) + dylibFile->forceWeakImport = true; + if (isReexport) { + config->hasReexports = true; + dylibFile->reexport = true; + } + } + return; + } + error("framework not found for -framework " + name); +} + +// Parses LC_LINKER_OPTION contents, which can add additional command line +// flags. +void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) { + SmallVector argv; + size_t offset = 0; + for (unsigned i = 0; i < argc && offset < data.size(); ++i) { + argv.push_back(data.data() + offset); + offset += strlen(data.data() + offset) + 1; + } + 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"); + } + } } -static std::array archNames{"arm", "arm64", "i386", - "x86_64", "ppc", "ppc64"}; -static bool isArchString(StringRef s) { - static DenseSet archNamesSet(archNames.begin(), archNames.end()); - return archNamesSet.find(s) != archNamesSet.end(); +static void addFileList(StringRef path) { + 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. +// 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) { +static void parseOrderFile(StringRef path) { Optional buffer = readFile(path); if (!buffer) { error("Could not read order file at " + path); @@ -213,59 +480,38 @@ void parseOrderFile(StringRef path) { MemoryBufferRef mbref = *buffer; size_t priority = std::numeric_limits::max(); - for (StringRef rest : args::getLines(mbref)) { - StringRef arch, objectFile, symbol; - - std::array fields; - uint8_t fieldCount = 0; - while (rest != "" && fieldCount < 3) { - std::pair p = getToken(rest, ": \t\n\v\f\r"); - StringRef tok = p.first; - rest = p.second; - - // Check if we have a comment - if (tok == "" || tok[0] == '#') - break; + for (StringRef line : args::getLines(mbref)) { + StringRef objectFile, symbol; + line = line.take_until([](char c) { return c == '#'; }); // ignore comments + line = line.ltrim(); - fields[fieldCount++] = tok; - } + 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); - switch (fieldCount) { - case 3: - arch = fields[0]; - objectFile = fields[1]; - symbol = fields[2]; - break; - case 2: - (isArchString(fields[0]) ? arch : objectFile) = fields[0]; - symbol = fields[1]; - break; - case 1: - symbol = fields[0]; - break; - case 0: - break; - default: - llvm_unreachable("too many fields in order file"); - } + if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType) + continue; - if (!arch.empty()) { - if (!isArchString(arch)) { - error("invalid arch \"" + arch + "\" in order file: expected one of " + - llvm::join(archNames, ", ")); - 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(); - // TODO: Update when we extend support for other archs - if (arch != "x86_64") - continue; - } - - if (!objectFile.empty() && !objectFile.endswith(".o")) { - error("invalid object file name \"" + objectFile + - "\" in order file: should end with .o"); - continue; + 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]; @@ -280,12 +526,16 @@ void parseOrderFile(StringRef path) { } // We expect sub-library names of the form "libfoo", which will match a dylib -// with a path of .*/libfoo.dylib. -static bool markSubLibrary(StringRef searchName) { +// with a path of .*/libfoo.{dylib, tbd}. +// XXX ld64 seems to ignore the extension entirely when matching sub-libraries; +// I'm not sure what the use case for that is. +static bool markReexport(StringRef searchName, ArrayRef extensions) { for (InputFile *file : inputFiles) { if (auto *dylibFile = dyn_cast(file)) { StringRef filename = path::filename(dylibFile->getName()); - if (filename.consume_front(searchName) && filename == ".dylib") { + if (filename.consume_front(searchName) && + (filename.empty() || + find(extensions, filename) != extensions.end())) { dylibFile->reexport = true; return true; } @@ -294,11 +544,230 @@ static bool markSubLibrary(StringRef searchName) { return false; } -static void handlePlatformVersion(const opt::Arg *arg) { - // TODO: implementation coming very soon ... +// This function is called on startup. We need this for LTO since +// LTO calls LLVM functions to compile bitcode files to native code. +// Technically this can be delayed until we read bitcode files, but +// we don't bother to do lazily because the initialization is fast. +static void initLLVM() { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); +} + +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; + } + + TimeTraceScope timeScope("LTO"); + auto *lto = make(); + for (InputFile *file : inputFiles) + if (auto *bitcodeFile = dyn_cast(file)) + lto->add(*bitcodeFile); + + for (ObjFile *file : lto->compile()) + inputFiles.insert(file); +} + +// Replaces common symbols with defined symbols residing in __common sections. +// This function must be called after all symbol names are resolved (i.e. after +// all InputFiles have been loaded.) As a result, later operations won't see +// any CommonSymbols. +static void replaceCommonSymbols() { + TimeTraceScope timeScope("Replace common symbols"); + ConcatOutputSection *osec = nullptr; + for (Symbol *sym : symtab->getSymbols()) { + auto *common = dyn_cast(sym); + if (common == nullptr) + continue; + + // Casting to size_t will truncate large values on 32-bit architectures, + // 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); + if (!osec) + osec = ConcatOutputSection::getOrCreateForInput(isec); + isec->parent = osec; + inputSections.push_back(isec); + + // 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); + } +} + +static void initializeSectionRenameMap() { + if (config->dataConst) { + SmallVector v{section_names::got, + section_names::authGot, + section_names::authPtr, + section_names::nonLazySymbolPtr, + section_names::const_, + section_names::cfString, + section_names::moduleInitFunc, + section_names::moduleTermFunc, + section_names::objcClassList, + section_names::objcNonLazyClassList, + section_names::objcCatList, + section_names::objcNonLazyCatList, + section_names::objcProtoList, + section_names::objcImageInfo}; + for (StringRef s : v) + config->sectionRenameMap[{segment_names::data, s}] = { + segment_names::dataConst, s}; + } + config->sectionRenameMap[{segment_names::text, section_names::staticInit}] = { + segment_names::text, section_names::text}; + config->sectionRenameMap[{segment_names::import, section_names::pointers}] = { + config->dataConst ? segment_names::dataConst : segment_names::data, + section_names::nonLazySymbolPtr}; +} + +static inline char toLowerDash(char x) { + if (x >= 'A' && x <= 'Z') + return x - 'A' + 'a'; + else if (x == ' ') + return '-'; + return x; +} + +static std::string lowerDash(StringRef s) { + return std::string(map_iterator(s.begin(), toLowerDash), + 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; + } + + StringRef platformStr = arg->getValue(0); + StringRef minVersionStr = arg->getValue(1); + StringRef sdkVersionStr = arg->getValue(2); + + // 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) + 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)) + error(Twine("malformed minimum version: ") + minVersionStr); + if (config->platformInfo.sdk.tryParse(sdkVersionStr)) + error(Twine("malformed sdk version: ") + sdkVersionStr); + return 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); + + config->platformInfo.target = + MachO::Target(getArchitectureFromName(archName), platform); + + uint32_t cpuType; + uint32_t cpuSubtype; + std::tie(cpuType, cpuSubtype) = getCPUTypeFromArchitecture(config->arch()); + + switch (cpuType) { + case CPU_TYPE_X86_64: + return createX86_64TargetInfo(); + case CPU_TYPE_ARM64: + return createARM64TargetInfo(); + case CPU_TYPE_ARM64_32: + return createARM64_32TargetInfo(); + case CPU_TYPE_ARM: + return createARMTargetInfo(cpuSubtype); + default: + fatal("missing or unsupported -arch " + archName); + } } -static void warnIfDeprecatedOption(const opt::Option &opt) { +static UndefinedSymbolTreatment +getUndefinedSymbolTreatment(const ArgList &args) { + StringRef treatmentStr = args.getLastArgValue(OPT_undefined); + auto treatment = + StringSwitch(treatmentStr) + .Cases("error", "", UndefinedSymbolTreatment::error) + .Case("warning", UndefinedSymbolTreatment::warning) + .Case("suppress", UndefinedSymbolTreatment::suppress) + .Case("dynamic_lookup", UndefinedSymbolTreatment::dynamic_lookup) + .Default(UndefinedSymbolTreatment::unknown); + if (treatment == UndefinedSymbolTreatment::unknown) { + warn(Twine("unknown -undefined TREATMENT '") + treatmentStr + + "', defaulting to 'error'"); + treatment = UndefinedSymbolTreatment::error; + } else if (config->namespaceKind == NamespaceKind::twolevel && + (treatment == UndefinedSymbolTreatment::warning || + treatment == UndefinedSymbolTreatment::suppress)) { + if (treatment == UndefinedSymbolTreatment::warning) + error("'-undefined warning' only valid with '-flat_namespace'"); + else + error("'-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) + .Case("safe", ICFLevel::safe) + .Case("all", ICFLevel::all) + .Default(ICFLevel::unknown); + if (icfLevel == ICFLevel::unknown) { + 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 void warnIfDeprecatedOption(const Option &opt) { if (!opt.getGroup().isValid()) return; if (opt.getGroup().getID() == OPT_grp_deprecated) { @@ -307,8 +776,8 @@ static void warnIfDeprecatedOption(const opt::Option &opt) { } } -static void warnIfUnimplementedOption(const opt::Option &opt) { - if (!opt.getGroup().isValid()) +static void warnIfUnimplementedOption(const Option &opt) { + if (!opt.getGroup().isValid() || !opt.hasFlag(DriverFlag::HelpHidden)) return; switch (opt.getGroup().getID()) { case OPT_grp_deprecated: @@ -332,120 +801,698 @@ static void warnIfUnimplementedOption(const opt::Option &opt) { } } -bool macho::link(llvm::ArrayRef argsArr, bool canExitEarly, +static const char *getReproduceOption(InputArgList &args) { + if (const Arg *arg = args.getLastArg(OPT_reproduce)) + return arg->getValue(); + return getenv("LLD_REPRODUCE"); +} + +static void parseClangOption(StringRef opt, const Twine &msg) { + std::string err; + raw_string_ostream os(err); + + const char *argv[] = {"lld", opt.data()}; + if (cl::ParseCommandLineOptions(2, argv, "", &os)) + return; + os.flush(); + error(msg + ": " + StringRef(err).trim()); +} + +static uint32_t parseDylibVersion(const ArgList &args, unsigned id) { + const Arg *arg = args.getLastArg(id); + if (!arg) + return 0; + + if (config->outputType != MH_DYLIB) { + error(arg->getAsString(args) + ": only valid with -dylib"); + return 0; + } + + PackedVersion version; + if (!version.parse32(arg->getValue())) { + error(arg->getAsString(args) + ": malformed version"); + return 0; + } + + return version.rawValue(); +} + +static uint32_t parseProtection(StringRef protStr) { + uint32_t prot = 0; + for (char c : protStr) { + switch (c) { + case 'r': + prot |= VM_PROT_READ; + break; + case 'w': + prot |= VM_PROT_WRITE; + break; + case 'x': + prot |= VM_PROT_EXECUTE; + break; + case '-': + break; + default: + error("unknown -segprot letter '" + Twine(c) + "' in " + protStr); + return 0; + } + } + return prot; +} + +static std::vector parseSectAlign(const opt::InputArgList &args) { + std::vector sectAligns; + for (const Arg *arg : args.filtered(OPT_sectalign)) { + StringRef segName = arg->getValue(0); + StringRef sectName = arg->getValue(1); + StringRef alignStr = arg->getValue(2); + if (alignStr.startswith("0x") || alignStr.startswith("0X")) + alignStr = alignStr.drop_front(2); + uint32_t align; + if (alignStr.getAsInteger(16, align)) { + error("-sectalign: failed to parse '" + StringRef(arg->getValue(2)) + + "' as number"); + continue; + } + if (!isPowerOf2_32(align)) { + error("-sectalign: '" + StringRef(arg->getValue(2)) + + "' (in base 16) not a power of two"); + continue; + } + sectAligns.push_back({segName, sectName, align}); + } + return sectAligns; +} + +PlatformKind macho::removeSimulator(PlatformKind platform) { + switch (platform) { + case PlatformKind::iOSSimulator: + return PlatformKind::iOS; + case PlatformKind::tvOSSimulator: + return PlatformKind::tvOS; + case PlatformKind::watchOSSimulator: + return PlatformKind::watchOS; + default: + return platform; + } +} + +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); + auto it = llvm::find_if(minVersion, + [&](const auto &p) { return p.first == platform; }); + if (it != minVersion.end()) + if (config->platformInfo.minimum < it->second) + return false; + + switch (config->outputType) { + case MH_EXECUTE: + return !args.hasArg(OPT_no_pie); + case MH_BUNDLE: + // FIXME: return false when -final_name ... + // has prefix "/System/Library/UserEventPlugins/" + // or matches "/usr/libexec/locationd" "/usr/libexec/terminusd" + return true; + case MH_DYLIB: + return true; + case MH_OBJECT: + return false; + default: + llvm_unreachable( + "unsupported output type for determining data-const default"); + } + return false; +} + +void SymbolPatterns::clear() { + literals.clear(); + globs.clear(); +} + +void SymbolPatterns::insert(StringRef symbolName) { + if (symbolName.find_first_of("*?[]") == StringRef::npos) + literals.insert(CachedHashStringRef(symbolName)); + else if (Expected pattern = GlobPattern::create(symbolName)) + globs.emplace_back(*pattern); + else + error("invalid symbol-name pattern: " + symbolName); +} + +bool SymbolPatterns::matchLiteral(StringRef symbolName) const { + return literals.contains(CachedHashStringRef(symbolName)); +} + +bool SymbolPatterns::matchGlob(StringRef symbolName) const { + for (const GlobPattern &glob : globs) + if (glob.match(symbolName)) + return true; + return false; +} + +bool SymbolPatterns::match(StringRef symbolName) const { + return matchLiteral(symbolName) || matchGlob(symbolName); +} + +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); + } + } +} + +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(). + for (const Arg *arg : args) { + const Option &opt = arg->getOption(); + warnIfDeprecatedOption(opt); + warnIfUnimplementedOption(opt); + + switch (opt.getID()) { + case OPT_INPUT: + addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false); + break; + case OPT_needed_library: + if (auto *dylibFile = dyn_cast_or_null( + addFile(rerootPath(arg->getValue()), false))) + dylibFile->forceNeeded = true; + break; + case OPT_reexport_library: + if (auto *dylibFile = dyn_cast_or_null(addFile( + rerootPath(arg->getValue()), /*forceLoadArchive=*/false))) { + config->hasReexports = true; + dylibFile->reexport = true; + } + break; + case OPT_weak_library: + if (auto *dylibFile = dyn_cast_or_null( + addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false))) + dylibFile->forceWeakImport = true; + break; + case OPT_filelist: + addFileList(arg->getValue()); + break; + case OPT_force_load: + addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/true); + break; + case OPT_l: + case OPT_needed_l: + case OPT_reexport_l: + case OPT_weak_l: + addLibrary(arg->getValue(), opt.getID() == OPT_needed_l, + opt.getID() == OPT_weak_l, opt.getID() == OPT_reexport_l, + /*isExplicit=*/true, /*forceLoad=*/false); + break; + case OPT_framework: + case OPT_needed_framework: + case OPT_reexport_framework: + case OPT_weak_framework: + addFramework(arg->getValue(), opt.getID() == OPT_needed_framework, + opt.getID() == OPT_weak_framework, + opt.getID() == OPT_reexport_framework, /*isExplicit=*/true); + break; + default: + break; + } + } +} + +static void gatherInputSections() { + TimeTraceScope timeScope("Gathering input sections"); + int inputOrder = 0; + for (const InputFile *file : inputFiles) { + for (const SubsectionMap &map : file->subsections) { + ConcatOutputSection *osec = nullptr; + for (const SubsectionEntry &entry : map) { + if (auto *isec = dyn_cast(entry.isec)) { + if (isec->isCoalescedWeak()) + continue; + if (isec->getSegName() == segment_names::ld) { + assert(isec->getName() == section_names::compactUnwind); + in.unwindInfo->addInput(isec); + continue; + } + isec->outSecOff = inputOrder++; + if (!osec) + 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)) { + if (in.wordLiteralSection->inputOrder == UnspecifiedInputOrder) + in.wordLiteralSection->inputOrder = inputOrder++; + in.wordLiteralSection->addInput(isec); + } else { + llvm_unreachable("unexpected input section kind"); + } + } + } + } + assert(inputOrder <= UnspecifiedInputOrder); +} + +static void foldIdenticalLiterals() { + // 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(); +} + +static void referenceStubBinder() { + bool needsStubHelper = config->outputType == MH_DYLIB || + config->outputType == MH_EXECUTE || + config->outputType == MH_BUNDLE; + if (!needsStubHelper || !symtab->find("dyld_stub_binder")) + return; + + // dyld_stub_binder is used by dyld to resolve lazy bindings. This code here + // adds a opportunistic reference to dyld_stub_binder if it happens to exist. + // 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 + // 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; + errorHandler().cleanupCallback = []() { freeArena(); }; + + errorHandler().logName = args::getFilenameWithoutExe(argsArr[0]); stderrOS.enable_colors(stderrOS.has_colors()); - // TODO: Set up error handler properly, e.g. the errorLimitExceededMsg MachOOptTable parser; - opt::InputArgList args = parser.parse(argsArr.slice(1)); + 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); if (args.hasArg(OPT_help_hidden)) { parser.printHelp(argsArr[0], /*showHidden=*/true); return true; - } else if (args.hasArg(OPT_help)) { + } + if (args.hasArg(OPT_help)) { parser.printHelp(argsArr[0], /*showHidden=*/false); return true; } + if (args.hasArg(OPT_version)) { + message(getLLDVersion()); + return true; + } config = make(); symtab = make(); target = createTargetInfo(args); + depTracker = + make(args.getLastArgValue(OPT_dependency_info)); + + // Must be set before any InputSections and Symbols are created. + config->deadStrip = args.hasArg(OPT_dead_strip); + + config->systemLibraryRoots = getSystemLibraryRoots(args); + if (const char *path = getReproduceOption(args)) { + // Note that --reproduce is a debug option so you can ignore it + // if you are trying to understand the whole picture of the code. + Expected> errOrWriter = + TarWriter::create(path, path::stem(path)); + if (errOrWriter) { + tar = std::move(*errOrWriter); + tar->append("response.txt", createResponseFile(args)); + tar->append("version.txt", getLLDVersion() + "\n"); + } else { + error("--reproduce: " + toString(errOrWriter.takeError())); + } + } + + if (auto *arg = args.getLastArg(OPT_threads_eq)) { + StringRef v(arg->getValue()); + unsigned threads = 0; + if (!llvm::to_integer(v, threads, 0) || threads == 0) + error(arg->getSpelling() + ": expected a positive integer, but got '" + + arg->getValue() + "'"); + parallel::strategy = hardware_concurrency(threads); + config->thinLTOJobs = v; + } + if (auto *arg = args.getLastArg(OPT_thinlto_jobs_eq)) + config->thinLTOJobs = arg->getValue(); + if (!get_threadpool_strategy(config->thinLTOJobs)) + error("--thinlto-jobs: invalid job count: " + config->thinLTOJobs); + + for (const Arg *arg : args.filtered(OPT_u)) { + config->explicitUndefineds.push_back(symtab->addUndefined( + arg->getValue(), /*file=*/nullptr, /*isWeakRef=*/false)); + } - config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main")); + for (const Arg *arg : args.filtered(OPT_U)) + config->explicitDynamicLookups.insert(arg->getValue()); + + config->mapFile = args.getLastArgValue(OPT_map); + config->optimize = args::getInteger(args, OPT_O, 1); config->outputFile = args.getLastArgValue(OPT_o, "a.out"); - config->installName = - args.getLastArgValue(OPT_install_name, config->outputFile); - getLibrarySearchPaths(config->librarySearchPaths, args); - getFrameworkSearchPaths(config->frameworkSearchPaths, args); - config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE; + config->finalOutput = + args.getLastArgValue(OPT_final_output, config->outputFile); + config->astPaths = args.getAllArgValues(OPT_add_ast_path); + config->headerPad = args::getHex(args, OPT_headerpad, /*Default=*/32); + config->headerPadMaxInstallNames = + args.hasArg(OPT_headerpad_max_install_names); + config->printDylibSearch = + 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); + 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); + } + 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->runtimePaths = args::getStrings(args, OPT_rpath); + config->allLoad = args.hasArg(OPT_all_load); + config->archMultiple = args.hasArg(OPT_arch_multiple); + config->applicationExtension = args.hasFlag( + OPT_application_extension, OPT_no_application_extension, false); + config->exportDynamic = args.hasArg(OPT_export_dynamic); + config->forceLoadObjC = args.hasArg(OPT_ObjC); + config->forceLoadSwift = args.hasArg(OPT_force_load_swift_libs); + config->deadStripDylibs = args.hasArg(OPT_dead_strip_dylibs); + config->demangle = args.hasArg(OPT_demangle); + config->implicitDylibs = !args.hasArg(OPT_no_implicit_dylibs); + config->emitFunctionStarts = + args.hasFlag(OPT_function_starts, OPT_no_function_starts, true); + config->emitBitcodeBundle = args.hasArg(OPT_bitcode_bundle); + config->emitDataInCodeInfo = + args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, true); + config->icfLevel = getICFLevel(args); + config->dedupLiterals = args.hasArg(OPT_deduplicate_literals) || + config->icfLevel != ICFLevel::none; + + // FIXME: Add a commandline flag for this too. + config->zeroModTime = getenv("ZERO_AR_DATE"); + + std::array encryptablePlatforms{ + PlatformKind::iOS, PlatformKind::watchOS, PlatformKind::tvOS}; + config->emitEncryptionInfo = + args.hasFlag(OPT_encryptable, OPT_no_encryption, + is_contained(encryptablePlatforms, config->platform())); + +#ifndef LLVM_HAVE_LIBXAR + if (config->emitBitcodeBundle) + error("-bitcode_bundle unsupported because LLD wasn't built with libxar"); +#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"); + else + config->installName = arg->getValue(); + } else if (config->outputType == MH_DYLIB) { + config->installName = config->finalOutput; + } + + if (args.hasArg(OPT_mark_dead_strippable_dylib)) { + if (config->outputType != MH_DYLIB) + warn("-mark_dead_strippable_dylib: ignored, only has effect with -dylib"); + else + config->markDeadStrippableDylib = true; + } + + if (const Arg *arg = args.getLastArg(OPT_static, OPT_dynamic)) + config->staticLink = (arg->getOption().getID() == OPT_static); + + if (const Arg *arg = + args.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) + config->namespaceKind = arg->getOption().getID() == OPT_twolevel_namespace + ? NamespaceKind::twolevel + : NamespaceKind::flat; + + config->undefinedSymbolTreatment = getUndefinedSymbolTreatment(args); + + if (config->outputType == MH_EXECUTE) + config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"), + /*file=*/nullptr, + /*isWeakRef=*/false); + + config->librarySearchPaths = + getLibrarySearchPaths(args, config->systemLibraryRoots); + config->frameworkSearchPaths = + getFrameworkSearchPaths(args, config->systemLibraryRoots); + if (const Arg *arg = + args.getLastArg(OPT_search_paths_first, OPT_search_dylibs_first)) + config->searchDylibsFirst = + arg->getOption().getID() == OPT_search_dylibs_first; + + config->dylibCompatibilityVersion = + parseDylibVersion(args, OPT_compatibility_version); + config->dylibCurrentVersion = parseDylibVersion(args, OPT_current_version); + + config->dataConst = + args.hasFlag(OPT_data_const, OPT_no_data_const, dataConstDefault(args)); + // Populate config->sectionRenameMap with builtin default renames. + // Options -rename_section and -rename_segment are able to override. + initializeSectionRenameMap(); + // Reject every special character except '.' and '$' + // TODO(gkm): verify that this is the proper set of invalid chars + StringRef invalidNameChars("!\"#%&'()*+,-/:;<=>?@[\\]^`{|}~"); + auto validName = [invalidNameChars](StringRef s) { + if (s.find_first_of(invalidNameChars) != StringRef::npos) + error("invalid name for segment or section: " + s); + return s; + }; + for (const Arg *arg : args.filtered(OPT_rename_section)) { + config->sectionRenameMap[{validName(arg->getValue(0)), + validName(arg->getValue(1))}] = { + validName(arg->getValue(2)), validName(arg->getValue(3))}; + } + for (const Arg *arg : args.filtered(OPT_rename_segment)) { + config->segmentRenameMap[validName(arg->getValue(0))] = + validName(arg->getValue(1)); + } + + config->sectionAlignments = parseSectAlign(args); + + for (const Arg *arg : args.filtered(OPT_segprot)) { + StringRef segName = arg->getValue(0); + uint32_t maxProt = parseProtection(arg->getValue(1)); + uint32_t initProt = parseProtection(arg->getValue(2)); + if (maxProt != initProt && config->arch() != AK_i386) + error("invalid argument '" + arg->getAsString(args) + + "': max and init must be the same for non-i386 archs"); + if (segName == segment_names::linkEdit) + error("-segprot cannot be used to change __LINKEDIT's protections"); + config->segmentProtections.push_back({segName, maxProt, initProt}); + } + + 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(); + } + // Explicitly-exported literal symbols must be defined, but might + // languish in an archive if unreferenced elsewhere. Light a fire + // under those lazy symbols! + for (const CachedHashStringRef &cachedName : config->exportedSymbols.literals) + symtab->addUndefined(cachedName.val(), /*file=*/nullptr, + /*isWeakRef=*/false); + + 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); if (args.hasArg(OPT_v)) { message(getLLDVersion()); message(StringRef("Library search paths:") + - (config->librarySearchPaths.size() - ? "\n\t" + llvm::join(config->librarySearchPaths, "\n\t") - : "")); + (config->librarySearchPaths.empty() + ? "" + : "\n\t" + join(config->librarySearchPaths, "\n\t"))); message(StringRef("Framework search paths:") + - (config->frameworkSearchPaths.size() - ? "\n\t" + llvm::join(config->frameworkSearchPaths, "\n\t") - : "")); - freeArena(); - return !errorCount(); + (config->frameworkSearchPaths.empty() + ? "" + : "\n\t" + join(config->frameworkSearchPaths, "\n\t"))); } - for (const auto &arg : args) { - const auto &opt = arg->getOption(); - warnIfDeprecatedOption(opt); - switch (arg->getOption().getID()) { - case OPT_INPUT: - addFile(arg->getValue()); - break; - case OPT_l: { - StringRef name = arg->getValue(); - if (Optional path = findLibrary(name)) { - addFile(*path); - break; - } - error("library not found for -l" + name); - break; - } - case OPT_platform_version: - handlePlatformVersion(arg); - break; - case OPT_o: - case OPT_dylib: - case OPT_e: - case OPT_L: - case OPT_Z: - case OPT_arch: - // handled elsewhere - break; - default: - warnIfUnimplementedOption(opt); - break; + config->progName = argsArr[0]; + + config->timeTraceEnabled = args.hasArg( + OPT_time_trace, OPT_time_trace_granularity_eq, OPT_time_trace_file_eq); + config->timeTraceGranularity = + args::getInteger(args, OPT_time_trace_granularity_eq, 500); + + // Initialize time trace profiler. + if (config->timeTraceEnabled) + timeTraceProfilerInitialize(config->timeTraceGranularity, config->progName); + + { + TimeTraceScope timeScope("ExecuteLinker"); + + 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. + { + auto reexportHandler = [](const Arg *arg, + const std::vector &extensions) { + config->hasReexports = true; + StringRef searchName = arg->getValue(); + if (!markReexport(searchName, extensions)) + error(arg->getSpelling() + " " + searchName + + " does not match a supplied dylib"); + }; + std::vector extensions = {".tbd"}; + for (const Arg *arg : args.filtered(OPT_sub_umbrella)) + reexportHandler(arg, extensions); + + extensions.push_back(".dylib"); + for (const Arg *arg : args.filtered(OPT_sub_library)) + reexportHandler(arg, extensions); } - } - // Now that all dylibs have been loaded, search for those that should be - // re-exported. - for (opt::Arg *arg : args.filtered(OPT_sub_library)) { - config->hasReexports = true; - StringRef searchName = arg->getValue(); - if (!markSubLibrary(searchName)) - error("-sub_library " + searchName + " does not match a supplied dylib"); - } + // Parse LTO options. + if (const Arg *arg = args.getLastArg(OPT_mcpu)) + parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + arg->getSpelling()); - StringRef orderFile = args.getLastArgValue(OPT_order_file); - if (!orderFile.empty()) - parseOrderFile(orderFile); + for (const Arg *arg : args.filtered(OPT_mllvm)) + parseClangOption(arg->getValue(), arg->getSpelling()); - if (config->outputType == MH_EXECUTE && !isa(config->entry)) { - error("undefined symbol: " + config->entry->getName()); - return false; - } + compileBitcodeFiles(); + replaceCommonSymbols(); - createSyntheticSections(); + StringRef orderFile = args.getLastArgValue(OPT_order_file); + if (!orderFile.empty()) + parseOrderFile(orderFile); - // Initialize InputSections. - for (InputFile *file : inputFiles) { - for (SubsectionMap &map : file->subsections) { - for (auto &p : map) { - InputSection *isec = p.second; - inputSections.push_back(isec); + 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); + if (buffer) + inputFiles.insert(make(*buffer, segName, sectName)); + } + + gatherInputSections(); + + if (config->deadStrip) + markLive(); + + // ICF assumes that all literals have been folded already, so we must run + // foldIdenticalLiterals before foldIdenticalSections. + foldIdenticalLiterals(); + if (config->icfLevel != ICFLevel::none) + foldIdenticalSections(); + + // Write to an output file. + if (target->wordSize == 8) + writeResult(); + else + writeResult(); + + depTracker->write(getLLDVersion(), inputFiles, config->outputFile); } - // Write to an output file. - writeResult(); + 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()); }); + } + + timeTraceProfilerCleanup(); + } if (canExitEarly) exitLld(errorCount() ? 1 : 0); - freeArena(); return !errorCount(); } diff --git a/gnu/llvm/lld/MachO/Driver.h b/gnu/llvm/lld/MachO/Driver.h index 2233740d1db..10f307e780c 100644 --- a/gnu/llvm/lld/MachO/Driver.h +++ b/gnu/llvm/lld/MachO/Driver.h @@ -10,11 +10,28 @@ #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/Option/OptTable.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include + +namespace llvm { +namespace MachO { +class InterfaceFile; +enum class PlatformKind : unsigned; +} // namespace MachO +} // namespace llvm namespace lld { namespace macho { +class DylibFile; +class InputFile; + class MachOOptTable : public llvm::opt::OptTable { public: MachOOptTable(); @@ -30,6 +47,80 @@ enum { #undef OPTION }; +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); + +DylibFile *loadDylib(llvm::MemoryBufferRef mbref, DylibFile *umbrella = nullptr, + bool isBundleLoader = false); + +// Search for all possible combinations of `{root}/{name}.{extension}`. +// If \p extensions are not specified, then just search for `{root}/{name}`. +llvm::Optional +findPathCombination(const llvm::Twine &name, + const std::vector &roots, + ArrayRef extensions = {""}); + +// If -syslibroot is specified, absolute paths to non-object files may be +// 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); + +// Helper class to export dependency info. +class DependencyTracker { +public: + explicit DependencyTracker(llvm::StringRef path); + + // Adds the given path to the set of not-found files. + inline void logFileNotFound(const Twine &path) { + if (active) + notFounds.insert(path.str()); + } + + // Writes the dependencies to specified path. + // The content is sorted by its Op Code, then within each section, + // alphabetical order. + void write(llvm::StringRef version, + const llvm::SetVector &inputs, + llvm::StringRef output); + +private: + enum DepOpCode : uint8_t { + // Denotes the linker version. + Version = 0x00, + // Denotes the input files. + Input = 0x10, + // Denotes the files that do not exist(?) + NotFound = 0x11, + // Denotes the output files. + Output = 0x40, + }; + + const llvm::StringRef path; + bool active; + + // The paths need to be alphabetically ordered. + // We need to own the paths because some of them are temporarily + // constructed. + std::set notFounds; +}; + +extern DependencyTracker *depTracker; + } // namespace macho } // namespace lld diff --git a/gnu/llvm/lld/MachO/DriverUtils.cpp b/gnu/llvm/lld/MachO/DriverUtils.cpp new file mode 100644 index 00000000000..fc25182d914 --- /dev/null +++ b/gnu/llvm/lld/MachO/DriverUtils.cpp @@ -0,0 +1,372 @@ +//===- DriverUtils.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "Driver.h" +#include "InputFiles.h" +#include "ObjC.h" +#include "Target.h" + +#include "lld/Common/Args.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.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" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/TextAPIReader.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::opt; +using namespace llvm::sys; +using namespace lld; +using namespace lld::macho; + +// Create prefix string literals used in Options.td +#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +// Create table mapping all options defined in Options.td +static const 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}, +#include "Options.inc" +#undef OPTION +}; + +MachOOptTable::MachOOptTable() : OptTable(optInfo) {} + +// Set color diagnostics according to --color-diagnostics={auto,always,never} +// or --no-color-diagnostics flags. +static void handleColorDiagnostics(InputArgList &args) { + const Arg *arg = + args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, + OPT_no_color_diagnostics); + if (!arg) + return; + if (arg->getOption().getID() == OPT_color_diagnostics) { + lld::errs().enable_colors(true); + } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { + lld::errs().enable_colors(false); + } else { + StringRef s = arg->getValue(); + if (s == "always") + lld::errs().enable_colors(true); + else if (s == "never") + lld::errs().enable_colors(false); + else if (s != "auto") + error("unknown option: --color-diagnostics=" + s); + } +} + +InputArgList MachOOptTable::parse(ArrayRef argv) { + // Make InputArgList from string vectors. + unsigned missingIndex; + unsigned missingCount; + SmallVector vec(argv.data(), argv.data() + argv.size()); + + // Expand response files (arguments in the form of @) + // and then parse the argument again. + 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); + + if (missingCount) + error(Twine(args.getArgString(missingIndex)) + ": missing argument"); + + handleColorDiagnostics(args); + + for (const Arg *arg : args.filtered(OPT_UNKNOWN)) { + std::string nearest; + if (findNearest(arg->getAsString(args), nearest) > 1) + error("unknown argument '" + arg->getAsString(args) + "'"); + else + error("unknown argument '" + arg->getAsString(args) + + "', did you mean '" + nearest + "'"); + } + return args; +} + +void MachOOptTable::printHelp(const char *argv0, bool showHidden) const { + OptTable::printHelp(lld::outs(), + (std::string(argv0) + " [options] file...").c_str(), + "LLVM Linker", showHidden); + lld::outs() << "\n"; +} + +static std::string rewritePath(StringRef s) { + if (fs::exists(s)) + return relativeToRoot(s); + return std::string(s); +} + +static std::string rewriteInputPath(StringRef s) { + // Don't bother rewriting "absolute" paths that are actually under the + // syslibroot; simply rewriting the syslibroot is sufficient. + if (rerootPath(s) == s && fs::exists(s)) + return relativeToRoot(s); + return std::string(s); +} + +// Reconstructs command line arguments so that so that you can re-run +// the same command with the same inputs. This is for --reproduce. +std::string macho::createResponseFile(const InputArgList &args) { + SmallString<0> data; + raw_svector_ostream os(data); + + // Copy the command line to the output while rewriting paths. + for (const Arg *arg : args) { + switch (arg->getOption().getID()) { + case OPT_reproduce: + break; + case OPT_INPUT: + os << quote(rewriteInputPath(arg->getValue())) << "\n"; + break; + case OPT_o: + os << "-o " << quote(path::filename(arg->getValue())) << "\n"; + break; + case OPT_filelist: + if (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: + os << arg->getSpelling() << " " + << quote(rewriteInputPath(arg->getValue())) << "\n"; + break; + case OPT_F: + case OPT_L: + 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())) + << "\n"; + break; + case OPT_sectcreate: + os << arg->getSpelling() << " " << quote(arg->getValue(0)) << " " + << quote(arg->getValue(1)) << " " + << quote(rewritePath(arg->getValue(2))) << "\n"; + break; + default: + os << toString(*arg) << "\n"; + } + } + return std::string(data.str()); +} + +static void searchedDylib(const Twine &path, bool found) { + if (config->printDylibSearch) + message("searched " + path + (found ? ", found " : ", not found")); + if (!found) + depTracker->logFileNotFound(path); +} + +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 {}; +} + +// It's not uncommon to have multiple attempts to load a single dylib, +// especially if it's a commonly re-exported core library. +static DenseMap loadedDylibs; + +DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, + bool isBundleLoader) { + CachedHashStringRef path(mbref.getBufferIdentifier()); + DylibFile *&file = loadedDylibs[path]; + if (file) + return file; + + DylibFile *newFile; + file_magic magic = identify_magic(mbref.getBuffer()); + if (magic == file_magic::tapi_file) { + Expected> result = TextAPIReader::get(mbref); + if (!result) { + error("could not load TAPI file at " + mbref.getBufferIdentifier() + + ": " + toString(result.takeError())); + return nullptr; + } + file = make(**result, umbrella, isBundleLoader); + + // parseReexports() can recursively call loadDylib(). That's fine since + // we wrote the DylibFile we just loaded to the loadDylib cache via the + // `file` reference. But the recursive load can grow loadDylibs, so the + // `file` reference might become invalid after parseReexports() -- so copy + // the pointer it refers to before continuing. + newFile = file; + if (newFile->exportingFile) + newFile->parseReexports(**result); + } else { + assert(magic == file_magic::macho_dynamically_linked_shared_lib || + magic == file_magic::macho_dynamically_linked_shared_lib_stub || + magic == file_magic::macho_executable || + magic == file_magic::macho_bundle); + file = make(mbref, umbrella, isBundleLoader); + + // parseLoadCommands() can also recursively call loadDylib(). See comment + // in previous block for why this means we must copy `file` here. + newFile = file; + if (newFile->exportingFile) + newFile->parseLoadCommands(mbref); + } + return newFile; +} + +Optional +macho::findPathCombination(const Twine &name, + const std::vector &roots, + ArrayRef extensions) { + SmallString<261> base; + for (StringRef dir : roots) { + base = dir; + path::append(base, name); + for (StringRef ext : extensions) { + Twine location = base + ext; + bool exists = fs::exists(location); + searchedDylib(location, exists); + if (exists) + return saver.save(location.str()); + } + } + return {}; +} + +StringRef macho::rerootPath(StringRef path) { + if (!path::is_absolute(path, path::Style::posix) || path.endswith(".o")) + return path; + + if (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; + + fs::file_status stat; + if (!fs::status(path, stat)) + if (fs::exists(stat)) + return toTimeT(stat.getLastModificationTime()); + + warn("failed to get modification time of " + path); + return 0; +} + +void macho::printArchiveMemberLoad(StringRef reason, const InputFile *f) { + if (config->printEachFile) + message(toString(f)); + if (config->printWhyLoad) + message(reason + " forced load of " + toString(f)); +} + +macho::DependencyTracker::DependencyTracker(StringRef path) + : path(path), active(!path.empty()) { + if (active && fs::exists(path) && !fs::can_write(path)) { + warn("Ignoring dependency_info option since specified path is not " + "writeable."); + active = false; + } +} + +void macho::DependencyTracker::write(StringRef version, + const SetVector &inputs, + StringRef output) { + if (!active) + return; + + std::error_code ec; + raw_fd_ostream os(path, ec, fs::OF_None); + if (ec) { + warn("Error writing dependency info to file"); + return; + } + + auto addDep = [&os](DepOpCode opcode, const StringRef &path) { + // XXX: Even though DepOpCode's underlying type is uint8_t, + // this cast is still needed because Clang older than 10.x has a bug, + // where it doesn't know to cast the enum to its underlying type. + // Hence `<< DepOpCode` is ambiguous to it. + os << static_cast(opcode); + os << path; + os << '\0'; + }; + + addDep(DepOpCode::Version, version); + + // Sort the input by its names. + std::vector inputNames; + inputNames.reserve(inputs.size()); + for (InputFile *f : inputs) + inputNames.push_back(f->getName()); + llvm::sort(inputNames); + + for (const StringRef &in : inputNames) + addDep(DepOpCode::Input, in); + + for (const std::string &f : notFounds) + addDep(DepOpCode::NotFound, f); + + addDep(DepOpCode::Output, output); +} diff --git a/gnu/llvm/lld/MachO/Dwarf.cpp b/gnu/llvm/lld/MachO/Dwarf.cpp new file mode 100644 index 00000000000..c142cc1b169 --- /dev/null +++ b/gnu/llvm/lld/MachO/Dwarf.cpp @@ -0,0 +1,43 @@ +//===- DWARF.cpp ----------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Dwarf.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "OutputSegment.h" + +#include + +using namespace lld; +using namespace lld::macho; +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. + for (const InputSection *isec : obj->debugSections) { + if (StringRef *s = + StringSwitch(isec->getName()) + .Case(section_names::debugInfo, &dObj->infoSection.Data) + .Case(section_names::debugAbbrev, &dObj->abbrevSection) + .Case(section_names::debugStr, &dObj->strSection) + .Default(nullptr)) { + *s = toStringRef(isec->data); + hasDwarfInfo = true; + } + } + + if (hasDwarfInfo) + return dObj; + return nullptr; +} diff --git a/gnu/llvm/lld/MachO/Dwarf.h b/gnu/llvm/lld/MachO/Dwarf.h new file mode 100644 index 00000000000..119f2778fc6 --- /dev/null +++ b/gnu/llvm/lld/MachO/Dwarf.h @@ -0,0 +1,53 @@ +//===- DWARF.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===-------------------------------------------------------------------===// + +#ifndef LLD_MACHO_DWARF_H +#define LLD_MACHO_DWARF_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFObject.h" + +namespace lld { +namespace macho { + +class ObjFile; + +// Implements the interface between LLVM's DWARF-parsing utilities and LLD's +// InputSection structures. +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 { + // TODO: implement this + return llvm::None; + } + + void forEachInfoSections( + llvm::function_ref f) const override { + f(infoSection); + } + + llvm::StringRef getAbbrevSection() const override { return abbrevSection; } + llvm::StringRef getStrSection() const override { return strSection; } + + // 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::StringRef abbrevSection; + llvm::StringRef strSection; +}; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/ExportTrie.cpp b/gnu/llvm/lld/MachO/ExportTrie.cpp index 7cc81bcfd5f..372690a20df 100644 --- a/gnu/llvm/lld/MachO/ExportTrie.cpp +++ b/gnu/llvm/lld/MachO/ExportTrie.cpp @@ -44,7 +44,6 @@ #include "llvm/Support/LEB128.h" using namespace llvm; -using namespace llvm::MachO; using namespace lld; using namespace lld::macho; @@ -59,7 +58,23 @@ struct Edge { struct ExportInfo { uint64_t address; - // TODO: Add proper support for re-exports & stub-and-resolver flags. + uint8_t flags = 0; + ExportInfo(const Symbol &sym, uint64_t imageBase) + : address(sym.getVA() - imageBase) { + using namespace llvm::MachO; + // Set the symbol type. + if (sym.isWeakDef()) + flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + // TODO: Add proper support for re-exports & stub-and-resolver flags. + + // Set the symbol kind. + if (sym.isTlv()) { + flags |= EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; + } else if (auto *defined = dyn_cast(&sym)) { + if (defined->isAbsolute()) + flags |= EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE; + } + } }; } // namespace @@ -83,9 +98,8 @@ bool TrieNode::updateOffset(size_t &nextOffset) { // node. size_t nodeSize; if (info) { - uint64_t flags = 0; uint32_t terminalSize = - getULEB128Size(flags) + getULEB128Size(info->address); + getULEB128Size(info->flags) + getULEB128Size(info->address); // Overall node size so far is the uleb128 size of the length of the symbol // info + the symbol info itself. nodeSize = terminalSize + getULEB128Size(terminalSize); @@ -94,7 +108,7 @@ bool TrieNode::updateOffset(size_t &nextOffset) { } // Compute size of all child edges. ++nodeSize; // Byte for number of children. - for (Edge &edge : edges) { + for (const Edge &edge : edges) { nodeSize += edge.substring.size() + 1 // String length. + getULEB128Size(edge.child->offset); // Offset len. } @@ -110,11 +124,10 @@ void TrieNode::writeTo(uint8_t *buf) const { buf += offset; if (info) { // TrieNodes with Symbol info: size, flags address - uint64_t flags = 0; // TODO: emit proper flags uint32_t terminalSize = - getULEB128Size(flags) + getULEB128Size(info->address); + getULEB128Size(info->flags) + getULEB128Size(info->address); buf += encodeULEB128(terminalSize, buf); - buf += encodeULEB128(flags, buf); + buf += encodeULEB128(info->flags, buf); buf += encodeULEB128(info->address, buf); } else { // TrieNode with no Symbol info. @@ -194,7 +207,7 @@ tailcall: if (isTerminal) { assert(j - i == 1); // no duplicate symbols - node->info = {pivotSymbol->getVA()}; + node->info = ExportInfo(*pivotSymbol, imageBase); } else { // This is the tail-call-optimized version of the following: // sortAndBuild(vec.slice(i, j - i), node, lastPos, pos + 1); diff --git a/gnu/llvm/lld/MachO/ExportTrie.h b/gnu/llvm/lld/MachO/ExportTrie.h index 2bd8c33db9a..a43f4f2cce9 100644 --- a/gnu/llvm/lld/MachO/ExportTrie.h +++ b/gnu/llvm/lld/MachO/ExportTrie.h @@ -22,6 +22,7 @@ class Symbol; class TrieBuilder { public: + 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. size_t build(); @@ -32,6 +33,7 @@ private: void sortAndBuild(llvm::MutableArrayRef vec, TrieNode *node, size_t lastPos, size_t pos); + uint64_t imageBase = 0; std::vector exported; std::vector nodes; }; diff --git a/gnu/llvm/lld/MachO/ICF.cpp b/gnu/llvm/lld/MachO/ICF.cpp new file mode 100644 index 00000000000..370a325125c --- /dev/null +++ b/gnu/llvm/lld/MachO/ICF.cpp @@ -0,0 +1,369 @@ +//===- ICF.cpp ------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ICF.h" +#include "ConcatOutputSection.h" +#include "InputSection.h" +#include "Symbols.h" +#include "UnwindInfoSection.h" + +#include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" + +#include + +using namespace llvm; +using namespace lld; +using namespace lld::macho; + +class ICF { +public: + ICF(std::vector &inputs); + + void run(); + void segregate(size_t begin, size_t end, + std::function + equals); + 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); + + // ICF needs a copy of the inputs vector because its equivalence-class + // segregation algorithm destroys the proper sequence. + std::vector icfInputs; +}; + +ICF::ICF(std::vector &inputs) { + icfInputs.assign(inputs.begin(), inputs.end()); +} + +// ICF = Identical Code Folding +// +// We only fold __TEXT,__text, so this is really "code" folding, and not +// "COMDAT" folding. String and scalar constant literals are deduplicated +// elsewhere. +// +// Summary of segments & sections: +// +// The __TEXT segment is readonly at the MMU. Some sections are already +// deduplicated elsewhere (__TEXT,__cstring & __TEXT,__literal*) and some are +// synthetic and inherently free of duplicates (__TEXT,__stubs & +// __TEXT,__unwind_info). Note that we don't yet run ICF on __TEXT,__const, +// because doing so induces many test failures. +// +// The __LINKEDIT segment is readonly at the MMU, yet entirely synthetic, and +// thus ineligible for ICF. +// +// The __DATA_CONST segment is read/write at the MMU, but is logically const to +// the application after dyld applies fixups to pointer data. We currently +// fold only the __DATA_CONST,__cfstring section. +// +// The __DATA segment is read/write at the MMU, and as application-writeable +// data, none of its sections are eligible for ICF. +// +// Please see the large block comment in lld/ELF/ICF.cpp for an explanation +// of the segregation algorithm. +// +// 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) { + // We can only fold within the same OutputSection. + if (ia->parent != ib->parent) + return false; + if (ia->data.size() != ib->data.size()) + return false; + if (ia->data != ib->data) + return false; + if (ia->relocs.size() != ib->relocs.size()) + return false; + auto f = [](const Reloc &ra, const Reloc &rb) { + if (ra.type != rb.type) + return false; + if (ra.pcrel != rb.pcrel) + return false; + if (ra.length != rb.length) + 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; + 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; + } + } else { + isecA = ra.referent.get(); + isecB = rb.referent.get(); + } + + if (isecA->parent != isecB->parent) + return false; + // Sections with identical parents should be of the same kind. + assert(isecA->kind() == isecB->kind()); + // We will compare ConcatInputSection contents in equalsVariable. + if (isa(isecA)) + return true; + // 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); + }; + return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), + f); +} + +// Compare the "moving" parts of two ConcatInputSections -- i.e. everything not +// handled by equalsConstant(). +static bool equalsVariable(const ConcatInputSection *ia, + const ConcatInputSection *ib) { + assert(ia->relocs.size() == ib->relocs.size()); + auto f = [](const Reloc &ra, const Reloc &rb) { + // We already filtered out mismatching values/addends in equalsConstant. + if (ra.referent == rb.referent) + return true; + const ConcatInputSection *isecA, *isecB; + if (ra.referent.is()) { + // Matching DylibSymbols are already filtered out by the + // identical-referent check above. Non-matching DylibSymbols were filtered + // out in equalsConstant(). So we can safely cast to Defined here. + const auto *da = cast(ra.referent.get()); + const auto *db = cast(rb.referent.get()); + if (da->isAbsolute()) + return true; + isecA = dyn_cast(da->isec); + if (!isecA) + return true; // literal sections were checked in equalsConstant. + isecB = cast(db->isec); + } else { + const auto *sa = ra.referent.get(); + const auto *sb = rb.referent.get(); + isecA = dyn_cast(sa); + if (!isecA) + return true; + isecB = cast(sb); + } + return isecA->icfEqClass[icfPass % 2] == isecB->icfEqClass[icfPass % 2]; + }; + return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), + f); +} + +// Find the first InputSection after BEGIN whose equivalence class differs +size_t ICF::findBoundary(size_t begin, size_t end) { + uint64_t beginHash = icfInputs[begin]->icfEqClass[icfPass % 2]; + for (size_t i = begin + 1; i < end; ++i) + if (beginHash != icfInputs[i]->icfEqClass[icfPass % 2]) + return i; + return end; +} + +// Invoke FUNC on subranges with matching equivalence class +void ICF::forEachClassRange(size_t begin, size_t end, + std::function func) { + while (begin < end) { + size_t mid = findBoundary(begin, end); + func(begin, mid); + begin = mid; + } +} + +// Split icfInputs into shards, then parallelize invocation of FUNC on subranges +// with matching equivalence class +void ICF::forEachClass(std::function func) { + // Only use threads when the benefits outweigh the overhead. + const size_t threadingThreshold = 1024; + if (icfInputs.size() < threadingThreshold) { + forEachClassRange(0, icfInputs.size(), func); + ++icfPass; + return; + } + + // Shard into non-overlapping intervals, and call FUNC in parallel. The + // sharding must be completed before any calls to FUNC are made so that FUNC + // can modify the InputSection in its shard without causing data races. + const size_t shards = 256; + size_t step = icfInputs.size() / shards; + size_t boundaries[shards + 1]; + boundaries[0] = 0; + boundaries[shards] = icfInputs.size(); + parallelForEachN(1, shards, [&](size_t i) { + boundaries[i] = findBoundary((i - 1) * step, icfInputs.size()); + }); + parallelForEachN(1, shards + 1, [&](size_t i) { + if (boundaries[i - 1] < boundaries[i]) { + forEachClassRange(boundaries[i - 1], boundaries[i], func); + } + }); + ++icfPass; +} + +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]; + 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 (defined->isec) { + if (auto isec = dyn_cast(defined->isec)) + hash += defined->value + isec->icfEqClass[icfPass % 2]; + else + hash += defined->isec->kind() + + defined->isec->getOffset(defined->value); + } else { + hash += defined->value; + } + } else + llvm_unreachable("foldIdenticalSections symbol kind"); + } + } + // Set MSB to 1 to avoid collisions with non-hashed classes. + isec->icfEqClass[(icfPass + 1) % 2] = hash | (1ull << 63); + }); + } + + llvm::stable_sort( + 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); }); + + // Split equivalence groups by comparing relocations until convergence + do { + icfRepeat = false; + forEachClass([&](size_t begin, size_t end) { + segregate(begin, end, equalsVariable); + }); + } while (icfRepeat); + log("ICF needed " + Twine(icfPass) + " iterations"); + + // Fold sections within equivalence classes + forEachClass([&](size_t begin, size_t end) { + if (end - begin < 2) + return; + ConcatInputSection *beginIsec = icfInputs[begin]; + for (size_t i = begin + 1; i < end; ++i) + beginIsec->foldIdentical(icfInputs[i]); + }); +} + +// Split an equivalence class into smaller classes. +void ICF::segregate( + size_t begin, size_t end, + std::function + 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); + }); + size_t mid = bound - icfInputs.begin(); + + // Split [begin, end) into [begin, mid) and [mid, end). We use mid as an + // equivalence class ID because every group ends with a unique index. + for (size_t i = begin; i < mid; ++i) + icfInputs[i]->icfEqClass[(icfPass + 1) % 2] = mid; + + // If we created a group, we need to iterate the main loop again. + if (mid != end) + icfRepeat = true; + + begin = mid; + } +} + +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()); + } + } + return result; +} + +void macho::foldIdenticalSections() { + 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 + // sections referenced by their relocs. We could recursively traverse the + // relocs to find every referenced InputSection, but that precludes easy + // 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 + // equivalence-class IDs which begin at 0. Since hashes & unique IDs never + // 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; + 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 + isec->icfEqClass[0] = ++icfUniqueID; + } + parallelForEach(hashable, + [](ConcatInputSection *isec) { isec->hashForICF(); }); + // Now that every input section is either hashed or marked as unique, run the + // segregation algorithm to detect foldable subsections. + ICF(hashable).run(); +} diff --git a/gnu/llvm/lld/MachO/ICF.h b/gnu/llvm/lld/MachO/ICF.h new file mode 100644 index 00000000000..9500a946601 --- /dev/null +++ b/gnu/llvm/lld/MachO/ICF.h @@ -0,0 +1,23 @@ +//===- ICF.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_ICF_H +#define LLD_MACHO_ICF_H + +#include "lld/Common/LLVM.h" +#include + +namespace lld { +namespace macho { + +void foldIdenticalSections(); + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/InputFiles.cpp b/gnu/llvm/lld/MachO/InputFiles.cpp index 46fe82f9882..a4fb9035193 100644 --- a/gnu/llvm/lld/MachO/InputFiles.cpp +++ b/gnu/llvm/lld/MachO/InputFiles.cpp @@ -43,20 +43,32 @@ #include "InputFiles.h" #include "Config.h" +#include "Driver.h" +#include "Dwarf.h" #include "ExportTrie.h" #include "InputSection.h" #include "MachOStructs.h" +#include "ObjC.h" #include "OutputSection.h" +#include "OutputSegment.h" #include "SymbolTable.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.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/Endian.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/TarWriter.h" +#include "llvm/TextAPI/Architecture.h" +#include "llvm/TextAPI/InterfaceFile.h" using namespace llvm; using namespace llvm::MachO; @@ -65,13 +77,106 @@ using namespace llvm::sys; using namespace lld; using namespace lld::macho; -std::vector macho::inputFiles; +// Returns "", "foo.a(bar.o)", or "baz.o". +std::string lld::toString(const InputFile *f) { + if (!f) + return ""; + + // Multiple dylibs can be defined in one .tbd file. + if (auto dylibFile = dyn_cast(f)) + if (f->getName().endswith(".tbd")) + return (f->getName() + "(" + dylibFile->installName + ")").str(); + + if (f->archiveName.empty()) + return std::string(f->getName()); + return (f->archiveName + "(" + path::filename(f->getName()) + ")").str(); +} + +SetVector macho::inputFiles; +std::unique_ptr macho::tar; +int InputFile::idCount = 0; + +static VersionTuple decodeVersion(uint32_t version) { + unsigned major = version >> 16; + unsigned minor = (version >> 8) & 0xffu; + unsigned subMinor = version & 0xffu; + return VersionTuple(major, minor, subMinor); +} + +static std::vector getPlatformInfos(const InputFile *input) { + if (!isa(input) && !isa(input)) + return {}; + + const char *hdr = input->mb.getBufferStart(); + + std::vector platformInfos; + for (auto *cmd : findCommands(hdr, LC_BUILD_VERSION)) { + PlatformInfo info; + info.target.Platform = static_cast(cmd->platform); + info.minimum = decodeVersion(cmd->minos); + platformInfos.emplace_back(std::move(info)); + } + for (auto *cmd : findCommands( + hdr, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, + LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS)) { + PlatformInfo info; + switch (cmd->cmd) { + case LC_VERSION_MIN_MACOSX: + info.target.Platform = PlatformKind::macOS; + break; + case LC_VERSION_MIN_IPHONEOS: + info.target.Platform = PlatformKind::iOS; + break; + case LC_VERSION_MIN_TVOS: + info.target.Platform = PlatformKind::tvOS; + break; + case LC_VERSION_MIN_WATCHOS: + info.target.Platform = PlatformKind::watchOS; + break; + } + info.minimum = decodeVersion(cmd->version); + platformInfos.emplace_back(std::move(info)); + } + + return platformInfos; +} + +static bool checkCompatibility(const InputFile *input) { + std::vector platformInfos = getPlatformInfos(input); + if (platformInfos.empty()) + return true; + + auto it = find_if(platformInfos, [&](const PlatformInfo &info) { + return removeSimulator(info.target.Platform) == + removeSimulator(config->platform()); + }); + if (it == platformInfos.end()) { + std::string platformNames; + raw_string_ostream os(platformNames); + interleave( + platformInfos, os, + [&](const PlatformInfo &info) { + os << getPlatformName(info.target.Platform); + }, + "/"); + error(toString(input) + " has platform " + platformNames + + Twine(", which is different from target platform ") + + getPlatformName(config->platform())); + return false; + } + + if (it->minimum > config->platformInfo.minimum) + warn(toString(input) + " has version " + it->minimum.getAsString() + + ", which is newer than target minimum of " + + config->platformInfo.minimum.getAsString()); + + return true; +} // Open a given file path and return it as a memory-mapped file. Optional macho::readFile(StringRef path) { - // Open a file. - auto mbOrErr = MemoryBuffer::getFile(path); - if (auto ec = mbOrErr.getError()) { + ErrorOr> mbOrErr = MemoryBuffer::getFile(path); + if (std::error_code ec = mbOrErr.getError()) { error("cannot open " + path + ": " + ec.message()); return None; } @@ -82,15 +187,18 @@ Optional macho::readFile(StringRef path) { // If this is a regular non-fat file, return it. const char *buf = mbref.getBufferStart(); - auto *hdr = reinterpret_cast(buf); - if (read32be(&hdr->magic) != MachO::FAT_MAGIC) + const auto *hdr = reinterpret_cast(buf); + if (mbref.getBufferSize() < sizeof(uint32_t) || + read32be(&hdr->magic) != FAT_MAGIC) { + if (tar) + tar->append(relativeToRoot(path), mbref.getBuffer()); return mbref; + } - // Object files and archive files may be fat files, which contains - // 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. - auto *arch = reinterpret_cast(buf + sizeof(*hdr)); + // 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)); for (uint32_t i = 0, n = read32be(&hdr->nfat_arch); i < n; ++i) { if (reinterpret_cast(arch + i + 1) > @@ -99,7 +207,7 @@ Optional macho::readFile(StringRef path) { return None; } - if (read32be(&arch[i].cputype) != target->cpuType || + if (read32be(&arch[i].cputype) != static_cast(target->cpuType) || read32be(&arch[i].cpusubtype) != target->cpuSubtype) continue; @@ -107,6 +215,8 @@ Optional macho::readFile(StringRef path) { uint32_t size = read32be(&arch[i].size); if (offset + size > mbref.getBufferSize()) 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)); } @@ -114,38 +224,76 @@ Optional macho::readFile(StringRef path) { return None; } -static const load_command *findCommand(const mach_header_64 *hdr, - uint32_t type) { - const uint8_t *p = - reinterpret_cast(hdr) + sizeof(mach_header_64); +InputFile::InputFile(Kind kind, const InterfaceFile &interface) + : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {} - for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { - auto *cmd = reinterpret_cast(p); - if (cmd->cmd == type) - return cmd; - p += cmd->cmdsize; - } - return nullptr; -} - -void InputFile::parseSections(ArrayRef sections) { +template +void ObjFile::parseSections(ArrayRef
sections) { subsections.reserve(sections.size()); auto *buf = reinterpret_cast(mb.getBufferStart()); - for (const section_64 &sec : sections) { - InputSection *isec = make(); - isec->file = this; - isec->name = StringRef(sec.sectname, strnlen(sec.sectname, 16)); - isec->segname = StringRef(sec.segname, strnlen(sec.segname, 16)); - isec->data = {isZeroFill(sec.flags) ? nullptr : buf + sec.offset, - static_cast(sec.size)}; - if (sec.align >= 32) - error("alignment " + std::to_string(sec.align) + " of section " + - isec->name + " is too large"); - else - isec->align = 1 << sec.align; - isec->flags = sec.flags; - subsections.push_back({{0, isec}}); + for (const Section &sec : sections) { + 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)}; + if (sec.align >= 32) { + error("alignment " + std::to_string(sec.align) + " of section " + name + + " is too large"); + subsections.push_back({}); + continue; + } + 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); + } + 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)}); + } 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 { + // 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({}); + debugSections.push_back(isec); + } + } } } @@ -157,251 +305,926 @@ void InputFile::parseSections(ArrayRef sections) { // same location as an offset relative to the start of the containing // subsection. static InputSection *findContainingSubsection(SubsectionMap &map, - uint32_t *offset) { - auto it = std::prev(map.upper_bound(*offset)); - *offset -= it->first; - return it->second; + uint64_t *offset) { + auto it = std::prev(llvm::upper_bound( + map, *offset, [](uint64_t value, SubsectionEntry subsecEntry) { + return value < subsecEntry.offset; + })); + *offset -= it->offset; + return it->isec; +} + +template +static bool validateRelocationInfo(InputFile *file, const Section &sec, + relocation_info rel) { + const RelocAttrs &relocAttrs = target->getRelocAttrs(rel.r_type); + bool valid = true; + auto message = [relocAttrs, file, sec, rel, &valid](const Twine &diagnostic) { + valid = false; + return (relocAttrs.name + " relocation " + diagnostic + " at offset " + + std::to_string(rel.r_address) + " of " + sec.segname + "," + + sec.sectname + " in " + toString(file)) + .str(); + }; + + if (!relocAttrs.hasAttr(RelocAttrBits::LOCAL) && !rel.r_extern) + error(message("must be extern")); + if (relocAttrs.hasAttr(RelocAttrBits::PCREL) != rel.r_pcrel) + error(message(Twine("must ") + (rel.r_pcrel ? "not " : "") + + "be PC-relative")); + if (isThreadLocalVariables(sec.flags) && + !relocAttrs.hasAttr(RelocAttrBits::UNSIGNED)) + error(message("not allowed in thread-local section, must be UNSIGNED")); + if (rel.r_length < 2 || rel.r_length > 3 || + !relocAttrs.hasAttr(static_cast(1 << rel.r_length))) { + static SmallVector widths{"0", "4", "8", "4 or 8"}; + error(message("has width " + std::to_string(1 << rel.r_length) + + " bytes, but must be " + + widths[(static_cast(relocAttrs.bits) >> 2) & 3] + + " bytes")); + } + return valid; } -void InputFile::parseRelocations(const section_64 &sec, - SubsectionMap &subsecMap) { +template +void ObjFile::parseRelocations(ArrayRef
sectionHeaders, + const Section &sec, SubsectionMap &subsecMap) { auto *buf = reinterpret_cast(mb.getBufferStart()); - ArrayRef relInfos( - reinterpret_cast(buf + sec.reloff), - sec.nreloc); + ArrayRef relInfos( + reinterpret_cast(buf + sec.reloff), sec.nreloc); - for (const any_relocation_info &anyRel : relInfos) { - if (anyRel.r_word0 & R_SCATTERED) - fatal("TODO: Scattered relocations not supported"); + auto subsecIt = subsecMap.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 + // need them because the *_RELOC_RELA records contain the extra + // addend field, vs. *_RELOC_REL which omit the addend. + // + // The {X86_64,ARM64}_RELOC_SUBTRACTOR record holds the subtrahend, + // and the paired *_RELOC_UNSIGNED record holds the minuend. The + // datum for each is a symbolic address. The result is the offset + // between two addresses. + // + // The ARM64_RELOC_ADDEND record holds the addend, and the paired + // 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. - auto rel = reinterpret_cast(anyRel); + int64_t pairedAddend = 0; + relocation_info relInfo = relInfos[i]; + if (target->hasAttr(relInfo.r_type, RelocAttrBits::ADDEND)) { + pairedAddend = SignExtend64<24>(relInfo.r_symbolnum); + relInfo = relInfos[++i]; + } + assert(i < relInfos.size()); + if (!validateRelocationInfo(this, sec, relInfo)) + continue; + 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; Reloc r; - r.type = rel.r_type; - r.pcrel = rel.r_pcrel; - r.length = rel.r_length; - uint64_t rawAddend = target->getImplicitAddend(mb, sec, rel); - - if (rel.r_extern) { - r.target = symbols[rel.r_symbolnum]; - r.addend = rawAddend; + r.type = relInfo.r_type; + r.pcrel = relInfo.r_pcrel; + r.length = relInfo.r_length; + r.offset = relInfo.r_address; + if (relInfo.r_extern) { + r.referent = symbols[relInfo.r_symbolnum]; + r.addend = isSubtrahend ? 0 : totalAddend; } else { - if (rel.r_symbolnum == 0 || rel.r_symbolnum > subsections.size()) - fatal("invalid section index in relocation for offset " + - std::to_string(r.offset) + " in section " + sec.sectname + - " of " + getName()); - - SubsectionMap &targetSubsecMap = subsections[rel.r_symbolnum - 1]; - const section_64 &targetSec = sectionHeaders[rel.r_symbolnum - 1]; - uint32_t targetOffset; - if (rel.r_pcrel) { + assert(!isSubtrahend); + const Section &referentSec = sectionHeaders[relInfo.r_symbolnum - 1]; + uint64_t referentOffset; + if (relInfo.r_pcrel) { // The implicit addend for pcrel section relocations is the pcrel offset // in terms of the addresses in the input file. Here we adjust it so - // that it describes the offset from the start of the target section. - // TODO: The offset of 4 is probably not right for ARM64, nor for - // relocations with r_length != 2. - targetOffset = - sec.addr + rel.r_address + 4 + rawAddend - targetSec.addr; + // that it describes the offset from the start of the referent section. + // FIXME This logic was written around x86_64 behavior -- ARM64 doesn't + // 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; } else { // The addend for a non-pcrel relocation is its absolute address. - targetOffset = rawAddend - targetSec.addr; + referentOffset = totalAddend - referentSec.addr; } - r.target = findContainingSubsection(targetSubsecMap, &targetOffset); - r.addend = targetOffset; + SubsectionMap &referentSubsecMap = subsections[relInfo.r_symbolnum - 1]; + r.referent = findContainingSubsection(referentSubsecMap, &referentOffset); + r.addend = referentOffset; } - r.offset = rel.r_address; - InputSection *subsec = findContainingSubsection(subsecMap, &r.offset); + // Find the subsection that this relocation belongs to. + // Though not required by the Mach-O format, clang and gcc seem to emit + // relocations in order, so let's take advantage of it. However, ld64 emits + // unsorted relocations (in `-r` mode), so we have a fallback for that + // uncommon case. + InputSection *subsec; + while (subsecIt != subsecMap.rend() && subsecIt->offset > r.offset) + ++subsecIt; + if (subsecIt == subsecMap.rend() || + subsecIt->offset + subsecIt->isec->getSize() <= r.offset) { + subsec = findContainingSubsection(subsecMap, &r.offset); + // Now that we know the relocs are unsorted, avoid trying the 'fast path' + // for the other relocations. + subsecIt = subsecMap.rend(); + } else { + subsec = subsecIt->isec; + r.offset -= subsecIt->offset; + } subsec->relocs.push_back(r); + + if (isSubtrahend) { + relocation_info minuendInfo = relInfos[++i]; + // SUBTRACTOR relocations should always be followed by an UNSIGNED one + // attached to the same address. + assert(target->hasAttr(minuendInfo.r_type, RelocAttrBits::UNSIGNED) && + relInfo.r_address == minuendInfo.r_address); + Reloc p; + p.type = minuendInfo.r_type; + if (minuendInfo.r_extern) { + p.referent = symbols[minuendInfo.r_symbolnum]; + p.addend = totalAddend; + } else { + uint64_t referentOffset = + totalAddend - sectionHeaders[minuendInfo.r_symbolnum - 1].addr; + SubsectionMap &referentSubsecMap = + subsections[minuendInfo.r_symbolnum - 1]; + p.referent = + findContainingSubsection(referentSubsecMap, &referentOffset); + p.addend = referentOffset; + } + subsec->relocs.push_back(p); + } } } -void InputFile::parseSymbols(ArrayRef nList, - const char *strtab, bool subsectionsViaSymbols) { - // resize(), not reserve(), because we are going to create N_ALT_ENTRY symbols - // out-of-sequence. - symbols.resize(nList.size()); - std::vector altEntrySymIdxs; +template +static macho::Symbol *createDefined(const NList &sym, StringRef name, + InputSection *isec, uint64_t value, + uint64_t size) { + // 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 + // linker sees them. + // N_EXT | N_PEXT: Linkage unit (think: dylib) scoped. These go in the + // symbol table during the link so that duplicates are + // either reported (for non-weak symbols) or merged + // (for weak symbols), but they do not go in the export + // table of the output. + // N_PEXT: llvm-mc does not emit these, but `ld -r` (wherein ld64 emits + // object files) may produce them. LLD does not yet support -r. + // These are translation-unit scoped, identical to the `0` case. + // 0: Translation-unit scoped. These are not in the symbol table during + // link, and not in the export table of the output either. + bool isWeakDefCanBeHidden = + (sym.n_desc & (N_WEAK_DEF | N_WEAK_REF)) == (N_WEAK_DEF | N_WEAK_REF); - auto createDefined = [&](const structs::nlist_64 &sym, InputSection *isec, - uint32_t value) -> Symbol * { - StringRef name = strtab + sym.n_strx; - if (sym.n_type & N_EXT) - // Global defined symbol - return symtab->addDefined(name, isec, value); - else - // Local defined symbol - return make(name, isec, value); - }; + if (sym.n_type & N_EXT) { + bool isPrivateExtern = sym.n_type & N_PEXT; + // 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 + // just merges metadata and keeps the contents of the first symbol + // with that name (see SymbolTable::addDefined). For: + // * inline function F in a TU built with -fvisibility-inlines-hidden + // * and inline function F in another TU built without that flag + // ld64 will pick the one from the file built without + // -fvisibility-inlines-hidden. + // lld will instead pick the one listed first on the link command line and + // give it visibility as if the function was built without + // -fvisibility-inlines-hidden. + // If both functions have the same contents, this will have the same + // behavior. If not, it won't, but the input had an ODR violation in + // that case. + // + // Similarly, merging a symbol + // that's isPrivateExtern and not isWeakDefCanBeHidden with one + // that's not isPrivateExtern but isWeakDefCanBeHidden technically + // should produce one + // that's not isPrivateExtern but isWeakDefCanBeHidden. That matters + // 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) + isPrivateExtern = true; - for (size_t i = 0, n = nList.size(); i < n; ++i) { - const structs::nlist_64 &sym = nList[i]; + 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); + } - // Undefined symbol - if (!sym.n_sect) { - StringRef name = strtab + sym.n_strx; - symbols[i] = symtab->addUndefined(name); - continue; - } + assert(!isWeakDefCanBeHidden && + "weak_def_can_be_hidden on already-hidden symbol?"); + return make( + name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF, + /*isExternal=*/false, /*isPrivateExtern=*/false, + sym.n_desc & N_ARM_THUMB_DEF, sym.n_desc & REFERENCED_DYNAMICALLY, + sym.n_desc & N_NO_DEAD_STRIP); +} + +// Absolute symbols are defined symbols that do not have an associated +// InputSection. They cannot be weak. +template +static macho::Symbol *createAbsolute(const NList &sym, InputFile *file, + StringRef name) { + if (sym.n_type & N_EXT) { + 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); + } + return make(name, file, nullptr, sym.n_value, /*size=*/0, + /*isWeakDef=*/false, + /*isExternal=*/false, /*isPrivateExtern=*/false, + 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) { + uint8_t type = sym.n_type & N_TYPE; + 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); + case N_ABS: + return createAbsolute(sym, this, name); + case N_PBUD: + case N_INDR: + error("TODO: support symbols of type " + std::to_string(type)); + return nullptr; + case N_SECT: + llvm_unreachable( + "N_SECT symbols should not be passed to parseNonSectionSymbol"); + default: + llvm_unreachable("invalid symbol type"); + } +} + +template +static bool isUndef(const NList &sym) { + return (sym.n_type & N_TYPE) == N_UNDF && sym.n_value == 0; +} - const section_64 &sec = sectionHeaders[sym.n_sect - 1]; - SubsectionMap &subsecMap = subsections[sym.n_sect - 1]; - uint64_t offset = sym.n_value - sec.addr; +template +void ObjFile::parseSymbols(ArrayRef sectionHeaders, + ArrayRef nList, + const char *strtab, bool subsectionsViaSymbols) { + using NList = typename LP::nlist; - // If the input file does not use subsections-via-symbols, all symbols can - // use the same subsection. Otherwise, we must split the sections along - // symbol boundaries. - if (!subsectionsViaSymbols) { - symbols[i] = createDefined(sym, subsecMap[0], offset); + // Groups indices of the symbols by the sections that contain them. + std::vector> symbolsBySection(subsections.size()); + symbols.resize(nList.size()); + SmallVector undefineds; + for (uint32_t i = 0; i < nList.size(); ++i) { + const NList &sym = nList[i]; + + // Ignore debug symbols for now. + // FIXME: may need special handling. + 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]; + // parseSections() may have chosen not to parse this section. + if (subsecMap.empty()) + continue; + symbolsBySection[sym.n_sect - 1].push_back(i); + } else if (isUndef(sym)) { + undefineds.push_back(i); + } else { + symbols[i] = parseNonSectionSymbol(sym, name); } + } - // nList entries aren't necessarily arranged in address order. Therefore, - // we can't create alt-entry symbols at this point because a later symbol - // may split its section, which may affect which subsection the alt-entry - // symbol is assigned to. So we need to handle them in a second pass below. - if (sym.n_desc & N_ALT_ENTRY) { - altEntrySymIdxs.push_back(i); + for (size_t i = 0; i < subsections.size(); ++i) { + SubsectionMap &subsecMap = subsections[i]; + if (subsecMap.empty()) continue; - } - // Find the subsection corresponding to the greatest section offset that is - // <= that of the current symbol. The subsection that we find either needs - // to be used directly or split in two. - uint32_t firstSize = offset; - InputSection *firstIsec = findContainingSubsection(subsecMap, &firstSize); + std::vector &symbolIndices = symbolsBySection[i]; + uint64_t sectionAddr = sectionHeaders[i].addr; + uint32_t sectionAlign = 1u << sectionHeaders[i].align; - if (firstSize == 0) { - // Alias of an existing symbol, or the first symbol in the section. These - // are handled by reusing the existing section. - symbols[i] = createDefined(sym, firstIsec, 0); + InputSection *isec = subsecMap.back().isec; + // __cfstring has 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)) { + for (size_t j = 0; j < symbolIndices.size(); ++j) { + 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); + if (symbolOffset != 0) { + error(toString(this) + ": __cfstring contains symbol " + name + + " at misaligned offset"); + continue; + } + symbols[symIndex] = createDefined(sym, name, isec, 0, isec->getSize()); + } continue; } - // We saw a symbol definition at a new offset. Split the section into two - // subsections. The new symbol uses the second subsection. - auto *secondIsec = make(*firstIsec); - secondIsec->data = firstIsec->data.slice(firstSize); - firstIsec->data = firstIsec->data.slice(0, firstSize); - // 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. - secondIsec->align = MinAlign(firstIsec->align, offset); + // Calculate symbol sizes and create subsections by splitting the sections + // along symbol boundaries. + // We populate subsecMap by repeatedly splitting the last (highest address) + // subsection. + llvm::stable_sort(symbolIndices, [&](uint32_t lhs, uint32_t rhs) { + 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 NList &sym = nList[symIndex]; + StringRef name = strtab + sym.n_strx; + InputSection *isec = subsecEntry.isec; + + uint64_t subsecAddr = sectionAddr + subsecEntry.offset; + size_t symbolOffset = sym.n_value - subsecAddr; + uint64_t symbolSize = + j + 1 < symbolIndices.size() + ? nList[symbolIndices[j + 1]].n_value - sym.n_value + : isec->data.size() - symbolOffset; + // There are 4 cases where we do not need to create a new subsection: + // 1. If the input file does not use subsections-via-symbols. + // 2. Multiple symbols at the same address only induce one subsection. + // (The symbolOffset == 0 check covers both this case as well as + // the first loop iteration.) + // 3. Alternative entry points do not induce new subsections. + // 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); + 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() + nextIsec->data = {nullptr, isec->data.size() - symbolOffset}; + isec->data = {nullptr, symbolOffset}; + } else { + nextIsec->data = isec->data.slice(symbolOffset); + isec->data = isec->data.slice(0, symbolOffset); + } - subsecMap[offset] = secondIsec; - // By construction, the symbol will be at offset zero in the new section. - symbols[i] = createDefined(sym, secondIsec, 0); + // By construction, the symbol will be at offset zero in the new + // subsection. + symbols[symIndex] = + createDefined(sym, name, nextIsec, /*value=*/0, symbolSize); + // 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(); + } } - for (size_t idx : altEntrySymIdxs) { - const structs::nlist_64 &sym = nList[idx]; - SubsectionMap &subsecMap = subsections[sym.n_sect - 1]; - uint32_t off = sym.n_value - sectionHeaders[sym.n_sect - 1].addr; - InputSection *subsec = findContainingSubsection(subsecMap, &off); - symbols[idx] = createDefined(sym, subsec, off); + // Undefined symbols can trigger recursive fetch from Archives due to + // LazySymbols. Process defined symbols first so that the relative order + // between a defined symbol and an undefined symbol does not change the + // 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); } } -ObjFile::ObjFile(MemoryBufferRef mb) : InputFile(ObjKind, mb) { +OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName, + StringRef sectName) + : 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); + isec->live = true; + subsections.push_back({{0, isec}}); +} + +ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName) + : InputFile(ObjKind, mb), modTime(modTime) { + this->archiveName = std::string(archiveName); + 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 NList = typename LP::nlist; + auto *buf = reinterpret_cast(mb.getBufferStart()); - auto *hdr = 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())); + return; + } + + if (!checkCompatibility(this)) + return; - if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) { - auto *c = reinterpret_cast(cmd); - sectionHeaders = ArrayRef{ - reinterpret_cast(c + 1), c->nsects}; + for (auto *cmd : findCommands(hdr, LC_LINKER_OPTION)) { + StringRef data{reinterpret_cast(cmd + 1), + cmd->cmdsize - sizeof(linker_option_command)}; + parseLCLinkerOption(this, cmd->count, data); + } + + ArrayRef
sectionHeaders; + if (const load_command *cmd = findCommand(hdr, LP::segmentLCType)) { + auto *c = reinterpret_cast(cmd); + sectionHeaders = + ArrayRef
{reinterpret_cast(c + 1), c->nsects}; parseSections(sectionHeaders); } // TODO: Error on missing LC_SYMTAB? if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) { auto *c = reinterpret_cast(cmd); - ArrayRef nList( - reinterpret_cast(buf + c->symoff), c->nsyms); + ArrayRef nList(reinterpret_cast(buf + c->symoff), + c->nsyms); const char *strtab = reinterpret_cast(buf) + c->stroff; bool subsectionsViaSymbols = hdr->flags & MH_SUBSECTIONS_VIA_SYMBOLS; - parseSymbols(nList, strtab, subsectionsViaSymbols); + parseSymbols(sectionHeaders, nList, strtab, subsectionsViaSymbols); } // 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) - parseRelocations(sectionHeaders[i], subsections[i]); + if (!subsections[i].empty()) + parseRelocations(sectionHeaders, sectionHeaders[i], subsections[i]); + + parseDebugInfo(); + if (config->emitDataInCodeInfo) + parseDataInCode(); +} + +void ObjFile::parseDebugInfo() { + std::unique_ptr dObj = DwarfObject::create(this); + if (!dObj) + return; + + auto *ctx = make( + std::move(dObj), "", + [&](Error err) { + warn(toString(this) + ": " + toString(std::move(err))); + }, + [&](Error warning) { + warn(toString(this) + ": " + toString(std::move(warning))); + }); + + // TODO: Since object files can contain a lot of DWARF info, we should verify + // that we are parsing just the info we need + const DWARFContext::compile_unit_range &units = ctx->compile_units(); + // FIXME: There can be more than one compile unit per object file. See + // PR48637. + auto it = units.begin(); + compileUnit = it->get(); +} + +void ObjFile::parseDataInCode() { + const auto *buf = reinterpret_cast(mb.getBufferStart()); + const load_command *cmd = findCommand(buf, LC_DATA_IN_CODE); + if (!cmd) + 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; + })); +} + +// The path can point to either a dylib or a .tbd file. +static DylibFile *loadDylib(StringRef path, DylibFile *umbrella) { + Optional mbref = readFile(path); + if (!mbref) { + error("could not read dylib file at " + path); + return nullptr; + } + return loadDylib(*mbref, umbrella); } -DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella) - : InputFile(DylibKind, mb) { +// TBD files are parsed into a series of TAPI documents (InterfaceFiles), with +// the first document storing child pointers to the rest of them. When we are +// processing a given TBD file, we store that top-level document in +// currentTopLevelTapi. When processing re-exports, we search its children for +// potentially matching documents in the same TBD file. Note that the children +// themselves don't point to further documents, i.e. this is a two-level tree. +// +// Re-exports can either refer to on-disk files, or to documents within .tbd +// files. +static DylibFile *findDylib(StringRef path, DylibFile *umbrella, + const InterfaceFile *currentTopLevelTapi) { + // Search order: + // 1. Install name basename in -F / -L directories. + { + StringRef stem = path::stem(path); + SmallString<128> frameworkName; + path::append(frameworkName, path::Style::posix, stem + ".framework", stem); + bool isFramework = path.endswith(frameworkName); + if (isFramework) { + for (StringRef dir : config->frameworkSearchPaths) { + SmallString<128> candidate = dir; + path::append(candidate, frameworkName); + if (Optional dylibPath = resolveDylibPath(candidate)) + return loadDylib(*dylibPath, umbrella); + } + } else if (Optional dylibPath = findPathCombination( + stem, config->librarySearchPaths, {".tbd", ".dylib"})) + return loadDylib(*dylibPath, umbrella); + } + + // 2. As absolute path. + if (path::is_absolute(path, path::Style::posix)) + for (StringRef root : config->systemLibraryRoots) + if (Optional dylibPath = + resolveDylibPath((root + path).str())) + return loadDylib(*dylibPath, umbrella); + + // 3. As relative path. + + // TODO: Handle -dylib_file + + // Replace @executable_path, @loader_path, @rpath prefixes in install name. + SmallString<128> newPath; + if (config->outputType == MH_EXECUTE && + path.consume_front("@executable_path/")) { + // ld64 allows overriding this with the undocumented flag -executable_path. + // lld doesn't currently implement that flag. + // FIXME: Consider using finalOutput instead of outputFile. + path::append(newPath, path::parent_path(config->outputFile), path); + path = newPath; + } else if (path.consume_front("@loader_path/")) { + fs::real_path(umbrella->getName(), newPath); + path::remove_filename(newPath); + path::append(newPath, path); + path = newPath; + } else if (path.startswith("@rpath/")) { + for (StringRef rpath : umbrella->rpaths) { + newPath.clear(); + if (rpath.consume_front("@loader_path/")) { + fs::real_path(umbrella->getName(), newPath); + path::remove_filename(newPath); + } + path::append(newPath, rpath, path.drop_front(strlen("@rpath/"))); + if (Optional dylibPath = resolveDylibPath(newPath)) + return loadDylib(*dylibPath, umbrella); + } + } + + // FIXME: Should this be further up? + if (currentTopLevelTapi) { + for (InterfaceFile &child : + make_pointee_range(currentTopLevelTapi->documents())) { + assert(child.documents().empty()); + if (path == child.getInstallName()) { + auto file = make(child, umbrella); + file->parseReexports(child); + return file; + } + } + } + + if (Optional dylibPath = resolveDylibPath(path)) + return loadDylib(*dylibPath, umbrella); + + return nullptr; +} + +// If a re-exported dylib is public (lives in /usr/lib or +// /System/Library/Frameworks), then it is considered implicitly linked: we +// should bind to its symbols directly instead of via the re-exporting umbrella +// library. +static bool isImplicitlyLinked(StringRef path) { + if (!config->implicitDylibs) + return false; + + if (path::parent_path(path) == "/usr/lib") + return true; + + // Match /System/Library/Frameworks/$FOO.framework/**/$FOO + if (path.consume_front("/System/Library/Frameworks/")) { + StringRef frameworkName = path.take_until([](char c) { return c == '.'; }); + return path::filename(path) == frameworkName; + } + + return false; +} + +static void 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); +} + +DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella, + bool isBundleLoader) + : InputFile(DylibKind, mb), refState(RefState::Unreferenced), + 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()); + auto *hdr = reinterpret_cast(mb.getBufferStart()); - // Initialize dylibName. + // Initialize installName. if (const load_command *cmd = findCommand(hdr, LC_ID_DYLIB)) { auto *c = reinterpret_cast(cmd); - dylibName = reinterpret_cast(cmd) + read32le(&c->dylib.name); - } else { - error("dylib " + getName() + " missing LC_ID_DYLIB load command"); + currentVersion = read32le(&c->dylib.current_version); + compatibilityVersion = read32le(&c->dylib.compatibility_version); + installName = + reinterpret_cast(cmd) + read32le(&c->dylib.name); + } 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"); return; } + if (config->printEachFile) + message(toString(this)); + inputFiles.insert(this); + + deadStrippable = hdr->flags & MH_DEAD_STRIPPABLE_DYLIB; + + if (!checkCompatibility(this)) + return; + + checkAppExtensionSafety(hdr->flags & MH_APP_EXTENSION_SAFE); + + for (auto *cmd : findCommands(hdr, LC_RPATH)) { + StringRef rpath{reinterpret_cast(cmd) + cmd->path}; + rpaths.push_back(rpath); + } + // 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) { - symbols.push_back(symtab->addDylib(saver.save(name), umbrella)); + 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)); }); } else { - error("LC_DYLD_INFO_ONLY not found in " + getName()); + error("LC_DYLD_INFO_ONLY not found in " + toString(this)); return; } +} - if (hdr->flags & MH_NO_REEXPORTED_DYLIBS) - return; - - const uint8_t *p = - reinterpret_cast(hdr) + sizeof(mach_header_64); +void DylibFile::parseLoadCommands(MemoryBufferRef mb) { + auto *hdr = reinterpret_cast(mb.getBufferStart()); + const uint8_t *p = reinterpret_cast(mb.getBufferStart()) + + target->headerSize; for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { auto *cmd = reinterpret_cast(p); p += cmd->cmdsize; - if (cmd->cmd != LC_REEXPORT_DYLIB) - continue; - auto *c = reinterpret_cast(cmd); - StringRef reexportPath = - reinterpret_cast(c) + read32le(&c->dylib.name); - // TODO: Expand @loader_path, @executable_path etc in reexportPath - Optional buffer = readFile(reexportPath); - if (!buffer) { - error("unable to read re-exported dylib at " + reexportPath); - return; + if (!(hdr->flags & MH_NO_REEXPORTED_DYLIBS) && + cmd->cmd == LC_REEXPORT_DYLIB) { + const auto *c = reinterpret_cast(cmd); + StringRef reexportPath = + reinterpret_cast(c) + read32le(&c->dylib.name); + loadReexport(reexportPath, exportingFile, nullptr); + } + + // FIXME: What about LC_LOAD_UPWARD_DYLIB, LC_LAZY_LOAD_DYLIB, + // LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB (..are reexports from dylibs with + // MH_NO_REEXPORTED_DYLIBS loaded for -flat_namespace)? + if (config->namespaceKind == NamespaceKind::flat && + cmd->cmd == LC_LOAD_DYLIB) { + const auto *c = reinterpret_cast(cmd); + StringRef dylibPath = + reinterpret_cast(c) + read32le(&c->dylib.name); + DylibFile *dylib = findDylib(dylibPath, umbrella, nullptr); + if (!dylib) + error(Twine("unable to locate library '") + dylibPath + + "' loaded from '" + toString(this) + "' for -flat_namespace"); } - reexported.push_back(make(*buffer, umbrella)); } } -DylibFile::DylibFile(std::shared_ptr interface, - DylibFile *umbrella) - : InputFile(DylibKind, MemoryBufferRef()) { +// Some versions of XCode ship with .tbd files that don't have the right +// platform settings. +static constexpr std::array skipPlatformChecks{ + "/usr/lib/system/libsystem_kernel.dylib", + "/usr/lib/system/libsystem_platform.dylib", + "/usr/lib/system/libsystem_pthread.dylib"}; + +DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, + bool isBundleLoader) + : InputFile(DylibKind, interface), refState(RefState::Unreferenced), + isBundleLoader(isBundleLoader) { + // FIXME: Add test for the missing TBD code path. + if (umbrella == nullptr) umbrella = this; + this->umbrella = umbrella; + + installName = saver.save(interface.getInstallName()); + compatibilityVersion = interface.getCompatibilityVersion().rawValue(); + currentVersion = interface.getCurrentVersion().rawValue(); - dylibName = saver.save(interface->getInstallName()); + if (config->printEachFile) + message(toString(this)); + inputFiles.insert(this); + + if (!is_contained(skipPlatformChecks, installName) && + !is_contained(interface.targets(), config->platformInfo.target)) { + error(toString(this) + " is incompatible with " + + std::string(config->platformInfo.target)); + return; + } + + 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)); + }; // TODO(compnerd) filter out symbols based on the target platform - for (const auto symbol : interface->symbols()) - if (symbol->getArchitectures().has(config->arch)) - symbols.push_back( - symtab->addDylib(saver.save(symbol->getName()), umbrella)); - // TODO(compnerd) properly represent the hierarchy of the documents as it is - // in theory possible to have re-exported dylibs from re-exported dylibs which - // should be parent'ed to the child. - for (auto document : interface->documents()) - reexported.push_back(make(document, umbrella)); -} - -ArchiveFile::ArchiveFile(std::unique_ptr &&f) + // TODO: handle weak defs, thread locals + for (const auto *symbol : interface.symbols()) { + if (!symbol->getArchitectures().has(config->arch())) + continue; + + if (handleLDSymbol(symbol->getName())) + continue; + + switch (symbol->getKind()) { + case SymbolKind::GlobalSymbol: + addSymbol(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()); + break; + case SymbolKind::ObjectiveCClassEHType: + addSymbol(objc::ehtype + symbol->getName()); + break; + case SymbolKind::ObjectiveCInstanceVariable: + addSymbol(objc::ivar + symbol->getName()); + break; + } + } +} + +void DylibFile::parseReexports(const InterfaceFile &interface) { + const InterfaceFile *topLevel = + interface.getParent() == nullptr ? &interface : interface.getParent(); + for (InterfaceFileRef intfRef : interface.reexportedLibraries()) { + InterfaceFile::const_target_range targets = intfRef.targets(); + if (is_contained(skipPlatformChecks, intfRef.getInstallName()) || + is_contained(targets, config->platformInfo.target)) + loadReexport(intfRef.getInstallName(), exportingFile, topLevel); + } +} + +// $ld$ symbols modify the properties/behavior of the library (e.g. its install +// name, compatibility version or hide/add symbols) for specific target +// versions. +bool DylibFile::handleLDSymbol(StringRef originalName) { + if (!originalName.startswith("$ld$")) + return false; + + StringRef action; + StringRef name; + std::tie(action, name) = originalName.drop_front(strlen("$ld$")).split('$'); + if (action == "previous") + handleLDPreviousSymbol(name, originalName); + else if (action == "install_name") + handleLDInstallNameSymbol(name, originalName); + return true; +} + +void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) { + // originalName: $ld$ previous $ $ $ + // $ $ $ $ + StringRef installName; + StringRef compatVersion; + StringRef platformStr; + StringRef startVersion; + StringRef endVersion; + StringRef symbolName; + StringRef rest; + + std::tie(installName, name) = name.split('$'); + std::tie(compatVersion, name) = name.split('$'); + 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; + unsigned platform; + if (platformStr.getAsInteger(10, platform) || + platform != static_cast(config->platform())) + return; + + VersionTuple start; + if (start.tryParse(startVersion)) { + warn("failed to parse start version, symbol '" + originalName + + "' ignored"); + return; + } + VersionTuple end; + if (end.tryParse(endVersion)) { + warn("failed to parse end version, symbol '" + originalName + "' ignored"); + return; + } + if (config->platformInfo.minimum < start || + config->platformInfo.minimum >= end) + return; + + this->installName = saver.save(installName); + + if (!compatVersion.empty()) { + VersionTuple cVersion; + if (cVersion.tryParse(compatVersion)) { + warn("failed to parse compatibility version, symbol '" + originalName + + "' ignored"); + return; + } + compatibilityVersion = encodeVersion(cVersion); + } +} + +void DylibFile::handleLDInstallNameSymbol(StringRef name, + StringRef originalName) { + // originalName: $ld$ install_name $ os $ install_name + StringRef condition, installName; + 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"); + else if (version == config->platformInfo.minimum) + this->installName = saver.save(installName); +} + +void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const { + if (config->applicationExtension && !dylibIsAppExtensionSafe) + warn("using '-application_extension' with unsafe dylib: " + toString(this)); +} + +ArchiveFile::ArchiveFile(std::unique_ptr &&f) : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) { for (const object::Archive::Symbol &sym : file->symbols()) symtab->addLazy(sym.getName(), this, sym); @@ -411,7 +1234,7 @@ void ArchiveFile::fetch(const object::Archive::Symbol &sym) { object::Archive::Child c = CHECK(sym.getMember(), toString(this) + ": could not get the member for symbol " + - sym.getName()); + toMachOString(sym)); if (!seen.insert(c.getChildOffset()).second) return; @@ -420,14 +1243,85 @@ void ArchiveFile::fetch(const object::Archive::Symbol &sym) { CHECK(c.getMemoryBufferRef(), toString(this) + ": could not get the buffer for the member defining symbol " + - sym.getName()); - auto file = make(mb); - symbols.insert(symbols.end(), file->symbols.begin(), file->symbols.end()); - subsections.insert(subsections.end(), file->subsections.begin(), - file->subsections.end()); + toMachOString(sym)); + + if (tar && c.getParent()->isThin()) + tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); + + uint32_t modTime = toTimeT( + CHECK(c.getLastModified(), toString(this) + + ": could not get the modification time " + "for 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); + } } -// Returns "" or "baz.o". -std::string lld::toString(const InputFile *file) { - return file ? std::string(file->getName()) : ""; +static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym, + BitcodeFile &file) { + 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"); + + // TODO: Write a test demonstrating why computing isPrivateExtern before + // LTO compilation is important. + bool isPrivateExtern = false; + switch (objSym.getVisibility()) { + case GlobalValue::HiddenVisibility: + isPrivateExtern = true; + break; + case GlobalValue::ProtectedVisibility: + error(name + " has protected visibility, which is not supported by Mach-O"); + break; + case GlobalValue::DefaultVisibility: + break; + } + + return symtab->addDefined(name, &file, /*isec=*/nullptr, /*value=*/0, + /*size=*/0, objSym.isWeak(), isPrivateExtern, + /*isThumb=*/false, + /*isReferencedDynamically=*/false, + /*noDeadStrip=*/false); +} + +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + std::string path = mb.getBufferIdentifier().str(); + // 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))); + + obj = check(lto::InputFile::create(mbref)); + + // Convert LTO Symbols to LLD Symbols in order to perform resolution. The + // "winning" symbol will then be marked as Prevailing at LTO compilation + // time. + for (const lto::InputFile::Symbol &objSym : obj->symbols()) + symbols.push_back(createBitcodeSymbol(objSym, *this)); } + +template void ObjFile::parse(); diff --git a/gnu/llvm/lld/MachO/InputFiles.h b/gnu/llvm/lld/MachO/InputFiles.h index bc5ad86ccaa..0101fb71c8a 100644 --- a/gnu/llvm/lld/MachO/InputFiles.h +++ b/gnu/llvm/lld/MachO/InputFiles.h @@ -10,74 +10,127 @@ #define LLD_MACHO_INPUT_FILES_H #include "MachOStructs.h" +#include "Target.h" #include "lld/Common/LLVM.h" +#include "lld/Common/Memory.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/TextAPI/MachO/InterfaceFile.h" -#include "llvm/TextAPI/MachO/TextAPIReader.h" +#include "llvm/TextAPI/TextAPIReader.h" -#include #include +namespace llvm { +namespace lto { +class InputFile; +} // namespace lto +namespace MachO { +class InterfaceFile; +} // namespace MachO +class TarWriter; +} // namespace llvm + namespace lld { namespace macho { -class InputSection; +struct PlatformInfo; +class ConcatInputSection; class Symbol; struct Reloc; +enum class RefState : uint8_t; + +// If --reproduce option is given, all input files are written +// to this tar archive. +extern std::unique_ptr tar; // If .subsections_via_symbols is set, each InputSection will be split along -// symbol boundaries. The keys of a SubsectionMap represent the offsets of -// each subsection from the start of the original pre-split InputSection. -using SubsectionMap = std::map; +// 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; +}; +using SubsectionMap = std::vector; class InputFile { public: enum Kind { ObjKind, + OpaqueKind, DylibKind, ArchiveKind, + BitcodeKind, }; virtual ~InputFile() = default; Kind kind() const { return fileKind; } - StringRef getName() const { return mb.getBufferIdentifier(); } + StringRef getName() const { return name; } MemoryBufferRef mb; + std::vector symbols; - ArrayRef sectionHeaders; std::vector subsections; + // Provides an easy way to sort InputFiles deterministically. + const int id; -protected: - InputFile(Kind kind, MemoryBufferRef mb) : mb(mb), fileKind(kind) {} - - void parseSections(ArrayRef); + // If not empty, this stores the name of the archive containing this file. + // We use this string for creating error messages. + std::string archiveName; - void parseSymbols(ArrayRef nList, const char *strtab, - bool subsectionsViaSymbols); +protected: + InputFile(Kind kind, MemoryBufferRef mb) + : mb(mb), id(idCount++), fileKind(kind), name(mb.getBufferIdentifier()) {} - void parseRelocations(const llvm::MachO::section_64 &, SubsectionMap &); + InputFile(Kind, const llvm::MachO::InterfaceFile &); private: const Kind fileKind; + const StringRef name; + + static int idCount; }; // .o file -class ObjFile : public InputFile { +class ObjFile final : public InputFile { public: - explicit ObjFile(MemoryBufferRef mb); + ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName); static bool classof(const InputFile *f) { return f->kind() == ObjKind; } + + llvm::DWARFUnit *compileUnit = nullptr; + const uint32_t modTime; + std::vector debugSections; + ArrayRef dataInCodeEntries; + +private: + template void parse(); + 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 &); + void parseDebugInfo(); + void parseDataInCode(); }; -// .dylib file -class DylibFile : public InputFile { +// command-line -sectcreate file +class OpaqueFile final : public InputFile { public: - explicit DylibFile(std::shared_ptr interface, - DylibFile *umbrella = nullptr); + OpaqueFile(MemoryBufferRef mb, StringRef segName, StringRef sectName); + static bool classof(const InputFile *f) { return f->kind() == OpaqueKind; } +}; +// .dylib or .tbd file +class DylibFile final : public InputFile { +public: // Mach-O dylibs can re-export other dylibs as sub-libraries, meaning that the // symbols in those sub-libraries will be available under the umbrella // library's namespace. Those sub-libraries can also have their own @@ -85,18 +138,50 @@ public: // the root dylib to ensure symbols in the child library are correctly bound // 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 = nullptr); + explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, + bool isBundleLoader = false); + explicit DylibFile(const llvm::MachO::InterfaceFile &interface, + DylibFile *umbrella = nullptr, + bool isBundleLoader = false); + + void parseLoadCommands(MemoryBufferRef mb); + void parseReexports(const llvm::MachO::InterfaceFile &interface); static bool classof(const InputFile *f) { return f->kind() == DylibKind; } - StringRef dylibName; - uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel + StringRef installName; + DylibFile *exportingFile = nullptr; + DylibFile *umbrella; + SmallVector rpaths; + uint32_t compatibilityVersion = 0; + uint32_t currentVersion = 0; + int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel + RefState refState; bool reexport = false; - std::vector reexported; + bool forceNeeded = false; + bool forceWeakImport = false; + bool deadStrippable = false; + bool explicitlyLinked = false; + + unsigned numReferencedSymbols = 0; + + bool isReferenced() const { return numReferencedSymbols > 0; } + + // 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. + bool isBundleLoader; + +private: + bool handleLDSymbol(StringRef originalName); + void handleLDPreviousSymbol(StringRef name, StringRef originalName); + void handleLDInstallNameSymbol(StringRef name, StringRef originalName); + void checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const; }; // .a file -class ArchiveFile : public InputFile { +class ArchiveFile final : public InputFile { public: explicit ArchiveFile(std::unique_ptr &&file); static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } @@ -109,10 +194,57 @@ private: llvm::DenseSet seen; }; -extern std::vector inputFiles; +class BitcodeFile final : public InputFile { +public: + explicit BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive); + static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + + std::unique_ptr obj; +}; + +extern llvm::SetVector inputFiles; llvm::Optional readFile(StringRef path); +namespace detail { + +template +std::vector +findCommands(const void *anyHdr, size_t maxCommands, Types... types) { + std::vector cmds; + std::initializer_list typesList{types...}; + const auto *hdr = reinterpret_cast(anyHdr); + const uint8_t *p = + reinterpret_cast(hdr) + target->headerSize; + for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { + auto *cmd = reinterpret_cast(p); + if (llvm::is_contained(typesList, cmd->cmd)) { + cmds.push_back(cmd); + if (cmds.size() == maxCommands) + return cmds; + } + p += cmd->cmdsize; + } + return cmds; +} + +} // namespace detail + +// anyHdr should be a pointer to either mach_header or mach_header_64 +template +const CommandType *findCommand(const void *anyHdr, Types... types) { + std::vector cmds = + detail::findCommands(anyHdr, 1, types...); + return cmds.size() ? cmds[0] : nullptr; +} + +template +std::vector findCommands(const void *anyHdr, + Types... types) { + return detail::findCommands(anyHdr, 0, types...); +} + } // namespace macho std::string toString(const macho::InputFile *file); diff --git a/gnu/llvm/lld/MachO/InputSection.cpp b/gnu/llvm/lld/MachO/InputSection.cpp index 72d48928305..eb5acf6ffed 100644 --- a/gnu/llvm/lld/MachO/InputSection.cpp +++ b/gnu/llvm/lld/MachO/InputSection.cpp @@ -7,11 +7,18 @@ //===----------------------------------------------------------------------===// #include "InputSection.h" +#include "ConcatOutputSection.h" +#include "Config.h" +#include "InputFiles.h" #include "OutputSegment.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" +#include "UnwindInfoSection.h" +#include "Writer.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/xxhash.h" using namespace llvm; using namespace llvm::MachO; @@ -19,30 +26,218 @@ using namespace llvm::support; using namespace lld; using namespace lld::macho; -std::vector macho::inputSections; +std::vector macho::inputSections; -uint64_t InputSection::getFileOffset() const { - return parent->fileOff + outSecFileOff; +uint64_t InputSection::getFileSize() const { + return isZeroFill(getFlags()) ? 0 : getSize(); } -uint64_t InputSection::getVA() const { return parent->addr + outSecOff; } +uint64_t InputSection::getVA(uint64_t off) const { + return parent->addr + getOffset(off); +} + +static uint64_t resolveSymbolVA(const Symbol *sym, uint8_t type) { + const RelocAttrs &relocAttrs = target->getRelocAttrs(type); + if (relocAttrs.hasAttr(RelocAttrBits::BRANCH)) + return sym->resolveBranchVA(); + if (relocAttrs.hasAttr(RelocAttrBits::GOT)) + return sym->resolveGotVA(); + if (relocAttrs.hasAttr(RelocAttrBits::TLV)) + return sym->resolveTlvVA(); + 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"); + } +} + +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); +} + +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; +} + +void ConcatInputSection::writeTo(uint8_t *buf) { + assert(!shouldOmitFromOutput()); -void InputSection::writeTo(uint8_t *buf) { if (getFileSize() == 0) return; memcpy(buf, data.data(), data.size()); - for (Reloc &r : relocs) { - uint64_t va = 0; - if (auto *s = r.target.dyn_cast()) - va = target->getSymbolVA(*s, r.type); - else if (auto *isec = r.target.dyn_cast()) - va = isec->getVA(); - - uint64_t val = va + r.addend; - if (r.pcrel) - val -= getVA() + r.offset; - target->relocateOne(buf + r.offset, r, val); + for (size_t i = 0; i < relocs.size(); i++) { + const Reloc &r = relocs[i]; + uint8_t *loc = buf + r.offset; + uint64_t referentVA = 0; + if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) { + const Symbol *fromSym = r.referent.get(); + const Reloc &minuend = relocs[++i]; + uint64_t minuendVA; + if (const Symbol *toSym = minuend.referent.dyn_cast()) + minuendVA = toSym->getVA() + minuend.addend; + else { + auto *referentIsec = minuend.referent.get(); + assert(!::shouldOmitFromOutput(referentIsec)); + minuendVA = referentIsec->getVA(minuend.addend); + } + referentVA = minuendVA - fromSym->getVA(); + } else if (auto *referentSym = r.referent.dyn_cast()) { + if (target->hasAttr(r.type, RelocAttrBits::LOAD) && + !referentSym->isInGot()) + target->relaxGotLoad(loc, r.type); + referentVA = resolveSymbolVA(referentSym, r.type) + r.addend; + + if (isThreadLocalVariables(getFlags())) { + // 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; + } + } else if (auto *referentIsec = r.referent.dyn_cast()) { + assert(!::shouldOmitFromOutput(referentIsec)); + referentVA = referentIsec->getVA(r.addend); + } + target->relocateOne(loc, r, referentVA, getVA() + r.offset); + } +} + +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; + pieces.emplace_back(off, hash); + s = s.substr(size); + off += size; + } +} + +StringPiece &CStringInputSection::getStringPiece(uint64_t off) { + if (off >= data.size()) + fatal(toString(this) + ": offset is outside the section"); + + auto it = + partition_point(pieces, [=](StringPiece p) { return p.inSecOff <= off; }); + return it[-1]; +} + +const StringPiece &CStringInputSection::getStringPiece(uint64_t off) const { + return const_cast(this)->getStringPiece(off); +} + +uint64_t CStringInputSection::getOffset(uint64_t off) const { + const StringPiece &piece = getStringPiece(off); + uint64_t addend = off - piece.inSecOff; + return piece.outSecOff + addend; +} + +WordLiteralInputSection::WordLiteralInputSection(StringRef segname, + StringRef name, + InputFile *file, + ArrayRef data, + uint32_t align, uint32_t flags) + : InputSection(WordLiteralKind, segname, name, file, data, align, flags) { + switch (sectionType(flags)) { + case S_4BYTE_LITERALS: + power2LiteralSize = 2; + break; + case S_8BYTE_LITERALS: + power2LiteralSize = 3; + break; + case S_16BYTE_LITERALS: + power2LiteralSize = 4; + break; + default: + llvm_unreachable("invalid literal section type"); + } + + live.resize(data.size() >> power2LiteralSize, !config->deadStrip); +} + +uint64_t WordLiteralInputSection::getOffset(uint64_t off) const { + auto *osec = cast(parent); + const uint8_t *buf = data.data(); + switch (sectionType(getFlags())) { + case S_4BYTE_LITERALS: + return osec->getLiteral4Offset(buf + off); + case S_8BYTE_LITERALS: + return osec->getLiteral8Offset(buf + off); + case S_16BYTE_LITERALS: + return osec->getLiteral16Offset(buf + off); + default: + llvm_unreachable("invalid literal section type"); } } + +bool macho::isCodeSection(const InputSection *isec) { + uint32_t type = sectionType(isec->getFlags()); + if (type != S_REGULAR && type != S_COALESCED) + return false; + + uint32_t attr = isec->getFlags() & SECTION_ATTRIBUTES_USR; + if (attr == S_ATTR_PURE_INSTRUCTIONS) + return true; + + if (isec->getSegName() == segment_names::text) + return StringSwitch(isec->getName()) + .Cases(section_names::textCoalNt, section_names::staticInit, true) + .Default(false); + + return false; +} + +bool macho::isCfStringSection(const InputSection *isec) { + return isec->getName() == section_names::cfString && + isec->getSegName() == segment_names::data; +} + +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 96ae0cbe6ea..a1045708704 100644 --- a/gnu/llvm/lld/MachO/InputSection.h +++ b/gnu/llvm/lld/MachO/InputSection.h @@ -9,66 +9,338 @@ #ifndef LLD_MACHO_INPUT_SECTION_H #define LLD_MACHO_INPUT_SECTION_H +#include "Config.h" +#include "Relocations.h" + #include "lld/Common/LLVM.h" +#include "lld/Common/Memory.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/CachedHashString.h" #include "llvm/BinaryFormat/MachO.h" namespace lld { namespace macho { class InputFile; -class InputSection; class OutputSection; -class Symbol; - -struct Reloc { - uint8_t type; - bool pcrel; - uint8_t length; - // The offset from the start of the subsection that this relocation belongs - // to. - uint32_t offset; - // Adding this offset to the address of the target symbol or subsection gives - // the destination that this relocation refers to. - uint64_t addend; - llvm::PointerUnion target; -}; - -inline bool isZeroFill(uint8_t flags) { - return (flags & llvm::MachO::SECTION_TYPE) == llvm::MachO::S_ZEROFILL; -} +class Defined; class InputSection { public: + enum Kind { + ConcatKind, + CStringLiteralKind, + WordLiteralKind, + }; + + Kind kind() const { return shared->sectionKind; } virtual ~InputSection() = default; virtual uint64_t getSize() const { return data.size(); } - virtual uint64_t getFileSize() const { - return isZeroFill(flags) ? 0 : getSize(); - } - uint64_t getFileOffset() const; - uint64_t getVA() const; - - virtual void writeTo(uint8_t *buf); - - InputFile *file = nullptr; - StringRef name; - StringRef segname; + 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; } + 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; + // 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; } OutputSection *parent = nullptr; - uint64_t outSecOff = 0; - uint64_t outSecFileOff = 0; uint32_t align = 1; - uint32_t flags = 0; + uint32_t callSiteCount : 31; + // is address assigned? + uint32_t isFinal : 1; ArrayRef data; std::vector relocs; + +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) {} + }; + + 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; }; -extern std::vector inputSections; +// ConcatInputSections are combined into (Concat)OutputSections through simple +// concatenation, in contrast with literal sections which may have their +// 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) {} + + 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 shouldOmitFromOutput() const { return !live || isCoalescedWeak(); } + bool isHashableForICF() const; + void hashForICF(); + void writeTo(uint8_t *buf); + + void foldIdentical(ConcatInputSection *redundant); + InputSection *canonical() override { + return replacement ? replacement : this; + } + + static bool classof(const InputSection *isec) { + return isec->kind() == ConcatKind; + } + + // Points to the surviving section after this one is folded by ICF + InputSection *replacement = nullptr; + // Equivalence-class ID for ICF + uint64_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 + // InputSection from one translation unit will make it to the output, + // while all copies in other translation units are coalesced into the + // 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; + // 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"); + +// Helper functions to make it easy to sprinkle asserts. + +inline bool shouldOmitFromOutput(InputSection *isec) { + return isa(isec) && + cast(isec)->shouldOmitFromOutput(); +} + +inline bool isCoalescedWeak(InputSection *isec) { + return isa(isec) && + cast(isec)->isCoalescedWeak(); +} + +// We allocate a lot of these and binary search on them, so they should be as +// compact as possible. Hence the use of 31 rather than 64 bits for the hash. +struct StringPiece { + // Offset from the start of the containing input section. + uint32_t inSecOff; + uint32_t live : 1; + // Only set if deduplicating literals + uint32_t hash : 31; + // Offset from the start of the containing output section. + uint64_t outSecOff = 0; + + StringPiece(uint64_t off, uint32_t hash) + : inSecOff(off), live(!config->deadStrip), hash(hash) {} +}; + +static_assert(sizeof(StringPiece) == 16, "StringPiece is too big!"); + +// CStringInputSections are composed of multiple null-terminated string +// literals, which we represent using StringPieces. These literals can be +// deduplicated and tail-merged, so translating offsets between the input and +// outputs sections is more complicated. +// +// NOTE: One significant difference between LLD and ld64 is that we merge all +// cstring literals, even those referenced directly by non-private symbols. +// ld64 is more conservative and does not do that. This was mostly done for +// implementation simplicity; if we find programs that need the more +// 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) {} + 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; } + // Find the StringPiece that contains this offset. + StringPiece &getStringPiece(uint64_t off); + const StringPiece &getStringPiece(uint64_t off) const; + // Split at each null byte. + void splitIntoPieces(); + + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringRef getStringRef(size_t i) const { + size_t begin = pieces[i].inSecOff; + size_t end = + (pieces.size() - 1 == i) ? data.size() : pieces[i + 1].inSecOff; + return toStringRef(data.slice(begin, end - begin)); + } + + // Returns i'th piece as a CachedHashStringRef. This function is very hot when + // string merging is enabled, so we want to inline. + LLVM_ATTRIBUTE_ALWAYS_INLINE + llvm::CachedHashStringRef getCachedHashStringRef(size_t i) const { + assert(config->dedupLiterals); + return {getStringRef(i), pieces[i].hash}; + } + + static bool classof(const InputSection *isec) { + return isec->kind() == CStringLiteralKind; + } + + std::vector pieces; +}; + +class WordLiteralInputSection final : public InputSection { +public: + WordLiteralInputSection(StringRef segname, StringRef name, InputFile *file, + ArrayRef data, uint32_t align, + uint32_t flags); + 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; } + + static bool classof(const InputSection *isec) { + return isec->kind() == WordLiteralKind; + } + +private: + unsigned power2LiteralSize; + // The liveness of data[off] is tracked by live[off >> power2LiteralSize]. + llvm::BitVector live; +}; + +inline uint8_t sectionType(uint32_t flags) { + return flags & llvm::MachO::SECTION_TYPE; +} + +inline bool isZeroFill(uint32_t flags) { + return llvm::MachO::isVirtualSection(sectionType(flags)); +} + +inline bool isThreadLocalVariables(uint32_t flags) { + return sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_VARIABLES; +} + +// These sections contain the data for initializing thread-local variables. +inline bool isThreadLocalData(uint32_t flags) { + return sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_REGULAR || + sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_ZEROFILL; +} + +inline bool isDebugSection(uint32_t flags) { + return (flags & llvm::MachO::SECTION_ATTRIBUTES_USR) == + llvm::MachO::S_ATTR_DEBUG; +} + +inline bool isWordLiteralSection(uint32_t flags) { + return sectionType(flags) == llvm::MachO::S_4BYTE_LITERALS || + sectionType(flags) == llvm::MachO::S_8BYTE_LITERALS || + sectionType(flags) == llvm::MachO::S_16BYTE_LITERALS; +} + +bool isCodeSection(const InputSection *); + +bool isCfStringSection(const InputSection *); + +extern std::vector inputSections; + +namespace section_names { + +constexpr const char authGot[] = "__auth_got"; +constexpr const char authPtr[] = "__auth_ptr"; +constexpr const char binding[] = "__binding"; +constexpr const char bitcodeBundle[] = "__bundle"; +constexpr const char cString[] = "__cstring"; +constexpr const char cfString[] = "__cfstring"; +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 debugStr[] = "__debug_str"; +constexpr const char ehFrame[] = "__eh_frame"; +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 const_[] = "__const"; +constexpr const char lazySymbolPtr[] = "__la_symbol_ptr"; +constexpr const char lazyBinding[] = "__lazy_binding"; +constexpr const char literals[] = "__literals"; +constexpr const char moduleInitFunc[] = "__mod_init_func"; +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 objcConst[] = "__objc_const"; +constexpr const char objcImageInfo[] = "__objc_imageinfo"; +constexpr const char objcNonLazyCatList[] = "__objc_nlcatlist"; +constexpr const char objcNonLazyClassList[] = "__objc_nlclslist"; +constexpr const char objcProtoList[] = "__objc_protolist"; +constexpr const char pageZero[] = "__pagezero"; +constexpr const char pointers[] = "__pointers"; +constexpr const char rebase[] = "__rebase"; +constexpr const char staticInit[] = "__StaticInit"; +constexpr const char stringTable[] = "__string_table"; +constexpr const char stubHelper[] = "__stub_helper"; +constexpr const char stubs[] = "__stubs"; +constexpr const char swift[] = "__swift"; +constexpr const char symbolTable[] = "__symbol_table"; +constexpr const char textCoalNt[] = "__textcoal_nt"; +constexpr const char text[] = "__text"; +constexpr const char threadPtrs[] = "__thread_ptrs"; +constexpr const char threadVars[] = "__thread_vars"; +constexpr const char unwindInfo[] = "__unwind_info"; +constexpr const char weakBinding[] = "__weak_binding"; +constexpr const char zeroFill[] = "__zerofill"; + +} // namespace section_names } // namespace macho + +std::string toString(const macho::InputSection *); + } // namespace lld #endif diff --git a/gnu/llvm/lld/MachO/LTO.cpp b/gnu/llvm/lld/MachO/LTO.cpp new file mode 100644 index 00000000000..366193a27eb --- /dev/null +++ b/gnu/llvm/lld/MachO/LTO.cpp @@ -0,0 +1,156 @@ +//===- LTO.cpp ------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LTO.h" +#include "Config.h" +#include "Driver.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" + +#include "lld/Common/Args.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Strings.h" +#include "lld/Common/TargetOptionsCommandFlags.h" +#include "llvm/LTO/Caching.h" +#include "llvm/LTO/Config.h" +#include "llvm/LTO/LTO.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/ObjCARC.h" + +using namespace lld; +using namespace lld::macho; +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::sys; + +static lto::Config createConfig() { + lto::Config c; + c.Options = initTargetOptionsFromCodeGenFlags(); + c.CodeModel = getCodeModelFromCMModel(); + c.CPU = getCPUStr(); + c.MAttrs = getMAttrs(); + c.UseNewPM = config->ltoNewPassManager; + c.PreCodeGenPassesHook = [](legacy::PassManager &pm) { + pm.add(createObjCARCContractPass()); + }; + c.TimeTraceEnabled = config->timeTraceEnabled; + c.TimeTraceGranularity = config->timeTraceGranularity; + c.OptLevel = config->ltoo; + c.CGOptLevel = args::getCGOptLevel(config->ltoo); + if (config->saveTemps) + checkError(c.addSaveTemps(config->outputFile.str() + ".", + /*UseInputModulePath=*/true)); + return c; +} + +BitcodeCompiler::BitcodeCompiler() { + lto::ThinBackend backend = lto::createInProcessThinBackend( + heavyweight_hardware_concurrency(config->thinLTOJobs)); + ltoObj = std::make_unique(createConfig(), backend); +} + +void BitcodeCompiler::add(BitcodeFile &f) { + ArrayRef objSyms = f.obj->symbols(); + std::vector resols; + resols.reserve(objSyms.size()); + + // Provide a resolution to the LTO API for each symbol. + auto symIt = f.symbols.begin(); + for (const lto::InputFile::Symbol &objSym : objSyms) { + resols.emplace_back(); + lto::SymbolResolution &r = resols.back(); + Symbol *sym = *symIt++; + + // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile + // reports two symbols for module ASM defined. Without this check, lld + // flags an undefined in IR with a definition in ASM as prevailing. + // Once IRObjectFile is fixed to report only one symbol this hack can + // be removed. + r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; + + // 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; + + // 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); + + // TODO: set the other resolution configs properly + } + checkError(ltoObj->add(std::move(f.obj), resols)); +} + +// Merge all the bitcode files we have seen, codegen the result +// and return the resulting ObjectFile(s). +std::vector BitcodeCompiler::compile() { + unsigned maxTasks = ltoObj->getMaxTasks(); + buf.resize(maxTasks); + files.resize(maxTasks); + + // 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; + if (!config->thinLTOCacheDir.empty()) + cache = check( + lto::localCache(config->thinLTOCacheDir, + [&](size_t task, std::unique_ptr mb) { + files[task] = std::move(mb); + })); + + checkError(ltoObj->run( + [&](size_t task) { + return std::make_unique( + std::make_unique(buf[task])); + }, + cache)); + + if (!config->thinLTOCacheDir.empty()) + pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); + + 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"); + } + + if (!config->ltoObjPath.empty()) + fs::create_directories(config->ltoObjPath); + + std::vector ret; + for (unsigned i = 0; i != maxTasks; ++i) { + if (buf[i].empty()) + continue; + SmallString<261> filePath("/tmp/lto.tmp"); + 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); + modTime = getModTime(filePath); + } + ret.push_back(make( + MemoryBufferRef(buf[i], 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 new file mode 100644 index 00000000000..d64016fb588 --- /dev/null +++ b/gnu/llvm/lld/MachO/LTO.h @@ -0,0 +1,45 @@ +//===- LTO.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_LTO_H +#define LLD_MACHO_LTO_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/MemoryBuffer.h" +#include +#include + +namespace llvm { +namespace lto { +class LTO; +} // namespace lto +} // namespace llvm + +namespace lld { +namespace macho { + +class BitcodeFile; +class ObjFile; + +class BitcodeCompiler { +public: + BitcodeCompiler(); + + void add(BitcodeFile &f); + std::vector compile(); + +private: + std::unique_ptr ltoObj; + std::vector> buf; + std::vector> files; +}; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/MachOStructs.h b/gnu/llvm/lld/MachO/MachOStructs.h index 69b50ec2317..44715bf29da 100644 --- a/gnu/llvm/lld/MachO/MachOStructs.h +++ b/gnu/llvm/lld/MachO/MachOStructs.h @@ -29,6 +29,21 @@ struct nlist_64 { llvm::support::ulittle64_t n_value; }; +struct nlist { + llvm::support::ulittle32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + llvm::support::ulittle16_t n_desc; + llvm::support::ulittle32_t n_value; +}; + +struct entry_point_command { + llvm::support::ulittle32_t cmd; + llvm::support::ulittle32_t cmdsize; + llvm::support::ulittle64_t entryoff; + llvm::support::ulittle64_t stacksize; +}; + } // namespace structs } // namespace lld diff --git a/gnu/llvm/lld/MachO/MapFile.cpp b/gnu/llvm/lld/MachO/MapFile.cpp new file mode 100644 index 00000000000..79471eecbd5 --- /dev/null +++ b/gnu/llvm/lld/MachO/MapFile.cpp @@ -0,0 +1,159 @@ +//===- MapFile.cpp --------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the -map option. It shows lists in order and +// hierarchically the outputFile, arch, input files, output sections and +// symbol: +// +// # Path: test +// # Arch: x86_84 +// # Object files: +// [ 0] linker synthesized +// [ 1] a.o +// # Sections: +// # Address Size Segment Section +// 0x1000005C0 0x0000004C __TEXT __text +// # Symbols: +// # Address File Name +// 0x1000005C0 [ 1] _main +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "Config.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "OutputSection.h" +#include "OutputSegment.h" +#include "Symbols.h" +#include "Target.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" + +using namespace llvm; +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; +} + +// 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) + if (auto *d = dyn_cast_or_null(sym)) + if (d->isLive() && d->isec && d->getFile() == file) { + assert(!shouldOmitFromOutput(d->isec)); + v.push_back(d); + } + return v; +} + +// 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 macho::writeMapFile() { + if (config->mapFile.empty()) + return; + + 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; + } + + // 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. + 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++; + } + } + + // 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) + for (OutputSection *osec : seg->getSections()) { + if (osec->isHidden()) + continue; + + os << format("0x%08llX\t0x%08llX\t%s\t%s\n", osec->addr, osec->getSize(), + 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()); + } + } + + // TODO: when we implement -dead_strip, we should dump dead stripped symbols +} diff --git a/gnu/llvm/lld/MachO/MapFile.h b/gnu/llvm/lld/MachO/MapFile.h new file mode 100644 index 00000000000..bf16ffdd038 --- /dev/null +++ b/gnu/llvm/lld/MachO/MapFile.h @@ -0,0 +1,18 @@ +//===- MapFile.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_MAPFILE_H +#define LLD_MACHO_MAPFILE_H + +namespace lld { +namespace macho { +void writeMapFile(); +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/MarkLive.cpp b/gnu/llvm/lld/MachO/MarkLive.cpp new file mode 100644 index 00000000000..11669126098 --- /dev/null +++ b/gnu/llvm/lld/MachO/MarkLive.cpp @@ -0,0 +1,188 @@ +//===- MarkLive.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 "MarkLive.h" +#include "Config.h" +#include "OutputSegment.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "UnwindInfoSection.h" +#include "mach-o/compact_unwind_encoding.h" +#include "llvm/Support/TimeProfiler.h" + +namespace lld { +namespace macho { + +using namespace llvm; +using namespace llvm::MachO; + +// 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); + }; + + // Add GC roots. + if (config->entry) + 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); + continue; + } + + // public symbols explicitly marked .no_dead_strip + if (defined->referencedDynamically || defined->noDeadStrip) { + addSym(defined); + continue; + } + + // FIXME: When we implement these flags, make symbols from them GC roots: + // * -reexported_symbol(s_list) + // * -alias(-list) + // * -init + + // In dylibs and bundles and in executables with -export_dynamic, + // all external functions are GC roots. + bool externsAreRoots = + config->outputType != MH_EXECUTE || config->exportDynamic; + if (externsAreRoots && !defined->privateExtern) { + addSym(defined); + continue; + } + } + } + // -u symbols + for (Symbol *sym : config->explicitUndefineds) + if (auto *defined = dyn_cast(sym)) + addSym(defined); + // 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); + if (auto *stubBinder = + dyn_cast_or_null(symtab->find("dyld_stub_binder"))) + addSym(stubBinder); + for (ConcatInputSection *isec : inputSections) { + // Sections marked no_dead_strip + if (isec->getFlags() & S_ATTR_NO_DEAD_STRIP) { + 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); + 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); + } + } + + 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()); +} + +} // namespace macho +} // namespace lld diff --git a/gnu/llvm/lld/MachO/MarkLive.h b/gnu/llvm/lld/MachO/MarkLive.h new file mode 100644 index 00000000000..4db657c5728 --- /dev/null +++ b/gnu/llvm/lld/MachO/MarkLive.h @@ -0,0 +1,20 @@ +//===- MarkLive.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_MARKLIVE_H +#define LLD_MACHO_MARKLIVE_H + +namespace lld { +namespace macho { + +void markLive(); + +} // namespace macho +} // namespace lld + +#endif // LLD_MACHO_MARKLIVE_H diff --git a/gnu/llvm/lld/MachO/ObjC.cpp b/gnu/llvm/lld/MachO/ObjC.cpp new file mode 100644 index 00000000000..7ed800827f3 --- /dev/null +++ b/gnu/llvm/lld/MachO/ObjC.cpp @@ -0,0 +1,54 @@ +//===- ObjC.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 "ObjC.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "OutputSegment.h" +#include "Target.h" + +#include "llvm/BinaryFormat/MachO.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; + + auto *hdr = + reinterpret_cast(mb.getBufferStart()); + if (hdr->magic != LP::magic) + return false; + + 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))); + if ((segname == segment_names::data && + sectname == section_names::objcCatList) || + (segname == segment_names::text && + sectname == section_names::swift)) { + return true; + } + } + } + return false; +} + +bool macho::hasObjCSection(MemoryBufferRef mb) { + if (target->wordSize == 8) + return ::hasObjCSection(mb); + else + return ::hasObjCSection(mb); +} diff --git a/gnu/llvm/lld/MachO/ObjC.h b/gnu/llvm/lld/MachO/ObjC.h new file mode 100644 index 00000000000..8db459ad8e2 --- /dev/null +++ b/gnu/llvm/lld/MachO/ObjC.h @@ -0,0 +1,31 @@ +//===- ObjC.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_OBJC_H +#define LLD_MACHO_OBJC_H + +#include "llvm/Support/MemoryBuffer.h" + +namespace lld { +namespace macho { + +namespace objc { + +constexpr const char klass[] = "_OBJC_CLASS_$_"; +constexpr const char metaclass[] = "_OBJC_METACLASS_$_"; +constexpr const char ehtype[] = "_OBJC_EHTYPE_$_"; +constexpr const char ivar[] = "_OBJC_IVAR_$_"; + +} // namespace objc + +bool hasObjCSection(llvm::MemoryBufferRef); + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/Options.td b/gnu/llvm/lld/MachO/Options.td index 1e42542b9ac..cda857d605b 100644 --- a/gnu/llvm/lld/MachO/Options.td +++ b/gnu/llvm/lld/MachO/Options.td @@ -1,8 +1,78 @@ include "llvm/Option/OptParser.td" -def help : Flag<["-", "--"], "help">; +// Flags that lld/MachO understands but ld64 doesn't. These take +// '--' instead of '-' and use dashes instead of underscores, so +// they don't collide with the ld64 compat options. +def grp_lld : OptionGroup<"kind">, HelpText<"LLD-SPECIFIC">; + +def help : Flag<["-", "--"], "help">, + Group; def help_hidden : Flag<["--"], "help-hidden">, - HelpText<"Display help for hidden options">; + HelpText<"Display help for hidden options">, + Group; +def verbose : Flag<["--"], "verbose">, + Group; +def error_limit_eq : Joined<["--"], "error-limit=">, + HelpText<"Maximum number of errors to print before exiting (default: 20)">, + Group; +def color_diagnostics: Flag<["--"], "color-diagnostics">, + HelpText<"Alias for --color-diagnostics=always">, + Group; +def no_color_diagnostics: Flag<["--"], "no-color-diagnostics">, + HelpText<"Alias for --color-diagnostics=never">, + Group; +def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, + HelpText<"Use colors in diagnostics (default: auto)">, + MetaVarName<"[auto,always,never]">, + Group; +def threads_eq : Joined<["--"], "threads=">, + HelpText<"Number of threads. '1' disables multi-threading. By default all available hardware threads are used">, + Group; +def thinlto_jobs_eq : Joined<["--"], "thinlto-jobs=">, + HelpText<"Number of ThinLTO jobs. Default to --threads=">, + Group; +def reproduce: Separate<["--"], "reproduce">, + Group; +def reproduce_eq: Joined<["--"], "reproduce=">, + Alias(reproduce)>, + HelpText<"Write tar file containing inputs and command to reproduce link">, + Group; +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">, + 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">, + Group; +def deduplicate_literals: Flag<["--"], "deduplicate-literals">, + HelpText<"Enable literal deduplication. This is implied by --icf={safe,all}">, + Group; +def print_dylib_search: Flag<["--"], "print-dylib-search">, + HelpText<"Print which paths lld searched when trying to find dylibs">, + Group; +def icf_eq: Joined<["--"], "icf=">, + HelpText<"Set level for identical code folding (default: none)">, + MetaVarName<"[none,safe,all]">, + Group; +def lto_O: Joined<["--"], "lto-O">, + HelpText<"Set optimization level for LTO (default: 2)">, + MetaVarName<"">, + Group; +def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">, + HelpText<"Pruning policy for the ThinLTO cache">, + Group; +def O : JoinedOrSeparate<["-"], "O">, + HelpText<"Optimize output file size">; // 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 @@ -17,1281 +87,1243 @@ def help_hidden : Flag<["--"], "help-hidden">, def grp_kind : OptionGroup<"kind">, HelpText<"OUTPUT KIND">; def execute : Flag<["-"], "execute">, - HelpText<"Produce a main executable (default)">, - Flags<[HelpHidden]>, - Group; + HelpText<"Produce a main executable (default)">, + Group; def dylib : Flag<["-"], "dylib">, - HelpText<"Produce a shared library">, - Group; + HelpText<"Produce a shared library">, + Group; def bundle : Flag<["-"], "bundle">, - HelpText<"Produce a bundle">, - Flags<[HelpHidden]>, - Group; + HelpText<"Produce a bundle">, + Group; def r : Flag<["-"], "r">, - HelpText<"Merge multiple object files into one, retaining relocations">, - Flags<[HelpHidden]>, - Group; + HelpText<"Merge multiple object files into one, retaining relocations">, + Flags<[HelpHidden]>, + Group; def dylinker : Flag<["-"], "dylinker">, - HelpText<"Produce a dylinker only used when building dyld">, - Flags<[HelpHidden]>, - Group; + HelpText<"Produce a dylinker only used when building dyld">, + Flags<[HelpHidden]>, + Group; def dynamic : Flag<["-"], "dynamic">, - HelpText<"Link dynamically (default)">, - Flags<[HelpHidden]>, - Group; + HelpText<"Link dynamically (default)">, + Group; def static : Flag<["-"], "static">, - HelpText<"Link statically">, - Flags<[HelpHidden]>, - Group; + HelpText<"Link statically">, + Flags<[HelpHidden]>, + Group; def preload : Flag<["-"], "preload">, - HelpText<"Produce an unsegmented binary for embedded systems">, - Flags<[HelpHidden]>, - Group; + HelpText<"Produce an unsegmented binary for embedded systems">, + Flags<[HelpHidden]>, + Group; def arch : Separate<["-"], "arch">, - MetaVarName<"">, - HelpText<"The architecture (e.g. ppc, ppc64, i386, x86_64)">, - Group; + MetaVarName<"">, + HelpText<"The architecture (e.g. ppc, ppc64, i386, x86_64)">, + Group; def o : Separate<["-"], "o">, - MetaVarName<"">, - HelpText<"The name of the output file (default: `a.out')">, - Group; + MetaVarName<"">, + HelpText<"The name of the output file (default: `a.out')">, + Group; def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARIES">; def l : Joined<["-"], "l">, - MetaVarName<"">, - HelpText<"Search for lib.dylib or lib.a on the library search path">, - Group; + MetaVarName<"">, + HelpText<"Search for lib.dylib or lib.a on the library search path">, + Group; def weak_l : Joined<["-"], "weak-l">, - MetaVarName<"">, - HelpText<"Like -l, but mark library and its references as weak imports">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like -l, but mark library and its references as weak imports">, + Group; def weak_library : Separate<["-"], "weak_library">, - MetaVarName<"">, - HelpText<"Like bare , but mark library and its references as weak imports">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like bare , but mark library and its references as weak imports">, + Group; +def needed_l : Joined<["-"], "needed-l">, + MetaVarName<"">, + HelpText<"Like -l, but link library even if its symbols are not used and -dead_strip_dylibs is active">, + Group; +def needed_library : Separate<["-"], "needed_library">, + MetaVarName<"">, + HelpText<"Like bare , but link library even if its symbols are not used and -dead_strip_dylibs is active">, + Group; def reexport_l : Joined<["-"], "reexport-l">, - MetaVarName<"">, - HelpText<"Like -l, but export all symbols of from newly created library">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like -l, but export all symbols of from newly created library">, + Group; def reexport_library : Separate<["-"], "reexport_library">, - MetaVarName<"">, - HelpText<"Like bare , but export all symbols of from newly created library">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like bare , but export all symbols of from newly created library">, + Group; def upward_l : Joined<["-"], "upward-l">, - MetaVarName<"">, - HelpText<"Like -l, but specify dylib as an upward dependency">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like -l, but specify dylib as an upward dependency">, + Flags<[HelpHidden]>, + Group; def upward_library : Separate<["-"], "upward_library">, - MetaVarName<"">, - HelpText<"Like bare , but specify dylib as an upward dependency">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like bare , but specify dylib as an upward dependency">, + Flags<[HelpHidden]>, + Group; def L : JoinedOrSeparate<["-"], "L">, - MetaVarName<"">, - HelpText<"Add dir to the library search path">, - Group; + MetaVarName<"">, + HelpText<"Add dir to the library search path">, + Group; def Z : Flag<["-"], "Z">, - HelpText<"Remove standard directories from the library and framework search paths">, - Group; + HelpText<"Remove standard directories from the library and framework search paths">, + Group; def syslibroot : Separate<["-"], "syslibroot">, - MetaVarName<"">, - HelpText<"Prepend to all library and framework search paths">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Prepend to all library and framework search paths">, + Group; def search_paths_first : Flag<["-"], "search_paths_first">, - HelpText<"Search for lib.dylib and lib.a at each step in traversing search path (default for Xcode 4 and later)">, - Flags<[HelpHidden]>, - Group; + HelpText<"Search for lib.dylib and lib.a at each step in traversing search path (default for Xcode 4 and later)">, + Group; def search_dylibs_first : Flag<["-"], "search_dylibs_first">, - HelpText<"Search for lib.dylib on first pass, then for lib.a on second pass through search path (default for Xcode 3 and earlier)">, - Flags<[HelpHidden]>, - Group; + HelpText<"Search for lib.dylib on first pass, then for lib.a on second pass through search path (default for Xcode 3 and earlier)">, + Group; def framework : Separate<["-"], "framework">, - MetaVarName<"">, - HelpText<"Search for .framework/ on the framework search path">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Search for .framework/ on the framework search path">, + Group; def weak_framework : Separate<["-"], "weak_framework">, - MetaVarName<"">, - HelpText<"Like -framework , but mark framework and its references as weak imports">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like -framework , but mark framework and its references as weak imports">, + Group; +def needed_framework : Separate<["-"], "needed_framework">, + MetaVarName<"">, + HelpText<"Like -framework , but link even if none of its symbols are used and -dead_strip_dylibs is active">, + Group; def reexport_framework : Separate<["-"], "reexport_framework">, - MetaVarName<"">, - HelpText<"Like -framework , but export all symbols of from the newly created library">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like -framework , but export all symbols of from the newly created library">, + Group; def upward_framework : Separate<["-"], "upward_framework">, - MetaVarName<"">, - HelpText<"Like -framework , but specify the framework as an upward dependency">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Like -framework , but specify the framework as an upward dependency">, + Flags<[HelpHidden]>, + Group; def F : JoinedOrSeparate<["-"], "F">, - MetaVarName<"">, - HelpText<"Add dir to the framework search path">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Add dir to the framework search path">, + Group; def all_load : Flag<["-"], "all_load">, - HelpText<"Load all members of all static archive libraries">, - Flags<[HelpHidden]>, - Group; + HelpText<"Load all members of all static archive libraries">, + Group; def ObjC : Flag<["-"], "ObjC">, - HelpText<"Load all members of static archives that are an Objective-C class or category.">, - Flags<[HelpHidden]>, - Group; + HelpText<"Load all members of static archives that are an Objective-C class or category.">, + Group; def force_load : Separate<["-"], "force_load">, - MetaVarName<"">, - HelpText<"Load all members static archive library at ">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Load all members static archive library at ">, + Group; +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 grp_content : OptionGroup<"content">, HelpText<"ADDITIONAL CONTENT">; def sectcreate : MultiArg<["-"], "sectcreate", 3>, - MetaVarName<"
">, - HelpText<"Create
in from the contents of ">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"
">, + HelpText<"Create
in from the contents of ">, + Group; def segcreate : MultiArg<["-"], "segcreate", 3>, - MetaVarName<"
">, - Alias, - HelpText<"Alias for -sectcreate">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"
">, + Alias, + HelpText<"Alias for -sectcreate">, + Group; def filelist : Separate<["-"], "filelist">, - MetaVarName<"">, - HelpText<"Read names of files to link from ">, - Flags<[HelpHidden]>, - Group; + MetaVarName<"">, + HelpText<"Read names of files to link from ">, + Group; def dtrace : Separate<["-"], "dtrace">, - MetaVarName<"