From: patrick Date: Wed, 28 Apr 2021 12:29:31 +0000 (+0000) Subject: Import LLVM 11.1.0 release including clang, lld and lldb. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=bb684c341a3e51760883ccd79a12f6a8e98ab5b9;p=openbsd Import LLVM 11.1.0 release including clang, lld and lldb. --- diff --git a/gnu/llvm/lld/.clang-tidy b/gnu/llvm/lld/.clang-tidy new file mode 100644 index 00000000000..87ec2ff53af --- /dev/null +++ b/gnu/llvm/lld/.clang-tidy @@ -0,0 +1,19 @@ +# 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' +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 641f71c114a..040bb2c8f6d 100644 --- a/gnu/llvm/lld/CMakeLists.txt +++ b/gnu/llvm/lld/CMakeLists.txt @@ -54,18 +54,41 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) include(AddLLVM) include(TableGen) include(HandleLLVMOptions) + include(CheckAtomic) if(LLVM_INCLUDE_TESTS) - include(FindPythonInterp) - if(NOT PYTHONINTERP_FOUND) - message(FATAL_ERROR -"Unable to find Python interpreter, required for testing. + 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() + 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() - if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7) - message(FATAL_ERROR "Python 2.7 or newer is required") + 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() # Check prebuilt llvm/utils. @@ -120,6 +143,13 @@ set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include ) set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(LLD_VENDOR ${PACKAGE_VENDOR} CACHE STRING + "Vendor-specific text for showing with version information.") + +if(LLD_VENDOR) + add_definitions(-DLLD_VENDOR="${LLD_VENDOR}") +endif() + # Compute the LLD version from the LLVM version. string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION ${PACKAGE_VERSION}) @@ -130,33 +160,6 @@ string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" LLD_VERSION_MAJOR string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR ${LLD_VERSION}) -# Determine LLD revision and repository. -# TODO: Figure out a way to get the revision and the repository on windows. -if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" ) - execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR} - OUTPUT_VARIABLE LLD_REVISION) - - execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR} - OUTPUT_VARIABLE LLD_REPOSITORY) - if ( LLD_REPOSITORY ) - # Replace newline characters with spaces - string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY}) - # Remove leading spaces - STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" ) - # Remove trailing spaces - string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY}) - endif() - - if ( LLD_REVISION ) - # Replace newline characters with spaces - string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION}) - # Remove leading spaces - STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" ) - # Remove trailing spaces - string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION}) - endif() -endif () - # Configure the Version.inc file. configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in @@ -221,5 +224,8 @@ endif() add_subdirectory(docs) add_subdirectory(COFF) add_subdirectory(ELF) +add_subdirectory(MachO) add_subdirectory(MinGW) add_subdirectory(wasm) + +add_subdirectory(cmake/modules) diff --git a/gnu/llvm/lld/COFF/CMakeLists.txt b/gnu/llvm/lld/COFF/CMakeLists.txt index 1d310f8419f..4592ace373e 100644 --- a/gnu/llvm/lld/COFF/CMakeLists.txt +++ b/gnu/llvm/lld/COFF/CMakeLists.txt @@ -14,6 +14,7 @@ add_lld_library(lldCOFF DriverUtils.cpp ICF.cpp InputFiles.cpp + LLDMapFile.cpp LTO.cpp MapFile.cpp MarkLive.cpp diff --git a/gnu/llvm/lld/COFF/Chunks.cpp b/gnu/llvm/lld/COFF/Chunks.cpp index 0e43d2b478b..e04ceed505c 100644 --- a/gnu/llvm/lld/COFF/Chunks.cpp +++ b/gnu/llvm/lld/COFF/Chunks.cpp @@ -333,7 +333,7 @@ static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk, } else { COFFSymbolRef coffSym = check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex)); - file->getCOFFObj()->getSymbolName(coffSym, name); + name = check(file->getCOFFObj()->getSymbolName(coffSym)); } std::vector symbolLocations = diff --git a/gnu/llvm/lld/COFF/Chunks.h b/gnu/llvm/lld/COFF/Chunks.h index 2be2a72c4a1..0528143383c 100644 --- a/gnu/llvm/lld/COFF/Chunks.h +++ b/gnu/llvm/lld/COFF/Chunks.h @@ -269,7 +269,8 @@ public: AssociatedIterator() = default; AssociatedIterator(SectionChunk *head) : cur(head) {} bool operator==(const AssociatedIterator &r) const { return cur == r.cur; } - const SectionChunk &operator*() const { return *cur; } + // FIXME: Wrong const-ness, but it makes filter ranges work. + SectionChunk &operator*() const { return *cur; } SectionChunk &operator*() { return *cur; } AssociatedIterator &operator++() { cur = cur->assocChildren; diff --git a/gnu/llvm/lld/COFF/Config.h b/gnu/llvm/lld/COFF/Config.h index 2690ea5c408..7c439176f3a 100644 --- a/gnu/llvm/lld/COFF/Config.h +++ b/gnu/llvm/lld/COFF/Config.h @@ -110,6 +110,7 @@ struct Configuration { bool showSummary = false; unsigned debugTypes = static_cast(DebugType::None); std::vector natvisFiles; + llvm::StringMap namedStreams; llvm::SmallString<128> pdbAltPath; llvm::SmallString<128> pdbPath; llvm::SmallString<128> pdbSourcePath; @@ -139,12 +140,13 @@ struct Configuration { bool safeSEH = false; Symbol *sehTable = nullptr; Symbol *sehCount = nullptr; + bool noSEH = false; // Used for /opt:lldlto=N unsigned ltoo = 2; // Used for /opt:lldltojobs=N - unsigned thinLTOJobs = 0; + std::string thinLTOJobs; // Used for /opt:lldltopartitions=N unsigned ltoPartitions = 1; @@ -182,6 +184,9 @@ struct Configuration { llvm::StringMap order; // Used for /lldmap. + std::string lldmapFile; + + // Used for /map. std::string mapFile; // Used for /thinlto-index-only: @@ -211,6 +216,7 @@ struct Configuration { uint32_t functionPadMin = 0; bool dynamicBase = true; bool allowBind = true; + bool cetCompat = false; bool nxCompat = true; bool allowIsolation = true; bool terminalServerAware = true; @@ -230,6 +236,8 @@ struct Configuration { bool swaprunNet = false; bool thinLTOEmitImportsFiles; bool thinLTOIndexOnly; + bool autoImport = false; + bool pseudoRelocs = false; }; extern Configuration *config; diff --git a/gnu/llvm/lld/COFF/DebugTypes.cpp b/gnu/llvm/lld/COFF/DebugTypes.cpp index 0960f16b01e..abe3bb9eef5 100644 --- a/gnu/llvm/lld/COFF/DebugTypes.cpp +++ b/gnu/llvm/lld/COFF/DebugTypes.cpp @@ -7,22 +7,26 @@ //===----------------------------------------------------------------------===// #include "DebugTypes.h" +#include "Chunks.h" #include "Driver.h" #include "InputFiles.h" +#include "TypeMerger.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::codeview; - -namespace lld { -namespace coff { +using namespace lld; +using namespace lld::coff; namespace { // The TypeServerSource class represents a PDB type server, a file referenced by @@ -34,36 +38,40 @@ namespace { // before any dependent OBJ. class TypeServerSource : public TpiSource { public: - explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s) - : TpiSource(PDB, nullptr), session(s), mb(m) {} - - // Queue a PDB type server for loading in the COFF Driver - static void enqueue(const ObjFile *dependentFile, - const TypeServer2Record &ts); - - // Create an instance - static Expected getInstance(MemoryBufferRef m); - - // Fetch the PDB instance loaded for a corresponding dependent OBJ. - static Expected - findFromFile(const ObjFile *dependentFile); - - static std::map> - instances; - - // The interface to the PDB (if it was opened successfully) - std::unique_ptr session; - -private: - MemoryBufferRef mb; + explicit TypeServerSource(PDBInputFile *f) + : TpiSource(PDB, nullptr), pdbInputFile(f) { + if (f->loadErr && *f->loadErr) + return; + pdb::PDBFile &file = f->session->getPDBFile(); + auto expectedInfo = file.getPDBInfoStream(); + if (!expectedInfo) + return; + auto it = mappings.emplace(expectedInfo->getGuid(), this); + assert(it.second); + (void)it; + tsIndexMap.isTypeServerMap = true; + } + + Expected mergeDebugT(TypeMerger *m, + CVIndexMap *indexMap) override; + bool isDependency() const override { return true; } + + PDBInputFile *pdbInputFile = nullptr; + + CVIndexMap tsIndexMap; + + static std::map mappings; }; // This class represents the debug type stream of an OBJ file that depends on a // PDB type server (see TypeServerSource). class UseTypeServerSource : public TpiSource { public: - UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) - : TpiSource(UsingPDB, f), typeServerDependency(*ts) {} + UseTypeServerSource(ObjFile *f, TypeServer2Record ts) + : TpiSource(UsingPDB, f), typeServerDependency(ts) {} + + Expected mergeDebugT(TypeMerger *m, + CVIndexMap *indexMap) override; // Information about the PDB type server dependency, that needs to be loaded // in before merging this OBJ. @@ -76,15 +84,35 @@ public: // such files, clang does not. class PrecompSource : public TpiSource { public: - PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {} + PrecompSource(ObjFile *f) : TpiSource(PCH, f) { + if (!f->pchSignature || !*f->pchSignature) + fatal(toString(f) + + " claims to be a PCH object, but does not have a valid signature"); + auto it = mappings.emplace(*f->pchSignature, this); + if (!it.second) + fatal("a PCH object with the same signature has already been provided (" + + toString(it.first->second->file) + " and " + toString(file) + ")"); + precompIndexMap.isPrecompiledTypeMap = true; + } + + Expected mergeDebugT(TypeMerger *m, + CVIndexMap *indexMap) override; + bool isDependency() const override { return true; } + + CVIndexMap precompIndexMap; + + static std::map mappings; }; // This class represents the debug type stream of an OBJ file that depends on a // Microsoft precompiled headers OBJ (see PrecompSource). class UsePrecompSource : public TpiSource { public: - UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) - : TpiSource(UsingPCH, f), precompDependency(*precomp) {} + UsePrecompSource(ObjFile *f, PrecompRecord precomp) + : TpiSource(UsingPCH, f), precompDependency(precomp) {} + + Expected mergeDebugT(TypeMerger *m, + CVIndexMap *indexMap) override; // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. @@ -92,173 +120,366 @@ public: }; } // namespace -TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {} +static std::vector gc; -TpiSource *makeTpiSource(const ObjFile *f) { - return make(TpiSource::Regular, f); +TpiSource::TpiSource(TpiKind k, ObjFile *f) : kind(k), file(f) { + gc.push_back(this); } -TpiSource *makeUseTypeServerSource(const ObjFile *f, - const TypeServer2Record *ts) { - TypeServerSource::enqueue(f, *ts); - return make(f, ts); +// Vtable key method. +TpiSource::~TpiSource() = default; + +TpiSource *lld::coff::makeTpiSource(ObjFile *file) { + return make(TpiSource::Regular, file); } -TpiSource *makePrecompSource(const ObjFile *f) { - return make(f); +TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) { + return make(pdbInputFile); } -TpiSource *makeUsePrecompSource(const ObjFile *f, - const PrecompRecord *precomp) { - return make(f, precomp); +TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file, + TypeServer2Record ts) { + return make(file, ts); } -template <> -const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { - assert(source->kind == TpiSource::UsingPCH); - return ((const UsePrecompSource *)source)->precompDependency; +TpiSource *lld::coff::makePrecompSource(ObjFile *file) { + return make(file); } -template <> -const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { - assert(source->kind == TpiSource::UsingPDB); - return ((const UseTypeServerSource *)source)->typeServerDependency; +TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file, + PrecompRecord precomp) { + return make(file, precomp); } -std::map> - TypeServerSource::instances; +void TpiSource::forEachSource(llvm::function_ref fn) { + for_each(gc, fn); +} -// Make a PDB path assuming the PDB is in the same folder as the OBJ -static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) { - StringRef localPath = - !file->parentName.empty() ? file->parentName : file->getName(); - SmallString<128> path = sys::path::parent_path(localPath); +std::map TypeServerSource::mappings; + +std::map PrecompSource::mappings; + +// A COFF .debug$H section is currently a clang extension. This function checks +// if a .debug$H section is in a format that we expect / understand, so that we +// can ignore any sections which are coincidentally also named .debug$H but do +// not contain a format we recognize. +static bool canUseDebugH(ArrayRef debugH) { + if (debugH.size() < sizeof(object::debug_h_header)) + return false; + auto *header = + reinterpret_cast(debugH.data()); + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && + header->Version == 0 && + header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && + (debugH.size() % 8 == 0); +} - // Currently, type server PDBs are only created by MSVC cl, which only runs - // on Windows, so we can assume type server paths are Windows style. - sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows)); - return path.str(); +static Optional> getDebugH(ObjFile *file) { + SectionChunk *sec = + SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); + if (!sec) + return llvm::None; + ArrayRef contents = sec->getContents(); + if (!canUseDebugH(contents)) + return None; + return contents; } -// The casing of the PDB path stamped in the OBJ can differ from the actual path -// on disk. With this, we ensure to always use lowercase as a key for the -// PDBInputFile::Instances map, at least on Windows. -static std::string normalizePdbPath(StringRef path) { -#if defined(_WIN32) - return path.lower(); -#else // LINUX - return path; -#endif +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}; } -// If existing, return the actual PDB path on disk. -static Optional findPdbPath(StringRef pdbPath, - const ObjFile *dependentFile) { - // Ensure the file exists before anything else. In some cases, if the path - // points to a removable device, Driver::enqueuePath() would fail with an - // error (EAGAIN, "resource unavailable try again") which we want to skip - // silently. - if (llvm::sys::fs::exists(pdbPath)) - return normalizePdbPath(pdbPath); - std::string ret = getPdbBaseName(dependentFile, pdbPath); - if (llvm::sys::fs::exists(ret)) - return normalizePdbPath(ret); - return None; +// Merge .debug$T for a generic object file. +Expected TpiSource::mergeDebugT(TypeMerger *m, + CVIndexMap *indexMap) { + 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; + } + + 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 (config->showSummary) { + // 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 + // collecting statistics. + m->tpiCounts.resize(m->getTypeTable().size()); + m->ipiCounts.resize(m->getIDTable().size()); + uint32_t srcIdx = nbHeadIndices; + for (CVType &ty : types) { + TypeIndex dstIdx = indexMap->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()) + continue; + SmallVectorImpl &counts = + isIdRecord(ty.kind()) ? m->ipiCounts : m->tpiCounts; + ++counts[dstIdx.toArrayIndex()]; + } + } + + return indexMap; } -// Fetch the PDB instance that was already loaded by the COFF Driver. -Expected -TypeServerSource::findFromFile(const ObjFile *dependentFile) { - const TypeServer2Record &ts = - retrieveDependencyInfo(dependentFile->debugTypesObj); +// Merge types from a type server PDB. +Expected TypeServerSource::mergeDebugT(TypeMerger *m, + CVIndexMap *) { + pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); + Expected expectedTpi = pdbFile.getPDBTpiStream(); + if (auto e = expectedTpi.takeError()) + fatal("Type server does not have TPI stream: " + toString(std::move(e))); + pdb::TpiStream *maybeIpi = nullptr; + if (pdbFile.hasPDBIpiStream()) { + Expected expectedIpi = pdbFile.getPDBIpiStream(); + if (auto e = expectedIpi.takeError()) + fatal("Error getting type server IPI stream: " + toString(std::move(e))); + maybeIpi = &*expectedIpi; + } + + if (config->debugGHashes) { + // PDBs do not actually store global hashes, so when merging a type server + // PDB we have to synthesize global hashes. To do this, we first synthesize + // global hashes for the TPI stream, since it is independent, then we + // synthesize hashes for the IPI stream, using the hashes for the TPI stream + // as inputs. + auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); + Optional endPrecomp; + // Merge TPI first, because the IPI stream will reference type indices. + if (auto err = + mergeTypeRecords(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))); + } + } + + if (config->showSummary) { + // 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) + if (!ti.isSimple()) + ++m->tpiCounts[ti.toArrayIndex()]; + for (TypeIndex ti : tsIndexMap.ipiMap) + if (!ti.isSimple()) + ++m->ipiCounts[ti.toArrayIndex()]; + } + + return &tsIndexMap; +} - Optional p = findPdbPath(ts.Name, dependentFile); - if (!p) - return createFileError(ts.Name, errorCodeToError(std::error_code( - ENOENT, std::generic_category()))); +Expected +UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { + const codeview::GUID &tsId = typeServerDependency.getGuid(); + StringRef tsPath = typeServerDependency.getName(); + + TypeServerSource *tsSrc; + auto it = TypeServerSource::mappings.find(tsId); + if (it != TypeServerSource::mappings.end()) { + tsSrc = it->second; + } else { + // The file failed to load, lookup by name + PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file); + if (!pdb) + return createFileError(tsPath, errorCodeToError(std::error_code( + ENOENT, std::generic_category()))); + // If an error occurred during loading, throw it now + if (pdb->loadErr && *pdb->loadErr) + return createFileError(tsPath, std::move(*pdb->loadErr)); + + tsSrc = (TypeServerSource *)pdb->debugTypesObj; + } + + 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)); - auto it = TypeServerSource::instances.find(*p); - // The PDB file exists on disk, at this point we expect it to have been - // inserted in the map by TypeServerSource::loadPDB() - assert(it != TypeServerSource::instances.end()); + return &tsSrc->tsIndexMap; +} + +static bool equalsPath(StringRef path1, StringRef path2) { +#if defined(_WIN32) + return path1.equals_lower(path2); +#else + return path1.equals(path2); +#endif +} - std::pair &pdb = it->second; +// Find by name an OBJ provided on the command line +static PrecompSource *findObjByName(StringRef fileNameOnly) { + SmallString<128> currentPath; + for (auto kv : PrecompSource::mappings) { + StringRef currentFileName = sys::path::filename(kv.second->file->getName(), + sys::path::Style::windows); + + // Compare based solely on the file name (link.exe behavior) + if (equalsPath(currentFileName, fileNameOnly)) + return kv.second; + } + return nullptr; +} - if (!pdb.second) +Expected findPrecompMap(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); + } + + if (!precomp) return createFileError( - *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str())); + prFileName, + make_error(pdb::pdb_error_code::no_matching_pch)); - pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile(); - pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); + if (pr.getSignature() != file->pchSignature) + return createFileError( + toString(file), + make_error(pdb::pdb_error_code::no_matching_pch)); - // Just because a file with a matching name was found doesn't mean it can be - // used. The GUID must match between the PDB header and the OBJ - // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. - if (info.getGuid() != ts.getGuid()) + if (pr.getSignature() != *precomp->file->pchSignature) return createFileError( - ts.Name, - make_error(pdb::pdb_error_code::signature_out_of_date)); + toString(precomp->file), + make_error(pdb::pdb_error_code::no_matching_pch)); - return pdb.second; + return &precomp->precompIndexMap; } -// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is -// moved here. -Expected findTypeServerSource(const ObjFile *f) { - Expected ts = TypeServerSource::findFromFile(f); - if (!ts) - return ts.takeError(); - return ts.get()->session.get(); +/// 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); + if (!e) + return e.takeError(); + + const CVIndexMap *precompIndexMap = *e; + assert(precompIndexMap->isPrecompiledTypeMap); + + if (precompIndexMap->tpiMap.empty()) + return precompIndexMap; + + assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); + assert(precomp.getTypesCount() <= precompIndexMap->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; } -// Queue a PDB type server for loading in the COFF Driver -void TypeServerSource::enqueue(const ObjFile *dependentFile, - const TypeServer2Record &ts) { - // Start by finding where the PDB is located (either the record path or next - // to the OBJ file) - Optional p = findPdbPath(ts.Name, dependentFile); - if (!p) - return; - auto it = TypeServerSource::instances.emplace( - *p, std::pair{}); - if (!it.second) - return; // another OBJ already scheduled this PDB for load - - driver->enqueuePath(*p, false, false); +Expected +UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { + // 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()); + + return TpiSource::mergeDebugT(m, indexMap); } -// Create an instance of TypeServerSource or an error string if the PDB couldn't -// be loaded. The error message will be displayed later, when the referring OBJ -// will be merged in. NOTE - a PDB load failure is not a link error: some -// debug info will simply be missing from the final PDB - that is the default -// accepted behavior. -void loadTypeServerSource(llvm::MemoryBufferRef m) { - std::string path = normalizePdbPath(m.getBufferIdentifier()); - - Expected ts = TypeServerSource::getInstance(m); - if (!ts) - TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; - else - TypeServerSource::instances[path] = {{}, *ts}; +Expected 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); } -Expected TypeServerSource::getInstance(MemoryBufferRef m) { - std::unique_ptr iSession; - Error err = pdb::NativeSession::createFromPdb( - MemoryBuffer::getMemBuffer(m, false), iSession); - if (err) - return std::move(err); - - std::unique_ptr session( - static_cast(iSession.release())); - - pdb::PDBFile &pdbFile = session->getPDBFile(); - Expected info = pdbFile.getPDBInfoStream(); - // All PDB Files should have an Info stream. - if (!info) - return info.takeError(); - return make(m, session.release()); +uint32_t TpiSource::countTypeServerPDBs() { + return TypeServerSource::mappings.size(); } -} // namespace coff -} // namespace lld +uint32_t TpiSource::countPrecompObjs() { + return PrecompSource::mappings.size(); +} + +void TpiSource::clear() { + gc.clear(); + TypeServerSource::mappings.clear(); + PrecompSource::mappings.clear(); +} diff --git a/gnu/llvm/lld/COFF/DebugTypes.h b/gnu/llvm/lld/COFF/DebugTypes.h index e37c727232d..24d79d83e4c 100644 --- a/gnu/llvm/lld/COFF/DebugTypes.h +++ b/gnu/llvm/lld/COFF/DebugTypes.h @@ -26,35 +26,55 @@ namespace lld { namespace coff { class ObjFile; +class PDBInputFile; +struct CVIndexMap; +class TypeMerger; class TpiSource { public: enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; - TpiSource(TpiKind k, const ObjFile *f); - virtual ~TpiSource() {} + TpiSource(TpiKind k, ObjFile *f); + virtual ~TpiSource(); - const TpiKind kind; - const ObjFile *file; -}; + /// Produce a mapping from the type and item indices used in the object + /// file to those in the destination PDB. + /// + /// If the object file uses a type server PDB (compiled with /Zi), merge TPI + /// and IPI from the type server PDB and return a map for it. Each unique type + /// server PDB is merged at most once, so this may return an existing index + /// mapping. + /// + /// If the object does not use a type server PDB (compiled with /Z7), we merge + /// all the type and item records from the .debug$S stream and fill in the + /// caller-provided ObjectIndexMap. + virtual llvm::Expected mergeDebugT(TypeMerger *m, + CVIndexMap *indexMap); + /// 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); -TpiSource *makeTpiSource(const ObjFile *f); -TpiSource *makeUseTypeServerSource(const ObjFile *f, - const llvm::codeview::TypeServer2Record *ts); -TpiSource *makePrecompSource(const ObjFile *f); -TpiSource *makeUsePrecompSource(const ObjFile *f, - const llvm::codeview::PrecompRecord *precomp); + static uint32_t countTypeServerPDBs(); + static uint32_t countPrecompObjs(); -void loadTypeServerSource(llvm::MemoryBufferRef m); + /// Clear global data structures for TpiSources. + static void clear(); -// Temporary interface to get the dependency -template const T &retrieveDependencyInfo(const TpiSource *source); + const TpiKind kind; + ObjFile *file; +}; -// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here -llvm::Expected -findTypeServerSource(const ObjFile *f); +TpiSource *makeTpiSource(ObjFile *file); +TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile); +TpiSource *makeUseTypeServerSource(ObjFile *file, + llvm::codeview::TypeServer2Record ts); +TpiSource *makePrecompSource(ObjFile *file); +TpiSource *makeUsePrecompSource(ObjFile *file, + llvm::codeview::PrecompRecord ts); } // namespace coff } // namespace lld -#endif \ No newline at end of file +#endif diff --git a/gnu/llvm/lld/COFF/Driver.cpp b/gnu/llvm/lld/COFF/Driver.cpp index f770fff80bc..9ceccef8677 100644 --- a/gnu/llvm/lld/COFF/Driver.cpp +++ b/gnu/llvm/lld/COFF/Driver.cpp @@ -21,7 +21,6 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" -#include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" @@ -39,6 +38,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" @@ -89,6 +89,8 @@ bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, ImportFile::instances.clear(); BitcodeFile::instances.clear(); memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); + TpiSource::clear(); + return !errorCount(); } @@ -218,7 +220,7 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, symtab->addFile(make(mbref)); break; case file_magic::pdb: - loadTypeServerSource(mbref); + symtab->addFile(make(mbref)); break; case file_magic::coff_cl_gl_object: error(filename + ": is not a native COFF file. Recompile without /GL"); @@ -237,9 +239,9 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, } void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { - auto future = - std::make_shared>(createFutureForFile(path)); - std::string pathStr = path; + auto future = std::make_shared>( + createFutureForFile(std::string(path))); + std::string pathStr = std::string(path); enqueueTask([=]() { auto mbOrErr = future->get(); if (mbOrErr.second) { @@ -251,7 +253,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { // the option `/nodefaultlib` than a reference to a file in the root // directory. std::string nearest; - if (COFFOptTable().findNearest(pathStr, nearest) > 1) + if (optTable.findNearest(pathStr, nearest) > 1) error(msg); else error(msg + "; did you mean '" + nearest + "'"); @@ -343,11 +345,9 @@ void LinkerDriver::parseDirectives(InputFile *file) { ArgParser parser; // .drectve is always tokenized using Windows shell rules. // /EXPORT: option can appear too many times, processing in fastpath. - opt::InputArgList args; - std::vector exports; - std::tie(args, exports) = parser.parseDirectives(s); + ParsedDirectives directives = parser.parseDirectives(s); - for (StringRef e : exports) { + for (StringRef e : directives.exports) { // If a common header file contains dllexported function // declarations, many object files may end up with having the // same /EXPORT options. In order to save cost of parsing them, @@ -366,7 +366,11 @@ void LinkerDriver::parseDirectives(InputFile *file) { config->exports.push_back(exp); } - for (auto *arg : args) { + // Handle /include: in bulk. + for (StringRef inc : directives.includes) + addUndefined(inc); + + for (auto *arg : directives.args) { switch (arg->getOption().getID()) { case OPT_aligncomm: parseAligncomm(arg->getValue()); @@ -452,7 +456,7 @@ Optional LinkerDriver::findFile(StringRef filename) { } if (path.endswith_lower(".lib")) - visitedLibs.insert(sys::path::filename(path)); + visitedLibs.insert(std::string(sys::path::filename(path))); return path; } @@ -624,6 +628,7 @@ static std::string createResponseFile(const opt::InputArgList &args, break; case OPT_implib: case OPT_pdb: + case OPT_pdbstripped: case OPT_out: os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n"; break; @@ -640,7 +645,7 @@ static std::string createResponseFile(const opt::InputArgList &args, for (StringRef path : filePaths) os << quote(relativeToRoot(path)) << "\n"; - return data.str(); + return std::string(data.str()); } enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab }; @@ -706,24 +711,25 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) { return debugTypes; } -static std::string getMapFile(const opt::InputArgList &args) { - auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file); +static std::string getMapFile(const opt::InputArgList &args, + opt::OptSpecifier os, opt::OptSpecifier osFile) { + auto *arg = args.getLastArg(os, osFile); if (!arg) return ""; - if (arg->getOption().getID() == OPT_lldmap_file) + if (arg->getOption().getID() == osFile.getID()) return arg->getValue(); - assert(arg->getOption().getID() == OPT_lldmap); + assert(arg->getOption().getID() == os.getID()); StringRef outFile = config->outputFile; return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); } static std::string getImplibPath() { if (!config->implib.empty()) - return config->implib; + return std::string(config->implib); SmallString<128> out = StringRef(config->outputFile); sys::path::replace_extension(out, ".lib"); - return out.str(); + return std::string(out.str()); } // The import name is calculated as follows: @@ -747,16 +753,16 @@ static std::string getImportName(bool asLib) { (config->dll || asLib) ? ".dll" : ".exe"); } - return out.str(); + return std::string(out.str()); } static void createImportLibrary(bool asLib) { std::vector exports; for (Export &e1 : config->exports) { COFFShortExport e2; - e2.Name = e1.name; - e2.SymbolName = e1.symbolName; - e2.ExtName = e1.extName; + e2.Name = std::string(e1.name); + e2.SymbolName = std::string(e1.symbolName); + e2.ExtName = std::string(e1.extName); e2.Ordinal = e1.ordinal; e2.Noname = e1.noname; e2.Data = e1.data; @@ -817,8 +823,8 @@ static void parseModuleDefs(StringRef path) { mb->getMemBufferRef(), config->machine, config->mingw)); if (config->outputFile.empty()) - config->outputFile = saver.save(m.OutputFile); - config->importName = saver.save(m.ImportName); + config->outputFile = std::string(saver.save(m.OutputFile)); + config->importName = std::string(saver.save(m.ImportName)); if (m.ImageBase) config->imageBase = m.ImageBase; if (m.StackReserve) @@ -844,7 +850,8 @@ static void parseModuleDefs(StringRef path) { // and set as "ExtName = Name". If Name has the form "OtherDll.Func", // it shouldn't be a normal exported function but a forward to another // DLL instead. This is supported by both MS and GNU linkers. - if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) { + if (!e1.ExtName.empty() && e1.ExtName != e1.Name && + StringRef(e1.Name).contains('.')) { e2.name = saver.save(e1.ExtName); e2.forwardTo = saver.save(e1.Name); config->exports.push_back(e2); @@ -903,7 +910,8 @@ static void parseOrderFile(StringRef arg) { // All symbols that were not present in a given order file are // considered to have the lowest priority 0 and are placed at // end of an output section. - for (std::string s : args::getLines(mb->getMemBufferRef())) { + for (StringRef arg : args::getLines(mb->getMemBufferRef())) { + std::string s(arg); if (config->machine == I386 && !isDecorated(s)) s = "_" + s; @@ -1093,13 +1101,15 @@ Optional getReproduceFile(const opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_linkrepro)) { SmallString<64> path = StringRef(arg->getValue()); sys::path::append(path, "repro.tar"); - return path.str().str(); + return std::string(path); } return None; } void LinkerDriver::link(ArrayRef argsArr) { + ScopedTimer rootTimer(Timer::root()); + // Needed for LTO. InitializeAllTargetInfos(); InitializeAllTargets(); @@ -1141,14 +1151,23 @@ void LinkerDriver::link(ArrayRef argsArr) { return; } - lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true); + // /threads: takes a positive integer and provides the default value for + // /opt:lldltojobs=. + if (auto *arg = args.getLastArg(OPT_threads)) { + 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.str(); + } if (args.hasArg(OPT_show_timing)) config->showTiming = true; config->showSummary = args.hasArg(OPT_summary); - ScopedTimer t(Timer::root()); // Handle --version, which is an lld extension. This option is a bit odd // because it doesn't start with "/", but we deliberately chose "--" to // avoid conflict with /version and for compatibility with clang-cl. @@ -1260,11 +1279,23 @@ void LinkerDriver::link(ArrayRef argsArr) { config->pdbAltPath = arg->getValue(); if (args.hasArg(OPT_natvis)) config->natvisFiles = args.getAllArgValues(OPT_natvis); + if (args.hasArg(OPT_pdbstream)) { + for (const StringRef value : args.getAllArgValues(OPT_pdbstream)) { + const std::pair nameFile = value.split("="); + const StringRef name = nameFile.first; + const std::string file = nameFile.second.str(); + config->namedStreams[name] = file; + } + } if (auto *arg = args.getLastArg(OPT_pdb_source_path)) config->pdbSourcePath = arg->getValue(); } + // Handle /pdbstripped + if (args.hasArg(OPT_pdbstripped)) + warn("ignoring /pdbstripped flag, it is not yet supported"); + // Handle /noentry if (args.hasArg(OPT_noentry)) { if (args.hasArg(OPT_dll)) @@ -1410,9 +1441,9 @@ void LinkerDriver::link(ArrayRef argsArr) { error("/opt:lldlto: invalid optimization level: " + optLevel); } else if (s.startswith("lldltojobs=")) { StringRef jobs = s.substr(11); - if (jobs.getAsInteger(10, config->thinLTOJobs) || - config->thinLTOJobs == 0) + if (!get_threadpool_strategy(jobs)) error("/opt:lldltojobs: invalid job count: " + jobs); + config->thinLTOJobs = jobs.str(); } else if (s.startswith("lldltopartitions=")) { StringRef n = s.substr(17); if (n.getAsInteger(10, config->ltoPartitions) || @@ -1543,6 +1574,7 @@ void LinkerDriver::link(ArrayRef argsArr) { !args.hasArg(OPT_profile)); config->integrityCheck = args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); + config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false); config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); for (auto *arg : args.filtered(OPT_swaprun)) parseSwaprun(arg->getValue()); @@ -1551,13 +1583,24 @@ void LinkerDriver::link(ArrayRef argsArr) { config->debugDwarf = debug == DebugKind::Dwarf; config->debugGHashes = debug == DebugKind::GHash; 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. if (config->mingw || config->debugDwarf) config->warnLongSectionNames = false; - config->mapFile = getMapFile(args); + config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file); + config->mapFile = getMapFile(args, OPT_map, OPT_map_file); + + if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) { + warn("/lldmap and /map have the same output file '" + config->mapFile + + "'.\n>>> ignoring /lldmap"); + config->lldmapFile.clear(); + } if (config->incremental && args.hasArg(OPT_profile)) { warn("ignoring '/incremental' due to '/profile' specification"); @@ -1657,9 +1700,10 @@ void LinkerDriver::link(ArrayRef argsArr) { config->wordsize = config->is64() ? 8 : 4; // Handle /safeseh, x86 only, on by default, except for mingw. - if (config->machine == I386 && - args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw)) - config->safeSEH = true; + if (config->machine == I386) { + config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw); + config->noSEH = args.hasArg(OPT_noseh); + } // Handle /functionpadmin for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) @@ -1805,9 +1849,11 @@ void LinkerDriver::link(ArrayRef argsArr) { // Needed for MSVC 2017 15.5 CRT. symtab->addAbsolute(mangle("__enclave_config"), 0); - if (config->mingw) { + if (config->pseudoRelocs) { symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); + } + if (config->mingw) { symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); } @@ -1865,7 +1911,8 @@ void LinkerDriver::link(ArrayRef argsArr) { while (run()); } - if (config->mingw) { + if (config->autoImport) { + // MinGW specific. // Load any further object files that might be needed for doing automatic // imports. // @@ -1997,7 +2044,7 @@ void LinkerDriver::link(ArrayRef argsArr) { writeResult(); // Stop early so we can print the results. - Timer::root().stop(); + rootTimer.stop(); if (config->showTiming) Timer::root().print(); } diff --git a/gnu/llvm/lld/COFF/Driver.h b/gnu/llvm/lld/COFF/Driver.h index cc2f25a6f95..3fee9b1fe50 100644 --- a/gnu/llvm/lld/COFF/Driver.h +++ b/gnu/llvm/lld/COFF/Driver.h @@ -41,6 +41,21 @@ public: COFFOptTable(); }; +// Constructing the option table is expensive. Use a global table to avoid doing +// it more than once. +extern COFFOptTable optTable; + +// The result of parsing the .drective section. The /export: and /include: +// options are handled separately because they reference symbols, and the number +// of symbols can be quite large. The LLVM Option library will perform at least +// one memory allocation per argument, and that is prohibitively slow for +// parsing directives. +struct ParsedDirectives { + std::vector exports; + std::vector includes; + llvm::opt::InputArgList args; +}; + class ArgParser { public: // Parses command line options. @@ -52,16 +67,13 @@ public: // Tokenizes a given string and then parses as command line options in // .drectve section. /EXPORT options are returned in second element // to be processed in fastpath. - std::pair> - parseDirectives(StringRef s); + ParsedDirectives parseDirectives(StringRef s); private: // Concatenate LINK environment variable. void addLINK(SmallVector &argv); std::vector tokenize(StringRef s); - - COFFOptTable table; }; class LinkerDriver { @@ -75,6 +87,8 @@ public: void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym, StringRef parentName); + void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); } + MemoryBufferRef takeBuffer(std::unique_ptr mb); void enqueuePath(StringRef path, bool wholeArchive, bool lazy); diff --git a/gnu/llvm/lld/COFF/DriverUtils.cpp b/gnu/llvm/lld/COFF/DriverUtils.cpp index 301a5c5efa8..6cb761abea4 100644 --- a/gnu/llvm/lld/COFF/DriverUtils.cpp +++ b/gnu/llvm/lld/COFF/DriverUtils.cpp @@ -220,7 +220,8 @@ void parseAligncomm(StringRef s) { error("/aligncomm: invalid argument: " + s); return; } - config->alignComm[name] = std::max(config->alignComm[name], 1 << v); + config->alignComm[std::string(name)] = + std::max(config->alignComm[std::string(name)], 1 << v); } // Parses /functionpadmin option argument. @@ -318,7 +319,7 @@ public: SmallString<128> s; if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s)) fatal("cannot create a temporary file: " + ec.message()); - path = s.str(); + path = std::string(s.str()); if (!contents.empty()) { std::error_code ec; @@ -403,7 +404,7 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { toString(std::move(e))); } - return merger.getMergedManifest().get()->getBuffer(); + return std::string(merger.getMergedManifest().get()->getBuffer()); } static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { @@ -431,9 +432,10 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { e.add("/out:" + StringRef(user.path)); e.run(); - return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) - .get() - ->getBuffer(); + return std::string( + CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) + .get() + ->getBuffer()); } static std::string createManifestXml() { @@ -507,7 +509,7 @@ std::unique_ptr createManifestRes() { } void createSideBySideManifest() { - std::string path = config->manifestFile; + std::string path = std::string(config->manifestFile); if (path == "") path = config->outputFile + ".manifest"; std::error_code ec; @@ -765,6 +767,8 @@ static const llvm::opt::OptTable::Info infoTable[] = { COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} +COFFOptTable optTable; + // Set color diagnostics according to --color-diagnostics={auto,always,never} // or --no-color-diagnostics flags. static void handleColorDiagnostics(opt::InputArgList &args) { @@ -810,8 +814,7 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { // options so we parse here before and ignore all the options but // --rsp-quoting and /lldignoreenv. // (This means --rsp-quoting can't be added through %LINK%.) - opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); - + opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount); // Expand response files (arguments in the form of @) and insert // flags from %LINK% and %_LINK_%, and then parse the argument again. @@ -820,8 +823,8 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { if (!args.hasArg(OPT_lldignoreenv)) addLINK(expandedArgv); cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); - args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, - missingCount); + args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), + missingIndex, missingCount); // Print the real command line if response files are expanded. if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { @@ -845,7 +848,7 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { for (auto *arg : args.filtered(OPT_UNKNOWN)) { std::string nearest; - if (table.findNearest(arg->getAsString(args), nearest) > 1) + if (optTable.findNearest(arg->getAsString(args), nearest) > 1) warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); else warn("ignoring unknown argument '" + arg->getAsString(args) + @@ -859,30 +862,38 @@ opt::InputArgList ArgParser::parse(ArrayRef argv) { } // Tokenizes and parses a given string as command line in .drective section. -// /EXPORT options are processed in fastpath. -std::pair> -ArgParser::parseDirectives(StringRef s) { - std::vector exports; +ParsedDirectives ArgParser::parseDirectives(StringRef s) { + ParsedDirectives result; SmallVector rest; - for (StringRef tok : tokenize(s)) { + // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for + // potentially every symbol in the object, so they must be handled quickly. + SmallVector tokens; + cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens); + for (StringRef tok : tokens) { if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:")) - exports.push_back(tok.substr(strlen("/export:"))); - else - rest.push_back(tok.data()); + result.exports.push_back(tok.substr(strlen("/export:"))); + else if (tok.startswith_lower("/include:") || + tok.startswith_lower("-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'; + rest.push_back(HasNul ? tok.data() : saver.save(tok).data()); + } } // Make InputArgList from unparsed string vectors. unsigned missingIndex; unsigned missingCount; - opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount); + result.args = optTable.ParseArgs(rest, missingIndex, missingCount); if (missingCount) - fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); - for (auto *arg : args.filtered(OPT_UNKNOWN)) - warn("ignoring unknown argument: " + arg->getAsString(args)); - return {std::move(args), std::move(exports)}; + fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument"); + for (auto *arg : result.args.filtered(OPT_UNKNOWN)) + warn("ignoring unknown argument: " + arg->getAsString(result.args)); + return result; } // link.exe has an interesting feature. If LINK or _LINK_ environment @@ -907,9 +918,9 @@ std::vector ArgParser::tokenize(StringRef s) { } void printHelp(const char *argv0) { - COFFOptTable().PrintHelp(lld::outs(), - (std::string(argv0) + " [options] file...").c_str(), - "LLVM Linker", false); + optTable.PrintHelp(lld::outs(), + (std::string(argv0) + " [options] file...").c_str(), + "LLVM Linker", false); } } // namespace coff diff --git a/gnu/llvm/lld/COFF/ICF.cpp b/gnu/llvm/lld/COFF/ICF.cpp index c821569e345..1b33634b63d 100644 --- a/gnu/llvm/lld/COFF/ICF.cpp +++ b/gnu/llvm/lld/COFF/ICF.cpp @@ -21,7 +21,6 @@ #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" -#include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/Debug.h" @@ -127,15 +126,19 @@ void ICF::segregate(size_t begin, size_t end, bool constant) { // Returns true if two sections' associative children are equal. bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { - auto childClasses = [&](const SectionChunk *sc) { - std::vector classes; - for (const SectionChunk &c : sc->children()) - if (!c.getSectionName().startswith(".debug") && - c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y") - classes.push_back(c.eqClass[cnt % 2]); - return classes; + // Ignore associated metadata sections that don't participate in ICF, such as + // debug info and CFGuard metadata. + auto considerForICF = [](const SectionChunk &assoc) { + StringRef Name = assoc.getSectionName(); + return !(Name.startswith(".debug") || Name == ".gfids$y" || + Name == ".gljmp$y"); }; - return childClasses(a) == childClasses(b); + auto ra = make_filter_range(a->children(), considerForICF); + auto rb = make_filter_range(b->children(), considerForICF); + return std::equal(ra.begin(), ra.end(), rb.begin(), rb.end(), + [&](const SectionChunk &ia, const SectionChunk &ib) { + return ia.eqClass[cnt % 2] == ib.eqClass[cnt % 2]; + }); } // Compare "non-moving" part of two sections, namely everything diff --git a/gnu/llvm/lld/COFF/InputFiles.cpp b/gnu/llvm/lld/COFF/InputFiles.cpp index 272f9c0717c..4346b3a2ffa 100644 --- a/gnu/llvm/lld/COFF/InputFiles.cpp +++ b/gnu/llvm/lld/COFF/InputFiles.cpp @@ -25,6 +25,8 @@ #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" @@ -44,32 +46,31 @@ using namespace llvm::COFF; using namespace llvm::codeview; using namespace llvm::object; using namespace llvm::support::endian; +using namespace lld; +using namespace lld::coff; using llvm::Triple; using llvm::support::ulittle32_t; -namespace lld { - // Returns the last element of a path, which is supposed to be a filename. static StringRef getBasename(StringRef path) { return sys::path::filename(path, sys::path::Style::windows); } // Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". -std::string toString(const coff::InputFile *file) { +std::string lld::toString(const coff::InputFile *file) { if (!file) return ""; if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) - return file->getName(); + return std::string(file->getName()); return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + ")") .str(); } -namespace coff { - std::vector ObjFile::instances; +std::map PDBInputFile::instances; std::vector ImportFile::instances; std::vector BitcodeFile::instances; @@ -121,7 +122,7 @@ void ArchiveFile::addMember(const Archive::Symbol &sym) { driver->enqueueArchiveMember(c, sym, getName()); } -std::vector getArchiveMembers(Archive *file) { +std::vector lld::coff::getArchiveMembers(Archive *file) { std::vector v; Error err = Error::success(); for (const Archive::Child &c : file->children(err)) { @@ -171,8 +172,7 @@ void LazyObjFile::parse() { if (coffSym.isUndefined() || !coffSym.isExternal() || coffSym.isWeakExternal()) continue; - StringRef name; - coffObj->getSymbolName(coffSym, name); + StringRef name = check(coffObj->getSymbolName(coffSym)); if (coffSym.isAbsolute() && ignoredSymbolName(name)) continue; symtab->addLazyObject(this, name); @@ -198,11 +198,11 @@ void ObjFile::parse() { initializeDependencies(); } -const coff_section* ObjFile::getSection(uint32_t i) { - const coff_section *sec; - if (auto ec = coffObj->getSection(i, sec)) - fatal("getSection failed: #" + Twine(i) + ": " + ec.message()); - return sec; +const coff_section *ObjFile::getSection(uint32_t i) { + auto sec = coffObj->getSection(i); + if (!sec) + fatal("getSection failed: #" + Twine(i) + ": " + toString(sec.takeError())); + return *sec; } // We set SectionChunk pointers in the SparseChunks vector to this value @@ -215,7 +215,6 @@ static SectionChunk *const pendingComdat = reinterpret_cast(1); void ObjFile::initializeChunks() { uint32_t numSections = coffObj->getNumberOfSections(); - chunks.reserve(numSections); sparseChunks.resize(numSections + 1); for (uint32_t i = 1; i < numSections + 1; ++i) { const coff_section *sec = getSection(i); @@ -279,7 +278,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, else if (name == ".gljmp$y") guardLJmpChunks.push_back(c); else if (name == ".sxdata") - sXDataChunks.push_back(c); + sxDataChunks.push_back(c); else if (config->tailMerge && sec->NumberOfRelocations == 0 && name == ".rdata" && leaderName.startswith("??_C@")) // COFF sections that look like string literal sections (i.e. no @@ -310,9 +309,9 @@ void ObjFile::readAssociativeDefinition(COFFSymbolRef sym, int32_t sectionNumber = sym.getSectionNumber(); auto diag = [&]() { - StringRef name, parentName; - coffObj->getSymbolName(sym, name); + StringRef name = check(coffObj->getSymbolName(sym)); + StringRef parentName; const coff_section *parentSec = getSection(parentIndex); if (Expected e = coffObj->getSectionName(parentSec)) parentName = *e; @@ -349,13 +348,13 @@ void ObjFile::recordPrevailingSymbolForMingw( // of the section chunk we actually include instead of discarding it, // add the symbol to a map to allow using it for implicitly // associating .[px]data$ sections to it. + // Use the suffix from the .text$ instead of the leader symbol + // name, for cases where the names differ (i386 mangling/decorations, + // cases where the leader is a weak symbol named .weak.func.default*). int32_t sectionNumber = sym.getSectionNumber(); SectionChunk *sc = sparseChunks[sectionNumber]; if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { - StringRef name; - coffObj->getSymbolName(sym, name); - if (getMachineType() == I386) - name.consume_front("_"); + StringRef name = sc->getSectionName().split('$').second; prevailingSectionMap[name] = sectionNumber; } } @@ -363,8 +362,7 @@ void ObjFile::recordPrevailingSymbolForMingw( void ObjFile::maybeAssociateSEHForMingw( COFFSymbolRef sym, const coff_aux_section_definition *def, const DenseMap &prevailingSectionMap) { - StringRef name; - coffObj->getSymbolName(sym, name); + StringRef name = check(coffObj->getSymbolName(sym)); if (name.consume_front(".pdata$") || name.consume_front(".xdata$") || name.consume_front(".eh_frame$")) { // For MinGW, treat .[px]data$ and .eh_frame$ as implicitly @@ -378,8 +376,7 @@ void ObjFile::maybeAssociateSEHForMingw( Symbol *ObjFile::createRegular(COFFSymbolRef sym) { SectionChunk *sc = sparseChunks[sym.getSectionNumber()]; if (sym.isExternal()) { - StringRef name; - coffObj->getSymbolName(sym, name); + StringRef name = check(coffObj->getSymbolName(sym)); if (sc) return symtab->addRegular(this, name, sym.getGeneric(), sc, sym.getValue()); @@ -447,8 +444,7 @@ void ObjFile::initializeSymbols() { maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); } if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { - StringRef name; - coffObj->getSymbolName(sym, name); + StringRef name = check(coffObj->getSymbolName(sym)); log("comdat section " + name + " without leader and unassociated, discarding"); continue; @@ -461,11 +457,13 @@ void ObjFile::initializeSymbols() { uint32_t idx = kv.second; checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); } + + // Free the memory used by sparseChunks now that symbol loading is finished. + decltype(sparseChunks)().swap(sparseChunks); } Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { - StringRef name; - coffObj->getSymbolName(sym, name); + StringRef name = check(coffObj->getSymbolName(sym)); return symtab->addUndefined(name, this, sym.isWeakExternal()); } @@ -561,8 +559,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, case IMAGE_COMDAT_SELECT_LARGEST: if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) { // Replace the existing comdat symbol with the new one. - StringRef name; - coffObj->getSymbolName(sym, name); + StringRef name = check(coffObj->getSymbolName(sym)); // FIXME: This is incorrect: With /opt:noref, the previous sections // make it into the final executable as well. Correct handling would // be to undo reading of the whole old section that's being replaced, @@ -586,11 +583,7 @@ Optional ObjFile::createDefined( std::vector &comdatDefs, bool &prevailing) { prevailing = false; - auto getName = [&]() { - StringRef s; - coffObj->getSymbolName(sym, s); - return s; - }; + auto getName = [&]() { return check(coffObj->getSymbolName(sym)); }; if (sym.isCommon()) { auto *c = make(sym); @@ -767,10 +760,11 @@ void ObjFile::initializeDependencies() { if (data.empty()) return; + // Get the first type record. It will indicate if this object uses a type + // server (/Zi) or a PCH file (/Yu). CVTypeArray types; BinaryStreamReader reader(data, support::little); cantFail(reader.readArray(types, reader.getLength())); - CVTypeArray::Iterator firstType = types.begin(); if (firstType == types.end()) return; @@ -778,28 +772,120 @@ void ObjFile::initializeDependencies() { // Remember the .debug$T or .debug$P section. debugTypes = data; + // This object file is a PCH file that others will depend on. if (isPCH) { debugTypesObj = makePrecompSource(this); return; } + // This object file was compiled with /Zi. Enqueue the PDB dependency. if (firstType->kind() == LF_TYPESERVER2) { TypeServer2Record ts = cantFail( TypeDeserializer::deserializeAs(firstType->data())); - debugTypesObj = makeUseTypeServerSource(this, &ts); + debugTypesObj = makeUseTypeServerSource(this, ts); + PDBInputFile::enqueue(ts.getName(), this); return; } + // This object was compiled with /Yu. It uses types from another object file + // with a matching signature. if (firstType->kind() == LF_PRECOMP) { PrecompRecord precomp = cantFail( TypeDeserializer::deserializeAs(firstType->data())); - debugTypesObj = makeUsePrecompSource(this, &precomp); + debugTypesObj = makeUsePrecompSource(this, precomp); return; } + // This is a plain old object file. debugTypesObj = makeTpiSource(this); } +// Make a PDB path assuming the PDB is in the same folder as the OBJ +static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) { + StringRef localPath = + !file->parentName.empty() ? file->parentName : file->getName(); + SmallString<128> path = sys::path::parent_path(localPath); + + // Currently, type server PDBs are only created by MSVC cl, which only runs + // on Windows, so we can assume type server paths are Windows style. + sys::path::append(path, + sys::path::filename(tSPath, sys::path::Style::windows)); + return std::string(path.str()); +} + +// The casing of the PDB path stamped in the OBJ can differ from the actual path +// on disk. With this, we ensure to always use lowercase as a key for the +// PDBInputFile::instances map, at least on Windows. +static std::string normalizePdbPath(StringRef path) { +#if defined(_WIN32) + return path.lower(); +#else // LINUX + return std::string(path); +#endif +} + +// If existing, return the actual PDB path on disk. +static Optional findPdbPath(StringRef pdbPath, + ObjFile *dependentFile) { + // Ensure the file exists before anything else. In some cases, if the path + // points to a removable device, Driver::enqueuePath() would fail with an + // error (EAGAIN, "resource unavailable try again") which we want to skip + // silently. + if (llvm::sys::fs::exists(pdbPath)) + return normalizePdbPath(pdbPath); + std::string ret = getPdbBaseName(dependentFile, pdbPath); + if (llvm::sys::fs::exists(ret)) + return normalizePdbPath(ret); + return None; +} + +PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {} + +PDBInputFile::~PDBInputFile() = default; + +PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path, + ObjFile *fromFile) { + auto p = findPdbPath(path.str(), fromFile); + if (!p) + return nullptr; + auto it = PDBInputFile::instances.find(*p); + if (it != PDBInputFile::instances.end()) + return it->second; + return nullptr; +} + +void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) { + auto p = findPdbPath(path.str(), fromFile); + if (!p) + return; + auto it = PDBInputFile::instances.emplace(*p, nullptr); + if (!it.second) + return; // already scheduled for load + driver->enqueuePDB(*p); +} + +void PDBInputFile::parse() { + PDBInputFile::instances[mb.getBufferIdentifier().str()] = this; + + std::unique_ptr thisSession; + loadErr.emplace(pdb::NativeSession::createFromPdb( + MemoryBuffer::getMemBuffer(mb, false), thisSession)); + if (*loadErr) + return; // fail silently at this point - the error will be handled later, + // when merging the debug type stream + + session.reset(static_cast(thisSession.release())); + + pdb::PDBFile &pdbFile = session->getPDBFile(); + auto expectedInfo = pdbFile.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!expectedInfo) { + loadErr.emplace(expectedInfo.takeError()); + return; + } + debugTypesObj = makeTypeServerSource(this); +} + // Used only for DWARF debug info, which is not common (except in MinGW // environments). This returns an optional pair of file name and line // number for where the variable was defined. @@ -831,7 +917,7 @@ Optional ObjFile::getDILineInfo(uint32_t offset, return dwarf->getDILineInfo(offset, sectionIndex); } -StringRef ltrim1(StringRef s, const char *chars) { +static StringRef ltrim1(StringRef s, const char *chars) { if (!s.empty() && strchr(chars, s[0])) return s.substr(1); return s; @@ -849,7 +935,7 @@ void ImportFile::parse() { StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); StringRef impName = saver.save("__imp_" + name); const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; - dllName = StringRef(nameStart); + dllName = std::string(StringRef(nameStart)); StringRef extName; switch (hdr->getNameType()) { case IMPORT_ORDINAL: @@ -907,8 +993,9 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, // filename unique. MemoryBufferRef mbref( mb.getBuffer(), - saver.save(archiveName + path + - (archiveName.empty() ? "" : utostr(offsetInArchive)))); + saver.save(archiveName.empty() ? path + : archiveName + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); } @@ -932,7 +1019,7 @@ void BitcodeFile::parse() { } else if (objSym.isWeak() && objSym.isIndirect()) { // Weak external. sym = symtab->addUndefined(symName, this, true); - std::string fallback = objSym.getCOFFWeakExternalFallback(); + std::string fallback = std::string(objSym.getCOFFWeakExternalFallback()); Symbol *alias = symtab->addUndefined(saver.save(fallback)); checkAndSetWeakAlias(symtab, this, sym, alias); } else if (comdatIndex != -1) { @@ -967,14 +1054,11 @@ MachineTypes BitcodeFile::getMachineType() { } } -std::string replaceThinLTOSuffix(StringRef path) { +std::string lld::coff::replaceThinLTOSuffix(StringRef path) { StringRef suffix = config->thinLTOObjectSuffixReplace.first; StringRef repl = config->thinLTOObjectSuffixReplace.second; if (path.consume_back(suffix)) return (path + repl).str(); - return path; + return std::string(path); } - -} // namespace coff -} // namespace lld diff --git a/gnu/llvm/lld/COFF/InputFiles.h b/gnu/llvm/lld/COFF/InputFiles.h index 805d9121d8b..50323f596e2 100644 --- a/gnu/llvm/lld/COFF/InputFiles.h +++ b/gnu/llvm/lld/COFF/InputFiles.h @@ -26,6 +26,7 @@ namespace llvm { struct DILineInfo; namespace pdb { class DbiModuleDescriptorBuilder; +class NativeSession; } namespace lto { class InputFile; @@ -64,6 +65,7 @@ public: ArchiveKind, ObjectKind, LazyObjectKind, + PDBKind, ImportKind, BitcodeKind }; @@ -140,7 +142,7 @@ public: MachineTypes getMachineType() override; ArrayRef getChunks() { return chunks; } ArrayRef getDebugChunks() { return debugChunks; } - ArrayRef getSXDataChunks() { return sXDataChunks; } + ArrayRef getSXDataChunks() { return sxDataChunks; } ArrayRef getGuardFidChunks() { return guardFidChunks; } ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } ArrayRef getSymbols() { return symbols; } @@ -276,29 +278,55 @@ private: // Chunks containing symbol table indices of exception handlers. Only used for // 32-bit x86. - std::vector sXDataChunks; + std::vector sxDataChunks; // Chunks containing symbol table indices of address taken symbols and longjmp // targets. These are not linked into the final binary when /guard:cf is set. std::vector guardFidChunks; std::vector guardLJmpChunks; - // This vector contains the same chunks as Chunks, but they are - // indexed such that you can get a SectionChunk by section index. - // Nonexistent section indices are filled with null pointers. - // (Because section number is 1-based, the first slot is always a - // null pointer.) - std::vector sparseChunks; - // This vector contains a list of all symbols defined or referenced by this // file. They are indexed such that you can get a Symbol by symbol // index. Nonexistent indices (which are occupied by auxiliary // symbols in the real symbol table) are filled with null pointers. std::vector symbols; + // This vector contains the same chunks as Chunks, but they are + // indexed such that you can get a SectionChunk by section index. + // Nonexistent section indices are filled with null pointers. + // (Because section number is 1-based, the first slot is always a + // null pointer.) This vector is only valid during initialization. + std::vector sparseChunks; + DWARFCache *dwarf = nullptr; }; +// This is a PDB type server dependency, that is not a input file per se, but +// needs to be treated like one. Such files are discovered from the debug type +// stream. +class PDBInputFile : public InputFile { +public: + explicit PDBInputFile(MemoryBufferRef m); + ~PDBInputFile(); + static bool classof(const InputFile *f) { return f->kind() == PDBKind; } + void parse() override; + + static void enqueue(StringRef path, ObjFile *fromFile); + + static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile); + + static std::map instances; + + // Record possible errors while opening the PDB file + llvm::Optional loadErr; + + // This is the actual interface to the PDB (if it was opened successfully) + std::unique_ptr session; + + // If the PDB has a .debug$T stream, this tells how it will be handled. + TpiSource *debugTypesObj = nullptr; +}; + // This type represents import library members that contain DLL names // and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7 // for details about the format. diff --git a/gnu/llvm/lld/COFF/LLDMapFile.cpp b/gnu/llvm/lld/COFF/LLDMapFile.cpp new file mode 100644 index 00000000000..79df33a3535 --- /dev/null +++ b/gnu/llvm/lld/COFF/LLDMapFile.cpp @@ -0,0 +1,123 @@ +//===- LLDMapFile.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the /lldmap option. It shows lists in order and +// hierarchically the output sections, input sections, input files and +// symbol: +// +// Address Size Align Out File Symbol +// 00201000 00000015 4 .text +// 00201000 0000000e 4 test.o:(.text) +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) +// +//===----------------------------------------------------------------------===// + +#include "LLDMapFile.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Writer.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; +using namespace lld; +using namespace lld::coff; + +using SymbolMapTy = + DenseMap>; + +static constexpr char indent8[] = " "; // 8 spaces +static constexpr char indent16[] = " "; // 16 spaces + +// Print out the first three columns of a line. +static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, + uint64_t align) { + os << format("%08llx %08llx %5lld ", addr, size, align); +} + +// Returns a list of all symbols that we want to print out. +static std::vector getSymbols() { + std::vector v; + for (ObjFile *file : ObjFile::instances) + for (Symbol *b : file->getSymbols()) + if (auto *sym = dyn_cast_or_null(b)) + if (sym && !sym->getCOFFSymbol().isSectionDefinition()) + v.push_back(sym); + return v; +} + +// Returns a map from sections to their symbols. +static SymbolMapTy getSectionSyms(ArrayRef syms) { + SymbolMapTy ret; + for (DefinedRegular *s : syms) + ret[s->getChunk()].push_back(s); + + // Sort symbols by address. + for (auto &it : ret) { + SmallVectorImpl &v = it.second; + std::stable_sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) { + return a->getRVA() < b->getRVA(); + }); + } + return ret; +} + +// Construct a map from symbols to their stringified representations. +static DenseMap +getSymbolStrings(ArrayRef syms) { + std::vector str(syms.size()); + parallelForEachN((size_t)0, syms.size(), [&](size_t i) { + raw_string_ostream os(str[i]); + writeHeader(os, syms[i]->getRVA(), 0, 0); + os << indent16 << toString(*syms[i]); + }); + + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = std::move(str[i]); + return ret; +} + +void lld::coff::writeLLDMapFile(ArrayRef outputSections) { + if (config->lldmapFile.empty()) + return; + + std::error_code ec; + raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None); + if (ec) + fatal("cannot open " + config->lldmapFile + ": " + ec.message()); + + // Collect symbol info that we want to print out. + std::vector syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap symStr = getSymbolStrings(syms); + + // Print out the header line. + os << "Address Size Align Out In Symbol\n"; + + // Print out file contents. + for (OutputSection *sec : outputSections) { + writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); + os << sec->name << '\n'; + + for (Chunk *c : sec->chunks) { + auto *sc = dyn_cast(c); + if (!sc) + continue; + + writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment()); + os << indent8 << sc->file->getName() << ":(" << sc->getSectionName() + << ")\n"; + for (DefinedRegular *sym : sectionSyms[sc]) + os << symStr[sym] << '\n'; + } + } +} diff --git a/gnu/llvm/lld/COFF/LLDMapFile.h b/gnu/llvm/lld/COFF/LLDMapFile.h new file mode 100644 index 00000000000..b731293a862 --- /dev/null +++ b/gnu/llvm/lld/COFF/LLDMapFile.h @@ -0,0 +1,21 @@ +//===- LLDMapFile.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_LLDMAPFILE_H +#define LLD_COFF_LLDMAPFILE_H + +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { +class OutputSection; +void writeLLDMapFile(llvm::ArrayRef outputSections); +} +} + +#endif diff --git a/gnu/llvm/lld/COFF/LTO.cpp b/gnu/llvm/lld/COFF/LTO.cpp index 1c21236dce2..bb44819e60f 100644 --- a/gnu/llvm/lld/COFF/LTO.cpp +++ b/gnu/llvm/lld/COFF/LTO.cpp @@ -38,9 +38,8 @@ using namespace llvm; using namespace llvm::object; - -namespace lld { -namespace coff { +using namespace lld; +using namespace lld::coff; // Creates an empty file to and returns a raw_fd_ostream to write to it. static std::unique_ptr openFile(StringRef file) { @@ -55,9 +54,9 @@ static std::unique_ptr openFile(StringRef file) { } static std::string getThinLTOOutputFile(StringRef path) { - return lto::getThinLTOOutputFile(path, - config->thinLTOPrefixReplace.first, - config->thinLTOPrefixReplace.second); + return lto::getThinLTOOutputFile( + std::string(path), std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second)); } static lto::Config createConfig() { @@ -82,6 +81,7 @@ static lto::Config createConfig() { c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); c.CGOptLevel = args::getCGOptLevel(config->ltoo); + c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); if (config->saveTemps) checkError(c.addSaveTemps(std::string(config->outputFile) + ".", @@ -99,10 +99,12 @@ BitcodeCompiler::BitcodeCompiler() { if (config->thinLTOIndexOnly) { auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; backend = lto::createWriteIndexesThinBackend( - config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second, + std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second), config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); - } else if (config->thinLTOJobs != 0) { - backend = lto::createInProcessThinBackend(config->thinLTOJobs); + } else { + backend = lto::createInProcessThinBackend( + llvm::heavyweight_hardware_concurrency(config->thinLTOJobs)); } ltoObj = std::make_unique(createConfig(), backend, @@ -143,7 +145,7 @@ void BitcodeCompiler::add(BitcodeFile &f) { // Merge all the bitcode files we have seen, codegen the result // and return the resulting objects. -std::vector BitcodeCompiler::compile() { +std::vector BitcodeCompiler::compile() { unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); @@ -187,25 +189,32 @@ std::vector BitcodeCompiler::compile() { if (!config->ltoCache.empty()) pruneCache(config->ltoCache, config->ltoCachePolicy); - std::vector ret; + std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) { - if (buf[i].empty()) + // Assign unique names to LTO objects. This ensures they have unique names + // in the PDB if one is produced. The names should look like: + // - foo.exe.lto.obj + // - foo.exe.lto.1.obj + // - ... + StringRef ltoObjName = + saver.save(Twine(config->outputFile) + ".lto" + + (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); + + // Get the native object contents either from the cache or from memory. Do + // not use the cached MemoryBuffer directly, or the PDB will not be + // deterministic. + StringRef objBuf; + if (files[i]) + objBuf = files[i]->getBuffer(); + else + objBuf = buf[i]; + if (objBuf.empty()) continue; - if (config->saveTemps) { - if (i == 0) - saveBuffer(buf[i], config->outputFile + ".lto.obj"); - else - saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj"); - } - ret.emplace_back(buf[i].data(), buf[i].size()); - } - for (std::unique_ptr &file : files) - if (file) - ret.push_back(file->getBuffer()); + if (config->saveTemps) + saveBuffer(buf[i], ltoObjName); + ret.push_back(make(MemoryBufferRef(objBuf, ltoObjName))); + } return ret; } - -} // namespace coff -} // namespace lld diff --git a/gnu/llvm/lld/COFF/LTO.h b/gnu/llvm/lld/COFF/LTO.h index 2a0cfa061c9..a2b321df790 100644 --- a/gnu/llvm/lld/COFF/LTO.h +++ b/gnu/llvm/lld/COFF/LTO.h @@ -45,7 +45,7 @@ public: ~BitcodeCompiler(); void add(BitcodeFile &f); - std::vector compile(); + std::vector compile(); private: std::unique_ptr ltoObj; diff --git a/gnu/llvm/lld/COFF/MapFile.cpp b/gnu/llvm/lld/COFF/MapFile.cpp index 0fea60aab99..41e169ef56e 100644 --- a/gnu/llvm/lld/COFF/MapFile.cpp +++ b/gnu/llvm/lld/COFF/MapFile.cpp @@ -6,16 +6,25 @@ // //===----------------------------------------------------------------------===// // -// This file implements the /lldmap option. It shows lists in order and -// hierarchically the output sections, input sections, input files and -// symbol: +// This file implements the /map option in the same format as link.exe +// (based on observations) // -// Address Size Align Out File Symbol -// 00201000 00000015 4 .text -// 00201000 0000000e 4 test.o:(.text) -// 0020100e 00000000 0 local -// 00201005 00000000 0 f(int) +// Header (program name, timestamp info, preferred load address) // +// Section list (Start = Section index:Base address): +// Start Length Name Class +// 0001:00001000 00000015H .text CODE +// +// Symbols list: +// Address Publics by Value Rva + Base Lib:Object +// 0001:00001000 main 0000000140001000 main.obj +// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj +// +// entry point at 0001:00000360 +// +// Static symbols +// +// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj //===----------------------------------------------------------------------===// #include "MapFile.h" @@ -23,71 +32,176 @@ #include "Symbols.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" -#include "lld/Common/Threads.h" +#include "lld/Common/Timer.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; +using namespace lld; +using namespace lld::coff; -namespace lld { -namespace coff { +static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root()); +static Timer symbolGatherTimer("Gather symbols", totalMapTimer); +static Timer symbolStringsTimer("Build symbol strings", totalMapTimer); +static Timer writeTimer("Write to file", totalMapTimer); -using SymbolMapTy = - DenseMap>; +// Print out the first two columns of a line. +static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) { + os << format(" %04x:%08llx", sec, addr); +} -static constexpr char indent8[] = " "; // 8 spaces -static constexpr char indent16[] = " "; // 16 spaces +// Write the time stamp with the format used by link.exe +// It seems identical to strftime with "%c" on msvc build, but we need a +// locale-agnostic version. +static void writeFormattedTimestamp(raw_ostream &os, time_t tds) { + constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec"}; + tm *time = localtime(&tds); + os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday], + months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min, + time->tm_sec, time->tm_year + 1900); +} -// Print out the first three columns of a line. -static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, - uint64_t align) { - os << format("%08llx %08llx %5lld ", addr, size, align); +static void sortUniqueSymbols(std::vector &syms) { + // Build helper vector + using SortEntry = std::pair; + std::vector v; + v.resize(syms.size()); + for (size_t i = 0, e = syms.size(); i < e; ++i) + v[i] = SortEntry(syms[i], i); + + // Remove duplicate symbol pointers + parallelSort(v, std::less()); + auto end = std::unique(v.begin(), v.end(), + [](const SortEntry &a, const SortEntry &b) { + return a.first == b.first; + }); + v.erase(end, v.end()); + + // Sort by RVA then original order + parallelSort(v, [](const SortEntry &a, const SortEntry &b) { + // Add config->imageBase to avoid comparing "negative" RVAs. + // This can happen with symbols of Absolute kind + uint64_t rvaa = config->imageBase + a.first->getRVA(); + uint64_t rvab = config->imageBase + b.first->getRVA(); + return rvaa < rvab || (rvaa == rvab && a.second < b.second); + }); + + syms.resize(v.size()); + for (size_t i = 0, e = v.size(); i < e; ++i) + syms[i] = v[i].first; } -// Returns a list of all symbols that we want to print out. -static std::vector getSymbols() { - std::vector v; +// Returns the lists of all symbols that we want to print out. +static void getSymbols(std::vector &syms, + std::vector &staticSyms) { + for (ObjFile *file : ObjFile::instances) - for (Symbol *b : file->getSymbols()) - if (auto *sym = dyn_cast_or_null(b)) - if (sym && !sym->getCOFFSymbol().isSectionDefinition()) - v.push_back(sym); - return v; -} + for (Symbol *b : file->getSymbols()) { + if (!b || !b->isLive()) + continue; + if (auto *sym = dyn_cast(b)) { + COFFSymbolRef symRef = sym->getCOFFSymbol(); + if (!symRef.isSectionDefinition() && + symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) { + if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) + staticSyms.push_back(sym); + else + syms.push_back(sym); + } + } else if (auto *sym = dyn_cast(b)) { + syms.push_back(sym); + } + } + + for (ImportFile *file : ImportFile::instances) { + if (!file->live) + continue; + + if (!file->thunkSym) + continue; + + if (!file->thunkLive) + continue; + + if (auto *thunkSym = dyn_cast(file->thunkSym)) + syms.push_back(thunkSym); -// Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef syms) { - SymbolMapTy ret; - for (DefinedRegular *s : syms) - ret[s->getChunk()].push_back(s); - - // Sort symbols by address. - for (auto &it : ret) { - SmallVectorImpl &v = it.second; - std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) { - return a->getRVA() < b->getRVA(); - }); + if (auto *impSym = dyn_cast_or_null(file->impSym)) + syms.push_back(impSym); } - return ret; + + sortUniqueSymbols(syms); + sortUniqueSymbols(staticSyms); } // Construct a map from symbols to their stringified representations. -static DenseMap -getSymbolStrings(ArrayRef syms) { +static DenseMap +getSymbolStrings(ArrayRef syms) { std::vector str(syms.size()); parallelForEachN((size_t)0, syms.size(), [&](size_t i) { raw_string_ostream os(str[i]); - writeHeader(os, syms[i]->getRVA(), 0, 0); - os << indent16 << toString(*syms[i]); + Defined *sym = syms[i]; + + uint16_t sectionIdx = 0; + uint64_t address = 0; + SmallString<128> fileDescr; + + if (auto *absSym = dyn_cast(sym)) { + address = absSym->getVA(); + fileDescr = ""; + } else if (isa(sym)) { + fileDescr = ""; + } else if (isa(sym)) { + fileDescr = ""; + } else if (Chunk *chunk = sym->getChunk()) { + address = sym->getRVA(); + if (OutputSection *sec = chunk->getOutputSection()) + address -= sec->header.VirtualAddress; + + sectionIdx = chunk->getOutputSectionIdx(); + + InputFile *file; + if (auto *impSym = dyn_cast(sym)) + file = impSym->file; + else if (auto *thunkSym = dyn_cast(sym)) + file = thunkSym->wrappedSym->file; + else + file = sym->getFile(); + + if (file) { + if (!file->parentName.empty()) { + fileDescr = sys::path::filename(file->parentName); + sys::path::replace_extension(fileDescr, ""); + fileDescr += ":"; + } + fileDescr += sys::path::filename(file->getName()); + } + } + writeHeader(os, sectionIdx, address); + os << " "; + os << left_justify(sym->getName(), 26); + os << " "; + os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16); + if (!fileDescr.empty()) { + os << " "; // FIXME : Handle "f" and "i" flags sometimes generated + // by link.exe in those spaces + os << fileDescr; + } }); - DenseMap ret; + DenseMap ret; for (size_t i = 0, e = syms.size(); i < e; ++i) ret[syms[i]] = std::move(str[i]); return ret; } -void writeMapFile(ArrayRef outputSections) { +void lld::coff::writeMapFile(ArrayRef outputSections) { if (config->mapFile.empty()) return; @@ -96,32 +210,113 @@ void writeMapFile(ArrayRef outputSections) { if (ec) fatal("cannot open " + config->mapFile + ": " + ec.message()); + ScopedTimer t1(totalMapTimer); + // Collect symbol info that we want to print out. - std::vector syms = getSymbols(); - SymbolMapTy sectionSyms = getSectionSyms(syms); - DenseMap symStr = getSymbolStrings(syms); + ScopedTimer t2(symbolGatherTimer); + std::vector syms; + std::vector staticSyms; + getSymbols(syms, staticSyms); + t2.stop(); - // Print out the header line. - os << "Address Size Align Out In Symbol\n"; + ScopedTimer t3(symbolStringsTimer); + DenseMap symStr = getSymbolStrings(syms); + DenseMap staticSymStr = getSymbolStrings(staticSyms); + t3.stop(); - // Print out file contents. - for (OutputSection *sec : outputSections) { - writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); - os << sec->name << '\n'; + ScopedTimer t4(writeTimer); + SmallString<128> AppName = sys::path::filename(config->outputFile); + sys::path::replace_extension(AppName, ""); + + // Print out the file header + os << " " << AppName << "\n"; + os << "\n"; + os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " ("; + if (config->repro) { + os << "Repro mode"; + } else { + writeFormattedTimestamp(os, config->timestamp); + } + os << ")\n"; + + os << "\n"; + os << " Preferred load address is " + << format_hex_no_prefix(config->imageBase, 16) << "\n"; + os << "\n"; + + // Print out section table. + os << " Start Length Name Class\n"; + + for (OutputSection *sec : outputSections) { + // Merge display of chunks with same sectionName + std::vector> ChunkRanges; for (Chunk *c : sec->chunks) { auto *sc = dyn_cast(c); if (!sc) continue; - writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment()); - os << indent8 << sc->file->getName() << ":(" << sc->getSectionName() - << ")\n"; - for (DefinedRegular *sym : sectionSyms[sc]) - os << symStr[sym] << '\n'; + if (ChunkRanges.empty() || + c->getSectionName() != ChunkRanges.back().first->getSectionName()) { + ChunkRanges.emplace_back(sc, sc); + } else { + ChunkRanges.back().second = sc; + } + } + + const bool isCodeSection = + (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) && + (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) && + (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE); + StringRef SectionClass = (isCodeSection ? "CODE" : "DATA"); + + for (auto &cr : ChunkRanges) { + size_t size = + cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA(); + + auto address = cr.first->getRVA() - sec->header.VirtualAddress; + writeHeader(os, sec->sectionIndex, address); + os << " " << format_hex_no_prefix(size, 8) << "H"; + os << " " << left_justify(cr.first->getSectionName(), 23); + os << " " << SectionClass; + os << '\n'; } } -} -} // namespace coff -} // namespace lld + // Print out the symbols table (without static symbols) + os << "\n"; + os << " Address Publics by Value Rva+Base" + " Lib:Object\n"; + os << "\n"; + for (Defined *sym : syms) + os << symStr[sym] << '\n'; + + // Print out the entry point. + os << "\n"; + + uint16_t entrySecIndex = 0; + uint64_t entryAddress = 0; + + if (!config->noEntry) { + Defined *entry = dyn_cast_or_null(config->entry); + if (entry) { + Chunk *chunk = entry->getChunk(); + entrySecIndex = chunk->getOutputSectionIdx(); + entryAddress = + entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress; + } + } + os << " entry point at "; + os << format("%04x:%08llx", entrySecIndex, entryAddress); + os << "\n"; + + // Print out the static symbols + os << "\n"; + os << " Static symbols\n"; + os << "\n"; + for (Defined *sym : staticSyms) + os << staticSymStr[sym] << '\n'; + + t4.stop(); + t1.stop(); +} diff --git a/gnu/llvm/lld/COFF/MinGW.cpp b/gnu/llvm/lld/COFF/MinGW.cpp index 270cdaab4d9..e24cdca6ee3 100644 --- a/gnu/llvm/lld/COFF/MinGW.cpp +++ b/gnu/llvm/lld/COFF/MinGW.cpp @@ -15,9 +15,8 @@ using namespace llvm; using namespace llvm::COFF; - -namespace lld { -namespace coff { +using namespace lld; +using namespace lld::coff; AutoExporter::AutoExporter() { excludeLibs = { @@ -35,6 +34,11 @@ AutoExporter::AutoExporter() { "libclang_rt.builtins-arm", "libclang_rt.builtins-i386", "libclang_rt.builtins-x86_64", + "libclang_rt.profile", + "libclang_rt.profile-aarch64", + "libclang_rt.profile-arm", + "libclang_rt.profile-i386", + "libclang_rt.profile-x86_64", "libc++", "libc++abi", "libunwind", @@ -58,6 +62,10 @@ AutoExporter::AutoExporter() { "__builtin_", // Artificial symbols such as .refptr ".", + // profile generate symbols + "__profc_", + "__profd_", + "__profvp_", }; excludeSymbolSuffixes = { @@ -147,7 +155,7 @@ bool AutoExporter::shouldExport(Defined *sym) const { return !excludeObjects.count(fileName); } -void writeDefFile(StringRef name) { +void lld::coff::writeDefFile(StringRef name) { std::error_code ec; raw_fd_ostream os(name, ec, sys::fs::OF_None); if (ec) @@ -165,6 +173,3 @@ void writeDefFile(StringRef name) { os << "\n"; } } - -} // namespace coff -} // namespace lld diff --git a/gnu/llvm/lld/COFF/Options.td b/gnu/llvm/lld/COFF/Options.td index 468e4da7d05..087d53b5d2d 100644 --- a/gnu/llvm/lld/COFF/Options.td +++ b/gnu/llvm/lld/COFF/Options.td @@ -16,6 +16,13 @@ multiclass B { def _no : F, HelpText; } +// Same as B<> above, but without help texts, for private undocumented +// options. +multiclass B_priv { + def "" : F; + def _no : F; +} + def align : P<"align", "Section alignment">; def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; @@ -64,7 +71,11 @@ 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">; +def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">, + MetaVarName<"=">, + HelpText<"Embed the contents of in the PDB as named stream ">; def section : P<"section", "Specify section attributes">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; @@ -147,6 +158,8 @@ defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)", defm appcontainer : B<"appcontainer", "Image can only be run in an app container", "Image can run outside an app container (default)">; +defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack", + "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">; defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)", "Disable ASLR (default when /fixed)">; defm fixed : B<"fixed", "Disable base relocations", @@ -178,6 +191,8 @@ def help : F<"help">; def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; // LLD extensions +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">; def exclude_all_symbols : F<"exclude-all-symbols">; @@ -189,6 +204,7 @@ def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">, HelpText<"Add symbol as undefined, but allow it to remain undefined">; def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; +def noseh : F<"noseh">; def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def pdb_source_path : P<"pdbsourcepath", "Base path used to make relative source file path absolute in PDB">; @@ -216,13 +232,15 @@ def lto_obj_path : P< "output native object for merged LTO unit to this path">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; -defm threads: B<"threads", - "Run the linker multi-threaded (default)", - "Do not run the linker multi-threaded">; +def threads + : P<"threads", "Number of threads. '1' disables multi-threading. By " + "default all available hardware threads are used">; // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">; +def map : F<"map">; +def map_file : Joined<["/", "-", "/?", "-?"], "map:">; def show_timing : F<"time">; def summary : F<"summary">; diff --git a/gnu/llvm/lld/COFF/PDB.cpp b/gnu/llvm/lld/COFF/PDB.cpp index f68c60a1325..49d04add5be 100644 --- a/gnu/llvm/lld/COFF/PDB.cpp +++ b/gnu/llvm/lld/COFF/PDB.cpp @@ -16,7 +16,6 @@ #include "TypeMerger.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" -#include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" @@ -27,11 +26,7 @@ #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" -#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" -#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" -#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" -#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/DebugInfo/PDB/GenericError.h" @@ -63,12 +58,11 @@ using namespace llvm; using namespace llvm::codeview; +using namespace lld; +using namespace lld::coff; using llvm::object::coff_section; -namespace lld { -namespace coff { - static ExitOnError exitOnErr; static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); @@ -76,7 +70,7 @@ static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer); static Timer typeMergingTimer("Type Merging", addObjectsTimer); static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer); -static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer); +static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer); static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer); static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer); @@ -88,7 +82,7 @@ class PDBLinker { public: PDBLinker(SymbolTable *symtab) - : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) { + : symtab(symtab), builder(bAlloc), tMerger(bAlloc) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. @@ -101,53 +95,26 @@ public: /// Add natvis files specified on the command line. void addNatvisFiles(); + /// Add named streams specified on the command line. + void addNamedStreams(); + /// Link CodeView from each object file in the symbol table into the PDB. void addObjectsToPDB(); + /// Add every live, defined public symbol to the PDB. + void addPublicsToPDB(); + /// Link info for each import file in the symbol table into the PDB. void addImportFilesToPDB(ArrayRef outputSections); /// Link CodeView from a single object file into the target (output) PDB. /// When a precompiled headers object is linked, its TPI map might be provided /// externally. - void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr); - - /// Produce a mapping from the type and item indices used in the object - /// file to those in the destination PDB. - /// - /// If the object file uses a type server PDB (compiled with /Zi), merge TPI - /// and IPI from the type server PDB and return a map for it. Each unique type - /// server PDB is merged at most once, so this may return an existing index - /// mapping. - /// - /// If the object does not use a type server PDB (compiled with /Z7), we merge - /// all the type and item records from the .debug$S stream and fill in the - /// caller-provided objectIndexMap. - Expected mergeDebugT(ObjFile *file, - CVIndexMap *objectIndexMap); - - /// Reads and makes available a PDB. - Expected maybeMergeTypeServerPDB(ObjFile *file); - - /// Merges a precompiled headers TPI map into the current TPI map. The - /// precompiled headers object will also be loaded and remapped in the - /// process. - Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap); - - /// Reads and makes available a precompiled headers object. - /// - /// This is a requirement for objects compiled with cl.exe /Yu. In that - /// case, the referenced object (which was compiled with /Yc) has to be loaded - /// first. This is mainly because the current object's TPI stream has external - /// references to the precompiled headers object. - /// - /// If the precompiled headers object was already loaded, this function will - /// simply return its (remapped) TPI map. - Expected aquirePrecompObj(ObjFile *file); - - /// Adds a precompiled headers object signature -> TPI mapping. - std::pair - registerPrecompiledHeaders(uint32_t signature); + void 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, @@ -164,8 +131,6 @@ public: void printStats(); private: - BumpPtrAllocator alloc; - SymbolTable *symtab; pdb::PDBFileBuilder builder; @@ -178,24 +143,10 @@ private: llvm::SmallString<128> nativePath; - std::vector sectionMap; - - /// Type index mappings of type server PDBs that we've loaded so far. - std::map typeServerIndexMappings; - - /// Type index mappings of precompiled objects type map that we've loaded so - /// far. - std::map precompTypeIndexMappings; - // For statistics uint64_t globalSymbols = 0; uint64_t moduleSymbols = 0; uint64_t publicSymbols = 0; - - // When showSummary is enabled, these are histograms of TPI and IPI records - // keyed by type index. - SmallVector tpiCounts; - SmallVector ipiCounts; }; class DebugSHandler { @@ -205,24 +156,20 @@ class DebugSHandler { ObjFile &file; /// The result of merging type indices. - const CVIndexMap &indexMap; + const CVIndexMap *indexMap; /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by /// index from other records in the .debug$S section. All of these strings /// need to be added to the global PDB string table, and all references to /// these strings need to have their indices re-written to refer to the /// global PDB string table. - DebugStringTableSubsectionRef cVStrTab; + DebugStringTableSubsectionRef cvStrTab; /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to /// by other records in the .debug$S section and need to be merged into the /// PDB. DebugChecksumsSubsectionRef checksums; - /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per - /// object file. - DebugInlineeLinesSubsectionRef inlineeLines; - /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of /// these and they need not appear in any specific order. However, they /// contain string table references which need to be re-written, so we @@ -238,14 +185,13 @@ class DebugSHandler { /// references. std::vector stringTableReferences; + void mergeInlineeLines(const DebugSubsectionRecord &inlineeLines); + public: - DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap) + DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap *indexMap) : linker(linker), file(file), indexMap(indexMap) {} - void handleDebugS(lld::coff::SectionChunk &debugS); - - std::shared_ptr - mergeInlineeLines(DebugChecksumsSubsection *newChecksums); + void handleDebugS(ArrayRef relocatedDebugContents); void finish(); }; @@ -290,42 +236,6 @@ static void pdbMakeAbsolute(SmallVectorImpl &fileName) { fileName = std::move(absoluteFileName); } -// A COFF .debug$H section is currently a clang extension. This function checks -// if a .debug$H section is in a format that we expect / understand, so that we -// can ignore any sections which are coincidentally also named .debug$H but do -// not contain a format we recognize. -static bool canUseDebugH(ArrayRef debugH) { - if (debugH.size() < sizeof(object::debug_h_header)) - return false; - auto *header = - reinterpret_cast(debugH.data()); - debugH = debugH.drop_front(sizeof(object::debug_h_header)); - return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && - header->Version == 0 && - header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && - (debugH.size() % 8 == 0); -} - -static Optional> getDebugH(ObjFile *file) { - SectionChunk *sec = - SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); - if (!sec) - return llvm::None; - ArrayRef contents = sec->getContents(); - if (!canUseDebugH(contents)) - return None; - return contents; -} - -static ArrayRef -getHashesFromDebugH(ArrayRef debugH) { - assert(canUseDebugH(debugH)); - - debugH = debugH.drop_front(sizeof(object::debug_h_header)); - uint32_t count = debugH.size() / sizeof(GloballyHashedType); - return {reinterpret_cast(debugH.data()), count}; -} - static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, TypeCollection &typeTable) { // Start the TPI or IPI stream header. @@ -340,281 +250,6 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, }); } -Expected -PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { - ScopedTimer t(typeMergingTimer); - - if (!file->debugTypesObj) - return *objectIndexMap; // no Types stream - - // Precompiled headers objects need to save the index map for further - // reference by other objects which use the precompiled headers. - if (file->debugTypesObj->kind == TpiSource::PCH) { - uint32_t pchSignature = file->pchSignature.getValueOr(0); - if (pchSignature == 0) - fatal("No signature found for the precompiled headers OBJ (" + - file->getName() + ")"); - - // When a precompiled headers object comes first on the command-line, we - // update the mapping here. Otherwise, if an object referencing the - // precompiled headers object comes first, the mapping is created in - // aquirePrecompObj(), thus we would skip this block. - if (!objectIndexMap->isPrecompiledTypeMap) { - auto r = registerPrecompiledHeaders(pchSignature); - if (r.second) - fatal( - "A precompiled headers OBJ with the same signature was already " - "provided! (" + - file->getName() + ")"); - - objectIndexMap = &r.first; - } - } - - if (file->debugTypesObj->kind == TpiSource::UsingPDB) { - // Look through type servers. If we've already seen this type server, - // don't merge any type information. - return maybeMergeTypeServerPDB(file); - } - - CVTypeArray types; - BinaryStreamReader reader(file->debugTypes, support::little); - cantFail(reader.readArray(types, reader.getLength())); - - if (file->debugTypesObj->kind == TpiSource::UsingPCH) { - // This object was compiled with /Yu, so process the corresponding - // precompiled headers object (/Yc) first. Some type indices in the current - // object are referencing data in the precompiled headers object, so we need - // both to be loaded. - Error e = mergeInPrecompHeaderObj(file, objectIndexMap); - if (e) - return std::move(e); - - // Drop LF_PRECOMP record from the input stream, as it has been replaced - // with the precompiled headers Type stream in the mergeInPrecompHeaderObj() - // call above. Note that we can't just call Types.drop_front(), as we - // explicitly want to rebase the stream. - CVTypeArray::Iterator firstType = types.begin(); - types.setUnderlyingStream( - types.getUnderlyingStream().drop_front(firstType->RecordData.size())); - } - - // Fill in the temporary, caller-provided ObjectIndexMap. - if (config->debugGHashes) { - ArrayRef hashes; - std::vector ownedHashes; - if (Optional> debugH = getDebugH(file)) - hashes = getHashesFromDebugH(*debugH); - else { - ownedHashes = GloballyHashedType::hashTypes(types); - hashes = ownedHashes; - } - - if (auto err = mergeTypeAndIdRecords( - tMerger.globalIDTable, tMerger.globalTypeTable, - objectIndexMap->tpiMap, types, hashes, file->pchSignature)) - fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(err))); - } else { - if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable, - objectIndexMap->tpiMap, types, - file->pchSignature)) - fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(err))); - } - - if (config->showSummary) { - // 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 - // collecting statistics. - tpiCounts.resize(tMerger.getTypeTable().size()); - ipiCounts.resize(tMerger.getIDTable().size()); - uint32_t srcIdx = 0; - for (CVType &ty : types) { - TypeIndex dstIdx = objectIndexMap->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()) - continue; - SmallVectorImpl &counts = - isIdRecord(ty.kind()) ? ipiCounts : tpiCounts; - ++counts[dstIdx.toArrayIndex()]; - } - } - - return *objectIndexMap; -} - -Expected PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) { - Expected pdbSession = findTypeServerSource(file); - if (!pdbSession) - return pdbSession.takeError(); - - pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile(); - pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); - - auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap()); - CVIndexMap &indexMap = it.first->second; - if (!it.second) - return indexMap; // already merged - - // Mark this map as a type server map. - indexMap.isTypeServerMap = true; - - Expected expectedTpi = pdbFile.getPDBTpiStream(); - if (auto e = expectedTpi.takeError()) - fatal("Type server does not have TPI stream: " + toString(std::move(e))); - pdb::TpiStream *maybeIpi = nullptr; - if (pdbFile.hasPDBIpiStream()) { - Expected expectedIpi = pdbFile.getPDBIpiStream(); - if (auto e = expectedIpi.takeError()) - fatal("Error getting type server IPI stream: " + toString(std::move(e))); - maybeIpi = &*expectedIpi; - } - - if (config->debugGHashes) { - // PDBs do not actually store global hashes, so when merging a type server - // PDB we have to synthesize global hashes. To do this, we first synthesize - // global hashes for the TPI stream, since it is independent, then we - // synthesize hashes for the IPI stream, using the hashes for the TPI stream - // as inputs. - auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); - Optional endPrecomp; - // Merge TPI first, because the IPI stream will reference type indices. - if (auto err = - mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap, - expectedTpi->typeArray(), tpiHashes, endPrecomp)) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); - - // Merge IPI. - if (maybeIpi) { - auto ipiHashes = - GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); - if (auto err = - mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap, - indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes)) - fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); - } - } else { - // Merge TPI first, because the IPI stream will reference type indices. - if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap, - expectedTpi->typeArray())) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); - - // Merge IPI. - if (maybeIpi) { - if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap, - indexMap.ipiMap, maybeIpi->typeArray())) - fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); - } - } - - if (config->showSummary) { - // 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. - tpiCounts.resize(tMerger.getTypeTable().size()); - ipiCounts.resize(tMerger.getIDTable().size()); - for (TypeIndex ti : indexMap.tpiMap) - if (!ti.isSimple()) - ++tpiCounts[ti.toArrayIndex()]; - for (TypeIndex ti : indexMap.ipiMap) - if (!ti.isSimple()) - ++ipiCounts[ti.toArrayIndex()]; - } - - return indexMap; -} - -Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file, - CVIndexMap *objectIndexMap) { - const PrecompRecord &precomp = - retrieveDependencyInfo(file->debugTypesObj); - - Expected e = aquirePrecompObj(file); - if (!e) - return e.takeError(); - - const CVIndexMap &precompIndexMap = *e; - assert(precompIndexMap.isPrecompiledTypeMap); - - if (precompIndexMap.tpiMap.empty()) - return Error::success(); - - assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); - assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size()); - // Use the previously remapped index map from the precompiled headers. - objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(), - precompIndexMap.tpiMap.begin() + - precomp.getTypesCount()); - return Error::success(); -} - -static bool equals_path(StringRef path1, StringRef path2) { -#if defined(_WIN32) - return path1.equals_lower(path2); -#else - return path1.equals(path2); -#endif -} -// Find by name an OBJ provided on the command line -static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly, - uint32_t precompSignature) { - for (ObjFile *f : ObjFile::instances) { - StringRef currentFileName = sys::path::filename(f->getName()); - - if (f->pchSignature.hasValue() && - f->pchSignature.getValue() == precompSignature && - equals_path(fileNameOnly, currentFileName)) - return f; - } - return nullptr; -} - -std::pair -PDBLinker::registerPrecompiledHeaders(uint32_t signature) { - auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()}); - CVIndexMap &indexMap = insertion.first->second; - if (!insertion.second) - return {indexMap, true}; - // Mark this map as a precompiled types map. - indexMap.isPrecompiledTypeMap = true; - return {indexMap, false}; -} - -Expected PDBLinker::aquirePrecompObj(ObjFile *file) { - const PrecompRecord &precomp = - retrieveDependencyInfo(file->debugTypesObj); - - // First, check if we already loaded the precompiled headers object with this - // signature. Return the type index mapping if we've already seen it. - auto r = registerPrecompiledHeaders(precomp.getSignature()); - if (r.second) - return r.first; - - CVIndexMap &indexMap = r.first; - - // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP - // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, - // the paths embedded in the OBJs are in the Windows format. - SmallString<128> precompFileName = sys::path::filename( - precomp.getPrecompFilePath(), sys::path::Style::windows); - - // link.exe requires that a precompiled headers object must always be provided - // on the command-line, even if that's not necessary. - auto precompFile = - findObjWithPrecompSignature(precompFileName, precomp.Signature); - if (!precompFile) - return createFileError( - precomp.getPrecompFilePath().str(), - make_error(pdb::pdb_error_code::no_matching_pch)); - - addObjFile(precompFile, &indexMap); - - return indexMap; -} - static bool remapTypeIndex(TypeIndex &ti, ArrayRef typeIndexMap) { if (ti.isSimple()) return true; @@ -695,7 +330,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) { + TypeCollection &idTable) { RecordPrefix *prefix = reinterpret_cast(recordData.data()); SymbolKind kind = symbolKind(recordData); @@ -725,11 +360,10 @@ static void translateIdSymbols(MutableArrayRef &recordData, // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and // in both cases we just need the second type index. if (!ti->isSimple() && !ti->isNoneType()) { - CVType funcIdData = iDTable.getType(*ti); - SmallVector indices; - discoverTypeIndices(funcIdData, indices); - assert(indices.size() == 2); - *ti = indices[1]; + 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()); } kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 @@ -795,6 +429,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { switch (sym.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_CONSTANT: + case SymbolKind::S_GTHREAD32: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place // since they are synthesized by the linker in response to S_GPROC32 and // S_LPROC32, but if we do see them, don't put them in the module stream I @@ -807,17 +442,18 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { return !isGlobalScope; // S_GDATA32 does not go in the module stream, but S_LDATA32 does. case SymbolKind::S_LDATA32: + case SymbolKind::S_LTHREAD32: default: return true; } } -static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) { +static bool symbolGoesInGlobalsStream(const CVSymbol &sym, + bool isFunctionScope) { switch (sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_GDATA32: - // S_LDATA32 goes in both the module stream and the globals stream. - case SymbolKind::S_LDATA32: + case SymbolKind::S_GTHREAD32: case SymbolKind::S_GPROC32: case SymbolKind::S_LPROC32: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place @@ -826,9 +462,11 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) { case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: return true; - // S_UDT records go in the globals stream if it is a global S_UDT. + // Records that go in the globals stream, unless they are function-local. case SymbolKind::S_UDT: - return isGlobalScope; + case SymbolKind::S_LDATA32: + case SymbolKind::S_LTHREAD32: + return !isFunctionScope; default: return false; } @@ -840,6 +478,8 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, case SymbolKind::S_CONSTANT: case SymbolKind::S_UDT: case SymbolKind::S_GDATA32: + case SymbolKind::S_GTHREAD32: + case SymbolKind::S_LTHREAD32: case SymbolKind::S_LDATA32: case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: @@ -899,7 +539,7 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, MutableArrayRef alignedSymbolMem; if (needsRealignment) { void *alignedData = - alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); + bAlloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); alignedSymbolMem = makeMutableArrayRef( reinterpret_cast(alignedData), totalRealignedSize); } @@ -953,7 +593,7 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, // 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())) { + if (symbolGoesInGlobalsStream(sym, !scopes.empty())) { addGlobalSymbol(builder.getGsiBuilder(), file->moduleDBI->getModuleIndex(), curSymOffset, sym); ++globalSymbols; @@ -980,16 +620,6 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, file->moduleDBI->addSymbolsInBulk(bulkSymbols); } -// Allocate memory for a .debug$S / .debug$F section and relocate it. -static ArrayRef relocateDebugChunk(BumpPtrAllocator &alloc, - SectionChunk &debugChunk) { - uint8_t *buffer = alloc.Allocate(debugChunk.getSize()); - assert(debugChunk.getOutputSectionIdx() == 0 && - "debug sections should not be in output sections"); - debugChunk.writeTo(buffer); - return makeArrayRef(buffer, debugChunk.getSize()); -} - static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { OutputSection *os = c ? c->getOutputSection() : nullptr; pdb::SectionContrib sc; @@ -1027,15 +657,19 @@ translateStringTableIndex(uint32_t objIndex, return pdbStrTable.insert(*expectedString); } -void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { - DebugSubsectionArray subsections; - - ArrayRef relocatedDebugContents = SectionChunk::consumeDebugMagic( - relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName()); +void DebugSHandler::handleDebugS(ArrayRef relocatedDebugContents) { + relocatedDebugContents = + SectionChunk::consumeDebugMagic(relocatedDebugContents, ".debug$S"); + DebugSubsectionArray subsections; BinaryStreamReader reader(relocatedDebugContents, support::little); exitOnErr(reader.readArray(subsections, relocatedDebugContents.size())); + // If there is no index map, use an empty one. + CVIndexMap tempIndexMap; + if (!indexMap) + indexMap = &tempIndexMap; + for (const DebugSubsectionRecord &ss : subsections) { // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++ // runtime have subsections with this bit set. @@ -1044,9 +678,9 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { switch (ss.kind()) { case DebugSubsectionKind::StringTable: { - assert(!cVStrTab.valid() && + assert(!cvStrTab.valid() && "Encountered multiple string table subsections!"); - exitOnErr(cVStrTab.initialize(ss.getRecordData())); + exitOnErr(cvStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: @@ -1060,9 +694,10 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { file.moduleDBI->addDebugSubsection(ss); break; case DebugSubsectionKind::InlineeLines: - assert(!inlineeLines.valid() && - "Encountered multiple inlinee lines subsections!"); - exitOnErr(inlineeLines.initialize(ss.getRecordData())); + // 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); break; case DebugSubsectionKind::FrameData: { // We need to re-write string table indices here, so save off all @@ -1074,7 +709,7 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { break; } case DebugSubsectionKind::Symbols: { - linker.mergeSymbolRecords(&file, indexMap, stringTableReferences, + linker.mergeSymbolRecords(&file, *indexMap, stringTableReferences, ss.getRecordData()); break; } @@ -1114,39 +749,24 @@ getFileName(const DebugStringTableSubsectionRef &strings, return strings.getString(offset); } -std::shared_ptr -DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) { - auto newInlineeLines = std::make_shared( - *newChecksums, inlineeLines.hasExtraFiles()); +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 = line.Header->Inlinee; - uint32_t fileID = line.Header->FileID; - uint32_t sourceLine = line.Header->SourceLineNum; - + TypeIndex &inlinee = *const_cast(&line.Header->Inlinee); ArrayRef typeOrItemMap = - indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap; + indexMap->isTypeServerMap ? indexMap->ipiMap : indexMap->tpiMap; if (!remapTypeIndex(inlinee, typeOrItemMap)) { - log("ignoring inlinee line record in " + file.getName() + + log("bad inlinee line record in " + file.getName() + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); - continue; - } - - SmallString<128> filename = - exitOnErr(getFileName(cVStrTab, checksums, fileID)); - pdbMakeAbsolute(filename); - newInlineeLines->addInlineSite(inlinee, filename, sourceLine); - - if (inlineeLines.hasExtraFiles()) { - for (uint32_t extraFileId : line.ExtraFiles) { - filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId)); - pdbMakeAbsolute(filename); - newInlineeLines->addExtraFile(filename); - } } } - return newInlineeLines; + // Add the modified inlinee line subsection directly. + file.moduleDBI->addDebugSubsection(inlineeSubsection); } void DebugSHandler::finish() { @@ -1155,7 +775,7 @@ void DebugSHandler::finish() { // We should have seen all debug subsections across the entire object file now // which means that if a StringTable subsection and Checksums subsection were // present, now is the time to handle them. - if (!cVStrTab.valid()) { + if (!cvStrTab.valid()) { if (checksums.valid()) fatal(".debug$S sections with a checksums subsection must also contain a " "string table subsection"); @@ -1173,77 +793,92 @@ void DebugSHandler::finish() { for (codeview::FrameData fd : fds) { fd.RvaStart += *reloc; fd.FrameFunc = - translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab); + translateStringTableIndex(fd.FrameFunc, cvStrTab, linker.pdbStrTab); dbiBuilder.addNewFpoData(fd); } } for (ulittle32_t *ref : stringTableReferences) - *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab); + *ref = translateStringTableIndex(*ref, cvStrTab, linker.pdbStrTab); // Make a new file checksum table that refers to offsets in the PDB-wide // string table. Generally the string table subsection appears after the // checksum table, so we have to do this after looping over all the - // subsections. + // subsections. The new checksum table must have the exact same layout and + // size as the original. Otherwise, the file references in the line and + // inlinee line tables will be incorrect. auto newChecksums = std::make_unique(linker.pdbStrTab); for (FileChecksumEntry &fc : checksums) { SmallString<128> filename = - exitOnErr(cVStrTab.getString(fc.FileNameOffset)); + exitOnErr(cvStrTab.getString(fc.FileNameOffset)); pdbMakeAbsolute(filename); exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); } - - // Rewrite inlinee item indices if present. - if (inlineeLines.valid()) - file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get())); + assert(checksums.getArray().getUnderlyingStream().getLength() == + newChecksums->calculateSerializedSize() && + "file checksum table must have same layout"); file.moduleDBI->addDebugSubsection(std::move(newChecksums)); } -void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { - if (file->mergedIntoPDB) +static void warnUnusable(InputFile *f, Error e) { + if (!config->warnDebugInfoUnusable) { + consumeError(std::move(e)); return; - file->mergedIntoPDB = true; + } + auto msg = "Cannot use debug info for '" + toString(f) + "' [LNK4099]"; + if (e) + warn(msg + "\n>>> failed to load reference " + toString(std::move(e))); + else + 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. - CVIndexMap objectIndexMap; - auto indexMapResult = - mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap); + Expected r = source->mergeDebugT(&tMerger, localMap); // If the .debug$T sections fail to merge, assume there is no debug info. - if (!indexMapResult) { - if (!config->warnDebugInfoUnusable) { - consumeError(indexMapResult.takeError()); - return; - } - warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" + - ">>> failed to load reference " + - StringRef(toString(indexMapResult.takeError()))); - return; + if (!r) { + warnUnusable(source->file, r.takeError()); + return nullptr; } + return *r; +} - ScopedTimer t(symbolMergingTimer); +// Allocate memory for a .debug$S / .debug$F section and relocate it. +static ArrayRef relocateDebugChunk(SectionChunk &debugChunk) { + uint8_t *buffer = bAlloc.Allocate(debugChunk.getSize()); + assert(debugChunk.getOutputSectionIdx() == 0 && + "debug sections should not be in output sections"); + debugChunk.writeTo(buffer); + return makeArrayRef(buffer, debugChunk.getSize()); +} +void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) { + ScopedTimer t(symbolMergingTimer); pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); - DebugSHandler dsh(*this, *file, *indexMapResult); + DebugSHandler dsh(*this, *file, indexMap); // Now do all live .debug$S and .debug$F sections. for (SectionChunk *debugChunk : file->getDebugChunks()) { if (!debugChunk->live || debugChunk->getSize() == 0) continue; - if (debugChunk->getSectionName() == ".debug$S") { - dsh.handleDebugS(*debugChunk); + bool isDebugS = debugChunk->getSectionName() == ".debug$S"; + bool isDebugF = debugChunk->getSectionName() == ".debug$F"; + if (!isDebugS && !isDebugF) continue; - } - if (debugChunk->getSectionName() == ".debug$F") { - ArrayRef relocatedDebugContents = - relocateDebugChunk(alloc, *debugChunk); + ArrayRef relocatedDebugContents = relocateDebugChunk(*debugChunk); + if (isDebugS) { + dsh.handleDebugS(relocatedDebugContents); + } else if (isDebugF) { FixedStreamArray fpoRecords; BinaryStreamReader reader(relocatedDebugContents, support::little); uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData); @@ -1253,7 +888,6 @@ void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { // can just copy it. for (const object::FpoData &fd : fpoRecords) dbiBuilder.addOldFpoData(fd); - continue; } } @@ -1265,43 +899,54 @@ void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { // 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) { +static void createModuleDBI(pdb::PDBFileBuilder &builder, ObjFile *file) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); SmallString<128> objName; - for (ObjFile *file : ObjFile::instances) { + bool inArchive = !file->parentName.empty(); + objName = inArchive ? file->parentName : file->getName(); + pdbMakeAbsolute(objName); + StringRef modName = inArchive ? file->getName() : StringRef(objName); - bool inArchive = !file->parentName.empty(); - objName = inArchive ? file->parentName : file->getName(); - pdbMakeAbsolute(objName); - StringRef modName = inArchive ? file->getName() : StringRef(objName); + file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); + file->moduleDBI->setObjFileName(objName); - file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); - file->moduleDBI->setObjFileName(objName); + ArrayRef chunks = file->getChunks(); + uint32_t modi = file->moduleDBI->getModuleIndex(); - ArrayRef chunks = file->getChunks(); - uint32_t modi = file->moduleDBI->getModuleIndex(); - - for (Chunk *c : chunks) { - auto *secChunk = dyn_cast(c); - if (!secChunk || !secChunk->live) - continue; - pdb::SectionContrib sc = createSectionContrib(secChunk, modi); - file->moduleDBI->setFirstSectionContrib(sc); - break; - } + for (Chunk *c : chunks) { + auto *secChunk = dyn_cast(c); + if (!secChunk || !secChunk->live) + continue; + pdb::SectionContrib sc = createSectionContrib(secChunk, modi); + file->moduleDBI->setFirstSectionContrib(sc); + break; } } -static PublicSym32 createPublic(Defined *def) { - PublicSym32 pub(SymbolKind::S_PUB32); - pub.Name = def->getName(); +void PDBLinker::addDebug(TpiSource *source) { + CVIndexMap localMap; + const CVIndexMap *indexMap = mergeTypeRecords(source, &localMap); + + if (source->kind == TpiSource::PDB) + return; // No symbols in TypeServer PDBs + + addDebugSymbols(source->file, indexMap); +} + +static pdb::BulkPublic createPublic(Defined *def) { + pdb::BulkPublic pub; + pub.Name = def->getName().data(); + pub.NameLen = def->getName().size(); + + PublicSymFlags flags = PublicSymFlags::None; if (auto *d = dyn_cast(def)) { if (d->getCOFFSymbol().isFunctionDefinition()) - pub.Flags = PublicSymFlags::Function; + flags = PublicSymFlags::Function; } else if (isa(def)) { - pub.Flags = PublicSymFlags::Function; + flags = PublicSymFlags::Function; } + pub.setFlags(flags); OutputSection *os = def->getChunk()->getOutputSection(); assert(os && "all publics should be in final image"); @@ -1315,10 +960,30 @@ static PublicSym32 createPublic(Defined *def) { void PDBLinker::addObjectsToPDB() { ScopedTimer t1(addObjectsTimer); - createModuleDBI(builder); + // Create module descriptors + for_each(ObjFile::instances, + [&](ObjFile *obj) { createModuleDBI(builder, obj); }); - for (ObjFile *file : ObjFile::instances) - addObjFile(file); + // 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); + }); + + // Merge dependencies + TpiSource::forEachSource([&](TpiSource *source) { + if (source->isDependency()) + addDebug(source); + }); + + // Merge regular and dependent OBJs + TpiSource::forEachSource([&](TpiSource *source) { + if (!source->isDependency()) + addDebug(source); + }); builder.getStringTableBuilder().setStrings(pdbStrTab); t1.stop(); @@ -1328,13 +993,16 @@ void PDBLinker::addObjectsToPDB() { addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); t2.stop(); +} - ScopedTimer t3(globalsLayoutTimer); - // Compute the public and global symbols. +void PDBLinker::addPublicsToPDB() { + ScopedTimer t3(publicsLayoutTimer); + // Compute the public symbols. auto &gsiBuilder = builder.getGsiBuilder(); - std::vector publics; + std::vector publics; symtab->forEachSymbol([&publics](Symbol *s) { - // Only emit defined, live symbols that have a chunk. + // 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()) publics.push_back(createPublic(def)); @@ -1342,12 +1010,7 @@ void PDBLinker::addObjectsToPDB() { if (!publics.empty()) { publicSymbols = publics.size(); - // Sort the public symbols and add them to the stream. - parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) { - return l.Name < r.Name; - }); - for (const PublicSym32 &pub : publics) - gsiBuilder.addPublicSymbol(pub); + gsiBuilder.addPublicSymbols(std::move(publics)); } } @@ -1367,8 +1030,8 @@ void PDBLinker::printStats() { print(ObjFile::instances.size(), "Input OBJ files (expanded from all cmd-line inputs)"); - print(typeServerIndexMappings.size(), "PDB type server dependencies"); - print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies"); + print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies"); + print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies"); print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(), "Merged TPI records"); print(pdbStrTab.size(), "Output PDB strings"); @@ -1388,6 +1051,8 @@ void PDBLinker::printStats() { TypeIndex typeIndex; uint64_t totalInputSize() const { return uint64_t(dupCount) * typeSize; } bool operator<(const TypeSizeInfo &rhs) const { + if (totalInputSize() == rhs.totalInputSize()) + return typeIndex < rhs.typeIndex; return totalInputSize() < rhs.totalInputSize(); } }; @@ -1420,8 +1085,8 @@ void PDBLinker::printStats() { } }; - printLargeInputTypeRecs("TPI", tpiCounts, tMerger.getTypeTable()); - printLargeInputTypeRecs("IPI", ipiCounts, tMerger.getIDTable()); + printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable()); + printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable()); message(buffer); } @@ -1438,6 +1103,19 @@ void PDBLinker::addNatvisFiles() { } } +void PDBLinker::addNamedStreams() { + for (const auto &streamFile : config->namedStreams) { + const StringRef stream = streamFile.getKey(), file = streamFile.getValue(); + ErrorOr> dataOrErr = + MemoryBuffer::getFile(file); + if (!dataOrErr) { + warn("Cannot open input file: " + file); + continue; + } + exitOnErr(builder.addNamedStream(stream, (*dataOrErr)->getBuffer())); + } +} + static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) { switch (machine) { case COFF::IMAGE_FILE_MACHINE_AMD64: @@ -1473,7 +1151,7 @@ static std::string quote(ArrayRef args) { a.split(s, '"'); r.append(join(s, "\"\"")); } else { - r.append(a); + r.append(std::string(a)); } if (hasWS || hasQ) r.push_back('"'); @@ -1508,8 +1186,7 @@ static void fillLinkerVerRecord(Compile3Sym &cs) { } static void addCommonLinkerModuleSymbols(StringRef path, - pdb::DbiModuleDescriptorBuilder &mod, - BumpPtrAllocator &allocator) { + pdb::DbiModuleDescriptorBuilder &mod) { ObjNameSym ons(SymbolRecordKind::ObjNameSym); EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); @@ -1536,17 +1213,16 @@ static void addCommonLinkerModuleSymbols(StringRef path, ebs.Fields.push_back("cmd"); ebs.Fields.push_back(argStr); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - ons, allocator, CodeViewContainer::Pdb)); + ons, bAlloc, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - cs, allocator, CodeViewContainer::Pdb)); + cs, bAlloc, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - ebs, allocator, CodeViewContainer::Pdb)); + ebs, bAlloc, CodeViewContainer::Pdb)); } static void addLinkerModuleCoffGroup(PartialSection *sec, pdb::DbiModuleDescriptorBuilder &mod, - OutputSection &os, - BumpPtrAllocator &allocator) { + OutputSection &os) { // If there's a section, there's at least one chunk assert(!sec->chunks.empty()); const Chunk *firstChunk = *sec->chunks.begin(); @@ -1567,12 +1243,11 @@ static void addLinkerModuleCoffGroup(PartialSection *sec, cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - cgs, allocator, CodeViewContainer::Pdb)); + cgs, bAlloc, CodeViewContainer::Pdb)); } static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, - OutputSection &os, - BumpPtrAllocator &allocator) { + OutputSection &os) { SectionSym sym(SymbolRecordKind::SectionSym); sym.Alignment = 12; // 2^12 = 4KB sym.Characteristics = os.header.Characteristics; @@ -1581,7 +1256,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, sym.Rva = os.getRVA(); sym.SectionNumber = os.sectionIndex; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - sym, allocator, CodeViewContainer::Pdb)); + sym, bAlloc, CodeViewContainer::Pdb)); // Skip COFF groups in MinGW because it adds a significant footprint to the // PDB, due to each function being in its own section @@ -1590,7 +1265,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, // Output COFF groups for individual chunks of this section. for (PartialSection *sec : os.contribSections) { - addLinkerModuleCoffGroup(sec, mod, os, allocator); + addLinkerModuleCoffGroup(sec, mod, os); } } @@ -1657,18 +1332,18 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( - ons, alloc, CodeViewContainer::Pdb)); + ons, bAlloc, CodeViewContainer::Pdb)); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( - cs, alloc, CodeViewContainer::Pdb)); + cs, bAlloc, CodeViewContainer::Pdb)); SmallVector scopes; CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol( - ts, alloc, CodeViewContainer::Pdb); + ts, bAlloc, CodeViewContainer::Pdb); scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym); mod->addSymbol(newSym); - newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc, + newSym = codeview::SymbolSerializer::writeOneSymbol(es, bAlloc, CodeViewContainer::Pdb); scopeStackClose(scopes, mod->getNextSymbolOffset(), file); @@ -1681,10 +1356,10 @@ void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { } // Creates a PDB file. -void createPDB(SymbolTable *symtab, - ArrayRef outputSections, - ArrayRef sectionTable, - llvm::codeview::DebugInfo *buildId) { +void lld::coff::createPDB(SymbolTable *symtab, + ArrayRef outputSections, + ArrayRef sectionTable, + llvm::codeview::DebugInfo *buildId) { ScopedTimer t1(totalPdbLinkTimer); PDBLinker pdb(symtab); @@ -1693,6 +1368,8 @@ void createPDB(SymbolTable *symtab, pdb.addImportFilesToPDB(outputSections); pdb.addSections(outputSections, sectionTable); pdb.addNatvisFiles(); + pdb.addNamedStreams(); + pdb.addPublicsToPDB(); ScopedTimer t2(diskCommitTimer); codeview::GUID guid; @@ -1743,11 +1420,11 @@ void PDBLinker::addSections(ArrayRef outputSections, uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); linkerModule.setPdbFilePathNI(pdbFilePathNI); - addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc); + addCommonLinkerModuleSymbols(nativePath, linkerModule); // Add section contributions. They must be ordered by ascending RVA. for (OutputSection *os : outputSections) { - addLinkerModuleSectionSymbol(linkerModule, *os, alloc); + addLinkerModuleSectionSymbol(linkerModule, *os); for (Chunk *c : os->chunks) { pdb::SectionContrib sc = createSectionContrib(c, linkerModule.getModuleIndex()); @@ -1766,8 +1443,7 @@ void PDBLinker::addSections(ArrayRef outputSections, ArrayRef sections = { (const object::coff_section *)sectionTable.data(), sectionTable.size() / sizeof(object::coff_section)}; - sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections); - dbiBuilder.setSectionMap(sectionMap); + dbiBuilder.createSectionMap(sections); // Add COFF section header stream. exitOnErr( @@ -1801,7 +1477,7 @@ static uint32_t getSecrelReloc() { // table are stored in the output arguments. Returns whether a line table was // found. static bool findLineTable(const SectionChunk *c, uint32_t addr, - DebugStringTableSubsectionRef &cVStrTab, + DebugStringTableSubsectionRef &cvStrTab, DebugChecksumsSubsectionRef &checksums, DebugLinesSubsectionRef &lines, uint32_t &offsetInLinetable) { @@ -1833,9 +1509,9 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr, for (const DebugSubsectionRecord &ss : subsections) { switch (ss.kind()) { case DebugSubsectionKind::StringTable: { - assert(!cVStrTab.valid() && + assert(!cvStrTab.valid() && "Encountered multiple string table subsections!"); - exitOnErr(cVStrTab.initialize(ss.getRecordData())); + exitOnErr(cvStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: @@ -1871,7 +1547,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr, break; } - if (cVStrTab.valid() && checksums.valid() && lines.header()) + if (cvStrTab.valid() && checksums.valid() && lines.header()) return true; } } @@ -1883,15 +1559,15 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr, // offset into the given chunk and return them, or None if a line table was // not found. Optional> -getFileLineCodeView(const SectionChunk *c, uint32_t addr) { +lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) { ExitOnError exitOnErr; - DebugStringTableSubsectionRef cVStrTab; + DebugStringTableSubsectionRef cvStrTab; DebugChecksumsSubsectionRef checksums; DebugLinesSubsectionRef lines; uint32_t offsetInLinetable; - if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) + if (!findLineTable(c, addr, cvStrTab, checksums, lines, offsetInLinetable)) return None; Optional nameIndex; @@ -1905,7 +1581,7 @@ getFileLineCodeView(const SectionChunk *c, uint32_t addr) { lineNumber = li.getStartLine(); } StringRef filename = - exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + exitOnErr(getFileName(cvStrTab, checksums, *nameIndex)); return std::make_pair(filename, *lineNumber); } nameIndex = entry.NameIndex; @@ -1914,9 +1590,6 @@ getFileLineCodeView(const SectionChunk *c, uint32_t addr) { } if (!nameIndex) return None; - StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + StringRef filename = exitOnErr(getFileName(cvStrTab, checksums, *nameIndex)); return std::make_pair(filename, *lineNumber); } - -} // namespace coff -} // namespace lld diff --git a/gnu/llvm/lld/COFF/SymbolTable.cpp b/gnu/llvm/lld/COFF/SymbolTable.cpp index 7072f4d8d0e..173e32f628e 100644 --- a/gnu/llvm/lld/COFF/SymbolTable.cpp +++ b/gnu/llvm/lld/COFF/SymbolTable.cpp @@ -136,12 +136,16 @@ getFileLine(const SectionChunk *c, uint32_t addr) { // of all references to that symbol from that file. If no debug information is // available, returns just the name of the file, else one string per actual // reference as described in the debug info. -std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { +// Returns up to maxStrings string descriptions, along with the total number of +// locations found. +static std::pair, size_t> +getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) { struct Location { Symbol *sym; std::pair fileLine; }; std::vector locations; + size_t numLocations = 0; for (Chunk *c : file->getChunks()) { auto *sc = dyn_cast(c); @@ -150,6 +154,10 @@ std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { for (const coff_relocation &r : sc->getRelocs()) { if (r.SymbolTableIndex != symIndex) continue; + numLocations++; + if (locations.size() >= maxStrings) + continue; + Optional> fileLine = getFileLine(sc, r.VirtualAddress); Symbol *sym = getSymbol(sc, r.VirtualAddress); @@ -160,8 +168,12 @@ std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { } } - if (locations.empty()) - return std::vector({"\n>>> referenced by " + toString(file)}); + if (maxStrings == 0) + return std::make_pair(std::vector(), numLocations); + + if (numLocations == 0) + return std::make_pair( + std::vector{"\n>>> referenced by " + toString(file)}, 1); std::vector symbolLocations(locations.size()); size_t i = 0; @@ -175,17 +187,26 @@ std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { if (loc.sym) os << ":(" << toString(*loc.sym) << ')'; } - return symbolLocations; + return std::make_pair(symbolLocations, numLocations); } -std::vector getSymbolLocations(InputFile *file, - uint32_t symIndex) { +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { + return getSymbolLocations(file, symIndex, SIZE_MAX).first; +} + +static std::pair, size_t> +getSymbolLocations(InputFile *file, uint32_t symIndex, size_t maxStrings) { if (auto *o = dyn_cast(file)) - return getSymbolLocations(o, symIndex); - if (auto *b = dyn_cast(file)) - return getSymbolLocations(b); + return getSymbolLocations(o, symIndex, maxStrings); + if (auto *b = dyn_cast(file)) { + std::vector symbolLocations = getSymbolLocations(b); + size_t numLocations = symbolLocations.size(); + if (symbolLocations.size() > maxStrings) + symbolLocations.resize(maxStrings); + return std::make_pair(symbolLocations, numLocations); + } llvm_unreachable("unsupported file type passed to getSymbolLocations"); - return {}; + return std::make_pair(std::vector(), (size_t)0); } // For an undefined symbol, stores all files referencing it and the index of @@ -204,21 +225,22 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { llvm::raw_string_ostream os(out); os << "undefined symbol: " << toString(*undefDiag.sym); - const size_t maxUndefReferences = 10; - size_t i = 0, numRefs = 0; + const size_t maxUndefReferences = 3; + size_t numDisplayedRefs = 0, numRefs = 0; for (const UndefinedDiag::File &ref : undefDiag.files) { - std::vector symbolLocations = - getSymbolLocations(ref.file, ref.symIndex); - numRefs += symbolLocations.size(); + std::vector symbolLocations; + size_t totalLocations = 0; + std::tie(symbolLocations, totalLocations) = getSymbolLocations( + ref.file, ref.symIndex, maxUndefReferences - numDisplayedRefs); + + numRefs += totalLocations; + numDisplayedRefs += symbolLocations.size(); for (const std::string &s : symbolLocations) { - if (i >= maxUndefReferences) - break; os << s; - i++; } } - if (i < numRefs) - os << "\n>>> referenced " << numRefs - i << " more times"; + if (numDisplayedRefs < numRefs) + os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times"; errorOrWarn(os.str()); } @@ -438,7 +460,7 @@ void SymbolTable::resolveRemainingUndefines() { if (name.contains("_PchSym_")) continue; - if (config->mingw && handleMinGWAutomaticImport(sym, name)) + if (config->autoImport && handleMinGWAutomaticImport(sym, name)) continue; // Remaining undefined symbols are not fatal if /force is specified. @@ -789,20 +811,16 @@ Symbol *SymbolTable::addUndefined(StringRef name) { return addUndefined(name, nullptr, false); } -std::vector SymbolTable::compileBitcodeFiles() { - lto.reset(new BitcodeCompiler); - for (BitcodeFile *f : BitcodeFile::instances) - lto->add(*f); - return lto->compile(); -} - void SymbolTable::addCombinedLTOObjects() { if (BitcodeFile::instances.empty()) return; ScopedTimer t(ltoTimer); - for (StringRef object : compileBitcodeFiles()) { - auto *obj = make(MemoryBufferRef(object, "lto.tmp")); + lto.reset(new BitcodeCompiler); + for (BitcodeFile *f : BitcodeFile::instances) + lto->add(*f); + for (InputFile *newObj : lto->compile()) { + ObjFile *obj = cast(newObj); obj->parse(); ObjFile::instances.push_back(obj); } diff --git a/gnu/llvm/lld/COFF/SymbolTable.h b/gnu/llvm/lld/COFF/SymbolTable.h index cd8a53dcecd..870a7151fa8 100644 --- a/gnu/llvm/lld/COFF/SymbolTable.h +++ b/gnu/llvm/lld/COFF/SymbolTable.h @@ -77,7 +77,6 @@ public: // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. void addCombinedLTOObjects(); - std::vector compileBitcodeFiles(); // Creates an Undefined symbol for a given name. Symbol *addUndefined(StringRef name); diff --git a/gnu/llvm/lld/COFF/Symbols.cpp b/gnu/llvm/lld/COFF/Symbols.cpp index 938c9c527ff..60ff72aeb52 100644 --- a/gnu/llvm/lld/COFF/Symbols.cpp +++ b/gnu/llvm/lld/COFF/Symbols.cpp @@ -36,12 +36,12 @@ static std::string maybeDemangleSymbol(StringRef symName) { StringRef demangleInput = prefixless; if (config->machine == I386) demangleInput.consume_front("_"); - std::string demangled = demangle(demangleInput); + std::string demangled = demangle(std::string(demangleInput)); if (demangled != demangleInput) - return prefix + demangle(demangleInput); + return prefix + demangle(std::string(demangleInput)); return (prefix + prefixless).str(); } - return symName; + return std::string(symName); } std::string toString(coff::Symbol &b) { return maybeDemangleSymbol(b.getName()); @@ -52,23 +52,15 @@ std::string toCOFFString(const Archive::Symbol &b) { namespace coff { -StringRef Symbol::getName() { - // COFF symbol names are read lazily for a performance reason. - // Non-external symbol names are never used by the linker except for logging - // or debugging. Their internal references are resolved not by name but by - // symbol index. And because they are not external, no one can refer them by - // name. Object files contain lots of non-external symbols, and creating - // StringRefs for them (which involves lots of strlen() on the string table) - // is a waste of time. - if (nameData == nullptr) { - auto *d = cast(this); - StringRef nameStr; - cast(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr); - nameData = nameStr.data(); - nameSize = nameStr.size(); - assert(nameSize == nameStr.size() && "name length truncated"); - } - return StringRef(nameData, nameSize); +void Symbol::computeName() { + assert(nameData == nullptr && + "should only compute the name once for DefinedCOFF symbols"); + auto *d = cast(this); + StringRef nameStr = + check(cast(d->file)->getCOFFObj()->getSymbolName(d->sym)); + nameData = nameStr.data(); + nameSize = nameStr.size(); + assert(nameSize == nameStr.size() && "name length truncated"); } InputFile *Symbol::getFile() { diff --git a/gnu/llvm/lld/COFF/Symbols.h b/gnu/llvm/lld/COFF/Symbols.h index a8e70320b99..1da4df36696 100644 --- a/gnu/llvm/lld/COFF/Symbols.h +++ b/gnu/llvm/lld/COFF/Symbols.h @@ -69,7 +69,18 @@ public: Kind kind() const { return static_cast(symbolKind); } // Returns the symbol name. - StringRef getName(); + StringRef getName() { + // COFF symbol names are read lazily for a performance reason. + // Non-external symbol names are never used by the linker except for logging + // or debugging. Their internal references are resolved not by name but by + // symbol index. And because they are not external, no one can refer them by + // name. Object files contain lots of non-external symbols, and creating + // StringRefs for them (which involves lots of strlen() on the string table) + // is a waste of time. + if (nameData == nullptr) + computeName(); + return StringRef(nameData, nameSize); + } void replaceKeepingName(Symbol *other, size_t size); @@ -84,6 +95,9 @@ public: return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; } +private: + void computeName(); + protected: friend SymbolTable; explicit Symbol(Kind k, StringRef n = "") diff --git a/gnu/llvm/lld/COFF/TypeMerger.h b/gnu/llvm/lld/COFF/TypeMerger.h index e2cfe668cfa..858f55b6856 100644 --- a/gnu/llvm/lld/COFF/TypeMerger.h +++ b/gnu/llvm/lld/COFF/TypeMerger.h @@ -20,7 +20,7 @@ namespace coff { class TypeMerger { public: TypeMerger(llvm::BumpPtrAllocator &alloc) - : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc), + : typeTable(alloc), idTable(alloc), globalTypeTable(alloc), globalIDTable(alloc) {} /// Get the type table or the global type table if /DEBUG:GHASH is enabled. @@ -34,20 +34,25 @@ public: inline llvm::codeview::TypeCollection &getIDTable() { if (config->debugGHashes) return globalIDTable; - return iDTable; + return idTable; } /// 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; + 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 @@ -62,4 +67,4 @@ struct CVIndexMap { } // namespace coff } // namespace lld -#endif \ No newline at end of file +#endif diff --git a/gnu/llvm/lld/COFF/Writer.cpp b/gnu/llvm/lld/COFF/Writer.cpp index fcad7739d30..0188f0971a7 100644 --- a/gnu/llvm/lld/COFF/Writer.cpp +++ b/gnu/llvm/lld/COFF/Writer.cpp @@ -10,13 +10,13 @@ #include "Config.h" #include "DLL.h" #include "InputFiles.h" +#include "LLDMapFile.h" #include "MapFile.h" #include "PDB.h" #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" -#include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" @@ -41,9 +41,8 @@ using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; - -namespace lld { -namespace coff { +using namespace lld; +using namespace lld::coff; /* To re-generate DOSProgram: $ cat > /tmp/DOSProgram.asm @@ -92,7 +91,8 @@ namespace { class DebugDirectoryChunk : public NonSectionChunk { public: - DebugDirectoryChunk(const std::vector &r, bool writeRepro) + DebugDirectoryChunk(const std::vector> &r, + bool writeRepro) : records(r), writeRepro(writeRepro) {} size_t getSize() const override { @@ -102,11 +102,11 @@ public: void writeTo(uint8_t *b) const override { auto *d = reinterpret_cast(b); - for (const Chunk *record : records) { - OutputSection *os = record->getOutputSection(); - uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA()); - fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(), - record->getRVA(), offs); + for (const std::pair& record : records) { + Chunk *c = record.second; + OutputSection *os = c->getOutputSection(); + uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); + fillEntry(d, record.first, c->getSize(), c->getRVA(), offs); ++d; } @@ -141,7 +141,7 @@ private: } mutable std::vector timeDateStamps; - const std::vector &records; + const std::vector> &records; bool writeRepro; }; @@ -166,6 +166,17 @@ public: mutable codeview::DebugInfo *buildId = nullptr; }; +class ExtendedDllCharacteristicsChunk : public NonSectionChunk { +public: + ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {} + + size_t getSize() const override { return 4; } + + void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); } + + uint32_t characteristics = 0; +}; + // PartialSection represents a group of chunks that contribute to an // OutputSection. Collating a collection of PartialSections of same name and // characteristics constitutes the OutputSection. @@ -251,7 +262,7 @@ private: bool setNoSEHCharacteristic = false; DebugDirectoryChunk *debugDirectory = nullptr; - std::vector debugRecords; + std::vector> debugRecords; CVDebugRecordChunk *buildId = nullptr; ArrayRef sectionTable; @@ -290,7 +301,7 @@ private: static Timer codeLayoutTimer("Code Layout", Timer::root()); static Timer diskCommitTimer("Commit Output File", Timer::root()); -void writeResult() { Writer().run(); } +void lld::coff::writeResult() { Writer().run(); } void OutputSection::addChunk(Chunk *c) { chunks.push_back(c); @@ -588,6 +599,9 @@ void Writer::finalizeAddresses() { void Writer::run() { ScopedTimer t1(codeLayoutTimer); + // First, clear the output sections from previous runs + outputSections.clear(); + createImportTables(); createSections(); createMiscChunks(); @@ -622,6 +636,7 @@ void Writer::run() { } writeBuildId(); + writeLLDMapFile(outputSections); writeMapFile(outputSections); if (errorCount()) @@ -921,8 +936,9 @@ void Writer::createMiscChunks() { // Create Debug Information Chunks OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; - if (config->debug || config->repro) { + if (config->debug || config->repro || config->cetCompat) { debugDirectory = make(debugRecords, config->repro); + debugDirectory->setAlignment(4); debugInfoSec->addChunk(debugDirectory); } @@ -932,10 +948,20 @@ void Writer::createMiscChunks() { // allowing a debugger to match a PDB and an executable. So we need it even // if we're ultimately not going to write CodeView data to the PDB. buildId = make(); - debugRecords.push_back(buildId); + debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId}); + } - for (Chunk *c : debugRecords) - debugInfoSec->addChunk(c); + if (config->cetCompat) { + ExtendedDllCharacteristicsChunk *extendedDllChars = + make( + IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT); + debugRecords.push_back( + {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars}); + } + + if (debugRecords.size() > 0) { + for (std::pair r : debugRecords) + debugInfoSec->addChunk(r.second); } // Create SEH table. x86-only. @@ -946,11 +972,11 @@ void Writer::createMiscChunks() { if (config->guardCF != GuardCFLevel::Off) createGuardCFTables(); - if (config->mingw) { + if (config->autoImport) createRuntimePseudoRelocs(); + if (config->mingw) insertCtorDtorSymbols(); - } } // Create .idata section for the DLL-imported symbol table. @@ -1370,7 +1396,7 @@ template void Writer::writeHeader() { pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; if (config->integrityCheck) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; - if (setNoSEHCharacteristic) + if (setNoSEHCharacteristic || config->noSEH) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; if (config->terminalServerAware) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; @@ -1699,6 +1725,15 @@ void Writer::createRuntimePseudoRelocs() { sc->getRuntimePseudoRelocs(rels); } + if (!config->pseudoRelocs) { + // Not writing any pseudo relocs; if some were needed, error out and + // indicate what required them. + for (const RuntimePseudoReloc &rpr : rels) + error("automatic dllimport of " + rpr.sym->getName() + " in " + + toString(rpr.target->file) + " requires pseudo relocations"); + return; + } + if (!rels.empty()) log("Writing " + Twine(rels.size()) + " runtime pseudo relocations"); PseudoRelocTableChunk *table = make(rels); @@ -1832,6 +1867,10 @@ void Writer::sortExceptionTable() { uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); if (config->machine == AMD64) { struct Entry { ulittle32_t begin, end, unwind; }; + if ((end - begin) % sizeof(Entry) != 0) { + fatal("unexpected .pdata size: " + Twine(end - begin) + + " is not a multiple of " + Twine(sizeof(Entry))); + } parallelSort( MutableArrayRef((Entry *)begin, (Entry *)end), [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); @@ -1839,6 +1878,10 @@ void Writer::sortExceptionTable() { } if (config->machine == ARMNT || config->machine == ARM64) { struct Entry { ulittle32_t begin, unwind; }; + if ((end - begin) % sizeof(Entry) != 0) { + fatal("unexpected .pdata size: " + Twine(end - begin) + + " is not a multiple of " + Twine(sizeof(Entry))); + } parallelSort( MutableArrayRef((Entry *)begin, (Entry *)end), [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); @@ -1950,6 +1993,3 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { return it->second; return nullptr; } - -} // namespace coff -} // namespace lld diff --git a/gnu/llvm/lld/Common/CMakeLists.txt b/gnu/llvm/lld/Common/CMakeLists.txt index 7d5ad654925..41eb58c1519 100644 --- a/gnu/llvm/lld/Common/CMakeLists.txt +++ b/gnu/llvm/lld/Common/CMakeLists.txt @@ -2,6 +2,12 @@ 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) + list(APPEND LLD_SYSTEM_LIBS atomic) +endif() + find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc) find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc) @@ -36,7 +42,6 @@ add_lld_library(lldCommon Reproduce.cpp Strings.cpp TargetOptionsCommandFlags.cpp - Threads.cpp Timer.cpp VCSVersion.inc Version.cpp @@ -55,7 +60,7 @@ add_lld_library(lldCommon Target LINK_LIBS - ${LLVM_PTHREAD_LIB} + ${LLD_SYSTEM_LIBS} DEPENDS ${tablegen_deps} diff --git a/gnu/llvm/lld/Common/ErrorHandler.cpp b/gnu/llvm/lld/Common/ErrorHandler.cpp index b6066b557cb..94ff23173d6 100644 --- a/gnu/llvm/lld/Common/ErrorHandler.cpp +++ b/gnu/llvm/lld/Common/ErrorHandler.cpp @@ -8,7 +8,7 @@ #include "lld/Common/ErrorHandler.h" -#include "lld/Common/Threads.h" +#include "llvm/Support/Parallel.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" @@ -62,8 +62,11 @@ void lld::exitLld(int val) { // avoid intermittent crashes on Windows when exiting. llvm_shutdown(); - lld::outs().flush(); - lld::errs().flush(); + { + std::lock_guard lock(mu); + lld::outs().flush(); + lld::errs().flush(); + } _exit(val); } @@ -114,7 +117,7 @@ void lld::checkError(Error e) { // extracted from an error message using regexps. std::string ErrorHandler::getLocation(const Twine &msg) { if (!vsDiagnostics) - return logName; + return std::string(logName); static std::regex regexes[] = { std::regex( @@ -146,7 +149,7 @@ std::string ErrorHandler::getLocation(const Twine &msg) { return m.str(1) + "(" + m.str(2) + ")"; } - return logName; + return std::string(logName); } void ErrorHandler::log(const Twine &msg) { @@ -191,20 +194,26 @@ void ErrorHandler::error(const Twine &msg) { } } - std::lock_guard lock(mu); + bool exit = false; + { + std::lock_guard lock(mu); + + if (errorLimit == 0 || errorCount < errorLimit) { + lld::errs() << sep << getLocation(msg) << ": " << Colors::RED + << "error: " << Colors::RESET << msg << "\n"; + } else if (errorCount == errorLimit) { + lld::errs() << sep << getLocation(msg) << ": " << Colors::RED + << "error: " << Colors::RESET << errorLimitExceededMsg + << "\n"; + exit = exitEarly; + } - if (errorLimit == 0 || errorCount < errorLimit) { - lld::errs() << sep << getLocation(msg) << ": " << Colors::RED - << "error: " << Colors::RESET << msg << "\n"; - } else if (errorCount == errorLimit) { - lld::errs() << sep << getLocation(msg) << ": " << Colors::RED - << "error: " << Colors::RESET << errorLimitExceededMsg << "\n"; - if (exitEarly) - exitLld(1); + sep = getSeparator(msg); + ++errorCount; } - sep = getSeparator(msg); - ++errorCount; + if (exit) + exitLld(1); } void ErrorHandler::fatal(const Twine &msg) { diff --git a/gnu/llvm/lld/Common/Filesystem.cpp b/gnu/llvm/lld/Common/Filesystem.cpp index 75e88dbce1a..671b352a3f6 100644 --- a/gnu/llvm/lld/Common/Filesystem.cpp +++ b/gnu/llvm/lld/Common/Filesystem.cpp @@ -11,10 +11,11 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Filesystem.h" -#include "lld/Common/Threads.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" #if LLVM_ON_UNIX #include #endif @@ -39,12 +40,41 @@ using namespace lld; // This function spawns a background thread to remove the file. // The calling thread returns almost immediately. void lld::unlinkAsync(StringRef path) { + if (!sys::fs::exists(path) || !sys::fs::is_regular_file(path)) + return; + // Removing a file is async on windows. #if defined(_WIN32) + // On Windows co-operative programs can be expected to open LLD's + // output in FILE_SHARE_DELETE mode. This allows us to delete the + // file (by moving it to a temporary filename and then deleting + // it) so that we can link another output file that overwrites + // the existing file, even if the current file is in use. + // + // This is done on a best effort basis - we do not error if the + // operation fails. The consequence is merely that the user + // experiences an inconvenient work-flow. + // + // The code here allows LLD to work on all versions of Windows. + // However, at Windows 10 1903 it seems that the behavior of + // Windows has changed, so that we could simply delete the output + // file. This code should be simplified once support for older + // versions of Windows is dropped. + // + // Warning: It seems that the WINVER and _WIN32_WINNT preprocessor + // defines affect the behavior of the Windows versions of the calls + // we are using here. If this code stops working this is worth + // bearing in mind. + SmallString<128> tmpName; + if (!sys::fs::createUniqueFile(path + "%%%%%%%%.tmp", tmpName)) { + if (!sys::fs::rename(path, tmpName)) + path = tmpName; + else + sys::fs::remove(tmpName); + } sys::fs::remove(path); #else - if (!threadsEnabled || !sys::fs::exists(path) || - !sys::fs::is_regular_file(path)) + if (parallel::strategy.ThreadsRequested == 1) return; // We cannot just remove path from a different thread because we are now going diff --git a/gnu/llvm/lld/Common/Reproduce.cpp b/gnu/llvm/lld/Common/Reproduce.cpp index 24210c42041..00309f58b93 100644 --- a/gnu/llvm/lld/Common/Reproduce.cpp +++ b/gnu/llvm/lld/Common/Reproduce.cpp @@ -24,7 +24,7 @@ using namespace llvm::sys; std::string lld::relativeToRoot(StringRef path) { SmallString<128> abs = path; if (fs::make_absolute(abs)) - return path; + return std::string(path); path::remove_dots(abs, /*remove_dot_dot=*/true); // This is Windows specific. root_name() returns a drive letter @@ -45,13 +45,13 @@ std::string lld::relativeToRoot(StringRef path) { std::string lld::quote(StringRef s) { if (s.contains(' ')) return ("\"" + s + "\"").str(); - return s; + return std::string(s); } // Converts an Arg to a string representation suitable for a response file. // To show an Arg in a diagnostic, use Arg::getAsString() instead. std::string lld::toString(const opt::Arg &arg) { - std::string k = arg.getSpelling(); + std::string k = std::string(arg.getSpelling()); if (arg.getNumValues() == 0) return k; std::string v = quote(arg.getValue()); diff --git a/gnu/llvm/lld/Common/Strings.cpp b/gnu/llvm/lld/Common/Strings.cpp index 627435f141d..17c2c207491 100644 --- a/gnu/llvm/lld/Common/Strings.cpp +++ b/gnu/llvm/lld/Common/Strings.cpp @@ -10,6 +10,7 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/GlobPattern.h" #include #include @@ -26,23 +27,33 @@ std::string lld::demangleItanium(StringRef name) { // does not look like a C++ symbol name to avoid getting unexpected // result for a C symbol that happens to match a mangled type name. if (!name.startswith("_Z")) - return name; + return std::string(name); - return demangle(name); + return demangle(std::string(name)); } -StringMatcher::StringMatcher(ArrayRef pat) { - for (StringRef s : pat) { - Expected pat = GlobPattern::create(s); - if (!pat) - error(toString(pat.takeError())); - else - patterns.push_back(*pat); +SingleStringMatcher::SingleStringMatcher(StringRef Pattern) { + if (Pattern.size() > 2 && Pattern.startswith("\"") && + Pattern.endswith("\"")) { + ExactMatch = true; + ExactPattern = Pattern.substr(1, Pattern.size() - 2); + } else { + Expected Glob = GlobPattern::create(Pattern); + if (!Glob) { + error(toString(Glob.takeError())); + return; + } + ExactMatch = false; + GlobPatternMatcher = *Glob; } } +bool SingleStringMatcher::match(StringRef s) const { + return ExactMatch ? (ExactPattern == s) : GlobPatternMatcher.match(s); +} + bool StringMatcher::match(StringRef s) const { - for (const GlobPattern &pat : patterns) + for (const SingleStringMatcher &pat : patterns) if (pat.match(s)) return true; return false; diff --git a/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp b/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp index 0137feb63f3..9b166a3e130 100644 --- a/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp +++ b/gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp @@ -5,35 +5,26 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// This file exists as a place for global variables defined in LLVM's -// CodeGen/CommandFlags.inc. By putting the resulting object file in -// an archive and linking with it, the definitions will automatically be -// included when needed and skipped when already present. -// -//===----------------------------------------------------------------------===// #include "lld/Common/TargetOptionsCommandFlags.h" -#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/Target/TargetOptions.h" -// Define an externally visible version of -// initTargetOptionsFromCodeGenFlags, so that its functionality can be -// used without having to include llvm/CodeGen/CommandFlags.inc, which -// would lead to multiple definitions of the command line flags. +static llvm::codegen::RegisterCodeGenFlags CGF; + llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { - return ::InitTargetOptionsFromCodeGenFlags(); + return llvm::codegen::InitTargetOptionsFromCodeGenFlags(); } llvm::Optional lld::getRelocModelFromCMModel() { - return getRelocModel(); + return llvm::codegen::getExplicitRelocModel(); } llvm::Optional lld::getCodeModelFromCMModel() { - return getCodeModel(); + return llvm::codegen::getExplicitCodeModel(); } -std::string lld::getCPUStr() { return ::getCPUStr(); } +std::string lld::getCPUStr() { return llvm::codegen::getCPUStr(); } -std::vector lld::getMAttrs() { return ::MAttrs; } +std::vector lld::getMAttrs() { return llvm::codegen::getMAttrs(); } diff --git a/gnu/llvm/lld/Common/Timer.cpp b/gnu/llvm/lld/Common/Timer.cpp index 4b7d11003b2..ea221fd86f3 100644 --- a/gnu/llvm/lld/Common/Timer.cpp +++ b/gnu/llvm/lld/Common/Timer.cpp @@ -13,29 +13,22 @@ using namespace lld; using namespace llvm; -ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); } +ScopedTimer::ScopedTimer(Timer &t) : t(&t) { + startTime = std::chrono::high_resolution_clock::now(); +} void ScopedTimer::stop() { if (!t) return; - t->stop(); + t->addToTotal(std::chrono::high_resolution_clock::now() - startTime); t = nullptr; } ScopedTimer::~ScopedTimer() { stop(); } -Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {} -Timer::Timer(llvm::StringRef name, Timer &parent) - : name(name), parent(&parent) {} - -void Timer::start() { - if (parent && total.count() == 0) - parent->children.push_back(this); - startTime = std::chrono::high_resolution_clock::now(); -} - -void Timer::stop() { - total += (std::chrono::high_resolution_clock::now() - startTime); +Timer::Timer(llvm::StringRef name) : name(std::string(name)) {} +Timer::Timer(llvm::StringRef name, Timer &parent) : name(std::string(name)) { + parent.children.push_back(this); } Timer &Timer::root() { @@ -49,7 +42,8 @@ void Timer::print() { // We want to print the grand total under all the intermediate phases, so we // print all children first, then print the total under that. for (const auto &child : children) - child->print(1, totalDuration); + if (child->total > 0) + child->print(1, totalDuration); message(std::string(49, '-')); @@ -58,7 +52,7 @@ void Timer::print() { double Timer::millis() const { return std::chrono::duration_cast>( - total) + std::chrono::nanoseconds(total)) .count(); } @@ -74,6 +68,7 @@ void Timer::print(int depth, double totalDuration, bool recurse) const { if (recurse) { for (const auto &child : children) - child->print(depth + 1, totalDuration); + if (child->total > 0) + child->print(depth + 1, totalDuration); } } diff --git a/gnu/llvm/lld/Common/Version.cpp b/gnu/llvm/lld/Common/Version.cpp index ae10f2f28b2..cd9fcd4f405 100644 --- a/gnu/llvm/lld/Common/Version.cpp +++ b/gnu/llvm/lld/Common/Version.cpp @@ -19,9 +19,16 @@ // Returns a version string, e.g.: // lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef) std::string lld::getLLDVersion() { +#ifdef LLD_VENDOR +#define LLD_VENDOR_DISPLAY LLD_VENDOR " " +#else +#define LLD_VENDOR_DISPLAY +#endif #if defined(LLD_REPOSITORY) && defined(LLD_REVISION) - return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")"; + return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY + " " LLD_REVISION ")"; #else - return "LLD " LLD_VERSION_STRING; + return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING; #endif +#undef LLD_VENDOR_DISPLAY } diff --git a/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp b/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp index 398320af71e..724d668449b 100644 --- a/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp +++ b/gnu/llvm/lld/ELF/AArch64ErrataFix.cpp @@ -44,9 +44,8 @@ using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; // Helper functions to identify instructions and conditions needed to trigger // the Cortex-A53-843419 erratum. @@ -371,7 +370,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, return patchOff; } -class Patch843419Section : public SyntheticSection { +class elf::Patch843419Section : public SyntheticSection { public: Patch843419Section(InputSection *p, uint64_t off); @@ -421,7 +420,7 @@ void Patch843419Section::writeTo(uint8_t *buf) { // Return address is the next instruction after the one we have just copied. uint64_t s = getLDSTAddr() + 4; uint64_t p = patchSym->getVA() + 4; - target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p); + target->relocateNoSym(buf + 4, R_AARCH64_JUMP26, s - p); } void AArch64Err843419Patcher::init() { @@ -645,5 +644,3 @@ bool AArch64Err843419Patcher::createFixes() { } return addressesChanged; } -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/ARMErrataFix.cpp b/gnu/llvm/lld/ELF/ARMErrataFix.cpp index 91cd2b5a2f5..bd6f689b584 100644 --- a/gnu/llvm/lld/ELF/ARMErrataFix.cpp +++ b/gnu/llvm/lld/ELF/ARMErrataFix.cpp @@ -33,9 +33,8 @@ using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; // The documented title for Erratum 657417 is: // "A 32bit branch instruction that spans two 4K regions can result in an @@ -71,7 +70,7 @@ namespace elf { // 00001002 2 - bytes padding // 00001004 __CortexA8657417_00000FFE: B.w func -class Patch657417Section : public SyntheticSection { +class elf::Patch657417Section : public SyntheticSection { public: Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM); @@ -189,7 +188,7 @@ void Patch657417Section::writeTo(uint8_t *buf) { // been altered to point to us! uint64_t s = getThumbDestAddr(getBranchAddr(), instr); uint64_t p = getVA(4); - target->relocateOne(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p); + target->relocateNoSym(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p); } // Given a branch instruction spanning two 4KiB regions, at offset off from the @@ -527,6 +526,3 @@ bool ARMErr657417Patcher::createFixes() { } return addressesChanged; } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/AArch64.cpp b/gnu/llvm/lld/ELF/Arch/AArch64.cpp index df41a12f745..637046e90bb 100644 --- a/gnu/llvm/lld/ELF/Arch/AArch64.cpp +++ b/gnu/llvm/lld/ELF/Arch/AArch64.cpp @@ -17,14 +17,13 @@ using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; // Page(Expr) is the page address of the expression Expr, defined // as (Expr & ~0xFFF). (This applies even if the machine page size // supported by the platform has a different value.) -uint64_t getAArch64Page(uint64_t expr) { +uint64_t elf::getAArch64Page(uint64_t expr) { return expr & ~static_cast(0xFFF); } @@ -45,12 +44,16 @@ public: uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; bool usesOnlyLowPageBits(RelType type) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + 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; - void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -123,6 +126,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_CONDBR19: case R_AARCH64_JUMP26: case R_AARCH64_TSTBR14: + case R_AARCH64_PLT32: return R_PLT_PC; case R_AARCH64_PREL16: case R_AARCH64_PREL32: @@ -208,10 +212,10 @@ void AArch64::writePltHeader(uint8_t *buf) const { uint64_t got = in.gotPlt->getVA(); uint64_t plt = in.plt->getVA(); - relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(got + 16) - getAArch64Page(plt + 4)); - relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); - relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); + relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 4)); + relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); } void AArch64::writePlt(uint8_t *buf, const Symbol &sym, @@ -225,10 +229,10 @@ void AArch64::writePlt(uint8_t *buf, const Symbol &sym, memcpy(buf, inst, sizeof(inst)); uint64_t gotPltEntryAddr = sym.getGotPltVA(); - relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); - relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); - relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); + relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); + relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); } bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, @@ -241,7 +245,8 @@ bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, // ELF for the ARM 64-bit architecture, section Call and Jump relocations // only permits range extension thunks for R_AARCH64_CALL26 and // R_AARCH64_JUMP26 relocation types. - if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 && + type != R_AARCH64_PLT32) return false; uint64_t dst = expr == R_PLT_PC ? s.getPltVA() : s.getVA(a); return !inBranchRange(type, branchAddr, dst); @@ -255,11 +260,13 @@ uint32_t AArch64::getThunkSectionSpacing() const { } bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { - if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 && + type != R_AARCH64_PLT32) return true; // The AArch64 call and unconditional branch instructions have a range of - // +/- 128 MiB. - uint64_t range = 128 * 1024 * 1024; + // +/- 128 MiB. The PLT32 relocation supports a range up to +/- 2 GiB. + uint64_t range = + type == R_AARCH64_PLT32 ? (UINT64_C(1) << 31) : (128 * 1024 * 1024); if (dst > src) { // Immediate of branch is signed. range -= 4; @@ -309,16 +316,21 @@ static void writeSMovWImm(uint8_t *loc, uint32_t imm) { write32le(loc, inst | ((imm & 0xFFFF) << 5)); } -void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void AArch64::relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: - checkIntUInt(loc, val, 16, type); + checkIntUInt(loc, val, 16, rel); write16le(loc, val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: - checkIntUInt(loc, val, 32, type); + checkIntUInt(loc, val, 32, rel); + write32le(loc, val); + break; + case R_AARCH64_PLT32: + checkInt(loc, val, 32, rel); write32le(loc, val); break; case R_AARCH64_ABS64: @@ -332,13 +344,13 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: - checkInt(loc, val, 33, type); + checkInt(loc, val, 33, rel); LLVM_FALLTHROUGH; case R_AARCH64_ADR_PREL_PG_HI21_NC: write32AArch64Addr(loc, val >> 12); break; case R_AARCH64_ADR_PREL_LO21: - checkInt(loc, val, 21, type); + checkInt(loc, val, 21, rel); write32AArch64Addr(loc, val); break; case R_AARCH64_JUMP26: @@ -352,13 +364,13 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { write32le(loc, 0x14000000); LLVM_FALLTHROUGH; case R_AARCH64_CALL26: - checkInt(loc, val, 28, type); + checkInt(loc, val, 28, rel); or32le(loc, (val & 0x0FFFFFFC) >> 2); break; case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: - checkAlignment(loc, val, 4, type); - checkInt(loc, val, 21, type); + checkAlignment(loc, val, 4, rel); + checkInt(loc, val, 21, rel); or32le(loc, (val & 0x1FFFFC) << 3); break; case R_AARCH64_LDST8_ABS_LO12_NC: @@ -367,12 +379,12 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: - checkAlignment(loc, val, 2, type); + checkAlignment(loc, val, 2, rel); or32AArch64Imm(loc, getBits(val, 1, 11)); break; case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: - checkAlignment(loc, val, 4, type); + checkAlignment(loc, val, 4, rel); or32AArch64Imm(loc, getBits(val, 2, 11)); break; case R_AARCH64_LDST64_ABS_LO12_NC: @@ -380,28 +392,28 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: - checkAlignment(loc, val, 8, type); + checkAlignment(loc, val, 8, rel); or32AArch64Imm(loc, getBits(val, 3, 11)); break; case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: - checkAlignment(loc, val, 16, type); + checkAlignment(loc, val, 16, rel); or32AArch64Imm(loc, getBits(val, 4, 11)); break; case R_AARCH64_MOVW_UABS_G0: - checkUInt(loc, val, 16, type); + checkUInt(loc, val, 16, rel); LLVM_FALLTHROUGH; case R_AARCH64_MOVW_UABS_G0_NC: or32le(loc, (val & 0xFFFF) << 5); break; case R_AARCH64_MOVW_UABS_G1: - checkUInt(loc, val, 32, type); + checkUInt(loc, val, 32, rel); LLVM_FALLTHROUGH; case R_AARCH64_MOVW_UABS_G1_NC: or32le(loc, (val & 0xFFFF0000) >> 11); break; case R_AARCH64_MOVW_UABS_G2: - checkUInt(loc, val, 48, type); + checkUInt(loc, val, 48, rel); LLVM_FALLTHROUGH; case R_AARCH64_MOVW_UABS_G2_NC: or32le(loc, (val & 0xFFFF00000000) >> 27); @@ -412,7 +424,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_AARCH64_MOVW_PREL_G0: case R_AARCH64_MOVW_SABS_G0: case R_AARCH64_TLSLE_MOVW_TPREL_G0: - checkInt(loc, val, 17, type); + checkInt(loc, val, 17, rel); LLVM_FALLTHROUGH; case R_AARCH64_MOVW_PREL_G0_NC: case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: @@ -421,7 +433,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_AARCH64_MOVW_PREL_G1: case R_AARCH64_MOVW_SABS_G1: case R_AARCH64_TLSLE_MOVW_TPREL_G1: - checkInt(loc, val, 33, type); + checkInt(loc, val, 33, rel); LLVM_FALLTHROUGH; case R_AARCH64_MOVW_PREL_G1_NC: case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: @@ -430,7 +442,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_AARCH64_MOVW_PREL_G2: case R_AARCH64_MOVW_SABS_G2: case R_AARCH64_TLSLE_MOVW_TPREL_G2: - checkInt(loc, val, 49, type); + checkInt(loc, val, 49, rel); LLVM_FALLTHROUGH; case R_AARCH64_MOVW_PREL_G2_NC: writeSMovWImm(loc, val >> 32); @@ -439,11 +451,11 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { writeSMovWImm(loc, val >> 48); break; case R_AARCH64_TSTBR14: - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); or32le(loc, (val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - checkUInt(loc, val, 24, type); + checkUInt(loc, val, 24, rel); or32AArch64Imm(loc, val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: @@ -455,7 +467,8 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { } } -void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { +void AArch64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] @@ -467,9 +480,9 @@ void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // movk x0, #0x10 // nop // nop - checkUInt(loc, val, 32, type); + checkUInt(loc, val, 32, rel); - switch (type) { + switch (rel.type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: write32le(loc, 0xd503201f); // nop @@ -485,7 +498,8 @@ void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { } } -void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { +void AArch64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] @@ -498,34 +512,35 @@ void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // nop // nop - switch (type) { + switch (rel.type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: write32le(loc, 0xd503201f); // nop break; case R_AARCH64_TLSDESC_ADR_PAGE21: write32le(loc, 0x90000000); // adrp - relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); + relocateNoSym(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); break; case R_AARCH64_TLSDESC_LD64_LO12: write32le(loc, 0xf9400000); // ldr - relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); + relocateNoSym(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { - checkUInt(loc, val, 32, type); +void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + checkUInt(loc, val, 32, rel); - if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { + if (rel.type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { // Generate MOVZ. uint32_t regNo = read32le(loc) & 0x1f; write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5)); return; } - if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { + if (rel.type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { // Generate MOVK. uint32_t regNo = read32le(loc) & 0x1f; write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5)); @@ -593,8 +608,10 @@ AArch64BtiPac::AArch64BtiPac() { // the function in an executable being taken by a shared library. // FIXME: There is a potential optimization to omit the BTI if we detect // that the address of the PLT entry isn't taken. + // The PAC PLT entries require dynamic loader support and this isn't known + // from properties in the objects, so we use the command line flag. btiEntry = btiHeader && !config->shared; - pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + pacEntry = config->zPacPlt; if (btiEntry || pacEntry) { pltEntrySize = 24; @@ -627,10 +644,10 @@ void AArch64BtiPac::writePltHeader(uint8_t *buf) const { } memcpy(buf, pltData, sizeof(pltData)); - relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(got + 16) - getAArch64Page(plt + 8)); - relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); - relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); + relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 8)); + relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); if (!btiHeader) // We didn't add the BTI c instruction so round out size with NOP. memcpy(buf + sizeof(pltData), nopData, sizeof(nopData)); @@ -664,11 +681,10 @@ void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym, uint64_t gotPltEntryAddr = sym.getGotPltVA(); memcpy(buf, addrInst, sizeof(addrInst)); - relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(gotPltEntryAddr) - - getAArch64Page(pltEntryAddr)); - relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); - relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); + relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); + relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); if (pacEntry) memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); @@ -689,7 +705,4 @@ static TargetInfo *getTargetInfo() { return &t; } -TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); } - -} // namespace elf -} // namespace lld +TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } diff --git a/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp b/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp index b42ca774674..3610a38692d 100644 --- a/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp +++ b/gnu/llvm/lld/ELF/Arch/AMDGPU.cpp @@ -17,16 +17,16 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class AMDGPU final : public TargetInfo { public: AMDGPU(); uint32_t calcEFlags() const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; @@ -58,8 +58,8 @@ uint32_t AMDGPU::calcEFlags() const { return ret; } -void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: @@ -108,10 +108,7 @@ RelType AMDGPU::getDynRel(RelType type) const { return R_AMDGPU_NONE; } -TargetInfo *getAMDGPUTargetInfo() { +TargetInfo *elf::getAMDGPUTargetInfo() { static AMDGPU target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/ARM.cpp b/gnu/llvm/lld/ELF/Arch/ARM.cpp index 08cae59b294..fd90557cc4f 100644 --- a/gnu/llvm/lld/ELF/Arch/ARM.cpp +++ b/gnu/llvm/lld/ELF/Arch/ARM.cpp @@ -18,9 +18,8 @@ using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class ARM final : public TargetInfo { @@ -43,7 +42,8 @@ public: int64_t a) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -64,6 +64,7 @@ ARM::ARM() { ipltEntrySize = 16; trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; needsThunks = true; + defaultMaxPageSize = 65536; } uint32_t ARM::calcEFlags() const { @@ -120,6 +121,8 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s, return R_TLSGD_PC; case R_ARM_TLS_LDM32: return R_TLSLD_PC; + case R_ARM_TLS_LDO32: + return R_DTPREL; case R_ARM_BASE_PREL: // B(S) + A - P // FIXME: currently B(S) assumed to be .got, this may not hold for all @@ -131,6 +134,19 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s, case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: return R_PC; + case R_ARM_ALU_PC_G0: + case R_ARM_LDR_PC_G0: + case R_ARM_THM_ALU_PREL_11_0: + case R_ARM_THM_PC8: + case R_ARM_THM_PC12: + return R_ARM_PCA; + case R_ARM_MOVW_BREL_NC: + case R_ARM_MOVW_BREL: + case R_ARM_MOVT_BREL: + case R_ARM_THM_MOVW_BREL_NC: + case R_ARM_THM_MOVW_BREL: + case R_ARM_THM_MOVT_BREL: + return R_ARM_SBREL; case R_ARM_NONE: return R_NONE; case R_ARM_TLS_LE32: @@ -262,7 +278,8 @@ 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 { + uint64_t branchAddr, const Symbol &s, + int64_t /*a*/) const { // If S is an undefined weak symbol and does not have a PLT entry then it // will be resolved as a branch to the next instruction. if (s.isUndefWeak() && !s.isInPlt()) @@ -375,8 +392,82 @@ bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { return distance <= range; } -void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +// Helper to produce message text when LLD detects that a CALL relocation to +// a non STT_FUNC symbol that may result in incorrect interworking between ARM +// or Thumb. +static void stateChangeWarning(uint8_t *loc, RelType relt, const Symbol &s) { + assert(!s.isFunc()); + if (s.isSection()) { + // Section symbols must be defined and in a section. Users cannot change + // the type. Use the section name as getName() returns an empty string. + warn(getErrorLocation(loc) + "branch and link relocation: " + + toString(relt) + " to STT_SECTION symbol " + + cast(s).section->name + " ; interworking not performed"); + } else { + // Warn with hint on how to alter the symbol type. + warn(getErrorLocation(loc) + "branch and link relocation: " + + toString(relt) + " to non STT_FUNC symbol: " + s.getName() + + " interworking not performed; consider using directive '.type " + + s.getName() + + ", %function' to give symbol type STT_FUNC if" + " interworking between ARM and Thumb is required"); + } +} + +// Utility functions taken from ARMAddressingModes.h, only changes are LLD +// coding style. + +// Rotate a 32-bit unsigned value right by a specified amt of bits. +static uint32_t rotr32(uint32_t val, uint32_t amt) { + assert(amt < 32 && "Invalid rotate amount"); + return (val >> amt) | (val << ((32 - amt) & 31)); +} + +// Rotate a 32-bit unsigned value left by a specified amt of bits. +static uint32_t rotl32(uint32_t val, uint32_t amt) { + assert(amt < 32 && "Invalid rotate amount"); + return (val << amt) | (val >> ((32 - amt) & 31)); +} + +// Try to encode a 32-bit unsigned immediate imm with an immediate shifter +// operand, this form is an 8-bit immediate rotated right by an even number of +// bits. We compute the rotate amount to use. If this immediate value cannot be +// handled with a single shifter-op, determine a good rotate amount that will +// take a maximal chunk of bits out of the immediate. +static uint32_t getSOImmValRotate(uint32_t imm) { + // 8-bit (or less) immediates are trivially shifter_operands with a rotate + // of zero. + if ((imm & ~255U) == 0) + return 0; + + // Use CTZ to compute the rotate amount. + unsigned tz = llvm::countTrailingZeros(imm); + + // Rotate amount must be even. Something like 0x200 must be rotated 8 bits, + // not 9. + unsigned rotAmt = tz & ~1; + + // If we can handle this spread, return it. + if ((rotr32(imm, rotAmt) & ~255U) == 0) + return (32 - rotAmt) & 31; // HW rotates right, not left. + + // For values like 0xF000000F, we should ignore the low 6 bits, then + // retry the hunt. + if (imm & 63U) { + unsigned tz2 = countTrailingZeros(imm & ~63U); + unsigned rotAmt2 = tz2 & ~1; + if ((rotr32(imm, rotAmt2) & ~255U) == 0) + return (32 - rotAmt2) & 31; // HW rotates right, not left. + } + + // Otherwise, we have no way to cover this span of bits with a single + // shifter_op immediate. Return a chunk of bits that will be useful to + // handle. + return (32 - rotAmt) & 31; // HW rotates right, not left. +} + +void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: case R_ARM_GOTOFF32: @@ -397,40 +488,49 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { write32le(loc, val); break; case R_ARM_PREL31: - checkInt(loc, val, 31, type); + checkInt(loc, val, 31, rel); write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); break; - case R_ARM_CALL: - // R_ARM_CALL is used for BL and BLX instructions, depending on the - // value of bit 0 of Val, we must select a BL or BLX instruction - if (val & 1) { - // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. + case R_ARM_CALL: { + // R_ARM_CALL is used for BL and BLX instructions, for symbols of type + // STT_FUNC we choose whether to write a BL or BLX depending on the + // value of bit 0 of Val. With bit 0 == 1 denoting Thumb. If the symbol is + // not of type STT_FUNC then we must preserve the original instruction. + // PLT entries are always ARM state so we know we don't need to interwork. + assert(rel.sym); // R_ARM_CALL is always reached via relocate(). + bool bit0Thumb = val & 1; + bool isBlx = (read32le(loc) & 0xfe000000) == 0xfa000000; + // lld 10.0 and before always used bit0Thumb when deciding to write a BLX + // even when type not STT_FUNC. + if (!rel.sym->isFunc() && isBlx != bit0Thumb) + stateChangeWarning(loc, rel.type, *rel.sym); + if (rel.sym->isFunc() ? bit0Thumb : isBlx) { // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' - checkInt(loc, val, 26, type); + checkInt(loc, val, 26, rel); write32le(loc, 0xfa000000 | // opcode ((val & 2) << 23) | // H ((val >> 2) & 0x00ffffff)); // imm24 break; } - if ((read32le(loc) & 0xfe000000) == 0xfa000000) - // BLX (always unconditional) instruction to an ARM Target, select an - // unconditional BL. - write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); + // BLX (always unconditional) instruction to an ARM Target, select an + // unconditional BL. + write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B + } LLVM_FALLTHROUGH; case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - checkInt(loc, val, 26, type); + checkInt(loc, val, 26, rel); write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); break; case R_ARM_THM_JUMP11: - checkInt(loc, val, 12, type); + checkInt(loc, val, 12, rel); write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); break; case R_ARM_THM_JUMP19: // Encoding T3: Val = S:J2:J1:imm6:imm11:0 - checkInt(loc, val, 21, type); + checkInt(loc, val, 21, rel); write16le(loc, (read16le(loc) & 0xfbc0) | // opcode cond ((val >> 10) & 0x0400) | // S @@ -441,20 +541,32 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { ((val >> 5) & 0x2000) | // J1 ((val >> 1) & 0x07ff)); // imm11 break; - case R_ARM_THM_CALL: - // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the - // value of bit 0 of Val, we must select a BL or BLX instruction - if ((val & 1) == 0) { - // Ensure BLX destination is 4-byte aligned. As BLX instruction may - // only be two byte aligned. This must be done before overflow check + case R_ARM_THM_CALL: { + // R_ARM_THM_CALL is used for BL and BLX instructions, for symbols of type + // STT_FUNC we choose whether to write a BL or BLX depending on the + // value of bit 0 of Val. With bit 0 == 0 denoting ARM, if the symbol is + // not of type STT_FUNC then we must preserve the original instruction. + // PLT entries are always ARM state so we know we need to interwork. + assert(rel.sym); // R_ARM_THM_CALL is always reached via relocate(). + bool bit0Thumb = val & 1; + bool isBlx = (read16le(loc + 2) & 0x1000) == 0; + // lld 10.0 and before always used bit0Thumb when deciding to write a BLX + // even when type not STT_FUNC. PLT entries generated by LLD are always ARM. + if (!rel.sym->isFunc() && !rel.sym->isInPlt() && isBlx == bit0Thumb) + stateChangeWarning(loc, rel.type, *rel.sym); + if (rel.sym->isFunc() || rel.sym->isInPlt() ? !bit0Thumb : isBlx) { + // We are writing a BLX. Ensure BLX destination is 4-byte aligned. As + // the BLX instruction may only be two byte aligned. This must be done + // before overflow check. val = alignTo(val, 4); + write16le(loc + 2, read16le(loc + 2) & ~0x1000); + } else { + write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | 1 << 12); } - // Bit 12 is 0 for BLX, 1 for BL - write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - checkInt(loc, val, 23, type); + checkInt(loc, val, 23, rel); write16le(loc, 0xf000 | // opcode ((val >> 12) & 0x07ff)); // imm11 @@ -464,11 +576,12 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { ((val >> 1) & 0x07ff)); // imm11 break; } + } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 - checkInt(loc, val, 25, type); + checkInt(loc, val, 25, rel); write16le(loc, 0xf000 | // opcode ((val >> 14) & 0x0400) | // S @@ -481,16 +594,19 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVW_PREL_NC: + case R_ARM_MOVW_BREL_NC: write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) | (val & 0x0fff)); break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: + case R_ARM_MOVT_BREL: write32le(loc, (read32le(loc) & ~0x000f0fff) | (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff)); break; case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVT_PREL: + case R_ARM_THM_MOVT_BREL: // Encoding T1: A = imm4:i:imm3:imm8 write16le(loc, 0xf2c0 | // opcode @@ -503,6 +619,7 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVW_PREL_NC: + case R_ARM_THM_MOVW_BREL_NC: // Encoding T3: A = imm4:i:imm3:imm8 write16le(loc, 0xf240 | // opcode @@ -513,8 +630,92 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { ((val << 4) & 0x7000) | // imm3 (val & 0x00ff)); // imm8 break; + case R_ARM_ALU_PC_G0: { + // ADR (literal) add = bit23, sub = bit22 + // literal is a 12-bit modified immediate, made up of a 4-bit even rotate + // right and an 8-bit immediate. The code-sequence here is derived from + // ARMAddressingModes.h in llvm/Target/ARM/MCTargetDesc. In our case we + // want to give an error if we cannot encode the constant. + uint32_t opcode = 0x00800000; + if (val >> 63) { + opcode = 0x00400000; + val = ~val + 1; + } + if ((val & ~255U) != 0) { + uint32_t rotAmt = getSOImmValRotate(val); + // Error if we cannot encode this with a single shift + if (rotr32(~255U, rotAmt) & val) + error(getErrorLocation(loc) + "unencodeable immediate " + + Twine(val).str() + " for relocation " + toString(rel.type)); + val = rotl32(val, rotAmt) | ((rotAmt >> 1) << 8); + } + write32le(loc, (read32le(loc) & 0xff0ff000) | opcode | val); + break; + } + case R_ARM_LDR_PC_G0: { + // R_ARM_LDR_PC_G0 is S + A - P, we have ((S + A) | T) - P, if S is a + // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear + // bottom bit to recover S + A - P. + if (rel.sym->isFunc()) + val &= ~0x1; + // LDR (literal) u = bit23 + int64_t imm = val; + uint32_t u = 0x00800000; + if (imm < 0) { + imm = -imm; + u = 0; + } + checkUInt(loc, imm, 12, rel); + write32le(loc, (read32le(loc) & 0xff7ff000) | u | imm); + break; + } + case R_ARM_THM_ALU_PREL_11_0: { + // ADR encoding T2 (sub), T3 (add) i:imm3:imm8 + int64_t imm = val; + uint16_t sub = 0; + if (imm < 0) { + imm = -imm; + sub = 0x00a0; + } + checkUInt(loc, imm, 12, rel); + write16le(loc, (read16le(loc) & 0xfb0f) | sub | (imm & 0x800) >> 1); + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | (imm & 0x700) << 4 | (imm & 0xff)); + break; + } + case R_ARM_THM_PC8: + // ADR and LDR literal encoding T1 positive offset only imm8:00 + // R_ARM_THM_PC8 is S + A - Pa, we have ((S + A) | T) - Pa, if S is a + // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear + // bottom bit to recover S + A - Pa. + if (rel.sym->isFunc()) + val &= ~0x1; + checkUInt(loc, val, 10, rel); + checkAlignment(loc, val, 4, rel); + write16le(loc, (read16le(loc) & 0xff00) | (val & 0x3fc) >> 2); + break; + case R_ARM_THM_PC12: { + // LDR (literal) encoding T2, add = (U == '1') imm12 + // imm12 is unsigned + // R_ARM_THM_PC12 is S + A - Pa, we have ((S + A) | T) - Pa, if S is a + // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear + // bottom bit to recover S + A - Pa. + if (rel.sym->isFunc()) + val &= ~0x1; + int64_t imm12 = val; + uint16_t u = 0x0080; + if (imm12 < 0) { + imm12 = -imm12; + u = 0; + } + checkUInt(loc, imm12, 12, rel); + write16le(loc, read16le(loc) | u); + write16le(loc + 2, (read16le(loc + 2) & 0xf000) | imm12); + break; + } default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + error(getErrorLocation(loc) + "unrecognized relocation " + + toString(rel.type)); } } @@ -582,14 +783,18 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: - case R_ARM_MOVT_PREL: { + case R_ARM_MOVT_PREL: + case R_ARM_MOVW_BREL_NC: + case R_ARM_MOVT_BREL: { uint64_t val = read32le(buf) & 0x000f0fff; return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff)); } case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: - case R_ARM_THM_MOVT_PREL: { + case R_ARM_THM_MOVT_PREL: + case R_ARM_THM_MOVW_BREL_NC: + case R_ARM_THM_MOVT_BREL: { // Encoding T3: A = imm4:i:imm3:imm8 uint16_t hi = read16le(buf); uint16_t lo = read16le(buf + 2); @@ -598,13 +803,50 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { ((lo & 0x7000) >> 4) | // imm3 (lo & 0x00ff)); // imm8 } + case R_ARM_ALU_PC_G0: { + // 12-bit immediate is a modified immediate made up of a 4-bit even + // right rotation and 8-bit constant. After the rotation the value + // is zero-extended. When bit 23 is set the instruction is an add, when + // bit 22 is set it is a sub. + uint32_t instr = read32le(buf); + uint32_t val = rotr32(instr & 0xff, ((instr & 0xf00) >> 8) * 2); + return (instr & 0x00400000) ? -val : val; + } + case R_ARM_LDR_PC_G0: { + // ADR (literal) add = bit23, sub = bit22 + // LDR (literal) u = bit23 unsigned imm12 + bool u = read32le(buf) & 0x00800000; + uint32_t imm12 = read32le(buf) & 0xfff; + return u ? imm12 : -imm12; + } + case R_ARM_THM_ALU_PREL_11_0: { + // Thumb2 ADR, which is an alias for a sub or add instruction with an + // unsigned immediate. + // ADR encoding T2 (sub), T3 (add) i:imm3:imm8 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + uint64_t imm = (hi & 0x0400) << 1 | // i + (lo & 0x7000) >> 4 | // imm3 + (lo & 0x00ff); // imm8 + // For sub, addend is negative, add is positive. + return (hi & 0x00f0) ? -imm : imm; + } + case R_ARM_THM_PC8: + // ADR and LDR (literal) encoding T1 + // From ELF for the ARM Architecture the initial signed addend is formed + // from an unsigned field using expression (((imm8:00 + 4) & 0x3ff) – 4) + // this trick permits the PC bias of -4 to be encoded using imm8 = 0xff + return ((((read16le(buf) & 0xff) << 2) + 4) & 0x3ff) - 4; + case R_ARM_THM_PC12: { + // LDR (literal) encoding T2, add = (U == '1') imm12 + bool u = read16le(buf) & 0x0080; + uint64_t imm12 = read16le(buf + 2) & 0x0fff; + return u ? imm12 : -imm12; + } } } -TargetInfo *getARMTargetInfo() { +TargetInfo *elf::getARMTargetInfo() { static ARM target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/AVR.cpp b/gnu/llvm/lld/ELF/Arch/AVR.cpp index cb33ff448ba..4513a970b32 100644 --- a/gnu/llvm/lld/ELF/Arch/AVR.cpp +++ b/gnu/llvm/lld/ELF/Arch/AVR.cpp @@ -36,9 +36,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class AVR final : public TargetInfo { @@ -46,7 +45,8 @@ public: AVR(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -54,11 +54,131 @@ AVR::AVR() { noneRel = R_AVR_NONE; } RelExpr AVR::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { - return R_ABS; + switch (type) { + case R_AVR_7_PCREL: + case R_AVR_13_PCREL: + return R_PC; + default: + return R_ABS; + } } -void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +static void writeLDI(uint8_t *loc, uint64_t val) { + write16le(loc, (read16le(loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f)); +} + +void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { + case R_AVR_8: + checkUInt(loc, val, 8, rel); + *loc = val; + break; + case R_AVR_16: + // Note: this relocation is often used between code and data space, which + // are 0x800000 apart in the output ELF file. The bitmask cuts off the high + // bit. + write16le(loc, val & 0xffff); + break; + case R_AVR_16_PM: + checkAlignment(loc, val, 2, rel); + checkUInt(loc, val >> 1, 16, rel); + write16le(loc, val >> 1); + break; + case R_AVR_32: + checkUInt(loc, val, 32, rel); + write32le(loc, val); + break; + + case R_AVR_LDI: + checkUInt(loc, val, 8, rel); + writeLDI(loc, val & 0xff); + break; + + case R_AVR_LO8_LDI_NEG: + writeLDI(loc, -val & 0xff); + break; + case R_AVR_LO8_LDI: + writeLDI(loc, val & 0xff); + break; + case R_AVR_HI8_LDI_NEG: + writeLDI(loc, (-val >> 8) & 0xff); + break; + case R_AVR_HI8_LDI: + writeLDI(loc, (val >> 8) & 0xff); + break; + case R_AVR_HH8_LDI_NEG: + writeLDI(loc, (-val >> 16) & 0xff); + break; + case R_AVR_HH8_LDI: + writeLDI(loc, (val >> 16) & 0xff); + break; + case R_AVR_MS8_LDI_NEG: + writeLDI(loc, (-val >> 24) & 0xff); + break; + case R_AVR_MS8_LDI: + writeLDI(loc, (val >> 24) & 0xff); + break; + + case R_AVR_LO8_LDI_PM: + checkAlignment(loc, val, 2, rel); + writeLDI(loc, (val >> 1) & 0xff); + break; + case R_AVR_HI8_LDI_PM: + checkAlignment(loc, val, 2, rel); + writeLDI(loc, (val >> 9) & 0xff); + break; + case R_AVR_HH8_LDI_PM: + checkAlignment(loc, val, 2, rel); + writeLDI(loc, (val >> 17) & 0xff); + break; + + case R_AVR_LO8_LDI_PM_NEG: + checkAlignment(loc, val, 2, rel); + writeLDI(loc, (-val >> 1) & 0xff); + break; + case R_AVR_HI8_LDI_PM_NEG: + checkAlignment(loc, val, 2, rel); + writeLDI(loc, (-val >> 9) & 0xff); + break; + case R_AVR_HH8_LDI_PM_NEG: + checkAlignment(loc, val, 2, rel); + writeLDI(loc, (-val >> 17) & 0xff); + break; + + case R_AVR_PORT5: + checkUInt(loc, val, 5, rel); + write16le(loc, (read16le(loc) & 0xff07) | (val << 3)); + break; + case R_AVR_PORT6: + checkUInt(loc, val, 6, rel); + write16le(loc, (read16le(loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f)); + break; + + // Since every jump destination is word aligned we gain an extra bit + case R_AVR_7_PCREL: { + checkInt(loc, val, 7, rel); + checkAlignment(loc, val, 2, rel); + const uint16_t target = (val - 2) >> 1; + write16le(loc, (read16le(loc) & 0xfc07) | ((target & 0x7f) << 3)); + break; + } + case R_AVR_13_PCREL: { + checkAlignment(loc, val, 2, rel); + const uint16_t target = (val - 2) >> 1; + write16le(loc, (read16le(loc) & 0xf000) | (target & 0xfff)); + break; + } + + case R_AVR_6: + checkInt(loc, val, 6, rel); + write16le(loc, (read16le(loc) & 0xd3f8) | (val & 0x20) << 8 | + (val & 0x18) << 7 | (val & 0x07)); + break; + case R_AVR_6_ADIW: + checkInt(loc, val, 6, rel); + write16le(loc, (read16le(loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F)); + break; + case R_AVR_CALL: { uint16_t hi = val >> 17; uint16_t lo = val >> 1; @@ -67,14 +187,12 @@ void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; } default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + error(getErrorLocation(loc) + "unrecognized relocation " + + toString(rel.type)); } } -TargetInfo *getAVRTargetInfo() { +TargetInfo *elf::getAVRTargetInfo() { static AVR target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/Hexagon.cpp b/gnu/llvm/lld/ELF/Arch/Hexagon.cpp index 106bc9bab5b..7740ce9a71e 100644 --- a/gnu/llvm/lld/ELF/Arch/Hexagon.cpp +++ b/gnu/llvm/lld/ELF/Arch/Hexagon.cpp @@ -19,9 +19,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class Hexagon final : public TargetInfo { @@ -31,7 +30,8 @@ public: RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; RelType getDynRel(RelType type) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; @@ -55,6 +55,8 @@ Hexagon::Hexagon() { defaultMaxPageSize = 0x10000; noneRel = R_HEX_NONE; tlsGotRel = R_HEX_TPREL_32; + tlsModuleIndexRel = R_HEX_DTPMOD_32; + tlsOffsetRel = R_HEX_DTPREL_32; } uint32_t Hexagon::calcEFlags() const { @@ -102,6 +104,7 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, case R_HEX_32_6_X: case R_HEX_HI16: case R_HEX_LO16: + case R_HEX_DTPREL_32: return R_ABS; case R_HEX_B9_PCREL: case R_HEX_B13_PCREL: @@ -115,12 +118,19 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, case R_HEX_PLT_B22_PCREL: case R_HEX_B22_PCREL_X: case R_HEX_B32_PCREL_X: + case R_HEX_GD_PLT_B22_PCREL: + case R_HEX_GD_PLT_B22_PCREL_X: + case R_HEX_GD_PLT_B32_PCREL_X: return R_PLT_PC; case R_HEX_IE_32_6_X: case R_HEX_IE_16_X: case R_HEX_IE_HI16: case R_HEX_IE_LO16: return R_GOT; + case R_HEX_GD_GOT_11_X: + case R_HEX_GD_GOT_16_X: + case R_HEX_GD_GOT_32_6_X: + return R_TLSGD_GOTPLT; case R_HEX_GOTREL_11_X: case R_HEX_GOTREL_16_X: case R_HEX_GOTREL_32_6_X: @@ -152,6 +162,13 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, } } +static bool isDuplex(uint32_t insn) { + // Duplex forms have a fixed mask and parse bits 15:14 are always + // zero. Non-duplex insns will always have at least one bit set in the + // parse field. + return (0xC000 & insn) == 0; +} + static uint32_t findMaskR6(uint32_t insn) { // There are (arguably too) many relocation masks for the DSP's // R_HEX_6_X type. The table below is used to select the correct mask @@ -176,10 +193,7 @@ static uint32_t findMaskR6(uint32_t insn) { {0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0}, {0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}}; - // Duplex forms have a fixed mask and parse bits 15:14 are always - // zero. Non-duplex insns will always have at least one bit set in the - // parse field. - if ((0xC000 & insn) == 0x0) + if (isDuplex(insn)) return 0x03f00000; for (InstructionMask i : r6) @@ -215,6 +229,9 @@ static uint32_t findMaskR16(uint32_t insn) { if ((0xff000000 & insn) == 0xb0000000) return 0x0fe03fe0; + if (isDuplex(insn)) + return 0x03f00000; + error("unrecognized instruction for R_HEX_16_X relocation: 0x" + utohexstr(insn)); return 0; @@ -222,8 +239,9 @@ static uint32_t findMaskR16(uint32_t insn) { static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } -void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void Hexagon::relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { case R_HEX_NONE: break; case R_HEX_6_PCREL_X: @@ -240,6 +258,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { or32le(loc, applyMask(0x00203fe0, val & 0x3f)); break; case R_HEX_11_X: + case R_HEX_GD_GOT_11_X: case R_HEX_IE_GOT_11_X: case R_HEX_GOT_11_X: case R_HEX_GOTREL_11_X: @@ -252,6 +271,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_HEX_16_X: // These relocs only have 6 effective bits. case R_HEX_IE_16_X: case R_HEX_IE_GOT_16_X: + case R_HEX_GD_GOT_16_X: case R_HEX_GOT_16_X: case R_HEX_GOTREL_16_X: case R_HEX_TPREL_16_X: @@ -262,9 +282,11 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; case R_HEX_32: case R_HEX_32_PCREL: + case R_HEX_DTPREL_32: or32le(loc, val); break; case R_HEX_32_6_X: + case R_HEX_GD_GOT_32_6_X: case R_HEX_GOT_32_6_X: case R_HEX_GOTREL_32_6_X: case R_HEX_IE_GOT_32_6_X: @@ -273,32 +295,35 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_B9_PCREL: - checkInt(loc, val, 11, type); + checkInt(loc, val, 11, rel); or32le(loc, applyMask(0x003000fe, val >> 2)); break; case R_HEX_B9_PCREL_X: or32le(loc, applyMask(0x003000fe, val & 0x3f)); break; case R_HEX_B13_PCREL: - checkInt(loc, val, 15, type); + checkInt(loc, val, 15, rel); or32le(loc, applyMask(0x00202ffe, val >> 2)); break; case R_HEX_B15_PCREL: - checkInt(loc, val, 17, type); + checkInt(loc, val, 17, rel); or32le(loc, applyMask(0x00df20fe, val >> 2)); break; case R_HEX_B15_PCREL_X: or32le(loc, applyMask(0x00df20fe, val & 0x3f)); break; case R_HEX_B22_PCREL: + case R_HEX_GD_PLT_B22_PCREL: case R_HEX_PLT_B22_PCREL: - checkInt(loc, val, 22, type); + checkInt(loc, val, 22, rel); or32le(loc, applyMask(0x1ff3ffe, val >> 2)); break; case R_HEX_B22_PCREL_X: + case R_HEX_GD_PLT_B22_PCREL_X: or32le(loc, applyMask(0x1ff3ffe, val & 0x3f)); break; case R_HEX_B32_PCREL_X: + case R_HEX_GD_PLT_B32_PCREL_X: or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_GOTREL_HI16: @@ -335,8 +360,8 @@ void Hexagon::writePltHeader(uint8_t *buf) const { // Offset from PLT0 to the GOT. uint64_t off = in.gotPlt->getVA() - in.plt->getVA(); - relocateOne(buf, R_HEX_B32_PCREL_X, off); - relocateOne(buf + 4, R_HEX_6_PCREL_X, off); + relocateNoSym(buf, R_HEX_B32_PCREL_X, off); + relocateNoSym(buf + 4, R_HEX_6_PCREL_X, off); } void Hexagon::writePlt(uint8_t *buf, const Symbol &sym, @@ -350,8 +375,8 @@ void Hexagon::writePlt(uint8_t *buf, const Symbol &sym, memcpy(buf, inst, sizeof(inst)); uint64_t gotPltEntryAddr = sym.getGotPltVA(); - relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); - relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); + relocateNoSym(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); + relocateNoSym(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); } RelType Hexagon::getDynRel(RelType type) const { @@ -360,10 +385,7 @@ RelType Hexagon::getDynRel(RelType type) const { return R_HEX_NONE; } -TargetInfo *getHexagonTargetInfo() { +TargetInfo *elf::getHexagonTargetInfo() { static Hexagon target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/MSP430.cpp b/gnu/llvm/lld/ELF/Arch/MSP430.cpp index f03e8181923..4af90b40a34 100644 --- a/gnu/llvm/lld/ELF/Arch/MSP430.cpp +++ b/gnu/llvm/lld/ELF/Arch/MSP430.cpp @@ -26,9 +26,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class MSP430 final : public TargetInfo { @@ -36,7 +35,8 @@ public: MSP430(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -60,38 +60,36 @@ RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, } } -void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void MSP430::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_MSP430_8: - checkIntUInt(loc, val, 8, type); + checkIntUInt(loc, val, 8, rel); *loc = val; break; case R_MSP430_16: case R_MSP430_16_PCREL: case R_MSP430_16_BYTE: case R_MSP430_16_PCREL_BYTE: - checkIntUInt(loc, val, 16, type); + checkIntUInt(loc, val, 16, rel); write16le(loc, val); break; case R_MSP430_32: - checkIntUInt(loc, val, 32, type); + checkIntUInt(loc, val, 32, rel); write32le(loc, val); break; case R_MSP430_10_PCREL: { int16_t offset = ((int16_t)val >> 1) - 1; - checkInt(loc, offset, 10, type); + checkInt(loc, offset, 10, rel); write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF)); break; } default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + error(getErrorLocation(loc) + "unrecognized relocation " + + toString(rel.type)); } } -TargetInfo *getMSP430TargetInfo() { +TargetInfo *elf::getMSP430TargetInfo() { static MSP430 target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/Mips.cpp b/gnu/llvm/lld/ELF/Arch/Mips.cpp index ed6f4ca2413..fd1c5f50773 100644 --- a/gnu/llvm/lld/ELF/Arch/Mips.cpp +++ b/gnu/llvm/lld/ELF/Arch/Mips.cpp @@ -18,9 +18,9 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { namespace { template class MIPS final : public TargetInfo { public: @@ -37,7 +37,8 @@ public: bool needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s, int64_t a) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; bool usesOnlyLowPageBits(RelType type) const override; }; } // namespace @@ -274,12 +275,12 @@ template void MIPS::writePltHeader(uint8_t *buf) const { write16(buf + 18, 0x0f83); // move $28, $3 write16(buf + 20, 0x472b); // jalrc $25 write16(buf + 22, 0x0c00); // nop - relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); + relocateNoSym(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); } else { write16(buf + 18, 0x45f9); // jalrc $25 write16(buf + 20, 0x0f83); // move $28, $3 write16(buf + 22, 0x0c00); // nop - relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); + relocateNoSym(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); } return; } @@ -330,13 +331,13 @@ void MIPS::writePlt(uint8_t *buf, const Symbol &sym, write16(buf + 4, 0xff22); // lw $25, 0($2) write16(buf + 8, 0x0f02); // move $24, $2 write16(buf + 10, 0x4723); // jrc $25 / jr16 $25 - relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); + relocateNoSym(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); } else { write16(buf, 0x7900); // addiupc $2, (GOTPLT) - . write16(buf + 4, 0xff22); // lw $25, 0($2) write16(buf + 8, 0x4599); // jrc $25 / jr16 $25 write16(buf + 10, 0x0f02); // move $24, $2 - relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); + relocateNoSym(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); } return; } @@ -537,8 +538,10 @@ static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) { } template -void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { +void MIPS::relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const { const endianness e = ELFT::TargetEndianness; + RelType type = rel.type; if (ELFT::Is64Bits || config->mipsN32Abi) std::tie(type, val) = calculateMipsRelChain(loc, type, val); @@ -577,7 +580,7 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { if (config->relocatable) { writeValue(loc, val + 0x8000, 16, 16); } else { - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); writeValue(loc, val, 16, 0); } break; @@ -585,7 +588,7 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { if (config->relocatable) { writeShuffleValue(loc, val + 0x8000, 16, 16); } else { - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); writeShuffleValue(loc, val, 16, 0); } break; @@ -596,7 +599,7 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_MIPS_TLS_GD: case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_LDM: - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); LLVM_FALLTHROUGH; case R_MIPS_CALL_LO16: case R_MIPS_GOT_LO16: @@ -610,7 +613,7 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_MICROMIPS_GPREL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_CALL16: @@ -622,7 +625,7 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL7_S2: - checkInt(loc, val, 7, type); + checkInt(loc, val, 7, rel); writeShuffleValue(loc, val, 7, 2); break; case R_MIPS_CALL_HI16: @@ -665,23 +668,23 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // Ignore this optimization relocation for now break; case R_MIPS_PC16: - checkAlignment(loc, val, 4, type); - checkInt(loc, val, 18, type); + checkAlignment(loc, val, 4, rel); + checkInt(loc, val, 18, rel); writeValue(loc, val, 16, 2); break; case R_MIPS_PC19_S2: - checkAlignment(loc, val, 4, type); - checkInt(loc, val, 21, type); + checkAlignment(loc, val, 4, rel); + checkInt(loc, val, 21, rel); writeValue(loc, val, 19, 2); break; case R_MIPS_PC21_S2: - checkAlignment(loc, val, 4, type); - checkInt(loc, val, 23, type); + checkAlignment(loc, val, 4, rel); + checkInt(loc, val, 23, rel); writeValue(loc, val, 21, 2); break; case R_MIPS_PC26_S2: - checkAlignment(loc, val, 4, type); - checkInt(loc, val, 28, type); + checkAlignment(loc, val, 4, rel); + checkInt(loc, val, 28, rel); writeValue(loc, val, 26, 2); break; case R_MIPS_PC32: @@ -689,35 +692,35 @@ void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; case R_MICROMIPS_26_S1: case R_MICROMIPS_PC26_S1: - checkInt(loc, val, 27, type); + checkInt(loc, val, 27, rel); writeShuffleValue(loc, val, 26, 1); break; case R_MICROMIPS_PC7_S1: - checkInt(loc, val, 8, type); + checkInt(loc, val, 8, rel); writeMicroRelocation16(loc, val, 7, 1); break; case R_MICROMIPS_PC10_S1: - checkInt(loc, val, 11, type); + checkInt(loc, val, 11, rel); writeMicroRelocation16(loc, val, 10, 1); break; case R_MICROMIPS_PC16_S1: - checkInt(loc, val, 17, type); + checkInt(loc, val, 17, rel); writeShuffleValue(loc, val, 16, 1); break; case R_MICROMIPS_PC18_S3: - checkInt(loc, val, 21, type); + checkInt(loc, val, 21, rel); writeShuffleValue(loc, val, 18, 3); break; case R_MICROMIPS_PC19_S2: - checkInt(loc, val, 21, type); + checkInt(loc, val, 21, rel); writeShuffleValue(loc, val, 19, 2); break; case R_MICROMIPS_PC21_S1: - checkInt(loc, val, 22, type); + checkInt(loc, val, 22, rel); writeShuffleValue(loc, val, 21, 1); break; case R_MICROMIPS_PC23_S2: - checkInt(loc, val, 25, type); + checkInt(loc, val, 25, rel); writeShuffleValue(loc, val, 23, 2); break; default: @@ -731,7 +734,7 @@ template bool MIPS::usesOnlyLowPageBits(RelType type) const { } // Return true if the symbol is a PIC function. -template bool isMipsPIC(const Defined *sym) { +template bool elf::isMipsPIC(const Defined *sym) { if (!sym->isFunc()) return false; @@ -749,20 +752,17 @@ template bool isMipsPIC(const Defined *sym) { return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; } -template TargetInfo *getMipsTargetInfo() { +template TargetInfo *elf::getMipsTargetInfo() { static MIPS target; return ⌖ } -template TargetInfo *getMipsTargetInfo(); -template TargetInfo *getMipsTargetInfo(); -template TargetInfo *getMipsTargetInfo(); -template TargetInfo *getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); +template TargetInfo *elf::getMipsTargetInfo(); -template bool isMipsPIC(const Defined *); -template bool isMipsPIC(const Defined *); -template bool isMipsPIC(const Defined *); -template bool isMipsPIC(const Defined *); - -} // namespace elf -} // namespace lld +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); diff --git a/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp b/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp index 923458afae0..85329c3bef5 100644 --- a/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp +++ b/gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp @@ -23,8 +23,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { struct ArchTreeEdge { @@ -294,7 +294,7 @@ static uint32_t getArchFlags(ArrayRef files) { return ret; } -template uint32_t calcMipsEFlags() { +template uint32_t elf::calcMipsEFlags() { std::vector v; for (InputFile *f : objectFiles) v.push_back({f, cast>(f)->getObj().getHeader()->e_flags}); @@ -350,7 +350,8 @@ static StringRef getMipsFpAbiName(uint8_t fpAbi) { } } -uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) { +uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, + StringRef fileName) { if (compareMipsFpAbi(newFlag, oldFlag) >= 0) return newFlag; if (compareMipsFpAbi(oldFlag, newFlag) < 0) @@ -366,7 +367,7 @@ template static bool isN32Abi(const InputFile *f) { return false; } -bool isMipsN32Abi(const InputFile *f) { +bool elf::isMipsN32Abi(const InputFile *f) { switch (config->ekind) { case ELF32LEKind: return isN32Abi(f); @@ -381,17 +382,14 @@ bool isMipsN32Abi(const InputFile *f) { } } -bool isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } +bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } -bool isMipsR6() { +bool elf::isMipsR6() { uint32_t arch = config->eflags & EF_MIPS_ARCH; return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; } -template uint32_t calcMipsEFlags(); -template uint32_t calcMipsEFlags(); -template uint32_t calcMipsEFlags(); -template uint32_t calcMipsEFlags(); - -} // namespace elf -} // namespace lld +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); diff --git a/gnu/llvm/lld/ELF/Arch/PPC64.cpp b/gnu/llvm/lld/ELF/Arch/PPC64.cpp index e48a184c9db..71c568088fb 100644 --- a/gnu/llvm/lld/ELF/Arch/PPC64.cpp +++ b/gnu/llvm/lld/ELF/Arch/PPC64.cpp @@ -6,20 +6,21 @@ // //===----------------------------------------------------------------------===// +#include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; static uint64_t ppc64TocOffset = 0x8000; static uint64_t dynamicThreadPointerOffset = 0x8000; @@ -61,7 +62,7 @@ enum DFormOpcd { ADDI = 14 }; -uint64_t getPPC64TocBase() { +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 // .got when we see a relocation that uses it, so for us the start is always @@ -75,7 +76,7 @@ uint64_t getPPC64TocBase() { return tocVA + ppc64TocOffset; } -unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { // The offset is encoded into the 3 most significant bits of the st_other // field, with some special values described in section 3.4.1 of the ABI: // 0 --> Zero offset between the GEP and LEP, and the function does NOT use @@ -100,11 +101,89 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { return 0; } -bool isPPC64SmallCodeModelTocReloc(RelType type) { +bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { // The only small code model relocations that access the .toc section. return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; } +static bool addOptional(StringRef name, uint64_t value, + std::vector &defined) { + Symbol *sym = symtab->find(name); + if (!sym || sym->isDefined()) + return false; + sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL, + STV_HIDDEN, STT_FUNC, value, + /*size=*/0, /*section=*/nullptr}); + defined.push_back(cast(sym)); + return true; +} + +// If from is 14, write ${prefix}14: firstInsn; ${prefix}15: +// firstInsn+0x200008; ...; ${prefix}31: firstInsn+(31-14)*0x200008; $tail +// The labels are defined only if they exist in the symbol table. +static void writeSequence(MutableArrayRef buf, const char *prefix, + int from, uint32_t firstInsn, + ArrayRef tail) { + std::vector defined; + char name[16]; + int first; + uint32_t *ptr = buf.data(); + for (int r = from; r < 32; ++r) { + format("%s%d", prefix, r).snprint(name, sizeof(name)); + if (addOptional(name, 4 * (r - from), defined) && defined.size() == 1) + first = r - from; + write32(ptr++, firstInsn + 0x200008 * (r - from)); + } + for (uint32_t insn : tail) + write32(ptr++, insn); + assert(ptr == &*buf.end()); + + if (defined.empty()) + return; + // The full section content has the extent of [begin, end). We drop unused + // instructions and write [first,end). + auto *sec = make( + nullptr, SHF_ALLOC, SHT_PROGBITS, 4, + makeArrayRef(reinterpret_cast(buf.data() + first), + 4 * (buf.size() - first)), + ".text"); + inputSections.push_back(sec); + for (Defined *sym : defined) { + sym->section = sec; + sym->value -= 4 * first; + } +} + +// Implements some save and restore functions as described by ELF V2 ABI to be +// compatible with GCC. With GCC -Os, when the number of call-saved registers +// exceeds a certain threshold, GCC generates _savegpr0_* _restgpr0_* calls and +// expects the linker to define them. See +// https://sourceware.org/pipermail/binutils/2002-February/017444.html and +// https://sourceware.org/pipermail/binutils/2004-August/036765.html . This is +// weird because libgcc.a would be the natural place. The linker generation +// approach has the advantage that the linker can generate multiple copies to +// avoid long branch thunks. However, we don't consider the advantage +// significant enough to complicate our trunk implementation, so we take the +// simple approach and synthesize .text sections providing the implementation. +void elf::addPPC64SaveRestore() { + static uint32_t savegpr0[20], restgpr0[21], savegpr1[19], restgpr1[19]; + constexpr uint32_t blr = 0x4e800020, mtlr_0 = 0x7c0803a6; + + // _restgpr0_14: ld 14, -144(1); _restgpr0_15: ld 15, -136(1); ... + // Tail: ld 0, 16(1); mtlr 0; blr + writeSequence(restgpr0, "_restgpr0_", 14, 0xe9c1ff70, + {0xe8010010, mtlr_0, blr}); + // _restgpr1_14: ld 14, -144(12); _restgpr1_15: ld 15, -136(12); ... + // Tail: blr + writeSequence(restgpr1, "_restgpr1_", 14, 0xe9ccff70, {blr}); + // _savegpr0_14: std 14, -144(1); _savegpr0_15: std 15, -136(1); ... + // Tail: std 0, 16(1); blr + writeSequence(savegpr0, "_savegpr0_", 14, 0xf9c1ff70, {0xf8010010, blr}); + // _savegpr1_14: std 14, -144(12); _savegpr1_15: std 15, -136(12); ... + // Tail: blr + writeSequence(savegpr1, "_savegpr1_", 14, 0xf9ccff70, {blr}); +} + // Find the R_PPC64_ADDR64 in .rela.toc with matching offset. template static std::pair @@ -137,7 +216,7 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { // When accessing a symbol defined in another translation unit, compilers // reserve a .toc entry, allocate a local label and generate toc-indirect -// instuctions: +// instructions: // // addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA // ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry @@ -155,8 +234,7 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { // ld/lwa 3, 0(3) # load the value from the address // // Returns true if the relaxation is performed. -bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, - uint8_t *bufLoc) { +bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) { assert(config->tocOptimize); if (rel.addend < 0) return false; @@ -186,8 +264,8 @@ bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, if (!isInt<32>(tocRelative)) return false; - // Add PPC64TocOffset that will be subtracted by relocateOne(). - target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset); + // Add PPC64TocOffset that will be subtracted by PPC64::relocate(). + target->relaxGot(bufLoc, rel, tocRelative + ppc64TocOffset); return true; } @@ -205,7 +283,8 @@ public: uint64_t pltEntryAddr) const override; void writeIplt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; void writeGotHeader(uint8_t *buf) const override; bool needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s, @@ -214,11 +293,16 @@ public: bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const override; - void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const override; @@ -292,6 +376,20 @@ 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; +} + PPC64::PPC64() { copyRel = R_PPC64_COPY; gotRel = R_PPC64_GLOB_DAT; @@ -365,11 +463,11 @@ uint32_t PPC64::calcEFlags() const { return 2; } -void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void PPC64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_PPC64_TOC16_HA: // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop". - relocateOne(loc, type, val); + relocate(loc, rel, val); break; case R_PPC64_TOC16_LO_DS: { // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or @@ -378,7 +476,7 @@ void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { if (getPrimaryOpCode(insn) != LD) error("expected a 'ld' for got-indirect to toc-relative relaxing"); writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000); - relocateOne(loc, R_PPC64_TOC16_LO, val); + relocateNoSym(loc, R_PPC64_TOC16_LO, val); break; } default: @@ -386,7 +484,8 @@ void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { } } -void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { +void PPC64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol @@ -402,14 +501,14 @@ void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, x@tprel@l - switch (type) { + switch (rel.type) { case R_PPC64_GOT_TLSGD16_HA: writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 - relocateOne(loc, R_PPC64_TPREL16_HA, val); + relocateNoSym(loc, R_PPC64_TPREL16_HA, val); break; case R_PPC64_TLSGD: write32(loc, 0x60000000); // nop @@ -417,15 +516,16 @@ void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Since we are relocating a half16 type relocation and Loc + 4 points to // the start of an instruction we need to advance the buffer by an extra // 2 bytes on BE. - relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, val); + relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { +void PPC64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. // The local dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol @@ -441,7 +541,7 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, 4096 - switch (type) { + switch (rel.type) { case R_PPC64_GOT_TLSLD16_HA: writeFromHalf16(loc, 0x60000000); // nop break; @@ -458,14 +558,14 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: - relocateOne(loc, type, val); + relocate(loc, rel, val); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); } } -unsigned getPPCDFormOp(unsigned secondaryOp) { +unsigned elf::getPPCDFormOp(unsigned secondaryOp) { switch (secondaryOp) { case LBZX: return LBZ; @@ -490,7 +590,8 @@ unsigned getPPCDFormOp(unsigned secondaryOp) { } } -void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { +void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { // The initial exec code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x @@ -511,7 +612,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // indexed load or store instructions. unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; - switch (type) { + switch (rel.type) { case R_PPC64_GOT_TPREL16_HA: write32(loc - offset, 0x60000000); // nop break; @@ -519,7 +620,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { case R_PPC64_GOT_TPREL16_DS: { uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10 write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13 - relocateOne(loc, R_PPC64_TPREL16_HA, val); + relocateNoSym(loc, R_PPC64_TPREL16_HA, val); break; } case R_PPC64_TLS: { @@ -531,7 +632,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { if (dFormOp == 0) error("unrecognized instruction for IE to LE R_PPC64_TLS"); write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); - relocateOne(loc + offset, R_PPC64_TPREL16_LO, val); + relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val); break; } default: @@ -570,6 +671,8 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_TOC16_HI: case R_PPC64_TOC16_LO: return R_GOTREL; + case R_PPC64_GOT_PCREL34: + return R_GOT_PC; case R_PPC64_TOC16_HA: case R_PPC64_TOC16_LO_DS: return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL; @@ -578,11 +681,14 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, case R_PPC64_REL14: case R_PPC64_REL24: return R_PPC64_CALL_PLT; + case R_PPC64_REL24_NOTOC: + return R_PLT_PC; case R_PPC64_REL16_LO: case R_PPC64_REL16_HA: case R_PPC64_REL16_HI: case R_PPC64_REL32: case R_PPC64_REL64: + case R_PPC64_PCREL34: return R_PC; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_HA: @@ -770,11 +876,8 @@ static bool isTocOptType(RelType type) { } } -void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - // We need to save the original relocation type to use in diagnostics, and - // use the original type to determine if we should toc-optimize the - // instructions being relocated. - RelType originalType = type; +void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + RelType type = rel.type; bool shouldTocOptimize = isTocOptType(type); // For dynamic thread pointer relative, toc-relative, and got-indirect // relocations, proceed in terms of the corresponding ADDR16 relocation type. @@ -782,27 +885,27 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { switch (type) { case R_PPC64_ADDR14: { - checkAlignment(loc, val, 4, type); + checkAlignment(loc, val, 4, rel); // Preserve the AA/LK bits in the branch instruction uint8_t aalk = loc[3]; write16(loc + 2, (aalk & 3) | (val & 0xfffc)); break; } case R_PPC64_ADDR16: - checkIntUInt(loc, val, 16, originalType); + checkIntUInt(loc, val, 16, rel); write16(loc, val); break; case R_PPC64_ADDR32: - checkIntUInt(loc, val, 32, originalType); + checkIntUInt(loc, val, 32, rel); write32(loc, val); break; case R_PPC64_ADDR16_DS: case R_PPC64_TPREL16_DS: { - checkInt(loc, val, 16, originalType); + checkInt(loc, val, 16, rel); // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3; - checkAlignment(loc, lo(val), mask + 1, originalType); + checkAlignment(loc, lo(val), mask + 1, rel); write16(loc, (read16(loc) & mask) | lo(val)); } break; case R_PPC64_ADDR16_HA: @@ -857,7 +960,7 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // DS-form instructions only use bits 30-31. uint32_t insn = readFromHalf16(loc); uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3; - checkAlignment(loc, lo(val), mask + 1, originalType); + checkAlignment(loc, lo(val), mask + 1, rel); if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { // When the high-adjusted part of a toc relocation evaluates to 0, it is // changed into a nop. The lo part then needs to be updated to use the toc @@ -873,11 +976,11 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { } } break; case R_PPC64_TPREL16: - checkInt(loc, val, 16, originalType); + checkInt(loc, val, 16, rel); write16(loc, val); break; case R_PPC64_REL32: - checkInt(loc, val, 32, type); + checkInt(loc, val, 32, rel); write32(loc, val); break; case R_PPC64_ADDR64: @@ -887,21 +990,44 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; case R_PPC64_REL14: { uint32_t mask = 0x0000FFFC; - checkInt(loc, val, 16, type); - checkAlignment(loc, val, 4, type); + checkInt(loc, val, 16, rel); + checkAlignment(loc, val, 4, rel); write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } - case R_PPC64_REL24: { + case R_PPC64_REL24: + case R_PPC64_REL24_NOTOC: { uint32_t mask = 0x03FFFFFC; - checkInt(loc, val, 26, type); - checkAlignment(loc, val, 4, type); + checkInt(loc, val, 26, rel); + checkAlignment(loc, val, 4, rel); write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_DTPREL64: write64(loc, val - dynamicThreadPointerOffset); break; + case R_PPC64_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)); + 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)); + break; + } default: llvm_unreachable("unknown relocation"); } @@ -909,13 +1035,30 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s, int64_t a) const { - if (type != R_PPC64_REL14 && type != R_PPC64_REL24) + if (type != R_PPC64_REL14 && type != R_PPC64_REL24 && + 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 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) @@ -941,7 +1084,7 @@ bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { int64_t offset = dst - src; if (type == R_PPC64_REL14) return isInt<16>(offset); - if (type == R_PPC64_REL24) + if (type == R_PPC64_REL24 || type == R_PPC64_REL24_NOTOC) return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } @@ -972,12 +1115,13 @@ RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, // thread pointer. // Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is // used as the relaxation hint for both steps 2 and 3. -void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { case R_PPC64_GOT_TLSGD16_HA: // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to // addis rT, r2, sym@got@tprel@ha. - relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val); + relocateNoSym(loc, R_PPC64_GOT_TPREL16_HA, val); return; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: { @@ -985,7 +1129,7 @@ void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // ld r3, sym@got@tprel@l(rA) uint32_t ra = (readFromHalf16(loc) & (0x1f << 16)); writeFromHalf16(loc, 0xe8600000 | ra); - relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val); + relocateNoSym(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } case R_PPC64_TLSGD: @@ -1104,10 +1248,7 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, return true; } -TargetInfo *getPPC64TargetInfo() { +TargetInfo *elf::getPPC64TargetInfo() { static PPC64 target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/RISCV.cpp b/gnu/llvm/lld/ELF/Arch/RISCV.cpp index 527f9db0ef2..b340fd00dee 100644 --- a/gnu/llvm/lld/ELF/Arch/RISCV.cpp +++ b/gnu/llvm/lld/ELF/Arch/RISCV.cpp @@ -15,9 +15,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { @@ -33,7 +32,8 @@ public: RelType getDynRel(RelType type) const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // end anonymous namespace @@ -76,6 +76,7 @@ RISCV::RISCV() { noneRel = R_RISCV_NONE; pltRel = R_RISCV_JUMP_SLOT; relativeRel = R_RISCV_RELATIVE; + iRelativeRel = R_RISCV_IRELATIVE; if (config->is64) { symbolicRel = R_RISCV_64; tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64; @@ -257,11 +258,10 @@ static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { return (v & ((1ULL << (begin + 1)) - 1)) >> end; } -void RISCV::relocateOne(uint8_t *loc, const RelType type, - const uint64_t val) const { +void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { const unsigned bits = config->wordsize * 8; - switch (type) { + switch (rel.type) { case R_RISCV_32: write32le(loc, val); return; @@ -270,8 +270,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, return; case R_RISCV_RVC_BRANCH: { - checkInt(loc, static_cast(val) >> 1, 8, type); - checkAlignment(loc, val, 2, type); + checkInt(loc, static_cast(val) >> 1, 8, rel); + checkAlignment(loc, val, 2, rel); uint16_t insn = read16le(loc) & 0xE383; uint16_t imm8 = extractBits(val, 8, 8) << 12; uint16_t imm4_3 = extractBits(val, 4, 3) << 10; @@ -285,8 +285,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, } case R_RISCV_RVC_JUMP: { - checkInt(loc, static_cast(val) >> 1, 11, type); - checkAlignment(loc, val, 2, type); + checkInt(loc, static_cast(val) >> 1, 11, rel); + checkAlignment(loc, val, 2, rel); uint16_t insn = read16le(loc) & 0xE003; uint16_t imm11 = extractBits(val, 11, 11) << 12; uint16_t imm4 = extractBits(val, 4, 4) << 11; @@ -304,7 +304,7 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, case R_RISCV_RVC_LUI: { int64_t imm = SignExtend64(val + 0x800, bits) >> 12; - checkInt(loc, imm, 6, type); + checkInt(loc, imm, 6, rel); if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` write16le(loc, (read16le(loc) & 0x0F83) | 0x4000); } else { @@ -316,8 +316,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, } case R_RISCV_JAL: { - checkInt(loc, static_cast(val) >> 1, 20, type); - checkAlignment(loc, val, 2, type); + checkInt(loc, static_cast(val) >> 1, 20, rel); + checkAlignment(loc, val, 2, rel); uint32_t insn = read32le(loc) & 0xFFF; uint32_t imm20 = extractBits(val, 20, 20) << 31; @@ -331,8 +331,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, } case R_RISCV_BRANCH: { - checkInt(loc, static_cast(val) >> 1, 12, type); - checkAlignment(loc, val, 2, type); + checkInt(loc, static_cast(val) >> 1, 12, rel); + checkAlignment(loc, val, 2, rel); uint32_t insn = read32le(loc) & 0x1FFF07F; uint32_t imm12 = extractBits(val, 12, 12) << 31; @@ -349,10 +349,10 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, case R_RISCV_CALL: case R_RISCV_CALL_PLT: { int64_t hi = SignExtend64(val + 0x800, bits) >> 12; - checkInt(loc, hi, 20, type); + checkInt(loc, hi, 20, rel); if (isInt<20>(hi)) { - relocateOne(loc, R_RISCV_PCREL_HI20, val); - relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val); + relocateNoSym(loc, R_RISCV_PCREL_HI20, val); + relocateNoSym(loc + 4, R_RISCV_PCREL_LO12_I, val); } return; } @@ -364,7 +364,7 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, case R_RISCV_TPREL_HI20: case R_RISCV_HI20: { uint64_t hi = val + 0x800; - checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type); + checkInt(loc, SignExtend64(hi, bits) >> 12, 20, rel); write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000)); return; } @@ -445,10 +445,7 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type, } } -TargetInfo *getRISCVTargetInfo() { +TargetInfo *elf::getRISCVTargetInfo() { static RISCV target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp b/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp index 08ef52099de..f137c21fc89 100644 --- a/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp +++ b/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp @@ -16,9 +16,8 @@ using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class SPARCV9 final : public TargetInfo { @@ -28,7 +27,8 @@ public: const uint8_t *loc) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -54,6 +54,14 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, case R_SPARC_UA32: case R_SPARC_64: case R_SPARC_UA64: + case R_SPARC_H44: + case R_SPARC_M44: + case R_SPARC_L44: + case R_SPARC_HH22: + case R_SPARC_HM10: + case R_SPARC_LM22: + case R_SPARC_HI22: + case R_SPARC_LO10: return R_ABS; case R_SPARC_PC10: case R_SPARC_PC22: @@ -68,6 +76,9 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, return R_PLT_PC; case R_SPARC_NONE: return R_NONE; + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + return R_TLS; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); @@ -75,38 +86,45 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, } } -void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void SPARCV9::relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { case R_SPARC_32: case R_SPARC_UA32: // V-word32 - checkUInt(loc, val, 32, type); + checkUInt(loc, val, 32, rel); write32be(loc, val); break; case R_SPARC_DISP32: // V-disp32 - checkInt(loc, val, 32, type); + checkInt(loc, val, 32, rel); write32be(loc, val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 - checkInt(loc, val, 32, type); + checkInt(loc, val, 32, rel); write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); break; case R_SPARC_22: // V-imm22 - checkUInt(loc, val, 22, type); + checkUInt(loc, val, 22, rel); write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; case R_SPARC_GOT22: case R_SPARC_PC22: + case R_SPARC_LM22: // T-imm22 write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); break; + case R_SPARC_HI22: + // V-imm22 + checkUInt(loc, val >> 10, 22, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); + break; case R_SPARC_WDISP19: // V-disp19 - checkInt(loc, val, 21, type); + checkInt(loc, val, 21, rel); write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); break; case R_SPARC_GOT10: @@ -114,11 +132,45 @@ void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // T-simm10 write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; + case R_SPARC_LO10: + // T-simm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff)); + break; case R_SPARC_64: case R_SPARC_UA64: // V-xword64 write64be(loc, val); break; + case R_SPARC_HH22: + // V-imm22 + checkUInt(loc, val >> 42, 22, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 42) & 0x003fffff)); + break; + case R_SPARC_HM10: + // T-simm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | ((val >> 32) & 0x000003ff)); + break; + case R_SPARC_H44: + // V-imm22 + checkUInt(loc, val >> 22, 22, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 22) & 0x003fffff)); + break; + case R_SPARC_M44: + // T-imm10 + write32be(loc, (read32be(loc) & ~0x000003ff) | ((val >> 12) & 0x000003ff)); + break; + case R_SPARC_L44: + // T-imm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00000fff)); + break; + case R_SPARC_TLS_LE_HIX22: + // T-imm22 + write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff)); + break; + case R_SPARC_TLS_LE_LOX10: + // T-simm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00); + break; default: llvm_unreachable("unknown relocation"); } @@ -139,14 +191,11 @@ void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/, memcpy(buf, pltData, sizeof(pltData)); uint64_t off = pltEntryAddr - in.plt->getVA(); - relocateOne(buf, R_SPARC_22, off); - relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); + relocateNoSym(buf, R_SPARC_22, off); + relocateNoSym(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); } -TargetInfo *getSPARCV9TargetInfo() { +TargetInfo *elf::getSPARCV9TargetInfo() { static SPARCV9 target; return ⌖ } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/X86.cpp b/gnu/llvm/lld/ELF/Arch/X86.cpp index b4daedc0f5d..8c8824d53cc 100644 --- a/gnu/llvm/lld/ELF/Arch/X86.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86.cpp @@ -16,9 +16,8 @@ using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class X86 : public TargetInfo { @@ -35,14 +34,19 @@ public: void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) 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; - void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -262,21 +266,21 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { } } -void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are // being used for some 16-bit programs such as boot loaders, so // we want to support them. - checkIntUInt(loc, val, 8, type); + checkIntUInt(loc, val, 8, rel); *loc = val; break; case R_386_PC8: - checkInt(loc, val, 8, type); + checkInt(loc, val, 8, rel); *loc = val; break; case R_386_16: - checkIntUInt(loc, val, 16, type); + checkIntUInt(loc, val, 16, rel); write16le(loc, val); break; case R_386_PC16: @@ -290,7 +294,7 @@ void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. - checkInt(loc, val, 17, type); + checkInt(loc, val, 17, rel); write16le(loc, val); break; case R_386_32: @@ -312,7 +316,7 @@ void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_386_TLS_LE_32: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: - checkInt(loc, val, 32, type); + checkInt(loc, val, 32, rel); write32le(loc, val); break; default: @@ -320,7 +324,7 @@ void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { } } -void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { +void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt @@ -335,7 +339,7 @@ void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { write32le(loc + 5, val); } -void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { +void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt @@ -352,14 +356,15 @@ void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // In some conditions, relocations can be optimized to avoid using GOT. // This function does that for Initial Exec to Local Exec case. -void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { +void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { // Ulrich's document section 6.2 says that @gotntpoff can // be used with MOVL or ADDL instructions. // @indntpoff is similar to @gotntpoff, but for use in // position dependent code. uint8_t reg = (loc[-1] >> 3) & 7; - if (type == R_386_TLS_IE) { + if (rel.type == R_386_TLS_IE) { if (loc[-1] == 0xa1) { // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" // This case is different from the generic case below because @@ -375,7 +380,7 @@ void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { loc[-1] = 0xc0 | reg; } } else { - assert(type == R_386_TLS_GOTIE); + assert(rel.type == R_386_TLS_GOTIE); if (loc[-2] == 0x8b) { // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" loc[-2] = 0xc7; @@ -389,8 +394,9 @@ void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { write32le(loc, val); } -void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_386_TLS_LDO_32) { +void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_386_TLS_LDO_32) { write32le(loc, val); return; } @@ -608,7 +614,7 @@ void RetpolineNoPic::writePlt(uint8_t *buf, const Symbol &sym, write32le(buf + 22, -off - 26); } -TargetInfo *getX86TargetInfo() { +TargetInfo *elf::getX86TargetInfo() { if (config->zRetpolineplt) { if (config->isPic) { static RetpolinePic t; @@ -626,6 +632,3 @@ TargetInfo *getX86TargetInfo() { static X86 t; return &t; } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Arch/X86_64.cpp b/gnu/llvm/lld/ELF/Arch/X86_64.cpp index 74b72eb9129..24711ec210a 100644 --- a/gnu/llvm/lld/ELF/Arch/X86_64.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86_64.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -18,9 +19,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class X86_64 : public TargetInfo { @@ -35,20 +35,44 @@ public: void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) 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; - void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const override; + bool deleteFallThruJmpInsn(InputSection &is, InputFile *file, + InputSection *nextIS) const override; }; } // namespace +// This is vector of NOP instructions of sizes from 1 to 8 bytes. The +// appropriately sized instructions are used to fill the gaps between sections +// which are executed during fall through. +static const std::vector> nopInstructions = { + {0x90}, + {0x66, 0x90}, + {0x0f, 0x1f, 0x00}, + {0x0f, 0x1f, 0x40, 0x00}, + {0x0f, 0x1f, 0x44, 0x00, 0x00}, + {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00}, + {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00}, + {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}}; + X86_64::X86_64() { copyRel = R_X86_64_COPY; gotRel = R_X86_64_GLOB_DAT; @@ -65,6 +89,7 @@ X86_64::X86_64() { pltEntrySize = 16; ipltEntrySize = 16; trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + nopInstrs = nopInstructions; // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. @@ -73,6 +98,216 @@ X86_64::X86_64() { int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } +// Opcodes for the different X86_64 jmp instructions. +enum JmpInsnOpcode : uint32_t { + J_JMP_32, + J_JNE_32, + J_JE_32, + J_JG_32, + J_JGE_32, + J_JB_32, + J_JBE_32, + J_JL_32, + J_JLE_32, + J_JA_32, + J_JAE_32, + J_UNKNOWN, +}; + +// Given the first (optional) and second byte of the insn's opcode, this +// returns the corresponding enum value. +static JmpInsnOpcode getJmpInsnType(const uint8_t *first, + const uint8_t *second) { + if (*second == 0xe9) + return J_JMP_32; + + if (first == nullptr) + return J_UNKNOWN; + + if (*first == 0x0f) { + switch (*second) { + case 0x84: + return J_JE_32; + case 0x85: + return J_JNE_32; + case 0x8f: + return J_JG_32; + case 0x8d: + return J_JGE_32; + case 0x82: + return J_JB_32; + case 0x86: + return J_JBE_32; + case 0x8c: + return J_JL_32; + case 0x8e: + return J_JLE_32; + case 0x87: + return J_JA_32; + case 0x83: + return J_JAE_32; + } + } + return J_UNKNOWN; +} + +// Return the relocation index for input section IS with a specific Offset. +// Returns the maximum size of the vector if no such relocation is found. +static unsigned getRelocationWithOffset(const InputSection &is, + uint64_t offset) { + unsigned size = is.relocations.size(); + for (unsigned i = size - 1; i + 1 > 0; --i) { + if (is.relocations[i].offset == offset && is.relocations[i].expr != R_NONE) + return i; + } + return size; +} + +// Returns true if R corresponds to a relocation used for a jump instruction. +// TODO: Once special relocations for relaxable jump instructions are available, +// this should be modified to use those relocations. +static bool isRelocationForJmpInsn(Relocation &R) { + return R.type == R_X86_64_PLT32 || R.type == R_X86_64_PC32 || + R.type == R_X86_64_PC8; +} + +// Return true if Relocation R points to the first instruction in the +// next section. +// TODO: Delete this once psABI reserves a new relocation type for fall thru +// jumps. +static bool isFallThruRelocation(InputSection &is, InputFile *file, + InputSection *nextIS, Relocation &r) { + if (!isRelocationForJmpInsn(r)) + return false; + + uint64_t addrLoc = is.getOutputSection()->addr + is.outSecOff + r.offset; + uint64_t targetOffset = InputSectionBase::getRelocTargetVA( + file, r.type, r.addend, addrLoc, *r.sym, r.expr); + + // If this jmp is a fall thru, the target offset is the beginning of the + // next section. + uint64_t nextSectionOffset = + nextIS->getOutputSection()->addr + nextIS->outSecOff; + return (addrLoc + 4 + targetOffset) == nextSectionOffset; +} + +// Return the jmp instruction opcode that is the inverse of the given +// opcode. For example, JE inverted is JNE. +static JmpInsnOpcode invertJmpOpcode(const JmpInsnOpcode opcode) { + switch (opcode) { + case J_JE_32: + return J_JNE_32; + case J_JNE_32: + return J_JE_32; + case J_JG_32: + return J_JLE_32; + case J_JGE_32: + return J_JL_32; + case J_JB_32: + return J_JAE_32; + case J_JBE_32: + return J_JA_32; + case J_JL_32: + return J_JGE_32; + case J_JLE_32: + return J_JG_32; + case J_JA_32: + return J_JBE_32; + case J_JAE_32: + return J_JB_32; + default: + return J_UNKNOWN; + } +} + +// Deletes direct jump instruction in input sections that jumps to the +// following section as it is not required. If there are two consecutive jump +// instructions, it checks if they can be flipped and one can be deleted. +// For example: +// .section .text +// a.BB.foo: +// ... +// 10: jne aa.BB.foo +// 16: jmp bar +// aa.BB.foo: +// ... +// +// can be converted to: +// a.BB.foo: +// ... +// 10: je bar #jne flipped to je and the jmp is deleted. +// aa.BB.foo: +// ... +bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, + InputSection *nextIS) const { + const unsigned sizeOfDirectJmpInsn = 5; + + if (nextIS == nullptr) + return false; + + if (is.getSize() < sizeOfDirectJmpInsn) + return false; + + // If this jmp insn can be removed, it is the last insn and the + // relocation is 4 bytes before the end. + unsigned rIndex = getRelocationWithOffset(is, is.getSize() - 4); + if (rIndex == is.relocations.size()) + return false; + + Relocation &r = is.relocations[rIndex]; + + // Check if the relocation corresponds to a direct jmp. + const uint8_t *secContents = is.data().data(); + // If it is not a direct jmp instruction, there is nothing to do here. + if (*(secContents + r.offset - 1) != 0xe9) + return false; + + if (isFallThruRelocation(is, file, nextIS, r)) { + // This is a fall thru and can be deleted. + r.expr = R_NONE; + r.offset = 0; + is.drop_back(sizeOfDirectJmpInsn); + is.nopFiller = true; + return true; + } + + // Now, check if flip and delete is possible. + const unsigned sizeOfJmpCCInsn = 6; + // To flip, there must be atleast one JmpCC and one direct jmp. + if (is.getSize() < sizeOfDirectJmpInsn + sizeOfJmpCCInsn) + return 0; + + unsigned rbIndex = + getRelocationWithOffset(is, (is.getSize() - sizeOfDirectJmpInsn - 4)); + if (rbIndex == is.relocations.size()) + return 0; + + Relocation &rB = is.relocations[rbIndex]; + + const uint8_t *jmpInsnB = secContents + rB.offset - 1; + JmpInsnOpcode jmpOpcodeB = getJmpInsnType(jmpInsnB - 1, jmpInsnB); + if (jmpOpcodeB == J_UNKNOWN) + return false; + + if (!isFallThruRelocation(is, file, nextIS, rB)) + return false; + + // jmpCC jumps to the fall thru block, the branch can be flipped and the + // jmp can be deleted. + JmpInsnOpcode jInvert = invertJmpOpcode(jmpOpcodeB); + if (jInvert == J_UNKNOWN) + return false; + is.jumpInstrMods.push_back({jInvert, (rB.offset - 1), 4}); + // Move R's values to rB except the offset. + rB = {r.expr, r.type, rB.offset, r.addend, r.sym}; + // Cancel R + r.expr = R_NONE; + r.offset = 0; + is.drop_back(sizeOfDirectJmpInsn); + is.nopFiller = true; + return true; +} + RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { if (type == R_X86_64_GOTTPOFF) @@ -177,8 +412,9 @@ RelType X86_64::getDynRel(RelType type) const { return R_X86_64_NONE; } -void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_X86_64_TLSGD) { +void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_X86_64_TLSGD) { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi @@ -201,7 +437,7 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // lea x@tlsgd(%rip), %rax // call *(%rax) // to the following two instructions. - assert(type == R_X86_64_GOTPC32_TLSDESC); + assert(rel.type == R_X86_64_GOTPC32_TLSDESC); if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " "in callq *x@tlsdesc(%rip), %rax"); @@ -217,8 +453,9 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { } } -void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_X86_64_TLSGD) { +void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_X86_64_TLSGD) { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi @@ -241,7 +478,7 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // lea x@tlsgd(%rip), %rax // call *(%rax) // to the following two instructions. - assert(type == R_X86_64_GOTPC32_TLSDESC); + assert(rel.type == R_X86_64_GOTPC32_TLSDESC); if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " "in callq *x@tlsdesc(%rip), %rax"); @@ -258,7 +495,8 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. -void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { +void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &, + uint64_t val) const { uint8_t *inst = loc - 3; uint8_t reg = loc[-1] >> 3; uint8_t *regSlot = loc - 1; @@ -299,12 +537,13 @@ void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { write32le(loc, val + 4); } -void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_X86_64_DTPOFF64) { +void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_X86_64_DTPOFF64) { write64le(loc, val); return; } - if (type == R_X86_64_DTPOFF32) { + if (rel.type == R_X86_64_DTPOFF32) { write32le(loc, val); return; } @@ -347,26 +586,114 @@ void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); } -void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { +// A JumpInstrMod at a specific offset indicates that the jump instruction +// opcode at that offset must be modified. This is specifically used to relax +// jump instructions with basic block sections. This function looks at the +// JumpMod and effects the change. +void X86_64::applyJumpInstrMod(uint8_t *loc, JumpModType type, + unsigned size) const { switch (type) { + case J_JMP_32: + if (size == 4) + *loc = 0xe9; + else + *loc = 0xeb; + break; + case J_JE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x84; + } else + *loc = 0x74; + break; + case J_JNE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x85; + } else + *loc = 0x75; + break; + case J_JG_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8f; + } else + *loc = 0x7f; + break; + case J_JGE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8d; + } else + *loc = 0x7d; + break; + case J_JB_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x82; + } else + *loc = 0x72; + break; + case J_JBE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x86; + } else + *loc = 0x76; + break; + case J_JL_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8c; + } else + *loc = 0x7c; + break; + case J_JLE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8e; + } else + *loc = 0x7e; + break; + case J_JA_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x87; + } else + *loc = 0x77; + break; + case J_JAE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x83; + } else + *loc = 0x73; + break; + case J_UNKNOWN: + llvm_unreachable("Unknown Jump Relocation"); + } +} + +void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_X86_64_8: - checkIntUInt(loc, val, 8, type); + checkIntUInt(loc, val, 8, rel); *loc = val; break; case R_X86_64_PC8: - checkInt(loc, val, 8, type); + checkInt(loc, val, 8, rel); *loc = val; break; case R_X86_64_16: - checkIntUInt(loc, val, 16, type); + checkIntUInt(loc, val, 16, rel); write16le(loc, val); break; case R_X86_64_PC16: - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); write16le(loc, val); break; case R_X86_64_32: - checkUInt(loc, val, 32, type); + checkUInt(loc, val, 32, rel); write32le(loc, val); break; case R_X86_64_32S: @@ -384,7 +711,7 @@ void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: - checkInt(loc, val, 32, type); + checkInt(loc, val, 32, rel); write32le(loc, val); break; case R_X86_64_64: @@ -495,7 +822,7 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, write32le(loc, val); } -void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { +void X86_64::relaxGot(uint8_t *loc, const Relocation &, uint64_t val) const { const uint8_t op = loc[-2]; const uint8_t modRm = loc[-1]; @@ -758,7 +1085,4 @@ static TargetInfo *getTargetInfo() { return &t; } -TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); } - -} // namespace elf -} // namespace lld +TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } diff --git a/gnu/llvm/lld/ELF/CallGraphSort.cpp b/gnu/llvm/lld/ELF/CallGraphSort.cpp index 6dad7c965f1..21c641b5161 100644 --- a/gnu/llvm/lld/ELF/CallGraphSort.cpp +++ b/gnu/llvm/lld/ELF/CallGraphSort.cpp @@ -48,9 +48,8 @@ #include using namespace llvm; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { struct Edge { @@ -263,11 +262,8 @@ DenseMap CallGraphSort::run() { // Sort sections by the profile data provided by -callgraph-profile-file // // This first builds a call graph based on the profile data then merges sections -// according to the C³ huristic. All clusters are then sorted by a density +// according to the C³ heuristic. All clusters are then sorted by a density // metric to further improve locality. -DenseMap computeCallGraphProfileOrder() { +DenseMap elf::computeCallGraphProfileOrder() { return CallGraphSort().run(); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Config.h b/gnu/llvm/lld/ELF/Config.h index 44477e1b2e9..e74a4a0c5b2 100644 --- a/gnu/llvm/lld/ELF/Config.h +++ b/gnu/llvm/lld/ELF/Config.h @@ -17,6 +17,7 @@ #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/GlobPattern.h" #include #include @@ -90,6 +91,7 @@ struct Configuration { uint32_t andFeatures = 0; llvm::CachePruningPolicy thinLTOCachePolicy; llvm::StringMap sectionStartMap; + llvm::StringRef bfdname; llvm::StringRef chroot; llvm::StringRef dynamicLinker; llvm::StringRef dwoDir; @@ -108,11 +110,13 @@ struct Configuration { llvm::StringRef optRemarksPasses; llvm::StringRef optRemarksFormat; llvm::StringRef progName; + llvm::StringRef printArchiveStats; llvm::StringRef printSymbolOrder; llvm::StringRef soName; llvm::StringRef sysroot; llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; + llvm::StringRef ltoBasicBlockSections; std::pair thinLTOObjectSuffixReplace; std::pair thinLTOPrefixReplace; std::string rpath; @@ -121,6 +125,7 @@ struct Configuration { std::vector filterList; std::vector searchPaths; std::vector symbolOrderingFile; + std::vector thinLTOModulesToCompile; std::vector undefined; std::vector dynamicList; std::vector buildIdVector; @@ -140,6 +145,7 @@ struct Configuration { bool checkSections; bool compressDebugSections; bool cref; + std::vector> deadRelocInNonAlloc; bool defineCommon; bool demangle = true; bool dependentLibraries; @@ -152,19 +158,20 @@ struct Configuration { bool exportDynamic; bool fixCortexA53Errata843419; bool fixCortexA8; - bool forceBTI; bool formatBinary = false; bool gcSections; bool gdbIndex; bool gnuHash = false; bool gnuUnique; - bool hasDynamicList = false; bool hasDynSymTab; bool ignoreDataAddressEquality; bool ignoreFunctionAddressEquality; bool ltoCSProfileGenerate; bool ltoDebugPassManager; + bool ltoEmitAsm; bool ltoNewPassManager; + bool ltoUniqueBasicBlockSectionNames; + bool ltoWholeProgramVisibility; bool mergeArmExidx; bool mipsN32Abi = false; bool mmapOutputFile; @@ -174,8 +181,8 @@ struct Configuration { bool nostdlib; bool oFormatBinary; bool omagic; + bool optimizeBBJumps; bool optRemarksWithHotness; - bool pacPlt; bool picThunk; bool pie; bool printGcSections; @@ -183,18 +190,23 @@ struct Configuration { bool relocatable; bool relrPackDynRelocs; bool saveTemps; + llvm::Optional shuffleSectionSeed; bool singleRoRx; bool shared; + bool symbolic; bool isStatic = false; bool sysvHash = false; bool target1Rel; bool trace; bool thinLTOEmitImportsFiles; bool thinLTOIndexOnly; + bool timeTraceEnabled; bool tocOptimize; bool undefinedVersion; + bool unique; bool useAndroidRelrTags = false; bool warnBackrefs; + std::vector warnBackrefsExclude; bool warnCommon; bool warnIfuncTextrel; bool warnMissingEntry; @@ -202,6 +214,7 @@ struct Configuration { bool writeAddends; bool zCombreloc; bool zCopyreloc; + bool zForceBti; bool zForceIbt; bool zGlobal; bool zHazardplt; @@ -214,9 +227,11 @@ struct Configuration { bool zNodlopen; bool zNow; bool zOrigin; + bool zPacPlt; bool zRelro; bool zRodynamic; bool zShstk; + uint8_t zStartStopVisibility; bool zText; bool zRetpolineplt; bool zWxneeded; @@ -234,19 +249,15 @@ struct Configuration { ELFKind ekind = ELFNoneKind; uint16_t emachine = llvm::ELF::EM_NONE; llvm::Optional imageBase; - // commonPageSize and maxPageSize are influenced by nmagic or omagic - // so may be set to 1 if either of those options is given. uint64_t commonPageSize; uint64_t maxPageSize; - // textAlignPageSize is the target max page size for the purpose - // of aligning text sections, which may be unaligned if given nmagic - uint64_t textAlignPageSize; uint64_t mipsGotSize; uint64_t zStackSize; unsigned ltoPartitions; unsigned ltoo; unsigned optimize; - unsigned thinLTOJobs; + StringRef thinLTOJobs; + unsigned timeTraceGranularity; int32_t splitStackAdjustSize; // The following config options do not directly correspond to any diff --git a/gnu/llvm/lld/ELF/DWARF.cpp b/gnu/llvm/lld/ELF/DWARF.cpp index a00189a0e3a..5767f6020f9 100644 --- a/gnu/llvm/lld/ELF/DWARF.cpp +++ b/gnu/llvm/lld/ELF/DWARF.cpp @@ -22,11 +22,16 @@ using namespace llvm; using namespace llvm::object; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { - for (InputSectionBase *sec : obj->getSections()) { + // Get the ELF sections to retrieve sh_flags. See the SHF_GROUP comment below. + ArrayRef objSections = + CHECK(obj->getObj().sections(), obj); + assert(objSections.size() == obj->getSections().size()); + for (auto it : llvm::enumerate(obj->getSections())) { + InputSectionBase *sec = it.value(); if (!sec) continue; @@ -35,7 +40,7 @@ template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { .Case(".debug_addr", &addrSection) .Case(".debug_gnu_pubnames", &gnuPubnamesSection) .Case(".debug_gnu_pubtypes", &gnuPubtypesSection) - .Case(".debug_info", &infoSection) + .Case(".debug_loclists", &loclistsSection) .Case(".debug_ranges", &rangesSection) .Case(".debug_rnglists", &rnglistsSection) .Case(".debug_str_offsets", &strOffsetsSection) @@ -52,6 +57,20 @@ template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { strSection = toStringRef(sec->data()); else if (sec->name == ".debug_line_str") lineStrSection = toStringRef(sec->data()); + else if (sec->name == ".debug_info" && + !(objSections[it.index()].sh_flags & ELF::SHF_GROUP)) { + // In DWARF v5, -fdebug-types-section places type units in .debug_info + // sections in COMDAT groups. They are not compile units and thus should + // be ignored for .gdb_index/diagnostics purposes. + // + // We use a simple heuristic: the compile unit does not have the SHF_GROUP + // flag. If we place compile units in COMDAT groups in the future, we may + // need to perform a lightweight parsing. We drop the SHF_GROUP flag when + // the InputSection was created, so we need to retrieve sh_flags from the + // associated ELF section header. + infoSection.Data = toStringRef(sec->data()); + infoSection.sec = sec; + } } } @@ -99,15 +118,9 @@ LLDDwarfObj::findAux(const InputSectionBase &sec, uint64_t pos, // its zero value will terminate the decoding of .debug_ranges prematurely. Symbol &s = file->getRelocTargetSym(rel); uint64_t val = 0; - if (auto *dr = dyn_cast(&s)) { + if (auto *dr = dyn_cast(&s)) val = dr->value; - // FIXME: We should be consistent about always adding the file - // offset or not. - if (dr->section->flags & ELF::SHF_ALLOC) - val += cast(dr->section)->getOffsetInFile(); - } - DataRefImpl d; d.p = getAddend(rel); return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), @@ -124,10 +137,7 @@ Optional LLDDwarfObj::find(const llvm::DWARFSection &s, return findAux(*sec.sec, pos, sec.sec->template rels()); } -template class LLDDwarfObj; -template class LLDDwarfObj; -template class LLDDwarfObj; -template class LLDDwarfObj; - -} // namespace elf -} // namespace lld +template class elf::LLDDwarfObj; +template class elf::LLDDwarfObj; +template class elf::LLDDwarfObj; +template class elf::LLDDwarfObj; diff --git a/gnu/llvm/lld/ELF/DWARF.h b/gnu/llvm/lld/ELF/DWARF.h index 51ec9092f17..900c63de26f 100644 --- a/gnu/llvm/lld/ELF/DWARF.h +++ b/gnu/llvm/lld/ELF/DWARF.h @@ -32,6 +32,14 @@ public: f(infoSection); } + InputSection *getInfoSection() const { + return cast(infoSection.sec); + } + + const llvm::DWARFSection &getLoclistsSection() const override { + return loclistsSection; + } + const llvm::DWARFSection &getRangesSection() const override { return rangesSection; } @@ -52,11 +60,11 @@ public: return addrSection; } - const llvm::DWARFSection &getGnuPubnamesSection() const override { + const LLDDWARFSection &getGnuPubnamesSection() const override { return gnuPubnamesSection; } - const llvm::DWARFSection &getGnuPubtypesSection() const override { + const LLDDWARFSection &getGnuPubtypesSection() const override { return gnuPubtypesSection; } @@ -81,6 +89,7 @@ private: LLDDWARFSection gnuPubnamesSection; LLDDWARFSection gnuPubtypesSection; LLDDWARFSection infoSection; + LLDDWARFSection loclistsSection; LLDDWARFSection rangesSection; LLDDWARFSection rnglistsSection; LLDDWARFSection strOffsetsSection; diff --git a/gnu/llvm/lld/ELF/Driver.cpp b/gnu/llvm/lld/ELF/Driver.cpp index ce95a038589..4637a3b306d 100644 --- a/gnu/llvm/lld/ELF/Driver.cpp +++ b/gnu/llvm/lld/ELF/Driver.cpp @@ -43,7 +43,6 @@ #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" -#include "lld/Common/Threads.h" #include "lld/Common/Version.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringExtras.h" @@ -53,9 +52,11 @@ #include "llvm/Support/Compression.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.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/Support/raw_ostream.h" #include #include @@ -65,18 +66,17 @@ using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::support; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { - -Configuration *config; -LinkerDriver *driver; +Configuration *elf::config; +LinkerDriver *elf::driver; static void setConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args); -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { +bool elf::link(ArrayRef args, bool canExitEarly, + raw_ostream &stdoutOS, raw_ostream &stderrOS) { lld::stdoutOS = &stdoutOS; lld::stderrOS = &stderrOS; @@ -89,10 +89,13 @@ bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, inputSections.clear(); outputSections.clear(); + archiveFiles.clear(); binaryFiles.clear(); bitcodeFiles.clear(); + lazyObjFiles.clear(); objectFiles.clear(); sharedFiles.clear(); + backwardReferences.clear(); config = make(); driver = make(); @@ -139,7 +142,6 @@ static std::tuple parseEmulation(StringRef emul) { .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) - .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) @@ -148,6 +150,7 @@ static std::tuple parseEmulation(StringRef emul) { .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) .Case("elf_i386", {ELF32LEKind, EM_386}) .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) + .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9}) .Default({ELFNoneKind, EM_NONE}); if (ret.first == ELFNoneKind) @@ -351,9 +354,9 @@ static void checkOptions() { error("-z force-ibt may not be used with -z retpolineplt"); if (config->emachine != EM_AARCH64) { - if (config->pacPlt) + if (config->zPacPlt) error("-z pac-plt only supported on AArch64"); - if (config->forceBTI) + if (config->zForceBti) error("-z force-bti only supported on AArch64"); } } @@ -408,6 +411,24 @@ static GnuStackKind getZGnuStack(opt::InputArgList &args) { return GnuStackKind::NoExec; } +static uint8_t getZStartStopVisibility(opt::InputArgList &args) { + for (auto *arg : args.filtered_reverse(OPT_z)) { + std::pair kv = StringRef(arg->getValue()).split('='); + if (kv.first == "start-stop-visibility") { + if (kv.second == "default") + return STV_DEFAULT; + else if (kv.second == "internal") + return STV_INTERNAL; + else if (kv.second == "hidden") + return STV_HIDDEN; + else if (kv.second == "protected") + return STV_PROTECTED; + error("unknown -z start-stop-visibility= value: " + StringRef(kv.second)); + } + } + return STV_PROTECTED; +} + static bool isKnownZFlag(StringRef s) { return s == "combreloc" || s == "copyreloc" || s == "defs" || s == "execstack" || s == "force-bti" || s == "force-ibt" || @@ -418,13 +439,14 @@ static bool isKnownZFlag(StringRef s) { s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" || s == "noexecstack" || s == "nognustack" || s == "nokeep-text-section-prefix" || - s == "norelro" || s == "noretpolineplt" || - s == "noseparate-code" || s == "notext" || - s == "now" || s == "origin" || s == "pac-plt" || s == "relro" || - s == "retpolineplt" || s == "rodynamic" || s == "shstk" || - s == "text" || s == "undefs" || s == "wxneeded" || - s.startswith("common-page-size=") || s.startswith("max-page-size=") || - s.startswith("stack-size="); + s == "norelro" || s == "noseparate-code" || s == "notext" || + s == "now" || s == "origin" || s == "pac-plt" || s == "rel" || + s == "rela" || s == "relro" || s == "retpolineplt" || + s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" || + s == "wxneeded" || s.startswith("common-page-size=") || + s.startswith("dead-reloc-in-nonalloc=") || + s.startswith("max-page-size=") || s.startswith("stack-size=") || + s.startswith("start-stop-visibility="); } // Report an error for an unknown -z option. @@ -489,37 +511,57 @@ void LinkerDriver::main(ArrayRef argsArr) { if (args.hasArg(OPT_version)) return; - initLLVM(); - createFiles(args); - if (errorCount()) - return; + // Initialize time trace profiler. + if (config->timeTraceEnabled) + timeTraceProfilerInitialize(config->timeTraceGranularity, config->progName); - inferMachineType(); - setConfigs(args); - checkOptions(); - if (errorCount()) - return; + { + llvm::TimeTraceScope timeScope("ExecuteLinker"); - // The Target instance handles target-specific stuff, such as applying - // relocations or writing a PLT section. It also contains target-dependent - // values such as a default image base address. - target = getTarget(); + initLLVM(); + createFiles(args); + if (errorCount()) + return; - switch (config->ekind) { - case ELF32LEKind: - link(args); - return; - case ELF32BEKind: - link(args); - return; - case ELF64LEKind: - link(args); - return; - case ELF64BEKind: - link(args); - return; - default: - llvm_unreachable("unknown Config->EKind"); + inferMachineType(); + setConfigs(args); + checkOptions(); + if (errorCount()) + return; + + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + switch (config->ekind) { + case ELF32LEKind: + link(args); + break; + case ELF32BEKind: + link(args); + break; + case ELF64LEKind: + link(args); + break; + case ELF64BEKind: + link(args); + break; + default: + llvm_unreachable("unknown Config->EKind"); + } + } + + 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()); + }); + return; + } + + timeTraceProfilerCleanup(); } } @@ -588,9 +630,6 @@ static bool isOutputFormatBinary(opt::InputArgList &args) { } static DiscardPolicy getDiscard(opt::InputArgList &args) { - if (args.hasArg(OPT_relocatable)) - return DiscardPolicy::None; - auto *arg = args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); if (!arg) @@ -823,6 +862,22 @@ static std::vector getSymbolOrderingFile(MemoryBufferRef mb) { return names.takeVector(); } +static bool getIsRela(opt::InputArgList &args) { + // If -z rel or -z rela is specified, use the last option. + for (auto *arg : args.filtered_reverse(OPT_z)) { + StringRef s(arg->getValue()); + if (s == "rel") + return false; + if (s == "rela") + return true; + } + + // Otherwise use the psABI defined relocation entry format. + uint16_t m = config->emachine; + return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC || + m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64; +} + static void parseClangOption(StringRef opt, const Twine &msg) { std::string err; raw_string_ostream os(err); @@ -841,7 +896,6 @@ static void readConfigs(opt::InputArgList &args) { args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); errorHandler().vsDiagnostics = args.hasArg(OPT_visual_studio_diagnostics_format, false); - threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true); config->allowMultipleDefinition = args.hasFlag(OPT_allow_multiple_definition, @@ -860,6 +914,8 @@ static void readConfigs(opt::InputArgList &args) { config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false); config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common, !args.hasArg(OPT_relocatable)); + config->optimizeBBJumps = + args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false); config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true); config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true); config->disableVerify = args.hasArg(OPT_disable_verify); @@ -881,9 +937,10 @@ static void readConfigs(opt::InputArgList &args) { args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); config->filterList = args::getStrings(args, OPT_filter); config->fini = args.getLastArgValue(OPT_fini, "_fini"); - config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419); - config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8); - config->forceBTI = hasZOption(args, "force-bti"); + config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) && + !args.hasArg(OPT_relocatable); + config->fixCortexA8 = + args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable); config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); @@ -891,19 +948,26 @@ static void readConfigs(opt::InputArgList &args) { config->ignoreDataAddressEquality = args.hasArg(OPT_ignore_data_address_equality); config->ignoreFunctionAddressEquality = - args.hasFlag(OPT_ignore_function_address_equality, - OPT_no_ignore_function_address_equality, true); + args.hasArg(OPT_ignore_function_address_equality); config->init = args.getLastArgValue(OPT_init, "_init"); config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline); config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager); + config->ltoEmitAsm = args.hasArg(OPT_lto_emit_asm); config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager); config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes); + config->ltoWholeProgramVisibility = + args.hasArg(OPT_lto_whole_program_visibility); config->ltoo = args::getInteger(args, OPT_lto_O, 2); config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq); config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile); + config->ltoBasicBlockSections = + args.getLastArgValue(OPT_lto_basicblock_sections); + config->ltoUniqueBasicBlockSectionNames = + args.hasFlag(OPT_lto_unique_bb_section_names, + OPT_no_lto_unique_bb_section_names, false); config->mapFile = args.getLastArgValue(OPT_Map); config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0); config->mergeArmExidx = @@ -922,26 +986,23 @@ static void readConfigs(opt::InputArgList &args) { config->optimize = args::getInteger(args, OPT_O, 1); config->orphanHandling = getOrphanHandling(args); config->outputFile = args.getLastArgValue(OPT_o); - config->pacPlt = hasZOption(args, "pac-plt"); -#ifdef __OpenBSD__ - config->pie = args.hasFlag(OPT_pie, OPT_no_pie, - !args.hasArg(OPT_shared) && !args.hasArg(OPT_relocatable)); -#else config->pie = args.hasFlag(OPT_pie, OPT_no_pie, false); -#endif config->printIcfSections = args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); config->printGcSections = args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); + config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats); config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); config->rpath = getRpath(args); config->relocatable = args.hasArg(OPT_relocatable); config->saveTemps = args.hasArg(OPT_save_temps); + if (args.hasArg(OPT_shuffle_sections)) + config->shuffleSectionSeed = args::getInteger(args, OPT_shuffle_sections, 0); config->searchPaths = args::getStrings(args, OPT_library_path); config->sectionStartMap = getSectionStartMap(args); config->shared = args.hasArg(OPT_shared); - config->singleRoRx = args.hasArg(OPT_no_rosegment); + config->singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true); config->soName = args.getLastArgValue(OPT_soname); config->sortSection = getSortSection(args); config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384); @@ -957,15 +1018,20 @@ static void readConfigs(opt::InputArgList &args) { config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || args.hasArg(OPT_thinlto_index_only_eq); config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq); - config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u); config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); config->thinLTOPrefixReplace = getOldNewOptions(args, OPT_thinlto_prefix_replace_eq); + config->thinLTOModulesToCompile = + args::getStrings(args, OPT_thinlto_single_module_eq); + config->timeTraceEnabled = args.hasArg(OPT_time_trace); + config->timeTraceGranularity = + args::getInteger(args, OPT_time_trace_granularity, 500); config->trace = args.hasArg(OPT_trace); config->undefined = args::getStrings(args, OPT_undefined); config->undefinedVersion = args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true); + config->unique = args.hasArg(OPT_unique); config->useAndroidRelrTags = args.hasFlag( OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false); config->unresolvedSymbols = getUnresolvedSymbolPolicy(args); @@ -978,6 +1044,7 @@ static void readConfigs(opt::InputArgList &args) { args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true); config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true); + config->zForceBti = hasZOption(args, "force-bti"); config->zForceIbt = hasZOption(args, "force-ibt"); config->zGlobal = hasZOption(args, "global"); config->zGnustack = getZGnuStack(args); @@ -992,37 +1059,78 @@ static void readConfigs(opt::InputArgList &args) { config->zNodlopen = hasZOption(args, "nodlopen"); config->zNow = getZFlag(args, "now", "lazy", false); config->zOrigin = hasZOption(args, "origin"); + config->zPacPlt = hasZOption(args, "pac-plt"); config->zRelro = getZFlag(args, "relro", "norelro", true); -#ifndef __OpenBSD__ - config->zRetpolineplt = getZFlag(args, "retpolineplt", "noretpolineplt", false); -#else - config->zRetpolineplt = getZFlag(args, "retpolineplt", "noretpolineplt", true); -#endif + config->zRetpolineplt = hasZOption(args, "retpolineplt"); config->zRodynamic = hasZOption(args, "rodynamic"); config->zSeparate = getZSeparate(args); config->zShstk = hasZOption(args, "shstk"); config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0); + config->zStartStopVisibility = getZStartStopVisibility(args); config->zText = getZFlag(args, "text", "notext", true); config->zWxneeded = hasZOption(args, "wxneeded"); + for (opt::Arg *arg : args.filtered(OPT_z)) { + std::pair option = + StringRef(arg->getValue()).split('='); + if (option.first != "dead-reloc-in-nonalloc") + continue; + constexpr StringRef errPrefix = "-z dead-reloc-in-nonalloc=: "; + std::pair kv = option.second.split('='); + if (kv.first.empty() || kv.second.empty()) { + error(errPrefix + "expected ="); + continue; + } + uint64_t v; + if (!to_integer(kv.second, v)) + error(errPrefix + "expected a non-negative integer, but got '" + + kv.second + "'"); + else if (Expected pat = GlobPattern::create(kv.first)) + config->deadRelocInNonAlloc.emplace_back(std::move(*pat), v); + else + error(errPrefix + toString(pat.takeError())); + } + // Parse LTO options. if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq)) parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), arg->getSpelling()); - for (auto *arg : args.filtered(OPT_plugin_opt)) - parseClangOption(arg->getValue(), arg->getSpelling()); + for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq_minus)) + parseClangOption(std::string("-") + arg->getValue(), arg->getSpelling()); + + // GCC collect2 passes -plugin-opt=path/to/lto-wrapper with an absolute or + // relative path. Just ignore. If not ended with "lto-wrapper", consider it an + // unsupported LLVMgold.so option and error. + for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq)) + if (!StringRef(arg->getValue()).endswith("lto-wrapper")) + error(arg->getSpelling() + ": unknown plugin option '" + arg->getValue() + + "'"); // Parse -mllvm options. for (auto *arg : args.filtered(OPT_mllvm)) parseClangOption(arg->getValue(), arg->getSpelling()); + // --threads= takes a positive integer and provides the default value for + // --thinlto-jobs=. + if (auto *arg = args.getLastArg(OPT_threads)) { + 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)) + config->thinLTOJobs = arg->getValue(); + if (config->ltoo > 3) error("invalid optimization level for LTO: " + Twine(config->ltoo)); if (config->ltoPartitions == 0) error("--lto-partitions: number of threads must be > 0"); - if (config->thinLTOJobs == 0) - error("--thinlto-jobs: number of threads must be > 0"); + if (!get_threadpool_strategy(config->thinLTOJobs)) + error("--thinlto-jobs: invalid job count: " + config->thinLTOJobs); if (config->splitStackAdjustSize < 0) error("--split-stack-adjust-size: size must be >= 0"); @@ -1100,25 +1208,30 @@ static void readConfigs(opt::InputArgList &args) { {s, /*isExternCpp=*/false, /*hasWildcard=*/false}); } - // Parses -dynamic-list and -export-dynamic-symbol. They make some - // symbols private. Note that -export-dynamic takes precedence over them - // as it says all symbols should be exported. - if (!config->exportDynamic) { - for (auto *arg : args.filtered(OPT_dynamic_list)) - if (Optional buffer = readFile(arg->getValue())) - readDynamicList(*buffer); - - for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) - config->dynamicList.push_back( - {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false}); + for (opt::Arg *arg : args.filtered(OPT_warn_backrefs_exclude)) { + StringRef pattern(arg->getValue()); + if (Expected pat = GlobPattern::create(pattern)) + config->warnBackrefsExclude.push_back(std::move(*pat)); + else + error(arg->getSpelling() + ": " + toString(pat.takeError())); } - // If --export-dynamic-symbol=foo is given and symbol foo is defined in - // an object file in an archive file, that object file should be pulled - // out and linked. (It doesn't have to behave like that from technical - // point of view, but this is needed for compatibility with GNU.) + // When producing an executable, --dynamic-list specifies non-local defined + // symbols whith are required to be exported. When producing a shared object, + // symbols not specified by --dynamic-list are non-preemptible. + config->symbolic = + args.hasArg(OPT_Bsymbolic) || args.hasArg(OPT_dynamic_list); + for (auto *arg : args.filtered(OPT_dynamic_list)) + if (Optional buffer = readFile(arg->getValue())) + readDynamicList(*buffer); + + // --export-dynamic-symbol specifies additional --dynamic-list symbols if any + // other option expresses a symbolic intention: -no-pie, -pie, -Bsymbolic, + // -Bsymbolic-functions (if STT_FUNC), --dynamic-list. for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) - config->undefined.push_back(arg->getValue()); + config->dynamicList.push_back( + {arg->getValue(), /*isExternCpp=*/false, + /*hasWildcard=*/hasWildcard(arg->getValue())}); for (auto *arg : args.filtered(OPT_version_script)) if (Optional path = searchScript(arg->getValue())) { @@ -1148,20 +1261,19 @@ static void setConfigs(opt::InputArgList &args) { // ELF defines two different ways to store relocation addends as shown below: // - // Rel: Addends are stored to the location where relocations are applied. + // Rel: Addends are stored to the location where relocations are applied. It + // cannot pack the full range of addend values for all relocation types, but + // this only affects relocation types that we don't support emitting as + // dynamic relocations (see getDynRel). // Rela: Addends are stored as part of relocation entry. // // In other words, Rela makes it easy to read addends at the price of extra - // 4 or 8 byte for each relocation entry. We don't know why ELF defined two - // different mechanisms in the first place, but this is how the spec is - // defined. + // 4 or 8 byte for each relocation entry. // - // You cannot choose which one, Rel or Rela, you want to use. Instead each - // ABI defines which one you need to use. The following expression expresses - // that. - config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || - m == EM_PPC || m == EM_PPC64 || m == EM_RISCV || - m == EM_X86_64; + // We pick the format for dynamic relocations according to the psABI for each + // processor, but a contrary choice can be made if the dynamic loader + // supports. + config->isRela = getIsRela(args); // If the output uses REL relocations we must store the dynamic relocation // addends to the output sections. We also store addends for RELA relocations @@ -1309,7 +1421,7 @@ void LinkerDriver::inferMachineType() { } // Parse -z max-page-size=. The default value is defined by -// each target. Is set to 1 if given nmagic or omagic. +// each target. static uint64_t getMaxPageSize(opt::InputArgList &args) { uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size", target->defaultMaxPageSize); @@ -1324,7 +1436,7 @@ static uint64_t getMaxPageSize(opt::InputArgList &args) { } // Parse -z common-page-size=. The default value is defined by -// each target. Is set to 1 if given nmagic or omagic. +// each target. static uint64_t getCommonPageSize(opt::InputArgList &args) { uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size", target->defaultCommonPageSize); @@ -1341,16 +1453,6 @@ static uint64_t getCommonPageSize(opt::InputArgList &args) { return val; } -// Parse -z max-page-size=. The default value is defined by -// each target. -static uint64_t getRealMaxPageSize(opt::InputArgList &args) { - uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size", - target->defaultMaxPageSize); - if (!isPowerOf2_64(val)) - error("max-page-size: value isn't a power of 2"); - return val; -} - // Parses -image-base option. static Optional getImageBase(opt::InputArgList &args) { // Because we are using "Config->maxPageSize" here, this function has to be @@ -1413,7 +1515,7 @@ static void excludeLibs(opt::InputArgList &args) { visit(file); } -// Force Sym to be entered in the output. Used for -u or equivalent. +// Force Sym to be entered in the output. static void handleUndefined(Symbol *sym) { // Since a symbol may not be used inside the program, LTO may // eliminate it. Mark the symbol as "used" to prevent it. @@ -1614,6 +1716,12 @@ static Symbol *addUndefined(StringRef name) { Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0}); } +static Symbol *addUnusedUndefined(StringRef name) { + Undefined sym{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0}; + sym.isUsedInRegularObj = false; + return symtab->addSymbol(sym); +} + // This function is where all the optimizations of link-time // optimization takes place. When LTO is in use, some input files are // not in native object file format but in the LLVM bitcode format. @@ -1622,6 +1730,7 @@ static Symbol *addUndefined(StringRef name) { // Because all bitcode files that the program consists of are passed to // the compiler at once, it can do a whole-program optimization. template void LinkerDriver::compileBitcodeFiles() { + llvm::TimeTraceScope timeScope("LTO"); // Compile bitcode files and replace bitcode symbols. lto.reset(new BitcodeCompiler); for (BitcodeFile *file : bitcodeFiles) @@ -1630,8 +1739,11 @@ template void LinkerDriver::compileBitcodeFiles() { for (InputFile *file : lto->compile()) { auto *obj = cast>(file); obj->parse(/*ignoreComdats=*/true); - for (Symbol *sym : obj->getGlobalSymbols()) - sym->parseSymbolVersion(); + + // Parse '@' in symbol names for non-relocatable output. + if (!config->relocatable) + for (Symbol *sym : obj->getGlobalSymbols()) + sym->parseSymbolVersion(); objectFiles.push_back(file); } } @@ -1725,8 +1837,9 @@ template static uint32_t getAndFeatures() { uint32_t ret = -1; for (InputFile *f : objectFiles) { uint32_t features = cast>(f)->andFeatures; - if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { - warn(toString(f) + ": -z force-bti: file does not have BTI property"); + if (config->zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { + warn(toString(f) + ": -z force-bti: file does not have " + "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property"); features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI; } else if (config->zForceIbt && !(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) { @@ -1734,13 +1847,14 @@ template static uint32_t getAndFeatures() { "GNU_PROPERTY_X86_FEATURE_1_IBT property"); features |= GNU_PROPERTY_X86_FEATURE_1_IBT; } + if (config->zPacPlt && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + warn(toString(f) + ": -z pac-plt: file does not have " + "GNU_PROPERTY_AARCH64_FEATURE_1_PAC property"); + features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; + } ret &= features; } - // Force enable pointer authentication Plt, we don't warn in this case as - // this does not require support in the object for correctness. - if (config->pacPlt) - ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; // Force enable Shadow Stack. if (config->zShstk) ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; @@ -1751,6 +1865,7 @@ template static uint32_t getAndFeatures() { // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &args) { + llvm::TimeTraceScope timeScope("Link", StringRef("LinkerDriver::Link")); // If a -hash-style option was not given, set to a default value, // which varies depending on the target. if (!args.hasArg(OPT_hash_style)) { @@ -1786,12 +1901,20 @@ template void LinkerDriver::link(opt::InputArgList &args) { for (auto *arg : args.filtered(OPT_trace_symbol)) symtab->insert(arg->getValue())->traced = true; + // Handle -u/--undefined before input files. If both a.a and b.so define foo, + // -u foo a.a b.so will fetch a.a. + for (StringRef name : config->undefined) + addUnusedUndefined(name); + // Add all files to the symbol table. This will add almost all // symbols that we need to the symbol table. This process might // add files to the link, via autolinking, these files are always // appended to the Files vector. - for (size_t i = 0; i < files.size(); ++i) - parseFile(files[i]); + { + llvm::TimeTraceScope timeScope("Parse input files"); + for (size_t i = 0; i < files.size(); ++i) + parseFile(files[i]); + } // Now that we have every file, we can decide if we will need a // dynamic symbol table. @@ -1807,10 +1930,10 @@ template void LinkerDriver::link(opt::InputArgList &args) { for (StringRef name : script->referencedSymbols) addUndefined(name); - // Handle the `--undefined ` options. - for (StringRef arg : config->undefined) - if (Symbol *sym = symtab->find(arg)) - handleUndefined(sym); + // Prevent LTO from removing any definition referenced by -u. + for (StringRef name : config->undefined) + if (Defined *sym = dyn_cast_or_null(symtab->find(name))) + sym->isUsedInRegularObj = true; // If an entry symbol is in a static archive, pull out that file now. if (Symbol *sym = symtab->find(config->entry)) @@ -1821,9 +1944,9 @@ template void LinkerDriver::link(opt::InputArgList &args) { handleUndefinedGlob(pat); // Mark -init and -fini symbols so that the LTO doesn't eliminate them. - if (Symbol *sym = symtab->find(config->init)) + if (Symbol *sym = dyn_cast_or_null(symtab->find(config->init))) sym->isUsedInRegularObj = true; - if (Symbol *sym = symtab->find(config->fini)) + if (Symbol *sym = dyn_cast_or_null(symtab->find(config->fini))) sym->isUsedInRegularObj = true; // If any of our inputs are bitcode files, the LTO code generator may create @@ -1851,10 +1974,6 @@ template void LinkerDriver::link(opt::InputArgList &args) { if (errorCount()) return; - // Now when we read all script files, we want to finalize order of linker - // script commands, which can be not yet final because of INSERT commands. - script->processInsertCommands(); - // We want to declare linker script's symbols early, // so that we can version them. // They also might be exported if referenced by DSOs. @@ -1890,19 +2009,22 @@ template void LinkerDriver::link(opt::InputArgList &args) { // With this the symbol table should be complete. After this, no new names // except a few linker-synthesized ones will be added to the symbol table. compileBitcodeFiles(); + + // Symbol resolution finished. Report backward reference problems. + reportBackrefs(); if (errorCount()) return; // If -thinlto-index-only is given, we should create only "index // files" and not object files. Index file creation is already done // in addCombinedLTOObject, so we are done if that's the case. - if (config->thinLTOIndexOnly) - return; - - // Likewise, --plugin-opt=emit-llvm is an option to make LTO create - // an output file in bitcode and exit, so that you can just get a - // combined bitcode file. - if (config->emitLLVM) + // Likewise, --plugin-opt=emit-llvm and --plugin-opt=emit-asm are the + // options to create output files in bitcode or assembly code + // repsectively. No object files are generated. + // Also bail out here when only certain thinLTO modules are specified for + // compilation. The intermediate object file are the expected output. + if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm || + !config->thinLTOModulesToCompile.empty()) return; // Apply symbol renames for -wrap. @@ -1966,11 +2088,6 @@ template void LinkerDriver::link(opt::InputArgList &args) { // optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it // is limited to writing trap instructions on the last executable segment. config->commonPageSize = getCommonPageSize(args); - // textAlignPageSize is the alignment page size to use when aligning PT_LOAD - // sections. This is the same as maxPageSize except under -omagic, where data - // sections are non-aligned (maxPageSize set to 1) but text sections are aligned - // to the target page size. - config->textAlignPageSize = config->omagic ? getRealMaxPageSize(args) : config->maxPageSize; config->imageBase = getImageBase(args); @@ -2047,6 +2164,3 @@ template void LinkerDriver::link(opt::InputArgList &args) { // Write the result to the file. writeResult(); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/DriverUtils.cpp b/gnu/llvm/lld/ELF/DriverUtils.cpp index f1a3608f5b5..e33b07c0c9c 100644 --- a/gnu/llvm/lld/ELF/DriverUtils.cpp +++ b/gnu/llvm/lld/ELF/DriverUtils.cpp @@ -23,15 +23,15 @@ #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" using namespace llvm; using namespace llvm::sys; using namespace llvm::opt; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; // Create OptTable @@ -82,7 +82,7 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } - if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32) + if (Triple(sys::getProcessTriple()).isOSWindows()) return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } @@ -143,7 +143,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef argv) { return args; } -void printHelp() { +void elf::printHelp() { ELFOptTable().PrintHelp( lld::outs(), (config->progName + " [options] file...").str().c_str(), "lld", false /*ShowHidden*/, true /*ShowAllAliases*/); @@ -160,12 +160,12 @@ void printHelp() { static std::string rewritePath(StringRef s) { if (fs::exists(s)) return relativeToRoot(s); - return 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 createResponseFile(const opt::InputArgList &args) { +std::string elf::createResponseFile(const opt::InputArgList &args) { SmallString<0> data; raw_svector_ostream os(data); os << "--chroot .\n"; @@ -199,7 +199,7 @@ std::string createResponseFile(const opt::InputArgList &args) { os << toString(*arg) << "\n"; } } - return data.str(); + return std::string(data.str()); } // Find a file by concatenating given paths. If a resulting path @@ -212,11 +212,11 @@ static Optional findFile(StringRef path1, const Twine &path2) { path::append(s, path1, path2); if (fs::exists(s)) - return s.str().str(); + return std::string(s); return None; } -Optional findFromSearchPaths(StringRef path) { +Optional elf::findFromSearchPaths(StringRef path) { for (StringRef dir : config->searchPaths) if (Optional s = findFile(dir, path)) return s; @@ -225,38 +225,11 @@ Optional findFromSearchPaths(StringRef path) { // This is for -l. We'll look for lib.so or lib.a from // search paths. -Optional searchLibraryBaseName(StringRef name) { +Optional elf::searchLibraryBaseName(StringRef name) { for (StringRef dir : config->searchPaths) { - if (!config->isStatic) { + if (!config->isStatic) if (Optional s = findFile(dir, "lib" + name + ".so")) return s; - - // Handle OpenBSD-style maj/min shlib scheme - llvm::SmallString<128> Scratch; - const StringRef LibName = ("lib" + name + ".so.").toStringRef(Scratch); - int MaxMaj = -1, MaxMin = -1; - std::error_code EC; - for (fs::directory_iterator LI(dir, EC), LE; - LI != LE; LI = LI.increment(EC)) { - StringRef FilePath = LI->path(); - StringRef FileName = path::filename(FilePath); - if (!(FileName.startswith(LibName))) - continue; - std::pair MajMin = - FileName.substr(LibName.size()).split('.'); - int Maj, Min; - if (MajMin.first.getAsInteger(10, Maj) || Maj < 0) - continue; - if (MajMin.second.getAsInteger(10, Min) || Min < 0) - continue; - if (Maj > MaxMaj) - MaxMaj = Maj, MaxMin = Min; - if (MaxMaj == Maj && Min > MaxMin) - MaxMin = Min; - } - if (MaxMaj >= 0) - return findFile(dir, LibName + Twine(MaxMaj) + "." + Twine(MaxMin)); - } if (Optional s = findFile(dir, "lib" + name + ".a")) return s; } @@ -264,7 +237,7 @@ Optional searchLibraryBaseName(StringRef name) { } // This is for -l. -Optional searchLibrary(StringRef name) { +Optional elf::searchLibrary(StringRef name) { if (name.startswith(":")) return findFromSearchPaths(name.substr(1)); return searchLibraryBaseName(name); @@ -273,11 +246,8 @@ Optional searchLibrary(StringRef name) { // If a linker/version script doesn't exist in the current directory, we also // look for the script in the '-L' search paths. This matches the behaviour of // '-T', --version-script=, and linker script INPUT() command in ld.bfd. -Optional searchScript(StringRef name) { +Optional elf::searchScript(StringRef name) { if (fs::exists(name)) return name.str(); return findFromSearchPaths(name); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/EhFrame.cpp b/gnu/llvm/lld/ELF/EhFrame.cpp index a9c66f29446..f97e3b604eb 100644 --- a/gnu/llvm/lld/ELF/EhFrame.cpp +++ b/gnu/llvm/lld/ELF/EhFrame.cpp @@ -29,9 +29,9 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::dwarf; using namespace llvm::object; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { namespace { class EhReader { public: @@ -56,7 +56,7 @@ private: }; } -size_t readEhRecordSize(InputSectionBase *s, size_t off) { +size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) { return EhReader(s, s->data().slice(off)).readEhRecordSize(); } @@ -148,7 +148,7 @@ void EhReader::skipAugP() { d = d.slice(size); } -uint8_t getFdeEncoding(EhSectionPiece *p) { +uint8_t elf::getFdeEncoding(EhSectionPiece *p) { return EhReader(p->sec, p->data()).getFdeEncoding(); } @@ -194,6 +194,3 @@ uint8_t EhReader::getFdeEncoding() { } return DW_EH_PE_absptr; } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/ICF.cpp b/gnu/llvm/lld/ELF/ICF.cpp index 8992b6564a8..ecf0a282420 100644 --- a/gnu/llvm/lld/ELF/ICF.cpp +++ b/gnu/llvm/lld/ELF/ICF.cpp @@ -80,10 +80,11 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Writer.h" -#include "lld/Common/Threads.h" #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" #include #include @@ -91,9 +92,9 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { namespace { template class ICF { public: @@ -399,7 +400,7 @@ template void ICF::forEachClass(llvm::function_ref fn) { // If threading is disabled or the number of sections are // too small to use threading, call Fn sequentially. - if (!threadsEnabled || sections.size() < 1024) { + if (parallel::strategy.ThreadsRequested == 1 || sections.size() < 1024) { forEachClassRange(0, sections.size(), fn); ++cnt; return; @@ -466,9 +467,8 @@ template void ICF::run() { } // Initially, we use hash values to partition sections. - parallelForEach(sections, [&](InputSection *s) { - s->eqClass[0] = xxHash64(s->data()); - }); + parallelForEach( + sections, [&](InputSection *s) { s->eqClass[0] = xxHash64(s->data()); }); for (unsigned cnt = 0; cnt != 2; ++cnt) { parallelForEach(sections, [&](InputSection *s) { @@ -525,12 +525,12 @@ template void ICF::run() { } // ICF entry point function. -template void doIcf() { ICF().run(); } - -template void doIcf(); -template void doIcf(); -template void doIcf(); -template void doIcf(); +template void elf::doIcf() { + llvm::TimeTraceScope timeScope("ICF"); + ICF().run(); +} -} // namespace elf -} // namespace lld +template void elf::doIcf(); +template void elf::doIcf(); +template void elf::doIcf(); +template void elf::doIcf(); diff --git a/gnu/llvm/lld/ELF/InputFiles.cpp b/gnu/llvm/lld/ELF/InputFiles.cpp index 43978cd66c6..c2f1830a981 100644 --- a/gnu/llvm/lld/ELF/InputFiles.cpp +++ b/gnu/llvm/lld/ELF/InputFiles.cpp @@ -36,33 +36,35 @@ using namespace llvm::object; using namespace llvm::sys; using namespace llvm::sys::fs; using namespace llvm::support::endian; +using namespace lld; +using namespace lld::elf; + +bool InputFile::isInGroup; +uint32_t InputFile::nextGroupId; + +std::vector elf::archiveFiles; +std::vector elf::binaryFiles; +std::vector elf::bitcodeFiles; +std::vector elf::lazyObjFiles; +std::vector elf::objectFiles; +std::vector elf::sharedFiles; + +std::unique_ptr elf::tar; -namespace lld { // Returns "", "foo.a(bar.o)" or "baz.o". -std::string toString(const elf::InputFile *f) { +std::string lld::toString(const InputFile *f) { if (!f) return ""; if (f->toStringCache.empty()) { if (f->archiveName.empty()) - f->toStringCache = f->getName(); + f->toStringCache = std::string(f->getName()); else f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str(); } return f->toStringCache; } -namespace elf { -bool InputFile::isInGroup; -uint32_t InputFile::nextGroupId; -std::vector binaryFiles; -std::vector bitcodeFiles; -std::vector lazyObjFiles; -std::vector objectFiles; -std::vector sharedFiles; - -std::unique_ptr tar; - static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) { unsigned char size; unsigned char endian; @@ -101,7 +103,7 @@ InputFile::InputFile(Kind k, MemoryBufferRef m) ++nextGroupId; } -Optional readFile(StringRef path) { +Optional elf::readFile(StringRef 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("/")) @@ -138,8 +140,10 @@ static bool isCompatible(InputFile *file) { return true; } - if (!config->emulation.empty()) { - error(toString(file) + " is incompatible with " + config->emulation); + StringRef target = + !config->bfdname.empty() ? config->bfdname : config->emulation; + if (!target.empty()) { + error(toString(file) + " is incompatible with " + target); return false; } @@ -148,8 +152,11 @@ static bool isCompatible(InputFile *file) { existing = objectFiles[0]; else if (!sharedFiles.empty()) existing = sharedFiles[0]; - else + else if (!bitcodeFiles.empty()) existing = bitcodeFiles[0]; + else + llvm_unreachable("Must have -m, OUTPUT_FORMAT or existing input file to " + "determine target emulation"); error(toString(file) + " is incompatible with " + toString(existing)); return false; @@ -168,6 +175,7 @@ template static void doParseFile(InputFile *file) { // .a file if (auto *f = dyn_cast(file)) { + archiveFiles.push_back(f); f->parse(); return; } @@ -201,7 +209,7 @@ template static void doParseFile(InputFile *file) { } // Add symbols in File to the symbol table. -void parseFile(InputFile *file) { +void elf::parseFile(InputFile *file) { switch (config->ekind) { case ELF32LEKind: doParseFile(file); @@ -222,7 +230,7 @@ void parseFile(InputFile *file) { // Concatenates arguments to construct a string representing an error location. static std::string createFileLineMsg(StringRef path, unsigned line) { - std::string filename = path::filename(path); + std::string filename = std::string(path::filename(path)); std::string lineno = ":" + std::to_string(line); if (filename == path) return filename + lineno; @@ -243,7 +251,7 @@ static std::string getSrcMsgAux(ObjFile &file, const Symbol &sym, return createFileLineMsg(fileLine->first, fileLine->second); // File.sourceFile contains STT_FILE symbol, and that is a last resort. - return file.sourceFile; + return std::string(file.sourceFile); } std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec, @@ -264,9 +272,17 @@ std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec, } } -template void ObjFile::initializeDwarf() { - dwarf = make(std::make_unique( - std::make_unique>(this))); +template DWARFCache *ObjFile::getDwarf() { + llvm::call_once(initDwarf, [this]() { + dwarf = std::make_unique(std::make_unique( + std::make_unique>(this), "", + [&](Error err) { warn(getName() + ": " + toString(std::move(err))); }, + [&](Error warning) { + warn(getName() + ": " + toString(std::move(warning))); + })); + }); + + return dwarf.get(); } // Returns the pair of file name and line number describing location of data @@ -274,9 +290,7 @@ template void ObjFile::initializeDwarf() { template Optional> ObjFile::getVariableLoc(StringRef name) { - llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); - - return dwarf->getVariableLoc(name); + return getDwarf()->getVariableLoc(name); } // Returns source line information for a given offset @@ -284,8 +298,6 @@ ObjFile::getVariableLoc(StringRef name) { template Optional ObjFile::getDILineInfo(InputSectionBase *s, uint64_t offset) { - llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); - // Detect SectionIndex for specified section. uint64_t sectionIndex = object::SectionedAddress::UndefSection; ArrayRef sections = s->file->getSections(); @@ -296,9 +308,7 @@ Optional ObjFile::getDILineInfo(InputSectionBase *s, } } - // Use fake address calculated by adding section file offset and offset in - // section. See comments for ObjectInfo class. - return dwarf->getDILineInfo(s->getOffsetInFile() + offset, sectionIndex); + return getDwarf()->getDILineInfo(offset, sectionIndex); } ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) { @@ -417,6 +427,9 @@ StringRef ObjFile::getShtGroupSignature(ArrayRef sections, template bool ObjFile::shouldMerge(const Elf_Shdr &sec, StringRef name) { + if (!(sec.sh_flags & SHF_MERGE)) + return false; + // On a regular link we don't merge sections if -O0 (default is -O1). This // sometimes makes the linker significantly faster, although the output will // be bigger. @@ -452,10 +465,7 @@ bool ObjFile::shouldMerge(const Elf_Shdr &sec, StringRef name) { Twine(sec.sh_size) + ") must be a multiple of sh_entsize (" + Twine(entSize) + ")"); - uint64_t flags = sec.sh_flags; - if (!(flags & SHF_MERGE)) - return false; - if (flags & SHF_WRITE) + if (sec.sh_flags & SHF_WRITE) fatal(toString(this) + ":(" + name + "): writable SHF_MERGE section is not supported"); @@ -622,6 +632,8 @@ void ObjFile::initializeSections(bool ignoreComdats) { break; case SHT_SYMTAB: case SHT_STRTAB: + case SHT_REL: + case SHT_RELA: case SHT_NULL: break; default: @@ -629,11 +641,21 @@ void ObjFile::initializeSections(bool ignoreComdats) { } } - // This block handles SHF_LINK_ORDER. + // We have a second loop. It is used to: + // 1) handle SHF_LINK_ORDER sections. + // 2) create SHT_REL[A] sections. In some cases the section header index of a + // relocation section may be smaller than that of the relocated section. In + // such cases, the relocation section would attempt to reference a target + // section that has not yet been created. For simplicity, delay creation of + // relocation sections until now. for (size_t i = 0, e = objSections.size(); i < e; ++i) { if (this->sections[i] == &InputSection::discarded) continue; const Elf_Shdr &sec = objSections[i]; + + if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA) + this->sections[i] = createInputSection(sec); + if (!(sec.sh_flags & SHF_LINK_ORDER)) continue; @@ -662,7 +684,9 @@ void ObjFile::initializeSections(bool ignoreComdats) { // the input objects have been compiled. static void updateARMVFPArgs(const ARMAttributeParser &attributes, const InputFile *f) { - if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) + Optional attr = + attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + if (!attr.hasValue()) // If an ABI tag isn't present then it is implicitly given the value of 0 // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files, // including some in glibc that don't use FP args (and should have value 3) @@ -670,7 +694,7 @@ static void updateARMVFPArgs(const ARMAttributeParser &attributes, // as a clash. return; - unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + unsigned vfpArgs = attr.getValue(); ARMVFPArgKind arg; switch (vfpArgs) { case ARMBuildAttrs::BaseAAPCS: @@ -707,9 +731,11 @@ static void updateARMVFPArgs(const ARMAttributeParser &attributes, // is compiled with an architecture that supports these features then lld is // permitted to use them. static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) { - if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) + Optional attr = + attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + if (!attr.hasValue()) return; - auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + auto arch = attr.getValue(); switch (arch) { case ARMBuildAttrs::Pre_v4: case ARMBuildAttrs::v4: @@ -842,7 +868,13 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { break; ARMAttributeParser attributes; ArrayRef contents = check(this->getObj().getSectionContents(&sec)); - attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind); + 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; + } updateSupportedARMFeatures(attributes); updateARMVFPArgs(attributes, this); @@ -1029,51 +1061,68 @@ template void ObjFile::initializeSymbols() { ArrayRef eSyms = this->getELFSyms(); this->symbols.resize(eSyms.size()); - // Our symbol table may have already been partially initialized + // Fill in InputFile::symbols. Some entries have been initialized // because of LazyObjFile. - for (size_t i = 0, end = eSyms.size(); i != end; ++i) - if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL) - this->symbols[i] = - symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this)); - - // Fill this->Symbols. A symbol is either local or global. for (size_t i = 0, end = eSyms.size(); i != end; ++i) { + if (this->symbols[i]) + continue; const Elf_Sym &eSym = eSyms[i]; - - // Read symbol attributes. uint32_t secIdx = getSectionIndex(eSym); if (secIdx >= this->sections.size()) fatal(toString(this) + ": invalid section index: " + Twine(secIdx)); + if (eSym.getBinding() != STB_LOCAL) { + if (i < firstGlobal) + error(toString(this) + ": non-local symbol (" + Twine(i) + + ") found at index < .symtab's sh_info (" + Twine(firstGlobal) + + ")"); + this->symbols[i] = + symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this)); + continue; + } + + // Handle local symbols. Local symbols are not added to the symbol + // table because they are not visible from other object files. We + // allocate symbol instances and add their pointers to symbols. + if (i >= firstGlobal) + errorOrWarn(toString(this) + ": STB_LOCAL symbol (" + Twine(i) + + ") found at index >= .symtab's sh_info (" + + Twine(firstGlobal) + ")"); InputSectionBase *sec = this->sections[secIdx]; + uint8_t type = eSym.getType(); + if (type == STT_FILE) + sourceFile = CHECK(eSym.getName(this->stringTable), this); + if (this->stringTable.size() <= eSym.st_name) + fatal(toString(this) + ": invalid symbol name offset"); + StringRefZ name = this->stringTable.data() + eSym.st_name; + + if (eSym.st_shndx == SHN_UNDEF) + this->symbols[i] = + make(this, name, STB_LOCAL, eSym.st_other, type); + else if (sec == &InputSection::discarded) + this->symbols[i] = + make(this, name, STB_LOCAL, eSym.st_other, type, + /*discardedSecIdx=*/secIdx); + else + this->symbols[i] = make(this, name, STB_LOCAL, eSym.st_other, + type, eSym.st_value, eSym.st_size, sec); + } + + // Symbol resolution of non-local symbols. + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; uint8_t binding = eSym.getBinding(); + if (binding == STB_LOCAL) + continue; // Errored above. + + uint32_t secIdx = getSectionIndex(eSym); + InputSectionBase *sec = this->sections[secIdx]; uint8_t stOther = eSym.st_other; uint8_t type = eSym.getType(); uint64_t value = eSym.st_value; uint64_t size = eSym.st_size; StringRefZ name = this->stringTable.data() + eSym.st_name; - // Handle local symbols. Local symbols are not added to the symbol - // table because they are not visible from other object files. We - // allocate symbol instances and add their pointers to Symbols. - if (binding == STB_LOCAL) { - if (eSym.getType() == STT_FILE) - sourceFile = CHECK(eSym.getName(this->stringTable), this); - - if (this->stringTable.size() <= eSym.st_name) - fatal(toString(this) + ": invalid symbol name offset"); - - if (eSym.st_shndx == SHN_UNDEF) - this->symbols[i] = make(this, name, binding, stOther, type); - else if (sec == &InputSection::discarded) - this->symbols[i] = make(this, name, binding, stOther, type, - /*DiscardedSecIdx=*/secIdx); - else - this->symbols[i] = - make(this, name, binding, stOther, type, value, size, sec); - continue; - } - // Handle global undefined symbols. if (eSym.st_shndx == SHN_UNDEF) { this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type}); @@ -1097,8 +1146,20 @@ template void ObjFile::initializeSymbols() { // COMDAT member sections, and if a comdat group is discarded, some // defined symbol in a .eh_frame becomes dangling symbols. if (sec == &InputSection::discarded) { - this->symbols[i]->resolve( - Undefined{this, name, binding, stOther, type, secIdx}); + Undefined und{this, name, binding, stOther, type, secIdx}; + Symbol *sym = this->symbols[i]; + // !ArchiveFile::parsed or LazyObjFile::fetched means that the file + // containing this object has not finished processing, i.e. this symbol is + // a result of a lazy symbol fetch. We should demote the lazy symbol to an + // Undefined so that any relocations outside of the group to it will + // trigger a discarded section error. + if ((sym->symbolKind == Symbol::LazyArchiveKind && + !cast(sym->file)->parsed) || + (sym->symbolKind == Symbol::LazyObjectKind && + cast(sym->file)->fetched)) + sym->replace(und); + else + sym->resolve(und); continue; } @@ -1121,6 +1182,10 @@ ArchiveFile::ArchiveFile(std::unique_ptr &&file) void ArchiveFile::parse() { for (const Archive::Symbol &sym : file->symbols()) symtab->addSymbol(LazyArchive{*this, sym}); + + // Inform a future invocation of ObjFile::initializeSymbols() that this + // archive has been processed. + parsed = true; } // Returns a buffer pointing to a member file containing a given symbol. @@ -1142,12 +1207,24 @@ void ArchiveFile::fetch(const Archive::Symbol &sym) { if (tar && c.getParent()->isThin()) tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); - InputFile *file = createObjectFile( - mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset()); + InputFile *file = createObjectFile(mb, getName(), c.getChildOffset()); file->groupId = groupId; parseFile(file); } +size_t ArchiveFile::getMemberCount() const { + size_t count = 0; + Error err = Error::success(); + for (const Archive::Child &c : file->children(err)) { + (void)c; + ++count; + } + // This function is used by --print-archive-stats=, where an error does not + // really matter. + consumeError(std::move(err)); + return count; +} + unsigned SharedFile::vernauxNum; // Parse the version definitions in the object file if present, and return a @@ -1179,6 +1256,40 @@ static std::vector parseVerdefs(const uint8_t *base, return verdefs; } +// Parse SHT_GNU_verneed to properly set the name of a versioned undefined +// symbol. We detect fatal issues which would cause vulnerabilities, but do not +// implement sophisticated error checking like in llvm-readobj because the value +// of such diagnostics is low. +template +std::vector SharedFile::parseVerneed(const ELFFile &obj, + const typename ELFT::Shdr *sec) { + if (!sec) + return {}; + std::vector verneeds; + 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()) + fatal(toString(this) + " has an invalid Verneed"); + auto *vn = reinterpret_cast(verneedBuf); + const uint8_t *vernauxBuf = verneedBuf + vn->vn_aux; + for (unsigned j = 0; j != vn->vn_cnt; ++j) { + if (vernauxBuf + sizeof(typename ELFT::Vernaux) > data.end()) + fatal(toString(this) + " has an invalid Vernaux"); + auto *aux = reinterpret_cast(vernauxBuf); + if (aux->vna_name >= this->stringTable.size()) + fatal(toString(this) + " has a Vernaux with an invalid vna_name"); + uint16_t version = aux->vna_other & VERSYM_VERSION; + if (version >= verneeds.size()) + verneeds.resize(version + 1); + verneeds[version] = aux->vna_name; + vernauxBuf += aux->vna_next; + } + verneedBuf += vn->vn_next; + } + return verneeds; +} + // We do not usually care about alignments of data in shared object // files because the loader takes care of it. However, if we promote a // DSO symbol to point to .bss due to copy relocation, we need to keep @@ -1222,6 +1333,7 @@ template void SharedFile::parse() { const Elf_Shdr *versymSec = nullptr; const Elf_Shdr *verdefSec = nullptr; + const Elf_Shdr *verneedSec = nullptr; // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. for (const Elf_Shdr &sec : sections) { @@ -1238,6 +1350,9 @@ template void SharedFile::parse() { case SHT_GNU_verdef: verdefSec = &sec; break; + case SHT_GNU_verneed: + verneedSec = &sec; + break; } } @@ -1277,12 +1392,13 @@ template void SharedFile::parse() { sharedFiles.push_back(this); verdefs = parseVerdefs(obj.base(), verdefSec); + std::vector verneeds = parseVerneed(obj, verneedSec); // Parse ".gnu.version" section which is a parallel array for the symbol // table. If a given file doesn't have a ".gnu.version" section, we use // VER_NDX_GLOBAL. size_t size = numELFSyms - firstGlobal; - std::vector versyms(size, VER_NDX_GLOBAL); + std::vector versyms(size, VER_NDX_GLOBAL); if (versymSec) { ArrayRef versym = CHECK(obj.template getSectionContentsAsArray(versymSec), @@ -1313,7 +1429,22 @@ template void SharedFile::parse() { continue; } + uint16_t idx = versyms[i] & ~VERSYM_HIDDEN; if (sym.isUndefined()) { + // For unversioned undefined symbols, VER_NDX_GLOBAL makes more sense but + // as of binutils 2.34, GNU ld produces VER_NDX_LOCAL. + if (idx != VER_NDX_LOCAL && idx != VER_NDX_GLOBAL) { + if (idx >= verneeds.size()) { + error("corrupt input file: version need index " + Twine(idx) + + " for symbol " + name + " is out of bounds\n>>> defined in " + + toString(this)); + continue; + } + StringRef verName = this->stringTable.data() + verneeds[idx]; + versionedNameBuffer.clear(); + name = + saver.save((name + "@" + verName).toStringRef(versionedNameBuffer)); + } Symbol *s = symtab->addSymbol( Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); s->exportDynamic = true; @@ -1323,7 +1454,6 @@ template void SharedFile::parse() { // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly // assigns VER_NDX_LOCAL to this section global symbol. Here is a // workaround for this bug. - uint32_t idx = versyms[i] & ~VERSYM_HIDDEN; if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL && name == "_gp_disp") continue; @@ -1405,7 +1535,7 @@ static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, uint64_t offsetInArchive) : InputFile(BitcodeKind, mb) { - this->archiveName = archiveName; + this->archiveName = std::string(archiveName); std::string path = mb.getBufferIdentifier().str(); if (config->thinLTOIndexOnly) @@ -1417,10 +1547,11 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. - StringRef name = archiveName.empty() - ? saver.save(path) - : saver.save(archiveName + "(" + path + " at " + - utostr(offsetInArchive) + ")"); + StringRef name = + archiveName.empty() + ? saver.save(path) + : saver.save(archiveName + "(" + path::filename(path) + " at " + + utostr(offsetInArchive) + ")"); MemoryBufferRef mbref(mb.getBuffer(), name); obj = CHECK(lto::InputFile::create(mbref), this); @@ -1509,8 +1640,8 @@ void BinaryFile::parse() { STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr}); } -InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) { +InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) { if (isBitcode(mb)) return make(mb, archiveName, offsetInArchive); @@ -1529,14 +1660,13 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName, } void LazyObjFile::fetch() { - if (mb.getBuffer().empty()) + if (fetched) return; + fetched = true; InputFile *file = createObjectFile(mb, archiveName, offsetInArchive); file->groupId = groupId; - mb = {}; - // Copy symbol vector so that the new InputFile doesn't have to // insert the same defined symbols to the symbol table again. file->symbols = std::move(symbols); @@ -1593,21 +1723,22 @@ template void LazyObjFile::parse() { continue; sym->resolve(LazyObject{*this, sym->getName()}); - // MemoryBuffer is emptied if this file is instantiated as ObjFile. - if (mb.getBuffer().empty()) + // If fetched, stop iterating because this->symbols has been transferred + // to the instantiated ObjFile. + if (fetched) return; } return; } } -std::string replaceThinLTOSuffix(StringRef path) { +std::string elf::replaceThinLTOSuffix(StringRef path) { StringRef suffix = config->thinLTOObjectSuffixReplace.first; StringRef repl = config->thinLTOObjectSuffixReplace.second; if (path.consume_back(suffix)) return (path + repl).str(); - return path; + return std::string(path); } template void BitcodeFile::parse(); @@ -1620,15 +1751,12 @@ template void LazyObjFile::parse(); template void LazyObjFile::parse(); template void LazyObjFile::parse(); -template class ObjFile; -template class ObjFile; -template class ObjFile; -template class ObjFile; +template class elf::ObjFile; +template class elf::ObjFile; +template class elf::ObjFile; +template class elf::ObjFile; template void SharedFile::parse(); template void SharedFile::parse(); template void SharedFile::parse(); template void SharedFile::parse(); - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/InputFiles.h b/gnu/llvm/lld/ELF/InputFiles.h index a310ba551bd..7af85e417ca 100644 --- a/gnu/llvm/lld/ELF/InputFiles.h +++ b/gnu/llvm/lld/ELF/InputFiles.h @@ -38,8 +38,6 @@ class DWARFCache; std::string toString(const elf::InputFile *f); namespace elf { -class InputFile; -class InputSectionBase; using llvm::object::Archive; @@ -200,7 +198,7 @@ public: ArrayRef getGlobalSymbols(); ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) { - this->archiveName = archiveName; + this->archiveName = std::string(archiveName); } void parse(bool ignoreComdats = false); @@ -250,11 +248,14 @@ public: // SHT_LLVM_CALL_GRAPH_PROFILE table ArrayRef cgProfile; + // Get cached DWARF information. + DWARFCache *getDwarf(); + private: void initializeSections(bool ignoreComdats); void initializeSymbols(); void initializeJustSymbols(); - void initializeDwarf(); + InputSectionBase *getRelocTarget(const Elf_Shdr &sec); InputSectionBase *createInputSection(const Elf_Shdr &sec); StringRef getSectionName(const Elf_Shdr &sec); @@ -282,8 +283,8 @@ private: // reporting. Linker may find reasonable number of errors in a // single object file, so we cache debugging information in order to // parse it only once for each object file we link. - DWARFCache *dwarf; - llvm::once_flag initDwarfLine; + std::unique_ptr dwarf; + llvm::once_flag initDwarf; }; // LazyObjFile is analogous to ArchiveFile in the sense that @@ -298,7 +299,7 @@ public: LazyObjFile(MemoryBufferRef m, StringRef archiveName, uint64_t offsetInArchive) : InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) { - this->archiveName = archiveName; + this->archiveName = std::string(archiveName); } static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; } @@ -306,6 +307,8 @@ public: template void parse(); void fetch(); + bool fetched = false; + private: uint64_t offsetInArchive; }; @@ -323,6 +326,11 @@ public: // more than once.) void fetch(const Archive::Symbol &sym); + size_t getMemberCount() const; + size_t getFetchedMemberCount() const { return seen.size(); } + + bool parsed = false; + private: std::unique_ptr file; llvm::DenseSet seen; @@ -341,7 +349,7 @@ public: class SharedFile : public ELFFileBase { public: SharedFile(MemoryBufferRef m, StringRef defaultSoName) - : ELFFileBase(SharedKind, m), soName(defaultSoName), + : ELFFileBase(SharedKind, m), soName(std::string(defaultSoName)), isNeeded(!config->asNeeded) {} // This is actually a vector of Elf_Verdef pointers. @@ -366,6 +374,11 @@ public: // Used for --as-needed bool isNeeded; + +private: + template + std::vector parseVerneed(const llvm::object::ELFFile &obj, + const typename ELFT::Shdr *sec); }; class BinaryFile : public InputFile { @@ -384,6 +397,7 @@ inline bool isBitcode(MemoryBufferRef mb) { std::string replaceThinLTOSuffix(StringRef path); +extern std::vector archiveFiles; extern std::vector binaryFiles; extern std::vector bitcodeFiles; extern std::vector lazyObjFiles; diff --git a/gnu/llvm/lld/ELF/InputSection.h b/gnu/llvm/lld/ELF/InputSection.h index fe2c3c516a9..112c6ab49a3 100644 --- a/gnu/llvm/lld/ELF/InputSection.h +++ b/gnu/llvm/lld/ELF/InputSection.h @@ -128,6 +128,26 @@ public: return cast_or_null>(file); } + // If basic block sections are enabled, many code sections could end up with + // one or two jump instructions at the end that could be relaxed to a smaller + // instruction. The members below help trimming the trailing jump instruction + // and shrinking a section. + unsigned bytesDropped = 0; + + void drop_back(uint64_t num) { bytesDropped += num; } + + void push_back(uint64_t num) { + assert(bytesDropped >= num); + bytesDropped -= num; + } + + void trim() { + if (bytesDropped) { + rawData = rawData.drop_back(bytesDropped); + bytesDropped = 0; + } + } + ArrayRef data() const { if (uncompressedSize >= 0) uncompress(); @@ -183,12 +203,25 @@ public: // the mmap'ed output buffer. template void relocate(uint8_t *buf, uint8_t *bufEnd); void relocateAlloc(uint8_t *buf, uint8_t *bufEnd); + static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, + int64_t A, uint64_t P, const Symbol &Sym, + RelExpr Expr); // 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; + + // 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; + // A function compiled with -fsplit-stack calling a function // compiled without -fsplit-stack needs its prologue adjusted. Find // such functions and adjust their prologues. This is very similar @@ -364,6 +397,11 @@ inline bool isDebugSection(const InputSectionBase &sec) { // The list of all input sections. extern std::vector inputSections; +// The set of TOC entries (.toc + addend) for which we should not apply +// toc-indirect to toc-relative relaxation. const Symbol * refers to the +// STT_SECTION symbol associated to the .toc input section. +extern llvm::DenseSet> ppc64noTocRelax; + } // namespace elf std::string toString(const elf::InputSectionBase *); diff --git a/gnu/llvm/lld/ELF/LTO.cpp b/gnu/llvm/lld/ELF/LTO.cpp index 2148ac50029..b8041afed6c 100644 --- a/gnu/llvm/lld/ELF/LTO.cpp +++ b/gnu/llvm/lld/ELF/LTO.cpp @@ -41,9 +41,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; // Creates an empty file to store a list of object files for final // linking of distributed ThinLTO. @@ -59,9 +58,9 @@ static std::unique_ptr openFile(StringRef file) { } static std::string getThinLTOOutputFile(StringRef modulePath) { - return lto::getThinLTOOutputFile(modulePath, - config->thinLTOPrefixReplace.first, - config->thinLTOPrefixReplace.second); + return lto::getThinLTOOutputFile( + std::string(modulePath), std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second)); } static lto::Config createConfig() { @@ -76,6 +75,33 @@ static lto::Config createConfig() { c.Options.FunctionSections = true; c.Options.DataSections = true; + // Check if basic block sections must be used. + // Allowed values for --lto-basicblock-sections are "all", "labels", + // "", or none. This is the equivalent + // of -fbasic-block-sections= flag in clang. + if (!config->ltoBasicBlockSections.empty()) { + if (config->ltoBasicBlockSections == "all") { + c.Options.BBSections = BasicBlockSection::All; + } else if (config->ltoBasicBlockSections == "labels") { + c.Options.BBSections = BasicBlockSection::Labels; + } else if (config->ltoBasicBlockSections == "none") { + c.Options.BBSections = BasicBlockSection::None; + } else { + ErrorOr> MBOrErr = + MemoryBuffer::getFile(config->ltoBasicBlockSections.str()); + if (!MBOrErr) { + error("cannot open " + config->ltoBasicBlockSections + ":" + + MBOrErr.getError().message()); + } else { + c.Options.BBSectionsFuncListBuf = std::move(*MBOrErr); + } + c.Options.BBSections = BasicBlockSection::List; + } + } + + c.Options.UniqueBasicBlockSectionNames = + config->ltoUniqueBasicBlockSectionNames; + if (auto relocModel = getRelocModelFromCMModel()) c.RelocModel = *relocModel; else if (config->relocatable) @@ -97,21 +123,30 @@ static lto::Config createConfig() { c.PTO.SLPVectorization = c.OptLevel > 1; // Set up a custom pipeline if we've been asked to. - c.OptPipeline = config->ltoNewPmPasses; - c.AAPipeline = config->ltoAAPipeline; + c.OptPipeline = std::string(config->ltoNewPmPasses); + c.AAPipeline = std::string(config->ltoAAPipeline); // Set up optimization remarks if we've been asked to. - c.RemarksFilename = config->optRemarksFilename; - c.RemarksPasses = config->optRemarksPasses; + c.RemarksFilename = std::string(config->optRemarksFilename); + c.RemarksPasses = std::string(config->optRemarksPasses); c.RemarksWithHotness = config->optRemarksWithHotness; - c.RemarksFormat = config->optRemarksFormat; + c.RemarksFormat = std::string(config->optRemarksFormat); - c.SampleProfile = config->ltoSampleProfile; + c.SampleProfile = std::string(config->ltoSampleProfile); c.UseNewPM = config->ltoNewPassManager; c.DebugPassManager = config->ltoDebugPassManager; - c.DwoDir = config->dwoDir; + c.DwoDir = std::string(config->dwoDir); - c.CSIRProfile = config->ltoCSProfileFile; + c.HasWholeProgramVisibility = config->ltoWholeProgramVisibility; + c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); + + for (const llvm::StringRef &name : config->thinLTOModulesToCompile) + c.ThinLTOModulesToCompile.emplace_back(name); + + c.TimeTraceEnabled = config->timeTraceEnabled; + c.TimeTraceGranularity = config->timeTraceGranularity; + + c.CSIRProfile = std::string(config->ltoCSProfileFile); c.RunCSIRInstr = config->ltoCSProfileGenerate; if (config->emitLLVM) { @@ -122,6 +157,9 @@ static lto::Config createConfig() { }; } + if (config->ltoEmitAsm) + c.CGFileType = CGFT_AssemblyFile; + if (config->saveTemps) checkError(c.addSaveTemps(config->outputFile.str() + ".", /*UseInputModulePath*/ true)); @@ -138,10 +176,12 @@ BitcodeCompiler::BitcodeCompiler() { if (config->thinLTOIndexOnly) { auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); }; backend = lto::createWriteIndexesThinBackend( - config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second, + std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second), config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite); - } else if (config->thinLTOJobs != -1U) { - backend = lto::createInProcessThinBackend(config->thinLTOJobs); + } else { + backend = lto::createInProcessThinBackend( + llvm::heavyweight_hardware_concurrency(config->thinLTOJobs)); } ltoObj = std::make_unique(createConfig(), backend, @@ -218,7 +258,7 @@ void BitcodeCompiler::add(BitcodeFile &f) { // distributed build system that depends on that behavior. static void thinLTOCreateEmptyIndexFiles() { for (LazyObjFile *f : lazyObjFiles) { - if (!isBitcode(f->mb)) + if (f->fetched || !isBitcode(f->mb)) continue; std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName())); std::unique_ptr os = openFile(path + ".thinlto.bc"); @@ -259,12 +299,14 @@ std::vector BitcodeCompiler::compile() { }, cache)); - // Emit empty index files for non-indexed files - for (StringRef s : thinIndices) { - std::string path = getThinLTOOutputFile(s); - openFile(path + ".thinlto.bc"); - if (config->thinLTOEmitImportsFiles) - openFile(path + ".imports"); + // Emit empty index files for non-indexed files but not in single-module mode. + if (config->thinLTOModulesToCompile.empty()) { + for (StringRef s : thinIndices) { + std::string path = getThinLTOOutputFile(s); + openFile(path + ".thinlto.bc"); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } } if (config->thinLTOIndexOnly) { @@ -291,11 +333,19 @@ std::vector BitcodeCompiler::compile() { } if (config->saveTemps) { - saveBuffer(buf[0], config->outputFile + ".lto.o"); + 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->ltoEmitAsm) { + saveBuffer(buf[0], config->outputFile); + for (unsigned i = 1; i != maxTasks; ++i) + saveBuffer(buf[i], config->outputFile + Twine(i)); + return {}; + } + std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) if (!buf[i].empty()) @@ -306,6 +356,3 @@ std::vector BitcodeCompiler::compile() { ret.push_back(createObjectFile(*file)); return ret; } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/LinkerScript.cpp b/gnu/llvm/lld/ELF/LinkerScript.cpp index c1e1800257c..7314b27659b 100644 --- a/gnu/llvm/lld/ELF/LinkerScript.cpp +++ b/gnu/llvm/lld/ELF/LinkerScript.cpp @@ -21,7 +21,6 @@ #include "Writer.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" -#include "lld/Common/Threads.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/ELF.h" @@ -29,6 +28,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include #include @@ -43,10 +43,10 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support::endian; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { -LinkerScript *script; +LinkerScript *elf::script; static uint64_t getOutputSectionVA(SectionBase *sec) { OutputSection *os = sec->getOutputSection(); @@ -88,7 +88,7 @@ OutputSection *LinkerScript::createOutputSection(StringRef name, if (!secRef) secRef = sec; } - sec->location = location; + sec->location = std::string(location); return sec; } @@ -103,10 +103,11 @@ OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) { static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size, StringRef regionName, StringRef secName) { memRegion->curPos += size; - uint64_t newSize = memRegion->curPos - memRegion->origin; - if (newSize > memRegion->length) + uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue(); + uint64_t length = (memRegion->length)().getValue(); + if (newSize > length) error("section '" + secName + "' will not fit in region '" + regionName + - "': overflowed by " + Twine(newSize - memRegion->length) + " bytes"); + "': overflowed by " + Twine(newSize - length) + " bytes"); } void LinkerScript::expandMemoryRegions(uint64_t size) { @@ -179,7 +180,7 @@ void LinkerScript::addSymbol(SymbolAssignment *cmd) { // write expressions like this: `alignment = 16; . = ALIGN(., alignment)`. uint64_t symValue = value.sec ? 0 : value.getValue(); - Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, + Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, value.type, symValue, 0, sec); Symbol *sym = symtab->insert(cmd->name); @@ -246,32 +247,30 @@ getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) { return changed; } -// This method is used to handle INSERT AFTER statement. Here we rebuild -// the list of script commands to mix sections inserted into. +// Process INSERT [AFTER|BEFORE] commands. For each command, we move the +// specified output section to the designated place. void LinkerScript::processInsertCommands() { - std::vector v; - auto insert = [&](std::vector &from) { - v.insert(v.end(), from.begin(), from.end()); - from.clear(); - }; - - for (BaseCommand *base : sectionCommands) { - if (auto *os = dyn_cast(base)) { - insert(insertBeforeCommands[os->name]); - v.push_back(base); - insert(insertAfterCommands[os->name]); + for (const InsertCommand &cmd : insertCommands) { + // If cmd.os is empty, it may have been discarded by + // adjustSectionsBeforeSorting(). We do not handle such output sections. + auto from = llvm::find(sectionCommands, cmd.os); + if (from == sectionCommands.end()) continue; + sectionCommands.erase(from); + + auto insertPos = llvm::find_if(sectionCommands, [&cmd](BaseCommand *base) { + auto *to = dyn_cast(base); + return to != nullptr && to->name == cmd.where; + }); + if (insertPos == sectionCommands.end()) { + error("unable to insert " + cmd.os->name + + (cmd.isAfter ? " after " : " before ") + cmd.where); + } else { + if (cmd.isAfter) + ++insertPos; + sectionCommands.insert(insertPos, cmd.os); } - v.push_back(base); } - - for (auto &cmds : {insertBeforeCommands, insertAfterCommands}) - for (const std::pair> &p : cmds) - if (!p.second.empty()) - error("unable to INSERT AFTER/BEFORE " + p.first + - ": section not defined"); - - sectionCommands = std::move(v); } // Symbols defined in script should not be inlined by LTO. At the same time @@ -318,14 +317,15 @@ void LinkerScript::assignSymbol(SymbolAssignment *cmd, bool inSec) { cmd->sym->section = v.sec; cmd->sym->value = v.getSectionOffset(); } + cmd->sym->type = v.type; } static std::string getFilename(InputFile *file) { if (!file) return ""; if (file->archiveName.empty()) - return file->getName(); - return (file->archiveName + "(" + file->getName() + ")").str(); + return std::string(file->getName()); + return (file->archiveName + ':' + file->getName()).str(); } bool LinkerScript::shouldKeep(InputSectionBase *s) { @@ -335,7 +335,9 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) { for (InputSectionDescription *id : keptSections) if (id->filePat.match(filename)) for (SectionPattern &p : id->sectionPatterns) - if (p.sectionPat.match(s->name)) + if (p.sectionPat.match(s->name) && + (s->flags & id->withFlags) == id->withFlags && + (s->flags & id->withoutFlags) == 0) return true; return false; } @@ -406,14 +408,15 @@ static void sortInputSections(MutableArrayRef vec, // Compute and remember which sections the InputSectionDescription matches. std::vector -LinkerScript::computeInputSections(const InputSectionDescription *cmd) { +LinkerScript::computeInputSections(const InputSectionDescription *cmd, + ArrayRef sections) { std::vector ret; // Collects all sections that satisfy constraints of Cmd. for (const SectionPattern &pat : cmd->sectionPatterns) { size_t sizeBefore = ret.size(); - for (InputSectionBase *sec : inputSections) { + for (InputSectionBase *sec : sections) { if (!sec->isLive() || sec->parent) continue; @@ -426,10 +429,15 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) { cast(sec)->getRelocatedSection()) continue; + // Check the name early to improve performance in the common case. + if (!pat.sectionPat.match(sec->name)) + continue; + std::string filename = getFilename(sec->file); if (!cmd->filePat.match(filename) || pat.excludedFilePat.match(filename) || - !pat.sectionPat.match(sec->name)) + (sec->flags & cmd->withFlags) != cmd->withFlags || + (sec->flags & cmd->withoutFlags) != 0) continue; ret.push_back(sec); @@ -459,13 +467,29 @@ void LinkerScript::discard(InputSectionBase *s) { discard(ds); } +void LinkerScript::discardSynthetic(OutputSection &outCmd) { + for (Partition &part : partitions) { + if (!part.armExidx || !part.armExidx->isLive()) + continue; + std::vector secs(part.armExidx->exidxSections.begin(), + part.armExidx->exidxSections.end()); + for (BaseCommand *base : outCmd.sectionCommands) + if (auto *cmd = dyn_cast(base)) { + std::vector matches = + computeInputSections(cmd, secs); + for (InputSectionBase *s : matches) + discard(s); + } + } +} + std::vector LinkerScript::createInputSectionList(OutputSection &outCmd) { std::vector ret; for (BaseCommand *base : outCmd.sectionCommands) { if (auto *cmd = dyn_cast(base)) { - cmd->sectionBases = computeInputSections(cmd); + cmd->sectionBases = computeInputSections(cmd, inputSections); for (InputSectionBase *s : cmd->sectionBases) s->parent = &outCmd; ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end()); @@ -486,6 +510,7 @@ void LinkerScript::processSectionCommands() { if (sec->name == "/DISCARD/") { for (InputSectionBase *s : v) discard(s); + discardSynthetic(*sec); sec->sectionCommands.clear(); continue; } @@ -654,8 +679,11 @@ addInputSec(StringMap> &map, auto *firstIsec = cast( cast(sec->sectionCommands[0]) ->sectionBases[0]); - if (firstIsec->getLinkOrderDep()->getOutputSection() != - isec->getLinkOrderDep()->getOutputSection()) + OutputSection *firstIsecOut = + firstIsec->flags & SHF_LINK_ORDER + ? firstIsec->getLinkOrderDep()->getOutputSection() + : nullptr; + if (firstIsecOut != isec->getLinkOrderDep()->getOutputSection()) continue; } @@ -676,14 +704,12 @@ void LinkerScript::addOrphanSections() { std::function add; add = [&](InputSectionBase *s) { if (s->isLive() && !s->parent) { - StringRef name = getOutputSectionName(s); - - if (config->orphanHandling == OrphanHandlingPolicy::Error) - error(toString(s) + " is being placed in '" + name + "'"); - else if (config->orphanHandling == OrphanHandlingPolicy::Warn) - warn(toString(s) + " is being placed in '" + name + "'"); + orphanSections.push_back(s); - if (OutputSection *sec = findByName(sectionCommands, name)) { + StringRef name = getOutputSectionName(s); + if (config->unique) { + v.push_back(createSection(s, name)); + } else if (OutputSection *sec = findByName(sectionCommands, name)) { sec->recordSection(s); } else { if (OutputSection *os = addInputSec(map, s, name)) @@ -727,6 +753,22 @@ void LinkerScript::addOrphanSections() { sectionCommands.insert(sectionCommands.begin(), v.begin(), v.end()); } +void LinkerScript::diagnoseOrphanHandling() const { + for (const InputSectionBase *sec : orphanSections) { + // Input SHT_REL[A] retained by --emit-relocs are ignored by + // computeInputSections(). Don't warn/error. + if (isa(sec) && + cast(sec)->getRelocatedSection()) + continue; + + StringRef name = getOutputSectionName(sec); + if (config->orphanHandling == OrphanHandlingPolicy::Error) + error(toString(sec) + " is being placed in '" + name + "'"); + else if (config->orphanHandling == OrphanHandlingPolicy::Warn) + warn(toString(sec) + " is being placed in '" + name + "'"); + } +} + uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) { bool isTbss = (ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS; @@ -756,9 +798,16 @@ void LinkerScript::output(InputSection *s) { void LinkerScript::switchTo(OutputSection *sec) { ctx->outSec = sec; - uint64_t before = advance(0, 1); - ctx->outSec->addr = advance(0, ctx->outSec->alignment); - expandMemoryRegions(ctx->outSec->addr - before); + uint64_t pos = advance(0, 1); + if (sec->addrExpr && script->hasSectionsCommand) { + // The alignment is ignored. + ctx->outSec->addr = pos; + } else { + // ctx->outSec->alignment is the max of ALIGN and the maximum of input + // section alignments. + ctx->outSec->addr = advance(0, ctx->outSec->alignment); + expandMemoryRegions(ctx->outSec->addr - pos); + } } // This function searches for a memory region to place the given output @@ -806,6 +855,8 @@ void LinkerScript::assignOffsets(OutputSection *sec) { if (!(sec->flags & SHF_ALLOC)) dot = 0; + const bool sameMemRegion = ctx->memRegion == sec->memRegion; + const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr; ctx->memRegion = sec->memRegion; ctx->lmaRegion = sec->lmaRegion; if (ctx->memRegion) @@ -822,20 +873,22 @@ void LinkerScript::assignOffsets(OutputSection *sec) { expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos, ctx->memRegion->name, sec->name); + switchTo(sec); + + // ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or + // AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA + // region is the default, and the two sections are in the same memory region, + // reuse previous lmaOffset; otherwise, reset lmaOffset to 0. This emulates + // heuristics described in + // https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html if (sec->lmaExpr) ctx->lmaOffset = sec->lmaExpr().getValue() - dot; + else if (MemoryRegion *mr = sec->lmaRegion) + ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot; + else if (!sameMemRegion || !prevLMARegionIsDefault) + ctx->lmaOffset = 0; - if (MemoryRegion *mr = sec->lmaRegion) - ctx->lmaOffset = mr->curPos - dot; - - switchTo(sec); - - // If neither AT nor AT> is specified for an allocatable section, the linker - // will set the LMA such that the difference between VMA and LMA for the - // section is the same as the preceding output section in the same region - // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html - // This, however, should only be done by the first "non-header" section - // in the segment. + // Propagate ctx->lmaOffset to the first "non-header" section. if (PhdrEntry *l = ctx->outSec->ptLoad) if (sec == findFirstSection(l)) l->lmaOffset = ctx->lmaOffset; @@ -946,7 +999,7 @@ void LinkerScript::adjustSectionsBeforeSorting() { // We do not want to keep any special flags for output section // in case it is empty. - bool isEmpty = getInputSections(sec).empty(); + bool isEmpty = (getFirstInputSection(sec) == nullptr); if (isEmpty) sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) | SHF_WRITE | SHF_EXECINSTR); @@ -1068,7 +1121,7 @@ void LinkerScript::allocateHeaders(std::vector &phdrs) { LinkerScript::AddressState::AddressState() { for (auto &mri : script->memoryRegions) { MemoryRegion *mr = mri.second; - mr->curPos = mr->origin; + mr->curPos = (mr->origin)().getValue(); } } @@ -1166,8 +1219,14 @@ ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) { } if (Symbol *sym = symtab->find(name)) { - if (auto *ds = dyn_cast(sym)) - return {ds->section, false, ds->value, loc}; + if (auto *ds = dyn_cast(sym)) { + ExprValue v{ds->section, false, ds->value, loc}; + // Retain the original st_type, so that the alias will get the same + // behavior in relocation processing. Any operation will reset st_type to + // STT_NOTYPE. + v.type = ds->type; + return v; + } if (isa(sym)) if (!errorOnMissingSection) return {nullptr, false, 0, loc}; @@ -1195,11 +1254,8 @@ std::vector LinkerScript::getPhdrIndices(OutputSection *cmd) { if (Optional idx = getPhdrIndex(phdrsCommands, s)) ret.push_back(*idx); else if (s != "NONE") - error(cmd->location + ": section header '" + s + + error(cmd->location + ": program header '" + s + "' is not listed in PHDRS"); } return ret; } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/LinkerScript.h b/gnu/llvm/lld/ELF/LinkerScript.h index 25a14e08dad..4a1a5fd71b6 100644 --- a/gnu/llvm/lld/ELF/LinkerScript.h +++ b/gnu/llvm/lld/ELF/LinkerScript.h @@ -59,6 +59,10 @@ struct ExprValue { uint64_t val; uint64_t alignment = 1; + // The original st_type if the expression represents a symbol. Any operation + // resets type to STT_NOTYPE. + uint8_t type = llvm::ELF::STT_NOTYPE; + // Original source location. Used for error messages. std::string loc; }; @@ -109,11 +113,11 @@ struct SymbolAssignment : BaseCommand { std::string commandString; // Address of this assignment command. - unsigned addr; + uint64_t addr; // Size of this assignment command. This is usually 0, but if // you move '.' this may be greater than 0. - unsigned size; + uint64_t size; }; // Linker scripts allow additional constraints to be put on output sections. @@ -126,14 +130,14 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite }; // target memory. Instances of the struct are created by parsing the // MEMORY command. struct MemoryRegion { - MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags, + MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags, uint32_t negFlags) - : name(name), origin(origin), length(length), flags(flags), + : name(std::string(name)), origin(origin), length(length), flags(flags), negFlags(negFlags) {} std::string name; - uint64_t origin; - uint64_t length; + Expr origin; + Expr length; uint32_t flags; uint32_t negFlags; uint64_t curPos = 0; @@ -155,14 +159,16 @@ struct SectionPattern { }; struct InputSectionDescription : BaseCommand { - InputSectionDescription(StringRef filePattern) - : BaseCommand(InputSectionKind), filePat(filePattern) {} + InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0, + uint64_t withoutFlags = 0) + : BaseCommand(InputSectionKind), filePat(filePattern), + withFlags(withFlags), withoutFlags(withoutFlags) {} static bool classof(const BaseCommand *c) { return c->kind == InputSectionKind; } - StringMatcher filePat; + SingleStringMatcher filePat; // Input sections that matches at least one of SectionPatterns // will be associated with this InputSectionDescription. @@ -180,6 +186,10 @@ struct InputSectionDescription : BaseCommand { // they were created in. This is used to insert newly created ThunkSections // into Sections at the end of a createThunks() pass. std::vector> thunkSections; + + // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command. + uint64_t withFlags; + uint64_t withoutFlags; }; // Represents BYTE(), SHORT(), LONG(), or QUAD(). @@ -202,6 +212,12 @@ struct ByteCommand : BaseCommand { unsigned size; }; +struct InsertCommand { + OutputSection *os; + bool isAfter; + StringRef where; +}; + struct PhdrsCommand { StringRef name; unsigned type = llvm::ELF::PT_NULL; @@ -233,10 +249,13 @@ class LinkerScript final { void expandMemoryRegions(uint64_t size); std::vector - computeInputSections(const InputSectionDescription *); + computeInputSections(const InputSectionDescription *, + ArrayRef); std::vector createInputSectionList(OutputSection &cmd); + void discardSynthetic(OutputSection &); + std::vector getPhdrIndices(OutputSection *sec); MemoryRegion *findMemoryRegion(OutputSection *sec); @@ -270,6 +289,7 @@ public: ExprValue getSymbolValue(StringRef name, const Twine &loc); void addOrphanSections(); + void diagnoseOrphanHandling() const; void adjustSectionsBeforeSorting(); void adjustSectionsAfterSorting(); @@ -305,10 +325,12 @@ public: // A list of symbols referenced by the script. std::vector referencedSymbols; - // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need - // to be inserted into SECTIONS commands list. - llvm::DenseMap> insertAfterCommands; - llvm::DenseMap> insertBeforeCommands; + // Used to implement INSERT [AFTER|BEFORE]. Contains output sections that need + // to be reordered. + std::vector insertCommands; + + // Sections that will be warned/errored by --orphan-handling. + std::vector orphanSections; }; extern LinkerScript *script; diff --git a/gnu/llvm/lld/ELF/MapFile.cpp b/gnu/llvm/lld/ELF/MapFile.cpp index e5f5c4f4ff2..12cffead1f8 100644 --- a/gnu/llvm/lld/ELF/MapFile.cpp +++ b/gnu/llvm/lld/ELF/MapFile.cpp @@ -26,16 +26,16 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "lld/Common/Strings.h" -#include "lld/Common/Threads.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { using SymbolMapTy = DenseMap>; static constexpr char indent8[] = " "; // 8 spaces @@ -138,7 +138,7 @@ static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) { } } -void writeMapFile() { +void elf::writeMapFile() { if (config->mapFile.empty()) return; @@ -227,7 +227,7 @@ static void print(StringRef a, StringRef b) { // // In this case, strlen is defined by libc.so.6 and used by other two // files. -void writeCrossReferenceTable() { +void elf::writeCrossReferenceTable() { if (!config->cref) return; @@ -259,5 +259,20 @@ void writeCrossReferenceTable() { } } -} // namespace elf -} // namespace lld +void elf::writeArchiveStats() { + if (config->printArchiveStats.empty()) + return; + + std::error_code ec; + raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None); + if (ec) { + error("--print-archive-stats=: cannot open " + config->printArchiveStats + + ": " + ec.message()); + return; + } + + os << "members\tfetched\tarchive\n"; + for (const ArchiveFile *f : archiveFiles) + os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t' + << f->getName() << '\n'; +} diff --git a/gnu/llvm/lld/ELF/MapFile.h b/gnu/llvm/lld/ELF/MapFile.h index 7e7938919ed..c4da18f8ad7 100644 --- a/gnu/llvm/lld/ELF/MapFile.h +++ b/gnu/llvm/lld/ELF/MapFile.h @@ -13,6 +13,7 @@ namespace lld { namespace elf { void writeMapFile(); void writeCrossReferenceTable(); +void writeArchiveStats(); } // namespace elf } // namespace lld diff --git a/gnu/llvm/lld/ELF/MarkLive.cpp b/gnu/llvm/lld/ELF/MarkLive.cpp index bb0105c2892..28e13e8c123 100644 --- a/gnu/llvm/lld/ELF/MarkLive.cpp +++ b/gnu/llvm/lld/ELF/MarkLive.cpp @@ -31,17 +31,17 @@ #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/TimeProfiler.h" #include #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::elf; -namespace endian = llvm::support::endian; - -namespace lld { -namespace elf { namespace { template class MarkLive { public: @@ -141,7 +141,7 @@ void MarkLive::scanEhFrameSection(EhInputSection &eh, if (firstRelI == (unsigned)-1) continue; - if (endian::read32(piece.data().data() + 4) == 0) { + if (read32(piece.data().data() + 4) == 0) { // This is a CIE, we only need to worry about the first relocation. It is // known to point to the personality function. resolveReloc(eh, rels[firstRelI], false); @@ -322,7 +322,8 @@ template void MarkLive::moveToMain() { // Before calling this function, Live bits are off for all // input sections. This function make some or all of them on // so that they are emitted to the output file. -template void markLive() { +template void elf::markLive() { + llvm::TimeTraceScope timeScope("markLive"); // If -gc-sections is not given, no sections are removed. if (!config->gcSections) { for (InputSectionBase *sec : inputSections) @@ -390,10 +391,7 @@ template void markLive() { message("removing unused section " + toString(sec)); } -template void markLive(); -template void markLive(); -template void markLive(); -template void markLive(); - -} // namespace elf -} // namespace lld +template void elf::markLive(); +template void elf::markLive(); +template void elf::markLive(); +template void elf::markLive(); diff --git a/gnu/llvm/lld/ELF/Options.td b/gnu/llvm/lld/ELF/Options.td index a0c69342b89..c3c1309aca1 100644 --- a/gnu/llvm/lld/ELF/Options.td +++ b/gnu/llvm/lld/ELF/Options.td @@ -1,5 +1,23 @@ include "llvm/Option/OptParser.td" +// Convenience classes for long options which only accept two dashes. For lld +// specific or newer long options, we prefer two dashes to avoid collision with +// short options. For many others, we have to accept both forms to be compatible +// with GNU ld. +class FF : Flag<["--"], name>; +class JJ: Joined<["--"], name>; + +multiclass EEq { + def NAME: Separate<["--"], name>; + def NAME # _eq: Joined<["--"], name # "=">, Alias(NAME)>, + HelpText; +} + +multiclass BB { + def NAME: Flag<["--"], name>, HelpText; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText; +} + // For options whose names are multiple letters, either one dash or // two can precede the option name except those that start with 'o'. class F: Flag<["--", "-"], name>; @@ -42,6 +60,10 @@ defm compress_debug_sections: defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"=">; +defm optimize_bb_jumps: BB<"optimize-bb-jumps", + "Remove direct jumps at the end to the next basic block", + "Do not remove any direct jumps at the end to the next basic block (default)">; + defm split_stack_adjust_size : Eq<"split-stack-adjust-size", "Specify adjustment to stack size when a split-stack function calls a " @@ -69,11 +91,11 @@ defm allow_shlib_undefined: B<"allow-shlib-undefined", "Allow unresolved references in shared libraries (default when linking a shared library)", "Do not allow unresolved references in shared libraries (default when linking an executable)">; -defm apply_dynamic_relocs: B<"apply-dynamic-relocs", +defm apply_dynamic_relocs: BB<"apply-dynamic-relocs", "Apply link-time values for dynamic relocations", "Do not apply link-time values for dynamic relocations (default)">; -defm dependent_libraries: B<"dependent-libraries", +defm dependent_libraries: BB<"dependent-libraries", "Process dependent library specifiers from input files (default)", "Ignore dependent library specifiers from input files">; @@ -84,7 +106,7 @@ defm as_needed: B<"as-needed", defm call_graph_ordering_file: Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">; -defm call_graph_profile_sort: B<"call-graph-profile-sort", +defm call_graph_profile_sort: BB<"call-graph-profile-sort", "Reorder sections with call graph profile (default)", "Do not reorder sections with call graph profile">; @@ -123,7 +145,12 @@ def discard_none: F<"discard-none">, defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">; -defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">; +defm dynamic_list : Eq<"dynamic-list", + "Read a list of dynamic symbols. (executable) Put matched non-local defined" + "symbols to the dynamic symbol table. (shared object) References to matched" + "non-local STV_DEFAULT symbols shouldn't be bound to definitions within the " + "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">, + MetaVarName<"">; defm eh_frame_hdr: B<"eh-frame-hdr", "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header", @@ -151,7 +178,7 @@ def error_unresolved_symbols: F<"error-unresolved-symbols">, defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">; -defm execute_only: B<"execute-only", +defm execute_only: BB<"execute-only", "Mark executable sections unreadable", "Mark executable sections readable (default)">; @@ -159,8 +186,12 @@ defm export_dynamic: B<"export-dynamic", "Put symbols in the dynamic symbol table", "Do not put symbols in the dynamic symbol table (default)">; -defm export_dynamic_symbol: - Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">; +defm export_dynamic_symbol : EEq<"export-dynamic-symbol", + "(executable) Put matched symbols in the dynamic symbol table. " + "(shared object) References to matched non-local STV_DEFAULT symbols " + "shouldn't be bound to definitions within the shared object. " + "Does not imply -Bsymbolic.">, + MetaVarName<"glob">; defm fatal_warnings: B<"fatal-warnings", "Treat warnings as errors", @@ -183,11 +214,11 @@ defm gc_sections: B<"gc-sections", "Enable garbage collection of unused sections", "Disable garbage collection of unused sections (default)">; -defm gdb_index: B<"gdb-index", +defm gdb_index: BB<"gdb-index", "Generate .gdb_index section", "Do not generate .gdb_index section (default)">; -defm gnu_unique: B<"gnu-unique", +defm gnu_unique: BB<"gnu-unique", "Enable STB_GNU_UNIQUE symbol binding (default)", "Disable STB_GNU_UNIQUE symbol binding">; @@ -201,9 +232,8 @@ def icf_safe: F<"icf=safe">, HelpText<"Enable safe identical code folding">; def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">; -defm ignore_function_address_equality: B<"ignore-function-address-equality", - "lld can break the address equality of functions", - "lld cannot break the address equality of functions">; +def ignore_function_address_equality: F<"ignore-function-address-equality">, + HelpText<"lld can break the address equality of functions">; def ignore_data_address_equality: F<"ignore-data-address-equality">, HelpText<"lld can break the address equality of data">; @@ -228,7 +258,7 @@ defm merge_exidx_entries: B<"merge-exidx-entries", "Enable merging .ARM.exidx entries (default)", "Disable merging .ARM.exidx entries">; -defm mmap_output_file: B<"mmap-output-file", +defm mmap_output_file: BB<"mmap-output-file", "Mmap the output file for writing (default)", "Do not mmap the output file for writing">; @@ -253,9 +283,6 @@ def no_nmagic: F<"no-nmagic">, MetaVarName<"">, def no_omagic: F<"no-omagic">, MetaVarName<"">, HelpText<"Do not set the text data sections to be writable, page align sections (default)">; -def no_rosegment: F<"no-rosegment">, - HelpText<"Do not put read-only non-executable sections in their own segment">; - def no_undefined: F<"no-undefined">, HelpText<"Report unresolved symbols even if the linker is creating a shared library">; @@ -265,7 +292,7 @@ def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"">, def oformat: Separate<["--"], "oformat">, MetaVarName<"">, HelpText<"Specify the binary format for the output object file">; -def omagic: Flag<["--"], "omagic">, MetaVarName<"">, +def omagic: FF<"omagic">, MetaVarName<"">, HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">; defm orphan_handling: @@ -275,7 +302,7 @@ defm pack_dyn_relocs: Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">, MetaVarName<"[none,android,relr,android+relr]">; -defm use_android_relr_tags: B<"use-android-relr-tags", +defm use_android_relr_tags: BB<"use-android-relr-tags", "Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*", "Use SHT_RELR / DT_RELR* tags (default)">; @@ -294,6 +321,10 @@ defm print_icf_sections: B<"print-icf-sections", "List identical folded sections", "Do not list identical folded sections (default)">; +def print_archive_stats: J<"print-archive-stats=">, + HelpText<"Write archive usage statistics to the specified file. " + "Print the numbers of members and fetched members for each archive">; + defm print_symbol_order: Eq<"print-symbol-order", "Print a symbol order specified by --call-graph-ordering-file into the specified file">; @@ -308,6 +339,10 @@ def print_map: F<"print-map">, defm reproduce: Eq<"reproduce", "Write a tar file containing input files and command line options to reproduce link">; +defm rosegment: BB<"rosegment", + "Put read-only non-executable sections in their own segment (default)", + "Do not put read-only non-executable sections in their own segment">; + defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">; def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; @@ -351,9 +386,16 @@ defm target2: Eq<"target2", "Interpret R_ARM_TARGET2 as , where is one of rel, abs, or got-rel">, MetaVarName<"">; -defm threads: B<"threads", - "Run the linker multi-threaded (default)", - "Do not run the linker multi-threaded">; +defm threads + : Eq<"threads", + "Number of threads. '1' disables multi-threading. By default all " + "available hardware threads are used">; + +def time_trace: F<"time-trace">, HelpText<"Record time trace">; +def time_trace_file_eq: J<"time-trace-file=">, HelpText<"Specify time trace output file">; + +defm time_trace_granularity: Eq<"time-trace-granularity", + "Minimum time granularity (in microseconds) traced by time profiler">; defm toc_optimize : B<"toc-optimize", "(PowerPC64) Enable TOC related optimizations (default)", @@ -369,6 +411,8 @@ defm undefined: Eq<"undefined", "Force undefined symbol during linking">, defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">, MetaVarName<"">; +def unique: F<"unique">, HelpText<"Creates a separate output section for every orphan input section">; + defm unresolved_symbols: Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">; @@ -387,19 +431,25 @@ def version: F<"version">, HelpText<"Display the version number and exit">; defm version_script: Eq<"version-script", "Read a version script">; -defm warn_backrefs: B<"warn-backrefs", +defm warn_backrefs: BB<"warn-backrefs", "Warn about backward symbol references to fetch archive members", "Do not warn about backward symbol references to fetch archive members (default)">; +defm warn_backrefs_exclude + : EEq<"warn-backrefs-exclude", + "Glob describing an archive (or an object file within --start-lib) " + "which should be ignored for --warn-backrefs.">, + MetaVarName<"">; + defm warn_common: B<"warn-common", "Warn about duplicate common symbols", "Do not warn about duplicate common symbols (default)">; -defm warn_ifunc_textrel: B<"warn-ifunc-textrel", +defm warn_ifunc_textrel: BB<"warn-ifunc-textrel", "Warn about using ifunc symbols with text relocations", "Do not warn about using ifunc symbols with text relocations (default)">; -defm warn_symbol_ordering: B<"warn-symbol-ordering", +defm warn_symbol_ordering: BB<"warn-symbol-ordering", "Warn about problems with the symbol ordering file (default)", "Do not warn about problems with the symbol ordering file">; @@ -439,7 +489,6 @@ def: Separate<["-"], "F">, Alias, HelpText<"Alias for --filter">; def: Separate<["-"], "b">, Alias, HelpText<"Alias for --format">; def: JoinedOrSeparate<["-"], "l">, Alias, HelpText<"Alias for --library">; def: JoinedOrSeparate<["-"], "L">, Alias, HelpText<"Alias for --library-path">; -def: F<"nopie">, Alias, HelpText<"Alias for --no-pie">; def: F<"no-pic-executable">, Alias, HelpText<"Alias for --no-pie">; def: Flag<["-"], "n">, Alias, HelpText<"Alias for --nmagic">; def: Flag<["-"], "N">, Alias, HelpText<"Alias for --omagic">; @@ -462,84 +511,99 @@ def: JoinedOrSeparate<["-"], "u">, Alias, HelpText<"Alias for --undef def: Flag<["-"], "V">, Alias, HelpText<"Alias for --version">; // LTO-related options. -def lto_aa_pipeline: J<"lto-aa-pipeline=">, +def lto_aa_pipeline: JJ<"lto-aa-pipeline=">, HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">; -def lto_debug_pass_manager: F<"lto-debug-pass-manager">, +def lto_debug_pass_manager: FF<"lto-debug-pass-manager">, HelpText<"Debug new pass manager">; -def lto_new_pass_manager: F<"lto-new-pass-manager">, +def lto_emit_asm: FF<"lto-emit-asm">, + HelpText<"Emit assembly code">; +def lto_new_pass_manager: FF<"lto-new-pass-manager">, HelpText<"Use new pass manager">; -def lto_newpm_passes: J<"lto-newpm-passes=">, +def lto_newpm_passes: JJ<"lto-newpm-passes=">, HelpText<"Passes to run during LTO">; -def lto_O: J<"lto-O">, MetaVarName<"">, +def lto_O: JJ<"lto-O">, MetaVarName<"">, HelpText<"Optimization level for LTO">; -def lto_partitions: J<"lto-partitions=">, +def lto_partitions: JJ<"lto-partitions=">, HelpText<"Number of LTO codegen partitions">; -def lto_cs_profile_generate: F<"lto-cs-profile-generate">, +def lto_cs_profile_generate: FF<"lto-cs-profile-generate">, HelpText<"Perform context sensitive PGO instrumentation">; -def lto_cs_profile_file: J<"lto-cs-profile-file=">, +def lto_cs_profile_file: JJ<"lto-cs-profile-file=">, HelpText<"Context sensitive profile file path">; -def lto_obj_path_eq: J<"lto-obj-path=">; -def lto_sample_profile: J<"lto-sample-profile=">, +def lto_obj_path_eq: JJ<"lto-obj-path=">; +def lto_sample_profile: JJ<"lto-sample-profile=">, HelpText<"Sample profile file path">; +def lto_whole_program_visibility: FF<"lto-whole-program-visibility">, + HelpText<"Asserts that the LTO link has whole program visibility">; def disable_verify: F<"disable-verify">; defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">; def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">, HelpText<"YAML output file for optimization remarks">; def opt_remarks_passes: Separate<["--"], "opt-remarks-passes">, HelpText<"Regex for the passes that need to be serialized to the output file">; -def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">, +def opt_remarks_with_hotness: FF<"opt-remarks-with-hotness">, HelpText<"Include hotness information in the optimization remarks file">; def opt_remarks_format: Separate<["--"], "opt-remarks-format">, HelpText<"The format used for serializing remarks (default: YAML)">; -defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">; def save_temps: F<"save-temps">; -def thinlto_cache_dir: J<"thinlto-cache-dir=">, +def lto_basicblock_sections: JJ<"lto-basicblock-sections=">, + HelpText<"Enable basic block sections for LTO">; +defm lto_unique_bb_section_names: BB<"lto-unique-bb-section-names", + "Give unique names to every basic block section for LTO", + "Do not give unique names to every basic block section for LTO (default)">; +def shuffle_sections: JJ<"shuffle-sections=">, MetaVarName<"">, + HelpText<"Shuffle input sections using the given seed. If 0, use a random seed">; +def thinlto_cache_dir: JJ<"thinlto-cache-dir=">, HelpText<"Path to ThinLTO cached object file directory">; -defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">; -def thinlto_emit_imports_files: F<"thinlto-emit-imports-files">; -def thinlto_index_only: F<"thinlto-index-only">; -def thinlto_index_only_eq: J<"thinlto-index-only=">; -def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; -def thinlto_object_suffix_replace_eq: J<"thinlto-object-suffix-replace=">; -def thinlto_prefix_replace_eq: J<"thinlto-prefix-replace=">; - -def: J<"plugin-opt=O">, Alias, HelpText<"Alias for -lto-O">; +defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">; +def thinlto_emit_imports_files: FF<"thinlto-emit-imports-files">; +def thinlto_index_only: FF<"thinlto-index-only">; +def thinlto_index_only_eq: JJ<"thinlto-index-only=">; +def thinlto_jobs: JJ<"thinlto-jobs=">, + HelpText<"Number of ThinLTO jobs. Default to --threads=">; +def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">; +def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">; +def thinlto_single_module_eq: JJ<"thinlto-single-module=">, + HelpText<"Specific a single module to compile in ThinLTO mode, for debugging only">; + +def: J<"plugin-opt=O">, Alias, HelpText<"Alias for --lto-O">; def: F<"plugin-opt=debug-pass-manager">, - Alias, HelpText<"Alias for -lto-debug-pass-manager">; -def: F<"plugin-opt=disable-verify">, Alias, HelpText<"Alias for -disable-verify">; + Alias, HelpText<"Alias for --lto-debug-pass-manager">; +def: F<"plugin-opt=disable-verify">, Alias, HelpText<"Alias for --disable-verify">; def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">, HelpText<"Directory to store .dwo files when LTO and debug fission are used">; +def plugin_opt_emit_asm: F<"plugin-opt=emit-asm">, + Alias, HelpText<"Alias for --lto-emit-asm">; def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">; -def: J<"plugin-opt=jobs=">, Alias, HelpText<"Alias for -thinlto-jobs">; -def: J<"plugin-opt=lto-partitions=">, Alias, HelpText<"Alias for -lto-partitions">; +def: J<"plugin-opt=jobs=">, Alias, HelpText<"Alias for --thinlto-jobs">; +def: J<"plugin-opt=lto-partitions=">, Alias, HelpText<"Alias for --lto-partitions">; def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">; def: F<"plugin-opt=new-pass-manager">, - Alias, HelpText<"Alias for -lto-new-pass-manager">; + Alias, HelpText<"Alias for --lto-new-pass-manager">; def: F<"plugin-opt=cs-profile-generate">, - Alias, HelpText<"Alias for -lto-cs-profile-generate">; + Alias, HelpText<"Alias for --lto-cs-profile-generate">; def: J<"plugin-opt=cs-profile-path=">, - Alias, HelpText<"Alias for -lto-cs-profile-file">; + Alias, HelpText<"Alias for --lto-cs-profile-file">; def: J<"plugin-opt=obj-path=">, Alias, - HelpText<"Alias for -lto-obj-path=">; + HelpText<"Alias for --lto-obj-path=">; def: J<"plugin-opt=sample-profile=">, - Alias, HelpText<"Alias for -lto-sample-profile">; -def: F<"plugin-opt=save-temps">, Alias, HelpText<"Alias for -save-temps">; + Alias, HelpText<"Alias for --lto-sample-profile">; +def: F<"plugin-opt=save-temps">, Alias, HelpText<"Alias for --save-temps">; def: F<"plugin-opt=thinlto-emit-imports-files">, Alias, - HelpText<"Alias for -thinlto-emit-imports-files">; + HelpText<"Alias for --thinlto-emit-imports-files">; def: F<"plugin-opt=thinlto-index-only">, Alias, - HelpText<"Alias for -thinlto-index-only">; + HelpText<"Alias for --thinlto-index-only">; def: J<"plugin-opt=thinlto-index-only=">, Alias, - HelpText<"Alias for -thinlto-index-only=">; + HelpText<"Alias for --thinlto-index-only=">; def: J<"plugin-opt=thinlto-object-suffix-replace=">, Alias, - HelpText<"Alias for -thinlto-object-suffix-replace=">; + HelpText<"Alias for --thinlto-object-suffix-replace=">; def: J<"plugin-opt=thinlto-prefix-replace=">, Alias, - HelpText<"Alias for -thinlto-prefix-replace=">; + HelpText<"Alias for --thinlto-prefix-replace=">; // Ignore LTO plugin-related options. // clang -flto passes -plugin and -plugin-opt to the linker. This is required @@ -550,10 +614,17 @@ def: J<"plugin-opt=thinlto-prefix-replace=">, // --version output. defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">; -def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">; -def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">; -def plugin_opt_thinlto: J<"plugin-opt=thinlto">; -def plugin_opt_slash: J<"plugin-opt=/">; +def plugin_opt_eq_minus: J<"plugin-opt=-">, + HelpText<"Specify an LLVM option for compatibility with LLVMgold.so">; +def: J<"plugin-opt=thinlto">; + +// Ignore GCC collect2 LTO plugin related options. Note that we don't support +// GCC LTO, but GCC collect2 passes these options even in non-LTO mode. +def: J<"plugin-opt=-fresolution=">; +def: J<"plugin-opt=-pass-through=">; +// This may be either an unhandled LLVMgold.so feature or GCC passed +// -plugin-opt=path/to/{liblto_plugin.so,lto-wrapper} +def plugin_opt_eq : J<"plugin-opt=">; // Options listed below are silently ignored for now for compatibility. def: F<"detect-odr-violations">; @@ -564,6 +635,7 @@ def: F<"no-copy-dt-needed-entries">; def: F<"no-ctors-in-init-array">; def: F<"no-keep-memory">; def: F<"no-pipeline-knowledge">; +def: F<"no-relax">; def: F<"no-warn-mismatch">; def: Flag<["-"], "p">; def: Separate<["--", "-"], "rpath-link">; diff --git a/gnu/llvm/lld/ELF/OutputSections.cpp b/gnu/llvm/lld/ELF/OutputSections.cpp index b609878be31..881c375a115 100644 --- a/gnu/llvm/lld/ELF/OutputSections.cpp +++ b/gnu/llvm/lld/ELF/OutputSections.cpp @@ -14,11 +14,11 @@ #include "Target.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" -#include "lld/Common/Threads.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Compression.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/SHA1.h" #include @@ -27,9 +27,9 @@ using namespace llvm::dwarf; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { uint8_t *Out::bufferStart; uint8_t Out::first; PhdrEntry *Out::tlsPhdr; @@ -39,7 +39,7 @@ OutputSection *Out::preinitArray; OutputSection *Out::initArray; OutputSection *Out::finiArray; -std::vector outputSections; +std::vector elf::outputSections; uint32_t OutputSection::getPhdrFlags() const { uint32_t ret = 0; @@ -77,10 +77,14 @@ OutputSection::OutputSection(StringRef name, uint32_t type, uint64_t flags) // to be allocated for nobits sections. Other ones don't require // any special treatment on top of progbits, so there doesn't // seem to be a harm in merging them. +// +// NOTE: clang since rL252300 emits SHT_X86_64_UNWIND .eh_frame sections. Allow +// them to be merged into SHT_PROGBITS .eh_frame (GNU as .cfi_*). static bool canMergeToProgbits(unsigned type) { return type == SHT_NOBITS || type == SHT_PROGBITS || type == SHT_INIT_ARRAY || type == SHT_PREINIT_ARRAY || type == SHT_FINI_ARRAY || - type == SHT_NOTE; + type == SHT_NOTE || + (type == SHT_X86_64_UNWIND && config->emachine == EM_X86_64); } // Record that isec will be placed in the OutputSection. isec does not become @@ -225,7 +229,7 @@ static void sortByOrder(MutableArrayRef in, in[i] = v[i].second; } -uint64_t getHeaderSize() { +uint64_t elf::getHeaderSize() { if (config->oFormatBinary) return 0; return Out::elfHeader->size + Out::programHeaders->size; @@ -242,6 +246,25 @@ void OutputSection::sort(llvm::function_ref order) { sortByOrder(isd->sections, order); } +static void nopInstrFill(uint8_t *buf, size_t size) { + if (size == 0) + return; + unsigned i = 0; + if (size == 0) + return; + std::vector> nopFiller = *target->nopInstrs; + unsigned num = size / nopFiller.back().size(); + for (unsigned c = 0; c < num; ++c) { + memcpy(buf + i, nopFiller.back().data(), nopFiller.back().size()); + i += nopFiller.back().size(); + } + unsigned remaining = size - i; + if (!remaining) + return; + assert(nopFiller[remaining - 1].size() == remaining); + memcpy(buf + i, nopFiller[remaining - 1].data(), remaining); +} + // Fill [Buf, Buf + Size) with Filler. // This is used for linker script "=fillexp" command. static void fill(uint8_t *buf, size_t size, @@ -330,7 +353,11 @@ template void OutputSection::writeTo(uint8_t *buf) { end = buf + size; else end = buf + sections[i + 1]->outSecOff; - fill(start, end - start, filler); + if (isec->nopFiller) { + assert(target->nopInstrs); + nopInstrFill(start, end - start); + } else + fill(start, end - start, filler); } }); @@ -356,8 +383,7 @@ static void finalizeShtGroup(OutputSection *os, } void OutputSection::finalize() { - std::vector v = getInputSections(this); - InputSection *first = v.empty() ? nullptr : v[0]; + InputSection *first = getFirstInputSection(this); if (flags & SHF_LINK_ORDER) { // We must preserve the link order dependency of sections with the @@ -456,7 +482,7 @@ void OutputSection::sortCtorsDtors() { // 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. -int getPriority(StringRef s) { +int elf::getPriority(StringRef s) { size_t pos = s.rfind('.'); if (pos == StringRef::npos) return 65536; @@ -466,7 +492,15 @@ int getPriority(StringRef s) { return v; } -std::vector getInputSections(OutputSection *os) { +InputSection *elf::getFirstInputSection(const OutputSection *os) { + for (BaseCommand *base : os->sectionCommands) + if (auto *isd = dyn_cast(base)) + if (!isd->sections.empty()) + return isd->sections[0]; + return nullptr; +} + +std::vector elf::getInputSections(const OutputSection *os) { std::vector ret; for (BaseCommand *base : os->sectionCommands) if (auto *isd = dyn_cast(base)) @@ -507,6 +541,3 @@ template void OutputSection::maybeCompress(); template void OutputSection::maybeCompress(); template void OutputSection::maybeCompress(); template void OutputSection::maybeCompress(); - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/OutputSections.h b/gnu/llvm/lld/ELF/OutputSections.h index a24294eedf3..d5686f11ec8 100644 --- a/gnu/llvm/lld/ELF/OutputSections.h +++ b/gnu/llvm/lld/ELF/OutputSections.h @@ -118,7 +118,8 @@ private: int getPriority(StringRef s); -std::vector getInputSections(OutputSection* os); +InputSection *getFirstInputSection(const OutputSection *os); +std::vector getInputSections(const OutputSection *os); // All output sections that are handled by the linker specially are // globally accessible. Writer initializes them, so don't use them diff --git a/gnu/llvm/lld/ELF/Relocations.h b/gnu/llvm/lld/ELF/Relocations.h index bfec1e62885..ec59c63410d 100644 --- a/gnu/llvm/lld/ELF/Relocations.h +++ b/gnu/llvm/lld/ELF/Relocations.h @@ -24,6 +24,7 @@ class SectionBase; // Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL. using RelType = uint32_t; +using JumpModType = uint32_t; // List of target-independent relocation types. Relocations read // from files are converted to these types so that the main code @@ -80,6 +81,7 @@ enum RelExpr { R_AARCH64_PAGE_PC, R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, R_AARCH64_TLSDESC_PAGE, + R_ARM_PCA, R_ARM_SBREL, R_MIPS_GOTREL, R_MIPS_GOT_GP, @@ -107,6 +109,15 @@ struct Relocation { Symbol *sym; }; +// Manipulate jump instructions with these modifiers. These are used to relax +// jump instruction opcodes at basic block boundaries and are particularly +// useful when basic block sections are enabled. +struct JumpInstrMod { + JumpModType original; + uint64_t offset; + unsigned size; +}; + // This function writes undefined symbol diagnostics to an internal buffer. // Call reportUndefinedSymbols() after calling scanRelocations() to emit // the diagnostics. @@ -114,6 +125,9 @@ template void scanRelocations(InputSectionBase &); template void reportUndefinedSymbols(); +void hexagonTLSSymbolUpdate(ArrayRef outputSections); +bool hexagonNeedsTLSSymbol(ArrayRef outputSections); + class ThunkSection; class Thunk; struct InputSectionDescription; diff --git a/gnu/llvm/lld/ELF/ScriptLexer.cpp b/gnu/llvm/lld/ELF/ScriptLexer.cpp index 1fed3d06227..9ac8447eef0 100644 --- a/gnu/llvm/lld/ELF/ScriptLexer.cpp +++ b/gnu/llvm/lld/ELF/ScriptLexer.cpp @@ -36,9 +36,9 @@ #include "llvm/ADT/Twine.h" using namespace llvm; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { // Returns a whole line containing the current token. StringRef ScriptLexer::getLine() { StringRef s = getCurrentMB().getBuffer(); @@ -66,7 +66,7 @@ size_t ScriptLexer::getColumnNumber() { } std::string ScriptLexer::getCurrentLocation() { - std::string filename = getCurrentMB().getBufferIdentifier(); + std::string filename = std::string(getCurrentMB().getBufferIdentifier()); return (filename + ":" + Twine(getLineNumber())).str(); } @@ -189,7 +189,7 @@ static std::vector tokenizeExpr(StringRef s) { break; } - // Get a token before the opreator. + // Get a token before the operator. if (e != 0) ret.push_back(s.substr(0, e)); @@ -302,6 +302,3 @@ MemoryBufferRef ScriptLexer::getCurrentMB() { return mb; llvm_unreachable("getCurrentMB: failed to find a token"); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/ScriptLexer.h b/gnu/llvm/lld/ELF/ScriptLexer.h index 98e4cac95a7..306d428e98f 100644 --- a/gnu/llvm/lld/ELF/ScriptLexer.h +++ b/gnu/llvm/lld/ELF/ScriptLexer.h @@ -40,13 +40,14 @@ public: bool inExpr = false; size_t pos = 0; +protected: + MemoryBufferRef getCurrentMB(); + private: void maybeSplitExpr(); StringRef getLine(); size_t getLineNumber(); size_t getColumnNumber(); - - MemoryBufferRef getCurrentMB(); }; } // namespace elf diff --git a/gnu/llvm/lld/ELF/ScriptParser.cpp b/gnu/llvm/lld/ELF/ScriptParser.cpp index 80ec8b655b0..fea6b7a274e 100644 --- a/gnu/llvm/lld/ELF/ScriptParser.cpp +++ b/gnu/llvm/lld/ELF/ScriptParser.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/ScopedPrinter.h" #include #include #include @@ -37,9 +38,9 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::support::endian; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { namespace { class ScriptParser final : ScriptLexer { public: @@ -91,10 +92,13 @@ private: OutputSection *readOutputSectionDescription(StringRef outSec); std::vector readOverlay(); std::vector readOutputSectionPhdrs(); + std::pair readInputSectionFlags(); InputSectionDescription *readInputSectionDescription(StringRef tok); StringMatcher readFilePatterns(); std::vector readInputSectionsList(); - InputSectionDescription *readInputSectionRules(StringRef filePattern); + InputSectionDescription *readInputSectionRules(StringRef filePattern, + uint64_t withFlags, + uint64_t withoutFlags); unsigned readPhdrType(); SortSectionPolicy readSortKind(); SymbolAssignment *readProvideHidden(bool provide, bool hidden); @@ -104,7 +108,7 @@ private: Expr readConstant(); Expr getPageSize(); - uint64_t readMemoryAssignment(StringRef, StringRef, StringRef); + Expr readMemoryAssignment(StringRef, StringRef, StringRef); std::pair readMemoryAttributes(); Expr combine(StringRef op, Expr l, Expr r); @@ -171,7 +175,6 @@ static ExprValue bitOr(ExprValue a, ExprValue b) { } void ScriptParser::readDynamicList() { - config->hasDynamicList = true; expect("{"); std::vector locals; std::vector globals; @@ -286,22 +289,40 @@ void ScriptParser::addFile(StringRef s) { } if (s.startswith("/")) { + // Case 1: s is an absolute path. Just open it. driver->addFile(s, /*withLOption=*/false); } else if (s.startswith("=")) { + // Case 2: relative to the sysroot. if (config->sysroot.empty()) driver->addFile(s.substr(1), /*withLOption=*/false); else driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)), /*withLOption=*/false); } else if (s.startswith("-l")) { + // Case 3: search in the list of library paths. driver->addLibrary(s.substr(2)); - } else if (sys::fs::exists(s)) { - driver->addFile(s, /*withLOption=*/false); } else { - if (Optional path = findFromSearchPaths(s)) - driver->addFile(saver.save(*path), /*withLOption=*/true); - else - setError("unable to find " + s); + // Case 4: s is a relative path. Search in the directory of the script file. + std::string filename = std::string(getCurrentMB().getBufferIdentifier()); + StringRef directory = sys::path::parent_path(filename); + if (!directory.empty()) { + SmallString<0> path(directory); + sys::path::append(path, s); + if (sys::fs::exists(path)) { + driver->addFile(path, /*withLOption=*/false); + return; + } + } + // Then search in the current working directory. + if (sys::fs::exists(s)) { + driver->addFile(s, /*withLOption=*/false); + } else { + // Finally, search in the list of library paths. + if (Optional path = findFromSearchPaths(s)) + driver->addFile(saver.save(*path), /*withLOption=*/true); + else + setError("unable to find " + s); + } } } @@ -400,6 +421,7 @@ static std::pair parseBfdName(StringRef s) { .Case("elf64-tradlittlemips", {ELF64LEKind, EM_MIPS}) .Case("elf32-littleriscv", {ELF32LEKind, EM_RISCV}) .Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV}) + .Case("elf64-sparc", {ELF64BEKind, EM_SPARCV9}) .Default({ELFNoneKind, EM_NONE}); } @@ -408,14 +430,14 @@ static std::pair parseBfdName(StringRef s) { void ScriptParser::readOutputFormat() { expect("("); - StringRef name = unquote(next()); - StringRef s = name; + config->bfdname = unquote(next()); + StringRef s = config->bfdname; if (s.consume_back("-freebsd")) config->osabi = ELFOSABI_FREEBSD; std::tie(config->ekind, config->emachine) = parseBfdName(s); if (config->emachine == EM_NONE) - setError("unknown output format name: " + name); + setError("unknown output format name: " + config->bfdname); if (s == "elf32-ntradlittlemips" || s == "elf32-ntradbigmips") config->mipsN32Abi = true; @@ -519,13 +541,6 @@ std::vector ScriptParser::readOverlay() { } void ScriptParser::readSections() { - script->hasSectionsCommand = true; - - // -no-rosegment is used to avoid placing read only non-executable sections in - // their own segment. We do the same if SECTIONS command is present in linker - // script. See comment for computeFlags(). - config->singleRoRx = true; - expect("{"); std::vector v; while (!errorCount() && !consume("}")) { @@ -544,22 +559,23 @@ void ScriptParser::readSections() { else v.push_back(readOutputSectionDescription(tok)); } + script->sectionCommands.insert(script->sectionCommands.end(), v.begin(), + v.end()); - if (!atEOF() && consume("INSERT")) { - std::vector *dest = nullptr; - if (consume("AFTER")) - dest = &script->insertAfterCommands[next()]; - else if (consume("BEFORE")) - dest = &script->insertBeforeCommands[next()]; - else - setError("expected AFTER/BEFORE, but got '" + next() + "'"); - if (dest) - dest->insert(dest->end(), v.begin(), v.end()); + if (atEOF() || !consume("INSERT")) { + script->hasSectionsCommand = true; return; } - script->sectionCommands.insert(script->sectionCommands.end(), v.begin(), - v.end()); + bool isAfter = false; + if (consume("AFTER")) + isAfter = true; + else if (!consume("BEFORE")) + setError("expected AFTER/BEFORE, but got '" + next() + "'"); + StringRef where = next(); + for (BaseCommand *cmd : v) + if (auto *os = dyn_cast(cmd)) + script->insertCommands.push_back({os, isAfter, where}); } void ScriptParser::readTarget() { @@ -593,10 +609,11 @@ static int precedence(StringRef op) { } StringMatcher ScriptParser::readFilePatterns() { - std::vector v; + StringMatcher Matcher; + while (!errorCount() && !consume(")")) - v.push_back(next()); - return StringMatcher(v); + Matcher.addPattern(SingleStringMatcher(next())); + return Matcher; } SortSectionPolicy ScriptParser::readSortKind() { @@ -633,12 +650,12 @@ std::vector ScriptParser::readInputSectionsList() { excludeFilePat = readFilePatterns(); } - std::vector v; + StringMatcher SectionMatcher; while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE") - v.push_back(unquote(next())); + SectionMatcher.addPattern(unquote(next())); - if (!v.empty()) - ret.push_back({std::move(excludeFilePat), StringMatcher(v)}); + if (!SectionMatcher.empty()) + ret.push_back({std::move(excludeFilePat), std::move(SectionMatcher)}); else setError("section pattern is expected"); } @@ -657,8 +674,10 @@ std::vector ScriptParser::readInputSectionsList() { // // is parsed by readInputSectionsList(). InputSectionDescription * -ScriptParser::readInputSectionRules(StringRef filePattern) { - auto *cmd = make(filePattern); +ScriptParser::readInputSectionRules(StringRef filePattern, uint64_t withFlags, + uint64_t withoutFlags) { + auto *cmd = + make(filePattern, withFlags, withoutFlags); expect("("); while (!errorCount() && !consume(")")) { @@ -694,15 +713,23 @@ InputSectionDescription * ScriptParser::readInputSectionDescription(StringRef tok) { // Input section wildcard can be surrounded by KEEP. // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep + uint64_t withFlags = 0; + uint64_t withoutFlags = 0; if (tok == "KEEP") { expect("("); - StringRef filePattern = next(); - InputSectionDescription *cmd = readInputSectionRules(filePattern); + if (consume("INPUT_SECTION_FLAGS")) + std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + InputSectionDescription *cmd = + readInputSectionRules(next(), withFlags, withoutFlags); expect(")"); script->keptSections.push_back(cmd); return cmd; } - return readInputSectionRules(tok); + if (tok == "INPUT_SECTION_FLAGS") { + std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + tok = next(); + } + return readInputSectionRules(tok, withFlags, withoutFlags); } void ScriptParser::readSort() { @@ -782,9 +809,14 @@ OutputSection *ScriptParser::readOverlaySectionDescription() { script->createOutputSection(next(), getCurrentLocation()); cmd->inOverlay = true; expect("{"); - while (!errorCount() && !consume("}")) - cmd->sectionCommands.push_back(readInputSectionRules(next())); - cmd->phdrs = readOutputSectionPhdrs(); + while (!errorCount() && !consume("}")) { + uint64_t withFlags = 0; + uint64_t withoutFlags = 0; + if (consume("INPUT_SECTION_FLAGS")) + std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + cmd->sectionCommands.push_back( + readInputSectionRules(next(), withFlags, withoutFlags)); + } return cmd; } @@ -829,9 +861,9 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) { // We handle the FILL command as an alias for =fillexp section attribute, // which is different from what GNU linkers do. // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html - expect("("); + if (peek() != "(") + setError("( expected, but got " + peek()); cmd->filler = readFill(); - expect(")"); } else if (tok == "SORT") { readSort(); } else if (tok == "INCLUDE") { @@ -842,18 +874,21 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) { // We have a file name and no input sections description. It is not a // commonly used syntax, but still acceptable. In that case, all sections // from the file will be included. + // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not + // handle this case here as it will already have been matched by the + // case above. auto *isd = make(tok); - isd->sectionPatterns.push_back({{}, StringMatcher({"*"})}); + isd->sectionPatterns.push_back({{}, StringMatcher("*")}); cmd->sectionCommands.push_back(isd); } } if (consume(">")) - cmd->memoryRegionName = next(); + cmd->memoryRegionName = std::string(next()); if (consume("AT")) { expect(">"); - cmd->lmaRegionName = next(); + cmd->lmaRegionName = std::string(next()); } if (cmd->lmaExpr && !cmd->lmaRegionName.empty()) @@ -883,8 +918,11 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) { // When reading a hexstring, ld.bfd handles it as a blob of arbitrary // size, while ld.gold always handles it as a 32-bit big-endian number. // We are compatible with ld.gold because it's easier to implement. +// Also, we require that expressions with operators must be wrapped into +// round brackets. We did it to resolve the ambiguity when parsing scripts like: +// SECTIONS { .foo : { ... } =120+3 /DISCARD/ : { ... } } std::array ScriptParser::readFill() { - uint64_t value = readExpr()().val; + uint64_t value = readPrimary()().val; if (value > UINT32_MAX) setError("filler expression result does not fit 32-bit: 0x" + Twine::utohexstr(value)); @@ -1103,6 +1141,63 @@ ByteCommand *ScriptParser::readByteCommand(StringRef tok) { return make(e, size, commandString); } +static llvm::Optional parseFlag(StringRef tok) { + if (llvm::Optional asInt = parseInt(tok)) + return asInt; +#define CASE_ENT(enum) #enum, ELF::enum + return StringSwitch>(tok) + .Case(CASE_ENT(SHF_WRITE)) + .Case(CASE_ENT(SHF_ALLOC)) + .Case(CASE_ENT(SHF_EXECINSTR)) + .Case(CASE_ENT(SHF_MERGE)) + .Case(CASE_ENT(SHF_STRINGS)) + .Case(CASE_ENT(SHF_INFO_LINK)) + .Case(CASE_ENT(SHF_LINK_ORDER)) + .Case(CASE_ENT(SHF_OS_NONCONFORMING)) + .Case(CASE_ENT(SHF_GROUP)) + .Case(CASE_ENT(SHF_TLS)) + .Case(CASE_ENT(SHF_COMPRESSED)) + .Case(CASE_ENT(SHF_EXCLUDE)) + .Case(CASE_ENT(SHF_ARM_PURECODE)) + .Default(None); +#undef CASE_ENT +} + +// Reads the '(' ')' list of section flags in +// INPUT_SECTION_FLAGS '(' ')' in the +// following form: +// ::= +// | & flag +// ::= Recognized Flag Name, or Integer value of flag. +// If the first character of is a ! then this means without flag, +// otherwise with flag. +// Example: SHF_EXECINSTR & !SHF_WRITE means with flag SHF_EXECINSTR and +// without flag SHF_WRITE. +std::pair ScriptParser::readInputSectionFlags() { + uint64_t withFlags = 0; + uint64_t withoutFlags = 0; + expect("("); + while (!errorCount()) { + StringRef tok = unquote(next()); + bool without = tok.consume_front("!"); + if (llvm::Optional flag = parseFlag(tok)) { + if (without) + withoutFlags |= *flag; + else + withFlags |= *flag; + } else { + setError("unrecognised flag: " + tok); + } + if (consume(")")) + break; + if (!consume("&")) { + next(); + setError("expected & or )"); + } + } + return std::make_pair(withFlags, withoutFlags); +} + StringRef ScriptParser::readParenLiteral() { expect("("); bool orig = inExpr; @@ -1223,7 +1318,7 @@ Expr ScriptParser::readPrimary() { setError("memory region not defined: " + name); return [] { return 0; }; } - return [=] { return script->memoryRegions[name]->length; }; + return script->memoryRegions[name]->length; } if (tok == "LOADADDR") { StringRef name = readParenLiteral(); @@ -1250,7 +1345,7 @@ Expr ScriptParser::readPrimary() { setError("memory region not defined: " + name); return [] { return 0; }; } - return [=] { return script->memoryRegions[name]->origin; }; + return script->memoryRegions[name]->origin; } if (tok == "SEGMENT_START") { expect("("); @@ -1269,7 +1364,7 @@ Expr ScriptParser::readPrimary() { return [=] { return cmd->size; }; } if (tok == "SIZEOF_HEADERS") - return [=] { return getHeaderSize(); }; + return [=] { return elf::getHeaderSize(); }; // Tok is the dot. if (tok == ".") @@ -1375,12 +1470,11 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) { // as a parent. This version hierarchy is, probably against your // instinct, purely for hint; the runtime doesn't care about it // at all. In LLD, we simply ignore it. - if (peek() != ";") - skip(); - expect(";"); + if (next() != ";") + expect(";"); } -static bool hasWildcard(StringRef s) { +bool elf::hasWildcard(StringRef s) { return s.find_first_of("?*[") != StringRef::npos; } @@ -1441,14 +1535,14 @@ std::vector ScriptParser::readVersionExtern() { return ret; } -uint64_t ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2, - StringRef s3) { +Expr ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2, + StringRef s3) { if (!consume(s1) && !consume(s2) && !consume(s3)) { setError("expected one of: " + s1 + ", " + s2 + ", or " + s3); - return 0; + return [] { return 0; }; } expect("="); - return readExpr()().getValue(); + return readExpr(); } // Parse the MEMORY command as specified in: @@ -1472,9 +1566,9 @@ void ScriptParser::readMemory() { } expect(":"); - uint64_t origin = readMemoryAssignment("ORIGIN", "org", "o"); + Expr origin = readMemoryAssignment("ORIGIN", "org", "o"); expect(","); - uint64_t length = readMemoryAssignment("LENGTH", "len", "l"); + Expr length = readMemoryAssignment("LENGTH", "len", "l"); // Add the memory region to the region map. MemoryRegion *mr = make(tok, origin, length, flags, negFlags); @@ -1512,19 +1606,18 @@ std::pair ScriptParser::readMemoryAttributes() { return {flags, negFlags}; } -void readLinkerScript(MemoryBufferRef mb) { +void elf::readLinkerScript(MemoryBufferRef mb) { ScriptParser(mb).readLinkerScript(); } -void readVersionScript(MemoryBufferRef mb) { +void elf::readVersionScript(MemoryBufferRef mb) { ScriptParser(mb).readVersionScript(); } -void readDynamicList(MemoryBufferRef mb) { ScriptParser(mb).readDynamicList(); } +void elf::readDynamicList(MemoryBufferRef mb) { + ScriptParser(mb).readDynamicList(); +} -void readDefsym(StringRef name, MemoryBufferRef mb) { +void elf::readDefsym(StringRef name, MemoryBufferRef mb) { ScriptParser(mb).readDefsym(name); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/ScriptParser.h b/gnu/llvm/lld/ELF/ScriptParser.h index c953fb302b9..eed1958647f 100644 --- a/gnu/llvm/lld/ELF/ScriptParser.h +++ b/gnu/llvm/lld/ELF/ScriptParser.h @@ -27,6 +27,8 @@ void readDynamicList(MemoryBufferRef mb); // Parses the defsym expression. void readDefsym(StringRef name, MemoryBufferRef mb); +bool hasWildcard(StringRef s); + } // namespace elf } // namespace lld diff --git a/gnu/llvm/lld/ELF/SymbolTable.cpp b/gnu/llvm/lld/ELF/SymbolTable.cpp index f7a8a99cf8f..afc8b05f876 100644 --- a/gnu/llvm/lld/ELF/SymbolTable.cpp +++ b/gnu/llvm/lld/ELF/SymbolTable.cpp @@ -26,10 +26,10 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { -SymbolTable *symtab; +SymbolTable *elf::symtab; void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { // Swap symbols as instructed by -wrap. @@ -40,12 +40,18 @@ void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { idx2 = idx1; idx1 = idx3; - // Now renaming is complete. No one refers Real symbol. We could leave - // Real as-is, but if Real is written to the symbol table, that may - // contain irrelevant values. So, we copy all values from Sym to Real. - StringRef s = real->getName(); + if (real->exportDynamic) + sym->exportDynamic = true; + + // 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 + // leave it in .dynsym, because otherwise it might lead to an undefined symbol + // error in a subsequent link. If real is defined, we could emit real as an + // alias for sym, but that could degrade the user experience of some tools + // that can print out only one symbol for each location: sym is a preferred + // name than real, but they might print out real instead. memcpy(real, sym, sizeof(SymbolUnion)); - real->setName(s); + real->isUsedInRegularObj = false; } // Find an existing symbol or create a new one. @@ -88,7 +94,7 @@ Symbol *SymbolTable::insert(StringRef name) { } Symbol *SymbolTable::addSymbol(const Symbol &newSym) { - Symbol *sym = symtab->insert(newSym.getName()); + Symbol *sym = insert(newSym.getName()); sym->resolve(newSym); return sym; } @@ -103,6 +109,13 @@ Symbol *SymbolTable::find(StringRef name) { return sym; } +// A version script/dynamic list is only meaningful for a Defined symbol. +// A CommonSymbol will be converted to a Defined in replaceCommonSymbols(). +// A lazy symbol may be made Defined if an LTO libcall fetches it. +static bool canBeVersioned(const Symbol &sym) { + return sym.isDefined() || sym.isCommon() || sym.isLazy(); +} + // Initialize demangledSyms with a map from demangled symbols to symbol // objects. Used to handle "extern C++" directive in version scripts. // @@ -119,11 +132,9 @@ Symbol *SymbolTable::find(StringRef name) { StringMap> &SymbolTable::getDemangledSyms() { if (!demangledSyms) { demangledSyms.emplace(); - for (Symbol *sym : symVector) { - if (!sym->isDefined() && !sym->isCommon()) - continue; - (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym); - } + for (Symbol *sym : symVector) + if (canBeVersioned(*sym)) + (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym); } return *demangledSyms; } @@ -131,15 +142,15 @@ StringMap> &SymbolTable::getDemangledSyms() { std::vector SymbolTable::findByVersion(SymbolVersion ver) { if (ver.isExternCpp) return getDemangledSyms().lookup(ver.name); - if (Symbol *b = find(ver.name)) - if (b->isDefined() || b->isCommon()) - return {b}; + if (Symbol *sym = find(ver.name)) + if (canBeVersioned(*sym)) + return {sym}; return {}; } std::vector SymbolTable::findAllByVersion(SymbolVersion ver) { std::vector res; - StringMatcher m(ver.name); + SingleStringMatcher m(ver.name); if (ver.isExternCpp) { for (auto &p : getDemangledSyms()) @@ -149,7 +160,7 @@ std::vector SymbolTable::findAllByVersion(SymbolVersion ver) { } for (Symbol *sym : symVector) - if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName())) + if (canBeVersioned(*sym) && m.match(sym->getName())) res.push_back(sym); return res; } @@ -264,6 +275,3 @@ void SymbolTable::scanVersionScript() { // --dynamic-list. handleDynamicList(); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Symbols.cpp b/gnu/llvm/lld/ELF/Symbols.cpp index 39e4584202c..8f2f55418df 100644 --- a/gnu/llvm/lld/ELF/Symbols.cpp +++ b/gnu/llvm/lld/ELF/Symbols.cpp @@ -16,29 +16,40 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; -namespace lld { // Returns a symbol for an error message. static std::string demangle(StringRef symName) { if (elf::config->demangle) return demangleItanium(symName); - return symName; + return std::string(symName); } -std::string toString(const elf::Symbol &b) { return demangle(b.getName()); } -std::string toELFString(const Archive::Symbol &b) { +std::string lld::toString(const elf::Symbol &sym) { + StringRef name = sym.getName(); + std::string ret = demangle(name); + + // If sym has a non-default version, its name may have been truncated at '@' + // by Symbol::parseSymbolVersion(). Add the trailing part. This check is safe + // because every symbol name ends with '\0'. + if (name.data()[name.size()] == '@') + ret += name.data() + name.size(); + return ret; +} + +std::string lld::toELFString(const Archive::Symbol &b) { return demangle(b.getName()); } -namespace elf { Defined *ElfSym::bss; -Defined *ElfSym::data; Defined *ElfSym::etext1; Defined *ElfSym::etext2; Defined *ElfSym::edata1; @@ -53,6 +64,7 @@ Defined *ElfSym::relaIpltStart; Defined *ElfSym::relaIpltEnd; Defined *ElfSym::riscvGlobalPointer; Defined *ElfSym::tlsModuleBase; +DenseMap elf::backwardReferences; static uint64_t getSymVA(const Symbol &sym, int64_t &addend) { switch (sym.kind()) { @@ -100,7 +112,7 @@ static uint64_t getSymVA(const Symbol &sym, int64_t &addend) { // MIPS relocatable files can mix regular and microMIPS code. // Linker needs to distinguish such code. To do so microMIPS // symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other` - // field. Unfortunately, the `MIPS::relocateOne()` method has + // field. Unfortunately, the `MIPS::relocate()` method has // a symbol value only. To pass type of the symbol (regular/microMIPS) // to that routine as well as other places where we write // a symbol value as-is (.dynamic section, `Elf_Ehdr::e_entry` @@ -266,7 +278,7 @@ uint8_t Symbol::computeBinding() const { if (config->relocatable) return binding; if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) || - versionId == VER_NDX_LOCAL) + (versionId == VER_NDX_LOCAL && isDefined())) return STB_LOCAL; if (!config->gnuUnique && binding == STB_GNU_UNIQUE) return STB_GLOBAL; @@ -289,7 +301,7 @@ bool Symbol::includeInDynsym() const { } // Print out a log message for --trace-symbol. -void printTraceSymbol(const Symbol *sym) { +void elf::printTraceSymbol(const Symbol *sym) { std::string s; if (sym->isUndefined()) s = ": reference to "; @@ -305,7 +317,7 @@ void printTraceSymbol(const Symbol *sym) { message(toString(sym->file) + s + sym->getName()); } -void maybeWarnUnorderableSymbol(const Symbol *sym) { +void elf::maybeWarnUnorderableSymbol(const Symbol *sym) { if (!config->warnSymbolOrdering) return; @@ -337,7 +349,7 @@ void maybeWarnUnorderableSymbol(const Symbol *sym) { // Returns true if a symbol can be replaced at load-time by a symbol // with the same name defined in other ELF executable or DSO. -bool computeIsPreemptible(const Symbol &sym) { +bool elf::computeIsPreemptible(const Symbol &sym) { assert(!sym.isLocal()); // Only symbols with default visibility that appear in dynsym can be @@ -353,16 +365,22 @@ bool computeIsPreemptible(const Symbol &sym) { if (!config->shared) return false; - // If the dynamic list is present, it specifies preemptable symbols in a DSO. - if (config->hasDynamicList) + // If -Bsymbolic or --dynamic-list is specified, or -Bsymbolic-functions is + // specified and the symbol is STT_FUNC, the symbol is preemptible iff it is + // in the dynamic list. + if (config->symbolic || (config->bsymbolicFunctions && sym.isFunc())) return sym.inDynamicList; - - // -Bsymbolic means that definitions are not preempted. - if (config->bsymbolic || (config->bsymbolicFunctions && sym.isFunc())) - return false; return true; } +void elf::reportBackrefs() { + for (auto &it : backwardReferences) { + const Symbol &sym = *it.first; + warn("backward reference detected: " + sym.getName() + " in " + + toString(it.second) + " refers to " + toString(sym.file)); + } +} + static uint8_t getMinVisibility(uint8_t va, uint8_t vb) { if (va == STV_DEFAULT) return vb; @@ -495,13 +513,28 @@ void Symbol::resolveUndefined(const Undefined &other) { // group assignment rule simulates the traditional linker's semantics. bool backref = config->warnBackrefs && other.file && file->groupId < other.file->groupId; + if (backref) { + // Some libraries have known problems and can cause noise. Filter them out + // with --warn-backrefs-exclude=. + StringRef name = + !file->archiveName.empty() ? file->archiveName : file->getName(); + for (const llvm::GlobPattern &pat : config->warnBackrefsExclude) + if (pat.match(name)) { + backref = false; + break; + } + } fetch(); // We don't report backward references to weak symbols as they can be // overridden later. + // + // A traditional linker does not error for -ldef1 -lref -ldef2 (linking + // sandwich), where def2 may or may not be the same as def1. We don't want + // to warn for this case, so dismiss the warning if we see a subsequent lazy + // definition. if (backref && !isWeak()) - warn("backward reference detected: " + other.getName() + " in " + - toString(other.file) + " refers to " + toString(file)); + backwardReferences.try_emplace(this, other.file); return; } @@ -515,7 +548,6 @@ void Symbol::resolveUndefined(const Undefined &other) { // reference is weak. if (other.binding != STB_WEAK || !referenced) binding = other.binding; - referenced = true; } } @@ -659,8 +691,12 @@ void Symbol::resolveDefined(const Defined &other) { } template void Symbol::resolveLazy(const LazyT &other) { - if (!isUndefined()) + if (!isUndefined()) { + // See the comment in resolveUndefined(). + if (isDefined()) + backwardReferences.erase(this); return; + } // An undefined weak will not fetch archive members. See comment on Lazy in // Symbols.h for the details. @@ -688,9 +724,6 @@ void Symbol::resolveShared(const SharedSymbol &other) { uint8_t bind = binding; replace(other); binding = bind; - referenced = true; - } + } else if (traced) + printTraceSymbol(&other); } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Symbols.h b/gnu/llvm/lld/ELF/Symbols.h index 4e3e1b4df80..b69d263153d 100644 --- a/gnu/llvm/lld/ELF/Symbols.h +++ b/gnu/llvm/lld/ELF/Symbols.h @@ -17,10 +17,12 @@ #include "InputSection.h" #include "lld/Common/LLVM.h" #include "lld/Common/Strings.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" namespace lld { +// Returns a string representation for a symbol for diagnostics. std::string toString(const elf::Symbol &); // There are two different ways to convert an Archive::Symbol to a string: @@ -256,6 +258,9 @@ public: uint8_t isPreemptible : 1; // True if an undefined or shared symbol is used from a live section. + // + // NOTE: In Writer.cpp the field is used to mark local defined symbols + // which are referenced by relocations when -r or --emit-relocs is given. uint8_t used : 1; // True if a call to this symbol needs to be followed by a restore of the @@ -420,9 +425,6 @@ struct ElfSym { // __bss_start static Defined *bss; - // __data_start - static Defined *data; - // etext and _etext static Defined *etext1; static Defined *etext2; @@ -518,13 +520,16 @@ size_t Symbol::getSymbolSize() const { void Symbol::replace(const Symbol &newSym) { using llvm::ELF::STT_TLS; - // Symbols representing thread-local variables must be referenced by - // TLS-aware relocations, and non-TLS symbols must be reference by - // non-TLS relocations, so there's a clear distinction between TLS - // and non-TLS symbols. It is an error if the same symbol is defined - // as a TLS symbol in one file and as a non-TLS symbol in other file. - if (symbolKind != PlaceholderKind && !isLazy() && !newSym.isLazy() && - (type == STT_TLS) != (newSym.type == STT_TLS)) + // st_value of STT_TLS represents the assigned offset, not the actual address + // which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can only be + // referenced by special TLS relocations. It is usually an error if a STT_TLS + // symbol is replaced by a non-STT_TLS symbol, vice versa. There are two + // exceptions: (a) a STT_NOTYPE lazy/undefined symbol can be replaced by a + // STT_TLS symbol, (b) a STT_TLS undefined symbol can be replaced by a + // STT_NOTYPE lazy symbol. + if (symbolKind != PlaceholderKind && !newSym.isLazy() && + (type == STT_TLS) != (newSym.type == STT_TLS) && + type != llvm::ELF::STT_NOTYPE) error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " + toString(newSym.file) + "\n>>> defined in " + toString(file)); @@ -558,6 +563,11 @@ void Symbol::replace(const Symbol &newSym) { void maybeWarnUnorderableSymbol(const Symbol *sym); bool computeIsPreemptible(const Symbol &sym); +void reportBackrefs(); + +// A mapping from a symbol to an InputFile referencing it backward. Used by +// --warn-backrefs. +extern llvm::DenseMap backwardReferences; } // namespace elf } // namespace lld diff --git a/gnu/llvm/lld/ELF/SyntheticSections.h b/gnu/llvm/lld/ELF/SyntheticSections.h index 5f59178fb54..8ed82ba64a6 100644 --- a/gnu/llvm/lld/ELF/SyntheticSections.h +++ b/gnu/llvm/lld/ELF/SyntheticSections.h @@ -364,7 +364,7 @@ private: // Try to merge two GOTs. In case of success the `Dst` contains // result of merging and the function returns true. In case of - // ovwerflow the `Dst` is unchanged and the function returns false. + // overflow the `Dst` is unchanged and the function returns false. bool tryMergeGots(FileGot & dst, FileGot & src, bool isPrimary); }; @@ -684,7 +684,6 @@ public: size_t getNumEntries() const { return entries.size(); } size_t headerSize; - size_t footerSize = 0; std::vector entries; }; @@ -705,6 +704,16 @@ public: void addEntry(Symbol &sym); }; +class PPC32GlinkSection : public PltSection { +public: + PPC32GlinkSection(); + void writeTo(uint8_t *buf) override; + size_t getSize() const override; + + std::vector canonical_plts; + static constexpr size_t footerSize = 64; +}; + // This is x86-only. class IBTPltSection : public SyntheticSection { public: @@ -1037,7 +1046,7 @@ public: std::vector exidxSections; private: - size_t size; + size_t size = 0; // Instead of storing pointers to the .ARM.exidx InputSections from // InputObjects, we store pointers to the executable sections that need diff --git a/gnu/llvm/lld/ELF/Target.cpp b/gnu/llvm/lld/ELF/Target.cpp index d3899d0f18f..6abd8b452e2 100644 --- a/gnu/llvm/lld/ELF/Target.cpp +++ b/gnu/llvm/lld/ELF/Target.cpp @@ -35,19 +35,19 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; -namespace lld { -std::string toString(elf::RelType type) { +const TargetInfo *elf::target; + +std::string lld::toString(RelType type) { StringRef s = getELFRelocationTypeName(elf::config->emachine, type); if (s == "Unknown") return ("Unknown (" + Twine(type) + ")").str(); - return s; + return std::string(s); } -namespace elf { -const TargetInfo *target; - -TargetInfo *getTarget() { +TargetInfo *elf::getTarget() { switch (config->emachine) { case EM_386: case EM_IAMCU: @@ -112,7 +112,7 @@ template static ErrorPlace getErrPlace(const uint8_t *loc) { return {}; } -ErrorPlace getErrorPlace(const uint8_t *loc) { +ErrorPlace elf::getErrorPlace(const uint8_t *loc) { switch (config->ekind) { case ELF32LEKind: return getErrPlace(loc); @@ -155,26 +155,27 @@ RelExpr TargetInfo::adjustRelaxExpr(RelType type, const uint8_t *data, return expr; } -void TargetInfo::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { +void TargetInfo::relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const { llvm_unreachable("Should not have claimed to be relaxable"); } -void TargetInfo::relaxTlsGdToLe(uint8_t *loc, RelType type, +void TargetInfo::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const { llvm_unreachable("Should not have claimed to be relaxable"); } -void TargetInfo::relaxTlsGdToIe(uint8_t *loc, RelType type, +void TargetInfo::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const { llvm_unreachable("Should not have claimed to be relaxable"); } -void TargetInfo::relaxTlsIeToLe(uint8_t *loc, RelType type, +void TargetInfo::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const { llvm_unreachable("Should not have claimed to be relaxable"); } -void TargetInfo::relaxTlsLdToLe(uint8_t *loc, RelType type, +void TargetInfo::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const { llvm_unreachable("Should not have claimed to be relaxable"); } @@ -185,6 +186,3 @@ uint64_t TargetInfo::getImageBase() const { return *config->imageBase; return config->isPic ? 0 : defaultImageBase; } - -} // namespace elf -} // namespace lld diff --git a/gnu/llvm/lld/ELF/Target.h b/gnu/llvm/lld/ELF/Target.h index 949a7bfdf64..47905ae64a4 100644 --- a/gnu/llvm/lld/ELF/Target.h +++ b/gnu/llvm/lld/ELF/Target.h @@ -82,10 +82,27 @@ public: virtual bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const; - virtual void relocateOne(uint8_t *loc, RelType type, uint64_t val) const = 0; + virtual void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const = 0; + void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const { + relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val); + } + + virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, + JumpModType val) const {} virtual ~TargetInfo(); + // This deletes a jump insn at the end of the section if it is a fall thru to + // the next section. Further, if there is a conditional jump and a direct + // jump consecutively, it tries to flip the conditional jump to convert the + // direct jump into a fall thru and delete it. Returns true if a jump + // instruction can be deleted. + virtual bool deleteFallThruJmpInsn(InputSection &is, InputFile *file, + InputSection *nextIS) const { + return false; + } + unsigned defaultCommonPageSize = 4096; unsigned defaultMaxPageSize = 4096; @@ -122,6 +139,10 @@ public: // executable OutputSections. std::array trapInstr; + // Stores the NOP instructions of different sizes for the target and is used + // to pad sections that are relaxed. + llvm::Optional>> nopInstrs; + // If a target needs to rewrite calls to __morestack to instead call // __morestack_non_split when a split-stack enabled caller calls a // non-split-stack callee this will return true. Otherwise returns false. @@ -129,11 +150,16 @@ public: virtual RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, RelExpr expr) const; - virtual void relaxGot(uint8_t *loc, RelType type, uint64_t val) const; - virtual void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const; - virtual void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const; - virtual void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const; - virtual void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const; + virtual void relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const; + virtual void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const; + virtual void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const; + virtual void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const; + virtual void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const; protected: // On FreeBSD x86_64 the first page cannot be mmaped. @@ -171,8 +197,7 @@ static inline std::string getErrorLocation(const uint8_t *loc) { void writePPC32GlinkSection(uint8_t *buf, size_t numEntries); -bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, - uint8_t *bufLoc); +bool tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc); unsigned getPPCDFormOp(unsigned secondaryOp); // In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first @@ -188,6 +213,7 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther); // the .toc section. bool isPPC64SmallCodeModelTocReloc(RelType type); +void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); @@ -196,44 +222,36 @@ TargetInfo *getTarget(); template bool isMipsPIC(const Defined *sym); -static inline void reportRangeError(uint8_t *loc, RelType type, const Twine &v, - int64_t min, uint64_t max) { - ErrorPlace errPlace = getErrorPlace(loc); - StringRef hint; - if (errPlace.isec && errPlace.isec->name.startswith(".debug")) - hint = "; consider recompiling with -fdebug-types-section to reduce size " - "of debug sections"; - - errorOrWarn(errPlace.loc + "relocation " + lld::toString(type) + - " out of range: " + v.str() + " is not in [" + Twine(min).str() + - ", " + Twine(max).str() + "]" + hint); -} +void reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v, + int64_t min, uint64_t max); // Make sure that V can be represented as an N bit signed integer. -inline void checkInt(uint8_t *loc, int64_t v, int n, RelType type) { +inline void checkInt(uint8_t *loc, int64_t v, int n, const Relocation &rel) { if (v != llvm::SignExtend64(v, n)) - reportRangeError(loc, type, Twine(v), llvm::minIntN(n), llvm::maxIntN(n)); + reportRangeError(loc, rel, Twine(v), llvm::minIntN(n), llvm::maxIntN(n)); } // Make sure that V can be represented as an N bit unsigned integer. -inline void checkUInt(uint8_t *loc, uint64_t v, int n, RelType type) { +inline void checkUInt(uint8_t *loc, uint64_t v, int n, const Relocation &rel) { if ((v >> n) != 0) - reportRangeError(loc, type, Twine(v), 0, llvm::maxUIntN(n)); + reportRangeError(loc, rel, Twine(v), 0, llvm::maxUIntN(n)); } // Make sure that V can be represented as an N bit signed or unsigned integer. -inline void checkIntUInt(uint8_t *loc, uint64_t v, int n, RelType type) { +inline void checkIntUInt(uint8_t *loc, uint64_t v, int n, + const Relocation &rel) { // For the error message we should cast V to a signed integer so that error // messages show a small negative value rather than an extremely large one if (v != (uint64_t)llvm::SignExtend64(v, n) && (v >> n) != 0) - reportRangeError(loc, type, Twine((int64_t)v), llvm::minIntN(n), + reportRangeError(loc, rel, Twine((int64_t)v), llvm::minIntN(n), llvm::maxUIntN(n)); } -inline void checkAlignment(uint8_t *loc, uint64_t v, int n, RelType type) { +inline void checkAlignment(uint8_t *loc, uint64_t v, int n, + const Relocation &rel) { if ((v & (n - 1)) != 0) error(getErrorLocation(loc) + "improper alignment for relocation " + - lld::toString(type) + ": 0x" + llvm::utohexstr(v) + + lld::toString(rel.type) + ": 0x" + llvm::utohexstr(v) + " is not aligned to " + Twine(n) + " bytes"); } diff --git a/gnu/llvm/lld/ELF/Thunks.cpp b/gnu/llvm/lld/ELF/Thunks.cpp index f9c2e2d74e0..ea74d343ebb 100644 --- a/gnu/llvm/lld/ELF/Thunks.cpp +++ b/gnu/llvm/lld/ELF/Thunks.cpp @@ -40,9 +40,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { @@ -280,6 +279,20 @@ public: void addSymbols(ThunkSection &isec) override; }; +// PPC64 R2 Save Stub +// When the caller requires a valid R2 TOC pointer but the callee does not +// require a TOC pointer and the callee cannot guarantee that it doesn't +// clobber R2 then we need to save R2. This stub: +// 1) Saves the TOC pointer to the stack. +// 2) Tail calls the callee. +class PPC64R2SaveStub final : public Thunk { +public: + PPC64R2SaveStub(Symbol &dest) : Thunk(dest, 0) {} + uint32_t size() override { return 8; } + void writeTo(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) 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 @@ -352,7 +365,7 @@ void AArch64ABSLongThunk::writeTo(uint8_t *buf) { }; uint64_t s = getAArch64ThunkDestVA(destination, addend); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 8, R_AARCH64_ABS64, s); + target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s); } void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -376,9 +389,9 @@ void AArch64ADRPThunk::writeTo(uint8_t *buf) { uint64_t s = getAArch64ThunkDestVA(destination, addend); uint64_t p = getThunkTargetSym()->getVA(); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(s) - getAArch64Page(p)); - target->relocateOne(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s); + target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(s) - getAArch64Page(p)); + target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s); } void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { @@ -422,7 +435,7 @@ void ARMThunk::writeTo(uint8_t *buf) { 0x00, 0x00, 0x00, 0xea, // b S }; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_JUMP24, offset); + target->relocateNoSym(buf, R_ARM_JUMP24, offset); } bool ARMThunk::isCompatibleWith(const InputSection &isec, @@ -460,7 +473,7 @@ void ThumbThunk::writeTo(uint8_t *buf) { 0x00, 0xf0, 0x00, 0xb0, // b.w S }; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_THM_JUMP24, offset); + target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset); } bool ThumbThunk::isCompatibleWith(const InputSection &isec, @@ -477,8 +490,8 @@ void ARMV7ABSLongThunk::writeLong(uint8_t *buf) { }; uint64_t s = getARMThunkDestVA(destination); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_MOVW_ABS_NC, s); - target->relocateOne(buf + 4, R_ARM_MOVT_ABS, s); + target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s); + target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s); } void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -495,8 +508,8 @@ void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) { }; uint64_t s = getARMThunkDestVA(destination); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_THM_MOVW_ABS_NC, s); - target->relocateOne(buf + 4, R_ARM_THM_MOVT_ABS, s); + target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s); + target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s); } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -516,8 +529,8 @@ void ARMV7PILongThunk::writeLong(uint8_t *buf) { uint64_t p = getThunkTargetSym()->getVA(); int64_t offset = s - p - 16; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_MOVW_PREL_NC, offset); - target->relocateOne(buf + 4, R_ARM_MOVT_PREL, offset); + target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset); + target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset); } void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { @@ -537,8 +550,8 @@ void ThumbV7PILongThunk::writeLong(uint8_t *buf) { uint64_t p = getThunkTargetSym()->getVA() & ~0x1; int64_t offset = s - p - 12; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_THM_MOVW_PREL_NC, offset); - target->relocateOne(buf + 4, R_ARM_THM_MOVT_PREL, offset); + target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset); + target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset); } void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { @@ -553,7 +566,7 @@ void ARMV5ABSLongThunk::writeLong(uint8_t *buf) { 0x00, 0x00, 0x00, 0x00, // L1: .word S }; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination)); + target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination)); } void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -579,7 +592,7 @@ void ARMV5PILongThunk::writeLong(uint8_t *buf) { uint64_t s = getARMThunkDestVA(destination); uint64_t p = getThunkTargetSym()->getVA() & ~0x1; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12); + target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); } void ARMV5PILongThunk::addSymbols(ThunkSection &isec) { @@ -609,7 +622,7 @@ void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) { }; uint64_t s = getARMThunkDestVA(destination); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 8, R_ARM_ABS32, s); + target->relocateNoSym(buf + 8, R_ARM_ABS32, s); } void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { @@ -635,7 +648,7 @@ void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { uint64_t s = getARMThunkDestVA(destination); uint64_t p = getThunkTargetSym()->getVA() & ~0x1; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12); + target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); } void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { @@ -652,8 +665,8 @@ void MipsThunk::writeTo(uint8_t *buf) { write32(buf + 4, 0x08000000 | (s >> 2)); // j func write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func) write32(buf + 12, 0x00000000); // nop - target->relocateOne(buf, R_MIPS_HI16, s); - target->relocateOne(buf + 8, R_MIPS_LO16, s); + target->relocateNoSym(buf, R_MIPS_HI16, s); + target->relocateNoSym(buf + 8, R_MIPS_LO16, s); } void MipsThunk::addSymbols(ThunkSection &isec) { @@ -674,9 +687,9 @@ void MicroMipsThunk::writeTo(uint8_t *buf) { write16(buf + 4, 0xd400); // j func write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func) write16(buf + 12, 0x0c00); // nop - target->relocateOne(buf, R_MICROMIPS_HI16, s); - target->relocateOne(buf + 4, R_MICROMIPS_26_S1, s); - target->relocateOne(buf + 8, R_MICROMIPS_LO16, s); + target->relocateNoSym(buf, R_MICROMIPS_HI16, s); + target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s); + target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s); } void MicroMipsThunk::addSymbols(ThunkSection &isec) { @@ -698,9 +711,9 @@ void MicroMipsR6Thunk::writeTo(uint8_t *buf) { write16(buf, 0x1320); // lui $25, %hi(func) write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func) write16(buf + 8, 0x9400); // bc func - target->relocateOne(buf, R_MICROMIPS_HI16, s); - target->relocateOne(buf + 4, R_MICROMIPS_LO16, s); - target->relocateOne(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12); + target->relocateNoSym(buf, R_MICROMIPS_HI16, s); + target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s); + target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12); } void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { @@ -714,8 +727,8 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const { return dyn_cast(dr.section); } -void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, - const InputFile *file, int64_t addend) { +void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, + const InputFile *file, int64_t addend) { if (!config->isPic) { write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11) @@ -799,7 +812,7 @@ void PPC32LongThunk::writeTo(uint8_t *buf) { write32(buf + 4, 0x4e800420); // bctr } -void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { +void elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { uint16_t offHa = (offset + 0x8000) >> 16; uint16_t offLo = offset & 0xffff; @@ -823,6 +836,21 @@ void PPC64PltCallStub::addSymbols(ThunkSection &isec) { s->file = destination.file; } +void PPC64R2SaveStub::writeTo(uint8_t *buf) { + int64_t offset = destination.getVA() - (getThunkTargetSym()->getVA() + 4); + // 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 +} + +void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { + Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()), + STT_FUNC, 0, isec); + s->needsTocRestore = true; +} + void PPC64LongBranchThunk::writeTo(uint8_t *buf) { int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) - getPPC64TocBase(); @@ -839,7 +867,8 @@ Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {} Thunk::~Thunk() = default; static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) { - if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 && + type != R_AARCH64_PLT32) fatal("unrecognized relocation type"); if (config->picThunk) return make(s, a); @@ -945,17 +974,23 @@ static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel, } static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) { - assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk"); + assert((type == R_PPC64_REL14 || type == R_PPC64_REL24) && + "unexpected relocation type for thunk"); if (s.isInPlt()) return 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); + if (config->picThunk) return make(s, a); return make(s, a); } -Thunk *addThunk(const InputSection &isec, Relocation &rel) { +Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) { Symbol &s = *rel.sym; int64_t a = rel.addend; @@ -976,6 +1011,3 @@ Thunk *addThunk(const InputSection &isec, Relocation &rel) { llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC"); } - -} // end namespace elf -} // end namespace lld diff --git a/gnu/llvm/lld/ELF/Writer.cpp b/gnu/llvm/lld/ELF/Writer.cpp index 35941cf4f79..b9fd03bc2ed 100644 --- a/gnu/llvm/lld/ELF/Writer.cpp +++ b/gnu/llvm/lld/ELF/Writer.cpp @@ -22,22 +22,25 @@ #include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" -#include "lld/Common/Threads.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/SHA1.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" #include +#define DEBUG_TYPE "lld" + using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; +using namespace lld; +using namespace lld::elf; -namespace lld { -namespace elf { namespace { // The writer writes a SymbolTable result to a file. template class Writer { @@ -56,6 +59,7 @@ private: void sortSections(); void resolveShfLinkOrder(); void finalizeAddressDependentContent(); + void optimizeBasicBlockJumps(); void sortInputSections(); void finalizeSections(); void checkExecuteOnly(); @@ -91,7 +95,7 @@ static bool isSectionPrefix(StringRef prefix, StringRef name) { return name.startswith(prefix) || name == prefix.drop_back(); } -StringRef getOutputSectionName(const InputSectionBase *s) { +StringRef elf::getOutputSectionName(const InputSectionBase *s) { if (config->relocatable) return s->name; @@ -107,31 +111,39 @@ StringRef getOutputSectionName(const InputSectionBase *s) { } } - // This check is for -z keep-text-section-prefix. This option separates text - // sections with prefix ".text.hot", ".text.unlikely", ".text.startup" or - // ".text.exit". - // When enabled, this allows identifying the hot code region (.text.hot) in - // the final binary which can be selectively mapped to huge pages or mlocked, - // for instance. + // A BssSection created for a common symbol is identified as "COMMON" in + // linker scripts. It should go to .bss section. + if (s->name == "COMMON") + return ".bss"; + + if (script->hasSectionsCommand) + return s->name; + + // When no SECTIONS is specified, emulate GNU ld's internal linker scripts + // by grouping sections with certain prefixes. + + // GNU ld places text sections with prefix ".text.hot.", ".text.unknown.", + // ".text.unlikely.", ".text.startup." or ".text.exit." before others. + // We provide an option -z keep-text-section-prefix to group such sections + // into separate output sections. This is more flexible. See also + // sortISDBySectionOrder(). + // ".text.unknown" means the hotness of the section is unknown. When + // SampleFDO is used, if a function doesn't have sample, it could be very + // cold or it could be a new function never being sampled. Those functions + // will be kept in the ".text.unknown" section. if (config->zKeepTextSectionPrefix) - for (StringRef v : - {".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."}) + for (StringRef v : {".text.hot.", ".text.unknown.", ".text.unlikely.", + ".text.startup.", ".text.exit."}) if (isSectionPrefix(v, s->name)) return v.drop_back(); for (StringRef v : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.", ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.", - ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab.", - ".openbsd.randomdata."}) + ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."}) if (isSectionPrefix(v, s->name)) return v.drop_back(); - // CommonSection is identified as "COMMON" in linker scripts. - // By default, it should go to .bss section. - if (s->name == "COMMON") - return ".bss"; - return s->name; } @@ -140,20 +152,32 @@ static bool needsInterpSection() { !config->dynamicLinker.empty() && script->needsInterpSection(); } -template void writeResult() { Writer().run(); } +template void elf::writeResult() { + llvm::TimeTraceScope timeScope("Write output file"); + Writer().run(); +} static void removeEmptyPTLoad(std::vector &phdrs) { - llvm::erase_if(phdrs, [&](const PhdrEntry *p) { - if (p->p_type != PT_LOAD) - return false; - if (!p->firstSec) - return true; - uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr; - return size == 0; - }); + auto it = std::stable_partition( + phdrs.begin(), phdrs.end(), [&](const PhdrEntry *p) { + if (p->p_type != PT_LOAD) + return true; + if (!p->firstSec) + return false; + uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr; + return size != 0; + }); + + // Clear OutputSection::ptLoad for sections contained in removed + // segments. + DenseSet removed(it, phdrs.end()); + for (OutputSection *sec : outputSections) + if (removed.count(sec->ptLoad)) + sec->ptLoad = nullptr; + phdrs.erase(it, phdrs.end()); } -void copySectionsIntoPartitions() { +void elf::copySectionsIntoPartitions() { std::vector newSections; for (unsigned part = 2; part != partitions.size() + 1; ++part) { for (InputSectionBase *s : inputSections) { @@ -175,7 +199,7 @@ void copySectionsIntoPartitions() { newSections.end()); } -void combineEhSections() { +void elf::combineEhSections() { for (InputSectionBase *&s : inputSections) { // Ignore dead sections and the partition end marker (.part.end), // whose partition number is out of bounds. @@ -216,7 +240,7 @@ static Defined *addAbsolute(StringRef name) { // The linker is expected to define some symbols depending on // the linking result. This function defines such symbols. -void addReservedSymbols() { +void elf::addReservedSymbols() { if (config->emachine == EM_MIPS) { // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer // so that it points to an absolute address which by default is relative @@ -240,6 +264,8 @@ void addReservedSymbols() { // glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't // support Small Data Area, define it arbitrarily as 0. addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN); + } else if (config->emachine == EM_PPC64) { + addPPC64SaveRestore(); } // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which @@ -293,7 +319,6 @@ void addReservedSymbols() { }; ElfSym::bss = add("__bss_start", 0); - ElfSym::data = add("__data_start", 0); ElfSym::end1 = add("end", -1); ElfSym::end2 = add("_end", -1); ElfSym::etext1 = add("etext", -1); @@ -310,7 +335,7 @@ static OutputSection *findSection(StringRef name, unsigned partition = 1) { return nullptr; } -template void createSyntheticSections() { +template void elf::createSyntheticSections() { // Initialize all pointers with NULL. This is needed because // you can call lld::elf::main more than once as a library. memset(&Out::first, 0, sizeof(Out)); @@ -523,7 +548,8 @@ template void createSyntheticSections() { add(in.ibtPlt); } - in.plt = make(); + in.plt = config->emachine == EM_PPC ? make() + : make(); add(in.plt); in.iplt = make(); add(in.iplt); @@ -550,8 +576,7 @@ template void createSyntheticSections() { // The main function of the writer. template void Writer::run() { - if (config->discard != DiscardPolicy::All) - copyLocalSymbols(); + copyLocalSymbols(); if (config->copyRelocs) addSectionSymbols(); @@ -592,6 +617,14 @@ template void Writer::run() { for (OutputSection *sec : outputSections) sec->addr = 0; + // Handle --print-map(-M)/--Map, --cref and --print-archive-stats=. Dump them + // before checkSections() because the files may be useful in case + // checkSections() or openFile() fails, for example, due to an erroneous file + // size. + writeMapFile(); + writeCrossReferenceTable(); + writeArchiveStats(); + if (config->checkSections) checkSections(); @@ -618,27 +651,67 @@ template void Writer::run() { if (errorCount()) return; - // Handle -Map and -cref options. - writeMapFile(); - writeCrossReferenceTable(); - if (errorCount()) - return; - if (auto e = buffer->commit()) error("failed to write to the output file: " + toString(std::move(e))); } +template +static void markUsedLocalSymbolsImpl(ObjFile *file, + llvm::ArrayRef rels) { + for (const RelTy &rel : rels) { + Symbol &sym = file->getRelocTargetSym(rel); + if (sym.isLocal()) + sym.used = true; + } +} + +// The function ensures that the "used" field of local symbols reflects the fact +// that the symbol is used in a relocation from a live section. +template static void markUsedLocalSymbols() { + // With --gc-sections, the field is already filled. + // See MarkLive::resolveReloc(). + if (config->gcSections) + return; + // Without --gc-sections, the field is initialized with "true". + // Drop the flag first and then rise for symbols referenced in relocations. + for (InputFile *file : objectFiles) { + ObjFile *f = cast>(file); + for (Symbol *b : f->getLocalSymbols()) + b->used = false; + for (InputSectionBase *s : f->getSections()) { + InputSection *isec = dyn_cast_or_null(s); + if (!isec) + continue; + if (isec->type == SHT_REL) + markUsedLocalSymbolsImpl(f, isec->getDataAs()); + else if (isec->type == SHT_RELA) + markUsedLocalSymbolsImpl(f, isec->getDataAs()); + } + } +} + static bool shouldKeepInSymtab(const Defined &sym) { if (sym.isSection()) return false; - if (config->discard == DiscardPolicy::None) + // If --emit-reloc or -r is given, preserve symbols referenced by relocations + // from live sections. + if (config->copyRelocs && sym.used) return true; - // If -emit-reloc is given, all symbols including local ones need to be - // copied because they may be referenced by relocations. - if (config->emitRelocs) + // Exclude local symbols pointing to .ARM.exidx sections. + // They are probably mapping symbols "$d", which are optional for these + // sections. After merging the .ARM.exidx sections, some of these symbols + // may become dangling. The easiest way to avoid the issue is not to add + // them to the symbol table from the beginning. + if (config->emachine == EM_ARM && sym.section && + sym.section->type == SHT_ARM_EXIDX) + return false; + + if (config->discard == DiscardPolicy::None) return true; + if (config->discard == DiscardPolicy::All) + return false; // In ELF assembly .L symbols are normally discarded by the assembler. // If the assembler fails to do so, the linker discards them if @@ -685,12 +758,12 @@ static bool includeInSymtab(const Symbol &b) { template void Writer::copyLocalSymbols() { if (!in.symTab) return; + if (config->copyRelocs && config->discard != DiscardPolicy::None) + markUsedLocalSymbols(); for (InputFile *file : objectFiles) { ObjFile *f = cast>(file); for (Symbol *b : f->getLocalSymbols()) { - if (!b->isLocal()) - fatal(toString(f) + - ": broken object: getLocalSymbols returns a non-local symbol"); + assert(b->isLocal() && "should have been caught in initializeSymbols()"); auto *dr = dyn_cast(b); // No reason to keep local undefined symbol in symtab. @@ -797,11 +870,7 @@ static bool isRelroSection(const OutputSection *sec) { // However, if "-z now" is given, the lazy symbol resolution is // disabled, which enables us to put it into RELRO. if (sec == in.gotPlt->getParent()) -#ifndef __OpenBSD__ return config->zNow; -#else - return true; /* kbind(2) means we can always put these in RELRO */ -#endif // .dynamic section contains data for the dynamic linker, and // there's no need to write to it at runtime, so it's better to put @@ -816,7 +885,8 @@ static bool isRelroSection(const OutputSection *sec) { StringRef s = sec->name; return s == ".data.rel.ro" || s == ".bss.rel.ro" || s == ".ctors" || s == ".dtors" || s == ".jcr" || s == ".eh_frame" || - s == ".openbsd.randomdata"; + s == ".fini_array" || s == ".init_array" || + s == ".openbsd.randomdata" || s == ".preinit_array"; } // We compute a rank for each section. The rank indicates where the @@ -1110,9 +1180,6 @@ template void Writer::setReservedSymbolSections() { if (ElfSym::bss) ElfSym::bss->section = findSection(".bss"); - if (ElfSym::data) - ElfSym::data->section = findSection(".data"); - // Setup MIPS _gp_disp/__gnu_local_gp symbols which should // be equal to the _gp symbol's value. if (ElfSym::mipsGp) { @@ -1210,6 +1277,27 @@ findOrphanPos(std::vector::iterator b, return i; } +// Adds random priorities to sections not already in the map. +static void maybeShuffle(DenseMap &order) { + if (!config->shuffleSectionSeed) + return; + + std::vector priorities(inputSections.size() - order.size()); + // Existing priorities are < 0, so use priorities >= 0 for the missing + // sections. + int curPrio = 0; + for (int &prio : priorities) + prio = curPrio++; + uint32_t seed = *config->shuffleSectionSeed; + std::mt19937 g(seed ? seed : std::random_device()()); + llvm::shuffle(priorities.begin(), priorities.end(), g); + int prioIndex = 0; + for (InputSectionBase *sec : inputSections) { + if (order.try_emplace(sec, priorities[prioIndex]).second) + ++prioIndex; + } +} + // Builds section order for handling --symbol-ordering-file. static DenseMap buildSectionOrder() { DenseMap sectionOrder; @@ -1339,6 +1427,19 @@ static void sortSection(OutputSection *sec, const DenseMap &order) { StringRef name = sec->name; + // Never sort these. + if (name == ".init" || name == ".fini") + return; + + // Sort input sections by priority using the list provided by + // --symbol-ordering-file or --shuffle-sections=. This is a least significant + // digit radix sort. The sections may be sorted stably again by a more + // significant key. + if (!order.empty()) + for (BaseCommand *b : sec->sectionCommands) + if (auto *isd = dyn_cast(b)) + sortISDBySectionOrder(isd, order); + // Sort input sections by section name suffixes for // __attribute__((init_priority(N))). if (name == ".init_array" || name == ".fini_array") { @@ -1354,10 +1455,6 @@ static void sortSection(OutputSection *sec, return; } - // Never sort these. - if (name == ".init" || name == ".fini") - return; - // .toc is allocated just after .got and is accessed using GOT-relative // relocations. Object files compiled with small code model have an // addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations. @@ -1375,13 +1472,6 @@ static void sortSection(OutputSection *sec, }); return; } - - // Sort input sections by priority using the list provided - // by --symbol-ordering-file. - if (!order.empty()) - for (BaseCommand *b : sec->sectionCommands) - if (auto *isd = dyn_cast(b)) - sortISDBySectionOrder(isd, order); } // If no layout was provided by linker script, we want to apply default @@ -1389,6 +1479,7 @@ static void sortSection(OutputSection *sec, template void Writer::sortInputSections() { // Build the order once since it is expensive. DenseMap order = buildSectionOrder(); + maybeShuffle(order); for (BaseCommand *base : script->sectionCommands) if (auto *sec = dyn_cast(base)) sortSection(sec, order); @@ -1425,9 +1516,15 @@ template void Writer::sortSections() { llvm::find_if(script->sectionCommands, isSection), llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(), compareSections); + + // Process INSERT commands. From this point onwards the order of + // script->sectionCommands is fixed. + script->processInsertCommands(); return; } + script->processInsertCommands(); + // Orphan sections are sections present in the input files which are // not explicitly placed into the output file by the linker script. // @@ -1513,7 +1610,7 @@ static bool compareByFilePosition(InputSection *a, InputSection *b) { OutputSection *bOut = lb->getParent(); if (aOut != bOut) - return aOut->sectionIndex < bOut->sectionIndex; + return aOut->addr < bOut->addr; return la->outSecOff < lb->outSecOff; } @@ -1569,6 +1666,11 @@ template void Writer::resolveShfLinkOrder() { } } +static void finalizeSynthetic(SyntheticSection *sec) { + if (sec && sec->isNeeded() && sec->getParent()) + sec->finalizeContents(); +} + // We need to generate and finalize the content that depends on the address of // InputSections. As the generation of the content may also alter InputSection // addresses we must converge to a fixed point. We do that here. See the comment @@ -1578,6 +1680,17 @@ template void Writer::finalizeAddressDependentContent() { AArch64Err843419Patcher a64p; ARMErr657417Patcher a32p; script->assignAddresses(); + // .ARM.exidx and SHF_LINK_ORDER do not require precise addresses, but they + // do require the relative addresses of OutputSections because linker scripts + // can assign Virtual Addresses to OutputSections that are not monotonically + // increasing. + for (Partition &part : partitions) + finalizeSynthetic(part.armExidx); + resolveShfLinkOrder(); + + // Converts call x@GDPLT to call __tls_get_addr + if (config->emachine == EM_HEXAGON) + hexagonTLSSymbolUpdate(outputSections); int assignPasses = 0; for (;;) { @@ -1624,11 +1737,103 @@ template void Writer::finalizeAddressDependentContent() { } } } + + // If addrExpr is set, the address may not be a multiple of the alignment. + // Warn because this is error-prone. + for (BaseCommand *cmd : script->sectionCommands) + if (auto *os = dyn_cast(cmd)) + if (os->addr % os->alignment != 0) + warn("address (0x" + Twine::utohexstr(os->addr) + ") of section " + + os->name + " is not a multiple of alignment (" + + Twine(os->alignment) + ")"); +} + +// If Input Sections have been shrinked (basic block sections) then +// update symbol values and sizes associated with these sections. With basic +// block sections, input sections can shrink when the jump instructions at +// the end of the section are relaxed. +static void fixSymbolsAfterShrinking() { + for (InputFile *File : objectFiles) { + parallelForEach(File->getSymbols(), [&](Symbol *Sym) { + auto *def = dyn_cast(Sym); + if (!def) + return; + + const SectionBase *sec = def->section; + if (!sec) + return; + + const InputSectionBase *inputSec = dyn_cast(sec->repl); + if (!inputSec || !inputSec->bytesDropped) + return; + + const size_t OldSize = inputSec->data().size(); + const size_t NewSize = OldSize - inputSec->bytesDropped; + + if (def->value > NewSize && def->value <= OldSize) { + LLVM_DEBUG(llvm::dbgs() + << "Moving symbol " << Sym->getName() << " from " + << def->value << " to " + << def->value - inputSec->bytesDropped << " bytes\n"); + def->value -= inputSec->bytesDropped; + return; + } + + if (def->value + def->size > NewSize && def->value <= OldSize && + def->value + def->size <= OldSize) { + LLVM_DEBUG(llvm::dbgs() + << "Shrinking symbol " << Sym->getName() << " from " + << def->size << " to " << def->size - inputSec->bytesDropped + << " bytes\n"); + def->size -= inputSec->bytesDropped; + } + }); + } } -static void finalizeSynthetic(SyntheticSection *sec) { - if (sec && sec->isNeeded() && sec->getParent()) - sec->finalizeContents(); +// If basic block sections exist, there are opportunities to delete fall thru +// jumps and shrink jump instructions after basic block reordering. This +// relaxation pass does that. It is only enabled when --optimize-bb-jumps +// option is used. +template void Writer::optimizeBasicBlockJumps() { + assert(config->optimizeBBJumps); + + script->assignAddresses(); + // For every output section that has executable input sections, this + // does the following: + // 1. Deletes all direct jump instructions in input sections that + // jump to the following section as it is not required. + // 2. If there are two consecutive jump instructions, it checks + // if they can be flipped and one can be deleted. + for (OutputSection *os : outputSections) { + if (!(os->flags & SHF_EXECINSTR)) + continue; + std::vector sections = getInputSections(os); + std::vector result(sections.size()); + // Delete all fall through jump instructions. Also, check if two + // consecutive jump instructions can be flipped so that a fall + // through jmp instruction can be deleted. + parallelForEachN(0, sections.size(), [&](size_t i) { + InputSection *next = i + 1 < sections.size() ? sections[i + 1] : nullptr; + InputSection &is = *sections[i]; + result[i] = + target->deleteFallThruJmpInsn(is, is.getFile(), next) ? 1 : 0; + }); + size_t numDeleted = std::count(result.begin(), result.end(), 1); + if (numDeleted > 0) { + script->assignAddresses(); + LLVM_DEBUG(llvm::dbgs() + << "Removing " << numDeleted << " fall through jumps\n"); + } + } + + fixSymbolsAfterShrinking(); + + for (OutputSection *os : outputSections) { + std::vector sections = getInputSections(os); + for (InputSection *is : sections) + is->trim(); + } } // In order to allow users to manipulate linker-synthesized sections, @@ -1655,12 +1860,15 @@ static void removeUnusedSyntheticSections() { if (!os || ss->isNeeded()) continue; - // If we reach here, then SS is an unused synthetic section and we want to - // remove it from corresponding input section description of output section. + // If we reach here, then ss is an unused synthetic section and we want to + // remove it from the corresponding input section description, and + // orphanSections. for (BaseCommand *b : os->sectionCommands) if (auto *isd = dyn_cast(b)) llvm::erase_if(isd->sections, [=](InputSection *isec) { return isec == ss; }); + llvm::erase_if(script->orphanSections, + [=](const InputSectionBase *isec) { return isec == ss; }); } } @@ -1741,6 +1949,7 @@ template void Writer::finalizeSections() { // we can correctly decide if a dynamic relocation is needed. This is called // after processSymbolAssignments() because it needs to know whether a // linker-script-defined symbol is absolute. + ppc64noTocRelax.clear(); if (!config->relocatable) { forEachRelSec(scanRelocations); reportUndefinedSymbols(); @@ -1769,7 +1978,8 @@ template void Writer::finalizeSections() { if (sym->isUndefined() && !sym->isWeak()) if (auto *f = dyn_cast_or_null(sym->file)) if (f->allNeededIsKnown) - error(toString(f) + ": undefined reference to " + toString(*sym)); + errorOrWarn(toString(f) + ": undefined reference to " + + toString(*sym) + " [--no-allow-shlib-undefined]"); } // Now that we have defined all possible global symbols including linker- @@ -1807,6 +2017,7 @@ template void Writer::finalizeSections() { in.mipsGot->build(); removeUnusedSyntheticSections(); + script->diagnoseOrphanHandling(); sortSections(); @@ -1823,6 +2034,15 @@ template void Writer::finalizeSections() { sec->addrExpr = [=] { return i->second; }; } + // With the outputSections available check for GDPLT relocations + // and add __tls_get_addr symbol if needed. + if (config->emachine == EM_HEXAGON && hexagonNeedsTLSSymbol(outputSections)) { + Symbol *sym = symtab->addSymbol(Undefined{ + nullptr, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE}); + sym->isPreemptible = true; + partitions[0].dynSymTab->addSymbol(sym); + } + // This is a bit of a hack. A value of 0 means undef, so we set it // to 1 to make __ehdr_start defined. The section number is not // particularly relevant. @@ -1886,7 +2106,6 @@ template void Writer::finalizeSections() { // Dynamic section must be the last one in this list and dynamic // symbol table section (dynSymTab) must be the first one. for (Partition &part : partitions) { - finalizeSynthetic(part.armExidx); finalizeSynthetic(part.dynSymTab); finalizeSynthetic(part.gnuHashTab); finalizeSynthetic(part.hashTab); @@ -1902,12 +2121,6 @@ template void Writer::finalizeSections() { if (!script->hasSectionsCommand && !config->relocatable) fixSectionAlignments(); - // SHFLinkOrder processing must be processed after relative section placements are - // known but before addresses are allocated. - resolveShfLinkOrder(); - if (errorCount()) - return; - // This is used to: // 1) Create "thunks": // Jump instructions in many ISAs have small displacements, and therefore @@ -1930,11 +2143,19 @@ template void Writer::finalizeSections() { // sometimes using forward symbol declarations. We want to set the correct // values. They also might change after adding the thunks. finalizeAddressDependentContent(); + if (errorCount()) + return; // finalizeAddressDependentContent may have added local symbols to the static symbol table. finalizeSynthetic(in.symTab); finalizeSynthetic(in.ppc64LongBranchTarget); + // Relaxation to delete inter-basic block jumps created by basic block + // sections. Run after in.symTab is finalized as optimizeBasicBlockJumps + // can relax jump instructions based on symbol offset. + if (config->optimizeBBJumps) + optimizeBasicBlockJumps(); + // Fill other section headers. The dynamic table is finalized // at the end because some tags like RELSZ depend on result // of finalizing other sections. @@ -2008,8 +2229,10 @@ void Writer::addStartStopSymbols(OutputSection *sec) { StringRef s = sec->name; if (!isValidCIdentifier(s)) return; - addOptionalRegular(saver.save("__start_" + s), sec, 0, STV_PROTECTED); - addOptionalRegular(saver.save("__stop_" + s), sec, -1, STV_PROTECTED); + addOptionalRegular(saver.save("__start_" + s), sec, 0, + config->zStartStopVisibility); + addOptionalRegular(saver.save("__stop_" + s), sec, -1, + config->zStartStopVisibility); } static bool needsPtLoad(OutputSection *sec) { @@ -2128,12 +2351,11 @@ std::vector Writer::createPhdrs(Partition &part) { // time, we don't want to create a separate load segment for the headers, // even if the first output section has an AT or AT> attribute. uint64_t newFlags = computeFlags(sec->getPhdrFlags()); - if (!load || - ((sec->lmaExpr || - (sec->lmaRegion && (sec->lmaRegion != load->firstSec->lmaRegion))) && - load->lastSec != Out::programHeaders) || - sec->memRegion != load->firstSec->memRegion || flags != newFlags || - sec == relroEnd) { + bool sameLMARegion = + load && !sec->lmaExpr && sec->lmaRegion == load->firstSec->lmaRegion; + if (!(load && newFlags == flags && sec != relroEnd && + sec->memRegion == load->firstSec->memRegion && + (sameLMARegion || load->lastSec == Out::programHeaders))) { load = addHdr(PT_LOAD, newFlags); flags = newFlags; } @@ -2227,7 +2449,10 @@ template void Writer::fixSectionAlignments() { const PhdrEntry *prev; auto pageAlign = [&](const PhdrEntry *p) { OutputSection *cmd = p->firstSec; - if (cmd && !cmd->addrExpr) { + if (!cmd) + return; + cmd->alignExpr = [align = cmd->alignment]() { return align; }; + if (!cmd->addrExpr) { // Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid // padding in the file contents. // @@ -2275,31 +2500,6 @@ template void Writer::fixSectionAlignments() { } }; -#ifdef __OpenBSD__ - // On i386, produce binaries that are compatible with our W^X implementation - if (config->emachine == EM_386) { - auto NXAlign = [](OutputSection *Cmd) { - if (Cmd && !Cmd->addrExpr) - Cmd->addrExpr = [=] { - return alignTo(script->getDot(), 0x20000000); - }; - }; - - for (Partition &part : partitions) { - PhdrEntry *firstRW = nullptr; - for (PhdrEntry *P : part.phdrs) { - if (P->p_type == PT_LOAD && (P->p_flags & PF_W)) { - firstRW = P; - break; - } - } - - if (firstRW) - NXAlign(firstRW->firstSec); - } - } -#endif - for (Partition &part : partitions) { prev = nullptr; for (const PhdrEntry *p : part.phdrs) @@ -2763,15 +2963,12 @@ template void Writer::writeBuildId() { part.buildId->writeBuildId(buildId); } -template void createSyntheticSections(); -template void createSyntheticSections(); -template void createSyntheticSections(); -template void createSyntheticSections(); - -template void writeResult(); -template void writeResult(); -template void writeResult(); -template void writeResult(); +template void elf::createSyntheticSections(); +template void elf::createSyntheticSections(); +template void elf::createSyntheticSections(); +template void elf::createSyntheticSections(); -} // namespace elf -} // namespace lld +template void elf::writeResult(); +template void elf::writeResult(); +template void elf::writeResult(); +template void elf::writeResult(); diff --git a/gnu/llvm/lld/ELF/Writer.h b/gnu/llvm/lld/ELF/Writer.h index c2d96adc87c..3698544d977 100644 --- a/gnu/llvm/lld/ELF/Writer.h +++ b/gnu/llvm/lld/ELF/Writer.h @@ -30,7 +30,7 @@ template void writeResult(); // placed in it. struct PhdrEntry { PhdrEntry(unsigned type, unsigned flags) - : p_align(type == llvm::ELF::PT_LOAD ? config->textAlignPageSize : 0), + : p_align(type == llvm::ELF::PT_LOAD ? config->maxPageSize : 0), p_type(type), p_flags(flags) {} void add(OutputSection *sec); diff --git a/gnu/llvm/lld/MachO/Arch/X86_64.cpp b/gnu/llvm/lld/MachO/Arch/X86_64.cpp new file mode 100644 index 00000000000..36f686ca2f1 --- /dev/null +++ b/gnu/llvm/lld/MachO/Arch/X86_64.cpp @@ -0,0 +1,286 @@ +//===- X86_64.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" + +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::macho; + +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; + + void writeStub(uint8_t *buf, const DylibSymbol &) 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; +}; + +} // 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(); +} + +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: + 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: + 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; + default: + error("TODO: Unhandled relocation type " + std::to_string(rel.r_type)); + return 0; + } + + switch (rel.r_length) { + case 0: + return *loc; + case 1: + return read16le(loc); + case 2: + return read32le(loc); + case 3: + return read64le(loc); + 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"); + } + + switch (r.length) { + case 0: + *loc = val; + break; + case 1: + write16le(loc, val); + break; + case 2: + write32le(loc, val); + break; + case 3: + write64le(loc, val); + break; + default: + llvm_unreachable("invalid r_length"); + } +} + +// The following methods emit a number of assembly sequences with RIP-relative +// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing +// to the next instruction, not the current instruction, so we always have to +// account for the current instruction's size when calculating offsets. +// writeRipRelative helps with that. +// +// 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) { + uint64_t rip = bufAddr + bufOff; + // 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); +} + +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 { + 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); +} + +static constexpr uint8_t stubHelperHeader[] = { + 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11 + 0x41, 0x53, // 0x7: pushq %r11 + 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip) + 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, + in.got->addr + + in.stubHelper->stubBinder->gotIndex * WordSize); +} + +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); +} + +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"); + } +} + +X86_64::X86_64() { + cpuType = CPU_TYPE_X86_64; + cpuSubtype = CPU_SUBTYPE_X86_64_ALL; + + stubSize = sizeof(stub); + stubHelperHeaderSize = sizeof(stubHelperHeader); + stubHelperEntrySize = sizeof(stubHelperEntry); +} + +TargetInfo *macho::createX86_64TargetInfo() { + static X86_64 t; + return &t; +} diff --git a/gnu/llvm/lld/MachO/CMakeLists.txt b/gnu/llvm/lld/MachO/CMakeLists.txt new file mode 100644 index 00000000000..6fe356f5158 --- /dev/null +++ b/gnu/llvm/lld/MachO/CMakeLists.txt @@ -0,0 +1,36 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(MachOOptionsTableGen) + +add_lld_library(lldMachO2 + Arch/X86_64.cpp + Driver.cpp + ExportTrie.cpp + InputFiles.cpp + InputSection.cpp + MergedOutputSection.cpp + OutputSection.cpp + OutputSegment.cpp + SymbolTable.cpp + Symbols.cpp + SyntheticSections.cpp + Target.cpp + Writer.cpp + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + BinaryFormat + Core + Object + Option + Support + TextAPI + + LINK_LIBS + lldCommon + ${LLVM_PTHREAD_LIB} + + DEPENDS + MachOOptionsTableGen + ${tablegen_deps} + ) diff --git a/gnu/llvm/lld/MachO/Config.h b/gnu/llvm/lld/MachO/Config.h new file mode 100644 index 00000000000..79812a43356 --- /dev/null +++ b/gnu/llvm/lld/MachO/Config.h @@ -0,0 +1,57 @@ +//===- Config.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_MACHO_CONFIG_H +#define LLD_MACHO_CONFIG_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/TextAPI/MachO/Architecture.h" + +#include + +namespace lld { +namespace macho { + +class Symbol; +struct SymbolPriorityEntry; + +struct Configuration { + Symbol *entry; + bool hasReexports = false; + llvm::StringRef installName; + llvm::StringRef outputFile; + llvm::MachO::Architecture arch; + llvm::MachO::HeaderFileType outputType; + std::vector librarySearchPaths; + // TODO: use the framework search paths + std::vector frameworkSearchPaths; + llvm::DenseMap priorities; +}; + +// The symbol with the highest priority should be ordered first in the output +// section (modulo input section contiguity constraints). Using priority +// (highest first) instead of order (lowest first) has the convenient property +// that the default-constructed zero priority -- for symbols/sections without a +// user-defined order -- naturally ends up putting them at the end of the +// output. +struct SymbolPriorityEntry { + // The priority given to a matching symbol, regardless of which object file + // it originated from. + size_t anyObjectFile = 0; + // The priority given to a matching symbol from a particular object file. + llvm::DenseMap objectFiles; +}; + +extern Configuration *config; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/Driver.cpp b/gnu/llvm/lld/MachO/Driver.cpp new file mode 100644 index 00000000000..2a3b0042162 --- /dev/null +++ b/gnu/llvm/lld/MachO/Driver.cpp @@ -0,0 +1,451 @@ +//===- Driver.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Driver.h" +#include "Config.h" +#include "InputFiles.h" +#include "OutputSection.h" +#include "OutputSegment.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Target.h" +#include "Writer.h" + +#include "lld/Common/Args.h" +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/LLVM.h" +#include "lld/Common/Memory.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/Object/Archive.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::sys; +using namespace llvm::opt; +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()); + + opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount); + + if (missingCount) + error(Twine(args.getArgString(missingIndex)) + ": missing argument"); + + for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) + error("unknown argument: " + arg->getSpelling()); + return args; +} + +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) { + 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; + + 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(); + } + } + 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); + } +} + +static bool isDirectory(StringRef option, StringRef path) { + if (!fs::exists(path)) { + warn("directory not found for option -" + option + path); + return false; + } else if (!fs::is_directory(path)) { + warn("option -" + option + path + " references a non-directory path"); + return false; + } + 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)) + 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); + } + } +} + +static void getLibrarySearchPaths(std::vector &paths, + opt::InputArgList &args) { + getSearchPaths(paths, OPT_L, args, {"/usr/lib", "/usr/local/lib"}); +} + +static void getFrameworkSearchPaths(std::vector &paths, + opt::InputArgList &args) { + getSearchPaths(paths, OPT_F, args, + {"/Library/Frameworks", "/System/Library/Frameworks"}); +} + +static void addFile(StringRef path) { + Optional buffer = readFile(path); + if (!buffer) + return; + MemoryBufferRef mbref = *buffer; + + switch (identify_magic(mbref.getBuffer())) { + case file_magic::archive: { + 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))); + break; + } + case file_magic::macho_object: + inputFiles.push_back(make(mbref)); + break; + case file_magic::macho_dynamically_linked_shared_lib: + inputFiles.push_back(make(mbref)); + break; + case file_magic::tapi_file: { + llvm::Expected> result = + TextAPIReader::get(mbref); + if (!result) + return; + + inputFiles.push_back(make(std::move(*result))); + break; + } + default: + error(path + ": unhandled file type"); + } +} + +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(); +} + +// 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. +// +// 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) { + Optional buffer = readFile(path); + if (!buffer) { + error("Could not read order file at " + path); + return; + } + + MemoryBufferRef mbref = *buffer; + size_t priority = std::numeric_limits::max(); + for (StringRef 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; + + fields[fieldCount++] = tok; + } + + 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 (!arch.empty()) { + if (!isArchString(arch)) { + error("invalid arch \"" + arch + "\" in order file: expected one of " + + llvm::join(archNames, ", ")); + continue; + } + + // 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; + } + + if (!symbol.empty()) { + SymbolPriorityEntry &entry = config->priorities[symbol]; + if (!objectFile.empty()) + entry.objectFiles.insert(std::make_pair(objectFile, priority)); + else + entry.anyObjectFile = std::max(entry.anyObjectFile, priority); + } + + --priority; + } +} + +// 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) { + for (InputFile *file : inputFiles) { + if (auto *dylibFile = dyn_cast(file)) { + StringRef filename = path::filename(dylibFile->getName()); + if (filename.consume_front(searchName) && filename == ".dylib") { + dylibFile->reexport = true; + return true; + } + } + } + return false; +} + +static void handlePlatformVersion(const opt::Arg *arg) { + // TODO: implementation coming very soon ... +} + +static void warnIfDeprecatedOption(const opt::Option &opt) { + if (!opt.getGroup().isValid()) + return; + if (opt.getGroup().getID() == OPT_grp_deprecated) { + warn("Option `" + opt.getPrefixedName() + "' is deprecated in ld64:"); + warn(opt.getHelpText()); + } +} + +static void warnIfUnimplementedOption(const opt::Option &opt) { + if (!opt.getGroup().isValid()) + return; + switch (opt.getGroup().getID()) { + case OPT_grp_deprecated: + // warn about deprecated options elsewhere + break; + case OPT_grp_undocumented: + warn("Option `" + opt.getPrefixedName() + + "' is undocumented. Should lld implement it?"); + break; + case OPT_grp_obsolete: + warn("Option `" + opt.getPrefixedName() + + "' is obsolete. Please modernize your usage."); + break; + case OPT_grp_ignored: + warn("Option `" + opt.getPrefixedName() + "' is ignored."); + break; + default: + warn("Option `" + opt.getPrefixedName() + + "' is not yet implemented. Stay tuned..."); + break; + } +} + +bool macho::link(llvm::ArrayRef argsArr, bool canExitEarly, + raw_ostream &stdoutOS, raw_ostream &stderrOS) { + lld::stdoutOS = &stdoutOS; + lld::stderrOS = &stderrOS; + + 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)); + + if (args.hasArg(OPT_help_hidden)) { + parser.printHelp(argsArr[0], /*showHidden=*/true); + return true; + } else if (args.hasArg(OPT_help)) { + parser.printHelp(argsArr[0], /*showHidden=*/false); + return true; + } + + config = make(); + symtab = make(); + target = createTargetInfo(args); + + config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main")); + 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; + + if (args.hasArg(OPT_v)) { + message(getLLDVersion()); + message(StringRef("Library search paths:") + + (config->librarySearchPaths.size() + ? "\n\t" + llvm::join(config->librarySearchPaths, "\n\t") + : "")); + message(StringRef("Framework search paths:") + + (config->frameworkSearchPaths.size() + ? "\n\t" + llvm::join(config->frameworkSearchPaths, "\n\t") + : "")); + freeArena(); + return !errorCount(); + } + + 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; + } + } + + // 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"); + } + + StringRef orderFile = args.getLastArgValue(OPT_order_file); + if (!orderFile.empty()) + parseOrderFile(orderFile); + + if (config->outputType == MH_EXECUTE && !isa(config->entry)) { + error("undefined symbol: " + config->entry->getName()); + return false; + } + + createSyntheticSections(); + + // Initialize InputSections. + for (InputFile *file : inputFiles) { + for (SubsectionMap &map : file->subsections) { + for (auto &p : map) { + InputSection *isec = p.second; + inputSections.push_back(isec); + } + } + } + + // Write to an output file. + writeResult(); + + 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 new file mode 100644 index 00000000000..2233740d1db --- /dev/null +++ b/gnu/llvm/lld/MachO/Driver.h @@ -0,0 +1,36 @@ +//===- Driver.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_MACHO_DRIVER_H +#define LLD_MACHO_DRIVER_H + +#include "lld/Common/LLVM.h" +#include "llvm/Option/OptTable.h" + +namespace lld { +namespace macho { + +class MachOOptTable : public llvm::opt::OptTable { +public: + MachOOptTable(); + llvm::opt::InputArgList parse(ArrayRef argv); + void printHelp(const char *argv0, bool showHidden) const; +}; + +// Create enum with OPT_xxx values for each option in Options.td +enum { + OPT_INVALID = 0, +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/ExportTrie.cpp b/gnu/llvm/lld/MachO/ExportTrie.cpp new file mode 100644 index 00000000000..7cc81bcfd5f --- /dev/null +++ b/gnu/llvm/lld/MachO/ExportTrie.cpp @@ -0,0 +1,283 @@ +//===- ExportTrie.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 a partial implementation of the Mach-O export trie format. It's +// essentially a symbol table encoded as a compressed prefix trie, meaning that +// the common prefixes of each symbol name are shared for a more compact +// representation. The prefixes are stored on the edges of the trie, and one +// edge can represent multiple characters. For example, given two exported +// symbols _bar and _baz, we will have a trie like this (terminal nodes are +// marked with an asterisk): +// +// +-+-+ +// | | // root node +// +-+-+ +// | +// | _ba +// | +// +-+-+ +// | | +// +-+-+ +// r / \ z +// / \ +// +-+-+ +-+-+ +// | * | | * | +// +-+-+ +-+-+ +// +// More documentation of the format can be found in +// llvm/tools/obj2yaml/macho2yaml.cpp. +// +//===----------------------------------------------------------------------===// + +#include "ExportTrie.h" +#include "Symbols.h" + +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/ADT/Optional.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace lld; +using namespace lld::macho; + +namespace { + +struct Edge { + Edge(StringRef s, TrieNode *node) : substring(s), child(node) {} + + StringRef substring; + struct TrieNode *child; +}; + +struct ExportInfo { + uint64_t address; + // TODO: Add proper support for re-exports & stub-and-resolver flags. +}; + +} // namespace + +struct macho::TrieNode { + std::vector edges; + Optional info; + // Estimated offset from the start of the serialized trie to the current node. + // This will converge to the true offset when updateOffset() is run to a + // fixpoint. + size_t offset = 0; + + // Returns whether the new estimated offset differs from the old one. + bool updateOffset(size_t &nextOffset); + void writeTo(uint8_t *buf) const; +}; + +bool TrieNode::updateOffset(size_t &nextOffset) { + // Size of the whole node (including the terminalSize and the outgoing edges.) + // In contrast, terminalSize only records the size of the other data in the + // node. + size_t nodeSize; + if (info) { + uint64_t flags = 0; + uint32_t terminalSize = + getULEB128Size(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); + } else { + nodeSize = 1; // Size of terminalSize (which has a value of 0) + } + // Compute size of all child edges. + ++nodeSize; // Byte for number of children. + for (Edge &edge : edges) { + nodeSize += edge.substring.size() + 1 // String length. + + getULEB128Size(edge.child->offset); // Offset len. + } + // On input, 'nextOffset' is the new preferred location for this node. + bool result = (offset != nextOffset); + // Store new location in node object for use by parents. + offset = nextOffset; + nextOffset += nodeSize; + return result; +} + +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); + buf += encodeULEB128(terminalSize, buf); + buf += encodeULEB128(flags, buf); + buf += encodeULEB128(info->address, buf); + } else { + // TrieNode with no Symbol info. + *buf++ = 0; // terminalSize + } + // Add number of children. TODO: Handle case where we have more than 256. + assert(edges.size() < 256); + *buf++ = edges.size(); + // Append each child edge substring and node offset. + for (const Edge &edge : edges) { + memcpy(buf, edge.substring.data(), edge.substring.size()); + buf += edge.substring.size(); + *buf++ = '\0'; + buf += encodeULEB128(edge.child->offset, buf); + } +} + +TrieNode *TrieBuilder::makeNode() { + auto *node = make(); + nodes.emplace_back(node); + return node; +} + +static int charAt(const Symbol *sym, size_t pos) { + StringRef str = sym->getName(); + if (pos >= str.size()) + return -1; + return str[pos]; +} + +// Build the trie by performing a three-way radix quicksort: We start by sorting +// the strings by their first characters, then sort the strings with the same +// first characters by their second characters, and so on recursively. Each +// time the prefixes diverge, we add a node to the trie. +// +// node: The most recently created node along this path in the trie (i.e. +// the furthest from the root.) +// lastPos: The prefix length of the most recently created node, i.e. the number +// of characters along its path from the root. +// pos: The string index we are currently sorting on. Note that each symbol +// S contained in vec has the same prefix S[0...pos). +void TrieBuilder::sortAndBuild(MutableArrayRef vec, + TrieNode *node, size_t lastPos, size_t pos) { +tailcall: + if (vec.empty()) + return; + + // Partition items so that items in [0, i) are less than the pivot, + // [i, j) are the same as the pivot, and [j, vec.size()) are greater than + // the pivot. + const Symbol *pivotSymbol = vec[vec.size() / 2]; + int pivot = charAt(pivotSymbol, pos); + size_t i = 0; + size_t j = vec.size(); + for (size_t k = 0; k < j;) { + int c = charAt(vec[k], pos); + if (c < pivot) + std::swap(vec[i++], vec[k++]); + else if (c > pivot) + std::swap(vec[--j], vec[k]); + else + k++; + } + + bool isTerminal = pivot == -1; + bool prefixesDiverge = i != 0 || j != vec.size(); + if (lastPos != pos && (isTerminal || prefixesDiverge)) { + TrieNode *newNode = makeNode(); + node->edges.emplace_back(pivotSymbol->getName().slice(lastPos, pos), + newNode); + node = newNode; + lastPos = pos; + } + + sortAndBuild(vec.slice(0, i), node, lastPos, pos); + sortAndBuild(vec.slice(j), node, lastPos, pos); + + if (isTerminal) { + assert(j - i == 1); // no duplicate symbols + node->info = {pivotSymbol->getVA()}; + } else { + // This is the tail-call-optimized version of the following: + // sortAndBuild(vec.slice(i, j - i), node, lastPos, pos + 1); + vec = vec.slice(i, j - i); + ++pos; + goto tailcall; + } +} + +size_t TrieBuilder::build() { + if (exported.empty()) + return 0; + + TrieNode *root = makeNode(); + sortAndBuild(exported, root, 0, 0); + + // Assign each node in the vector an offset in the trie stream, iterating + // until all uleb128 sizes have stabilized. + size_t offset; + bool more; + do { + offset = 0; + more = false; + for (TrieNode *node : nodes) + more |= node->updateOffset(offset); + } while (more); + + return offset; +} + +void TrieBuilder::writeTo(uint8_t *buf) const { + for (TrieNode *node : nodes) + node->writeTo(buf); +} + +namespace { + +// Parse a serialized trie and invoke a callback for each entry. +class TrieParser { +public: + TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback) + : start(buf), end(start + size), callback(callback) {} + + void parse(const uint8_t *buf, const Twine &cumulativeString); + + void parse() { parse(start, ""); } + + const uint8_t *start; + const uint8_t *end; + const TrieEntryCallback &callback; +}; + +} // namespace + +void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) { + if (buf >= end) + fatal("Node offset points outside export section"); + + unsigned ulebSize; + uint64_t terminalSize = decodeULEB128(buf, &ulebSize); + buf += ulebSize; + uint64_t flags = 0; + size_t offset; + if (terminalSize != 0) { + flags = decodeULEB128(buf, &ulebSize); + callback(cumulativeString, flags); + } + buf += terminalSize; + uint8_t numEdges = *buf++; + for (uint8_t i = 0; i < numEdges; ++i) { + const char *cbuf = reinterpret_cast(buf); + StringRef substring = StringRef(cbuf, strnlen(cbuf, end - buf)); + buf += substring.size() + 1; + offset = decodeULEB128(buf, &ulebSize); + buf += ulebSize; + parse(start + offset, cumulativeString + substring); + } +} + +void macho::parseTrie(const uint8_t *buf, size_t size, + const TrieEntryCallback &callback) { + if (size == 0) + return; + + TrieParser(buf, size, callback).parse(); +} diff --git a/gnu/llvm/lld/MachO/ExportTrie.h b/gnu/llvm/lld/MachO/ExportTrie.h new file mode 100644 index 00000000000..2bd8c33db9a --- /dev/null +++ b/gnu/llvm/lld/MachO/ExportTrie.h @@ -0,0 +1,47 @@ +//===- ExportTrie.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_EXPORT_TRIE_H +#define LLD_MACHO_EXPORT_TRIE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" + +#include + +namespace lld { +namespace macho { + +struct TrieNode; +class Symbol; + +class TrieBuilder { +public: + void addSymbol(const Symbol &sym) { exported.push_back(&sym); } + // Returns the size in bytes of the serialized trie. + size_t build(); + void writeTo(uint8_t *buf) const; + +private: + TrieNode *makeNode(); + void sortAndBuild(llvm::MutableArrayRef vec, TrieNode *node, + size_t lastPos, size_t pos); + + std::vector exported; + std::vector nodes; +}; + +using TrieEntryCallback = + llvm::function_ref; + +void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &); + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/InputFiles.cpp b/gnu/llvm/lld/MachO/InputFiles.cpp new file mode 100644 index 00000000000..46fe82f9882 --- /dev/null +++ b/gnu/llvm/lld/MachO/InputFiles.cpp @@ -0,0 +1,433 @@ +//===- InputFiles.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains functions to parse Mach-O object files. In this comment, +// we describe the Mach-O file structure and how we parse it. +// +// Mach-O is not very different from ELF or COFF. The notion of symbols, +// sections and relocations exists in Mach-O as it does in ELF and COFF. +// +// Perhaps the notion that is new to those who know ELF/COFF is "subsections". +// In ELF/COFF, sections are an atomic unit of data copied from input files to +// output files. When we merge or garbage-collect sections, we treat each +// section as an atomic unit. In Mach-O, that's not the case. Sections can +// consist of multiple subsections, and subsections are a unit of merging and +// garbage-collecting. Therefore, Mach-O's subsections are more similar to +// ELF/COFF's sections than Mach-O's sections are. +// +// A section can have multiple symbols. A symbol that does not have the +// N_ALT_ENTRY attribute indicates a beginning of a subsection. Therefore, by +// definition, a symbol is always present at the beginning of each subsection. A +// symbol with N_ALT_ENTRY attribute does not start a new subsection and can +// point to a middle of a subsection. +// +// The notion of subsections also affects how relocations are represented in +// Mach-O. All references within a section need to be explicitly represented as +// relocations if they refer to different subsections, because we obviously need +// to fix up addresses if subsections are laid out in an output file differently +// than they were in object files. To represent that, Mach-O relocations can +// refer to an unnamed location via its address. Scattered relocations (those +// with the R_SCATTERED bit set) always refer to unnamed locations. +// Non-scattered relocations refer to an unnamed location if r_extern is not set +// and r_symbolnum is zero. +// +// Without the above differences, I think you can use your knowledge about ELF +// and COFF for Mach-O. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Config.h" +#include "ExportTrie.h" +#include "InputSection.h" +#include "MachOStructs.h" +#include "OutputSection.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Target.h" + +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::support::endian; +using namespace llvm::sys; +using namespace lld; +using namespace lld::macho; + +std::vector macho::inputFiles; + +// 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()) { + error("cannot open " + path + ": " + ec.message()); + return None; + } + + std::unique_ptr &mb = *mbOrErr; + MemoryBufferRef mbref = mb->getMemBufferRef(); + make>(std::move(mb)); // take mb ownership + + // 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) + 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)); + + for (uint32_t i = 0, n = read32be(&hdr->nfat_arch); i < n; ++i) { + if (reinterpret_cast(arch + i + 1) > + buf + mbref.getBufferSize()) { + error(path + ": fat_arch struct extends beyond end of file"); + return None; + } + + if (read32be(&arch[i].cputype) != target->cpuType || + read32be(&arch[i].cpusubtype) != target->cpuSubtype) + continue; + + uint32_t offset = read32be(&arch[i].offset); + uint32_t size = read32be(&arch[i].size); + if (offset + size > mbref.getBufferSize()) + error(path + ": slice extends beyond end of file"); + return MemoryBufferRef(StringRef(buf + offset, size), path.copy(bAlloc)); + } + + error("unable to find matching architecture in " + 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); + + 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) { + 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}}); + } +} + +// Find the subsection corresponding to the greatest section offset that is <= +// that of the given offset. +// +// offset: an offset relative to the start of the original InputSection (before +// any subsection splitting has occurred). It will be updated to represent the +// same location as an offset relative to the start of the containing +// subsection. +static InputSection *findContainingSubsection(SubsectionMap &map, + uint32_t *offset) { + auto it = std::prev(map.upper_bound(*offset)); + *offset -= it->first; + return it->second; +} + +void InputFile::parseRelocations(const section_64 &sec, + SubsectionMap &subsecMap) { + auto *buf = reinterpret_cast(mb.getBufferStart()); + 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 rel = reinterpret_cast(anyRel); + + 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; + } 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) { + // 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; + } else { + // The addend for a non-pcrel relocation is its absolute address. + targetOffset = rawAddend - targetSec.addr; + } + r.target = findContainingSubsection(targetSubsecMap, &targetOffset); + r.addend = targetOffset; + } + + r.offset = rel.r_address; + InputSection *subsec = findContainingSubsection(subsecMap, &r.offset); + subsec->relocs.push_back(r); + } +} + +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; + + 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); + }; + + for (size_t i = 0, n = nList.size(); i < n; ++i) { + const structs::nlist_64 &sym = nList[i]; + + // Undefined symbol + if (!sym.n_sect) { + StringRef name = strtab + sym.n_strx; + symbols[i] = symtab->addUndefined(name); + continue; + } + + const section_64 &sec = sectionHeaders[sym.n_sect - 1]; + SubsectionMap &subsecMap = subsections[sym.n_sect - 1]; + uint64_t offset = sym.n_value - sec.addr; + + // 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); + continue; + } + + // 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); + 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); + + 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); + 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); + + subsecMap[offset] = secondIsec; + // By construction, the symbol will be at offset zero in the new section. + symbols[i] = createDefined(sym, secondIsec, 0); + } + + 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); + } +} + +ObjFile::ObjFile(MemoryBufferRef mb) : InputFile(ObjKind, mb) { + auto *buf = reinterpret_cast(mb.getBufferStart()); + auto *hdr = reinterpret_cast(mb.getBufferStart()); + + if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) { + 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); + const char *strtab = reinterpret_cast(buf) + c->stroff; + bool subsectionsViaSymbols = hdr->flags & MH_SUBSECTIONS_VIA_SYMBOLS; + parseSymbols(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]); +} + +DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella) + : InputFile(DylibKind, mb) { + if (umbrella == nullptr) + umbrella = this; + + auto *buf = reinterpret_cast(mb.getBufferStart()); + auto *hdr = reinterpret_cast(mb.getBufferStart()); + + // Initialize dylibName. + 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"); + return; + } + + // Initialize symbols. + 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)); + }); + } else { + error("LC_DYLD_INFO_ONLY not found in " + getName()); + return; + } + + if (hdr->flags & MH_NO_REEXPORTED_DYLIBS) + return; + + const uint8_t *p = + reinterpret_cast(hdr) + sizeof(mach_header_64); + 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; + } + reexported.push_back(make(*buffer, umbrella)); + } +} + +DylibFile::DylibFile(std::shared_ptr interface, + DylibFile *umbrella) + : InputFile(DylibKind, MemoryBufferRef()) { + if (umbrella == nullptr) + umbrella = this; + + dylibName = saver.save(interface->getInstallName()); + // 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) + : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) { + for (const object::Archive::Symbol &sym : file->symbols()) + symtab->addLazy(sym.getName(), this, sym); +} + +void ArchiveFile::fetch(const object::Archive::Symbol &sym) { + object::Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member for symbol " + + sym.getName()); + + if (!seen.insert(c.getChildOffset()).second) + return; + + MemoryBufferRef mb = + 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()); +} + +// Returns "" or "baz.o". +std::string lld::toString(const InputFile *file) { + return file ? std::string(file->getName()) : ""; +} diff --git a/gnu/llvm/lld/MachO/InputFiles.h b/gnu/llvm/lld/MachO/InputFiles.h new file mode 100644 index 00000000000..bc5ad86ccaa --- /dev/null +++ b/gnu/llvm/lld/MachO/InputFiles.h @@ -0,0 +1,121 @@ +//===- InputFiles.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_MACHO_INPUT_FILES_H +#define LLD_MACHO_INPUT_FILES_H + +#include "MachOStructs.h" + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/Archive.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TextAPI/MachO/InterfaceFile.h" +#include "llvm/TextAPI/MachO/TextAPIReader.h" + +#include +#include + +namespace lld { +namespace macho { + +class InputSection; +class Symbol; +struct Reloc; + +// 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; + +class InputFile { +public: + enum Kind { + ObjKind, + DylibKind, + ArchiveKind, + }; + + virtual ~InputFile() = default; + Kind kind() const { return fileKind; } + StringRef getName() const { return mb.getBufferIdentifier(); } + + MemoryBufferRef mb; + std::vector symbols; + ArrayRef sectionHeaders; + std::vector subsections; + +protected: + InputFile(Kind kind, MemoryBufferRef mb) : mb(mb), fileKind(kind) {} + + void parseSections(ArrayRef); + + void parseSymbols(ArrayRef nList, const char *strtab, + bool subsectionsViaSymbols); + + void parseRelocations(const llvm::MachO::section_64 &, SubsectionMap &); + +private: + const Kind fileKind; +}; + +// .o file +class ObjFile : public InputFile { +public: + explicit ObjFile(MemoryBufferRef mb); + static bool classof(const InputFile *f) { return f->kind() == ObjKind; } +}; + +// .dylib file +class DylibFile : public InputFile { +public: + explicit DylibFile(std::shared_ptr interface, + DylibFile *umbrella = nullptr); + + // 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 + // re-exports. When loading a re-exported dylib, `umbrella` should be set to + // 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); + + 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 + bool reexport = false; + std::vector reexported; +}; + +// .a file +class ArchiveFile : public InputFile { +public: + explicit ArchiveFile(std::unique_ptr &&file); + static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } + void fetch(const llvm::object::Archive::Symbol &sym); + +private: + std::unique_ptr file; + // Keep track of children fetched from the archive by tracking + // which address offsets have been fetched already. + llvm::DenseSet seen; +}; + +extern std::vector inputFiles; + +llvm::Optional readFile(StringRef path); + +} // namespace macho + +std::string toString(const macho::InputFile *file); +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/InputSection.cpp b/gnu/llvm/lld/MachO/InputSection.cpp new file mode 100644 index 00000000000..72d48928305 --- /dev/null +++ b/gnu/llvm/lld/MachO/InputSection.cpp @@ -0,0 +1,48 @@ +//===- InputSection.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 "InputSection.h" +#include "OutputSegment.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/Memory.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::support; +using namespace lld; +using namespace lld::macho; + +std::vector macho::inputSections; + +uint64_t InputSection::getFileOffset() const { + return parent->fileOff + outSecFileOff; +} + +uint64_t InputSection::getVA() const { return parent->addr + outSecOff; } + +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); + } +} diff --git a/gnu/llvm/lld/MachO/InputSection.h b/gnu/llvm/lld/MachO/InputSection.h new file mode 100644 index 00000000000..96ae0cbe6ea --- /dev/null +++ b/gnu/llvm/lld/MachO/InputSection.h @@ -0,0 +1,74 @@ +//===- InputSection.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_INPUT_SECTION_H +#define LLD_MACHO_INPUT_SECTION_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/PointerUnion.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 InputSection { +public: + 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; + + OutputSection *parent = nullptr; + uint64_t outSecOff = 0; + uint64_t outSecFileOff = 0; + + uint32_t align = 1; + uint32_t flags = 0; + + ArrayRef data; + std::vector relocs; +}; + +extern std::vector inputSections; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/MachOStructs.h b/gnu/llvm/lld/MachO/MachOStructs.h new file mode 100644 index 00000000000..69b50ec2317 --- /dev/null +++ b/gnu/llvm/lld/MachO/MachOStructs.h @@ -0,0 +1,36 @@ +//===- MachOStructs.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 +// +//===----------------------------------------------------------------------===// +// +// This file defines structures used in the MachO object file format. Note that +// unlike llvm/BinaryFormat/MachO.h, the structs here are defined in terms of +// endian- and alignment-compatibility wrappers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_MACHO_MACHO_STRUCTS_H +#define LLD_MACHO_MACHO_STRUCTS_H + +#include "llvm/Support/Endian.h" + +namespace lld { + +namespace structs { + +struct nlist_64 { + llvm::support::ulittle32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + llvm::support::ulittle16_t n_desc; + llvm::support::ulittle64_t n_value; +}; + +} // namespace structs + +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/MergedOutputSection.cpp b/gnu/llvm/lld/MachO/MergedOutputSection.cpp new file mode 100644 index 00000000000..2d0be253834 --- /dev/null +++ b/gnu/llvm/lld/MachO/MergedOutputSection.cpp @@ -0,0 +1,74 @@ +//===- OutputSection.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 "MergedOutputSection.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/BinaryFormat/MachO.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace lld; +using namespace lld::macho; + +void MergedOutputSection::mergeInput(InputSection *input) { + if (inputs.empty()) { + align = input->align; + flags = input->flags; + } else { + mergeFlags(input->flags); + align = std::max(align, input->align); + } + + inputs.push_back(input); + input->parent = this; +} + +void MergedOutputSection::finalize() { + uint64_t isecAddr = addr; + uint64_t isecFileOff = fileOff; + for (InputSection *isec : inputs) { + isecAddr = alignTo(isecAddr, isec->align); + isecFileOff = alignTo(isecFileOff, isec->align); + isec->outSecOff = isecAddr - addr; + isec->outSecFileOff = isecFileOff - fileOff; + isecAddr += isec->getSize(); + isecFileOff += isec->getFileSize(); + } + size = isecAddr - addr; + fileSize = isecFileOff - fileOff; +} + +void MergedOutputSection::writeTo(uint8_t *buf) const { + for (InputSection *isec : inputs) { + isec->writeTo(buf + isec->outSecFileOff); + } +} + +// TODO: this is most likely wrong; reconsider how section flags +// are actually merged. The logic presented here was written without +// any form of informed research. +void MergedOutputSection::mergeFlags(uint32_t inputFlags) { + uint8_t sectionFlag = MachO::SECTION_TYPE & inputFlags; + if (sectionFlag != (MachO::SECTION_TYPE & flags)) + error("Cannot add merge section; inconsistent type flags " + + Twine(sectionFlag)); + + uint32_t inconsistentFlags = + MachO::S_ATTR_DEBUG | MachO::S_ATTR_STRIP_STATIC_SYMS | + MachO::S_ATTR_NO_DEAD_STRIP | MachO::S_ATTR_LIVE_SUPPORT; + if ((inputFlags ^ flags) & inconsistentFlags) + error("Cannot add merge section; cannot merge inconsistent flags"); + + // Negate pure instruction presence if any section isn't pure. + uint32_t pureMask = ~MachO::S_ATTR_PURE_INSTRUCTIONS | (inputFlags & flags); + + // Merge the rest + flags |= inputFlags; + flags &= pureMask; +} diff --git a/gnu/llvm/lld/MachO/MergedOutputSection.h b/gnu/llvm/lld/MachO/MergedOutputSection.h new file mode 100644 index 00000000000..279a7e0f75c --- /dev/null +++ b/gnu/llvm/lld/MachO/MergedOutputSection.h @@ -0,0 +1,56 @@ +//===- OutputSection.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/MapVector.h" + +namespace lld { +namespace macho { + +// 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 MergedOutputSection : public OutputSection { +public: + MergedOutputSection(StringRef name) : OutputSection(MergedKind, name) {} + + const InputSection *firstSection() const { return inputs.front(); } + const InputSection *lastSection() const { return inputs.back(); } + + // 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 mergeInput(InputSection *input); + void finalize() override; + + void writeTo(uint8_t *buf) const override; + + std::vector inputs; + + static bool classof(const OutputSection *sec) { + return sec->kind() == MergedKind; + } + +private: + void mergeFlags(uint32_t inputFlags); + + size_t size = 0; + uint64_t fileSize = 0; +}; + +} // namespace macho +} // namespace lld + +#endif diff --git a/gnu/llvm/lld/MachO/Options.td b/gnu/llvm/lld/MachO/Options.td new file mode 100644 index 00000000000..1e42542b9ac --- /dev/null +++ b/gnu/llvm/lld/MachO/Options.td @@ -0,0 +1,1297 @@ +include "llvm/Option/OptParser.td" + +def help : Flag<["-", "--"], "help">; +def help_hidden : Flag<["--"], "help-hidden">, + HelpText<"Display help for hidden options">; + +// 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 +// https://github.com/apple-opensource/ld64 at git tag "512.4" dated +// 2018-03-18. + +// Flags<[HelpHidden]> marks options that are not yet ported to lld, +// and serve as a scoreboard for annotating our progress toward +// implementing ld64 options in lld. As you add implementions to +// Driver.cpp, please remove the hidden flag here. + +def grp_kind : OptionGroup<"kind">, HelpText<"OUTPUT KIND">; + +def execute : Flag<["-"], "execute">, + HelpText<"Produce a main executable (default)">, + Flags<[HelpHidden]>, + Group; +def dylib : Flag<["-"], "dylib">, + HelpText<"Produce a shared library">, + Group; +def bundle : Flag<["-"], "bundle">, + HelpText<"Produce a bundle">, + Flags<[HelpHidden]>, + Group; +def r : Flag<["-"], "r">, + 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; +def dynamic : Flag<["-"], "dynamic">, + HelpText<"Link dynamically (default)">, + Flags<[HelpHidden]>, + Group; +def static : Flag<["-"], "static">, + HelpText<"Link statically">, + Flags<[HelpHidden]>, + Group; +def preload : Flag<["-"], "preload">, + 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; +def o : Separate<["-"], "o">, + 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; +def weak_l : Joined<["-"], "weak-l">, + MetaVarName<"">, + HelpText<"Like -l, but mark library and its references as weak imports">, + Flags<[HelpHidden]>, + Group; +def weak_library : Separate<["-"], "weak_library">, + MetaVarName<"">, + HelpText<"Like bare , but mark library and its references as weak imports">, + Flags<[HelpHidden]>, + Group; +def reexport_l : Joined<["-"], "reexport-l">, + MetaVarName<"">, + HelpText<"Like -l, but export all symbols of from newly created library">, + Flags<[HelpHidden]>, + Group; +def reexport_library : Separate<["-"], "reexport_library">, + MetaVarName<"">, + HelpText<"Like bare , but export all symbols of from newly created library">, + Flags<[HelpHidden]>, + Group; +def upward_l : Joined<["-"], "upward-l">, + 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; +def L : JoinedOrSeparate<["-"], "L">, + 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; +def syslibroot : Separate<["-"], "syslibroot">, + MetaVarName<"">, + HelpText<"Prepend to all library and framework search paths">, + Flags<[HelpHidden]>, + 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; +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; +def framework : Separate<["-"], "framework">, + MetaVarName<"">, + HelpText<"Search for .framework/ on the framework search path">, + Flags<[HelpHidden]>, + Group; +def weak_framework : Separate<["-"], "weak_framework">, + MetaVarName<"">, + HelpText<"Like -framework , but mark framework and its references as weak imports">, + Flags<[HelpHidden]>, + Group; +def reexport_framework : Separate<["-"], "reexport_framework">, + MetaVarName<"">, + HelpText<"Like -framework , but export all symbols of from the newly created library">, + Flags<[HelpHidden]>, + Group; +def upward_framework : Separate<["-"], "upward_framework">, + 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; +def all_load : Flag<["-"], "all_load">, + HelpText<"Load all members of all static archive libraries">, + Flags<[HelpHidden]>, + Group; +def ObjC : Flag<["-"], "ObjC">, + HelpText<"Load all members of static archives that are an Objective-C class or category.">, + Flags<[HelpHidden]>, + Group; +def force_load : Separate<["-"], "force_load">, + MetaVarName<"">, + HelpText<"Load all members static archive library at ">, + Flags<[HelpHidden]>, + 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; +def segcreate : MultiArg<["-"], "segcreate", 3>, + MetaVarName<"
">, + Alias, + HelpText<"Alias for -sectcreate">, + Flags<[HelpHidden]>, + Group; +def filelist : Separate<["-"], "filelist">, + MetaVarName<"">, + HelpText<"Read names of files to link from ">, + Flags<[HelpHidden]>, + Group; +def dtrace : Separate<["-"], "dtrace">, + MetaVarName<"