diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-12-30 11:57:38 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-12-30 11:57:38 +0000 |
commit | 5a5c549fe9a3fef595297bd21d36bed8409dc37d (patch) | |
tree | a964c8f5ac85b7b641cac022c5f9bf4eed3d2b9b /lib | |
parent | fb911942f1434f3d1750f83f25f5e42c80e60638 (diff) | |
download | src-5a5c549fe9a3fef595297bd21d36bed8409dc37d.tar.gz src-5a5c549fe9a3fef595297bd21d36bed8409dc37d.zip |
Vendor import of lld trunk r256633:vendor/lld/lld-trunk-r256633
Notes
Notes:
svn path=/vendor/lld/dist/; revision=292934
svn path=/vendor/lld/lld-trunk-r256633/; revision=292935; tag=vendor/lld/lld-trunk-r256633
Diffstat (limited to 'lib')
237 files changed, 14355 insertions, 19887 deletions
diff --git a/lib/Config/Makefile b/lib/Config/Makefile deleted file mode 100644 index b3c57f81418f..000000000000 --- a/lib/Config/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -##===- lib/Config/Makefile ---------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLD_LEVEL := ../.. -LIBRARYNAME := lldConfig - -include $(LLD_LEVEL)/Makefile diff --git a/lib/Core/DefinedAtom.cpp b/lib/Core/DefinedAtom.cpp index b3f81ca65a91..f1d308088ed4 100644 --- a/lib/Core/DefinedAtom.cpp +++ b/lib/Core/DefinedAtom.cpp @@ -76,6 +76,7 @@ DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) { case typeGnuLinkOnce: case typeUnknown: case typeTempLTO: + case typeSectCreate: return permUnknown; } llvm_unreachable("unknown content type"); diff --git a/lib/Core/Error.cpp b/lib/Core/Error.cpp index 24809c3869e5..3b7733746dcd 100644 --- a/lib/Core/Error.cpp +++ b/lib/Core/Error.cpp @@ -16,39 +16,6 @@ using namespace lld; -class _NativeReaderErrorCategory : public std::error_category { -public: - const char* name() const LLVM_NOEXCEPT override { - return "lld.native.reader"; - } - - std::string message(int ev) const override { - switch (static_cast<NativeReaderError>(ev)) { - case NativeReaderError::success: - return "Success"; - case NativeReaderError::unknown_file_format: - return "Unknown file format"; - case NativeReaderError::file_too_short: - return "file truncated"; - case NativeReaderError::file_malformed: - return "file malformed"; - case NativeReaderError::memory_error: - return "out of memory"; - case NativeReaderError::unknown_chunk_type: - return "unknown chunk type"; - case NativeReaderError::conflicting_target_machine: - return "conflicting target machine"; - } - llvm_unreachable("An enumerator of NativeReaderError does not have a " - "message defined."); - } -}; - -const std::error_category &lld::native_reader_category() { - static _NativeReaderErrorCategory o; - return o; -} - class _YamlReaderErrorCategory : public std::error_category { public: const char* name() const LLVM_NOEXCEPT override { @@ -57,8 +24,6 @@ public: std::string message(int ev) const override { switch (static_cast<YamlReaderError>(ev)) { - case YamlReaderError::success: - return "Success"; case YamlReaderError::unknown_keyword: return "Unknown keyword found in yaml file"; case YamlReaderError::illegal_value: @@ -91,6 +56,14 @@ public: case LinkerScriptReaderError::unrecognized_function_in_expr: return "Unrecognized function call when evaluating linker script " "expression"; + case LinkerScriptReaderError::unknown_phdr_ids: + return "Unknown header identifiers (missing in PHDRS command) are used"; + case LinkerScriptReaderError::extra_program_phdr: + return "Extra program header is found"; + case LinkerScriptReaderError::misplaced_program_phdr: + return "Program header must precede load segments"; + case LinkerScriptReaderError::program_phdr_wrong_phdrs: + return "Program header has invalid PHDRS attribute"; } llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a " "message defined."); @@ -102,7 +75,6 @@ const std::error_category &lld::LinkerScriptReaderCategory() { return o; } - namespace lld { /// Temporary class to enable make_dynamic_error_code() until @@ -110,7 +82,7 @@ namespace lld { /// other than error_code. class dynamic_error_category : public std::error_category { public: - ~dynamic_error_category() LLVM_NOEXCEPT {} + ~dynamic_error_category() override = default; const char *name() const LLVM_NOEXCEPT override { return "lld.dynamic_error"; @@ -140,6 +112,10 @@ private: static dynamic_error_category categorySingleton; +std::error_code make_dynamic_error_code(const char *msg) { + return make_dynamic_error_code(StringRef(msg)); +} + std::error_code make_dynamic_error_code(StringRef msg) { return std::error_code(categorySingleton.add(msg), categorySingleton); } @@ -148,4 +124,4 @@ std::error_code make_dynamic_error_code(const Twine &msg) { return std::error_code(categorySingleton.add(msg.str()), categorySingleton); } -} +} // namespace lld diff --git a/lib/Core/File.cpp b/lib/Core/File.cpp index dbac86b368aa..ac95f1016797 100644 --- a/lib/Core/File.cpp +++ b/lib/Core/File.cpp @@ -15,10 +15,10 @@ namespace lld { File::~File() {} -File::atom_collection_empty<DefinedAtom> File::_noDefinedAtoms; -File::atom_collection_empty<UndefinedAtom> File::_noUndefinedAtoms; -File::atom_collection_empty<SharedLibraryAtom> File::_noSharedLibraryAtoms; -File::atom_collection_empty<AbsoluteAtom> File::_noAbsoluteAtoms; +File::AtomVector<DefinedAtom> File::_noDefinedAtoms; +File::AtomVector<UndefinedAtom> File::_noUndefinedAtoms; +File::AtomVector<SharedLibraryAtom> File::_noSharedLibraryAtoms; +File::AtomVector<AbsoluteAtom> File::_noAbsoluteAtoms; std::error_code File::parse() { std::lock_guard<std::mutex> lock(_parseMutex); diff --git a/lib/Core/LinkingContext.cpp b/lib/Core/LinkingContext.cpp index c6656b935916..cbcf25c17df2 100644 --- a/lib/Core/LinkingContext.cpp +++ b/lib/Core/LinkingContext.cpp @@ -24,7 +24,7 @@ LinkingContext::LinkingContext() _warnIfCoalesableAtomsHaveDifferentCanBeNull(false), _warnIfCoalesableAtomsHaveDifferentLoadName(false), _printRemainingUndefines(true), _allowRemainingUndefines(false), - _logInputFiles(false), _allowShlibUndefines(false), + _logInputFiles(false), _allowShlibUndefines(true), _outputFileType(OutputFileType::Default), _nextOrdinal(0) {} LinkingContext::~LinkingContext() {} @@ -37,9 +37,9 @@ std::error_code LinkingContext::writeFile(const File &linkedFile) const { return this->writer().writeFile(linkedFile, _outputPath); } -bool LinkingContext::createImplicitFiles( - std::vector<std::unique_ptr<File> > &result) { - return this->writer().createImplicitFiles(result); +void LinkingContext::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + this->writer().createImplicitFiles(result); } std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const { diff --git a/lib/Core/Makefile b/lib/Core/Makefile deleted file mode 100644 index 042d01a1e1b3..000000000000 --- a/lib/Core/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -##===- lld/lib/Core/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLD_LEVEL := ../.. -LIBRARYNAME := lldCore - -include $(LLD_LEVEL)/Makefile diff --git a/lib/Core/Reader.cpp b/lib/Core/Reader.cpp index 6f8b8cbd1bf8..6069093d211e 100644 --- a/lib/Core/Reader.cpp +++ b/lib/Core/Reader.cpp @@ -13,7 +13,6 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" #include <memory> #include <system_error> @@ -29,22 +28,17 @@ void Registry::add(std::unique_ptr<YamlIOTaggedDocumentHandler> handler) { _yamlHandlers.push_back(std::move(handler)); } -std::error_code -Registry::loadFile(std::unique_ptr<MemoryBuffer> mb, - std::vector<std::unique_ptr<File>> &result) const { - // Get file type. +ErrorOr<std::unique_ptr<File>> +Registry::loadFile(std::unique_ptr<MemoryBuffer> mb) const { + // Get file magic. StringRef content(mb->getBufferStart(), mb->getBufferSize()); llvm::sys::fs::file_magic fileType = llvm::sys::fs::identify_magic(content); - // Get file extension. - StringRef extension = llvm::sys::path::extension(mb->getBufferIdentifier()); // Ask each registered reader if it can handle this file type or extension. for (const std::unique_ptr<Reader> &reader : _readers) { - if (!reader->canParse(fileType, extension, *mb)) + if (!reader->canParse(fileType, mb->getMemBufferRef())) continue; - if (std::error_code ec = reader->loadFile(std::move(mb), *this, result)) - return ec; - return std::error_code(); + return reader->loadFile(std::move(mb), *this); } // No Reader could parse this file. diff --git a/lib/Core/Resolver.cpp b/lib/Core/Resolver.cpp index 393a7ef2bfc8..8f89856c4a47 100644 --- a/lib/Core/Resolver.cpp +++ b/lib/Core/Resolver.cpp @@ -153,7 +153,6 @@ void Resolver::maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom) { llvm::errs() << "SymbolTable: error while merging " << atom.name() << "\n"; llvm::report_fatal_error("duplicate symbol error"); - return; } for (const Reference *r : atom) { @@ -180,6 +179,8 @@ void Resolver::doDefinedAtom(const DefinedAtom &atom) { << atom.ordinal() << ", name=" << atom.name() + << ", type=" + << atom.contentType() << "\n"); // add to list of known atoms @@ -295,11 +296,15 @@ void Resolver::updatePreloadArchiveMap() { // Keep adding atoms until _ctx.getNextFile() returns an error. This // function is where undefined atoms are resolved. bool Resolver::resolveUndefines() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Resolving undefines:\n"); ScopedTask task(getDefaultDomain(), "resolveUndefines"); int index = 0; std::set<File *> seen; for (;;) { bool undefAdded = false; + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "Loading file #" << index << "\n"); File *file = getFile(index); if (!file) return true; @@ -308,6 +313,8 @@ bool Resolver::resolveUndefines() { << ": " << ec.message() << "\n"; return false; } + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "Loaded file: " << file->path() << "\n"); file->beforeLink(); updatePreloadArchiveMap(); switch (file->kind()) { @@ -340,6 +347,8 @@ bool Resolver::resolveUndefines() { // switch all references to undefined or coalesced away atoms // to the new defined atom void Resolver::updateReferences() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Updating references:\n"); ScopedTask task(getDefaultDomain(), "updateReferences"); for (const Atom *atom : _atoms) { if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) { @@ -388,6 +397,8 @@ static bool isBackref(const Reference *ref) { // remove all atoms not actually used void Resolver::deadStripOptimize() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Dead stripping unused atoms:\n"); ScopedTask task(getDefaultDomain(), "deadStripOptimize"); // only do this optimization with -dead_strip if (!_ctx.deadStrip()) @@ -433,6 +444,9 @@ void Resolver::deadStripOptimize() { // error out if some undefines remain bool Resolver::checkUndefines() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Checking for undefines:\n"); + // build vector of remaining undefined symbols std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines(); if (_ctx.deadStrip()) { @@ -479,6 +493,8 @@ bool Resolver::checkUndefines() { // remove from _atoms all coaleseced away atoms void Resolver::removeCoalescedAwayAtoms() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Removing coalesced away atoms:\n"); ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms"); _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) { return _symbolTable.isCoalescedAway(a) || _deadAtoms.count(a); @@ -487,28 +503,53 @@ void Resolver::removeCoalescedAwayAtoms() { } bool Resolver::resolve() { + DEBUG_WITH_TYPE("resolver", + llvm::dbgs() << "******** Resolving atom references:\n"); updatePreloadArchiveMap(); if (!resolveUndefines()) return false; updateReferences(); deadStripOptimize(); - if (checkUndefines()) - if (!_ctx.allowRemainingUndefines()) + if (checkUndefines()) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... "); + if (!_ctx.allowRemainingUndefines()) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n"); return false; + } + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n"); + } removeCoalescedAwayAtoms(); _result->addAtoms(_atoms); + DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n"); return true; } void Resolver::MergedFile::addAtoms(std::vector<const Atom *> &all) { ScopedTask task(getDefaultDomain(), "addAtoms"); DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n"); + for (const Atom *atom : all) { - DEBUG_WITH_TYPE("resolver", llvm::dbgs() - << llvm::format(" 0x%09lX", atom) - << ", name=" - << atom->name() - << "\n"); +#ifndef NDEBUG + if (auto *definedAtom = dyn_cast<DefinedAtom>(atom)) { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << llvm::format(" 0x%09lX", atom) + << ", file=#" + << definedAtom->file().ordinal() + << ", atom=#" + << definedAtom->ordinal() + << ", name=" + << definedAtom->name() + << ", type=" + << definedAtom->contentType() + << "\n"); + } else { + DEBUG_WITH_TYPE("resolver", llvm::dbgs() + << llvm::format(" 0x%09lX", atom) + << ", name=" + << atom->name() + << "\n"); + } +#endif addAtom(*atom); } } diff --git a/lib/Core/SymbolTable.cpp b/lib/Core/SymbolTable.cpp index f3f2da9262e0..b85a83ffbfe6 100644 --- a/lib/Core/SymbolTable.cpp +++ b/lib/Core/SymbolTable.cpp @@ -28,7 +28,7 @@ #include <vector> namespace lld { -SymbolTable::SymbolTable(LinkingContext &context) : _context(context) {} +SymbolTable::SymbolTable(LinkingContext &context) : _ctx(context) {} bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); } @@ -185,7 +185,7 @@ bool SymbolTable::addByName(const Atom &newAtom) { // fallthrough } case MCR_Error: - if (!_context.getAllowDuplicates()) { + if (!_ctx.getAllowDuplicates()) { llvm::errs() << "Duplicate symbols: " << existing->name() << ":" @@ -207,8 +207,7 @@ bool SymbolTable::addByName(const Atom &newAtom) { const UndefinedAtom* newUndef = cast<UndefinedAtom>(&newAtom); bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull()); - if (!sameCanBeNull && - _context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) { + if (!sameCanBeNull && _ctx.warnIfCoalesableAtomsHaveDifferentCanBeNull()) { llvm::errs() << "lld warning: undefined symbol " << existingUndef->name() << " has different weakness in " @@ -244,14 +243,14 @@ bool SymbolTable::addByName(const Atom &newAtom) { (curShLib->canBeNullAtRuntime() == newShLib->canBeNullAtRuntime()); bool sameName = curShLib->loadName().equals(newShLib->loadName()); if (sameName && !sameNullness && - _context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) { + _ctx.warnIfCoalesableAtomsHaveDifferentCanBeNull()) { // FIXME: need diagonstics interface for writing warning messages llvm::errs() << "lld warning: shared library symbol " << curShLib->name() << " has different weakness in " << curShLib->file().path() << " and in " << newShLib->file().path(); } - if (!sameName && _context.warnIfCoalesableAtomsHaveDifferentLoadName()) { + if (!sameName && _ctx.warnIfCoalesableAtomsHaveDifferentLoadName()) { // FIXME: need diagonstics interface for writing warning messages llvm::errs() << "lld warning: shared library symbol " << curShLib->name() << " has different load path in " @@ -268,7 +267,7 @@ bool SymbolTable::addByName(const Atom &newAtom) { } // Give context a chance to change which is kept. - _context.notifySymbolTableCoalesce(existing, &newAtom, useNew); + _ctx.notifySymbolTableCoalesce(existing, &newAtom, useNew); if (useNew) { // Update name table to use new atom. diff --git a/lib/Core/TODO.txt b/lib/Core/TODO.txt deleted file mode 100644 index 196a3e02c2fc..000000000000 --- a/lib/Core/TODO.txt +++ /dev/null @@ -1,18 +0,0 @@ -lib/Core -~~~~~~~~ - -* Add endianness support to the native reader and writer. - -* The NativeReader has lots of similar code for converting arrays of ivar - data in mapped memory into arrays of objects. The commonality can be - factored out, maybe templatized. - -* The NativeFileFormat.h is old school C structs and constants. We scope - things better by defining constants used with a struct inside the struct - declaration. - -* The native reader and writer currently just blast in memory enumeration - values (e.g. DefinedAtom::Scope) into a byte in the disk format. To support - future changes to the enumerations, there should be a translation layer - to map disk values to in-memory values. - diff --git a/lib/Core/Writer.cpp b/lib/Core/Writer.cpp index 39bcc9e68523..93e6438a28f5 100644 --- a/lib/Core/Writer.cpp +++ b/lib/Core/Writer.cpp @@ -16,8 +16,4 @@ Writer::Writer() { Writer::~Writer() { } - -bool Writer::createImplicitFiles(std::vector<std::unique_ptr<File> > &) { - return true; -} } // end namespace lld diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 5a410e7eed7e..64498ccf78ba 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -6,8 +6,6 @@ set(LLVM_TARGET_DEFINITIONS CoreOptions.td) tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs) set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td) tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs) -set(LLVM_TARGET_DEFINITIONS WinLinkOptions.td) -tablegen(LLVM WinLinkOptions.inc -gen-opt-parser-defs) add_public_tablegen_target(DriverOptionsTableGen) add_llvm_library(lldDriver @@ -16,14 +14,14 @@ add_llvm_library(lldDriver Driver.cpp GnuLdDriver.cpp UniversalDriver.cpp - WinLinkDriver.cpp - WinLinkModuleDef.cpp LINK_LIBS lldConfig lldMachO - lldPECOFF + lldCOFF lldELF + lldELF2 lldAArch64ELFTarget + lldAMDGPUELFTarget lldARMELFTarget lldHexagonELFTarget lldMipsELFTarget @@ -31,7 +29,6 @@ add_llvm_library(lldDriver lldExampleSubTarget lldX86_64ELFTarget lldCore - lldNative lldReaderWriter lldYAML LLVMObject diff --git a/lib/Driver/CoreDriver.cpp b/lib/Driver/CoreDriver.cpp index b8adee55746f..ce8648595109 100644 --- a/lib/Driver/CoreDriver.cpp +++ b/lib/Driver/CoreDriver.cpp @@ -56,7 +56,7 @@ static const llvm::opt::OptTable::Info infoTable[] = { // Create OptTable class for parsing actual command line arguments class CoreOptTable : public llvm::opt::OptTable { public: - CoreOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} + CoreOptTable() : OptTable(infoTable) {} }; } // namespace anonymous @@ -73,32 +73,31 @@ static const Registry::KindStrings coreKindStrings[] = { LLD_KIND_STRING_END }; -bool CoreDriver::link(int argc, const char *argv[], raw_ostream &diagnostics) { +bool CoreDriver::link(llvm::ArrayRef<const char *> args, + raw_ostream &diagnostics) { CoreLinkingContext ctx; // Register possible input file parsers. - ctx.registry().addSupportNativeObjects(); ctx.registry().addSupportYamlFiles(); ctx.registry().addKindTable(Reference::KindNamespace::testing, Reference::KindArch::all, coreKindStrings); - if (!parse(argc, argv, ctx)) + if (!parse(args, ctx)) return false; return Driver::link(ctx); } -bool CoreDriver::parse(int argc, const char *argv[], CoreLinkingContext &ctx, - raw_ostream &diagnostics) { +bool CoreDriver::parse(llvm::ArrayRef<const char *> args, + CoreLinkingContext &ctx, raw_ostream &diagnostics) { // Parse command line options using CoreOptions.td - std::unique_ptr<llvm::opt::InputArgList> parsedArgs; CoreOptTable table; unsigned missingIndex; unsigned missingCount; - parsedArgs.reset( - table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + llvm::opt::InputArgList parsedArgs = + table.ParseArgs(args.slice(1), missingIndex, missingCount); if (missingCount) { diagnostics << "error: missing arg value for '" - << parsedArgs->getArgString(missingIndex) << "' expected " + << parsedArgs.getArgString(missingIndex) << "' expected " << missingCount << " argument(s).\n"; return false; } @@ -112,7 +111,7 @@ bool CoreDriver::parse(int argc, const char *argv[], CoreLinkingContext &ctx, ctx.setSearchArchivesToOverrideTentativeDefinitions(false); // Process all the arguments and create input files. - for (auto inputArg : *parsedArgs) { + for (auto inputArg : parsedArgs) { switch (inputArg->getOption().getID()) { case OPT_mllvm: ctx.appendLLVMOption(inputArg->getValue()); @@ -160,6 +159,8 @@ bool CoreDriver::parse(int argc, const char *argv[], CoreLinkingContext &ctx, } } + parseLLVMOptions(ctx); + if (ctx.getNodes().empty()) { diagnostics << "No input files\n"; return false; diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp index 2c64aeee38a5..40fad74c9529 100644 --- a/lib/Driver/DarwinLdDriver.cpp +++ b/lib/Driver/DarwinLdDriver.cpp @@ -20,6 +20,7 @@ #include "lld/ReaderWriter/MachOLinkingContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Option/Arg.h" #include "llvm/Option/Option.h" @@ -68,7 +69,7 @@ static const llvm::opt::OptTable::Info infoTable[] = { // Create OptTable class for parsing actual command line arguments class DarwinLdOptTable : public llvm::opt::OptTable { public: - DarwinLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} + DarwinLdOptTable() : OptTable(infoTable) {} }; std::vector<std::unique_ptr<File>> @@ -80,20 +81,23 @@ loadFile(MachOLinkingContext &ctx, StringRef path, ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path); if (std::error_code ec = mbOrErr.getError()) return makeErrorFile(path, ec); - std::vector<std::unique_ptr<File>> files; - if (std::error_code ec = ctx.registry().loadFile(std::move(mbOrErr.get()), files)) + ErrorOr<std::unique_ptr<File>> fileOrErr = + ctx.registry().loadFile(std::move(mbOrErr.get())); + if (std::error_code ec = fileOrErr.getError()) return makeErrorFile(path, ec); - for (std::unique_ptr<File> &pf : files) { - // If file is a dylib, inform LinkingContext about it. - if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(pf.get())) { - if (std::error_code ec = shl->parse()) - return makeErrorFile(path, ec); - ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile*>(shl), - upwardDylib); - } + std::unique_ptr<File> &file = fileOrErr.get(); + + // If file is a dylib, inform LinkingContext about it. + if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(file.get())) { + if (std::error_code ec = shl->parse()) + return makeErrorFile(path, ec); + ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile *>(shl), + upwardDylib); } if (wholeArchive) - return parseMemberFiles(files); + return parseMemberFiles(std::move(file)); + std::vector<std::unique_ptr<File>> files; + files.push_back(std::move(file)); return files; } @@ -263,42 +267,40 @@ static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) { namespace lld { -bool DarwinLdDriver::linkMachO(int argc, const char *argv[], +bool DarwinLdDriver::linkMachO(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) { MachOLinkingContext ctx; - if (!parse(argc, argv, ctx, diagnostics)) + if (!parse(args, ctx, diagnostics)) return false; if (ctx.doNothing()) return true; return link(ctx, diagnostics); } -bool DarwinLdDriver::parse(int argc, const char *argv[], +bool DarwinLdDriver::parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, raw_ostream &diagnostics) { // Parse command line options using DarwinLdOptions.td - std::unique_ptr<llvm::opt::InputArgList> parsedArgs; DarwinLdOptTable table; unsigned missingIndex; unsigned missingCount; - bool globalWholeArchive = false; - parsedArgs.reset( - table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + llvm::opt::InputArgList parsedArgs = + table.ParseArgs(args.slice(1), missingIndex, missingCount); if (missingCount) { diagnostics << "error: missing arg value for '" - << parsedArgs->getArgString(missingIndex) << "' expected " + << parsedArgs.getArgString(missingIndex) << "' expected " << missingCount << " argument(s).\n"; return false; } - for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { - diagnostics << "warning: ignoring unknown argument: " - << unknownArg->getAsString(*parsedArgs) << "\n"; + for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) { + diagnostics << "warning: ignoring unknown argument: " + << unknownArg->getAsString(parsedArgs) << "\n"; } // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE; - if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_dylib, OPT_relocatable, - OPT_bundle, OPT_static, OPT_preload)) { + if (llvm::opt::Arg *kind = parsedArgs.getLastArg( + OPT_dylib, OPT_relocatable, OPT_bundle, OPT_static, OPT_preload)) { switch (kind->getOption().getID()) { case OPT_dylib: fileType = llvm::MachO::MH_DYLIB; @@ -320,7 +322,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], // Handle -arch xxx MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown; - if (llvm::opt::Arg *archStr = parsedArgs->getLastArg(OPT_arch)) { + if (llvm::opt::Arg *archStr = parsedArgs.getLastArg(OPT_arch)) { arch = MachOLinkingContext::archFromName(archStr->getValue()); if (arch == MachOLinkingContext::arch_unknown) { diagnostics << "error: unknown arch named '" << archStr->getValue() @@ -330,17 +332,17 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // If no -arch specified, scan input files to find first non-fat .o file. if (arch == MachOLinkingContext::arch_unknown) { - for (auto &inFile: parsedArgs->filtered(OPT_INPUT)) { + for (auto &inFile : parsedArgs.filtered(OPT_INPUT)) { // This is expensive because it opens and maps the file. But that is // ok because no -arch is rare. if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch)) break; } - if (arch == MachOLinkingContext::arch_unknown - && !parsedArgs->getLastArg(OPT_test_file_usage)) { + if (arch == MachOLinkingContext::arch_unknown && + !parsedArgs.getLastArg(OPT_test_file_usage)) { // If no -arch and no options at all, print usage message. - if (parsedArgs->size() == 0) - table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + if (parsedArgs.size() == 0) + table.PrintHelp(llvm::outs(), args[0], "LLVM Linker", false); else diagnostics << "error: -arch not specified and could not be inferred\n"; return false; @@ -351,8 +353,8 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], MachOLinkingContext::OS os = MachOLinkingContext::OS::macOSX; uint32_t minOSVersion = 0; if (llvm::opt::Arg *minOS = - parsedArgs->getLastArg(OPT_macosx_version_min, OPT_ios_version_min, - OPT_ios_simulator_version_min)) { + parsedArgs.getLastArg(OPT_macosx_version_min, OPT_ios_version_min, + OPT_ios_simulator_version_min)) { switch (minOS->getOption().getID()) { case OPT_macosx_version_min: os = MachOLinkingContext::OS::macOSX; @@ -388,17 +390,17 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], ctx.configure(fileType, arch, os, minOSVersion); // Handle -e xxx - if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry)) + if (llvm::opt::Arg *entry = parsedArgs.getLastArg(OPT_entry)) ctx.setEntrySymbolName(entry->getValue()); // Handle -o xxx - if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_output)) + if (llvm::opt::Arg *outpath = parsedArgs.getLastArg(OPT_output)) ctx.setOutputPath(outpath->getValue()); else ctx.setOutputPath("a.out"); // Handle -image_base XXX and -seg1addr XXXX - if (llvm::opt::Arg *imageBase = parsedArgs->getLastArg(OPT_image_base)) { + if (llvm::opt::Arg *imageBase = parsedArgs.getLastArg(OPT_image_base)) { uint64_t baseAddress; if (parseNumberBase16(imageBase->getValue(), baseAddress)) { diagnostics << "error: image_base expects a hex number\n"; @@ -408,7 +410,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], return false; } else if (baseAddress % ctx.pageSize()) { diagnostics << "error: image_base must be a multiple of page size (" - << llvm::format("0x%" PRIx64, ctx.pageSize()) << ")\n"; + << "0x" << llvm::utohexstr(ctx.pageSize()) << ")\n"; return false; } @@ -416,26 +418,26 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -dead_strip - if (parsedArgs->getLastArg(OPT_dead_strip)) + if (parsedArgs.getLastArg(OPT_dead_strip)) ctx.setDeadStripping(true); + bool globalWholeArchive = false; // Handle -all_load - if (parsedArgs->getLastArg(OPT_all_load)) + if (parsedArgs.getLastArg(OPT_all_load)) globalWholeArchive = true; // Handle -install_name - if (llvm::opt::Arg *installName = parsedArgs->getLastArg(OPT_install_name)) + if (llvm::opt::Arg *installName = parsedArgs.getLastArg(OPT_install_name)) ctx.setInstallName(installName->getValue()); else ctx.setInstallName(ctx.outputPath()); // Handle -mark_dead_strippable_dylib - if (parsedArgs->getLastArg(OPT_mark_dead_strippable_dylib)) + if (parsedArgs.getLastArg(OPT_mark_dead_strippable_dylib)) ctx.setDeadStrippableDylib(true); // Handle -compatibility_version and -current_version - if (llvm::opt::Arg *vers = - parsedArgs->getLastArg(OPT_compatibility_version)) { + if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_compatibility_version)) { if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { diagnostics << "error: -compatibility_version can only be used with -dylib\n"; @@ -449,7 +451,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], ctx.setCompatibilityVersion(parsedVers); } - if (llvm::opt::Arg *vers = parsedArgs->getLastArg(OPT_current_version)) { + if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_current_version)) { if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { diagnostics << "-current_version can only be used with -dylib\n"; return false; @@ -463,11 +465,11 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -bundle_loader - if (llvm::opt::Arg *loader = parsedArgs->getLastArg(OPT_bundle_loader)) + if (llvm::opt::Arg *loader = parsedArgs.getLastArg(OPT_bundle_loader)) ctx.setBundleLoader(loader->getValue()); // Handle -sectalign segname sectname align - for (auto &alignArg : parsedArgs->filtered(OPT_sectalign)) { + for (auto &alignArg : parsedArgs.filtered(OPT_sectalign)) { const char* segName = alignArg->getValue(0); const char* sectName = alignArg->getValue(1); const char* alignStr = alignArg->getValue(2); @@ -479,43 +481,43 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], << alignStr << "' not a valid number\n"; return false; } - uint8_t align2 = llvm::countTrailingZeros(alignValue); - if ( (unsigned long)(1 << align2) != alignValue ) { + uint16_t align = 1 << llvm::countTrailingZeros(alignValue); + if (!llvm::isPowerOf2_64(alignValue)) { diagnostics << "warning: alignment for '-sectalign " << segName << " " << sectName << llvm::format(" 0x%llX", alignValue) << "' is not a power of two, using " - << llvm::format("0x%08X", (1 << align2)) << "\n"; + << llvm::format("0x%08X", align) << "\n"; } - ctx.addSectionAlignment(segName, sectName, align2); + ctx.addSectionAlignment(segName, sectName, align); } // Handle -mllvm - for (auto &llvmArg : parsedArgs->filtered(OPT_mllvm)) { + for (auto &llvmArg : parsedArgs.filtered(OPT_mllvm)) { ctx.appendLLVMOption(llvmArg->getValue()); } // Handle -print_atoms - if (parsedArgs->getLastArg(OPT_print_atoms)) + if (parsedArgs.getLastArg(OPT_print_atoms)) ctx.setPrintAtoms(); // Handle -t (trace) option. - if (parsedArgs->getLastArg(OPT_t)) + if (parsedArgs.getLastArg(OPT_t)) ctx.setLogInputFiles(true); // Handle -demangle option. - if (parsedArgs->getLastArg(OPT_demangle)) + if (parsedArgs.getLastArg(OPT_demangle)) ctx.setDemangleSymbols(true); // Handle -keep_private_externs - if (parsedArgs->getLastArg(OPT_keep_private_externs)) { + if (parsedArgs.getLastArg(OPT_keep_private_externs)) { ctx.setKeepPrivateExterns(true); if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT) diagnostics << "warning: -keep_private_externs only used in -r mode\n"; } // Handle -dependency_info <path> used by Xcode. - if (llvm::opt::Arg *depInfo = parsedArgs->getLastArg(OPT_dependency_info)) { + if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info)) { if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) { diagnostics << "warning: " << ec.message() << ", processing '-dependency_info " @@ -528,14 +530,14 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], // exist. We'll also be expected to print out information about how we located // libraries and so on that the user specified, but not to actually do any // linking. - if (parsedArgs->getLastArg(OPT_test_file_usage)) { + if (parsedArgs.getLastArg(OPT_test_file_usage)) { ctx.setTestingFileUsage(); // With paths existing by fiat, linking is not going to end well. ctx.setDoNothing(true); // Only bother looking for an existence override if we're going to use it. - for (auto existingPath : parsedArgs->filtered(OPT_path_exists)) { + for (auto existingPath : parsedArgs.filtered(OPT_path_exists)) { ctx.addExistingPathForDebug(existingPath->getValue()); } } @@ -544,7 +546,6 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], if (!ctx.doNothing()) { ctx.registry().addSupportMachOObjects(ctx); ctx.registry().addSupportArchives(ctx.logInputFiles()); - ctx.registry().addSupportNativeObjects(); ctx.registry().addSupportYamlFiles(); } @@ -559,7 +560,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], // 3. If the last -syslibroot is "/", all of them are ignored entirely. // 4. If { syslibroots } x path == {}, the original path is kept. std::vector<StringRef> sysLibRoots; - for (auto syslibRoot : parsedArgs->filtered(OPT_syslibroot)) { + for (auto syslibRoot : parsedArgs.filtered(OPT_syslibroot)) { sysLibRoots.push_back(syslibRoot->getValue()); } if (!sysLibRoots.empty()) { @@ -570,17 +571,17 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], // Paths specified with -L come first, and are not considered system paths for // the case where there is precisely 1 -syslibroot. - for (auto libPath : parsedArgs->filtered(OPT_L)) { + for (auto libPath : parsedArgs.filtered(OPT_L)) { ctx.addModifiedSearchDir(libPath->getValue()); } // Process -F directories (where to look for frameworks). - for (auto fwPath : parsedArgs->filtered(OPT_F)) { + for (auto fwPath : parsedArgs.filtered(OPT_F)) { ctx.addFrameworkSearchDir(fwPath->getValue()); } // -Z suppresses the standard search paths. - if (!parsedArgs->hasArg(OPT_Z)) { + if (!parsedArgs.hasArg(OPT_Z)) { ctx.addModifiedSearchDir("/usr/lib", true); ctx.addModifiedSearchDir("/usr/local/lib", true); ctx.addFrameworkSearchDir("/Library/Frameworks", true); @@ -589,7 +590,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], // Now that we've constructed the final set of search paths, print out those // search paths in verbose mode. - if (parsedArgs->getLastArg(OPT_v)) { + if (parsedArgs.getLastArg(OPT_v)) { diagnostics << "Library search paths:\n"; for (auto path : ctx.searchDirs()) { diagnostics << " " << path << '\n'; @@ -601,7 +602,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -exported_symbols_list <file> - for (auto expFile : parsedArgs->filtered(OPT_exported_symbols_list)) { + for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { diagnostics << "error: -exported_symbols_list cannot be combined " << "with -unexported_symbol[s_list]\n"; @@ -619,7 +620,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -exported_symbol <symbol> - for (auto symbol : parsedArgs->filtered(OPT_exported_symbol)) { + for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { diagnostics << "error: -exported_symbol cannot be combined " << "with -unexported_symbol[s_list]\n"; @@ -630,7 +631,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -unexported_symbols_list <file> - for (auto expFile : parsedArgs->filtered(OPT_unexported_symbols_list)) { + for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { diagnostics << "error: -unexported_symbols_list cannot be combined " << "with -exported_symbol[s_list]\n"; @@ -648,7 +649,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -unexported_symbol <symbol> - for (auto symbol : parsedArgs->filtered(OPT_unexported_symbol)) { + for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { diagnostics << "error: -unexported_symbol cannot be combined " << "with -exported_symbol[s_list]\n"; @@ -659,8 +660,8 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle obosolete -multi_module and -single_module - if (llvm::opt::Arg *mod = parsedArgs->getLastArg(OPT_multi_module, - OPT_single_module)) { + if (llvm::opt::Arg *mod = + parsedArgs.getLastArg(OPT_multi_module, OPT_single_module)) { if (mod->getOption().getID() == OPT_multi_module) { diagnostics << "warning: -multi_module is obsolete and being ignored\n"; } @@ -673,7 +674,7 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } // Handle -pie or -no_pie - if (llvm::opt::Arg *pie = parsedArgs->getLastArg(OPT_pie, OPT_no_pie)) { + if (llvm::opt::Arg *pie = parsedArgs.getLastArg(OPT_pie, OPT_no_pie)) { switch (ctx.outputMachOType()) { case llvm::MachO::MH_EXECUTE: switch (ctx.os()) { @@ -718,12 +719,28 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } } + // Handle stack_size + if (llvm::opt::Arg *stackSize = parsedArgs.getLastArg(OPT_stack_size)) { + uint64_t stackSizeVal; + if (parseNumberBase16(stackSize->getValue(), stackSizeVal)) { + diagnostics << "error: stack_size expects a hex number\n"; + return false; + } + if ((stackSizeVal % ctx.pageSize()) != 0) { + diagnostics << "error: stack_size must be a multiple of page size (" + << "0x" << llvm::utohexstr(ctx.pageSize()) << ")\n"; + return false; + } + + ctx.setStackSize(stackSizeVal); + } + // Handle debug info handling options: -S - if (parsedArgs->hasArg(OPT_S)) + if (parsedArgs.hasArg(OPT_S)) ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap); // Handle -order_file <file> - for (auto orderFile : parsedArgs->filtered(OPT_order_file)) { + for (auto orderFile : parsedArgs.filtered(OPT_order_file)) { if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx, diagnostics)) { diagnostics << "error: " << ec.message() @@ -734,8 +751,51 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], } } + // Handle -flat_namespace. + if (llvm::opt::Arg *ns = + parsedArgs.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) { + if (ns->getOption().getID() == OPT_flat_namespace) + ctx.setUseFlatNamespace(true); + } + + // Handle -undefined + if (llvm::opt::Arg *undef = parsedArgs.getLastArg(OPT_undefined)) { + MachOLinkingContext::UndefinedMode UndefMode; + if (StringRef(undef->getValue()).equals("error")) + UndefMode = MachOLinkingContext::UndefinedMode::error; + else if (StringRef(undef->getValue()).equals("warning")) + UndefMode = MachOLinkingContext::UndefinedMode::warning; + else if (StringRef(undef->getValue()).equals("suppress")) + UndefMode = MachOLinkingContext::UndefinedMode::suppress; + else if (StringRef(undef->getValue()).equals("dynamic_lookup")) + UndefMode = MachOLinkingContext::UndefinedMode::dynamicLookup; + else { + diagnostics << "error: invalid option to -undefined " + "[ warning | error | suppress | dynamic_lookup ]\n"; + return false; + } + + if (ctx.useFlatNamespace()) { + // If we're using -flat_namespace then 'warning', 'suppress' and + // 'dynamic_lookup' are all equivalent, so map them to 'suppress'. + if (UndefMode != MachOLinkingContext::UndefinedMode::error) + UndefMode = MachOLinkingContext::UndefinedMode::suppress; + } else { + // If we're using -twolevel_namespace then 'warning' and 'suppress' are + // illegal. Emit a diagnostic if they've been (mis)used. + if (UndefMode == MachOLinkingContext::UndefinedMode::warning || + UndefMode == MachOLinkingContext::UndefinedMode::suppress) { + diagnostics << "error: can't use -undefined warning or suppress with " + "-twolevel_namespace\n"; + return false; + } + } + + ctx.setUndefinedMode(UndefMode); + } + // Handle -rpath <path> - if (parsedArgs->hasArg(OPT_rpath)) { + if (parsedArgs.hasArg(OPT_rpath)) { switch (ctx.outputMachOType()) { case llvm::MachO::MH_EXECUTE: case llvm::MachO::MH_DYLIB: @@ -757,13 +817,17 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], return false; } - for (auto rPath : parsedArgs->filtered(OPT_rpath)) { + for (auto rPath : parsedArgs.filtered(OPT_rpath)) { ctx.addRpath(rPath->getValue()); } } - // Handle input files - for (auto &arg : *parsedArgs) { + // Parse the LLVM options before we process files in case the file handling + // makes use of things like DEBUG(). + parseLLVMOptions(ctx); + + // Handle input files and sectcreate. + for (auto &arg : parsedArgs) { bool upward; ErrorOr<StringRef> resolvedPath = StringRef(); switch (arg->getOption().getID()) { @@ -816,6 +880,22 @@ bool DarwinLdDriver::parse(int argc, const char *argv[], return false; } break; + case OPT_sectcreate: { + const char* seg = arg->getValue(0); + const char* sect = arg->getValue(1); + const char* fileName = arg->getValue(2); + + ErrorOr<std::unique_ptr<MemoryBuffer>> contentOrErr = + MemoryBuffer::getFile(fileName); + + if (!contentOrErr) { + diagnostics << "error: can't open -sectcreate file " << fileName << "\n"; + return false; + } + + ctx.addSectCreateSection(seg, sect, std::move(*contentOrErr)); + } + break; } } diff --git a/lib/Driver/DarwinLdOptions.td b/lib/Driver/DarwinLdOptions.td index 81dcc0a1d925..cbf6ac1d4a4b 100644 --- a/lib/Driver/DarwinLdOptions.td +++ b/lib/Driver/DarwinLdOptions.td @@ -55,6 +55,19 @@ def order_file : Separate<["-"], "order_file">, MetaVarName<"<file-path>">, HelpText<"re-order and move specified symbols to start of their section">, Group<grp_opts>; +def flat_namespace : Flag<["-"], "flat_namespace">, + HelpText<"Resolves symbols in any (transitively) linked dynamic libraries. " + "Source libraries are not recorded: dyld will re-search all " + "images at runtime and use the first definition found.">, + Group<grp_opts>; +def twolevel_namespace : Flag<["-"], "twolevel_namespace">, + HelpText<"Resolves symbols in listed libraries only. Source libraries are " + "recorded in the symbol table.">, + Group<grp_opts>; +def undefined : Separate<["-"], "undefined">, + MetaVarName<"<undefined>">, + HelpText<"Determines how undefined symbols are handled.">, + Group<grp_opts>; // main executable options def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">; @@ -67,6 +80,10 @@ def pie : Flag<["-"], "pie">, def no_pie : Flag<["-"], "no_pie">, HelpText<"Do not create Position Independent Executable">, Group<grp_main>; +def stack_size : Separate<["-"], "stack_size">, + HelpText<"Specifies the maximum stack size for the main thread in a program. " + "Must be a page-size multiple. (default=8Mb)">, + Group<grp_main>; // dylib executable options def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">; @@ -160,7 +177,10 @@ def arch : Separate<["-"], "arch">, HelpText<"Architecture to link">; def sectalign : MultiArg<["-"], "sectalign", 3>, MetaVarName<"<segname> <sectname> <alignment>">, - HelpText<"alignment for segment/section">; + HelpText<"Alignment for segment/section">; +def sectcreate : MultiArg<["-"], "sectcreate", 3>, + MetaVarName<"<segname> <sectname> <file>">, + HelpText<"Create section <segname>/<sectname> from contents of <file>">; def image_base : Separate<["-"], "image_base">; def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>; def demangle : Flag<["-"], "demangle">, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index d32bfa6e47be..6a7a26b3b0f6 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1,4 +1,4 @@ -//===- lib/Driver/Driver.cpp - Linker Driver Emulator ---------------------===// +//===- lib/Driver/Driver.cpp - Linker Driver Emulator -----------*- C++ -*-===// // // The LLVM Linker // @@ -36,15 +36,13 @@ FileVector makeErrorFile(StringRef path, std::error_code ec) { return result; } -FileVector parseMemberFiles(FileVector &files) { +FileVector parseMemberFiles(std::unique_ptr<File> file) { std::vector<std::unique_ptr<File>> members; - for (std::unique_ptr<File> &file : files) { - if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) { - if (std::error_code ec = archive->parseAllMembers(members)) - return makeErrorFile(file->path(), ec); - } else { - members.push_back(std::move(file)); - } + if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) { + if (std::error_code ec = archive->parseAllMembers(members)) + return makeErrorFile(file->path(), ec); + } else { + members.push_back(std::move(file)); } return members; } @@ -54,72 +52,86 @@ FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive) { = MemoryBuffer::getFileOrSTDIN(path); if (std::error_code ec = mb.getError()) return makeErrorFile(path, ec); - std::vector<std::unique_ptr<File>> files; - if (std::error_code ec = ctx.registry().loadFile(std::move(mb.get()), files)) + ErrorOr<std::unique_ptr<File>> fileOrErr = + ctx.registry().loadFile(std::move(mb.get())); + if (std::error_code ec = fileOrErr.getError()) return makeErrorFile(path, ec); + std::unique_ptr<File> &file = fileOrErr.get(); if (wholeArchive) - return parseMemberFiles(files); + return parseMemberFiles(std::move(file)); + std::vector<std::unique_ptr<File>> files; + files.push_back(std::move(file)); return files; } -/// This is where the link is actually performed. -bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) { +void Driver::parseLLVMOptions(const LinkingContext &ctx) { // Honor -mllvm - if (!context.llvmOptions().empty()) { - unsigned numArgs = context.llvmOptions().size(); - const char **args = new const char *[numArgs + 2]; + if (!ctx.llvmOptions().empty()) { + unsigned numArgs = ctx.llvmOptions().size(); + auto **args = new const char *[numArgs + 2]; args[0] = "lld (LLVM option parsing)"; for (unsigned i = 0; i != numArgs; ++i) - args[i + 1] = context.llvmOptions()[i]; - args[numArgs + 1] = 0; + args[i + 1] = ctx.llvmOptions()[i]; + args[numArgs + 1] = nullptr; llvm::cl::ParseCommandLineOptions(numArgs + 1, args); } - if (context.getNodes().empty()) +} + +/// This is where the link is actually performed. +bool Driver::link(LinkingContext &ctx, raw_ostream &diagnostics) { + if (ctx.getNodes().empty()) return false; - for (std::unique_ptr<Node> &ie : context.getNodes()) + for (std::unique_ptr<Node> &ie : ctx.getNodes()) if (FileNode *node = dyn_cast<FileNode>(ie.get())) - context.getTaskGroup().spawn([node] { node->getFile()->parse(); }); + ctx.getTaskGroup().spawn([node] { node->getFile()->parse(); }); std::vector<std::unique_ptr<File>> internalFiles; - context.createInternalFiles(internalFiles); + ctx.createInternalFiles(internalFiles); for (auto i = internalFiles.rbegin(), e = internalFiles.rend(); i != e; ++i) { - auto &members = context.getNodes(); + auto &members = ctx.getNodes(); members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i))); } // Give target a chance to add files. std::vector<std::unique_ptr<File>> implicitFiles; - context.createImplicitFiles(implicitFiles); + ctx.createImplicitFiles(implicitFiles); for (auto i = implicitFiles.rbegin(), e = implicitFiles.rend(); i != e; ++i) { - auto &members = context.getNodes(); + auto &members = ctx.getNodes(); members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i))); } // Give target a chance to postprocess input files. // Mach-O uses this chance to move all object files before library files. // ELF adds specific undefined symbols resolver. - context.finalizeInputFiles(); + ctx.finalizeInputFiles(); // Do core linking. ScopedTask resolveTask(getDefaultDomain(), "Resolve"); - Resolver resolver(context); - if (!resolver.resolve()) + Resolver resolver(ctx); + if (!resolver.resolve()) { + ctx.getTaskGroup().sync(); return false; - std::unique_ptr<MutableFile> merged = resolver.resultFile(); + } + std::unique_ptr<SimpleFile> merged = resolver.resultFile(); resolveTask.end(); // Run passes on linked atoms. ScopedTask passTask(getDefaultDomain(), "Passes"); PassManager pm; - context.addPasses(pm); - pm.runOnFile(merged); + ctx.addPasses(pm); + if (std::error_code ec = pm.runOnFile(*merged)) { + diagnostics << "Failed to write file '" << ctx.outputPath() + << "': " << ec.message() << "\n"; + return false; + } + passTask.end(); // Give linked atoms to Writer to generate output file. ScopedTask writeTask(getDefaultDomain(), "Write"); - if (std::error_code ec = context.writeFile(*merged)) { - diagnostics << "Failed to write file '" << context.outputPath() + if (std::error_code ec = ctx.writeFile(*merged)) { + diagnostics << "Failed to write file '" << ctx.outputPath() << "': " << ec.message() << "\n"; return false; } @@ -127,4 +139,4 @@ bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) { return true; } -} // namespace +} // namespace lld diff --git a/lib/Driver/GnuLdDriver.cpp b/lib/Driver/GnuLdDriver.cpp index b9af04d4b615..8c75126d6d41 100644 --- a/lib/Driver/GnuLdDriver.cpp +++ b/lib/Driver/GnuLdDriver.cpp @@ -15,7 +15,6 @@ #include "lld/Driver/Driver.h" #include "lld/ReaderWriter/ELFLinkingContext.h" -#include "lld/ReaderWriter/ELFTargets.h" #include "lld/ReaderWriter/LinkerScript.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" @@ -32,6 +31,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include <cstring> @@ -72,21 +72,7 @@ static const llvm::opt::OptTable::Info infoTable[] = { // Create OptTable class for parsing actual command line arguments class GnuLdOptTable : public llvm::opt::OptTable { public: - GnuLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} -}; - -class DriverStringSaver : public llvm::cl::StringSaver { -public: - DriverStringSaver(BumpPtrAllocator &alloc) : _alloc(alloc) {} - - const char *SaveString(const char *s) override { - char *p = _alloc.Allocate<char>(strlen(s) + 1); - strcpy(p, s); - return p; - } - -private: - BumpPtrAllocator &_alloc; + GnuLdOptTable() : OptTable(infoTable){} }; } // anonymous namespace @@ -96,37 +82,21 @@ private: // at the original @file position. If file cannot be read, @file is not expanded // and left unmodified. @file can appear in a response file, so it's a recursive // process. -static std::tuple<int, const char **> -maybeExpandResponseFiles(int argc, const char **argv, BumpPtrAllocator &alloc) { +static llvm::ArrayRef<const char *> +maybeExpandResponseFiles(llvm::ArrayRef<const char *> args, + BumpPtrAllocator &alloc) { // Expand response files. SmallVector<const char *, 256> smallvec; - for (int i = 0; i < argc; ++i) - smallvec.push_back(argv[i]); - DriverStringSaver saver(alloc); + for (const char *arg : args) + smallvec.push_back(arg); + llvm::StringSaver saver(alloc); llvm::cl::ExpandResponseFiles(saver, llvm::cl::TokenizeGNUCommandLine, smallvec); // Pack the results to a C-array and return it. - argc = smallvec.size(); - const char **copy = alloc.Allocate<const char *>(argc + 1); + const char **copy = alloc.Allocate<const char *>(smallvec.size() + 1); std::copy(smallvec.begin(), smallvec.end(), copy); - copy[argc] = nullptr; - return std::make_tuple(argc, copy); -} - -static std::error_code -getFileMagic(StringRef path, llvm::sys::fs::file_magic &magic) { - std::error_code ec = llvm::sys::fs::identify_magic(path, magic); - if (ec) - return ec; - switch (magic) { - case llvm::sys::fs::file_magic::archive: - case llvm::sys::fs::file_magic::elf_relocatable: - case llvm::sys::fs::file_magic::elf_shared_object: - case llvm::sys::fs::file_magic::unknown: - return std::error_code(); - default: - return make_dynamic_error_code(StringRef("unknown type of object file")); - } + copy[smallvec.size()] = nullptr; + return llvm::makeArrayRef(copy, smallvec.size() + 1); } // Parses an argument of --defsym=<sym>=<number> @@ -164,11 +134,12 @@ static bool parseMaxPageSize(StringRef opt, uint64_t &val) { return true; } -bool GnuLdDriver::linkELF(int argc, const char *argv[], raw_ostream &diag) { +bool GnuLdDriver::linkELF(llvm::ArrayRef<const char *> args, + raw_ostream &diag) { BumpPtrAllocator alloc; - std::tie(argc, argv) = maybeExpandResponseFiles(argc, argv, alloc); + args = maybeExpandResponseFiles(args, alloc); std::unique_ptr<ELFLinkingContext> options; - if (!parse(argc, argv, options, diag)) + if (!parse(args, options, diag)) return false; if (!options) return true; @@ -193,13 +164,16 @@ getArchType(const llvm::Triple &triple, StringRef value) { if (value == "elf_x86_64") return llvm::Triple::x86_64; return llvm::None; + case llvm::Triple::mips: case llvm::Triple::mipsel: + case llvm::Triple::mips64: case llvm::Triple::mips64el: - if (value == "elf32ltsmip") - return llvm::Triple::mipsel; - if (value == "elf64ltsmip") - return llvm::Triple::mips64el; - return llvm::None; + return llvm::StringSwitch<llvm::Optional<llvm::Triple::ArchType>>(value) + .Cases("elf32btsmip", "elf32btsmipn32", llvm::Triple::mips) + .Cases("elf32ltsmip", "elf32ltsmipn32", llvm::Triple::mipsel) + .Case("elf64btsmip", llvm::Triple::mips64) + .Case("elf64ltsmip", llvm::Triple::mips64el) + .Default(llvm::None); case llvm::Triple::aarch64: if (value == "aarch64linux") return llvm::Triple::aarch64; @@ -215,9 +189,9 @@ getArchType(const llvm::Triple &triple, StringRef value) { static bool isLinkerScript(StringRef path, raw_ostream &diag) { llvm::sys::fs::file_magic magic = llvm::sys::fs::file_magic::unknown; - std::error_code ec = getFileMagic(path, magic); - if (ec) { - diag << "unknown input file format for file " << path << "\n"; + if (std::error_code ec = llvm::sys::fs::identify_magic(path, magic)) { + diag << "unknown input file format: " << path << ": " + << ec.message() << "\n"; return false; } return magic == llvm::sys::fs::file_magic::unknown; @@ -350,17 +324,14 @@ void GnuLdDriver::addPlatformSearchDirs(ELFLinkingContext &ctx, std::unique_ptr<ELFLinkingContext> GnuLdDriver::createELFLinkingContext(llvm::Triple triple) { std::unique_ptr<ELFLinkingContext> p; - // FIXME: #include "llvm/Config/Targets.def" -#define LLVM_TARGET(targetName) \ - if ((p = elf::targetName##LinkingContext::create(triple))) return p; - LLVM_TARGET(AArch64) - LLVM_TARGET(ARM) - LLVM_TARGET(Hexagon) - LLVM_TARGET(Mips) - LLVM_TARGET(X86) - LLVM_TARGET(Example) - LLVM_TARGET(X86_64) -#undef LLVM_TARGET + if ((p = elf::createAArch64LinkingContext(triple))) return p; + if ((p = elf::createAMDGPULinkingContext(triple))) return p; + if ((p = elf::createARMLinkingContext(triple))) return p; + if ((p = elf::createExampleLinkingContext(triple))) return p; + if ((p = elf::createHexagonLinkingContext(triple))) return p; + if ((p = elf::createMipsLinkingContext(triple))) return p; + if ((p = elf::createX86LinkingContext(triple))) return p; + if ((p = elf::createX86_64LinkingContext(triple))) return p; return nullptr; } @@ -372,40 +343,39 @@ getBool(const llvm::opt::InputArgList &parsedArgs, return llvm::None; } -bool GnuLdDriver::parse(int argc, const char *argv[], +bool GnuLdDriver::parse(llvm::ArrayRef<const char *> args, std::unique_ptr<ELFLinkingContext> &context, raw_ostream &diag) { // Parse command line options using GnuLdOptions.td - std::unique_ptr<llvm::opt::InputArgList> parsedArgs; GnuLdOptTable table; unsigned missingIndex; unsigned missingCount; - parsedArgs.reset( - table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + llvm::opt::InputArgList parsedArgs = + table.ParseArgs(args.slice(1), missingIndex, missingCount); if (missingCount) { diag << "error: missing arg value for '" - << parsedArgs->getArgString(missingIndex) << "' expected " + << parsedArgs.getArgString(missingIndex) << "' expected " << missingCount << " argument(s).\n"; return false; } // Handle --help - if (parsedArgs->hasArg(OPT_help)) { - table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + if (parsedArgs.hasArg(OPT_help)) { + table.PrintHelp(llvm::outs(), args[0], "LLVM Linker", false); return true; } // Use -target or use default target triple to instantiate LinkingContext llvm::Triple baseTriple; - if (auto *arg = parsedArgs->getLastArg(OPT_target)) { + if (auto *arg = parsedArgs.getLastArg(OPT_target)) { baseTriple = llvm::Triple(arg->getValue()); } else { - baseTriple = getDefaultTarget(argv[0]); + baseTriple = getDefaultTarget(args[0]); } llvm::Triple triple(baseTriple); - if (!applyEmulation(triple, *parsedArgs, diag)) + if (!applyEmulation(triple, parsedArgs, diag)) return false; std::unique_ptr<ELFLinkingContext> ctx(createELFLinkingContext(triple)); @@ -416,39 +386,39 @@ bool GnuLdDriver::parse(int argc, const char *argv[], } // Copy mllvm - for (auto *arg : parsedArgs->filtered(OPT_mllvm)) + for (auto *arg : parsedArgs.filtered(OPT_mllvm)) ctx->appendLLVMOption(arg->getValue()); // Ignore unknown arguments. - for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) + for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) diag << "warning: ignoring unknown argument: " << unknownArg->getValue() << "\n"; // Set sys root path. - if (auto *arg = parsedArgs->getLastArg(OPT_sysroot)) + if (auto *arg = parsedArgs.getLastArg(OPT_sysroot)) ctx->setSysroot(arg->getValue()); // Handle --demangle option(For compatibility) - if (parsedArgs->hasArg(OPT_demangle)) + if (parsedArgs.hasArg(OPT_demangle)) ctx->setDemangleSymbols(true); // Handle --no-demangle option. - if (parsedArgs->hasArg(OPT_no_demangle)) + if (parsedArgs.hasArg(OPT_no_demangle)) ctx->setDemangleSymbols(false); // Figure out output kind (-r, -static, -shared) - if (parsedArgs->hasArg(OPT_relocatable)) { + if (parsedArgs.hasArg(OPT_relocatable)) { ctx->setOutputELFType(llvm::ELF::ET_REL); ctx->setPrintRemainingUndefines(false); ctx->setAllowRemainingUndefines(true); } - if (parsedArgs->hasArg(OPT_static)) { + if (parsedArgs.hasArg(OPT_static)) { ctx->setOutputELFType(llvm::ELF::ET_EXEC); ctx->setIsStaticExecutable(true); } - if (parsedArgs->hasArg(OPT_shared)) { + if (parsedArgs.hasArg(OPT_shared)) { ctx->setOutputELFType(llvm::ELF::ET_DYN); ctx->setAllowShlibUndefines(true); ctx->setUseShlibUndefines(false); @@ -457,13 +427,13 @@ bool GnuLdDriver::parse(int argc, const char *argv[], } // Handle --stats. - if (parsedArgs->hasArg(OPT_stats)) { + if (parsedArgs.hasArg(OPT_stats)) { ctx->setCollectStats(true); } // Figure out if the output type is nmagic/omagic - if (auto *arg = parsedArgs->getLastArg( - OPT_nmagic, OPT_omagic, OPT_no_omagic)) { + if (auto *arg = + parsedArgs.getLastArg(OPT_nmagic, OPT_omagic, OPT_no_omagic)) { switch (arg->getOption().getID()) { case OPT_nmagic: ctx->setOutputMagic(ELFLinkingContext::OutputMagic::NMAGIC); @@ -480,19 +450,25 @@ bool GnuLdDriver::parse(int argc, const char *argv[], } } - if (parsedArgs->hasArg(OPT_strip_all)) + if (parsedArgs.hasArg(OPT_discard_loc)) + ctx->setDiscardLocals(true); + + if (parsedArgs.hasArg(OPT_discard_temp_loc)) + ctx->setDiscardTempLocals(true); + + if (parsedArgs.hasArg(OPT_strip_all)) ctx->setStripSymbols(true); - if (auto *arg = parsedArgs->getLastArg(OPT_soname)) + if (auto *arg = parsedArgs.getLastArg(OPT_soname)) ctx->setSharedObjectName(arg->getValue()); - if (parsedArgs->hasArg(OPT_rosegment)) + if (parsedArgs.hasArg(OPT_rosegment)) ctx->setCreateSeparateROSegment(); - if (parsedArgs->hasArg(OPT_no_align_segments)) + if (parsedArgs.hasArg(OPT_no_align_segments)) ctx->setAlignSegments(false); - if (auto *arg = parsedArgs->getLastArg(OPT_image_base)) { + if (auto *arg = parsedArgs.getLastArg(OPT_image_base)) { uint64_t baseAddress = 0; StringRef inputValue = arg->getValue(); if (inputValue.getAsInteger(0, baseAddress) || !baseAddress) { @@ -502,58 +478,94 @@ bool GnuLdDriver::parse(int argc, const char *argv[], ctx->setBaseAddress(baseAddress); } - if (parsedArgs->hasArg(OPT_merge_strings)) + if (parsedArgs.hasArg(OPT_merge_strings)) ctx->setMergeCommonStrings(true); - if (parsedArgs->hasArg(OPT_t)) + if (parsedArgs.hasArg(OPT_t)) ctx->setLogInputFiles(true); - if (parsedArgs->hasArg(OPT_use_shlib_undefs)) + if (parsedArgs.hasArg(OPT_use_shlib_undefs)) ctx->setUseShlibUndefines(true); - if (auto val = getBool(*parsedArgs, OPT_allow_shlib_undefs, + if (auto val = getBool(parsedArgs, OPT_allow_shlib_undefs, OPT_no_allow_shlib_undefs)) ctx->setAllowShlibUndefines(*val); - if (auto *arg = parsedArgs->getLastArg(OPT_e)) + if (auto *arg = parsedArgs.getLastArg(OPT_e)) ctx->setEntrySymbolName(arg->getValue()); - if (auto *arg = parsedArgs->getLastArg(OPT_output)) + if (auto *arg = parsedArgs.getLastArg(OPT_output)) ctx->setOutputPath(arg->getValue()); - if (parsedArgs->hasArg(OPT_noinhibit_exec)) + if (parsedArgs.hasArg(OPT_noinhibit_exec)) ctx->setAllowRemainingUndefines(true); - if (auto val = getBool(*parsedArgs, OPT_export_dynamic, - OPT_no_export_dynamic)) + if (auto val = getBool(parsedArgs, OPT_export_dynamic, OPT_no_export_dynamic)) ctx->setExportDynamic(*val); - if (parsedArgs->hasArg(OPT_allow_multiple_definition)) + if (parsedArgs.hasArg(OPT_allow_multiple_definition)) ctx->setAllowDuplicates(true); - if (auto *arg = parsedArgs->getLastArg(OPT_dynamic_linker)) + if (auto *arg = parsedArgs.getLastArg(OPT_dynamic_linker)) ctx->setInterpreter(arg->getValue()); - if (auto *arg = parsedArgs->getLastArg(OPT_init)) + if (auto *arg = parsedArgs.getLastArg(OPT_init)) ctx->setInitFunction(arg->getValue()); - if (auto *arg = parsedArgs->getLastArg(OPT_fini)) + if (auto *arg = parsedArgs.getLastArg(OPT_fini)) ctx->setFiniFunction(arg->getValue()); - if (auto *arg = parsedArgs->getLastArg(OPT_output_filetype)) + if (auto *arg = parsedArgs.getLastArg(OPT_output_filetype)) ctx->setOutputFileType(arg->getValue()); - for (auto *arg : parsedArgs->filtered(OPT_L)) + // Process ELF/ARM specific options + bool hasArmTarget1Rel = parsedArgs.hasArg(OPT_target1_rel); + bool hasArmTarget1Abs = parsedArgs.hasArg(OPT_target1_abs); + if (triple.getArch() == llvm::Triple::arm) { + if (hasArmTarget1Rel && hasArmTarget1Abs) { + diag << "error: options --target1-rel and --target1-abs" + " can't be used together.\n"; + return false; + } else if (hasArmTarget1Rel || hasArmTarget1Abs) { + ctx->setArmTarget1Rel(hasArmTarget1Rel && !hasArmTarget1Abs); + } + } else { + for (const auto *arg : parsedArgs.filtered(OPT_grp_arm_targetopts)) { + diag << "warning: ignoring unsupported ARM/ELF specific argument: " + << arg->getSpelling() << "\n"; + } + } + + // Process MIPS specific options. + if (triple.getArch() == llvm::Triple::mips || + triple.getArch() == llvm::Triple::mipsel || + triple.getArch() == llvm::Triple::mips64 || + triple.getArch() == llvm::Triple::mips64el) { + ctx->setMipsPcRelEhRel(parsedArgs.hasArg(OPT_pcrel_eh_reloc)); + auto *hashArg = parsedArgs.getLastArg(OPT_hash_style); + if (hashArg && hashArg->getValue() != StringRef("sysv")) { + diag << "error: .gnu.hash is incompatible with the MIPS ABI\n"; + return false; + } + } + else { + for (const auto *arg : parsedArgs.filtered(OPT_grp_mips_targetopts)) { + diag << "warning: ignoring unsupported MIPS specific argument: " + << arg->getSpelling() << "\n"; + } + } + + for (auto *arg : parsedArgs.filtered(OPT_L)) ctx->addSearchPath(arg->getValue()); // Add the default search directory specific to the target. - if (!parsedArgs->hasArg(OPT_nostdlib)) + if (!parsedArgs.hasArg(OPT_nostdlib)) addPlatformSearchDirs(*ctx, triple, baseTriple); - for (auto *arg : parsedArgs->filtered(OPT_u)) + for (auto *arg : parsedArgs.filtered(OPT_u)) ctx->addInitialUndefinedSymbol(arg->getValue()); - for (auto *arg : parsedArgs->filtered(OPT_defsym)) { + for (auto *arg : parsedArgs.filtered(OPT_defsym)) { StringRef sym, target; uint64_t addr; if (parseDefsymAsAbsolute(arg->getValue(), sym, addr)) { @@ -566,11 +578,15 @@ bool GnuLdDriver::parse(int argc, const char *argv[], } } - for (auto *arg : parsedArgs->filtered(OPT_z)) { + for (auto *arg : parsedArgs.filtered(OPT_z)) { StringRef opt = arg->getValue(); - if (opt == "muldefs") { + if (opt == "muldefs") ctx->setAllowDuplicates(true); - } else if (opt.startswith("max-page-size")) { + else if (opt == "now") + ctx->setDTFlag(ELFLinkingContext::DTFlag::DT_NOW); + else if (opt == "origin") + ctx->setDTFlag(ELFLinkingContext::DTFlag::DT_ORIGIN); + else if (opt.startswith("max-page-size")) { // Parse -z max-page-size option. // The default page size is considered the minimum page size the user // can set, check the user input if its atleast the minimum page size @@ -592,39 +608,46 @@ bool GnuLdDriver::parse(int argc, const char *argv[], } } - for (auto *arg : parsedArgs->filtered(OPT_rpath)) { + for (auto *arg : parsedArgs.filtered(OPT_rpath)) { SmallVector<StringRef, 2> rpaths; StringRef(arg->getValue()).split(rpaths, ":"); for (auto path : rpaths) ctx->addRpath(path); } - for (auto *arg : parsedArgs->filtered(OPT_rpath_link)) { + for (auto *arg : parsedArgs.filtered(OPT_rpath_link)) { SmallVector<StringRef, 2> rpaths; StringRef(arg->getValue()).split(rpaths, ":"); for (auto path : rpaths) ctx->addRpathLink(path); } + // Enable new dynamic tags. + if (parsedArgs.hasArg(OPT_enable_newdtags)) + ctx->setEnableNewDtags(true); + // Support --wrap option. - for (auto *arg : parsedArgs->filtered(OPT_wrap)) + for (auto *arg : parsedArgs.filtered(OPT_wrap)) ctx->addWrapForSymbol(arg->getValue()); // Register possible input file parsers. ctx->registry().addSupportELFObjects(*ctx); ctx->registry().addSupportArchives(ctx->logInputFiles()); ctx->registry().addSupportYamlFiles(); - ctx->registry().addSupportNativeObjects(); if (ctx->allowLinkWithDynamicLibraries()) ctx->registry().addSupportELFDynamicSharedObjects(*ctx); + // Parse the LLVM options before we process files in case the file handling + // makes use of things like DEBUG(). + parseLLVMOptions(*ctx); + std::stack<int> groupStack; int numfiles = 0; bool asNeeded = false; bool wholeArchive = false; // Process files - for (auto arg : *parsedArgs) { + for (auto arg : parsedArgs) { switch (arg->getOption().getID()) { case OPT_no_whole_archive: wholeArchive = false; @@ -685,7 +708,7 @@ bool GnuLdDriver::parse(int argc, const char *argv[], diag << "Cannot open " << path << ": " << ec.message() << "\n"; return false; } - bool nostdlib = parsedArgs->hasArg(OPT_nostdlib); + bool nostdlib = parsedArgs.hasArg(OPT_nostdlib); std::error_code ec = evalLinkerScript(*ctx, std::move(mb.get()), diag, nostdlib); if (ec) { @@ -721,9 +744,6 @@ bool GnuLdDriver::parse(int argc, const char *argv[], case LinkingContext::OutputFileType::YAML: ctx->setOutputPath("-"); break; - case LinkingContext::OutputFileType::Native: - ctx->setOutputPath("a.native"); - break; default: ctx->setOutputPath("a.out"); break; @@ -735,7 +755,10 @@ bool GnuLdDriver::parse(int argc, const char *argv[], return false; // Perform linker script semantic actions - ctx->linkerScriptSema().perform(); + if (auto ec = ctx->linkerScriptSema().perform()) { + diag << "Error in the linker script's semantics: " << ec.message() << "\n"; + return false; + } context.swap(ctx); return true; diff --git a/lib/Driver/GnuLdOptions.td b/lib/Driver/GnuLdOptions.td index 9d06f2935439..7d850d4d002e 100644 --- a/lib/Driver/GnuLdOptions.td +++ b/lib/Driver/GnuLdOptions.td @@ -25,6 +25,15 @@ multiclass dashEq<string opt1, string opt2, string help> { Alias<!cast<Option>(opt1)>; } +// Support --<option>,--<option>= +multiclass mDashEq<string opt1, string help> { + // Option + def "" : Separate<["--"], opt1>, HelpText<help>; + // Compatibility aliases + def opt2_eq : Joined<["--"], opt1#"=">, + Alias<!cast<Option>(opt1)>; +} + //===----------------------------------------------------------------------===// /// LLVM and Target options //===----------------------------------------------------------------------===// @@ -62,7 +71,7 @@ def grp_general : OptionGroup<"opts">, def output : Separate<["-"], "o">, MetaVarName<"<path>">, HelpText<"Path to file to write output">, Group<grp_general>; -def m : Separate<["-"], "m">, MetaVarName<"<emulation>">, +def m : JoinedOrSeparate<["-"], "m">, MetaVarName<"<emulation>">, HelpText<"Select target emulation">, Group<grp_general>; def build_id : Flag<["--"], "build-id">, @@ -142,7 +151,7 @@ def grp_dynlibexec : OptionGroup<"opts">, def dynamic_linker : Joined<["--"], "dynamic-linker=">, HelpText<"Set the path to the dynamic linker">, Group<grp_dynlibexec>; // Executable options - compatibility aliases -def dynamic_linker_alias : Separate<["-"], "dynamic-linker">, +def dynamic_linker_alias : Separate<["-", "--"], "dynamic-linker">, Alias<dynamic_linker>; defm rpath : dashEq<"rpath", "rpath", "Add a directory to the runtime library search path">, @@ -211,15 +220,23 @@ def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">, def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">, HelpText<"Allow multiple definitions">, Group<grp_resolveropt>; -def defsym : Joined<["--"], "defsym=">, - HelpText<"Create a defined symbol">, - Group<grp_resolveropt>; +defm defsym : mDashEq<"defsym", + "Create a global symbol in the output file " + "containing the absolute address given by expression">, + MetaVarName<"symbol=<expression>">, + Group<grp_resolveropt>; //===----------------------------------------------------------------------===// /// Custom Options //===----------------------------------------------------------------------===// def grp_customopts : OptionGroup<"opts">, HelpText<"CUSTOM OPTIONS">; +def disable_newdtags: Flag<["--"], "disable-new-dtags">, + HelpText<"Disable new dynamic tags">, + Group<grp_customopts>; +def enable_newdtags: Flag<["--"], "enable-new-dtags">, + HelpText<"Enable new dynamic tags">, + Group<grp_customopts>; def rosegment: Flag<["--"], "rosegment">, HelpText<"Put read-only non-executable sections in their own segment">, Group<grp_customopts>; @@ -238,6 +255,16 @@ def grp_symbolopts : OptionGroup<"opts">, def demangle : Flag<["--"], "demangle">, HelpText<"Demangle C++ symbols">, Group<grp_symbolopts>; +def discard_loc : Flag<["--"], "discard-all">, + HelpText<"Discard all local symbols">, + Group<grp_symbolopts>; +def alias_discard_loc: Flag<["-"], "x">, + Alias<discard_loc>; +def discard_temp_loc : Flag<["--"], "discard-locals">, + HelpText<"Discard temporary local symbols">, + Group<grp_symbolopts>; +def alias_discard_temp_loc : Flag<["-"], "X">, + Alias<discard_temp_loc>; def no_demangle : Flag<["--"], "no-demangle">, HelpText<"Dont demangle C++ symbols">, Group<grp_symbolopts>; @@ -296,12 +323,37 @@ def stats : Flag<["--"], "stats">, def grp_extns : OptionGroup<"opts">, HelpText<"Extensions">; def output_filetype: Separate<["--"], "output-filetype">, - HelpText<"Specify what type of output file that lld creates, YAML/Native">, + HelpText<"Specify yaml to create an output in YAML format">, Group<grp_extns>; def alias_output_filetype: Joined<["--"], "output-filetype=">, Alias<output_filetype>; //===----------------------------------------------------------------------===// +/// Target Specific Options +//===----------------------------------------------------------------------===// +def grp_targetopts : OptionGroup<"opts">, + HelpText<"ARCH SPECIFIC OPTIONS">; + +//===----------------------------------------------------------------------===// +/// ARM Target Specific Options +//===----------------------------------------------------------------------===// +def grp_arm_targetopts : OptionGroup<"ARM SPECIFIC OPTIONS">, + Group<grp_targetopts>; +def target1_rel : Flag<["--"], "target1-rel">, + Group<grp_arm_targetopts>, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">; +def target1_abs : Flag<["--"], "target1-abs">, + Group<grp_arm_targetopts>, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">; + +//===----------------------------------------------------------------------===// +/// MIPS Target Specific Options +//===----------------------------------------------------------------------===// +def grp_mips_targetopts : OptionGroup<"MIPS SPECIFIC OPTIONS">, + Group<grp_targetopts>; +def pcrel_eh_reloc : Flag<["-", "--"], "pcrel-eh-reloc">, + Group<grp_mips_targetopts>, + HelpText<"Interpret R_MIPS_EH as R_MIPS_PC32">; + +//===----------------------------------------------------------------------===// /// Ignored options //===----------------------------------------------------------------------===// def grp_ignored: OptionGroup<"ignored">, @@ -315,6 +367,9 @@ def Qy : Flag<["-"], "Qy">, def qmagic : Flag<["-"], "qmagic">, HelpText<"Ignored for Linux Compatibility">, Group<grp_ignored>; +def G : Separate<["-"], "G">, + HelpText<"Ignored for MIPS GNU Linker Compatibility">, + Group<grp_ignored>; //===----------------------------------------------------------------------===// /// Help diff --git a/lib/Driver/Makefile b/lib/Driver/Makefile deleted file mode 100644 index 19024cfab0f1..000000000000 --- a/lib/Driver/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -##===- lld/lib/Driver/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLD_LEVEL := ../.. -LIBRARYNAME := lldDriver - -BUILT_SOURCES = CoreOptions.inc UniversalDriverOptions.inc DarwinLdOptions.inc \ - GnuLdOptions.inc WinLinkOptions.inc - -TABLEGEN_INC_FILES_COMMON = 1 - -include $(LLD_LEVEL)/Makefile - -$(ObjDir)/CoreOptions.inc.tmp : CoreOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir - $(Echo) "Building LLD CoreOptions Option tables with tblgen" - $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< - -$(ObjDir)/UniversalDriverOptions.inc.tmp : UniversalDriverOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir - $(Echo) "Building LLD Universal Driver Options tables with tblgen" - $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< - -$(ObjDir)/DarwinLdOptions.inc.tmp : DarwinLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir - $(Echo) "Building LLD Darwin ld Option tables with tblgen" - $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< - -$(ObjDir)/GnuLdOptions.inc.tmp : GnuLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir - $(Echo) "Building LLD Gnu ld Option tables with tblgen" - $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< - -$(ObjDir)/WinLinkOptions.inc.tmp : WinLinkOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir - $(Echo) "Building LLD WinLinkOptions Option tables with tblgen" - $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< diff --git a/lib/Driver/TODO.rst b/lib/Driver/TODO.rst index e03d829c232d..868eaf02290c 100644 --- a/lib/Driver/TODO.rst +++ b/lib/Driver/TODO.rst @@ -35,8 +35,6 @@ Missing Options * -Ur * --unique * -v,--version,-V -* -x,--discard-all -* -X,--discard-locals * -y,--trace-symbol * -z (keywords need to be implemented) * --accept-unknown-input-arch,--no-accept-unknown-input-arch diff --git a/lib/Driver/UniversalDriver.cpp b/lib/Driver/UniversalDriver.cpp index 7d42ad7b4bfc..3dea7ebfae89 100644 --- a/lib/Driver/UniversalDriver.cpp +++ b/lib/Driver/UniversalDriver.cpp @@ -63,15 +63,16 @@ static const llvm::opt::OptTable::Info infoTable[] = { class UniversalDriverOptTable : public llvm::opt::OptTable { public: UniversalDriverOptTable() - : OptTable(infoTable, llvm::array_lengthof(infoTable)) {} + : OptTable(infoTable) {} }; enum class Flavor { invalid, - gnu_ld, // -flavor gnu - win_link, // -flavor link - darwin_ld, // -flavor darwin - core // -flavor core OR -core + old_gnu_ld, // -flavor old-gnu + gnu_ld, // -flavor gnu + win_link, // -flavor link + darwin_ld, // -flavor darwin + core // -flavor core OR -core }; struct ProgramNameParts { @@ -83,7 +84,9 @@ struct ProgramNameParts { static Flavor strToFlavor(StringRef str) { return llvm::StringSwitch<Flavor>(str) + .Case("old-gnu", Flavor::old_gnu_ld) .Case("gnu", Flavor::gnu_ld) + .Case("ld.lld", Flavor::gnu_ld) .Case("link", Flavor::win_link) .Case("lld-link", Flavor::win_link) .Case("darwin", Flavor::darwin_ld) @@ -124,27 +127,27 @@ static ProgramNameParts parseProgramName(StringRef programName) { // Removes the argument from argv along with its value, if exists, and updates // argc. -static void removeArg(llvm::opt::Arg *arg, int &argc, const char **&argv) { +static void removeArg(llvm::opt::Arg *arg, + llvm::MutableArrayRef<const char *> &args) { unsigned int numToRemove = arg->getNumValues() + 1; - unsigned int argIndex = arg->getIndex() + 1; - - std::rotate(&argv[argIndex], &argv[argIndex + numToRemove], argv + argc); - argc -= numToRemove; + auto sub = args.slice(arg->getIndex() + 1); + std::rotate(sub.begin(), sub.begin() + numToRemove, sub.end()); + args = args.drop_back(numToRemove); } -static Flavor getFlavor(int &argc, const char **&argv, - std::unique_ptr<llvm::opt::InputArgList> &parsedArgs) { - if (llvm::opt::Arg *argCore = parsedArgs->getLastArg(OPT_core)) { - removeArg(argCore, argc, argv); +static Flavor getFlavor(llvm::MutableArrayRef<const char *> &args, + const llvm::opt::InputArgList &parsedArgs) { + if (llvm::opt::Arg *argCore = parsedArgs.getLastArg(OPT_core)) { + removeArg(argCore, args); return Flavor::core; } - if (llvm::opt::Arg *argFlavor = parsedArgs->getLastArg(OPT_flavor)) { - removeArg(argFlavor, argc, argv); + if (llvm::opt::Arg *argFlavor = parsedArgs.getLastArg(OPT_flavor)) { + removeArg(argFlavor, args); return strToFlavor(argFlavor->getValue()); } #if LLVM_ON_UNIX - if (llvm::sys::path::filename(argv[0]).equals("ld")) { + if (llvm::sys::path::filename(args[0]).equals("ld")) { #if __APPLE__ // On a Darwin systems, if linker binary is named "ld", use Darwin driver. return Flavor::darwin_ld; @@ -154,59 +157,63 @@ static Flavor getFlavor(int &argc, const char **&argv, } #endif - StringRef name = llvm::sys::path::stem(argv[0]); + StringRef name = llvm::sys::path::filename(args[0]); + if (name.endswith_lower(".exe")) + name = llvm::sys::path::stem(name); return strToFlavor(parseProgramName(name)._flavor); } namespace lld { -bool UniversalDriver::link(int argc, const char *argv[], +bool UniversalDriver::link(llvm::MutableArrayRef<const char *> args, raw_ostream &diagnostics) { // Parse command line options using GnuLdOptions.td - std::unique_ptr<llvm::opt::InputArgList> parsedArgs; UniversalDriverOptTable table; unsigned missingIndex; unsigned missingCount; // Program name - StringRef programName = llvm::sys::path::stem(argv[0]); + StringRef programName = llvm::sys::path::stem(args[0]); - parsedArgs.reset( - table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + llvm::opt::InputArgList parsedArgs = + table.ParseArgs(args.slice(1), missingIndex, missingCount); if (missingCount) { diagnostics << "error: missing arg value for '" - << parsedArgs->getArgString(missingIndex) << "' expected " + << parsedArgs.getArgString(missingIndex) << "' expected " << missingCount << " argument(s).\n"; return false; } // Handle -help - if (parsedArgs->getLastArg(OPT_help)) { + if (parsedArgs.getLastArg(OPT_help)) { table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); return true; } // Handle -version - if (parsedArgs->getLastArg(OPT_version)) { + if (parsedArgs.getLastArg(OPT_version)) { diagnostics << "LLVM Linker Version: " << getLLDVersion() << getLLDRepositoryVersion() << "\n"; return true; } - Flavor flavor = getFlavor(argc, argv, parsedArgs); - std::vector<const char *> args(argv, argv + argc); + Flavor flavor = getFlavor(args, parsedArgs); // Switch to appropriate driver. switch (flavor) { + case Flavor::old_gnu_ld: + return GnuLdDriver::linkELF(args, diagnostics); case Flavor::gnu_ld: - return GnuLdDriver::linkELF(args.size(), args.data(), diagnostics); + elf2::link(args); + return true; case Flavor::darwin_ld: - return DarwinLdDriver::linkMachO(args.size(), args.data(), diagnostics); + return DarwinLdDriver::linkMachO(args, diagnostics); case Flavor::win_link: - return WinLinkDriver::linkPECOFF(args.size(), args.data(), diagnostics); + coff::link(args); + return true; case Flavor::core: - return CoreDriver::link(args.size(), args.data(), diagnostics); + return CoreDriver::link(args, diagnostics); case Flavor::invalid: diagnostics << "Select the appropriate flavor\n"; table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); diff --git a/lib/Driver/WinLinkDriver.cpp b/lib/Driver/WinLinkDriver.cpp deleted file mode 100644 index 6ee7a5a004b5..000000000000 --- a/lib/Driver/WinLinkDriver.cpp +++ /dev/null @@ -1,1371 +0,0 @@ -//===- lib/Driver/WinLinkDriver.cpp ---------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Concrete instance of the Driver for Windows link.exe. -/// -//===----------------------------------------------------------------------===// - -#include "lld/Driver/Driver.h" -#include "lld/Driver/WinLinkModuleDef.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Object/COFF.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cctype> -#include <map> -#include <memory> -#include <sstream> -#include <tuple> - -namespace lld { - -// -// Option definitions -// - -// Create enum with OPT_xxx values for each option in WinLinkOptions.td -enum { - OPT_INVALID = 0, -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELP, META) \ - OPT_##ID, -#include "WinLinkOptions.inc" -#undef OPTION -}; - -// Create prefix string literals used in WinLinkOptions.td -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "WinLinkOptions.inc" -#undef PREFIX - -// Create table mapping all options defined in WinLinkOptions.td -static const llvm::opt::OptTable::Info infoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS }, -#include "WinLinkOptions.inc" -#undef OPTION -}; - -namespace { - -// Create OptTable class for parsing actual command line arguments -class WinLinkOptTable : public llvm::opt::OptTable { -public: - // link.exe's command line options are case insensitive, unlike - // other driver's options for Unix. - WinLinkOptTable() - : OptTable(infoTable, llvm::array_lengthof(infoTable), - /* ignoreCase */ true) {} -}; - -} // anonymous namespace - -// -// Functions to parse each command line option -// - -// Split the given string with spaces. -static std::vector<std::string> splitArgList(const std::string &str) { - std::stringstream stream(str); - std::istream_iterator<std::string> begin(stream); - std::istream_iterator<std::string> end; - return std::vector<std::string>(begin, end); -} - -// Split the given string with the path separator. -static std::vector<StringRef> splitPathList(StringRef str) { - std::vector<StringRef> ret; - while (!str.empty()) { - StringRef path; - std::tie(path, str) = str.split(';'); - ret.push_back(path); - } - return ret; -} - -// Parse an argument for /alternatename. The expected string is -// "<string>=<string>". -static bool parseAlternateName(StringRef arg, StringRef &weak, StringRef &def, - raw_ostream &diag) { - std::tie(weak, def) = arg.split('='); - if (weak.empty() || def.empty()) { - diag << "Error: malformed /alternatename option: " << arg << "\n"; - return false; - } - return true; -} - -// Parse an argument for /base, /stack or /heap. The expected string -// is "<integer>[,<integer>]". -static bool parseMemoryOption(StringRef arg, uint64_t &reserve, - uint64_t &commit) { - StringRef reserveStr, commitStr; - std::tie(reserveStr, commitStr) = arg.split(','); - if (reserveStr.getAsInteger(0, reserve)) - return false; - if (!commitStr.empty() && commitStr.getAsInteger(0, commit)) - return false; - return true; -} - -// Parse an argument for /version or /subsystem. The expected string is -// "<integer>[.<integer>]". -static bool parseVersion(StringRef arg, uint32_t &major, uint32_t &minor) { - StringRef majorVersion, minorVersion; - std::tie(majorVersion, minorVersion) = arg.split('.'); - if (minorVersion.empty()) - minorVersion = "0"; - if (majorVersion.getAsInteger(0, major)) - return false; - if (minorVersion.getAsInteger(0, minor)) - return false; - return true; -} - -// Returns subsystem type for the given string. -static llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) { - return llvm::StringSwitch<llvm::COFF::WindowsSubsystem>(str.lower()) - .Case("windows", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI) - .Case("console", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI) - .Case("boot_application", - llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) - .Case("efi_application", llvm::COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION) - .Case("efi_boot_service_driver", - llvm::COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) - .Case("efi_rom", llvm::COFF::IMAGE_SUBSYSTEM_EFI_ROM) - .Case("efi_runtime_driver", - llvm::COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) - .Case("native", llvm::COFF::IMAGE_SUBSYSTEM_NATIVE) - .Case("posix", llvm::COFF::IMAGE_SUBSYSTEM_POSIX_CUI) - .Default(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN); -} - -// Parse /subsystem command line option. The form of /subsystem is -// "subsystem_name[,majorOSVersion[.minorOSVersion]]". -static bool parseSubsystem(StringRef arg, - llvm::COFF::WindowsSubsystem &subsystem, - llvm::Optional<uint32_t> &major, - llvm::Optional<uint32_t> &minor, raw_ostream &diag) { - StringRef subsystemStr, osVersion; - std::tie(subsystemStr, osVersion) = arg.split(','); - if (!osVersion.empty()) { - uint32_t v1, v2; - if (!parseVersion(osVersion, v1, v2)) - return false; - major = v1; - minor = v2; - } - subsystem = stringToWinSubsystem(subsystemStr); - if (subsystem == llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN) { - diag << "error: unknown subsystem name: " << subsystemStr << "\n"; - return false; - } - return true; -} - -static llvm::COFF::MachineTypes stringToMachineType(StringRef str) { - // FIXME: we have no way to differentiate between ARM and ARMNT currently. - // However, given that LLVM only supports ARM NT, default to that for now. - return llvm::StringSwitch<llvm::COFF::MachineTypes>(str.lower()) - .Case("arm", llvm::COFF::IMAGE_FILE_MACHINE_ARMNT) - .Case("x64", llvm::COFF::IMAGE_FILE_MACHINE_AMD64) - .Case("x86", llvm::COFF::IMAGE_FILE_MACHINE_I386) - .Default(llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN); -} - -// Parse /section:name,[[!]{DEKPRSW}] -// -// /section option is to set non-default bits in the Characteristics fields of -// the section header. D, E, K, P, R, S, and W represent discardable, -// execute, not_cachable, not_pageable, read, shared, and write bits, -// respectively. You can specify multiple flags in one /section option. -// -// If the flag starts with "!", the flags represent a mask that should be turned -// off regardless of the default value. You can even create a section which is -// not readable, writable nor executable with this -- although it's probably -// useless. -static bool parseSection(StringRef option, std::string §ion, - llvm::Optional<uint32_t> &flags, - llvm::Optional<uint32_t> &mask) { - StringRef flagString; - std::tie(section, flagString) = option.split(","); - - bool negative = false; - if (flagString.startswith("!")) { - negative = true; - flagString = flagString.substr(1); - } - if (flagString.empty()) - return false; - - uint32_t attribs = 0; - for (size_t i = 0, e = flagString.size(); i < e; ++i) { - switch (tolower(flagString[i])) { -#define CASE(c, flag) \ - case c: \ - attribs |= flag; \ - break - CASE('d', llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE); - CASE('e', llvm::COFF::IMAGE_SCN_MEM_EXECUTE); - CASE('k', llvm::COFF::IMAGE_SCN_MEM_NOT_CACHED); - CASE('p', llvm::COFF::IMAGE_SCN_MEM_NOT_PAGED); - CASE('r', llvm::COFF::IMAGE_SCN_MEM_READ); - CASE('s', llvm::COFF::IMAGE_SCN_MEM_SHARED); - CASE('w', llvm::COFF::IMAGE_SCN_MEM_WRITE); -#undef CASE - default: - return false; - } - } - - if (negative) { - mask = attribs; - } else { - flags = attribs; - } - return true; -} - -static bool readFile(PECOFFLinkingContext &ctx, StringRef path, - ArrayRef<uint8_t> &result) { - ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); - if (!buf) - return false; - StringRef Data = buf.get()->getBuffer(); - result = ctx.allocate(ArrayRef<uint8_t>( - reinterpret_cast<const uint8_t *>(Data.begin()), Data.size())); - return true; -} - -// Parse /manifest:EMBED[,ID=#]|NO. -static bool parseManifest(StringRef option, bool &enable, bool &embed, - int &id) { - if (option.equals_lower("no")) { - enable = false; - return true; - } - if (!option.startswith_lower("embed")) - return false; - - embed = true; - option = option.substr(strlen("embed")); - if (option.empty()) - return true; - if (!option.startswith_lower(",id=")) - return false; - option = option.substr(strlen(",id=")); - if (option.getAsInteger(0, id)) - return false; - return true; -} - -static bool isLibraryFile(StringRef path) { - return path.endswith_lower(".lib") || path.endswith_lower(".imp"); -} - -static StringRef getObjectPath(PECOFFLinkingContext &ctx, StringRef path) { - std::string result; - if (isLibraryFile(path)) { - result = ctx.searchLibraryFile(path); - } else if (llvm::sys::path::extension(path).empty()) { - result = path.str() + ".obj"; - } else { - result = path; - } - return ctx.allocate(result); -} - -static StringRef getLibraryPath(PECOFFLinkingContext &ctx, StringRef path) { - std::string result = isLibraryFile(path) - ? ctx.searchLibraryFile(path) - : ctx.searchLibraryFile(path.str() + ".lib"); - return ctx.allocate(result); -} - -// Returns true if the given file is a Windows resource file. -static bool isResoruceFile(StringRef path) { - llvm::sys::fs::file_magic fileType; - if (llvm::sys::fs::identify_magic(path, fileType)) { - // If we cannot read the file, assume it's not a resource file. - // The further stage will raise an error on this unreadable file. - return false; - } - return fileType == llvm::sys::fs::file_magic::windows_resource; -} - -// Merge Windows resource files and convert them to a single COFF file. -// The temporary file path is set to result. -static bool convertResourceFiles(PECOFFLinkingContext &ctx, - std::vector<std::string> inFiles, - std::string &result) { - // Create an output file path. - SmallString<128> outFile; - if (llvm::sys::fs::createTemporaryFile("resource", "obj", outFile)) - return false; - std::string outFileArg = ("/out:" + outFile).str(); - - // Construct CVTRES.EXE command line and execute it. - std::string program = "cvtres.exe"; - ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); - if (!programPathOrErr) { - llvm::errs() << "Unable to find " << program << " in PATH\n"; - return false; - } - const std::string &programPath = *programPathOrErr; - - std::vector<const char *> args; - args.push_back(programPath.c_str()); - args.push_back(ctx.is64Bit() ? "/machine:x64" : "/machine:x86"); - args.push_back("/readonly"); - args.push_back("/nologo"); - args.push_back(outFileArg.c_str()); - for (const std::string &path : inFiles) - args.push_back(path.c_str()); - args.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { - llvm::errs() << program << " failed\n"; - return false; - } - result = outFile.str(); - return true; -} - -// Parse /manifestuac:(level=<string>|uiAccess=<string>). -// -// The arguments will be embedded to the manifest XML file with no error check, -// so the values given via the command line must be valid as XML attributes. -// This may sound a bit odd, but that's how link.exe works, so we will follow. -static bool parseManifestUAC(StringRef option, - llvm::Optional<std::string> &level, - llvm::Optional<std::string> &uiAccess) { - for (;;) { - option = option.ltrim(); - if (option.empty()) - return true; - if (option.startswith_lower("level=")) { - option = option.substr(strlen("level=")); - StringRef value; - std::tie(value, option) = option.split(" "); - level = value.str(); - continue; - } - if (option.startswith_lower("uiaccess=")) { - option = option.substr(strlen("uiaccess=")); - StringRef value; - std::tie(value, option) = option.split(" "); - uiAccess = value.str(); - continue; - } - return false; - } -} - -// Returns the machine type (e.g. x86) of the given input file. -// If the file is not COFF, returns false. -static bool getMachineType(StringRef path, llvm::COFF::MachineTypes &result) { - llvm::sys::fs::file_magic fileType; - if (llvm::sys::fs::identify_magic(path, fileType)) - return false; - if (fileType != llvm::sys::fs::file_magic::coff_object) - return false; - ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); - if (!buf) - return false; - std::error_code ec; - llvm::object::COFFObjectFile obj(buf.get()->getMemBufferRef(), ec); - if (ec) - return false; - result = static_cast<llvm::COFF::MachineTypes>(obj.getMachine()); - return true; -} - -// Parse /export:entryname[=internalname][,@ordinal[,NONAME]][,DATA][,PRIVATE]. -// -// MSDN doesn't say anything about /export:foo=bar style option or PRIVATE -// attribtute, but link.exe actually accepts them. -static bool parseExport(StringRef option, - PECOFFLinkingContext::ExportDesc &ret) { - StringRef name; - StringRef rest; - std::tie(name, rest) = option.split(","); - if (name.empty()) - return false; - if (name.find('=') == StringRef::npos) { - ret.name = name; - } else { - std::tie(ret.externalName, ret.name) = name.split("="); - if (ret.name.empty()) - return false; - } - - for (;;) { - if (rest.empty()) - return true; - StringRef arg; - std::tie(arg, rest) = rest.split(","); - if (arg.equals_lower("noname")) { - if (ret.ordinal < 0) - return false; - ret.noname = true; - continue; - } - if (arg.equals_lower("data")) { - ret.isData = true; - continue; - } - if (arg.equals_lower("private")) { - ret.isPrivate = true; - continue; - } - if (arg.startswith("@")) { - int ordinal; - if (arg.substr(1).getAsInteger(0, ordinal)) - return false; - if (ordinal <= 0 || 65535 < ordinal) - return false; - ret.ordinal = ordinal; - continue; - } - return false; - } -} - -// Read module-definition file. -static bool parseDef(StringRef option, llvm::BumpPtrAllocator &alloc, - std::vector<moduledef::Directive *> &result) { - ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(option); - if (!buf) - return false; - moduledef::Lexer lexer(std::move(buf.get())); - moduledef::Parser parser(lexer, alloc); - return parser.parse(result); -} - -static StringRef replaceExtension(PECOFFLinkingContext &ctx, StringRef path, - StringRef extension) { - SmallString<128> val = path; - llvm::sys::path::replace_extension(val, extension); - return ctx.allocate(val.str()); -} - -// Create a manifest file contents. -static std::string createManifestXml(PECOFFLinkingContext &ctx) { - std::string ret; - llvm::raw_string_ostream out(ret); - // Emit the XML. Note that we do *not* verify that the XML attributes are - // syntactically correct. This is intentional for link.exe compatibility. - out << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" - "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" - " manifestVersion=\"1.0\">\n"; - if (ctx.getManifestUAC()) { - out << " <trustInfo>\n" - " <security>\n" - " <requestedPrivileges>\n" - " <requestedExecutionLevel level=" << ctx.getManifestLevel() - << " uiAccess=" << ctx.getManifestUiAccess() - << "/>\n" - " </requestedPrivileges>\n" - " </security>\n" - " </trustInfo>\n"; - const std::string &dependency = ctx.getManifestDependency(); - if (!dependency.empty()) { - out << " <dependency>\n" - " <dependentAssembly>\n" - " <assemblyIdentity " << dependency - << " />\n" - " </dependentAssembly>\n" - " </dependency>\n"; - } - } - out << "</assembly>\n"; - out.flush(); - return ret; -} - -// Convert one doublequote to two doublequotes, so that we can embed the string -// into a resource script file. -static void quoteAndPrintXml(raw_ostream &out, StringRef str) { - for (;;) { - if (str.empty()) - return; - StringRef line; - std::tie(line, str) = str.split("\n"); - if (line.empty()) - continue; - out << '\"'; - const char *p = line.data(); - for (int i = 0, size = line.size(); i < size; ++i) { - switch (p[i]) { - case '\"': - out << '\"'; - // fallthrough - default: - out << p[i]; - } - } - out << "\"\n"; - } -} - -// Create a resource file (.res file) containing the manifest XML. This is done -// in two steps: -// -// 1. Create a resource script file containing the XML as a literal string. -// 2. Run RC.EXE command to compile the script file to a resource file. -// -// The temporary file created in step 1 will be deleted on exit from this -// function. The file created in step 2 will have the same lifetime as the -// PECOFFLinkingContext. -static bool createManifestResourceFile(PECOFFLinkingContext &ctx, - raw_ostream &diag, - std::string &resFile) { - // Create a temporary file for the resource script file. - SmallString<128> rcFileSmallString; - if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) { - diag << "Cannot create a temporary file\n"; - return false; - } - StringRef rcFile(rcFileSmallString.str()); - llvm::FileRemover rcFileRemover((Twine(rcFile))); - - // Open the temporary file for writing. - std::error_code ec; - llvm::raw_fd_ostream out(rcFileSmallString, ec, llvm::sys::fs::F_Text); - if (ec) { - diag << "Failed to open " << ctx.getManifestOutputPath() << ": " - << ec.message() << "\n"; - return false; - } - - // Write resource script to the RC file. - out << "#define LANG_ENGLISH 9\n" - << "#define SUBLANG_DEFAULT 1\n" - << "#define APP_MANIFEST " << ctx.getManifestId() << "\n" - << "#define RT_MANIFEST 24\n" - << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n" - << "APP_MANIFEST RT_MANIFEST {\n"; - quoteAndPrintXml(out, createManifestXml(ctx)); - out << "}\n"; - out.close(); - - // Create output resource file. - SmallString<128> resFileSmallString; - if (llvm::sys::fs::createTemporaryFile("tmp", "res", resFileSmallString)) { - diag << "Cannot create a temporary file"; - return false; - } - resFile = resFileSmallString.str(); - - // Register the resource file path so that the file will be deleted when the - // context's destructor is called. - ctx.registerTemporaryFile(resFile); - - // Run RC.EXE /fo tmp.res tmp.rc - std::string program = "rc.exe"; - ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); - if (!programPathOrErr) { - diag << "Unable to find " << program << " in PATH\n"; - return false; - } - const std::string &programPath = *programPathOrErr; - std::vector<const char *> args; - args.push_back(programPath.c_str()); - args.push_back("/fo"); - args.push_back(resFile.c_str()); - args.push_back("/nologo"); - args.push_back(rcFileSmallString.c_str()); - args.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { - diag << program << " failed\n"; - return false; - } - return true; -} - - -// Create the a side-by-side manifest file. -// -// The manifest file will convey some information to the linker, such as whether -// the binary needs to run as Administrator or not. Instead of being placed in -// the PE/COFF header, it's in XML format for some reason -- I guess it's -// probably because it's invented in the early dot-com era. -// -// The side-by-side manifest file is a separate XML file having ".manifest" -// extension. It will be created in the same directory as the resulting -// executable. -static bool createSideBySideManifestFile(PECOFFLinkingContext &ctx, - raw_ostream &diag) { - std::string path = ctx.getManifestOutputPath(); - if (path.empty()) { - // Default name of the manifest file is "foo.exe.manifest" where "foo.exe" is - // the output path. - path = ctx.outputPath(); - path.append(".manifest"); - } - - std::error_code ec; - llvm::raw_fd_ostream out(path, ec, llvm::sys::fs::F_Text); - if (ec) { - diag << ec.message() << "\n"; - return false; - } - out << createManifestXml(ctx); - return true; -} - -// Handle /failifmismatch option. -static bool -handleFailIfMismatchOption(StringRef option, - std::map<StringRef, StringRef> &mustMatch, - raw_ostream &diag) { - StringRef key, value; - std::tie(key, value) = option.split('='); - if (key.empty() || value.empty()) { - diag << "error: malformed /failifmismatch option: " << option << "\n"; - return true; - } - auto it = mustMatch.find(key); - if (it != mustMatch.end() && it->second != value) { - diag << "error: mismatch detected: '" << it->second << "' and '" << value - << "' for key '" << key << "'\n"; - return true; - } - mustMatch[key] = value; - return false; -} - -// -// Environment variable -// - -// Process "LINK" environment variable. If defined, the value of the variable -// should be processed as command line arguments. -static std::vector<const char *> processLinkEnv(PECOFFLinkingContext &ctx, - int argc, const char **argv) { - std::vector<const char *> ret; - // The first argument is the name of the command. This should stay at the head - // of the argument list. - assert(argc > 0); - ret.push_back(argv[0]); - - // Add arguments specified by the LINK environment variable. - llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LINK"); - if (env.hasValue()) - for (std::string &arg : splitArgList(*env)) - ret.push_back(ctx.allocate(arg).data()); - - // Add the rest of arguments passed via the command line. - for (int i = 1; i < argc; ++i) - ret.push_back(argv[i]); - ret.push_back(nullptr); - return ret; -} - -// Process "LIB" environment variable. The variable contains a list of search -// paths separated by semicolons. -static void processLibEnv(PECOFFLinkingContext &ctx) { - llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LIB"); - if (env.hasValue()) - for (StringRef path : splitPathList(*env)) - ctx.appendInputSearchPath(ctx.allocate(path)); -} - -namespace { -class DriverStringSaver : public llvm::cl::StringSaver { -public: - DriverStringSaver(PECOFFLinkingContext &ctx) : _ctx(ctx) {} - - const char *SaveString(const char *s) override { - return _ctx.allocate(StringRef(s)).data(); - } - -private: - PECOFFLinkingContext &_ctx; -}; -} - -// Tokenize command line options in a given file and add them to result. -static bool readResponseFile(StringRef path, PECOFFLinkingContext &ctx, - std::vector<const char *> &result) { - ArrayRef<uint8_t> contents; - if (!readFile(ctx, path, contents)) - return false; - StringRef contentsStr(reinterpret_cast<const char *>(contents.data()), - contents.size()); - DriverStringSaver saver(ctx); - SmallVector<const char *, 0> args; - llvm::cl::TokenizeWindowsCommandLine(contentsStr, saver, args); - for (const char *s : args) - result.push_back(s); - return true; -} - -// Expand arguments starting with "@". It's an error if a specified file does -// not exist. Returns true on success. -static bool expandResponseFiles(int &argc, const char **&argv, - PECOFFLinkingContext &ctx, raw_ostream &diag, - bool &expanded) { - std::vector<const char *> newArgv; - for (int i = 0; i < argc; ++i) { - if (argv[i][0] != '@') { - newArgv.push_back(argv[i]); - continue; - } - StringRef filename = StringRef(argv[i] + 1); - if (!readResponseFile(filename, ctx, newArgv)) { - diag << "error: cannot read response file: " << filename << "\n"; - return false; - } - expanded = true; - } - if (!expanded) - return true; - argc = newArgv.size(); - newArgv.push_back(nullptr); - argv = &ctx.allocateCopy(newArgv)[0]; - return true; -} - -// Parses the given command line options and returns the result. Returns NULL if -// there's an error in the options. -static std::unique_ptr<llvm::opt::InputArgList> -parseArgs(int argc, const char **argv, PECOFFLinkingContext &ctx, - raw_ostream &diag, bool isReadingDirectiveSection) { - // Expand arguments starting with "@". - bool expanded = false; - if (!expandResponseFiles(argc, argv, ctx, diag, expanded)) - return nullptr; - - // Parse command line options using WinLinkOptions.td - std::unique_ptr<llvm::opt::InputArgList> parsedArgs; - WinLinkOptTable table; - unsigned missingIndex; - unsigned missingCount; - parsedArgs.reset(table.ParseArgs(&argv[1], &argv[argc], - missingIndex, missingCount)); - if (missingCount) { - diag << "error: missing arg value for '" - << parsedArgs->getArgString(missingIndex) << "' expected " - << missingCount << " argument(s).\n"; - return nullptr; - } - - // Show warning for unknown arguments. In .drectve section, unknown options - // starting with "-?" are silently ignored. This is a COFF's feature to embed a - // new linker option to an object file while keeping backward compatibility. - for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { - StringRef arg = unknownArg->getSpelling(); - if (isReadingDirectiveSection && arg.startswith("-?")) - continue; - diag << "warning: ignoring unknown argument: " << arg << "\n"; - } - - // Copy mllvm - for (auto arg : parsedArgs->filtered(OPT_mllvm)) - ctx.appendLLVMOption(arg->getValue()); - - // If we have expaneded response files and /verbose is given, print out the - // final command line. - if (!isReadingDirectiveSection && expanded && - parsedArgs->getLastArg(OPT_verbose)) { - diag << "Command line:"; - for (int i = 0; i < argc; ++i) - diag << " " << argv[i]; - diag << "\n\n"; - } - - return parsedArgs; -} - -// Returns true if the given file node has already been added to the input -// graph. -static bool hasLibrary(PECOFFLinkingContext &ctx, File *file) { - StringRef path = file->path(); - for (std::unique_ptr<Node> &p : ctx.getNodes()) - if (auto *f = dyn_cast<FileNode>(p.get())) - if (f->getFile()->path() == path) - return true; - return false; -} - -// If the first command line argument is "/lib", link.exe acts as if it's -// "lib.exe" command. This is for backward compatibility. -// http://msdn.microsoft.com/en-us/library/h34w59b3.aspx -static bool maybeRunLibCommand(int argc, const char **argv, raw_ostream &diag) { - if (argc <= 1) - return false; - if (!StringRef(argv[1]).equals_lower("/lib")) - return false; - ErrorOr<std::string> pathOrErr = llvm::sys::findProgramByName("lib.exe"); - if (!pathOrErr) { - diag << "Unable to find lib.exe in PATH\n"; - return true; - } - const std::string &path = *pathOrErr; - - // Run lib.exe - std::vector<const char *> vec; - vec.push_back(path.c_str()); - for (int i = 2; i < argc; ++i) - vec.push_back(argv[i]); - vec.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(path.c_str(), &vec[0]) != 0) - diag << "lib.exe failed\n"; - return true; -} - -/// \brief Parse the input file to lld::File. -void addFiles(PECOFFLinkingContext &ctx, StringRef path, raw_ostream &diag, - std::vector<std::unique_ptr<File>> &files) { - for (std::unique_ptr<File> &file : loadFile(ctx, path, false)) { - if (ctx.logInputFiles()) - diag << file->path() << "\n"; - files.push_back(std::move(file)); - } -} - -// -// Main driver -// - -bool WinLinkDriver::linkPECOFF(int argc, const char **argv, raw_ostream &diag) { - if (maybeRunLibCommand(argc, argv, diag)) - return true; - - PECOFFLinkingContext ctx; - ctx.setParseDirectives(parseDirectives); - ctx.registry().addSupportCOFFObjects(ctx); - ctx.registry().addSupportCOFFImportLibraries(ctx); - ctx.registry().addSupportArchives(ctx.logInputFiles()); - ctx.registry().addSupportNativeObjects(); - ctx.registry().addSupportYamlFiles(); - - std::vector<const char *> newargv = processLinkEnv(ctx, argc, argv); - processLibEnv(ctx); - if (!parse(newargv.size() - 1, &newargv[0], ctx, diag)) - return false; - - // Create the file if needed. - if (ctx.getCreateManifest() && !ctx.getEmbedManifest()) - if (!createSideBySideManifestFile(ctx, diag)) - return false; - - return link(ctx, diag); -} - -bool WinLinkDriver::parse(int argc, const char *argv[], - PECOFFLinkingContext &ctx, raw_ostream &diag, - bool isReadingDirectiveSection) { - // Parse may be called from multiple threads simultaneously to parse .drectve - // sections. This function is not thread-safe because it mutates the context - // object. So acquire the lock. - std::lock_guard<std::recursive_mutex> lock(ctx.getMutex()); - - std::map<StringRef, StringRef> failIfMismatchMap; - // Parse the options. - std::unique_ptr<llvm::opt::InputArgList> parsedArgs = - parseArgs(argc, argv, ctx, diag, isReadingDirectiveSection); - if (!parsedArgs) - return false; - - // The list of input files. - std::vector<std::unique_ptr<File>> files; - std::vector<std::unique_ptr<File>> libraries; - - // Handle /help - if (parsedArgs->hasArg(OPT_help)) { - WinLinkOptTable table; - table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); - return false; - } - - // Handle /machine before parsing all the other options, as the target machine - // type affects how to handle other options. For example, x86 needs the - // leading underscore to mangle symbols, while x64 doesn't need it. - if (llvm::opt::Arg *inputArg = parsedArgs->getLastArg(OPT_machine)) { - StringRef arg = inputArg->getValue(); - llvm::COFF::MachineTypes type = stringToMachineType(arg); - if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) { - diag << "error: unknown machine type: " << arg << "\n"; - return false; - } - ctx.setMachineType(type); - } else { - // If /machine option is missing, we need to take a look at - // the magic byte of the first object file to infer machine type. - std::vector<StringRef> filePaths; - for (auto arg : *parsedArgs) - if (arg->getOption().getID() == OPT_INPUT) - filePaths.push_back(arg->getValue()); - if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_DASH_DASH)) - filePaths.insert(filePaths.end(), arg->getValues().begin(), - arg->getValues().end()); - for (StringRef path : filePaths) { - llvm::COFF::MachineTypes type; - if (!getMachineType(path, type)) - continue; - if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) - continue; - ctx.setMachineType(type); - break; - } - } - - // Handle /nodefaultlib:<lib>. The same option without argument is handled in - // the following for loop. - for (auto *arg : parsedArgs->filtered(OPT_nodefaultlib)) - ctx.addNoDefaultLib(arg->getValue()); - - // Handle /defaultlib. Argument of the option is added to the input file list - // unless it's blacklisted by /nodefaultlib. - std::vector<StringRef> defaultLibs; - for (auto *arg : parsedArgs->filtered(OPT_defaultlib)) - defaultLibs.push_back(arg->getValue()); - - // -alternatename:<alias>=<symbol> - for (auto *arg : parsedArgs->filtered(OPT_alternatename)) { - StringRef weak, def; - if (!parseAlternateName(arg->getValue(), weak, def, diag)) - return false; - ctx.addAlternateName(weak, def); - } - - // Parse /base command line option. The argument for the parameter is in - // the form of "<address>[:<size>]". - if (auto *arg = parsedArgs->getLastArg(OPT_base)) { - uint64_t addr, size; - // Size should be set to SizeOfImage field in the COFF header, and if - // it's smaller than the actual size, the linker should warn about that. - // Currently we just ignore the value of size parameter. - if (!parseMemoryOption(arg->getValue(), addr, size)) - return false; - ctx.setBaseAddress(addr); - } - - // Parse /dll command line option - if (parsedArgs->hasArg(OPT_dll)) { - ctx.setIsDll(true); - // Default base address of a DLL is 0x10000000. - if (!parsedArgs->hasArg(OPT_base)) - ctx.setBaseAddress(0x10000000); - } - - // Parse /stack command line option - if (auto *arg = parsedArgs->getLastArg(OPT_stack)) { - uint64_t reserve; - uint64_t commit = ctx.getStackCommit(); - if (!parseMemoryOption(arg->getValue(), reserve, commit)) - return false; - ctx.setStackReserve(reserve); - ctx.setStackCommit(commit); - } - - // Parse /heap command line option - if (auto *arg = parsedArgs->getLastArg(OPT_heap)) { - uint64_t reserve; - uint64_t commit = ctx.getHeapCommit(); - if (!parseMemoryOption(arg->getValue(), reserve, commit)) - return false; - ctx.setHeapReserve(reserve); - ctx.setHeapCommit(commit); - } - - if (auto *arg = parsedArgs->getLastArg(OPT_align)) { - uint32_t align; - StringRef val = arg->getValue(); - if (val.getAsInteger(10, align)) { - diag << "error: invalid value for /align: " << val << "\n"; - return false; - } - ctx.setSectionDefaultAlignment(align); - } - - if (auto *arg = parsedArgs->getLastArg(OPT_version)) { - uint32_t major, minor; - if (!parseVersion(arg->getValue(), major, minor)) - return false; - ctx.setImageVersion(PECOFFLinkingContext::Version(major, minor)); - } - - // Parse /merge:<from>=<to>. - for (auto *arg : parsedArgs->filtered(OPT_merge)) { - StringRef from, to; - std::tie(from, to) = StringRef(arg->getValue()).split('='); - if (from.empty() || to.empty()) { - diag << "error: malformed /merge option: " << arg->getValue() << "\n"; - return false; - } - if (!ctx.addSectionRenaming(diag, from, to)) - return false; - } - - // Parse /subsystem:<subsystem>[,<majorOSVersion>[.<minorOSVersion>]]. - if (auto *arg = parsedArgs->getLastArg(OPT_subsystem)) { - llvm::COFF::WindowsSubsystem subsystem; - llvm::Optional<uint32_t> major, minor; - if (!parseSubsystem(arg->getValue(), subsystem, major, minor, diag)) - return false; - ctx.setSubsystem(subsystem); - if (major.hasValue()) - ctx.setMinOSVersion(PECOFFLinkingContext::Version(*major, *minor)); - } - - // Parse /section:name,[[!]{DEKPRSW}] - for (auto *arg : parsedArgs->filtered(OPT_section)) { - std::string section; - llvm::Optional<uint32_t> flags, mask; - if (!parseSection(arg->getValue(), section, flags, mask)) { - diag << "Unknown argument for /section: " << arg->getValue() << "\n"; - return false; - } - if (flags.hasValue()) - ctx.setSectionSetMask(section, *flags); - if (mask.hasValue()) - ctx.setSectionClearMask(section, *mask); - } - - // Parse /manifest:EMBED[,ID=#]|NO. - if (auto *arg = parsedArgs->getLastArg(OPT_manifest_colon)) { - bool enable = true; - bool embed = false; - int id = 1; - if (!parseManifest(arg->getValue(), enable, embed, id)) { - diag << "Unknown argument for /manifest: " << arg->getValue() << "\n"; - return false; - } - ctx.setCreateManifest(enable); - ctx.setEmbedManifest(embed); - ctx.setManifestId(id); - } - - // Parse /manifestuac. - if (auto *arg = parsedArgs->getLastArg(OPT_manifestuac)) { - if (StringRef(arg->getValue()).equals_lower("no")) { - ctx.setManifestUAC(false); - } else { - llvm::Optional<std::string> privilegeLevel; - llvm::Optional<std::string> uiAccess; - if (!parseManifestUAC(arg->getValue(), privilegeLevel, uiAccess)) { - diag << "Unknown argument for /manifestuac: " << arg->getValue() - << "\n"; - return false; - } - if (privilegeLevel.hasValue()) - ctx.setManifestLevel(privilegeLevel.getValue()); - if (uiAccess.hasValue()) - ctx.setManifestUiAccess(uiAccess.getValue()); - } - } - - if (auto *arg = parsedArgs->getLastArg(OPT_manifestfile)) - ctx.setManifestOutputPath(ctx.allocate(arg->getValue())); - - // /manifestdependency:<string> option. Note that the argument will be - // embedded to the manifest XML file with no error check, for link.exe - // compatibility. We do not gurantete that the resulting XML file is - // valid. - if (auto *arg = parsedArgs->getLastArg(OPT_manifestdependency)) - ctx.setManifestDependency(ctx.allocate(arg->getValue())); - - for (auto *arg : parsedArgs->filtered(OPT_failifmismatch)) - if (handleFailIfMismatchOption(arg->getValue(), failIfMismatchMap, diag)) - return false; - - if (auto *arg = parsedArgs->getLastArg(OPT_entry)) - ctx.setEntrySymbolName(ctx.allocate(arg->getValue())); - - for (auto *arg : parsedArgs->filtered(OPT_export)) { - PECOFFLinkingContext::ExportDesc desc; - if (!parseExport(arg->getValue(), desc)) { - diag << "Error: malformed /export option: " << arg->getValue() << "\n"; - return false; - } - - // Mangle the symbol name only if it is reading user-supplied command line - // arguments. Because the symbol name in the .drectve section is already - // mangled by the compiler, we shouldn't add a leading underscore in that - // case. It's odd that the command line option has different semantics in - // the .drectve section, but this behavior is needed for compatibility - // with MSVC's link.exe. - if (!isReadingDirectiveSection) - desc.name = ctx.decorateSymbol(desc.name); - ctx.addDllExport(desc); - } - - for (auto *arg : parsedArgs->filtered(OPT_deffile)) { - llvm::BumpPtrAllocator alloc; - std::vector<moduledef::Directive *> dirs; - if (!parseDef(arg->getValue(), alloc, dirs)) { - diag << "Error: invalid module-definition file\n"; - return false; - } - for (moduledef::Directive *dir : dirs) { - if (auto *exp = dyn_cast<moduledef::Exports>(dir)) { - for (PECOFFLinkingContext::ExportDesc desc : exp->getExports()) { - desc.name = ctx.decorateSymbol(desc.name); - ctx.addDllExport(desc); - } - } else if (auto *hs = dyn_cast<moduledef::Heapsize>(dir)) { - ctx.setHeapReserve(hs->getReserve()); - ctx.setHeapCommit(hs->getCommit()); - } else if (auto *lib = dyn_cast<moduledef::Library>(dir)) { - ctx.setIsDll(true); - ctx.setOutputPath(ctx.allocate(lib->getName())); - if (lib->getBaseAddress() && !ctx.getBaseAddress()) - ctx.setBaseAddress(lib->getBaseAddress()); - } else if (auto *name = dyn_cast<moduledef::Name>(dir)) { - if (!name->getOutputPath().empty() && ctx.outputPath().empty()) - ctx.setOutputPath(ctx.allocate(name->getOutputPath())); - if (name->getBaseAddress() && ctx.getBaseAddress()) - ctx.setBaseAddress(name->getBaseAddress()); - } else if (auto *ver = dyn_cast<moduledef::Version>(dir)) { - ctx.setImageVersion(PECOFFLinkingContext::Version( - ver->getMajorVersion(), ver->getMinorVersion())); - } else { - llvm::dbgs() << static_cast<int>(dir->getKind()) << "\n"; - llvm_unreachable("Unknown module-definition directive.\n"); - } - } - } - - for (auto *arg : parsedArgs->filtered(OPT_libpath)) - ctx.appendInputSearchPath(ctx.allocate(arg->getValue())); - - for (auto *arg : parsedArgs->filtered(OPT_opt)) { - std::string val = StringRef(arg->getValue()).lower(); - if (val == "noref") { - ctx.setDeadStripping(false); - } else if (val != "ref" && val != "icf" && val != "noicf" && - val != "lbr" && val != "nolbr" && - !StringRef(val).startswith("icf=")) { - diag << "unknown option for /opt: " << val << "\n"; - return false; - } - } - - // LLD is not yet capable of creating a PDB file, so /debug does not have - // any effect. - // TODO: This should disable dead stripping. Currently we can't do that - // because removal of associative sections depends on dead stripping. - if (parsedArgs->hasArg(OPT_debug)) - ctx.setDebug(true); - - if (parsedArgs->hasArg(OPT_verbose)) - ctx.setLogInputFiles(true); - - // /force and /force:unresolved mean the same thing. We do not currently - // support /force:multiple. - if (parsedArgs->hasArg(OPT_force) || - parsedArgs->hasArg(OPT_force_unresolved)) { - ctx.setAllowRemainingUndefines(true); - } - - if (parsedArgs->hasArg(OPT_fixed)) { - // /fixed is not compatible with /dynamicbase. Check for it. - if (parsedArgs->hasArg(OPT_dynamicbase)) { - diag << "/dynamicbase must not be specified with /fixed\n"; - return false; - } - ctx.setBaseRelocationEnabled(false); - ctx.setDynamicBaseEnabled(false); - } - - // /swaprun:{cd,net} options set IMAGE_FILE_{REMOVABLE,NET}_RUN_FROM_SWAP - // bits in the COFF header, respectively. If one of the bits is on, the - // Windows loader will copy the entire file to swap area then execute it, - // so that the user can eject a CD or disconnect from the network. - if (parsedArgs->hasArg(OPT_swaprun_cd)) - ctx.setSwapRunFromCD(true); - - if (parsedArgs->hasArg(OPT_swaprun_net)) - ctx.setSwapRunFromNet(true); - - if (parsedArgs->hasArg(OPT_profile)) { - // /profile implies /opt:ref, /opt:noicf, /incremental:no and /fixed:no. - ctx.setDeadStripping(true); - ctx.setBaseRelocationEnabled(true); - ctx.setDynamicBaseEnabled(true); - } - - for (auto *arg : parsedArgs->filtered(OPT_implib)) - ctx.setOutputImportLibraryPath(arg->getValue()); - - for (auto *arg : parsedArgs->filtered(OPT_delayload)) { - ctx.addInitialUndefinedSymbol(ctx.getDelayLoadHelperName()); - ctx.addDelayLoadDLL(arg->getValue()); - } - - if (auto *arg = parsedArgs->getLastArg(OPT_stub)) { - ArrayRef<uint8_t> contents; - if (!readFile(ctx, arg->getValue(), contents)) { - diag << "Failed to read DOS stub file " << arg->getValue() << "\n"; - return false; - } - ctx.setDosStub(contents); - } - - for (auto *arg : parsedArgs->filtered(OPT_incl)) - ctx.addInitialUndefinedSymbol(ctx.allocate(arg->getValue())); - - if (parsedArgs->hasArg(OPT_noentry)) - ctx.setHasEntry(false); - - if (parsedArgs->hasArg(OPT_nodefaultlib_all)) - ctx.setNoDefaultLibAll(true); - - if (auto *arg = parsedArgs->getLastArg(OPT_out)) - ctx.setOutputPath(ctx.allocate(arg->getValue())); - - if (auto *arg = parsedArgs->getLastArg(OPT_pdb)) - ctx.setPDBFilePath(arg->getValue()); - - if (auto *arg = parsedArgs->getLastArg(OPT_lldmoduledeffile)) - ctx.setModuleDefinitionFile(arg->getValue()); - - std::vector<StringRef> inputFiles; - for (auto *arg : parsedArgs->filtered(OPT_INPUT)) - inputFiles.push_back(ctx.allocate(arg->getValue())); - -#define BOOLEAN_FLAG(name, setter) \ - if (auto *arg = parsedArgs->getLastArg(OPT_##name, OPT_##name##_no)) \ - ctx.setter(arg->getOption().matches(OPT_##name)); - - BOOLEAN_FLAG(nxcompat, setNxCompat); - BOOLEAN_FLAG(largeaddressaware, setLargeAddressAware); - BOOLEAN_FLAG(allowbind, setAllowBind); - BOOLEAN_FLAG(allowisolation, setAllowIsolation); - BOOLEAN_FLAG(dynamicbase, setDynamicBaseEnabled); - BOOLEAN_FLAG(tsaware, setTerminalServerAware); - BOOLEAN_FLAG(highentropyva, setHighEntropyVA); - BOOLEAN_FLAG(safeseh, setSafeSEH); -#undef BOOLEAN_FLAG - - // Arguments after "--" are interpreted as filenames even if they - // start with a hypen or a slash. This is not compatible with link.exe - // but useful for us to test lld on Unix. - if (llvm::opt::Arg *dashdash = parsedArgs->getLastArg(OPT_DASH_DASH)) - for (const StringRef value : dashdash->getValues()) - inputFiles.push_back(value); - - // Compile Windows resource files to compiled resource file. - if (ctx.getCreateManifest() && ctx.getEmbedManifest() && - !isReadingDirectiveSection) { - std::string resFile; - if (!createManifestResourceFile(ctx, diag, resFile)) - return false; - inputFiles.push_back(ctx.allocate(resFile)); - } - - // A Windows Resource file is not an object file. It contains data, - // such as an icon image, and is not in COFF file format. If resource - // files are given, the linker merge them into one COFF file using - // CVTRES.EXE and then link the resulting file. - { - auto it = std::partition(inputFiles.begin(), inputFiles.end(), - isResoruceFile); - if (it != inputFiles.begin()) { - std::vector<std::string> resFiles(inputFiles.begin(), it); - std::string resObj; - if (!convertResourceFiles(ctx, resFiles, resObj)) { - diag << "Failed to convert resource files\n"; - return false; - } - inputFiles = std::vector<StringRef>(it, inputFiles.end()); - inputFiles.push_back(ctx.allocate(resObj)); - ctx.registerTemporaryFile(resObj); - } - } - - // Prepare objects to add them to the list of input files. - for (StringRef path : inputFiles) { - path = ctx.allocate(path); - if (isLibraryFile(path)) { - addFiles(ctx, getLibraryPath(ctx, path), diag, libraries); - } else { - addFiles(ctx, getObjectPath(ctx, path), diag, files); - } - } - - // If dead-stripping is enabled, we need to add the entry symbol and - // symbols given by /include to the dead strip root set, so that it - // won't be removed from the output. - if (ctx.deadStrip()) - for (const StringRef symbolName : ctx.initialUndefinedSymbols()) - ctx.addDeadStripRoot(symbolName); - - // Add the libraries specified by /defaultlib unless they are already added - // nor blacklisted by /nodefaultlib. - if (!ctx.getNoDefaultLibAll()) - for (const StringRef path : defaultLibs) - if (!ctx.hasNoDefaultLib(path)) - addFiles(ctx, getLibraryPath(ctx, path.lower()), diag, libraries); - - if (files.empty() && !isReadingDirectiveSection) { - diag << "No input files\n"; - return false; - } - - // If /out option was not specified, the default output file name is - // constructed by replacing an extension of the first input file - // with ".exe". - if (ctx.outputPath().empty()) { - StringRef path = files[0]->path(); - ctx.setOutputPath(replaceExtension(ctx, path, ".exe")); - } - - // Add the input files to the linking context. - for (std::unique_ptr<File> &file : files) { - if (isReadingDirectiveSection) { - File *f = file.get(); - ctx.getTaskGroup().spawn([f] { f->parse(); }); - } - ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); - } - - // Add the library group to the linking context. - if (!isReadingDirectiveSection) { - // Add a group-end marker. - ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(0)); - } - - // Add the library files to the library group. - for (std::unique_ptr<File> &file : libraries) { - if (!hasLibrary(ctx, file.get())) { - if (isReadingDirectiveSection) { - File *f = file.get(); - ctx.getTaskGroup().spawn([f] { f->parse(); }); - } - ctx.addLibraryFile(llvm::make_unique<FileNode>(std::move(file))); - } - } - - // Validate the combination of options used. - return ctx.validate(diag); -} - -} // namespace lld diff --git a/lib/Driver/WinLinkModuleDef.cpp b/lib/Driver/WinLinkModuleDef.cpp deleted file mode 100644 index e55a0bc5fe64..000000000000 --- a/lib/Driver/WinLinkModuleDef.cpp +++ /dev/null @@ -1,295 +0,0 @@ -//===- lib/Driver/WinLinkModuleDef.cpp ------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief Windows module definition file parser. -/// -//===----------------------------------------------------------------------===// - -#include "lld/Driver/WinLinkModuleDef.h" -#include "llvm/ADT/StringSwitch.h" - -namespace lld { -namespace moduledef { - -Token Lexer::lex() { - for (;;) { - _buffer = _buffer.trim(); - if (_buffer.empty() || _buffer[0] == '\0') - return Token(Kind::eof, _buffer); - - switch (_buffer[0]) { - case ';': { - size_t end = _buffer.find('\n'); - _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end); - continue; - } - case '=': - _buffer = _buffer.drop_front(); - return Token(Kind::equal, "="); - case ',': - _buffer = _buffer.drop_front(); - return Token(Kind::comma, ","); - case '"': { - size_t end = _buffer.find('"', 1); - Token ret; - if (end == _buffer.npos) { - ret = Token(Kind::identifier, _buffer.substr(1, end)); - _buffer = ""; - } else { - ret = Token(Kind::identifier, _buffer.substr(1, end - 1)); - _buffer = _buffer.drop_front(end + 1); - } - return ret; - } - default: { - size_t end = _buffer.find_first_not_of( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789_.*~+!@#$%^&*()/"); - StringRef word = _buffer.substr(0, end); - Kind kind = llvm::StringSwitch<Kind>(word) - .Case("BASE", Kind::kw_base) - .Case("DATA", Kind::kw_data) - .Case("EXPORTS", Kind::kw_exports) - .Case("HEAPSIZE", Kind::kw_heapsize) - .Case("LIBRARY", Kind::kw_library) - .Case("NAME", Kind::kw_name) - .Case("NONAME", Kind::kw_noname) - .Case("PRIVATE", Kind::kw_private) - .Case("STACKSIZE", Kind::kw_stacksize) - .Case("VERSION", Kind::kw_version) - .Default(Kind::identifier); - _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end); - return Token(kind, word); - } - } - } -} - -void Parser::consumeToken() { - if (_tokBuf.empty()) { - _tok = _lex.lex(); - return; - } - _tok = _tokBuf.back(); - _tokBuf.pop_back(); -} - -bool Parser::consumeTokenAsInt(uint64_t &result) { - consumeToken(); - if (_tok._kind != Kind::identifier) { - ungetToken(); - error(_tok, "Integer expected"); - return false; - } - if (_tok._range.getAsInteger(10, result)) { - error(_tok, "Integer expected"); - return false; - } - return true; -} - -bool Parser::expectAndConsume(Kind kind, Twine msg) { - consumeToken(); - if (_tok._kind != kind) { - error(_tok, msg); - return false; - } - return true; -} - -void Parser::ungetToken() { _tokBuf.push_back(_tok); } - -void Parser::error(const Token &tok, Twine msg) { - _lex.getSourceMgr().PrintMessage( - llvm::SMLoc::getFromPointer(tok._range.data()), llvm::SourceMgr::DK_Error, - msg); -} - -bool Parser::parse(std::vector<Directive *> &ret) { - for (;;) { - Directive *dir = nullptr; - if (!parseOne(dir)) - return false; - if (!dir) - return true; - ret.push_back(dir); - } -} - -bool Parser::parseOne(Directive *&ret) { - consumeToken(); - switch (_tok._kind) { - case Kind::eof: - return true; - case Kind::kw_exports: { - // EXPORTS - std::vector<PECOFFLinkingContext::ExportDesc> exports; - for (;;) { - PECOFFLinkingContext::ExportDesc desc; - if (!parseExport(desc)) - break; - exports.push_back(desc); - } - ret = new (_alloc) Exports(exports); - return true; - } - case Kind::kw_heapsize: { - // HEAPSIZE - uint64_t reserve, commit; - if (!parseMemorySize(reserve, commit)) - return false; - ret = new (_alloc) Heapsize(reserve, commit); - return true; - } - case Kind::kw_library: { - // LIBRARY - std::string name; - uint64_t baseaddr; - if (!parseName(name, baseaddr)) - return false; - if (!StringRef(name).endswith_lower(".dll")) - name.append(".dll"); - ret = new (_alloc) Library(name, baseaddr); - return true; - } - case Kind::kw_stacksize: { - // STACKSIZE - uint64_t reserve, commit; - if (!parseMemorySize(reserve, commit)) - return false; - ret = new (_alloc) Stacksize(reserve, commit); - return true; - } - case Kind::kw_name: { - // NAME - std::string outputPath; - uint64_t baseaddr; - if (!parseName(outputPath, baseaddr)) - return false; - ret = new (_alloc) Name(outputPath, baseaddr); - return true; - } - case Kind::kw_version: { - // VERSION - int major, minor; - if (!parseVersion(major, minor)) - return false; - ret = new (_alloc) Version(major, minor); - return true; - } - default: - error(_tok, Twine("Unknown directive: ") + _tok._range); - return false; - } -} - -bool Parser::parseExport(PECOFFLinkingContext::ExportDesc &result) { - consumeToken(); - if (_tok._kind != Kind::identifier) { - ungetToken(); - return false; - } - result.name = _tok._range; - - consumeToken(); - if (_tok._kind == Kind::equal) { - consumeToken(); - if (_tok._kind != Kind::identifier) - return false; - result.externalName = result.name; - result.name = _tok._range; - } else { - ungetToken(); - } - - for (;;) { - consumeToken(); - if (_tok._kind == Kind::identifier && _tok._range[0] == '@') { - _tok._range.drop_front().getAsInteger(10, result.ordinal); - consumeToken(); - if (_tok._kind == Kind::kw_noname) { - result.noname = true; - } else { - ungetToken(); - } - continue; - } - if (_tok._kind == Kind::kw_data) { - result.isData = true; - continue; - } - if (_tok._kind == Kind::kw_private) { - result.isPrivate = true; - continue; - } - ungetToken(); - return true; - } -} - -// HEAPSIZE/STACKSIZE reserve[,commit] -bool Parser::parseMemorySize(uint64_t &reserve, uint64_t &commit) { - if (!consumeTokenAsInt(reserve)) - return false; - - consumeToken(); - if (_tok._kind != Kind::comma) { - ungetToken(); - commit = 0; - return true; - } - - if (!consumeTokenAsInt(commit)) - return false; - return true; -} - -// NAME [outputPath] [BASE=address] -bool Parser::parseName(std::string &outputPath, uint64_t &baseaddr) { - consumeToken(); - if (_tok._kind == Kind::identifier) { - outputPath = _tok._range; - } else { - outputPath = ""; - ungetToken(); - return true; - } - consumeToken(); - if (_tok._kind == Kind::kw_base) { - if (!expectAndConsume(Kind::equal, "'=' expected")) - return false; - if (!consumeTokenAsInt(baseaddr)) - return false; - } else { - ungetToken(); - baseaddr = 0; - } - return true; -} - -// VERSION major[.minor] -bool Parser::parseVersion(int &major, int &minor) { - consumeToken(); - if (_tok._kind != Kind::identifier) - return false; - StringRef v1, v2; - std::tie(v1, v2) = _tok._range.split('.'); - if (v1.getAsInteger(10, major)) - return false; - if (v2.empty()) { - minor = 0; - } else if (v2.getAsInteger(10, minor)) { - return false; - } - return true; -} - -} // moddef -} // namespace lld diff --git a/lib/Driver/WinLinkOptions.td b/lib/Driver/WinLinkOptions.td deleted file mode 100644 index a545639b5bb2..000000000000 --- a/lib/Driver/WinLinkOptions.td +++ /dev/null @@ -1,120 +0,0 @@ -include "llvm/Option/OptParser.td" - -// link.exe accepts options starting with either a dash or a slash. - -// Flag that takes no arguments. -class F<string name> : Flag<["/", "-", "-?"], name>; - -// Flag that takes one argument after ":". -class P<string name, string help> : - Joined<["/", "-", "-?"], name#":">, HelpText<help>; - -// Boolean flag suffixed by ":no". -multiclass B<string name, string help> { - def "" : F<name>; - def _no : F<name#":no">, HelpText<help>; -} - -def alternatename : P<"alternatename", "Define weak alias">; -def base : P<"base", "Base address of the program">; -def defaultlib : P<"defaultlib", "Add the library to the list of input files">; -def nodefaultlib : P<"nodefaultlib", "Remove a default library">; -def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>; -def entry : P<"entry", "Name of entry point symbol">; -// No help text because /failifmismatch is not intended to be used by the user. -def export : P<"export", "Export a function">; -def failifmismatch : P<"failifmismatch", "">; -def heap : P<"heap", "Size of the heap">; -def align : P<"align", "Section alignment">; -def libpath : P<"libpath", "Additional library search path">; -def mllvm : P<"mllvm", "Options to pass to LLVM">; -def out : P<"out", "Path to file to write output">; -def stack : P<"stack", "Size of the stack">; -def machine : P<"machine", "Specify target platform">; -def version : P<"version", "Specify a version number in the PE header">; -def merge : P<"merge", "Combine sections">; -def section : P<"section", "Specify section attributes">; -def subsystem : P<"subsystem", "Specify subsystem">; -def stub : P<"stub", "Specify DOS stub file">; -def opt : P<"opt", "Control optimizations">; -def implib : P<"implib", "Import library name">; -def delayload : P<"delayload", "Delay loaded DLL name">; -def pdb : P<"pdb", "PDB file path">; - -def manifest : F<"manifest">; -def manifest_colon : P<"manifest", "Create manifest file">; -def manifestuac : P<"manifestuac", "User access control">; -def manifestfile : P<"manifestfile", "Manifest file path">; -def manifestdependency : P<"manifestdependency", - "Attributes for <dependency> in manifest file">; - -// We cannot use multiclass P because class name "incl" is different -// from its command line option name. We do this because "include" is -// a reserved keyword in tablegen. -def incl : Joined<["/", "-"], "include:">, - HelpText<"Force symbol to be added to symbol table as undefined one">; - -// "def" is also a keyword. -def deffile : Joined<["/", "-"], "def:">, - HelpText<"Use module-definition file">; - -def nodefaultlib_all : F<"nodefaultlib">; -def noentry : F<"noentry">; -def dll : F<"dll">; -def verbose : F<"verbose">; -def debug : F<"debug">; -def swaprun_cd : F<"swaprun:cd">; -def swaprun_net : F<"swaprun:net">; -def profile : F<"profile">; - -def force : F<"force">, - HelpText<"Allow undefined symbols when creating executables">; -def force_unresolved : F<"force:unresolved">; - -defm nxcompat : B<"nxcompat", "Disable data execution provention">; -defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">; -defm allowbind: B<"allowbind", "Disable DLL binding">; -defm fixed : B<"fixed", "Enable base relocations">; -defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">; -defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">; -defm dynamicbase : B<"dynamicbase", - "Disable address space layout randomization">; -defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">; -defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">; - -def help : F<"help">; -def help_q : Flag<["/?", "-?"], "">, Alias<help>; - -def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>; - -// Flag for debug -def lldmoduledeffile : Joined<["/", "-"], "lldmoduledeffile:">; - -//============================================================================== -// The flags below do nothing. They are defined only for link.exe compatibility. -//============================================================================== - -class QF<string name> : Joined<["/", "-", "-?"], name#":">; - -multiclass QB<string name> { - def "" : F<name>; - def _no : F<name#":no">; -} - -def functionpadmin : F<"functionpadmin">; -def ignoreidl : F<"ignoreidl">; -def incremental : F<"incremental">; -def no_incremental : F<"incremental:no">; -def nologo : F<"nologo">; - -def delay : QF<"delay">; -def errorreport : QF<"errorreport">; -def idlout : QF<"idlout">; -def ignore : QF<"ignore">; -def maxilksize : QF<"maxilksize">; -def pdbaltpath : QF<"pdbaltpath">; -def tlbid : QF<"tlbid">; -def tlbout : QF<"tlbout">; -def verbose_all : QF<"verbose">; - -defm wx : QB<"wx">; diff --git a/lib/Makefile b/lib/Makefile deleted file mode 100644 index 83112eaf972a..000000000000 --- a/lib/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- lib/Makefile ----------------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -LLD_LEVEL := .. - -# ARCMigrate and Rewrite are always needed because of libclang. -PARALLEL_DIRS = Config Core Driver ReaderWriter - -include $(LLD_LEVEL)/../../Makefile.config - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/CMakeLists.txt b/lib/ReaderWriter/CMakeLists.txt index 1fd19eb73a75..588f0d85a586 100644 --- a/lib/ReaderWriter/CMakeLists.txt +++ b/lib/ReaderWriter/CMakeLists.txt @@ -1,7 +1,5 @@ add_subdirectory(ELF) add_subdirectory(MachO) -add_subdirectory(Native) -add_subdirectory(PECOFF) add_subdirectory(YAML) if (MSVC) diff --git a/lib/ReaderWriter/CoreLinkingContext.cpp b/lib/ReaderWriter/CoreLinkingContext.cpp index 86fad4f6e77d..02f6263c0c3f 100644 --- a/lib/ReaderWriter/CoreLinkingContext.cpp +++ b/lib/ReaderWriter/CoreLinkingContext.cpp @@ -14,139 +14,19 @@ #include "lld/Core/Simple.h" #include "lld/ReaderWriter/CoreLinkingContext.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" using namespace lld; namespace { -/// \brief Simple atom created by the stubs pass. -class TestingStubAtom : public DefinedAtom { -public: - TestingStubAtom(const File &F, const Atom &) : _file(F) { - static uint32_t lastOrdinal = 0; - _ordinal = lastOrdinal++; - } - - const File &file() const override { return _file; } - - StringRef name() const override { return StringRef(); } - - uint64_t ordinal() const override { return _ordinal; } - - uint64_t size() const override { return 0; } - - Scope scope() const override { return DefinedAtom::scopeLinkageUnit; } - - Interposable interposable() const override { return DefinedAtom::interposeNo; } - - Merge merge() const override { return DefinedAtom::mergeNo; } - - ContentType contentType() const override { return DefinedAtom::typeStub; } - - Alignment alignment() const override { return Alignment(0, 0); } - - SectionChoice sectionChoice() const override { - return DefinedAtom::sectionBasedOnContent; - } - - StringRef customSectionName() const override { return StringRef(); } - - DeadStripKind deadStrip() const override { - return DefinedAtom::deadStripNormal; - } - - ContentPermissions permissions() const override { - return DefinedAtom::permR_X; - } - - ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } - - reference_iterator begin() const override { - return reference_iterator(*this, nullptr); - } - - reference_iterator end() const override { - return reference_iterator(*this, nullptr); - } - - const Reference *derefIterator(const void *iter) const override { - return nullptr; - } - - void incrementIterator(const void *&iter) const override {} - -private: - const File &_file; - uint32_t _ordinal; -}; - -/// \brief Simple atom created by the GOT pass. -class TestingGOTAtom : public DefinedAtom { -public: - TestingGOTAtom(const File &F, const Atom &) : _file(F) { - static uint32_t lastOrdinal = 0; - _ordinal = lastOrdinal++; - } - - const File &file() const override { return _file; } - - StringRef name() const override { return StringRef(); } - - uint64_t ordinal() const override { return _ordinal; } - - uint64_t size() const override { return 0; } - - Scope scope() const override { return DefinedAtom::scopeLinkageUnit; } - - Interposable interposable() const override { return DefinedAtom::interposeNo; } - - Merge merge() const override { return DefinedAtom::mergeNo; } - - ContentType contentType() const override { return DefinedAtom::typeGOT; } - - Alignment alignment() const override { return Alignment(3, 0); } - - SectionChoice sectionChoice() const override { - return DefinedAtom::sectionBasedOnContent; - } - - StringRef customSectionName() const override { return StringRef(); } - - DeadStripKind deadStrip() const override { - return DefinedAtom::deadStripNormal; - } - - ContentPermissions permissions() const override { - return DefinedAtom::permRW_; - } - - ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } - - reference_iterator begin() const override { - return reference_iterator(*this, nullptr); - } - - reference_iterator end() const override { - return reference_iterator(*this, nullptr); - } - - const Reference *derefIterator(const void *iter) const override { - return nullptr; - } - - void incrementIterator(const void *&iter) const override {} - -private: - const File &_file; - uint32_t _ordinal; -}; - class OrderPass : public Pass { public: /// Sorts atoms by position - void perform(std::unique_ptr<MutableFile> &file) override { - MutableFile::DefinedAtomRange defined = file->definedAtoms(); + std::error_code perform(SimpleFile &file) override { + SimpleFile::DefinedAtomRange defined = file.definedAtoms(); std::sort(defined.begin(), defined.end(), DefinedAtom::compareByPosition); + return std::error_code(); } }; @@ -161,10 +41,9 @@ bool CoreLinkingContext::validateImpl(raw_ostream &) { void CoreLinkingContext::addPasses(PassManager &pm) { for (StringRef name : _passNames) { - if (name.equals("order")) - pm.add(std::unique_ptr<Pass>(new OrderPass())); - else - llvm_unreachable("bad pass name"); + (void)name; + assert(name == "order" && "bad pass name"); + pm.add(llvm::make_unique<OrderPass>()); } } diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h index 12ba52a38f38..73864d2b4c38 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h @@ -16,51 +16,27 @@ namespace lld { namespace elf { -template <class ELFT> -class AArch64DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +class AArch64DynamicLibraryWriter : public DynamicLibraryWriter<ELF64LE> { public: - AArch64DynamicLibraryWriter(AArch64LinkingContext &context, - AArch64TargetLayout<ELFT> &layout); + AArch64DynamicLibraryWriter(AArch64LinkingContext &ctx, + TargetLayout<ELF64LE> &layout); protected: // Add any runtime files and their atoms to the output - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); - - virtual void finalizeDefaultAtomValues() { - return DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); - } - - virtual void addDefaultAtoms() { - return DynamicLibraryWriter<ELFT>::addDefaultAtoms(); - } - -private: - class GOTFile : public SimpleFile { - public: - GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} - llvm::BumpPtrAllocator _alloc; - }; - - std::unique_ptr<GOTFile> _gotFile; - AArch64LinkingContext &_context; - AArch64TargetLayout<ELFT> &_AArch64Layout; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; -template <class ELFT> -AArch64DynamicLibraryWriter<ELFT>::AArch64DynamicLibraryWriter( - AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) - : DynamicLibraryWriter<ELFT>(context, layout), - _gotFile(new GOTFile(context)), _context(context), - _AArch64Layout(layout) {} +AArch64DynamicLibraryWriter::AArch64DynamicLibraryWriter( + AArch64LinkingContext &ctx, TargetLayout<ELF64LE> &layout) + : DynamicLibraryWriter(ctx, layout) {} -template <class ELFT> -bool AArch64DynamicLibraryWriter<ELFT>::createImplicitFiles( +void AArch64DynamicLibraryWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - DynamicLibraryWriter<ELFT>::createImplicitFiles(result); - _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); - _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); - result.push_back(std::move(_gotFile)); - return true; + DynamicLibraryWriter::createImplicitFiles(result); + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) GlobalOffsetTableAtom(*gotFile)); + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h deleted file mode 100644 index 9d5207c1c4b4..000000000000 --- a/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h ----------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H -#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H - -#include "ELFReader.h" - -namespace lld { -namespace elf { - -class AArch64LinkingContext; - -template <class ELFT> class AArch64ELFFile : public ELFFile<ELFT> { -public: - AArch64ELFFile(std::unique_ptr<MemoryBuffer> mb, AArch64LinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} - - static ErrorOr<std::unique_ptr<AArch64ELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, AArch64LinkingContext &ctx) { - return std::unique_ptr<AArch64ELFFile<ELFT>>( - new AArch64ELFFile<ELFT>(std::move(mb), ctx)); - } -}; - -template <class ELFT> class AArch64DynamicFile : public DynamicFile<ELFT> { -public: - AArch64DynamicFile(const AArch64LinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} -}; - -} // elf -} // lld - -#endif // LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h deleted file mode 100644 index 05f312db3e7b..000000000000 --- a/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h --------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H -#define LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H - -#include "AArch64ELFFile.h" -#include "ELFReader.h" - -namespace lld { -namespace elf { - -typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; - -struct AArch64DynamicFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - AArch64LinkingContext &ctx) { - return lld::elf::AArch64DynamicFile<ELFT>::create(std::move(mb), ctx); - } -}; - -struct AArch64ELFFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - AArch64LinkingContext &ctx) { - return lld::elf::AArch64ELFFile<ELFT>::create(std::move(mb), ctx); - } -}; - -class AArch64ELFObjectReader - : public ELFObjectReader<AArch64ELFType, AArch64ELFFileCreateELFTraits, - AArch64LinkingContext> { -public: - AArch64ELFObjectReader(AArch64LinkingContext &ctx) - : ELFObjectReader<AArch64ELFType, AArch64ELFFileCreateELFTraits, - AArch64LinkingContext>(ctx, llvm::ELF::EM_AARCH64) {} -}; - -class AArch64ELFDSOReader - : public ELFDSOReader<AArch64ELFType, AArch64DynamicFileCreateELFTraits, - AArch64LinkingContext> { -public: - AArch64ELFDSOReader(AArch64LinkingContext &ctx) - : ELFDSOReader<AArch64ELFType, AArch64DynamicFileCreateELFTraits, - AArch64LinkingContext>(ctx, llvm::ELF::EM_AARCH64) {} -}; - -} // namespace elf -} // namespace lld - -#endif // LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.cpp new file mode 100644 index 000000000000..9a9ec6cba12b --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.cpp @@ -0,0 +1,52 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64LinkingContext.h" +#include "AArch64ExecutableWriter.h" +#include "AArch64TargetHandler.h" +#include "AArch64SectionChunks.h" + +namespace lld { +namespace elf { + +AArch64ExecutableWriter::AArch64ExecutableWriter(AArch64LinkingContext &ctx, + AArch64TargetLayout &layout) + : ExecutableWriter(ctx, layout), _targetLayout(layout) {} + +void AArch64ExecutableWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter::createImplicitFiles(result); + auto gotFile = llvm::make_unique<SimpleFile>("GOTFile"); + gotFile->addAtom(*new (gotFile->allocator()) GlobalOffsetTableAtom(*gotFile)); + if (this->_ctx.isDynamic()) + gotFile->addAtom(*new (gotFile->allocator()) DynamicAtom(*gotFile)); + result.push_back(std::move(gotFile)); +} + +void AArch64ExecutableWriter::buildDynamicSymbolTable(const File &file) { + for (auto sec : this->_layout.sections()) { + if (auto section = dyn_cast<AtomSection<ELF64LE>>(sec)) { + for (const auto &atom : section->atoms()) { + // Add all globals GOT symbols (in both .got and .got.plt sections) + // on dynamic symbol table. + for (const auto §ion : _targetLayout.getGOTSections()) { + if (section->hasGlobalGOTEntry(atom->_atom)) + _dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + } + } + } + + ExecutableWriter<ELF64LE>::buildDynamicSymbolTable(file); +} + +} // namespace elf +} // namespace lld + diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h index 73963f56ef70..eef825040ffa 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h @@ -9,59 +9,29 @@ #ifndef AARCH64_EXECUTABLE_WRITER_H #define AARCH64_EXECUTABLE_WRITER_H -#include "AArch64LinkingContext.h" #include "ExecutableWriter.h" namespace lld { namespace elf { -template <class ELFT> -class AArch64ExecutableWriter : public ExecutableWriter<ELFT> { +class AArch64TargetLayout; +class AArch64LinkingContext; + +class AArch64ExecutableWriter : public ExecutableWriter<ELF64LE> { public: - AArch64ExecutableWriter(AArch64LinkingContext &context, - AArch64TargetLayout<ELFT> &layout); + AArch64ExecutableWriter(AArch64LinkingContext &ctx, + AArch64TargetLayout &layout); protected: // Add any runtime files and their atoms to the output - bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - - void finalizeDefaultAtomValues() override { - return ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); - } + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - void addDefaultAtoms() override{ - return ExecutableWriter<ELFT>::addDefaultAtoms(); - } + void buildDynamicSymbolTable(const File &file) override; private: - class GOTFile : public SimpleFile { - public: - GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} - llvm::BumpPtrAllocator _alloc; - }; - - std::unique_ptr<GOTFile> _gotFile; - AArch64LinkingContext &_context; - AArch64TargetLayout<ELFT> &_AArch64Layout; + AArch64TargetLayout &_targetLayout; }; -template <class ELFT> -AArch64ExecutableWriter<ELFT>::AArch64ExecutableWriter( - AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(context, layout), _gotFile(new GOTFile(context)), - _context(context), _AArch64Layout(layout) {} - -template <class ELFT> -bool AArch64ExecutableWriter<ELFT>::createImplicitFiles( - std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); - _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); - if (_context.isDynamic()) - _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); - result.push_back(std::move(_gotFile)); - return true; -} - } // namespace elf } // namespace lld diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp index 9eb98f447709..ba883f7f59db 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp @@ -12,22 +12,34 @@ #include "AArch64TargetHandler.h" using namespace lld; +using namespace lld::elf; std::unique_ptr<ELFLinkingContext> -elf::AArch64LinkingContext::create(llvm::Triple triple) { +elf::createAArch64LinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::aarch64) - return std::unique_ptr<ELFLinkingContext>( - new elf::AArch64LinkingContext(triple)); + return llvm::make_unique<AArch64LinkingContext>(triple); return nullptr; } -elf::AArch64LinkingContext::AArch64LinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( - new AArch64TargetHandler(*this))) {} +AArch64LinkingContext::AArch64LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandler>( + new AArch64TargetHandler(*this))) {} -void elf::AArch64LinkingContext::addPasses(PassManager &pm) { +void AArch64LinkingContext::addPasses(PassManager &pm) { auto pass = createAArch64RelocationPass(*this); if (pass) pm.add(std::move(pass)); ELFLinkingContext::addPasses(pm); } + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/AArch64.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void AArch64LinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::AArch64, kindStrings); +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h index ebd91fe0a95b..25a173158318 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h @@ -24,10 +24,11 @@ enum { class AArch64LinkingContext final : public ELFLinkingContext { public: - static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_AARCH64; } AArch64LinkingContext(llvm::Triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; uint64_t getBaseAddress() const override { if (_baseAddress == 0) @@ -88,6 +89,11 @@ public: return false; } } + + /// \brief The path to the dynamic interpreter + StringRef getDefaultInterpreter() const override { + return "/lib/ld-linux-aarch64.so.1"; + } }; } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp index d1ecc7fa884b..ac7c769ec26d 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp @@ -13,11 +13,14 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/MathExtras.h" +#define DEBUG_TYPE "AArch64" + using namespace lld; using namespace lld::elf; +using namespace llvm; using namespace llvm::support::endian; -#define PAGE(X) ((X) & ~0x0FFFL) +static int64_t page(int64_t v) { return v & ~int64_t(0xFFF); } /// \brief Check X is in the interval (-2^(bits-1), 2^bits] static bool withinSignedUnsignedRange(int64_t X, int bits) { @@ -28,77 +31,130 @@ static bool withinSignedUnsignedRange(int64_t X, int bits) { static void relocR_AARCH64_ABS64(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { int64_t result = (int64_t)S + A; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); write64le(location, result | read64le(location)); } -/// \brief R_AARCH64_PREL32 - word32: S + A - P -static void relocR_AARCH64_PREL32(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { - int32_t result = (int32_t)((S + A) - P); - write32le(location, result + (int32_t)read32le(location)); -} - /// \brief R_AARCH64_ABS32 - word32: S + A static std::error_code relocR_AARCH64_ABS32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { int64_t result = S + A; if (!withinSignedUnsignedRange(result, 32)) return make_out_of_range_reloc_error(); - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); return std::error_code(); } -/// \brief R_AARCH64_ADR_PREL_PG_HI21 - Page(S+A) - Page(P) -static void relocR_AARCH64_ADR_PREL_PG_HI21(uint8_t *location, uint64_t P, +/// \brief R_AARCH64_ABS16 - word16: S + A +static std::error_code relocR_AARCH64_ABS16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { - uint64_t result = (PAGE(S + A) - PAGE(P)); + int64_t result = S + A; + if (!withinSignedUnsignedRange(result, 16)) + return make_out_of_range_reloc_error(); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + write16le(location, result | read16le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_PREL64 - word64: S + A - P +static void relocR_AARCH64_PREL64(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + write64le(location, result + read64le(location)); +} + +/// \brief R_AARCH64_PREL32 - word32: S + A - P +static std::error_code relocR_AARCH64_PREL32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + // ELF for the ARM 64-bit architecture manual states the overflow + // for R_AARCH64_PREL32 to be -2^(-31) <= X < 2^32 + if (!withinSignedUnsignedRange(result, 32)) + return make_out_of_range_reloc_error(); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + write32le(location, result + read32le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_PREL16 - word16: S + A - P +static std::error_code relocR_AARCH64_PREL16(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + if (!withinSignedUnsignedRange(result, 16)) + return make_out_of_range_reloc_error(); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + write16le(location, result + read16le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_ADR_PREL_PG_HI21 - Page(S+A) - Page(P) +static std::error_code relocR_AARCH64_ADR_PREL_PG_HI21(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int64_t result = page(S + A) - page(P); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); result = result >> 12; uint32_t immlo = result & 0x3; uint32_t immhi = result & 0x1FFFFC; immlo = immlo << 29; immhi = immhi << 3; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); - llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, immlo | immhi | read32le(location)); - // TODO: Make sure this is correct! + return std::error_code(); } /// \brief R_AARCH64_ADR_PREL_LO21 - S + A - P -static void relocR_AARCH64_ADR_PREL_LO21(uint8_t *location, uint64_t P, - uint64_t S, int64_t A) { - uint64_t result = (S + A) - P; +static std::error_code relocR_AARCH64_ADR_PREL_LO21(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = S + A - P; + if (!isInt<20>(result)) + return make_out_of_range_reloc_error(); uint32_t immlo = result & 0x3; uint32_t immhi = result & 0x1FFFFC; immlo = immlo << 29; immhi = immhi << 3; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); - llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, immlo | immhi | read32le(location)); - // TODO: Make sure this is correct! + return std::error_code(); } /// \brief R_AARCH64_ADD_ABS_LO12_NC @@ -106,41 +162,46 @@ static void relocR_AARCH64_ADD_ABS_LO12_NC(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { int32_t result = (int32_t)((S + A) & 0xFFF); result <<= 10; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } -static void relocJump26(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { - int32_t result = (int32_t)((S + A) - P); +/// \brief R_AARCH64_CALL26 and R_AARCH64_JUMP26 +static std::error_code relocJump26(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int64_t result = S + A - P; + if (!isInt<27>(result)) + return make_out_of_range_reloc_error(); result &= 0x0FFFFFFC; result >>= 2; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); + return std::error_code(); } /// \brief R_AARCH64_CONDBR19 -static void relocR_AARCH64_CONDBR19(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { - int32_t result = (int32_t)((S + A) - P); +static std::error_code relocR_AARCH64_CONDBR19(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A - P; + if (!isInt<20>(result)) + return make_out_of_range_reloc_error(); result &= 0x01FFFFC; result <<= 3; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); + return std::error_code(); } /// \brief R_AARCH64_LDST8_ABS_LO12_NC - S + A @@ -148,12 +209,11 @@ static void relocR_AARCH64_LDST8_ABS_LO12_NC(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { int32_t result = (int32_t)((S + A) & 0xFFF); result <<= 10; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } @@ -163,12 +223,11 @@ static void relocR_AARCH64_LDST16_ABS_LO12_NC(uint8_t *location, uint64_t P, int32_t result = (int32_t)(S + A); result &= 0x0FFC; result <<= 9; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } @@ -178,12 +237,11 @@ static void relocR_AARCH64_LDST32_ABS_LO12_NC(uint8_t *location, uint64_t P, int32_t result = (int32_t)(S + A); result &= 0x0FFC; result <<= 8; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } @@ -193,12 +251,11 @@ static void relocR_AARCH64_LDST64_ABS_LO12_NC(uint8_t *location, uint64_t P, int32_t result = (int32_t)(S + A); result &= 0x0FF8; result <<= 7; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } @@ -208,83 +265,89 @@ static void relocR_AARCH64_LDST128_ABS_LO12_NC(uint8_t *location, uint64_t P, int32_t result = (int32_t)(S + A); result &= 0x0FF8; result <<= 6; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } -static void relocR_AARCH64_ADR_GOT_PAGE(uint8_t *location, uint64_t P, - uint64_t S, int64_t A) { - uint64_t result = PAGE(S + A) - PAGE(P); - result >>= 12; +static std::error_code relocR_AARCH64_ADR_GOT_PAGE(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + uint64_t result = page(S + A) - page(P); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); + result = (result >> 12) & 0x3FFFF; uint32_t immlo = result & 0x3; uint32_t immhi = result & 0x1FFFFC; immlo = immlo << 29; immhi = immhi << 3; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); - llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); - write32le(location, result | read32le(location)); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); + return std::error_code(); } // R_AARCH64_LD64_GOT_LO12_NC -static void relocR_AARCH64_LD64_GOT_LO12_NC(uint8_t *location, uint64_t P, - uint64_t S, int64_t A) { +static std::error_code relocR_AARCH64_LD64_GOT_LO12_NC(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { int32_t result = S + A; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + if ((result & 0x7) != 0) + return make_unaligned_range_reloc_error(); result &= 0xFF8; result <<= 7; write32le(location, result | read32le(location)); + return std::error_code(); } // ADD_AARCH64_GOTRELINDEX static void relocADD_AARCH64_GOTRELINDEX(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { int32_t result = S + A; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); result &= 0xFFF; result <<= 10; write32le(location, result | read32le(location)); } // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 -static void relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(uint8_t *location, - uint64_t P, uint64_t S, - int64_t A) { - int64_t result = PAGE(S + A) - PAGE(P); +static std::error_code relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(uint8_t *location, + uint64_t P, + uint64_t S, + int64_t A) { + int64_t result = page(S + A) - page(P); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); result >>= 12; uint32_t immlo = result & 0x3; uint32_t immhi = result & 0x1FFFFC; immlo = immlo << 29; immhi = immhi << 3; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); - llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, immlo | immhi | read32le(location)); + return std::error_code(); } // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC @@ -294,28 +357,31 @@ static void relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(uint8_t *location, int32_t result = S + A; result &= 0xFF8; result <<= 7; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } /// \brief R_AARCH64_TLSLE_ADD_TPREL_HI12 -static void relocR_AARCH64_TLSLE_ADD_TPREL_HI12(uint8_t *location, uint64_t P, - uint64_t S, int64_t A) { - int32_t result = S + A; +static std::error_code relocR_AARCH64_TLSLE_ADD_TPREL_HI12(uint8_t *location, + uint64_t P, + uint64_t S, + int64_t A) { + int64_t result = S + A; + if (!isUInt<24>(result)) + return make_out_of_range_reloc_error(); result &= 0x0FFF000; result >>= 2; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); + return std::error_code(); } /// \brief R_AARCH64_TLSLE_ADD_TPREL_LO12_NC @@ -325,22 +391,76 @@ static void relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(uint8_t *location, int32_t result = S + A; result &= 0x0FFF; result <<= 10; - DEBUG_WITH_TYPE( - "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: " << Twine::utohexstr(S); - llvm::dbgs() << " A: " << Twine::utohexstr(A); - llvm::dbgs() << " P: " << Twine::utohexstr(P); - llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_TLSDESC_ADR_PAGE21 - Page(G(GTLSDESC(S+A))) - Page(P) +static std::error_code relocR_AARCH64_TLSDESC_ADR_PAGE21(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int64_t result = page(S + A) - page(P); + if (!isInt<32>(result)) + return make_out_of_range_reloc_error(); + result = result >> 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_TLSDESC_LD64_LO12_NC - G(GTLSDESC(S+A)) -> S + A +static std::error_code relocR_AARCH64_TLSDESC_LD64_LO12_NC(uint8_t *location, + uint64_t P, + uint64_t S, + int64_t A) { + int32_t result = S + A; + DEBUG(llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + if ((result & 0x7) != 0) + return make_unaligned_range_reloc_error(); + result &= 0xFF8; + result <<= 7; + write32le(location, result | read32le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_TLSDESC_ADD_LO12_NC - G(GTLSDESC(S+A)) -> S + A +static void relocR_AARCH64_TLSDESC_ADD_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) & 0xFFF); + result <<= 10; + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); write32le(location, result | read32le(location)); } std::error_code AArch64TargetRelocationHandler::applyRelocation( - ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom, const Reference &ref) const { uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; - uint8_t *location = atomContent + ref.offsetInAtom(); - uint64_t targetVAddress = writer.addressOfAtom(ref.target()); - uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); + int64_t addend = ref.addend(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); @@ -349,92 +469,88 @@ std::error_code AArch64TargetRelocationHandler::applyRelocation( case R_AARCH64_NONE: break; case R_AARCH64_ABS64: - relocR_AARCH64_ABS64(location, relocVAddress, targetVAddress, ref.addend()); - break; - case R_AARCH64_PREL32: - relocR_AARCH64_PREL32(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_ABS64(loc, reloc, target, addend); break; case R_AARCH64_ABS32: - return relocR_AARCH64_ABS32(location, relocVAddress, targetVAddress, - ref.addend()); - // Runtime only relocations. Ignore here. - case R_AARCH64_RELATIVE: - case R_AARCH64_IRELATIVE: - case R_AARCH64_JUMP_SLOT: - case R_AARCH64_GLOB_DAT: + return relocR_AARCH64_ABS32(loc, reloc, target, addend); + case R_AARCH64_ABS16: + return relocR_AARCH64_ABS16(loc, reloc, target, addend); + case R_AARCH64_PREL64: + relocR_AARCH64_PREL64(loc, reloc, target, addend); break; + case R_AARCH64_PREL32: + return relocR_AARCH64_PREL32(loc, reloc, target, addend); + case R_AARCH64_PREL16: + return relocR_AARCH64_PREL16(loc, reloc, target, addend); case R_AARCH64_ADR_PREL_PG_HI21: - relocR_AARCH64_ADR_PREL_PG_HI21(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_ADR_PREL_PG_HI21(loc, reloc, target, addend); case R_AARCH64_ADR_PREL_LO21: - relocR_AARCH64_ADR_PREL_LO21(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_ADR_PREL_LO21(loc, reloc, target, addend); case R_AARCH64_ADD_ABS_LO12_NC: - relocR_AARCH64_ADD_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_ADD_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_CALL26: case R_AARCH64_JUMP26: - relocJump26(location, relocVAddress, targetVAddress, ref.addend()); - break; + return relocJump26(loc, reloc, target, addend); case R_AARCH64_CONDBR19: - relocR_AARCH64_CONDBR19(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_CONDBR19(loc, reloc, target, addend); case R_AARCH64_ADR_GOT_PAGE: - relocR_AARCH64_ADR_GOT_PAGE(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_ADR_GOT_PAGE(loc, reloc, target, addend); case R_AARCH64_LD64_GOT_LO12_NC: - relocR_AARCH64_LD64_GOT_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); - break; + return relocR_AARCH64_LD64_GOT_LO12_NC(loc, reloc, target, addend); case R_AARCH64_LDST8_ABS_LO12_NC: - relocR_AARCH64_LDST8_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST8_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST16_ABS_LO12_NC: - relocR_AARCH64_LDST16_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST16_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST32_ABS_LO12_NC: - relocR_AARCH64_LDST32_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST32_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST64_ABS_LO12_NC: - relocR_AARCH64_LDST64_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST64_ABS_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_LDST128_ABS_LO12_NC: - relocR_AARCH64_LDST128_ABS_LO12_NC(location, relocVAddress, targetVAddress, - ref.addend()); + relocR_AARCH64_LDST128_ABS_LO12_NC(loc, reloc, target, addend); break; case ADD_AARCH64_GOTRELINDEX: - relocADD_AARCH64_GOTRELINDEX(location, relocVAddress, targetVAddress, - ref.addend()); + relocADD_AARCH64_GOTRELINDEX(loc, reloc, target, addend); break; case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: - relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(location, relocVAddress, - targetVAddress, ref.addend()); - break; + return relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(loc, reloc, target, addend); case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: - relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(location, relocVAddress, - targetVAddress, ref.addend()); + relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(loc, reloc, target, addend); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - relocR_AARCH64_TLSLE_ADD_TPREL_HI12(location, relocVAddress, targetVAddress, - ref.addend()); + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: { + auto tpoffset = _layout.getTPOffset(); + if (ref.kindValue() == R_AARCH64_TLSLE_ADD_TPREL_HI12) + return relocR_AARCH64_TLSLE_ADD_TPREL_HI12(loc, reloc, target + tpoffset, + addend); + else + relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(loc, reloc, target + tpoffset, + addend); + } break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + return relocR_AARCH64_TLSDESC_ADR_PAGE21(loc, reloc, target, addend); + case R_AARCH64_TLSDESC_LD64_LO12_NC: + return relocR_AARCH64_TLSDESC_LD64_LO12_NC(loc, reloc, target, addend); + case R_AARCH64_TLSDESC_ADD_LO12_NC: + relocR_AARCH64_TLSDESC_ADD_LO12_NC(loc, reloc, target, addend); + break; + case R_AARCH64_TLSDESC_CALL: + // Relaxation only to optimize TLS access. Ignore for now. break; - case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: - relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(location, relocVAddress, - targetVAddress, ref.addend()); + // Runtime only relocations. Ignore here. + case R_AARCH64_RELATIVE: + case R_AARCH64_IRELATIVE: + case R_AARCH64_JUMP_SLOT: + case R_AARCH64_GLOB_DAT: + case R_AARCH64_TLS_TPREL64: + case R_AARCH64_TLSDESC: break; default: return make_unhandled_reloc_error(); } - return std::error_code(); } diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h index b1d3c09dc936..8cde7a03e51a 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h @@ -10,21 +10,24 @@ #ifndef AARCH64_RELOCATION_HANDLER_H #define AARCH64_RELOCATION_HANDLER_H -#include "AArch64TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; -template <class ELFT> class AArch64TargetLayout; +class AArch64TargetLayout; class AArch64TargetRelocationHandler final : public TargetRelocationHandler { public: + AArch64TargetRelocationHandler(AArch64TargetLayout &layout) + : _layout(layout) {} + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; - static const Registry::KindStrings kindStrings[]; +private: + AArch64TargetLayout &_layout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp index 0bd12958b27b..4d94a793665c 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp @@ -28,52 +28,79 @@ using namespace lld; using namespace lld::elf; using namespace llvm::ELF; -namespace { // .got values -const uint8_t AArch64GotAtomContent[8] = {0}; +static const uint8_t AArch64GotAtomContent[8] = {0}; + +// tls descriptor .got values, the layout is: +// struct tlsdesc { +// ptrdiff_t (*entry) (struct tlsdesc *); +// void *arg; +// }; +static const uint8_t AArch64TlsdescGotAtomContent[16] = {0}; // .plt value (entry 0) -const uint8_t AArch64Plt0AtomContent[32] = { - 0xf0, 0x7b, 0xbf, - 0xa9, // stp x16, x30, [sp,#-16]! - 0x10, 0x00, 0x00, - 0x90, // adrp x16, Page(eh_frame) - 0x11, 0x02, 0x40, - 0xf9, // ldr x17, [x16,#offset] - 0x10, 0x02, 0x00, - 0x91, // add x16, x16, #offset - 0x20, 0x02, 0x1f, - 0xd6, // br x17 - 0x1f, 0x20, 0x03, - 0xd5, // nop - 0x1f, 0x20, 0x03, - 0xd5, // nop - 0x1f, 0x20, 0x03, - 0xd5 // nop +static const uint8_t AArch64Plt0AtomContent[32] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(eh_frame) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop }; // .plt values (other entries) -const uint8_t AArch64PltAtomContent[16] = { - 0x10, 0x00, 0x00, - 0x90, // adrp x16, PAGE(<GLOBAL_OFFSET_TABLE>) - 0x11, 0x02, 0x40, - 0xf9, // ldr x17, [x16,#offset] - 0x10, 0x02, 0x00, - 0x91, // add x16, x16, #offset - 0x20, 0x02, 0x1f, - 0xd6 // br x17 +static const uint8_t AArch64PltAtomContent[16] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, PAGE(<GLOBAL_OFFSET_TABLE>) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, 0xd6 // br x17 +}; + +// .plt tlsdesc values +static const uint8_t AArch64PltTlsdescAtomContent[32] = { + 0xe2, 0x0f, 0xbf, 0xa9, // stp x2, x3, [sp, #-16] + 0x02, 0x00, 0x00, 0x90, // adpr x2, 0 + 0x03, 0x00, 0x00, 0x90, // adpr x3, 0 + 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2, #0] + 0x63, 0x00, 0x00, 0x91, // add x3, x3, 0 + 0x40, 0x00, 0x1f, 0xd6, // br x2 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop }; +namespace { + /// \brief Atoms that are used by AArch64 dynamic linking class AArch64GOTAtom : public GOTAtom { public: - AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + AArch64GOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(AArch64GotAtomContent, 8); } + +protected: + // Constructor for AArch64GOTAtom + AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} +}; + +class AArch64GOTPLTAtom : public AArch64GOTAtom { +public: + AArch64GOTPLTAtom(const File &f) : AArch64GOTAtom(f, ".got.plt") {} }; +class AArch64TLSDESCGOTAtom : public AArch64GOTPLTAtom { +public: + AArch64TLSDESCGOTAtom(const File &f) : AArch64GOTPLTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64TlsdescGotAtomContent, 16); + } +}; + + class AArch64PLT0Atom : public PLT0Atom { public: AArch64PLT0Atom(const File &f) : PLT0Atom(f) {} @@ -84,13 +111,22 @@ public: class AArch64PLTAtom : public PLTAtom { public: - AArch64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + AArch64PLTAtom(const File &f) : PLTAtom(f, ".plt") {} ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(AArch64PltAtomContent, 16); } }; +class AArch64PLTTLSDESCAtom : public PLTAtom { +public: + AArch64PLTTLSDESCAtom(const File &f) : PLTAtom(f, ".plt") {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64PltTlsdescAtomContent, 32); + } +}; + class ELFPassFile : public SimpleFile { public: ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { @@ -149,9 +185,16 @@ template <class Derived> class AArch64RelocationPass : public Pass { break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_LD64_GOT_LO12_NC: + static_cast<Derived *>(this)->handleGOT(ref); + break; case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: - static_cast<Derived *>(this)->handleGOT(ref); + static_cast<Derived *>(this)->handleGOTTPREL(ref); + break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + case R_AARCH64_TLSDESC_LD64_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12_NC: + static_cast<Derived *>(this)->handleTLSDESC(ref); break; } } @@ -164,9 +207,9 @@ protected: auto plt = _pltMap.find(da); if (plt != _pltMap.end()) return plt->second; - auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + auto ga = new (_file._alloc) AArch64GOTPLTAtom(_file); ga->addReferenceELF_AArch64(R_AARCH64_IRELATIVE, 0, da, 0); - auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + auto pa = new (_file._alloc) AArch64PLTAtom(_file); pa->addReferenceELF_AArch64(R_AARCH64_PREL32, 2, ga, -4); #ifndef NDEBUG ga->_name = "__got_ifunc_"; @@ -193,11 +236,11 @@ protected: } /// \brief Create a GOT entry for the TP offset of a TLS atom. - const GOTAtom *getGOTTPOFF(const Atom *atom) { + const GOTAtom *getGOTTPREL(const Atom *atom) { auto got = _gotMap.find(atom); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); - g->addReferenceELF_AArch64(R_AARCH64_GOTREL64, 0, atom, 0); + auto g = new (_file._alloc) AArch64GOTAtom(_file); + g->addReferenceELF_AArch64(R_AARCH64_TLS_TPREL64, 0, atom, 0); #ifndef NDEBUG g->_name = "__got_tls_"; g->_name += atom->name(); @@ -209,17 +252,53 @@ protected: return got->second; } - /// \brief Create a TPOFF64 GOT entry and change the relocation to a PC32 to - /// the GOT. - void handleGOTTPOFF(const Reference &ref) { - const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); - const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + /// \brief Create a GOT TPREL entry to local or external TLS variable. + std::error_code handleGOTTPREL(const Reference &ref) { + if (isa<DefinedAtom>(ref.target()) || + isa<SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOTTPREL(ref.target())); + return std::error_code(); + } + + /// \brief Generates a double GOT entry with R_AARCH64_TLSDESC dynamic + /// relocation reference. Since the dynamic relocation is resolved + /// lazily so the GOT associated should be in .got.plt. + const GOTAtom *getTLSDESCPLTEntry(const Atom *da) { + auto got = _gotMap.find(da); + if (got != _gotMap.end()) + return got->second; + auto ga = new (_file._alloc) AArch64TLSDESCGOTAtom(_file); + ga->addReferenceELF_AArch64(R_AARCH64_TLSDESC, 0, da, 0); + auto pa = new (_file._alloc) AArch64PLTTLSDESCAtom(_file); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 4, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 8, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_LDST64_ABS_LO12_NC, 12, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_ADD_ABS_LO12_NC, 16, ga, 0); +#ifndef NDEBUG + ga->_name = "__got_tlsdesc_"; + ga->_name += da->name(); + pa->_name = "__plt_tlsdesc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _tlsdescVector.push_back(ga); + _pltVector.push_back(pa); + return ga; + } + + std::error_code handleTLSDESC(const Reference &ref) { + if (isa<DefinedAtom>(ref.target()) || + isa<SharedLibraryAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getTLSDESCPLTEntry(ref.target())); + } + return std::error_code(); } /// \brief Create a GOT entry containing 0. const GOTAtom *getNullGOT() { if (!_null) { - _null = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _null = new (_file._alloc) AArch64GOTPLTAtom(_file); #ifndef NDEBUG _null->_name = "__got_null"; #endif @@ -230,7 +309,7 @@ protected: const GOTAtom *getGOT(const DefinedAtom *da) { auto got = _gotMap.find(da); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + auto g = new (_file._alloc) AArch64GOTAtom(_file); g->addReferenceELF_AArch64(R_AARCH64_ABS64, 0, da, 0); #ifndef NDEBUG g->_name = "__got_"; @@ -244,9 +323,7 @@ protected: } public: - AArch64RelocationPass(const ELFLinkingContext &ctx) - : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), - _got1(nullptr) {} + AArch64RelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} /// \brief Do the pass. /// @@ -256,32 +333,32 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "AArch64 GOT/PLT Pass"); DEBUG_WITH_TYPE( "AArch64", llvm::dbgs() << "Undefined Atoms" << "\n"; for (const auto &atom - : mf->undefined()) { + : mf.undefined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Shared Library Atoms" << "\n"; for (const auto &atom - : mf->sharedLibrary()) { + : mf.sharedLibrary()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Absolute Atoms" << "\n"; for (const auto &atom - : mf->absolute()) { + : mf.absolute()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } // Process all references. llvm::dbgs() << "Defined Atoms" << "\n"); - for (const auto &atom : mf->defined()) { + for (const auto &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } @@ -289,32 +366,39 @@ public: // Add all created atoms to the link. uint64_t ordinal = 0; - if (_PLT0) { - _PLT0->setOrdinal(ordinal++); - mf->addAtom(*_PLT0); + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); } for (auto &plt : _pltVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } if (_null) { _null->setOrdinal(ordinal++); - mf->addAtom(*_null); + mf.addAtom(*_null); } - if (_PLT0) { + if (_plt0) { _got0->setOrdinal(ordinal++); _got1->setOrdinal(ordinal++); - mf->addAtom(*_got0); - mf->addAtom(*_got1); + mf.addAtom(*_got0); + mf.addAtom(*_got1); } for (auto &got : _gotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); + } + // Add any tlsdesc GOT relocation after default PLT and iFUNC entries. + for (auto &tlsdesc : _tlsdescVector) { + tlsdesc->setOrdinal(ordinal++); + mf.addAtom(*tlsdesc); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } + + return std::error_code(); } protected: @@ -333,18 +417,19 @@ protected: /// \brief the list of GOT/PLT atoms std::vector<GOTAtom *> _gotVector; + std::vector<GOTAtom *> _tlsdescVector; std::vector<PLTAtom *> _pltVector; std::vector<ObjectAtom *> _objectVector; /// \brief GOT entry that is always 0. Used for undefined weaks. - GOTAtom *_null; + GOTAtom *_null = nullptr; /// \brief The got and plt entries for .PLT0. This is used to call into the /// dynamic linker for symbol resolution. /// @{ - PLT0Atom *_PLT0; - GOTAtom *_got0; - GOTAtom *_got1; + PLT0Atom *_plt0 = nullptr; + GOTAtom *_got0 = nullptr; + GOTAtom *_got1 = nullptr; /// @} }; @@ -394,31 +479,31 @@ public: : AArch64RelocationPass(ctx) {} const PLT0Atom *getPLT0() { - if (_PLT0) - return _PLT0; + if (_plt0) + return _plt0; // Fill in the null entry. getNullGOT(); - _PLT0 = new (_file._alloc) AArch64PLT0Atom(_file); - _got0 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); - _got1 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); - _PLT0->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 4, _got0, 0); - _PLT0->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 8, _got1, 0); - _PLT0->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 12, _got1, 0); + _plt0 = new (_file._alloc) AArch64PLT0Atom(_file); + _got0 = new (_file._alloc) AArch64GOTPLTAtom(_file); + _got1 = new (_file._alloc) AArch64GOTPLTAtom(_file); + _plt0->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 4, _got0, 0); + _plt0->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 8, _got1, 0); + _plt0->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 12, _got1, 0); #ifndef NDEBUG - _PLT0->_name = "__PLT0"; + _plt0->_name = "__PLT0"; _got0->_name = "__got0"; _got1->_name = "__got1"; #endif - return _PLT0; + return _plt0; } const PLTAtom *getPLTEntry(const Atom *a) { auto plt = _pltMap.find(a); if (plt != _pltMap.end()) return plt->second; - auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + auto ga = new (_file._alloc) AArch64GOTPLTAtom(_file); ga->addReferenceELF_AArch64(R_AARCH64_JUMP_SLOT, 0, a, 0); - auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + auto pa = new (_file._alloc) AArch64PLTAtom(_file); pa->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 0, ga, 0); pa->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 4, ga, 0); pa->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 8, ga, 0); @@ -485,7 +570,7 @@ public: const GOTAtom *getSharedGOT(const SharedLibraryAtom *sla) { auto got = _gotMap.find(sla); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + auto g = new (_file._alloc) AArch64GOTAtom(_file); g->addReferenceELF_AArch64(R_AARCH64_GLOB_DAT, 0, sla, 0); #ifndef NDEBUG g->_name = "__got_"; diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp new file mode 100644 index 000000000000..2734bcdbda5f --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp @@ -0,0 +1,39 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64SectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +AArch64GOTSection::AArch64GOTSection(const ELFLinkingContext &ctx, + StringRef name, int32_t order) + : AtomSection<ELF64LE>(ctx, name, DefinedAtom::typeGOT, DefinedAtom::permRW_, + order) { + _alignment = 8; +} + +const AtomLayout *AArch64GOTSection::appendAtom(const Atom *atom) { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::AArch64); + if ((r->kindValue() == R_AARCH64_TLS_TPREL64) || + (r->kindValue() == R_AARCH64_TLSDESC)) + _tlsMap[r->target()] = _tlsMap.size(); + } + + return AtomSection<ELF64LE>::appendAtom(atom); +} + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h new file mode 100644 index 000000000000..2b7594c2db84 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h @@ -0,0 +1,37 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64SectionChunks.h ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_AARCH64_AARCH64_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_SECTION_CHUNKS_H + +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +class AArch64GOTSection : public AtomSection<ELF64LE> { +public: + AArch64GOTSection(const ELFLinkingContext &ctx, StringRef name, + int32_t order); + + bool hasGlobalGOTEntry(const Atom *a) const { + return _tlsMap.count(a); + } + + const AtomLayout *appendAtom(const Atom *atom) override; + +private: + /// \brief Map TLS Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _tlsMap; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp index 607f767f8b8a..083b492c1607 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp @@ -12,41 +12,40 @@ #include "AArch64ExecutableWriter.h" #include "AArch64LinkingContext.h" #include "AArch64TargetHandler.h" +#include "AArch64SectionChunks.h" using namespace lld; using namespace elf; -AArch64TargetHandler::AArch64TargetHandler(AArch64LinkingContext &context) - : _context(context), - _AArch64TargetLayout(new AArch64TargetLayout<AArch64ELFType>(context)), - _AArch64RelocationHandler(new AArch64TargetRelocationHandler()) {} - -void AArch64TargetHandler::registerRelocationNames(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, - Reference::KindArch::AArch64, kindStrings); +AArch64TargetLayout::AArch64TargetLayout(ELFLinkingContext &ctx) : + TargetLayout(ctx) {} + +AtomSection<ELF64LE> *AArch64TargetLayout::createSection( + StringRef name, int32_t type, DefinedAtom::ContentPermissions permissions, + TargetLayout<ELF64LE>::SectionOrder order) { + if (type == DefinedAtom::typeGOT && (name == ".got" || name == ".got.plt")) { + auto section = new (this->_allocator) AArch64GOTSection(this->_ctx, name, + order); + _gotSections.push_back(section); + return section; + } + return TargetLayout<ELF64LE>::createSection(name, type, permissions, order); } + +AArch64TargetHandler::AArch64TargetHandler(AArch64LinkingContext &ctx) + : _ctx(ctx), _targetLayout(new AArch64TargetLayout(ctx)), + _relocationHandler(new AArch64TargetRelocationHandler(*_targetLayout)) {} + std::unique_ptr<Writer> AArch64TargetHandler::getWriter() { - switch (this->_context.getOutputELFType()) { + switch (this->_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>(new AArch64ExecutableWriter<AArch64ELFType>( - _context, *_AArch64TargetLayout.get())); + return llvm::make_unique<AArch64ExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new AArch64DynamicLibraryWriter<AArch64ELFType>( - _context, *_AArch64TargetLayout.get())); + return llvm::make_unique<AArch64DynamicLibraryWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_REL: llvm_unreachable("TODO: support -r mode"); default: llvm_unreachable("unsupported output type"); } } - -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), - -const Registry::KindStrings AArch64TargetHandler::kindStrings[] = { -#include "llvm/Support/ELFRelocs/AArch64.def" - LLD_KIND_STRING_END -}; - -#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h index 4eb6786cdf1f..c0ecbfa9e44b 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h @@ -10,52 +10,78 @@ #ifndef LLD_READER_WRITER_ELF_AARCH64_AARCH64_TARGET_HANDLER_H #define LLD_READER_WRITER_ELF_AARCH64_AARCH64_TARGET_HANDLER_H -#include "AArch64ELFFile.h" -#include "AArch64ELFReader.h" #include "AArch64RelocationHandler.h" -#include "DefaultTargetHandler.h" +#include "ELFReader.h" #include "TargetLayout.h" #include "lld/Core/Simple.h" namespace lld { namespace elf { + class AArch64LinkingContext; +class AArch64GOTSection; -template <class ELFT> class AArch64TargetLayout : public TargetLayout<ELFT> { -public: - AArch64TargetLayout(AArch64LinkingContext &context) - : TargetLayout<ELFT>(context) {} -}; +class AArch64TargetLayout final : public TargetLayout<ELF64LE> { + typedef llvm::object::Elf_Shdr_Impl<ELF64LE> Elf_Shdr; -class AArch64TargetHandler final : public DefaultTargetHandler<AArch64ELFType> { public: - AArch64TargetHandler(AArch64LinkingContext &context); + AArch64TargetLayout(ELFLinkingContext &ctx); + + AtomSection<ELF64LE> * + createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + TargetLayout<ELF64LE>::SectionOrder order) override; + + const std::vector<AArch64GOTSection *> &getGOTSections() const { + return _gotSections; + } - AArch64TargetLayout<AArch64ELFType> &getTargetLayout() override { - return *(_AArch64TargetLayout.get()); + uint64_t getTPOffset() { + std::call_once(_tpOffOnce, [this]() { + for (const auto &phdr : *_programHeader) { + if (phdr->p_type == llvm::ELF::PT_TLS) { + _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); + break; + } + } + assert(_tpOff != 0 && "TLS segment not found"); + }); + return _tpOff; } - void registerRelocationNames(Registry ®istry) override; +private: + enum { + TCB_SIZE = 16, + }; + +private: + std::vector<AArch64GOTSection *> _gotSections; + uint64_t _tpOff = 0; + std::once_flag _tpOffOnce; +}; + +class AArch64TargetHandler final : public TargetHandler { +public: + AArch64TargetHandler(AArch64LinkingContext &ctx); - const AArch64TargetRelocationHandler &getRelocationHandler() const override { - return *(_AArch64RelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new AArch64ELFObjectReader(_context)); + return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new AArch64ELFDSOReader(_context)); + return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx); } std::unique_ptr<Writer> getWriter() override; private: - static const Registry::KindStrings kindStrings[]; - AArch64LinkingContext &_context; - std::unique_ptr<AArch64TargetLayout<AArch64ELFType>> _AArch64TargetLayout; - std::unique_ptr<AArch64TargetRelocationHandler> _AArch64RelocationHandler; + AArch64LinkingContext &_ctx; + std::unique_ptr<AArch64TargetLayout> _targetLayout; + std::unique_ptr<AArch64TargetRelocationHandler> _relocationHandler; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt index de94a4df5078..2347dda9adb0 100644 --- a/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt @@ -3,6 +3,8 @@ add_llvm_library(lldAArch64ELFTarget AArch64TargetHandler.cpp AArch64RelocationHandler.cpp AArch64RelocationPass.cpp + AArch64ExecutableWriter.cpp + AArch64SectionChunks.cpp LINK_LIBS lldELF lldReaderWriter diff --git a/lib/ReaderWriter/ELF/AArch64/Makefile b/lib/ReaderWriter/ELF/AArch64/Makefile deleted file mode 100644 index 02cff4747d0d..000000000000 --- a/lib/ReaderWriter/ELF/AArch64/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/AArch64/Makefile ----------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLD_LEVEL := ../../../.. -LIBRARYNAME := lldAArch64ELFTarget -USEDLIBS = lldCore.a -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/AArch64 -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp new file mode 100644 index 000000000000..89efeb23d6f8 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp @@ -0,0 +1,34 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AMDGPUExecutableWriter.h" + +using namespace lld; +using namespace lld::elf; + +AMDGPUExecutableWriter::AMDGPUExecutableWriter(AMDGPULinkingContext &ctx, + AMDGPUTargetLayout &layout) + : ExecutableWriter(ctx, layout), _ctx(ctx) {} + +void AMDGPUExecutableWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &Result) { + // ExecutableWriter::createImplicitFiles() adds C runtime symbols that we + // don't need, so we use the OutputELFWriter implementation instead. + OutputELFWriter<ELF64LE>::createImplicitFiles(Result); +} + +void AMDGPUExecutableWriter::finalizeDefaultAtomValues() { + + // ExecutableWriter::finalizeDefaultAtomValues() assumes the presence of + // C runtime symbols. However, since we skip the call to + // ExecutableWriter::createImplicitFiles(), these symbols are never added + // and ExectuableWriter::finalizeDefaultAtomValues() will crash if we call + // it. + OutputELFWriter<ELF64LE>::finalizeDefaultAtomValues(); +} diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h new file mode 100644 index 000000000000..accc00b8a054 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUExecutableWriter.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef AMDGPU_EXECUTABLE_WRITER_H +#define AMDGPU_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "AMDGPULinkingContext.h" +#include "AMDGPUSymbolTable.h" +#include "AMDGPUTargetHandler.h" + +namespace lld { +namespace elf { + +class AMDGPUTargetLayout; + +class AMDGPUExecutableWriter : public ExecutableWriter<ELF64LE> { +public: + AMDGPUExecutableWriter(AMDGPULinkingContext &ctx, AMDGPUTargetLayout &layout); + + unique_bump_ptr<SymbolTable<ELF64LE>> createSymbolTable() override { + return unique_bump_ptr<SymbolTable<ELF64LE>>(new (this->_alloc) + AMDGPUSymbolTable(_ctx)); + } + + void createImplicitFiles(std::vector<std::unique_ptr<File>> &Result) override; + void finalizeDefaultAtomValues() override; + +private: + AMDGPULinkingContext &_ctx; +}; + +} // namespace elf +} // namespace lld + +#endif // AMDGPU_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp new file mode 100644 index 000000000000..b1e83641fa82 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.cpp ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===------------------------------------------------------------------------===// + +#include "AMDGPULinkingContext.h" +#include "AMDGPUTargetHandler.h" + +namespace lld { +namespace elf { + +std::unique_ptr<ELFLinkingContext> +createAMDGPULinkingContext(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::amdgcn) + return llvm::make_unique<AMDGPULinkingContext>(triple); + return nullptr; +} + +AMDGPULinkingContext::AMDGPULinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, llvm::make_unique<AMDGPUTargetHandler>(*this)) { +} + +static const Registry::KindStrings kindStrings[] = {LLD_KIND_STRING_END}; + +void AMDGPULinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::AMDGPU, kindStrings); +} + +void setAMDGPUELFHeader(ELFHeader<ELF64LE> &elfHeader) { + elfHeader.e_ident(llvm::ELF::EI_OSABI, ELFOSABI_AMDGPU_HSA); +} + +StringRef AMDGPULinkingContext::entrySymbolName() const { return ""; } + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h new file mode 100644 index 000000000000..1cc7a3c7694f --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h @@ -0,0 +1,36 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPULinkingContext.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_LINKING_CONTEXT_H + +#include "OutputELFWriter.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +class AMDGPULinkingContext final : public ELFLinkingContext { +public: + AMDGPULinkingContext(llvm::Triple triple); + int getMachineType() const override { return llvm::ELF::EM_AMDGPU; } + + void registerRelocationNames(Registry &r) override; + + StringRef entrySymbolName() const override; +}; + +void setAMDGPUELFHeader(ELFHeader<ELF64LE> &elfHeader); + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_LINKING_CONTEXT_H diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp new file mode 100644 index 000000000000..ca5a77db9177 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp @@ -0,0 +1,19 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.cpp -----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AMDGPURelocationHandler.h" + +using namespace lld; +using namespace lld::elf; + +std::error_code AMDGPUTargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom, + const Reference &ref) const { + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h new file mode 100644 index 000000000000..90d37274aebf --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h @@ -0,0 +1,31 @@ +//===- lld/ReaderWriter/ELF/AMDGPU/AMDGPURelocationHandler.h --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_RELOCATION_HANDLER_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include <system_error> + +namespace lld { +namespace elf { +class AMDGPUTargetHandler; +class AMDGPUTargetLayout; + +class AMDGPUTargetRelocationHandler final : public TargetRelocationHandler { +public: + AMDGPUTargetRelocationHandler(AMDGPUTargetLayout &layout) { } + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const AtomLayout &, + const Reference &) const override; + +}; +} // elf +} // lld +#endif diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp new file mode 100644 index 000000000000..0824974d4602 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp @@ -0,0 +1,32 @@ +//===--------- lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.cpp ----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AMDGPUSymbolTable.h" +#include "ELFFile.h" +#include "Atoms.h" +#include "SectionChunks.h" + +using namespace lld; +using namespace lld::elf; + +AMDGPUSymbolTable::AMDGPUSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable(ctx, ".symtab", TargetLayout<ELF64LE>::ORDER_SYMBOL_TABLE) {} + +void AMDGPUSymbolTable::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable::addDefinedAtom(sym, da, addr); + + // FIXME: Only do this for kernel functions. + sym.setType(STT_AMDGPU_HSA_KERNEL); + + // Make st_value section relative. + // FIXME: This is hack to give kernel symbols a section relative offset. + // Because of this hack only on kernel can be included in a binary file. + sym.st_value = 0; +} diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h new file mode 100644 index 000000000000..41c3be5cb38f --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h @@ -0,0 +1,32 @@ +//===--------- lib/ReaderWriter/ELF/AMDGPU/AMDGPUSymbolTable.h ------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_SYMBOL_TABLE_H +#define LLD_READER_WRITER_ELF_AMDGPU_AMDGPU_SYMBOL_TABLE_H + +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +/// \brief The SymbolTable class represents the symbol table in a ELF file +class AMDGPUSymbolTable : public SymbolTable<ELF64LE> { +public: + typedef llvm::object::Elf_Sym_Impl<ELF64LE> Elf_Sym; + + AMDGPUSymbolTable(const ELFLinkingContext &ctx); + + void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) override; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp new file mode 100644 index 000000000000..ff4b600158bd --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp @@ -0,0 +1,65 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.cpp -------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TargetLayout.h" +#include "AMDGPUExecutableWriter.h" +#include "AMDGPULinkingContext.h" +#include "AMDGPUTargetHandler.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +AMDGPUTargetHandler::AMDGPUTargetHandler(AMDGPULinkingContext &ctx) + : _ctx(ctx), _targetLayout(new AMDGPUTargetLayout(ctx)), + _relocationHandler(new AMDGPUTargetRelocationHandler(*_targetLayout)) {} + +std::unique_ptr<Writer> AMDGPUTargetHandler::getWriter() { + switch (_ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return llvm::make_unique<AMDGPUExecutableWriter>(_ctx, *_targetLayout); + case llvm::ELF::ET_DYN: + llvm_unreachable("TODO: support dynamic libraries"); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +HSATextSection::HSATextSection(const ELFLinkingContext &ctx) + : AtomSection(ctx, ".hsatext", DefinedAtom::typeCode, 0, 0) { + _type = SHT_PROGBITS; + _flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR | SHF_AMDGPU_HSA_AGENT | + SHF_AMDGPU_HSA_CODE; + + // FIXME: What alignment should we use here? + _alignment = 4096; +} + +void AMDGPUTargetLayout::assignSectionsToSegments() { + + TargetLayout::assignSectionsToSegments(); + for (OutputSection<ELF64LE> *osi : _outputSections) { + for (Section<ELF64LE> *section : osi->sections()) { + StringRef InputSectionName = section->inputSectionName(); + if (InputSectionName != ".hsatext") + continue; + + auto *segment = new (_allocator) Segment<ELF64LE>( + _ctx, "PT_AMDGPU_HSA_LOAD_CODE_AGENT", PT_AMDGPU_HSA_LOAD_CODE_AGENT); + _segments.push_back(segment); + assert(segment); + segment->append(section); + } + } +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h new file mode 100644 index 000000000000..8d0f70b6e7f7 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h @@ -0,0 +1,80 @@ +//===- lib/ReaderWriter/ELF/AMDGPU/AMDGPUTargetHandler.h ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef AMDGPU_TARGET_HANDLER_H +#define AMDGPU_TARGET_HANDLER_H + +#include "ELFFile.h" +#include "ELFReader.h" +#include "AMDGPURelocationHandler.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { +class AMDGPULinkingContext; + +class HSATextSection : public AtomSection<ELF64LE> { +public: + HSATextSection(const ELFLinkingContext &ctx); +}; + +/// \brief TargetLayout for AMDGPU +class AMDGPUTargetLayout final : public TargetLayout<ELF64LE> { +public: + AMDGPUTargetLayout(AMDGPULinkingContext &ctx) : TargetLayout(ctx) {} + + void assignSectionsToSegments() override; + + /// \brief Gets or creates a section. + AtomSection<ELF64LE> * + createSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + TargetLayout::SectionOrder sectionOrder) override { + if (name == ".hsatext") + return new (_allocator) HSATextSection(_ctx); + + if (name == ".note") + contentType = DefinedAtom::typeRONote; + + return TargetLayout::createSection(name, contentType, contentPermissions, + sectionOrder); + } +}; + +/// \brief TargetHandler for AMDGPU +class AMDGPUTargetHandler final : public TargetHandler { +public: + AMDGPUTargetHandler(AMDGPULinkingContext &targetInfo); + + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; + } + + std::unique_ptr<Reader> getObjReader() override { + return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx); + } + + std::unique_ptr<Reader> getDSOReader() override { + return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + AMDGPULinkingContext &_ctx; + std::unique_ptr<AMDGPUTargetLayout> _targetLayout; + std::unique_ptr<AMDGPUTargetRelocationHandler> _relocationHandler; +}; + +void finalizeAMDGPURuntimeAtomValues(AMDGPUTargetLayout &layout); + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AMDGPU/CMakeLists.txt b/lib/ReaderWriter/ELF/AMDGPU/CMakeLists.txt new file mode 100644 index 000000000000..9c9cc10fe397 --- /dev/null +++ b/lib/ReaderWriter/ELF/AMDGPU/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_library(lldAMDGPUELFTarget + AMDGPUExecutableWriter.cpp + AMDGPULinkingContext.cpp + AMDGPURelocationHandler.cpp + AMDGPUSymbolTable.cpp + AMDGPUTargetHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h new file mode 100644 index 000000000000..da843b97abc0 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h @@ -0,0 +1,49 @@ +//===- lib/ReaderWriter/ELF/ARM/ARMDynamicLibraryWriter.h -----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLD_READER_WRITER_ELF_ARM_ARM_DYNAMIC_LIBRARY_WRITER_H +#define LLD_READER_WRITER_ELF_ARM_ARM_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "ARMELFWriters.h" +#include "ARMLinkingContext.h" +#include "ARMTargetHandler.h" + +namespace lld { +namespace elf { + +class ARMDynamicLibraryWriter + : public ARMELFWriter<DynamicLibraryWriter<ELF32LE>> { +public: + ARMDynamicLibraryWriter(ARMLinkingContext &ctx, ARMTargetLayout &layout); + +protected: + // Add any runtime files and their atoms to the output + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + +private: + ARMLinkingContext &_ctx; +}; + +ARMDynamicLibraryWriter::ARMDynamicLibraryWriter(ARMLinkingContext &ctx, + ARMTargetLayout &layout) + : ARMELFWriter(ctx, layout), _ctx(ctx) {} + +void ARMDynamicLibraryWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter::createImplicitFiles(result); + auto file = llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "ARM dynamic file"); + file->addAbsoluteAtom(gotSymbol); + file->addAbsoluteAtom(dynamicSymbol); + result.push_back(std::move(file)); +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_DYNAMIC_LIBRARY_WRITER_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h index bc5ee35b8213..8f5477017e55 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h +++ b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h @@ -17,53 +17,95 @@ namespace elf { class ARMLinkingContext; -template <class ELFT> class ARMELFDefinedAtom : public ELFDefinedAtom<ELFT> { - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; +class ARMELFBaseDefinedAtom : public ELFDefinedAtom<ELF32LE> { +public: + /// The values of custom content type enum must not interfere + /// with ones in base defined atom class' enum. + enum ARMContentType { + typeARMExidx = 0x1000, // Identifies ARM_EXIDX section + }; + + template <typename... T> + ARMELFBaseDefinedAtom(T &&... args) + : ELFDefinedAtom<ELF32LE>(std::forward<T>(args)...) {} + + DefinedAtom::ContentPermissions permissions() const override { + if (_permissions != DefinedAtom::permUnknown) + return _permissions; + + switch (_section->sh_type) { + case llvm::ELF::SHT_ARM_EXIDX: + return _permissions = permR__; + } + return ELFDefinedAtom::permissions(); + } + + DefinedAtom::ContentType contentType() const override { + if (_contentType != DefinedAtom::typeUnknown) + return _contentType; + + switch (_section->sh_type) { + case llvm::ELF::SHT_ARM_EXIDX: + return _contentType = (DefinedAtom::ContentType)typeARMExidx; + } + return ELFDefinedAtom::contentType(); + } +}; + +class ARMELFMappingAtom : public ARMELFBaseDefinedAtom { +public: + template <typename... T> + ARMELFMappingAtom(DefinedAtom::CodeModel model, T &&... args) + : ARMELFBaseDefinedAtom(std::forward<T>(args)...), _model(model) {} + + DefinedAtom::CodeModel codeModel() const override { return _model; } + +private: + DefinedAtom::CodeModel _model; +}; +class ARMELFDefinedAtom : public ARMELFBaseDefinedAtom { public: - ARMELFDefinedAtom(const ELFFile<ELFT> &file, StringRef symbolName, - StringRef sectionName, const Elf_Sym *symbol, - const Elf_Shdr *section, ArrayRef<uint8_t> contentData, - unsigned int referenceStart, unsigned int referenceEnd, - std::vector<ELFReference<ELFT> *> &referenceList) - : ELFDefinedAtom<ELFT>(file, symbolName, sectionName, symbol, section, - contentData, referenceStart, referenceEnd, - referenceList) {} - - bool isThumbFunc(const Elf_Sym *symbol) const { + template <typename... T> + ARMELFDefinedAtom(T &&... args) + : ARMELFBaseDefinedAtom(std::forward<T>(args)...) {} + + bool isThumbFunc() const { + const auto *symbol = _symbol; return symbol->getType() == llvm::ELF::STT_FUNC && - (static_cast<uint64_t>(symbol->st_value) & 0x1); + (static_cast<uint64_t>(symbol->st_value) & 0x1); } /// Correct st_value for symbols addressing Thumb instructions /// by removing its zero bit. - uint64_t getSymbolValue(const Elf_Sym *symbol) const override { - const auto value = static_cast<uint64_t>(symbol->st_value); - return isThumbFunc(symbol) ? value & ~0x1 : value; + uint64_t getSymbolValue() const override { + const auto value = static_cast<uint64_t>(_symbol->st_value); + return isThumbFunc() ? value & ~0x1 : value; } DefinedAtom::CodeModel codeModel() const override { - if (isThumbFunc(this->_symbol)) - return DefinedAtom::codeARMThumb; - return DefinedAtom::codeNA; + return isThumbFunc() ? DefinedAtom::codeARMThumb : DefinedAtom::codeNA; } }; -template <class ELFT> class ARMELFFile : public ELFFile<ELFT> { -public: - ARMELFFile(std::unique_ptr<MemoryBuffer> mb, ARMLinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} +class ARMELFFile : public ELFFile<ELF32LE> { + typedef llvm::object::Elf_Rel_Impl<ELF32LE, false> Elf_Rel; - static ErrorOr<std::unique_ptr<ARMELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, ARMLinkingContext &ctx) { - return std::unique_ptr<ARMELFFile<ELFT>>( - new ARMELFFile<ELFT>(std::move(mb), ctx)); +public: + ARMELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : ELFFile(std::move(mb), ctx) {} + +protected: + /// Returns initial addend; for ARM it is 0, because it is read + /// during the relocations applying + Reference::Addend getInitialAddend(ArrayRef<uint8_t>, uint64_t, + const Elf_Rel &) const override { + return 0; } private: - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> Elf_Shdr; /// Correct st_value for symbols addressing Thumb instructions /// by removing its zero bit. @@ -73,24 +115,39 @@ private: } /// Process the Defined symbol and create an atom for it. - ErrorOr<ELFDefinedAtom<ELFT> *> handleDefinedSymbol(StringRef symName, - StringRef sectionName, - const Elf_Sym *sym, const Elf_Shdr *sectionHdr, - ArrayRef<uint8_t> contentData, - unsigned int referenceStart, unsigned int referenceEnd, - std::vector<ELFReference<ELFT> *> &referenceList) override { - return new (this->_readerStorage) ARMELFDefinedAtom<ELFT>( + ELFDefinedAtom<ELF32LE> *createDefinedAtom( + StringRef symName, StringRef sectionName, const Elf_Sym *sym, + const Elf_Shdr *sectionHdr, ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELF32LE> *> &referenceList) override { + if (symName.size() >= 2 && symName[0] == '$') { + switch (symName[1]) { + case 'a': + return new (_readerStorage) + ARMELFMappingAtom(DefinedAtom::codeARM_a, *this, symName, + sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + case 'd': + return new (_readerStorage) + ARMELFMappingAtom(DefinedAtom::codeARM_d, *this, symName, + sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + case 't': + return new (_readerStorage) + ARMELFMappingAtom(DefinedAtom::codeARM_t, *this, symName, + sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + default: + // Fall through and create regular defined atom. + break; + } + } + return new (_readerStorage) ARMELFDefinedAtom( *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, referenceEnd, referenceList); } }; -template <class ELFT> class ARMDynamicFile : public DynamicFile<ELFT> { -public: - ARMDynamicFile(const ARMLinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} -}; - } // elf } // lld diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFReader.h b/lib/ReaderWriter/ELF/ARM/ARMELFReader.h deleted file mode 100644 index 31af531563ea..000000000000 --- a/lib/ReaderWriter/ELF/ARM/ARMELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===--------- lib/ReaderWriter/ELF/ARM/ARMELFReader.h --------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_ARM_ARM_ELF_READER_H -#define LLD_READER_WRITER_ARM_ARM_ELF_READER_H - -#include "ARMELFFile.h" -#include "ELFReader.h" - -namespace lld { -namespace elf { - -typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; - -struct ARMDynamicFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - ARMLinkingContext &ctx) { - return lld::elf::ARMDynamicFile<ELFT>::create(std::move(mb), ctx); - } -}; - -struct ARMELFFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - ARMLinkingContext &ctx) { - return lld::elf::ARMELFFile<ELFT>::create(std::move(mb), ctx); - } -}; - -class ARMELFObjectReader - : public ELFObjectReader<ARMELFType, ARMELFFileCreateELFTraits, - ARMLinkingContext> { -public: - ARMELFObjectReader(ARMLinkingContext &ctx) - : ELFObjectReader<ARMELFType, ARMELFFileCreateELFTraits, - ARMLinkingContext>(ctx, llvm::ELF::EM_ARM) {} -}; - -class ARMELFDSOReader - : public ELFDSOReader<ARMELFType, ARMDynamicFileCreateELFTraits, - ARMLinkingContext> { -public: - ARMELFDSOReader(ARMLinkingContext &ctx) - : ELFDSOReader<ARMELFType, ARMDynamicFileCreateELFTraits, - ARMLinkingContext>(ctx, llvm::ELF::EM_ARM) {} -}; - -} // namespace elf -} // namespace lld - -#endif // LLD_READER_WRITER_ARM_ARM_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFWriters.h b/lib/ReaderWriter/ELF/ARM/ARMELFWriters.h new file mode 100644 index 000000000000..a842ebe53038 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMELFWriters.h @@ -0,0 +1,120 @@ +//===- lib/ReaderWriter/ELF/ARM/ARMELFWriters.h ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLD_READER_WRITER_ELF_ARM_ARM_ELF_WRITERS_H +#define LLD_READER_WRITER_ELF_ARM_ARM_ELF_WRITERS_H + +#include "ARMLinkingContext.h" +#include "ARMSymbolTable.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +template <class WriterT> class ARMELFWriter : public WriterT { +public: + ARMELFWriter(ARMLinkingContext &ctx, TargetLayout<ELF32LE> &layout); + + void finalizeDefaultAtomValues() override; + + /// \brief Create symbol table. + unique_bump_ptr<SymbolTable<ELF32LE>> createSymbolTable() override; + + // Setup the ELF header. + std::error_code setELFHeader() override; + +protected: + static const char *gotSymbol; + static const char *dynamicSymbol; + +private: + ARMLinkingContext &_ctx; + TargetLayout<ELF32LE> &_armLayout; +}; + +template <class WriterT> +const char *ARMELFWriter<WriterT>::gotSymbol = "_GLOBAL_OFFSET_TABLE_"; +template <class WriterT> +const char *ARMELFWriter<WriterT>::dynamicSymbol = "_DYNAMIC"; + +template <class WriterT> +ARMELFWriter<WriterT>::ARMELFWriter(ARMLinkingContext &ctx, + TargetLayout<ELF32LE> &layout) + : WriterT(ctx, layout), _ctx(ctx), _armLayout(layout) {} + +template <class WriterT> +void ARMELFWriter<WriterT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + WriterT::finalizeDefaultAtomValues(); + + if (auto *gotAtom = _armLayout.findAbsoluteAtom(gotSymbol)) { + if (auto gotpltSection = _armLayout.findOutputSection(".got.plt")) + gotAtom->_virtualAddr = gotpltSection->virtualAddr(); + else if (auto gotSection = _armLayout.findOutputSection(".got")) + gotAtom->_virtualAddr = gotSection->virtualAddr(); + else + gotAtom->_virtualAddr = 0; + } + + if (auto *dynamicAtom = _armLayout.findAbsoluteAtom(dynamicSymbol)) { + if (auto dynamicSection = _armLayout.findOutputSection(".dynamic")) + dynamicAtom->_virtualAddr = dynamicSection->virtualAddr(); + else + dynamicAtom->_virtualAddr = 0; + } + + // Set required by gcc libc __ehdr_start symbol with pointer to ELF header + if (auto ehdr = _armLayout.findAbsoluteAtom("__ehdr_start")) + ehdr->_virtualAddr = this->_elfHeader->virtualAddr(); + + // Set required by gcc libc symbols __exidx_start/__exidx_end + this->updateScopeAtomValues("exidx", ".ARM.exidx"); +} + +template <class WriterT> +unique_bump_ptr<SymbolTable<ELF32LE>> +ARMELFWriter<WriterT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELF32LE>>(new (this->_alloc) + ARMSymbolTable(_ctx)); +} + +template <class WriterT> std::error_code ARMELFWriter<WriterT>::setELFHeader() { + if (std::error_code ec = WriterT::setELFHeader()) + return ec; + + // Set ARM-specific flags. + this->_elfHeader->e_flags(llvm::ELF::EF_ARM_EABI_VER5 | + llvm::ELF::EF_ARM_VFP_FLOAT); + + StringRef entryName = _ctx.entrySymbolName(); + if (const AtomLayout *al = _armLayout.findAtomLayoutByName(entryName)) { + if (const auto *ea = dyn_cast<DefinedAtom>(al->_atom)) { + switch (ea->codeModel()) { + case DefinedAtom::codeNA: + if (al->_virtualAddr & 0x3) { + llvm::report_fatal_error( + "Two least bits must be zero for ARM entry point"); + } + break; + case DefinedAtom::codeARMThumb: + // Fixup entry point for Thumb code. + this->_elfHeader->e_entry(al->_virtualAddr | 0x1); + break; + default: + llvm_unreachable("Wrong code model of entry point atom"); + } + } + } + + return std::error_code(); +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_ELF_WRITERS_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h index 19311d516e4d..974dab63a126 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h +++ b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h @@ -10,111 +10,58 @@ #define LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H #include "ExecutableWriter.h" +#include "ARMELFWriters.h" #include "ARMLinkingContext.h" #include "ARMTargetHandler.h" -#include "ARMSymbolTable.h" - -namespace { -const char *gotSymbol = "_GLOBAL_OFFSET_TABLE_"; -} namespace lld { namespace elf { -template <class ELFT> -class ARMExecutableWriter : public ExecutableWriter<ELFT> { +class ARMExecutableWriter : public ARMELFWriter<ExecutableWriter<ELF32LE>> { public: - ARMExecutableWriter(ARMLinkingContext &context, - ARMTargetLayout<ELFT> &layout); + ARMExecutableWriter(ARMLinkingContext &ctx, ARMTargetLayout &layout); protected: // Add any runtime files and their atoms to the output - bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - - void finalizeDefaultAtomValues() override; - - void addDefaultAtoms() override { - ExecutableWriter<ELFT>::addDefaultAtoms(); - } - - /// \brief Create symbol table. - unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; void processUndefinedSymbol(StringRef symName, - RuntimeFile<ELFT> &file) const override; - - // Setup the ELF header. - std::error_code setELFHeader() override; + RuntimeFile<ELF32LE> &file) const override; private: - ARMLinkingContext &_context; - ARMTargetLayout<ELFT> &_armLayout; + ARMLinkingContext &_ctx; }; -template <class ELFT> -ARMExecutableWriter<ELFT>::ARMExecutableWriter(ARMLinkingContext &context, - ARMTargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(context, layout), _context(context), - _armLayout(layout) {} +ARMExecutableWriter::ARMExecutableWriter(ARMLinkingContext &ctx, + ARMTargetLayout &layout) + : ARMELFWriter(ctx, layout), _ctx(ctx) {} -template <class ELFT> -bool ARMExecutableWriter<ELFT>::createImplicitFiles( +void ARMExecutableWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); - return true; -} - -template <class ELFT> -void ARMExecutableWriter<ELFT>::finalizeDefaultAtomValues() { - // Finalize the atom values that are part of the parent. - ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); - auto gotAtomIter = _armLayout.findAbsoluteAtom(gotSymbol); - if (gotAtomIter != _armLayout.absoluteAtoms().end()) { - auto *gotAtom = *gotAtomIter; - if (auto gotpltSection = _armLayout.findOutputSection(".got.plt")) - gotAtom->_virtualAddr = gotpltSection->virtualAddr(); - else if (auto gotSection = _armLayout.findOutputSection(".got")) - gotAtom->_virtualAddr = gotSection->virtualAddr(); - else - gotAtom->_virtualAddr = 0; + ExecutableWriter::createImplicitFiles(result); + // Add default atoms for ARM. + if (_ctx.isDynamic()) { + auto file = llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "ARM exec file"); + file->addAbsoluteAtom(gotSymbol); + file->addAbsoluteAtom(dynamicSymbol); + result.push_back(std::move(file)); } - // TODO: resolve addresses of __exidx_start/_end atoms -} - -template <class ELFT> -unique_bump_ptr<SymbolTable<ELFT>> - ARMExecutableWriter<ELFT>::createSymbolTable() { - return unique_bump_ptr<SymbolTable<ELFT>>( - new (this->_alloc) ARMSymbolTable<ELFT>(this->_context)); } -template <class ELFT> -void ARMExecutableWriter<ELFT>::processUndefinedSymbol( - StringRef symName, RuntimeFile<ELFT> &file) const { +void ARMExecutableWriter::processUndefinedSymbol( + StringRef symName, RuntimeFile<ELF32LE> &file) const { + ARMELFWriter<ExecutableWriter<ELF32LE>>::processUndefinedSymbol(symName, + file); if (symName == gotSymbol) { file.addAbsoluteAtom(gotSymbol); } else if (symName.startswith("__exidx")) { file.addAbsoluteAtom("__exidx_start"); file.addAbsoluteAtom("__exidx_end"); + } else if (symName == "__ehdr_start") { + file.addAbsoluteAtom("__ehdr_start"); } } -template <class ELFT> -std::error_code ARMExecutableWriter<ELFT>::setELFHeader() { - if (std::error_code ec = ExecutableWriter<ELFT>::setELFHeader()) - return ec; - - // Fixup entry point for Thumb code. - StringRef entryName = _context.entrySymbolName(); - if (const AtomLayout *al = _armLayout.findAtomLayoutByName(entryName)) { - const auto *ea = dyn_cast<DefinedAtom>(al->_atom); - if (ea && ea->codeModel() == DefinedAtom::codeARMThumb) - this->_elfHeader->e_entry(al->_virtualAddr | 0x1); - } - - return std::error_code(); -} - } // namespace elf } // namespace lld diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp index 5f2436674268..74905b47820f 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp @@ -11,24 +11,54 @@ #include "ARMRelocationPass.h" #include "ARMTargetHandler.h" -using namespace lld; -using namespace lld::elf; +namespace lld { +namespace elf { std::unique_ptr<ELFLinkingContext> -elf::ARMLinkingContext::create(llvm::Triple triple) { +createARMLinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::arm) - return std::unique_ptr<ELFLinkingContext>( - new elf::ARMLinkingContext(triple)); + return llvm::make_unique<ARMLinkingContext>(triple); return nullptr; } -elf::ARMLinkingContext::ARMLinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( - new ARMTargetHandler(*this))) {} +ARMLinkingContext::ARMLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, llvm::make_unique<ARMTargetHandler>(*this)) {} -void elf::ARMLinkingContext::addPasses(PassManager &pm) { +void ARMLinkingContext::addPasses(PassManager &pm) { auto pass = createARMRelocationPass(*this); if (pass) pm.add(std::move(pass)); ELFLinkingContext::addPasses(pm); } + +bool isARMCode(const DefinedAtom *atom) { + return isARMCode(atom->codeModel()); +} + +bool isARMCode(DefinedAtom::CodeModel codeModel) { + return !isThumbCode(codeModel); +} + +bool isThumbCode(const DefinedAtom *atom) { + return isThumbCode(atom->codeModel()); +} + +bool isThumbCode(DefinedAtom::CodeModel codeModel) { + return codeModel == DefinedAtom::codeARMThumb || + codeModel == DefinedAtom::codeARM_t; +} + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/ARM.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void ARMLinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::ARM, + kindStrings); +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h index 249b79c4f07d..f687713b25b8 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h +++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h @@ -19,17 +19,61 @@ namespace elf { class ARMLinkingContext final : public ELFLinkingContext { public: - static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_ARM; } ARMLinkingContext(llvm::Triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; + + bool isRelaOutputFormat() const override { return false; } uint64_t getBaseAddress() const override { if (_baseAddress == 0) return 0x400000; return _baseAddress; } + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_GLOB_DAT: + case llvm::ELF::R_ARM_TLS_TPOFF32: + case llvm::ELF::R_ARM_COPY: + return true; + default: + return false; + } + } + + bool isCopyRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + return r.kindValue() == llvm::ELF::R_ARM_COPY; + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::ARM); + switch (r.kindValue()) { + case llvm::ELF::R_ARM_JUMP_SLOT: + case llvm::ELF::R_ARM_IRELATIVE: + return true; + default: + return false; + } + } }; + +// Special methods to check code model of atoms. +bool isARMCode(const DefinedAtom *atom); +bool isARMCode(DefinedAtom::CodeModel codeModel); +bool isThumbCode(const DefinedAtom *atom); +bool isThumbCode(DefinedAtom::CodeModel codeModel); + } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp index d24fdf0fa410..97b149133ff2 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp @@ -14,6 +14,8 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/MathExtras.h" +#define DEBUG_TYPE "ARM" + using namespace lld; using namespace lld::elf; using namespace llvm::support::endian; @@ -74,7 +76,7 @@ static Reference::Addend readAddend_THM_JUMP11(const uint8_t *location) { const auto value = read16le(location); const uint16_t imm11 = value & 0x7FF; - return llvm::SignExtend32<12>(imm11 << 1); + return llvm::SignExtend64<12>(imm11 << 1); } static Reference::Addend readAddend(const uint8_t *location, @@ -82,11 +84,15 @@ static Reference::Addend readAddend(const uint8_t *location, switch (kindValue) { case R_ARM_ABS32: case R_ARM_REL32: + case R_ARM_TARGET1: + case R_ARM_GOT_BREL: + case R_ARM_BASE_PREL: case R_ARM_TLS_IE32: case R_ARM_TLS_LE32: + case R_ARM_TLS_TPOFF32: return (int32_t)read32le(location); case R_ARM_PREL31: - return (int32_t)(read32le(location) & 0x7FFFFFFF); + return llvm::SignExtend64<31>(read32le(location) & 0x7FFFFFFF); case R_ARM_THM_CALL: case R_ARM_THM_JUMP24: return readAddend_THM_CALL(location); @@ -106,81 +112,98 @@ static Reference::Addend readAddend(const uint8_t *location, } } -static inline void applyArmReloc(uint8_t *location, uint32_t result, - uint32_t mask = 0xFFFFFFFF) { +static inline void report_unsupported_range_group_reloc_error() { + llvm::report_fatal_error( + "Negative offsets for group relocations are not implemented"); +} + +static inline std::error_code applyArmReloc(uint8_t *location, uint32_t result, + uint32_t mask = 0xFFFFFFFF) { assert(!(result & ~mask)); write32le(location, (read32le(location) & ~mask) | (result & mask)); + return std::error_code(); } -static inline void applyThmReloc(uint8_t *location, uint16_t resHi, - uint16_t resLo, uint16_t maskHi, - uint16_t maskLo = 0xFFFF) { +static inline std::error_code applyThumb32Reloc(uint8_t *location, + uint16_t resHi, uint16_t resLo, + uint16_t maskHi, + uint16_t maskLo = 0xFFFF) { assert(!(resHi & ~maskHi) && !(resLo & ~maskLo)); write16le(location, (read16le(location) & ~maskHi) | (resHi & maskHi)); location += 2; write16le(location, (read16le(location) & ~maskLo) | (resLo & maskLo)); + return std::error_code(); } -static inline void applyThumb16Reloc(uint8_t *location, uint16_t result, - uint16_t mask = 0xFFFF) { +static inline std::error_code +applyThumb16Reloc(uint8_t *location, uint16_t result, uint16_t mask = 0xFFFF) { assert(!(result & ~mask)); write16le(location, (read16le(location) & ~mask) | (result & mask)); + return std::error_code(); } /// \brief R_ARM_ABS32 - (S + A) | T -static void relocR_ARM_ABS32(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_ABS32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)((S + A) | T); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - applyArmReloc(location, result); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); } /// \brief R_ARM_REL32 - ((S + A) | T) - P -static void relocR_ARM_REL32(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_REL32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)(((S + A) | T) - P); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - applyArmReloc(location, result); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); } /// \brief R_ARM_PREL31 - ((S + A) | T) - P -static void relocR_ARM_PREL31(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_PREL31(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)(((S + A) | T) - P); + if (!llvm::isInt<31>((int32_t)result)) + return make_out_of_range_reloc_error(); + const uint32_t mask = 0x7FFFFFFF; uint32_t rel31 = result & mask; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result); - llvm::dbgs() << " rel31: 0x" << Twine::utohexstr(rel31) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result); + llvm::dbgs() << " rel31: 0x" << Twine::utohexstr(rel31) << "\n"); - applyArmReloc(location, rel31, mask); + return applyArmReloc(location, rel31, mask); } /// \brief Relocate B/BL instructions. useJs defines whether J1 & J2 are used -static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) { +static std::error_code relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, + bool useJs) { + if ((useJs && !llvm::isInt<25>((int32_t)result)) || + (!useJs && !llvm::isInt<23>((int32_t)result))) + return make_out_of_range_reloc_error(); + result = (result & 0x01FFFFFE) >> 1; const uint16_t imm10 = (result >> 11) & 0x3FF; @@ -194,12 +217,13 @@ static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) { const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1; const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11; - applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF); + return applyThumb32Reloc(location, resHi, resLo, 0x7FF, 0x2FFF); } /// \brief R_ARM_THM_CALL - ((S + A) | T) - P -static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool useJs, bool addressesThumb) { +static std::error_code relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, bool useJs, + bool addressesThumb) { uint64_t T = addressesThumb; const bool switchMode = !addressesThumb; @@ -209,137 +233,171 @@ static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S, uint32_t result = (uint32_t)(((S + A) | T) - P); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - relocR_ARM_THM_B_L(location, result, useJs); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + if (auto ec = relocR_ARM_THM_B_L(location, result, useJs)) + return ec; if (switchMode) { - applyThmReloc(location, 0, 0, 0, 0x1001); + return applyThumb32Reloc(location, 0, 0, 0, 0x1001); } + return std::error_code(); } /// \brief R_ARM_THM_JUMP24 - ((S + A) | T) - P -static void relocR_ARM_THM_JUMP24(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_THM_JUMP24(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)(((S + A) | T) - P); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - relocR_ARM_THM_B_L(location, result, true); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_THM_B_L(location, result, true); } /// \brief R_ARM_THM_JUMP11 - S + A - P -static void relocR_ARM_THM_JUMP11(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { +static std::error_code relocR_ARM_THM_JUMP11(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { uint32_t result = (uint32_t)(S + A - P); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - //we cut off first bit because it is always 1 according to p. 4.5.3 + if (!llvm::isInt<12>((int32_t)result)) + return make_out_of_range_reloc_error(); + + // we cut off first bit because it is always 1 according to p. 4.5.3 result = (result & 0x0FFE) >> 1; + return applyThumb16Reloc(location, result, 0x7FF); +} + +/// \brief R_ARM_BASE_PREL - B(S) + A - P => S + A - P +static std::error_code relocR_ARM_BASE_PREL(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint32_t result = (uint32_t)(S + A - P); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); +} - applyThumb16Reloc(location, result, 0x7FF); +/// \brief R_ARM_GOT_BREL - GOT(S) + A - GOT_ORG => S + A - GOT_ORG +static std::error_code relocR_ARM_GOT_BREL(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + uint64_t GOT_ORG) { + uint32_t result = (uint32_t)(S + A - GOT_ORG); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); } /// \brief R_ARM_CALL - ((S + A) | T) - P -static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_CALL(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; const bool switchMode = addressesThumb; uint32_t result = (uint32_t)(((S + A) | T) - P); + if (!llvm::isInt<26>((int32_t)result)) + return make_out_of_range_reloc_error(); + const uint32_t imm24 = (result & 0x03FFFFFC) >> 2; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - applyArmReloc(location, imm24, 0xFFFFFF); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + if (auto ec = applyArmReloc(location, imm24, 0xFFFFFF)) + return ec; if (switchMode) { const uint32_t bitH = (result & 0x2) >> 1; - applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000); + return applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000); } + return std::error_code(); } /// \brief R_ARM_JUMP24 - ((S + A) | T) - P -static void relocR_ARM_JUMP24(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_JUMP24(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)(((S + A) | T) - P); + if (!llvm::isInt<26>((int32_t)result)) + return make_out_of_range_reloc_error(); + const uint32_t imm24 = (result & 0x03FFFFFC) >> 2; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - applyArmReloc(location, imm24, 0xFFFFFF); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, imm24, 0xFFFFFF); } /// \brief Relocate ARM MOVW/MOVT instructions -static void relocR_ARM_MOV(uint8_t *location, uint32_t result) { +static std::error_code relocR_ARM_MOV(uint8_t *location, uint32_t result) { const uint32_t imm12 = result & 0xFFF; const uint32_t imm4 = (result >> 12) & 0xF; - applyArmReloc(location, (imm4 << 16) | imm12, 0xF0FFF); + return applyArmReloc(location, (imm4 << 16) | imm12, 0xF0FFF); } /// \brief R_ARM_MOVW_ABS_NC - (S + A) | T -static void relocR_ARM_MOVW_ABS_NC(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, bool addressesThumb) { +static std::error_code relocR_ARM_MOVW_ABS_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)((S + A) | T); const uint32_t arg = result & 0x0000FFFF; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); return relocR_ARM_MOV(location, arg); } /// \brief R_ARM_MOVT_ABS - S + A -static void relocR_ARM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { +static std::error_code relocR_ARM_MOVT_ABS(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { uint32_t result = (uint32_t)(S + A); const uint32_t arg = (result & 0xFFFF0000) >> 16; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); return relocR_ARM_MOV(location, arg); } /// \brief Relocate Thumb MOVW/MOVT instructions -static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) { +static std::error_code relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) { const uint16_t imm8 = result & 0xFF; const uint16_t imm3 = (result >> 8) & 0x7; const uint16_t resLo = (imm3 << 12) | imm8; @@ -348,153 +406,275 @@ static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) { const uint16_t bitI = (result >> 11) & 0x1; const uint16_t resHi = (bitI << 10) | imm4; - applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF); + return applyThumb32Reloc(location, resHi, resLo, 0x40F, 0x70FF); } /// \brief R_ARM_THM_MOVW_ABS_NC - (S + A) | T -static void relocR_ARM_THM_MOVW_ABS_NC(uint8_t *location, uint64_t P, - uint64_t S, int64_t A, - bool addressesThumb) { +static std::error_code relocR_ARM_THM_MOVW_ABS_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { uint64_t T = addressesThumb; uint32_t result = (uint32_t)((S + A) | T); const uint32_t arg = result & 0x0000FFFF; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); return relocR_ARM_THM_MOV(location, arg); } /// \brief R_ARM_THM_MOVT_ABS - S + A -static void relocR_ARM_THM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { +static std::error_code relocR_ARM_THM_MOVT_ABS(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { uint32_t result = (uint32_t)(S + A); const uint32_t arg = (result & 0xFFFF0000) >> 16; - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); return relocR_ARM_THM_MOV(location, arg); } /// \brief R_ARM_TLS_IE32 - GOT(S) + A - P => S + A - P -static void relocR_ARM_TLS_IE32(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { +static std::error_code relocR_ARM_TLS_IE32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { uint32_t result = (uint32_t)(S + A - P); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - applyArmReloc(location, result); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); } /// \brief R_ARM_TLS_LE32 - S + A - tp => S + A + tpoff -static void relocR_ARM_TLS_LE32(uint8_t *location, uint64_t P, uint64_t S, - int64_t A, uint64_t tpoff) { +static std::error_code relocR_ARM_TLS_LE32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + uint64_t tpoff) { uint32_t result = (uint32_t)(S + A + tpoff); - DEBUG_WITH_TYPE( - "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; - llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); - llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); - llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); - llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); - applyArmReloc(location, result); + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); +} + +/// \brief R_ARM_TLS_TPOFF32 - S + A - tp => S + A (offset within TLS block) +static std::error_code relocR_ARM_TLS_TPOFF32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint32_t result = (uint32_t)(S + A); + + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return applyArmReloc(location, result); +} + +template <uint32_t lshift> +static std::error_code relocR_ARM_ALU_PC_GN_NC(uint8_t *location, + uint32_t result) { + static_assert(lshift < 32 && lshift % 2 == 0, + "lshift must be even and less than word size"); + + const uint32_t rshift = 32 - lshift; + result = ((result >> lshift) & 0xFF) | ((rshift / 2) << 8); + + return applyArmReloc(location, result, 0xFFF); +} + +/// \brief R_ARM_ALU_PC_G0_NC - ((S + A) | T) - P => S + A - P +static std::error_code relocR_ARM_ALU_PC_G0_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A - P); + if (result < 0) + report_unsupported_range_group_reloc_error(); + + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr((uint32_t)result) + << "\n"); + + return relocR_ARM_ALU_PC_GN_NC<20>(location, (uint32_t)result); +} + +/// \brief R_ARM_ALU_PC_G1_NC - ((S + A) | T) - P => S + A - P +static std::error_code relocR_ARM_ALU_PC_G1_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A - P); + if (result < 0) + report_unsupported_range_group_reloc_error(); + + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr((uint32_t)result) + << "\n"); + + return relocR_ARM_ALU_PC_GN_NC<12>(location, (uint32_t)result); +} + +/// \brief R_ARM_LDR_PC_G2 - S + A - P +static std::error_code relocR_ARM_LDR_PC_G2(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A - P); + if (result < 0) + report_unsupported_range_group_reloc_error(); + + DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr((uint32_t)result) + << "\n"); + + const uint32_t mask = 0xFFF; + return applyArmReloc(location, (uint32_t)result & mask, mask); +} + +/// \brief Fixup unresolved weak reference with NOP instruction +static bool fixupUnresolvedWeakCall(uint8_t *location, + Reference::KindValue kindValue) { + // TODO: workaround for archs without NOP instruction + switch (kindValue) { + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + // Thumb32 NOP.W + write32le(location, 0x8000F3AF); + break; + case R_ARM_THM_JUMP11: + // Thumb16 NOP + write16le(location, 0xBF00); + break; + case R_ARM_CALL: + case R_ARM_JUMP24: + // A1 NOP<c>, save condition bits + applyArmReloc(location, 0x320F000, 0xFFFFFFF); + break; + default: + return false; + } + + return true; } std::error_code ARMTargetRelocationHandler::applyRelocation( - ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom, const Reference &ref) const { uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; - uint8_t *location = atomContent + ref.offsetInAtom(); - uint64_t targetVAddress = writer.addressOfAtom(ref.target()); - uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + uint8_t *loc = atomContent + ref.offsetInAtom(); + uint64_t target = writer.addressOfAtom(ref.target()); + uint64_t reloc = atom._virtualAddr + ref.offsetInAtom(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); assert(ref.kindArch() == Reference::KindArch::ARM); + // Fixup unresolved weak references + if (!target) { + bool isCallFixed = fixupUnresolvedWeakCall(loc, ref.kindValue()); + + if (isCallFixed) { + DEBUG(llvm::dbgs() << "\t\tFixup unresolved weak reference '"; + llvm::dbgs() << ref.target()->name() << "'"; + llvm::dbgs() << " at address: 0x" << Twine::utohexstr(reloc); + llvm::dbgs() << (isCallFixed ? "\n" : " isn't possible\n")); + return std::error_code(); + } + } + // Calculate proper initial addend for the relocation const Reference::Addend addend = - readAddend(location, ref.kindValue()); + readAddend(loc, ref.kindValue()) + ref.addend(); // Flags that the relocation addresses Thumb instruction - bool addressesThumb = false; - + bool thumb = false; if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) { - addressesThumb = (DefinedAtom::codeARMThumb == definedAtom->codeModel()); + thumb = isThumbCode(definedAtom); } switch (ref.kindValue()) { case R_ARM_NONE: - break; + return std::error_code(); case R_ARM_ABS32: - relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_ABS32(loc, reloc, target, addend, thumb); case R_ARM_REL32: - relocR_ARM_REL32(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_REL32(loc, reloc, target, addend, thumb); + case R_ARM_TARGET1: + if (_armLayout.target1Rel()) + return relocR_ARM_REL32(loc, reloc, target, addend, thumb); + else + return relocR_ARM_ABS32(loc, reloc, target, addend, thumb); case R_ARM_THM_CALL: // TODO: consider adding bool variable to disable J1 & J2 for archs // before ARMv6 - relocR_ARM_THM_CALL(location, relocVAddress, targetVAddress, addend, true, - addressesThumb); - break; + return relocR_ARM_THM_CALL(loc, reloc, target, addend, true, thumb); case R_ARM_CALL: - relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_CALL(loc, reloc, target, addend, thumb); case R_ARM_JUMP24: - relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_JUMP24(loc, reloc, target, addend, thumb); case R_ARM_THM_JUMP24: - relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_THM_JUMP24(loc, reloc, target, addend, thumb); case R_ARM_THM_JUMP11: - relocR_ARM_THM_JUMP11(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_THM_JUMP11(loc, reloc, target, addend); case R_ARM_MOVW_ABS_NC: - relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_MOVW_ABS_NC(loc, reloc, target, addend, thumb); case R_ARM_MOVT_ABS: - relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_MOVT_ABS(loc, reloc, target, addend); case R_ARM_THM_MOVW_ABS_NC: - relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_THM_MOVW_ABS_NC(loc, reloc, target, addend, thumb); case R_ARM_THM_MOVT_ABS: - relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_THM_MOVT_ABS(loc, reloc, target, addend); case R_ARM_PREL31: - relocR_ARM_PREL31(location, relocVAddress, targetVAddress, addend, - addressesThumb); - break; + return relocR_ARM_PREL31(loc, reloc, target, addend, thumb); case R_ARM_TLS_IE32: - relocR_ARM_TLS_IE32(location, relocVAddress, targetVAddress, addend); - break; + return relocR_ARM_TLS_IE32(loc, reloc, target, addend); case R_ARM_TLS_LE32: - relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend, - _armLayout.getTPOffset()); - break; + return relocR_ARM_TLS_LE32(loc, reloc, target, addend, + _armLayout.getTPOffset()); + case R_ARM_TLS_TPOFF32: + return relocR_ARM_TLS_TPOFF32(loc, reloc, target, addend); + case R_ARM_GOT_BREL: + return relocR_ARM_GOT_BREL(loc, reloc, target, addend, + _armLayout.getGOTSymAddr()); + case R_ARM_BASE_PREL: + // GOT origin is used for NULL symbol and when explicitly specified + if (!target || ref.target()->name().equals("_GLOBAL_OFFSET_TABLE_")) { + target = _armLayout.getGOTSymAddr(); + } else { + return make_dynamic_error_code( + "Segment-base relative addressing is not supported"); + } + return relocR_ARM_BASE_PREL(loc, reloc, target, addend); + case R_ARM_ALU_PC_G0_NC: + return relocR_ARM_ALU_PC_G0_NC(loc, reloc, target, addend); + case R_ARM_ALU_PC_G1_NC: + return relocR_ARM_ALU_PC_G1_NC(loc, reloc, target, addend); + case R_ARM_LDR_PC_G2: + return relocR_ARM_LDR_PC_G2(loc, reloc, target, addend); + case R_ARM_JUMP_SLOT: + case R_ARM_GLOB_DAT: + case R_ARM_IRELATIVE: + // Runtime only relocations. Ignore here. + return std::error_code(); + case R_ARM_V4BX: + // TODO implement + return std::error_code(); default: return make_unhandled_reloc_error(); } - return std::error_code(); + llvm_unreachable("All switch cases must return directly"); } diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h index 227d68617bf9..a1f3d091f204 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h @@ -10,26 +10,23 @@ #ifndef LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H #define LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H -#include "ARMTargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; -template <class ELFT> class ARMTargetLayout; +class ARMTargetLayout; -class ARMTargetRelocationHandler final - : public TargetRelocationHandler { +class ARMTargetRelocationHandler final : public TargetRelocationHandler { public: - ARMTargetRelocationHandler(ARMTargetLayout<ARMELFType> &layout) - : _armLayout(layout) {} + ARMTargetRelocationHandler(ARMTargetLayout &layout) : _armLayout(layout) {} std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; private: - ARMTargetLayout<ARMELFType> &_armLayout; + ARMTargetLayout &_armLayout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp index 27ec66ac5557..fc2ae75cd7a7 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp @@ -20,7 +20,7 @@ #include "ARMLinkingContext.h" #include "Atoms.h" #include "lld/Core/Simple.h" -#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" @@ -28,34 +28,77 @@ using namespace lld; using namespace lld::elf; using namespace llvm::ELF; -// ARM B/BL instructions of static relocation veneer. +namespace { +// ARM B/BL instructions of absolute relocation veneer. // TODO: consider different instruction set for archs below ARMv5 // (one as for Thumb may be used though it's less optimal). -static const uint8_t Veneer_ARM_B_BL_StaticAtomContent[8] = { - 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] +static const uint8_t Veneer_ARM_B_BL_Abs_a_AtomContent[4] = { + 0x04, 0xf0, 0x1f, 0xe5 // ldr pc, [pc, #-4] +}; +static const uint8_t Veneer_ARM_B_BL_Abs_d_AtomContent[4] = { 0x00, 0x00, 0x00, 0x00 // <target_symbol_address> }; -// Thumb B/BL instructions of static relocation veneer. +// Thumb B/BL instructions of absolute relocation veneer. // TODO: consider different instruction set for archs above ARMv5 // (one as for ARM may be used since it's more optimal). -static const uint8_t Veneer_THM_B_BL_StaticAtomContent[8] = { +static const uint8_t Veneer_THM_B_BL_Abs_t_AtomContent[4] = { 0x78, 0x47, // bx pc - 0x00, 0x00, // nop + 0x00, 0x00 // nop +}; +static const uint8_t Veneer_THM_B_BL_Abs_a_AtomContent[4] = { 0xfe, 0xff, 0xff, 0xea // b <target_symbol_address> }; // .got values static const uint8_t ARMGotAtomContent[4] = {0}; -namespace { +// .plt value (entry 0) +static const uint8_t ARMPlt0_a_AtomContent[16] = { + 0x04, 0xe0, 0x2d, 0xe5, // push {lr} + 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, [pc, #4] + 0x0e, 0xe0, 0x8f, 0xe0, // add lr, pc, lr + 0x00, 0xf0, 0xbe, 0xe5 // ldr pc, [lr, #0]! +}; +static const uint8_t ARMPlt0_d_AtomContent[4] = { + 0x00, 0x00, 0x00, 0x00 // <got1_symbol_address> +}; + +// .plt values (other entries) +static const uint8_t ARMPltAtomContent[12] = { + 0x00, 0xc0, 0x8f, 0xe2, // add ip, pc, #offset[G0] + 0x00, 0xc0, 0x8c, 0xe2, // add ip, ip, #offset[G1] + 0x00, 0xf0, 0xbc, 0xe5 // ldr pc, [ip, #offset[G2]]! +}; + +// Veneer for switching from Thumb to ARM code for PLT entries. +static const uint8_t ARMPltVeneerAtomContent[4] = { + 0x78, 0x47, // bx pc + 0x00, 0x00 // nop +}; + +// Determine proper names for mapping symbols. +static std::string getMappingAtomName(DefinedAtom::CodeModel model, + const std::string &part) { + switch (model) { + case DefinedAtom::codeARM_a: + return part.empty() ? "$a" : "$a." + part; + case DefinedAtom::codeARM_d: + return part.empty() ? "$d" : "$d." + part; + case DefinedAtom::codeARM_t: + return part.empty() ? "$t" : "$t." + part; + default: + llvm_unreachable("Wrong code model of mapping atom"); + } +} + /// \brief Atoms that hold veneer code. class VeneerAtom : public SimpleELFDefinedAtom { StringRef _section; public: - VeneerAtom(const File &f, StringRef secName) - : SimpleELFDefinedAtom(f), _section(secName) {} + VeneerAtom(const File &f, StringRef secName, const std::string &name = "") + : SimpleELFDefinedAtom(f), _section(secName), _name(name) {} Scope scope() const override { return DefinedAtom::scopeTranslationUnit; } @@ -65,58 +108,208 @@ public: StringRef customSectionName() const override { return _section; } - ContentType contentType() const override { - return DefinedAtom::typeCode; - } + ContentType contentType() const override { return DefinedAtom::typeCode; } uint64_t size() const override { return rawContent().size(); } ContentPermissions permissions() const override { return permR_X; } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } StringRef name() const override { return _name; } + +private: std::string _name; }; -/// \brief Atoms that hold veneer for statically relocated -/// ARM B/BL instructions. -class Veneer_ARM_B_BL_StaticAtom : public VeneerAtom { +/// \brief Atoms that hold veneer for relocated ARM B/BL instructions +/// in absolute code. +class Veneer_ARM_B_BL_Abs_a_Atom : public VeneerAtom { public: - Veneer_ARM_B_BL_StaticAtom(const File &f, StringRef secName) - : VeneerAtom(f, secName) {} + Veneer_ARM_B_BL_Abs_a_Atom(const File &f, StringRef secName, + const std::string &name) + : VeneerAtom(f, secName, name) {} ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(Veneer_ARM_B_BL_StaticAtomContent); + return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_a_AtomContent); } }; -/// \brief Atoms that hold veneer for statically relocated -/// Thumb B/BL instructions. -class Veneer_THM_B_BL_StaticAtom : public VeneerAtom { +class Veneer_ARM_B_BL_Abs_d_Atom : public VeneerAtom { public: - Veneer_THM_B_BL_StaticAtom(const File &f, StringRef secName) + Veneer_ARM_B_BL_Abs_d_Atom(const File &f, StringRef secName) : VeneerAtom(f, secName) {} + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_d_AtomContent); + } +}; + +/// \brief Atoms that hold veneer for relocated Thumb B/BL instructions +/// in absolute code. +class Veneer_THM_B_BL_Abs_t_Atom : public VeneerAtom { +public: + Veneer_THM_B_BL_Abs_t_Atom(const File &f, StringRef secName, + const std::string &name) + : VeneerAtom(f, secName, name) {} + DefinedAtom::CodeModel codeModel() const override { return DefinedAtom::codeARMThumb; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(Veneer_THM_B_BL_StaticAtomContent); + return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_t_AtomContent); } }; +class Veneer_THM_B_BL_Abs_a_Atom : public VeneerAtom { +public: + Veneer_THM_B_BL_Abs_a_Atom(const File &f, StringRef secName) + : VeneerAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_a_AtomContent); + } +}; + +template <DefinedAtom::CodeModel Model> +class ARMVeneerMappingAtom : public VeneerAtom { +public: + ARMVeneerMappingAtom(const File &f, StringRef secName, StringRef name) + : VeneerAtom(f, secName, getMappingAtomName(Model, name)) { + static_assert((Model == DefinedAtom::codeARM_a || + Model == DefinedAtom::codeARM_d || + Model == DefinedAtom::codeARM_t), + "Only mapping atom types are allowed"); + } + + uint64_t size() const override { return 0; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + DefinedAtom::CodeModel codeModel() const override { return Model; } +}; + +template <class BaseAtom, DefinedAtom::CodeModel Model> +class BaseMappingAtom : public BaseAtom { +public: + BaseMappingAtom(const File &f, StringRef secName, StringRef name) + : BaseAtom(f, secName) { + static_assert((Model == DefinedAtom::codeARM_a || + Model == DefinedAtom::codeARM_d || + Model == DefinedAtom::codeARM_t), + "Only mapping atom types are allowed"); +#ifndef NDEBUG + _name = name; +#else + _name = getMappingAtomName(Model, name); +#endif + } + + DefinedAtom::CodeModel codeModel() const override { +#ifndef NDEBUG + return isThumbCode(Model) ? DefinedAtom::codeARMThumb : DefinedAtom::codeNA; +#else + return Model; +#endif + } + + StringRef name() const override { return _name; } + +private: + std::string _name; +}; + /// \brief Atoms that are used by ARM dynamic linking class ARMGOTAtom : public GOTAtom { public: - ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + ARMGOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef<uint8_t> rawContent() const override { return llvm::makeArrayRef(ARMGotAtomContent); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } + +protected: + // Constructor for PLTGOT atom. + ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} +}; + +class ARMGOTPLTAtom : public ARMGOTAtom { +public: + ARMGOTPLTAtom(const File &f) : ARMGOTAtom(f, ".got.plt") {} +}; + +/// \brief Proxy class to keep type compatibility with PLT0Atom. +class ARMPLT0Atom : public PLT0Atom { +public: + ARMPLT0Atom(const File &f, StringRef) : PLT0Atom(f) {} +}; + +/// \brief PLT0 entry atom. +/// Serves as a mapping symbol in the release mode. +class ARMPLT0_a_Atom + : public BaseMappingAtom<ARMPLT0Atom, DefinedAtom::codeARM_a> { +public: + ARMPLT0_a_Atom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPlt0_a_AtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +class ARMPLT0_d_Atom + : public BaseMappingAtom<ARMPLT0Atom, DefinedAtom::codeARM_d> { +public: + ARMPLT0_d_Atom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPlt0_d_AtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +/// \brief PLT entry atom. +/// Serves as a mapping symbol in the release mode. +class ARMPLTAtom : public BaseMappingAtom<PLTAtom, DefinedAtom::codeARM_a> { +public: + ARMPLTAtom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPltAtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +/// \brief Veneer atom for PLT entry. +/// Serves as a mapping symbol in the release mode. +class ARMPLTVeneerAtom + : public BaseMappingAtom<PLTAtom, DefinedAtom::codeARM_t> { +public: + ARMPLTVeneerAtom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPltVeneerAtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +/// \brief Atom which represents an object for which a COPY relocation will +/// be generated. +class ARMObjectAtom : public ObjectAtom { +public: + ARMObjectAtom(const File &f) : ObjectAtom(f) {} + Alignment alignment() const override { return 4; } }; class ELFPassFile : public SimpleFile { @@ -140,30 +333,92 @@ template <class Derived> class ARMRelocationPass : public Pass { return; assert(ref.kindArch() == Reference::KindArch::ARM); switch (ref.kindValue()) { + case R_ARM_ABS32: + case R_ARM_REL32: + case R_ARM_TARGET1: + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + static_cast<Derived *>(this)->handlePlain(isThumbCode(&atom), ref); + break; + case R_ARM_THM_CALL: + case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_THM_JUMP24: - static_cast<Derived *>(this)->handleVeneer(atom, ref); - break; + case R_ARM_THM_JUMP11: { + const auto actualModel = actualSourceCodeModel(atom, ref); + const bool fromThumb = isThumbCode(actualModel); + static_cast<Derived *>(this)->handlePlain(fromThumb, ref); + static_cast<Derived *>(this)->handleVeneer(atom, fromThumb, ref); + } break; case R_ARM_TLS_IE32: static_cast<Derived *>(this)->handleTLSIE32(ref); break; + case R_ARM_GOT_BREL: + static_cast<Derived *>(this)->handleGOT(ref); + break; + default: + break; } } protected: - std::error_code handleVeneer(const DefinedAtom &atom, const Reference &ref) { + /// \brief Determine source atom's actual code model. + /// + /// Actual code model may differ from the existing one if fixup + /// is possible on the later stages for given relocation type. + DefinedAtom::CodeModel actualSourceCodeModel(const DefinedAtom &atom, + const Reference &ref) { + const auto kindValue = ref.kindValue(); + if (kindValue != R_ARM_CALL && kindValue != R_ARM_THM_CALL) + return atom.codeModel(); + + // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL) + // fixup isn't possible without veneer generation for archs below ARMv5. + + auto actualModel = atom.codeModel(); + if (const auto *da = dyn_cast<DefinedAtom>(ref.target())) { + actualModel = da->codeModel(); + } else if (const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Code) { + // PLT entry will be generated here - assume we don't want a veneer + // on top of it and prefer instruction fixup if needed. + actualModel = DefinedAtom::codeNA; + } + } + return actualModel; + } + + std::error_code handleVeneer(const DefinedAtom &atom, bool fromThumb, + const Reference &ref) { + // Actual instruction mode differs meaning that further fixup will be + // applied. + if (isThumbCode(&atom) != fromThumb) + return std::error_code(); + + const VeneerAtom *(Derived::*getVeneer)(const DefinedAtom *, StringRef) = + nullptr; + const auto kindValue = ref.kindValue(); + switch (kindValue) { + case R_ARM_JUMP24: + getVeneer = &Derived::getVeneer_ARM_B_BL; + break; + case R_ARM_THM_JUMP24: + getVeneer = &Derived::getVeneer_THM_B_BL; + break; + default: + return std::error_code(); + } + // Target symbol and relocated place should have different // instruction sets in order a veneer to be generated in between. const auto *target = dyn_cast<DefinedAtom>(ref.target()); - if (!target || target->codeModel() == atom.codeModel()) + if (!target || isThumbCode(target) == isThumbCode(&atom)) return std::error_code(); - // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL) - // fixup isn't possible without veneer generation for archs below ARMv5. - // Veneers may only be generated for STT_FUNC target symbols // or for symbols located in sections different to the place of relocation. - const auto kindValue = ref.kindValue(); StringRef secName = atom.customSectionName(); if (DefinedAtom::typeCode != target->contentType() && !target->customSectionName().equals(secName)) { @@ -182,29 +437,69 @@ protected: llvm_unreachable(errStr.c_str()); } - const Atom *veneer = nullptr; - switch (kindValue) { - case R_ARM_JUMP24: - veneer = static_cast<Derived *>(this) - ->getVeneer_ARM_B_BL(target, secName); - break; - case R_ARM_THM_JUMP24: - veneer = static_cast<Derived *>(this) - ->getVeneer_THM_B_BL(target, secName); - break; - default: - llvm_unreachable("Unhandled reference type for veneer generation"); - } + assert(getVeneer && "The veneer handler is missing"); + const Atom *veneer = + (static_cast<Derived *>(this)->*getVeneer)(target, secName); assert(veneer && "The veneer is not set"); const_cast<Reference &>(ref).setTarget(veneer); return std::error_code(); } + /// \brief Get the veneer for ARM B/BL instructions + /// in absolute code. + const VeneerAtom *getVeneer_ARM_B_BL_Abs(const DefinedAtom *da, + StringRef secName) { + auto veneer = _veneerAtoms.lookup(da); + if (!veneer.empty()) + return veneer._veneer; + + std::string name = "__"; + name += da->name(); + name += "_from_arm"; + // Create parts of veneer with mapping symbols. + auto v_a = + new (_file._alloc) Veneer_ARM_B_BL_Abs_a_Atom(_file, secName, name); + addVeneerWithMapping<DefinedAtom::codeARM_a>(da, v_a, name); + auto v_d = new (_file._alloc) Veneer_ARM_B_BL_Abs_d_Atom(_file, secName); + addVeneerWithMapping<DefinedAtom::codeARM_d>(v_a, v_d, name); + + // Fake reference to show connection between parts of veneer. + v_a->addReferenceELF_ARM(R_ARM_NONE, 0, v_d, 0); + // Real reference to fixup. + v_d->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); + return v_a; + } + + /// \brief Get the veneer for Thumb B/BL instructions + /// in absolute code. + const VeneerAtom *getVeneer_THM_B_BL_Abs(const DefinedAtom *da, + StringRef secName) { + auto veneer = _veneerAtoms.lookup(da); + if (!veneer.empty()) + return veneer._veneer; + + std::string name = "__"; + name += da->name(); + name += "_from_thumb"; + // Create parts of veneer with mapping symbols. + auto v_t = + new (_file._alloc) Veneer_THM_B_BL_Abs_t_Atom(_file, secName, name); + addVeneerWithMapping<DefinedAtom::codeARM_t>(da, v_t, name); + auto v_a = new (_file._alloc) Veneer_THM_B_BL_Abs_a_Atom(_file, secName); + addVeneerWithMapping<DefinedAtom::codeARM_a>(v_t, v_a, name); + + // Fake reference to show connection between parts of veneer. + v_t->addReferenceELF_ARM(R_ARM_NONE, 0, v_a, 0); + // Real reference to fixup. + v_a->addReferenceELF_ARM(R_ARM_JUMP24, 0, da, 0); + return v_t; + } + std::error_code handleTLSIE32(const Reference &ref) { if (const auto *target = dyn_cast<DefinedAtom>(ref.target())) { - const_cast<Reference &>(ref).setTarget( - static_cast<Derived *>(this)->getTLSTPOFF32(target)); + const_cast<Reference &>(ref) + .setTarget(static_cast<Derived *>(this)->getTLSTPOFF32(target)); return std::error_code(); } llvm_unreachable("R_ARM_TLS_IE32 reloc targets wrong atom type"); @@ -213,20 +508,160 @@ protected: /// \brief Create a GOT entry for TLS with reloc type and addend specified. template <Reference::KindValue R_ARM_TLS, Reference::Addend A = 0> const GOTAtom *getGOTTLSEntry(const DefinedAtom *da) { - auto got = _gotMap.find(da); - if (got != _gotMap.end()) - return got->second; - auto g = new (_file._alloc) ARMGOTAtom(_file, ".got"); - g->addReferenceELF_ARM(R_ARM_TLS, 0, da, A); + StringRef source; #ifndef NDEBUG - g->_name = "__got_tls_"; + source = "_tls_"; +#endif + return getGOT<R_ARM_TLS, A>(da, source); + } + + /// \brief Add veneer with mapping symbol. + template <DefinedAtom::CodeModel Model> + void addVeneerWithMapping(const DefinedAtom *da, VeneerAtom *va, + const std::string &name) { + assert(_veneerAtoms.lookup(da).empty() && + "Veneer or mapping already exists"); + auto *ma = new (_file._alloc) + ARMVeneerMappingAtom<Model>(_file, va->customSectionName(), name); + + // Fake reference to show connection between the mapping symbol and veneer. + va->addReferenceELF_ARM(R_ARM_NONE, 0, ma, 0); + _veneerAtoms[da] = VeneerWithMapping(va, ma); + } + + /// \brief get a veneer for a PLT entry. + const PLTAtom *getPLTVeneer(const Atom *da, PLTAtom *pa, StringRef source) { + std::string name = "__plt_from_thumb"; + name += source.empty() ? "_" : source; + name += da->name(); + // Create veneer for PLT entry. + auto va = new (_file._alloc) ARMPLTVeneerAtom(_file, name); + // Fake reference to show connection between veneer and PLT entry. + va->addReferenceELF_ARM(R_ARM_NONE, 0, pa, 0); + + _pltAtoms[da] = PLTWithVeneer(pa, va); + return va; + } + + typedef const GOTAtom *(Derived::*GOTFactory)(const Atom *); + + /// \brief get a PLT entry referencing PLTGOT entry. + /// + /// If the entry does not exist, both GOT and PLT entry are created. + const PLTAtom *getPLT(const Atom *da, bool fromThumb, GOTFactory gotFactory, + StringRef source = "") { + auto pltVeneer = _pltAtoms.lookup(da); + if (!pltVeneer.empty()) { + // Return clean PLT entry provided it is ARM code. + if (!fromThumb) + return pltVeneer._plt; + + // Check if veneer is present for Thumb to ARM transition. + if (pltVeneer._veneer) + return pltVeneer._veneer; + + // Create veneer for existing PLT entry. + return getPLTVeneer(da, pltVeneer._plt, source); + } + + // Create specific GOT entry. + const auto *ga = (static_cast<Derived *>(this)->*gotFactory)(da); + assert(_gotpltAtoms.lookup(da) == ga && + "GOT entry should be added to the PLTGOT map"); + assert(ga->customSectionName() == ".got.plt" && + "GOT entry should be in a special section"); + + std::string name = "__plt"; + name += source.empty() ? "_" : source; + name += da->name(); + // Create PLT entry for the GOT entry. + auto pa = new (_file._alloc) ARMPLTAtom(_file, name); + pa->addReferenceELF_ARM(R_ARM_ALU_PC_G0_NC, 0, ga, -8); + pa->addReferenceELF_ARM(R_ARM_ALU_PC_G1_NC, 4, ga, -4); + pa->addReferenceELF_ARM(R_ARM_LDR_PC_G2, 8, ga, 0); + + // Since all PLT entries are in ARM code, Thumb to ARM + // switching should be added if the relocated place contais Thumb code. + if (fromThumb) + return getPLTVeneer(da, pa, source); + + // Otherwise just add PLT entry and return it to the caller. + _pltAtoms[da] = PLTWithVeneer(pa); + return pa; + } + + /// \brief Create the GOT entry for a given IFUNC Atom. + const GOTAtom *createIFUNCGOT(const Atom *da) { + assert(!_gotpltAtoms.lookup(da) && "IFUNC GOT entry already exists"); + auto g = new (_file._alloc) ARMGOTPLTAtom(_file); + g->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); + g->addReferenceELF_ARM(R_ARM_IRELATIVE, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_ifunc_"; g->_name += da->name(); #endif - _gotMap[da] = g; - _gotVector.push_back(g); + _gotpltAtoms[da] = g; return g; } + /// \brief get the PLT entry for a given IFUNC Atom. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da, bool fromThumb) { + return getPLT(da, fromThumb, &Derived::createIFUNCGOT, "_ifunc_"); + } + + /// \brief Redirect the call to the PLT stub for the target IFUNC. + /// + /// This create a PLT and GOT entry for the IFUNC if one does not exist. The + /// GOT entry and a IRELATIVE relocation to the original target resolver. + std::error_code handleIFUNC(bool fromThumb, const Reference &ref) { + auto target = dyn_cast<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) { + const_cast<Reference &>(ref) + .setTarget(getIFUNCPLTEntry(target, fromThumb)); + } + return std::error_code(); + } + + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) ARMGOTPLTAtom(_file); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + + /// \brief Create regular GOT entry which cannot be used in PLTGOT operation. + template <Reference::KindValue R_ARM_REL, Reference::Addend A = 0> + const GOTAtom *getGOT(const Atom *da, StringRef source = "") { + if (auto got = _gotAtoms.lookup(da)) + return got; + auto g = new (_file._alloc) ARMGOTAtom(_file); + g->addReferenceELF_ARM(R_ARM_REL, 0, da, A); +#ifndef NDEBUG + g->_name = "__got"; + g->_name += source.empty() ? "_" : source; + g->_name += da->name(); +#endif + _gotAtoms[da] = g; + return g; + } + + /// \brief get GOT entry for a regular defined atom. + const GOTAtom *getGOTEntry(const DefinedAtom *da) { + return getGOT<R_ARM_ABS32>(da); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const auto *da = dyn_cast<DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOTEntry(da)); + return std::error_code(); + } + public: ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} @@ -238,35 +673,35 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "ARM GOT/PLT Pass"); DEBUG_WITH_TYPE( "ARM", llvm::dbgs() << "Undefined Atoms" << "\n"; for (const auto &atom - : mf->undefined()) { + : mf.undefined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Shared Library Atoms" << "\n"; for (const auto &atom - : mf->sharedLibrary()) { + : mf.sharedLibrary()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Absolute Atoms" << "\n"; for (const auto &atom - : mf->absolute()) { + : mf.absolute()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Defined Atoms" << "\n"; for (const auto &atom - : mf->defined()) { + : mf.defined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; }); // Process all references. - for (const auto &atom : mf->defined()) { + for (const auto &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } @@ -274,14 +709,58 @@ public: // Add all created atoms to the link. uint64_t ordinal = 0; - for (auto &got : _gotVector) { + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); + _plt0_d->setOrdinal(ordinal++); + mf.addAtom(*_plt0_d); + } + for (auto &pltKV : _pltAtoms) { + auto &plt = pltKV.second; + if (auto *v = plt._veneer) { + v->setOrdinal(ordinal++); + mf.addAtom(*v); + } + auto *p = plt._plt; + p->setOrdinal(ordinal++); + mf.addAtom(*p); + } + if (_null) { + _null->setOrdinal(ordinal++); + mf.addAtom(*_null); + } + if (_plt0) { + _got0->setOrdinal(ordinal++); + mf.addAtom(*_got0); + _got1->setOrdinal(ordinal++); + mf.addAtom(*_got1); + } + for (auto &gotKV : _gotAtoms) { + auto &got = gotKV.second; got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); + } + for (auto &gotKV : _gotpltAtoms) { + auto &got = gotKV.second; + got->setOrdinal(ordinal++); + mf.addAtom(*got); + } + for (auto &objectKV : _objectAtoms) { + auto &obj = objectKV.second; + obj->setOrdinal(ordinal++); + mf.addAtom(*obj); } - for (auto &veneer : _veneerVector) { - veneer->setOrdinal(ordinal++); - mf->addAtom(*veneer); + for (auto &veneerKV : _veneerAtoms) { + auto &veneer = veneerKV.second; + auto *m = veneer._mapping; + m->setOrdinal(ordinal++); + mf.addAtom(*m); + auto *v = veneer._veneer; + v->setOrdinal(ordinal++); + mf.addAtom(*v); } + + return std::error_code(); } protected: @@ -290,16 +769,56 @@ protected: const ELFLinkingContext &_ctx; /// \brief Map Atoms to their GOT entries. - llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + llvm::MapVector<const Atom *, GOTAtom *> _gotAtoms; - /// \brief Map Atoms to their veneers. - llvm::DenseMap<const Atom *, VeneerAtom *> _veneerMap; + /// \brief Map Atoms to their PLTGOT entries. + llvm::MapVector<const Atom *, GOTAtom *> _gotpltAtoms; + + /// \brief Map Atoms to their Object entries. + llvm::MapVector<const Atom *, ObjectAtom *> _objectAtoms; - /// \brief the list of GOT/PLT atoms - std::vector<GOTAtom *> _gotVector; + /// \brief Map Atoms to their PLT entries depending on the code model. + struct PLTWithVeneer { + PLTWithVeneer(PLTAtom *p = nullptr, PLTAtom *v = nullptr) + : _plt(p), _veneer(v) {} - /// \brief the list of veneer atoms. - std::vector<VeneerAtom *> _veneerVector; + bool empty() const { + assert((_plt || !_veneer) && "Veneer appears without PLT entry"); + return !_plt && !_veneer; + } + + PLTAtom *_plt; + PLTAtom *_veneer; + }; + llvm::MapVector<const Atom *, PLTWithVeneer> _pltAtoms; + + /// \brief Map Atoms to their veneers. + struct VeneerWithMapping { + VeneerWithMapping(VeneerAtom *v = nullptr, VeneerAtom *m = nullptr) + : _veneer(v), _mapping(m) {} + + bool empty() const { + assert(((bool)_veneer == (bool)_mapping) && + "Mapping symbol should always be paired with veneer"); + return !_veneer && !_mapping; + } + + VeneerAtom *_veneer; + VeneerAtom *_mapping; + }; + llvm::MapVector<const Atom *, VeneerWithMapping> _veneerAtoms; + + /// \brief GOT entry that is always 0. Used for undefined weaks. + GOTAtom *_null = nullptr; + + /// \brief The got and plt entries for .PLT0. This is used to call into the + /// dynamic linker for symbol resolution. + /// @{ + PLT0Atom *_plt0 = nullptr; + PLT0Atom *_plt0_d = nullptr; + GOTAtom *_got0 = nullptr; + GOTAtom *_got1 = nullptr; + /// @} }; /// This implements the static relocation model. Meaning GOT and PLT entries are @@ -314,47 +833,138 @@ public: ARMStaticRelocationPass(const elf::ARMLinkingContext &ctx) : ARMRelocationPass(ctx) {} + /// \brief Handle ordinary relocation references. + std::error_code handlePlain(bool fromThumb, const Reference &ref) { + return handleIFUNC(fromThumb, ref); + } + /// \brief Get the veneer for ARM B/BL instructions. const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da, StringRef secName) { - auto veneer = _veneerMap.find(da); - if (_veneerMap.end() != veneer) - return veneer->second; + return getVeneer_ARM_B_BL_Abs(da, secName); + } + + /// \brief Get the veneer for Thumb B/BL instructions. + const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da, + StringRef secName) { + return getVeneer_THM_B_BL_Abs(da, secName); + } + + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + } +}; + +/// This implements the dynamic relocation model. GOT and PLT entries are +/// created for references that cannot be directly resolved. +class ARMDynamicRelocationPass final + : public ARMRelocationPass<ARMDynamicRelocationPass> { +public: + ARMDynamicRelocationPass(const elf::ARMLinkingContext &ctx) + : ARMRelocationPass(ctx) {} + + /// \brief get the PLT entry for a given atom. + const PLTAtom *getPLTEntry(const SharedLibraryAtom *sla, bool fromThumb) { + return getPLT(sla, fromThumb, &ARMDynamicRelocationPass::createPLTGOT); + } + + /// \brief Create the GOT entry for a given atom. + const GOTAtom *createPLTGOT(const Atom *da) { + assert(!_gotpltAtoms.lookup(da) && "PLTGOT entry already exists"); + auto g = new (_file._alloc) ARMGOTPLTAtom(_file); + g->addReferenceELF_ARM(R_ARM_ABS32, 0, getPLT0(), 0); + g->addReferenceELF_ARM(R_ARM_JUMP_SLOT, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_plt0_"; + g->_name += da->name(); +#endif + _gotpltAtoms[da] = g; + return g; + } + + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { + if (auto obj = _objectAtoms.lookup(a)) + return obj; - auto v = new (_file._alloc) Veneer_ARM_B_BL_StaticAtom(_file, secName); - v->addReferenceELF_ARM(R_ARM_ABS32, 4, da, 0); + auto oa = new (_file._alloc) ARMObjectAtom(_file); + oa->addReferenceELF_ARM(R_ARM_COPY, 0, oa, 0); - v->_name = "__"; - v->_name += da->name(); - v->_name += "_from_arm"; + oa->_name = a->name(); + oa->_size = a->size(); + + _objectAtoms[a] = oa; + return oa; + } - _veneerMap[da] = v; - _veneerVector.push_back(v); - return v; + /// \brief Handle ordinary relocation references. + std::error_code handlePlain(bool fromThumb, const Reference &ref) { + if (auto sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data && + _ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + } else if (sla->type() == SharedLibraryAtom::Type::Code) { + const_cast<Reference &>(ref).setTarget(getPLTEntry(sla, fromThumb)); + } + return std::error_code(); + } + return handleIFUNC(fromThumb, ref); + } + + /// \brief Get the veneer for ARM B/BL instructions. + const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da, + StringRef secName) { + if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + return getVeneer_ARM_B_BL_Abs(da, secName); + } + llvm_unreachable("Handle ARM veneer for DSOs"); } /// \brief Get the veneer for Thumb B/BL instructions. const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da, StringRef secName) { - auto veneer = _veneerMap.find(da); - if (_veneerMap.end() != veneer) - return veneer->second; + if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + return getVeneer_THM_B_BL_Abs(da, secName); + } + llvm_unreachable("Handle Thumb veneer for DSOs"); + } - auto v = new (_file._alloc) Veneer_THM_B_BL_StaticAtom(_file, secName); - v->addReferenceELF_ARM(R_ARM_JUMP24, 4, da, 0); + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_TPOFF32>(da); + } - v->_name = "__"; - v->_name += da->name(); - v->_name += "_from_thumb"; + const PLT0Atom *getPLT0() { + if (_plt0) + return _plt0; + // Fill in the null entry. + getNullGOT(); + _plt0 = new (_file._alloc) ARMPLT0_a_Atom(_file, "__PLT0"); + _plt0_d = new (_file._alloc) ARMPLT0_d_Atom(_file, "__PLT0_d"); + _got0 = new (_file._alloc) ARMGOTPLTAtom(_file); + _got1 = new (_file._alloc) ARMGOTPLTAtom(_file); + _plt0_d->addReferenceELF_ARM(R_ARM_REL32, 0, _got1, 0); + // Fake reference to show connection between the GOT and PLT entries. + _plt0->addReferenceELF_ARM(R_ARM_NONE, 0, _got0, 0); + // Fake reference to show connection between parts of PLT entry. + _plt0->addReferenceELF_ARM(R_ARM_NONE, 0, _plt0_d, 0); +#ifndef NDEBUG + _got0->_name = "__got0"; + _got1->_name = "__got1"; +#endif + return _plt0; + } - _veneerMap[da] = v; - _veneerVector.push_back(v); - return v; + const GOTAtom *getSharedGOTEntry(const SharedLibraryAtom *sla) { + return getGOT<R_ARM_GLOB_DAT>(sla); } - /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. - const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { - return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + std::error_code handleGOT(const Reference &ref) { + if (const auto sla = dyn_cast<const SharedLibraryAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getSharedGOTEntry(sla)); + return std::error_code(); + } + return ARMRelocationPass::handleGOT(ref); } }; @@ -365,8 +975,10 @@ lld::elf::createARMRelocationPass(const ARMLinkingContext &ctx) { switch (ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: if (ctx.isDynamic()) - llvm_unreachable("Unhandled output file type"); + return llvm::make_unique<ARMDynamicRelocationPass>(ctx); return llvm::make_unique<ARMStaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<ARMDynamicRelocationPass>(ctx); default: llvm_unreachable("Unhandled output file type"); } diff --git a/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h index 540a480421a8..85b9c9162589 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h +++ b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h @@ -10,34 +10,47 @@ #ifndef LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H #define LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H +#include "SectionChunks.h" +#include "TargetLayout.h" +#include "ARMELFFile.h" + namespace lld { namespace elf { /// \brief The SymbolTable class represents the symbol table in a ELF file -template<class ELFT> -class ARMSymbolTable : public SymbolTable<ELFT> { +class ARMSymbolTable : public SymbolTable<ELF32LE> { public: - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; - ARMSymbolTable(const ELFLinkingContext &context); + ARMSymbolTable(const ELFLinkingContext &ctx); void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, int64_t addr) override; }; -template <class ELFT> -ARMSymbolTable<ELFT>::ARMSymbolTable(const ELFLinkingContext &context) - : SymbolTable<ELFT>(context, ".symtab", - DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE) {} +ARMSymbolTable::ARMSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable(ctx, ".symtab", TargetLayout<ELF32LE>::ORDER_SYMBOL_TABLE) {} + +void ARMSymbolTable::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable::addDefinedAtom(sym, da, addr); -template <class ELFT> -void ARMSymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, - int64_t addr) { - SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); + if ((ARMELFDefinedAtom::ARMContentType)da->contentType() == + ARMELFDefinedAtom::typeARMExidx) + sym.st_value = addr; - // Set zero bit to distinguish symbols addressing Thumb instructions + // Set zero bit to distinguish real symbols addressing Thumb instructions. + // Don't care about mapping symbols like $t and others. if (DefinedAtom::codeARMThumb == da->codeModel()) sym.st_value = static_cast<int64_t>(sym.st_value) | 0x1; + + // Mapping symbols should have special values of binding, type and size set. + if ((DefinedAtom::codeARM_a == da->codeModel()) || + (DefinedAtom::codeARM_d == da->codeModel()) || + (DefinedAtom::codeARM_t == da->codeModel())) { + sym.setBindingAndType(llvm::ELF::STB_LOCAL, llvm::ELF::STT_NOTYPE); + sym.st_size = 0; + } } } // elf diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp index de90f490f621..e1f5eadbe789 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp @@ -9,36 +9,24 @@ #include "Atoms.h" #include "ARMExecutableWriter.h" +#include "ARMDynamicLibraryWriter.h" #include "ARMTargetHandler.h" #include "ARMLinkingContext.h" using namespace lld; using namespace elf; -ARMTargetHandler::ARMTargetHandler(ARMLinkingContext &context) - : _context(context), _armTargetLayout( - new ARMTargetLayout<ARMELFType>(context)), - _armRelocationHandler(new ARMTargetRelocationHandler( - *_armTargetLayout.get())) {} - -void ARMTargetHandler::registerRelocationNames(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::ARM, - kindStrings); -} +ARMTargetHandler::ARMTargetHandler(ARMLinkingContext &ctx) + : _ctx(ctx), _targetLayout(new ARMTargetLayout(ctx)), + _relocationHandler(new ARMTargetRelocationHandler(*_targetLayout)) {} std::unique_ptr<Writer> ARMTargetHandler::getWriter() { - switch (this->_context.getOutputELFType()) { + switch (this->_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new ARMExecutableWriter<ARMELFType>(_context, *_armTargetLayout.get())); + return llvm::make_unique<ARMExecutableWriter>(_ctx, *_targetLayout); + case llvm::ELF::ET_DYN: + return llvm::make_unique<ARMDynamicLibraryWriter>(_ctx, *_targetLayout); default: llvm_unreachable("unsupported output type"); } } - -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), - -const Registry::KindStrings ARMTargetHandler::kindStrings[] = { -#include "llvm/Support/ELFRelocs/ARM.def" - LLD_KIND_STRING_END -}; diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h index 10641954da25..0352e81a1f61 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h +++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h @@ -11,75 +11,161 @@ #define LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H #include "ARMELFFile.h" -#include "ARMELFReader.h" #include "ARMRelocationHandler.h" -#include "DefaultTargetHandler.h" +#include "ELFReader.h" #include "TargetLayout.h" -#include "lld/Core/Simple.h" -#include "llvm/ADT/Optional.h" -#include <map> - namespace lld { +class ELFLinkingContext; + namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; -class ARMLinkingContext; -template <class ELFT> class ARMTargetLayout : public TargetLayout<ELFT> { +/// \brief ARM specific section (.ARM.exidx) with indexes to exception handlers +class ARMExidxSection : public AtomSection<ELF32LE> { + typedef AtomSection<ELF32LE> Base; + public: - ARMTargetLayout(ARMLinkingContext &context) - : TargetLayout<ELFT>(context) {} + ARMExidxSection(const ELFLinkingContext &ctx, StringRef sectionName, + int32_t permissions, int32_t order) + : Base(ctx, sectionName, ARMELFDefinedAtom::typeARMExidx, permissions, + order) { + this->_type = SHT_ARM_EXIDX; + this->_isLoadedInMemory = true; + } - uint64_t getTPOffset() { - if (_tpOff.hasValue()) - return *_tpOff; + bool hasOutputSegment() const override { return true; } - for (const auto &phdr : *this->_programHeader) { - if (phdr->p_type == llvm::ELF::PT_TLS) { - _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); - return *_tpOff; - } + const AtomLayout *appendAtom(const Atom *atom) override { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + assert((ARMELFDefinedAtom::ARMContentType)definedAtom->contentType() == + ARMELFDefinedAtom::typeARMExidx && + "atom content type for .ARM.exidx section has to be typeARMExidx"); + + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = alignOffset(this->memSize(), atomAlign); + + _atoms.push_back(new (_alloc) AtomLayout(atom, fOffset, 0)); + this->_fsize = fOffset + definedAtom->size(); + this->_msize = mOffset + definedAtom->size(); + DEBUG_WITH_TYPE("Section", llvm::dbgs() + << "[" << this->name() << " " << this << "] " + << "Adding atom: " << atom->name() << "@" + << fOffset << "\n"); + + uint64_t alignment = atomAlign.value; + if (this->_alignment < alignment) + this->_alignment = alignment; + + return _atoms.back(); + } +}; + +class ARMTargetLayout : public TargetLayout<ELF32LE> { +public: + enum ARMSectionOrder { + ORDER_ARM_EXIDX = TargetLayout::ORDER_EH_FRAME + 1, + }; + + ARMTargetLayout(ELFLinkingContext &ctx) : TargetLayout(ctx) {} + + SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override { + switch (contentType) { + case ARMELFDefinedAtom::typeARMExidx: + return ORDER_ARM_EXIDX; + default: + return TargetLayout::getSectionOrder(name, contentType, + contentPermissions); } - llvm_unreachable("TLS segment not found"); } + StringRef getOutputSectionName(StringRef archivePath, StringRef memberPath, + StringRef inputSectionName) const override { + return llvm::StringSwitch<StringRef>(inputSectionName) + .StartsWith(".ARM.exidx", ".ARM.exidx") + .StartsWith(".ARM.extab", ".ARM.extab") + .Default(TargetLayout::getOutputSectionName(archivePath, memberPath, + inputSectionName)); + } + + SegmentType getSegmentType(const Section<ELF32LE> *section) const override { + switch (section->order()) { + case ORDER_ARM_EXIDX: + return llvm::ELF::PT_ARM_EXIDX; + default: + return TargetLayout::getSegmentType(section); + } + } + + AtomSection<ELF32LE> * + createSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + SectionOrder sectionOrder) override { + if ((ARMELFDefinedAtom::ARMContentType)contentType == + ARMELFDefinedAtom::typeARMExidx) + return new ARMExidxSection(_ctx, name, contentPermissions, sectionOrder); + + return TargetLayout::createSection(name, contentType, contentPermissions, + sectionOrder); + } + + uint64_t getGOTSymAddr() { + std::call_once(_gotSymOnce, [this]() { + if (AtomLayout *gotAtom = findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_")) + _gotSymAddr = gotAtom->_virtualAddr; + }); + return _gotSymAddr; + } + + uint64_t getTPOffset() { + std::call_once(_tpOffOnce, [this]() { + for (const auto &phdr : *_programHeader) { + if (phdr->p_type == llvm::ELF::PT_TLS) { + _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); + break; + } + } + assert(_tpOff != 0 && "TLS segment not found"); + }); + return _tpOff; + } + + bool target1Rel() const { return _ctx.armTarget1Rel(); } + private: // TCB block size of the TLS. enum { TCB_SIZE = 0x8 }; - // Cached value of the TLS offset from the $tp pointer. - llvm::Optional<uint64_t> _tpOff; +private: + uint64_t _gotSymAddr = 0; + uint64_t _tpOff = 0; + std::once_flag _gotSymOnce; + std::once_flag _tpOffOnce; }; -class ARMTargetHandler final : public DefaultTargetHandler<ARMELFType> { +class ARMTargetHandler final : public TargetHandler { public: - ARMTargetHandler(ARMLinkingContext &context); - - ARMTargetLayout<ARMELFType> &getTargetLayout() override { - return *(_armTargetLayout.get()); - } - - void registerRelocationNames(Registry ®istry) override; + ARMTargetHandler(ARMLinkingContext &ctx); - const ARMTargetRelocationHandler &getRelocationHandler() const override { - return *(_armRelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new ARMELFObjectReader(_context)); + return llvm::make_unique<ELFReader<ARMELFFile>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new ARMELFDSOReader(_context)); + return llvm::make_unique<ELFReader<DynamicFile<ELF32LE>>>(_ctx); } std::unique_ptr<Writer> getWriter() override; private: - static const Registry::KindStrings kindStrings[]; - ARMLinkingContext &_context; - std::unique_ptr<ARMTargetLayout<ARMELFType>> _armTargetLayout; - std::unique_ptr<ARMTargetRelocationHandler> _armRelocationHandler; + ARMLinkingContext &_ctx; + std::unique_ptr<ARMTargetLayout> _targetLayout; + std::unique_ptr<ARMTargetRelocationHandler> _relocationHandler; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/ARM/Makefile b/lib/ReaderWriter/ELF/ARM/Makefile deleted file mode 100644 index f67d36a1b612..000000000000 --- a/lib/ReaderWriter/ELF/ARM/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===------ lld/lib/ReaderWriter/ELF/ARM/Makefile ----------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LLD_LEVEL := ../../../.. -LIBRARYNAME := lldARMELFTarget -USEDLIBS = lldCore.a -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/ARM -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/ARM/TODO.rst b/lib/ReaderWriter/ELF/ARM/TODO.rst index d05419decb78..61b585ae698c 100644 --- a/lib/ReaderWriter/ELF/ARM/TODO.rst +++ b/lib/ReaderWriter/ELF/ARM/TODO.rst @@ -4,14 +4,15 @@ ELF ARM Unimplemented Features ###################### -* Static executable linking - in progress -* Dynamic executable linking * DSO linking -* PLT entries' generation for images larger than 2^28 bytes (see Sec. A.3 of the ELF reference) -* ARM and Thumb interworking (see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203j/Bcghfebi.html) -* .ARM.exidx section handling +* C++ code linking +* PLT entries' generation for images larger than 2^28 bytes (see Sec. A.3 of the ARM ELF reference) +* ARM/Thumb interwork veneers in position-independent code +* .ARM.exidx section (exception handling) * -init/-fini options -* Lots of relocations +* Proper debug information (DWARF data) +* TLS relocations for dynamic models +* Lots of other relocations Unimplemented Relocations ######################### diff --git a/lib/ReaderWriter/ELF/Atoms.cpp b/lib/ReaderWriter/ELF/Atoms.cpp new file mode 100644 index 000000000000..639633393161 --- /dev/null +++ b/lib/ReaderWriter/ELF/Atoms.cpp @@ -0,0 +1,297 @@ +//===- lib/ReaderWriter/ELF/Atoms.cpp -------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "DynamicFile.h" +#include "ELFFile.h" +#include "TargetHandler.h" + +namespace lld { +namespace elf { + +template <class ELFT> AbsoluteAtom::Scope ELFAbsoluteAtom<ELFT>::scope() const { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() == llvm::ELF::STB_LOCAL) + return scopeTranslationUnit; + return scopeGlobal; +} + +template <class ELFT> +UndefinedAtom::CanBeNull ELFUndefinedAtom<ELFT>::canBeNull() const { + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return CanBeNull::canBeNullAtBuildtime; + return CanBeNull::canBeNullNever; +} + +template <class ELFT> uint64_t ELFDefinedAtom<ELFT>::size() const { + // Common symbols are not allocated in object files, + // so use st_size to tell how many bytes are required. + if (_symbol && (_symbol->getType() == llvm::ELF::STT_COMMON || + _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return (uint64_t)_symbol->st_size; + + return _contentData.size(); +} + +template <class ELFT> AbsoluteAtom::Scope ELFDefinedAtom<ELFT>::scope() const { + if (!_symbol) + return scopeGlobal; + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; +} + +template <class ELFT> DefinedAtom::Merge ELFDefinedAtom<ELFT>::merge() const { + if (!_symbol) + return mergeNo; + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return mergeAsWeak; + if (_symbol->getType() == llvm::ELF::STT_COMMON || + _symbol->st_shndx == llvm::ELF::SHN_COMMON) + return mergeAsTentative; + return mergeNo; +} + +template <class ELFT> +DefinedAtom::ContentType ELFDefinedAtom<ELFT>::doContentType() const { + using namespace llvm::ELF; + + if (_section->sh_type == SHT_GROUP) + return typeGroupComdat; + if (!_symbol && _sectionName.startswith(".gnu.linkonce")) + return typeGnuLinkOnce; + + uint64_t flags = _section->sh_flags; + + if (!(flags & SHF_ALLOC)) { + if (_section->sh_type == SHT_NOTE) + return (flags == SHF_WRITE) ? typeRWNote : typeRONote; + return _contentType = typeNoAlloc; + } + + if (_section->sh_flags == (SHF_ALLOC | SHF_WRITE | SHF_TLS)) + return _section->sh_type == SHT_NOBITS ? typeThreadZeroFill + : typeThreadData; + + if (_section->sh_flags == SHF_ALLOC && _section->sh_type == SHT_PROGBITS) + return _contentType = typeConstant; + if (_symbol->getType() == STT_GNU_IFUNC) + return _contentType = typeResolver; + if (_symbol->st_shndx == SHN_COMMON) + return _contentType = typeZeroFill; + + if (_section->sh_type == SHT_PROGBITS) { + flags &= ~SHF_ALLOC; + flags &= ~SHF_GROUP; + if ((flags & SHF_STRINGS) || (flags & SHF_MERGE)) + return typeConstant; + if (flags == SHF_WRITE) + return typeData; + return typeCode; + } + if (_section->sh_type == SHT_NOTE) { + flags &= ~SHF_ALLOC; + return (flags == SHF_WRITE) ? typeRWNote : typeRONote; + } + if (_section->sh_type == SHT_NOBITS) + return typeZeroFill; + + if (_section->sh_type == SHT_NULL) + if (_symbol->getType() == STT_COMMON || _symbol->st_shndx == SHN_COMMON) + return typeZeroFill; + + if (_section->sh_type == SHT_INIT_ARRAY || + _section->sh_type == SHT_FINI_ARRAY) + return typeData; + return typeUnknown; +} + +template <class ELFT> +DefinedAtom::ContentType ELFDefinedAtom<ELFT>::contentType() const { + if (_contentType != typeUnknown) + return _contentType; + _contentType = doContentType(); + return _contentType; +} + +template <class ELFT> +DefinedAtom::Alignment ELFDefinedAtom<ELFT>::alignment() const { + if (!_symbol) + return 1; + + // Obtain proper value of st_value field. + const auto symValue = getSymbolValue(); + + // Unallocated common symbols specify their alignment constraints in + // st_value. + if ((_symbol->getType() == llvm::ELF::STT_COMMON) || + _symbol->st_shndx == llvm::ELF::SHN_COMMON) { + return symValue; + } + if (_section->sh_addralign == 0) { + // sh_addralign of 0 means no alignment + return Alignment(1, symValue); + } + return Alignment(_section->sh_addralign, symValue % _section->sh_addralign); +} + +// Do we have a choice for ELF? All symbols live in explicit sections. +template <class ELFT> +DefinedAtom::SectionChoice ELFDefinedAtom<ELFT>::sectionChoice() const { + switch (contentType()) { + case typeCode: + case typeData: + case typeZeroFill: + case typeThreadZeroFill: + case typeThreadData: + case typeConstant: + if ((_sectionName == ".text") || (_sectionName == ".data") || + (_sectionName == ".bss") || (_sectionName == ".rodata") || + (_sectionName == ".tdata") || (_sectionName == ".tbss")) + return sectionBasedOnContent; + default: + break; + } + return sectionCustomRequired; +} + +template <class ELFT> +StringRef ELFDefinedAtom<ELFT>::customSectionName() const { + if ((contentType() == typeZeroFill) || + (_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return ".bss"; + return _sectionName; +} + +template <class ELFT> +DefinedAtom::ContentPermissions ELFDefinedAtom<ELFT>::permissions() const { + if (_permissions != permUnknown) + return _permissions; + + uint64_t flags = _section->sh_flags; + + if (!(flags & llvm::ELF::SHF_ALLOC)) + return _permissions = perm___; + + switch (_section->sh_type) { + // permRW_L is for sections modified by the runtime + // loader. + case llvm::ELF::SHT_REL: + case llvm::ELF::SHT_RELA: + return _permissions = permRW_L; + + case llvm::ELF::SHT_DYNAMIC: + case llvm::ELF::SHT_PROGBITS: + case llvm::ELF::SHT_NOTE: + flags &= ~llvm::ELF::SHF_ALLOC; + flags &= ~llvm::ELF::SHF_GROUP; + switch (flags) { + // Code + case llvm::ELF::SHF_EXECINSTR: + return _permissions = permR_X; + case (llvm::ELF::SHF_WRITE | llvm::ELF::SHF_EXECINSTR): + return _permissions = permRWX; + // Data + case llvm::ELF::SHF_WRITE: + return _permissions = permRW_; + // Strings + case llvm::ELF::SHF_MERGE: + case llvm::ELF::SHF_STRINGS: + return _permissions = permR__; + + default: + if (flags & llvm::ELF::SHF_WRITE) + return _permissions = permRW_; + return _permissions = permR__; + } + + case llvm::ELF::SHT_NOBITS: + return _permissions = permRW_; + + case llvm::ELF::SHT_INIT_ARRAY: + case llvm::ELF::SHT_FINI_ARRAY: + return _permissions = permRW_; + + default: + return _permissions = perm___; + } +} + +template <class ELFT> +DefinedAtom::reference_iterator ELFDefinedAtom<ELFT>::begin() const { + uintptr_t index = _referenceStartIndex; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); +} + +template <class ELFT> +DefinedAtom::reference_iterator ELFDefinedAtom<ELFT>::end() const { + uintptr_t index = _referenceEndIndex; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); +} + +template <class ELFT> +const Reference *ELFDefinedAtom<ELFT>::derefIterator(const void *It) const { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + assert(index >= _referenceStartIndex); + assert(index < _referenceEndIndex); + return ((_referenceList)[index]); +} + +template <class ELFT> +void ELFDefinedAtom<ELFT>::incrementIterator(const void *&It) const { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + ++index; + It = reinterpret_cast<const void *>(index); +} + +template <class ELFT> +void ELFDefinedAtom<ELFT>::addReference(ELFReference<ELFT> *reference) { + _referenceList.push_back(reference); + _referenceEndIndex = _referenceList.size(); +} + +template <class ELFT> AbsoluteAtom::Scope ELFDynamicAtom<ELFT>::scope() const { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; +} + +template <class ELFT> +SharedLibraryAtom::Type ELFDynamicAtom<ELFT>::type() const { + switch (_symbol->getType()) { + case llvm::ELF::STT_FUNC: + case llvm::ELF::STT_GNU_IFUNC: + return Type::Code; + case llvm::ELF::STT_OBJECT: + return Type::Data; + default: + return Type::Unknown; + } +} + +#define INSTANTIATE(klass) \ + template class klass<ELF32LE>; \ + template class klass<ELF32BE>; \ + template class klass<ELF64LE>; \ + template class klass<ELF64BE> + +INSTANTIATE(ELFAbsoluteAtom); +INSTANTIATE(ELFDefinedAtom); +INSTANTIATE(ELFDynamicAtom); +INSTANTIATE(ELFUndefinedAtom); + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/Atoms.h b/lib/ReaderWriter/ELF/Atoms.h index 6a506d21d938..390c0e16baf8 100644 --- a/lib/ReaderWriter/ELF/Atoms.h +++ b/lib/ReaderWriter/ELF/Atoms.h @@ -13,6 +13,7 @@ #include "TargetHandler.h" #include "lld/Core/LLVM.h" #include "lld/Core/Simple.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringSwitch.h" #include <memory> @@ -41,19 +42,16 @@ public: ELFReference(const Elf_Rela *rela, uint64_t off, Reference::KindArch arch, Reference::KindValue relocType, uint32_t idx) : Reference(Reference::KindNamespace::ELF, arch, relocType), - _target(nullptr), _targetSymbolIndex(idx), _offsetInAtom(off), - _addend(rela->r_addend) {} + _targetSymbolIndex(idx), _offsetInAtom(off), _addend(rela->r_addend) {} ELFReference(uint64_t off, Reference::KindArch arch, Reference::KindValue relocType, uint32_t idx) : Reference(Reference::KindNamespace::ELF, arch, relocType), - _target(nullptr), _targetSymbolIndex(idx), _offsetInAtom(off), - _addend(0) {} + _targetSymbolIndex(idx), _offsetInAtom(off) {} ELFReference(uint32_t edgeKind) : Reference(Reference::KindNamespace::all, Reference::KindArch::all, - edgeKind), - _target(nullptr), _targetSymbolIndex(0), _offsetInAtom(0), _addend(0) {} + edgeKind) {} uint64_t offsetInAtom() const override { return _offsetInAtom; } @@ -73,10 +71,10 @@ public: void setTarget(const Atom *newAtom) override { _target = newAtom; } private: - const Atom *_target; - uint64_t _targetSymbolIndex; - uint64_t _offsetInAtom; - Addend _addend; + const Atom *_target = nullptr; + uint64_t _targetSymbolIndex = 0; + uint64_t _offsetInAtom = 0; + Addend _addend = 0; }; /// \brief These atoms store symbols that are fixed to a particular address. @@ -88,21 +86,11 @@ template <class ELFT> class ELFAbsoluteAtom : public AbsoluteAtom { public: ELFAbsoluteAtom(const ELFFile<ELFT> &file, StringRef name, const Elf_Sym *symbol, uint64_t value) - : _owningFile(file), _name(name), _symbol(symbol), _value(value) { - } + : _owningFile(file), _name(name), _symbol(symbol), _value(value) {} const ELFFile<ELFT> &file() const override { return _owningFile; } - - Scope scope() const override { - if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) - return scopeLinkageUnit; - if (_symbol->getBinding() == llvm::ELF::STB_LOCAL) - return scopeTranslationUnit; - return scopeGlobal; - } - + Scope scope() const override; StringRef name() const override { return _name; } - uint64_t value() const override { return _value; } private: @@ -114,7 +102,7 @@ private: /// \brief ELFUndefinedAtom: These atoms store undefined symbols and are place /// holders that will be replaced by defined atoms later in the linking process. -template <class ELFT> class ELFUndefinedAtom : public lld::UndefinedAtom { +template <class ELFT> class ELFUndefinedAtom : public UndefinedAtom { typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; public: @@ -122,16 +110,11 @@ public: : _owningFile(file), _name(name), _symbol(symbol) {} const File &file() const override { return _owningFile; } - StringRef name() const override { return _name; } // A symbol in ELF can be undefined at build time if the symbol is a undefined // weak symbol. - CanBeNull canBeNull() const override { - if (_symbol->getBinding() == llvm::ELF::STB_WEAK) - return CanBeNull::canBeNullAtBuildtime; - return CanBeNull::canBeNullNever; - } + CanBeNull canBeNull() const override; private: const File &_owningFile; @@ -157,283 +140,49 @@ public: _referenceList(referenceList), _contentType(typeUnknown), _permissions(permUnknown) {} - ~ELFDefinedAtom() {} + ~ELFDefinedAtom() override = default; const ELFFile<ELFT> &file() const override { return _owningFile; } - StringRef name() const override { return _symbolName; } - uint64_t ordinal() const override { return _ordinal; } - const Elf_Sym *symbol() const { return _symbol; } - const Elf_Shdr *section() const { return _section; } - - uint64_t size() const override { - // Common symbols are not allocated in object files, - // so use st_size to tell how many bytes are required. - if (_symbol && (_symbol->getType() == llvm::ELF::STT_COMMON || - _symbol->st_shndx == llvm::ELF::SHN_COMMON)) - return (uint64_t) _symbol->st_size; - - return _contentData.size(); - } - - Scope scope() const override { - if (!_symbol) - return scopeGlobal; - if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) - return scopeLinkageUnit; - if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) - return scopeGlobal; - return scopeTranslationUnit; - } + uint64_t size() const override; + Scope scope() const override; // FIXME: Need to revisit this in future. Interposable interposable() const override { return interposeNo; } - Merge merge() const override { - if (!_symbol) - return mergeNo; - - if (_symbol->getBinding() == llvm::ELF::STB_WEAK) - return mergeAsWeak; - - if ((_symbol->getType() == llvm::ELF::STT_COMMON) || - _symbol->st_shndx == llvm::ELF::SHN_COMMON) - return mergeAsTentative; - - return mergeNo; - } - - ContentType contentType() const override { - if (_contentType != typeUnknown) - return _contentType; - - ContentType ret = typeUnknown; - uint64_t flags = _section->sh_flags; - - if (_section->sh_type == llvm::ELF::SHT_GROUP) - return typeGroupComdat; - - if (!_symbol && _sectionName.startswith(".gnu.linkonce")) - return typeGnuLinkOnce; - - if (!(flags & llvm::ELF::SHF_ALLOC)) - return _contentType = typeNoAlloc; - - if (_section->sh_flags == - (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_TLS)) { - return _contentType = _section->sh_type == llvm::ELF::SHT_NOBITS ? typeThreadZeroFill - : typeThreadData; - } - - if ((_section->sh_flags == llvm::ELF::SHF_ALLOC) && - (_section->sh_type == llvm::ELF::SHT_PROGBITS)) - return _contentType = typeConstant; - - if (_symbol->getType() == llvm::ELF::STT_GNU_IFUNC) - return _contentType = typeResolver; - - if (_symbol->st_shndx == llvm::ELF::SHN_COMMON) - return _contentType = typeZeroFill; - - switch (_section->sh_type) { - case llvm::ELF::SHT_PROGBITS: - flags &= ~llvm::ELF::SHF_ALLOC; - flags &= ~llvm::ELF::SHF_GROUP; - switch (flags) { - case llvm::ELF::SHF_EXECINSTR: - case (llvm::ELF::SHF_WRITE|llvm::ELF::SHF_EXECINSTR): - ret = typeCode; - break; - case llvm::ELF::SHF_WRITE: - ret = typeData; - break; - case (llvm::ELF::SHF_MERGE|llvm::ELF::SHF_STRINGS): - case llvm::ELF::SHF_STRINGS: - case llvm::ELF::SHF_MERGE: - ret = typeConstant; - break; - default: - ret = typeCode; - break; - } - break; - case llvm::ELF::SHT_NOTE: - flags &= ~llvm::ELF::SHF_ALLOC; - switch (flags) { - case llvm::ELF::SHF_WRITE: - ret = typeRWNote; - break; - default: - ret = typeRONote; - break; - } - break; - case llvm::ELF::SHT_NOBITS: - ret = typeZeroFill; - break; - case llvm::ELF::SHT_NULL: - if ((_symbol->getType() == llvm::ELF::STT_COMMON) - || _symbol->st_shndx == llvm::ELF::SHN_COMMON) - ret = typeZeroFill; - break; - case llvm::ELF::SHT_INIT_ARRAY: - case llvm::ELF::SHT_FINI_ARRAY: - ret = typeData; - break; - } - - return _contentType = ret; - } - - Alignment alignment() const override { - if (!_symbol) - return Alignment(0); - - // Obtain proper value of st_value field. - const auto symValue = getSymbolValue(_symbol); - - // Unallocated common symbols specify their alignment constraints in - // st_value. - if ((_symbol->getType() == llvm::ELF::STT_COMMON) || - _symbol->st_shndx == llvm::ELF::SHN_COMMON) { - return Alignment(llvm::Log2_64(symValue)); - } - if (_section->sh_addralign == 0) { - // sh_addralign of 0 means no alignment - return Alignment(0, symValue); - } - return Alignment(llvm::Log2_64(_section->sh_addralign), - symValue % _section->sh_addralign); - } - - // Do we have a choice for ELF? All symbols live in explicit sections. - SectionChoice sectionChoice() const override { - switch (contentType()) { - case typeCode: - case typeData: - case typeZeroFill: - case typeThreadZeroFill: - case typeThreadData: - case typeConstant: - if ((_sectionName == ".text") || (_sectionName == ".data") || - (_sectionName == ".bss") || (_sectionName == ".rodata") || - (_sectionName == ".tdata") || (_sectionName == ".tbss")) - return sectionBasedOnContent; - default: - break; - } - return sectionCustomRequired; - } - - StringRef customSectionName() const override { - if ((contentType() == typeZeroFill) || - (_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON)) - return ".bss"; - return _sectionName; - } + Merge merge() const override; + ContentType contentType() const override; + Alignment alignment() const override; + SectionChoice sectionChoice() const override; + StringRef customSectionName() const override; // It isn't clear that __attribute__((used)) is transmitted to the ELF object // file. DeadStripKind deadStrip() const override { return deadStripNormal; } - ContentPermissions permissions() const override { - if (_permissions != permUnknown) - return _permissions; - - uint64_t flags = _section->sh_flags; - - if (!(flags & llvm::ELF::SHF_ALLOC)) - return _permissions = perm___; - - switch (_section->sh_type) { - // permRW_L is for sections modified by the runtime - // loader. - case llvm::ELF::SHT_REL: - case llvm::ELF::SHT_RELA: - return _permissions = permRW_L; - - case llvm::ELF::SHT_DYNAMIC: - case llvm::ELF::SHT_PROGBITS: - case llvm::ELF::SHT_NOTE: - flags &= ~llvm::ELF::SHF_ALLOC; - flags &= ~llvm::ELF::SHF_GROUP; - switch (flags) { - // Code - case llvm::ELF::SHF_EXECINSTR: - return _permissions = permR_X; - case (llvm::ELF::SHF_WRITE|llvm::ELF::SHF_EXECINSTR): - return _permissions = permRWX; - // Data - case llvm::ELF::SHF_WRITE: - return _permissions = permRW_; - // Strings - case llvm::ELF::SHF_MERGE: - case llvm::ELF::SHF_STRINGS: - return _permissions = permR__; - - default: - if (flags & llvm::ELF::SHF_WRITE) - return _permissions = permRW_; - return _permissions = permR__; - } - - case llvm::ELF::SHT_NOBITS: - return _permissions = permRW_; - - case llvm::ELF::SHT_INIT_ARRAY: - case llvm::ELF::SHT_FINI_ARRAY: - return _permissions = permRW_; - - default: - return _permissions = perm___; - } - } - + ContentPermissions permissions() const override; ArrayRef<uint8_t> rawContent() const override { return _contentData; } - DefinedAtom::reference_iterator begin() const override { - uintptr_t index = _referenceStartIndex; - const void *it = reinterpret_cast<const void*>(index); - return reference_iterator(*this, it); - } - - DefinedAtom::reference_iterator end() const override { - uintptr_t index = _referenceEndIndex; - const void *it = reinterpret_cast<const void*>(index); - return reference_iterator(*this, it); - } - - const Reference *derefIterator(const void *It) const override { - uintptr_t index = reinterpret_cast<uintptr_t>(It); - assert(index >= _referenceStartIndex); - assert(index < _referenceEndIndex); - return ((_referenceList)[index]); - } - - void incrementIterator(const void *&It) const override { - uintptr_t index = reinterpret_cast<uintptr_t>(It); - ++index; - It = reinterpret_cast<const void *>(index); - } - - void addReference(ELFReference<ELFT> *reference) { - _referenceList.push_back(reference); - _referenceEndIndex = _referenceList.size(); - } + DefinedAtom::reference_iterator begin() const override; + DefinedAtom::reference_iterator end() const override; + const Reference *derefIterator(const void *It) const override; + void incrementIterator(const void *&It) const override; + void addReference(ELFReference<ELFT> *reference); virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } protected: /// Returns correct st_value for the symbol depending on the architecture. /// For most architectures it's just a regular st_value with no changes. - virtual uint64_t getSymbolValue(const Elf_Sym *symbol) const { - return symbol->st_value; + virtual uint64_t getSymbolValue() const { + return _symbol->st_value; } -protected: + ContentType doContentType() const; + const ELFFile<ELFT> &_owningFile; StringRef _symbolName; StringRef _sectionName; @@ -463,39 +212,25 @@ public: } const ELFFile<ELFT> &file() const override { return _owningFile; } - StringRef name() const override { return ""; } - virtual uint64_t section() const { return _section->sh_name; } - virtual uint64_t offset() const { return _offset; } - virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } - uint64_t ordinal() const override { return _ordinal; } - uint64_t size() const override { return _contentData.size(); } - Scope scope() const override { return scopeTranslationUnit; } - Interposable interposable() const override { return interposeNo; } - Merge merge() const override { return mergeByContent; } - ContentType contentType() const override { return typeConstant; } Alignment alignment() const override { - return Alignment(llvm::Log2_64(_section->sh_addralign)); + return Alignment(_section->sh_addralign); } SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return _sectionName; } - DeadStripKind deadStrip() const override { return deadStripNormal; } - ContentPermissions permissions() const override { return permR__; } - ArrayRef<uint8_t> rawContent() const override { return _contentData; } DefinedAtom::reference_iterator begin() const override { @@ -517,7 +252,6 @@ public: void incrementIterator(const void *&It) const override {} private: - const ELFFile<ELFT> &_owningFile; StringRef _sectionName; const Elf_Shdr *_section; @@ -530,21 +264,14 @@ private: template <class ELFT> class ELFCommonAtom : public DefinedAtom { typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; public: - ELFCommonAtom(const ELFFile<ELFT> &file, - StringRef symbolName, + ELFCommonAtom(const ELFFile<ELFT> &file, StringRef symbolName, const Elf_Sym *symbol) - : _owningFile(file), - _symbolName(symbolName), - _symbol(symbol) {} + : _owningFile(file), _symbolName(symbolName), _symbol(symbol) {} const ELFFile<ELFT> &file() const override { return _owningFile; } - StringRef name() const override { return _symbolName; } - uint64_t ordinal() const override { return _ordinal; } - virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } - uint64_t size() const override { return _symbol->st_size; } Scope scope() const override { @@ -556,23 +283,13 @@ public: } Interposable interposable() const override { return interposeNo; } - Merge merge() const override { return mergeAsTentative; } - ContentType contentType() const override { return typeZeroFill; } - - Alignment alignment() const override { - return Alignment(llvm::Log2_64(_symbol->st_value)); - } - + Alignment alignment() const override { return Alignment(_symbol->st_value); } SectionChoice sectionChoice() const override { return sectionBasedOnContent; } - StringRef customSectionName() const override { return ".bss"; } - DeadStripKind deadStrip() const override { return deadStripNormal; } - ContentPermissions permissions() const override { return permRW_; } - ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } DefinedAtom::reference_iterator begin() const override { @@ -608,42 +325,19 @@ public: ELFDynamicAtom(const DynamicFile<ELFT> &file, StringRef symbolName, StringRef loadName, const Elf_Sym *symbol) : _owningFile(file), _symbolName(symbolName), _loadName(loadName), - _symbol(symbol) { - } + _symbol(symbol) {} const DynamicFile<ELFT> &file() const override { return _owningFile; } - StringRef name() const override { return _symbolName; } - - virtual Scope scope() const { - if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) - return scopeLinkageUnit; - if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) - return scopeGlobal; - return scopeTranslationUnit; - } - + virtual Scope scope() const; StringRef loadName() const override { return _loadName; } bool canBeNullAtRuntime() const override { return _symbol->getBinding() == llvm::ELF::STB_WEAK; } - Type type() const override { - switch (_symbol->getType()) { - case llvm::ELF::STT_FUNC: - case llvm::ELF::STT_GNU_IFUNC: - return Type::Code; - case llvm::ELF::STT_OBJECT: - return Type::Data; - default: - return Type::Unknown; - } - } - - uint64_t size() const override { - return _symbol->st_size; - } + Type type() const override; + uint64_t size() const override { return _symbol->st_size; } private: @@ -658,35 +352,33 @@ public: SimpleELFDefinedAtom(const File &f) : SimpleDefinedAtom(f) {} void addReferenceELF(Reference::KindArch arch, Reference::KindValue kindValue, - uint64_t off, const Atom *target, - Reference::Addend addend) { - this->addReference(Reference::KindNamespace::ELF, arch, kindValue, off, - target, addend); + uint64_t off, const Atom *t, Reference::Addend a) { + addReference(Reference::KindNamespace::ELF, arch, kindValue, off, t, a); } void addReferenceELF_Hexagon(Reference::KindValue relocType, uint64_t off, const Atom *t, Reference::Addend a) { - this->addReferenceELF(Reference::KindArch::Hexagon, relocType, off, t, a); + addReferenceELF(Reference::KindArch::Hexagon, relocType, off, t, a); } void addReferenceELF_x86_64(Reference::KindValue relocType, uint64_t off, const Atom *t, Reference::Addend a) { - this->addReferenceELF(Reference::KindArch::x86_64, relocType, off, t, a); + addReferenceELF(Reference::KindArch::x86_64, relocType, off, t, a); } void addReferenceELF_Mips(Reference::KindValue relocType, uint64_t off, const Atom *t, Reference::Addend a) { - this->addReferenceELF(Reference::KindArch::Mips, relocType, off, t, a); + addReferenceELF(Reference::KindArch::Mips, relocType, off, t, a); } void addReferenceELF_AArch64(Reference::KindValue relocType, uint64_t off, const Atom *t, Reference::Addend a) { - this->addReferenceELF(Reference::KindArch::AArch64, relocType, off, t, a); + addReferenceELF(Reference::KindArch::AArch64, relocType, off, t, a); } void addReferenceELF_ARM(Reference::KindValue relocType, uint64_t off, const Atom *t, Reference::Addend a) { - this->addReferenceELF(Reference::KindArch::ARM, relocType, off, t, a); + addReferenceELF(Reference::KindArch::ARM, relocType, off, t, a); } }; @@ -695,26 +387,14 @@ public: class ObjectAtom : public SimpleELFDefinedAtom { public: ObjectAtom(const File &f) : SimpleELFDefinedAtom(f) {} - Scope scope() const override { return scopeGlobal; } - SectionChoice sectionChoice() const override { return sectionBasedOnContent; } - ContentType contentType() const override { return typeZeroFill; } - uint64_t size() const override { return _size; } - DynamicExport dynamicExport() const override { return dynamicExportAlways; } - ContentPermissions permissions() const override { return permRW_; } - ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } - - Alignment alignment() const override { - // The alignment should be 8 byte aligned - return Alignment(3); - } - + Alignment alignment() const override { return 8; } StringRef name() const override { return _name; } std::string _name; @@ -729,21 +409,12 @@ public: : SimpleELFDefinedAtom(f), _section(secName) {} Scope scope() const override { return scopeTranslationUnit; } - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return _section; } - ContentType contentType() const override { return typeGOT; } - uint64_t size() const override { return rawContent().size(); } - ContentPermissions permissions() const override { return permRW_; } - - Alignment alignment() const override { - // The alignment should be 8 byte aligned - return Alignment(3); - } + Alignment alignment() const override { return 8; } #ifndef NDEBUG StringRef name() const override { return _name; } @@ -761,20 +432,12 @@ public: : SimpleELFDefinedAtom(f), _section(secName) {} Scope scope() const override { return scopeTranslationUnit; } - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return _section; } - ContentType contentType() const override { return typeStub; } - uint64_t size() const override { return rawContent().size(); } - ContentPermissions permissions() const override { return permR_X; } - - Alignment alignment() const override { - return Alignment(4); // 16 - } + Alignment alignment() const override { return 16; } #ifndef NDEBUG StringRef name() const override { return _name; } @@ -793,57 +456,38 @@ public: } }; -class GLOBAL_OFFSET_TABLEAtom : public SimpleELFDefinedAtom { +class GlobalOffsetTableAtom : public SimpleELFDefinedAtom { public: - GLOBAL_OFFSET_TABLEAtom(const File &f) : SimpleELFDefinedAtom(f) {} + GlobalOffsetTableAtom(const File &f) : SimpleELFDefinedAtom(f) {} StringRef name() const override { return "_GLOBAL_OFFSET_TABLE_"; } - Scope scope() const override { return scopeLinkageUnit; } - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return ".got.plt"; } - ContentType contentType() const override { return typeGOT; } - uint64_t size() const override { return 0; } - ContentPermissions permissions() const override { return permRW_; } - - Alignment alignment() const override { - // Needs 8 byte alignment - return Alignment(3); - } - + Alignment alignment() const override { return 8; } ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } }; -class DYNAMICAtom : public SimpleELFDefinedAtom { +class DynamicAtom : public SimpleELFDefinedAtom { public: - DYNAMICAtom(const File &f) : SimpleELFDefinedAtom(f) {} + DynamicAtom(const File &f) : SimpleELFDefinedAtom(f) {} StringRef name() const override { return "_DYNAMIC"; } - Scope scope() const override { return scopeLinkageUnit; } - Merge merge() const override { return mergeNo; } - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return ".dynamic"; } - ContentType contentType() const override { return typeData; } - uint64_t size() const override { return 0; } - ContentPermissions permissions() const override { return permRW_; } - - Alignment alignment() const override { return Alignment(0); } - + Alignment alignment() const override { return 1; } ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } }; + } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_ATOMS_H diff --git a/lib/ReaderWriter/ELF/CMakeLists.txt b/lib/ReaderWriter/ELF/CMakeLists.txt index fd4cb669904d..e3e4a02b2810 100644 --- a/lib/ReaderWriter/ELF/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/CMakeLists.txt @@ -1,11 +1,21 @@ add_llvm_library(lldELF + Atoms.cpp + DynamicFile.cpp + ELFFile.cpp ELFLinkingContext.cpp + FileCommon.cpp + HeaderChunks.cpp + OutputELFWriter.cpp Reader.cpp + SectionChunks.cpp + SegmentChunks.cpp + TargetLayout.cpp Writer.cpp LINK_LIBS lldReaderWriter lldCore lldYAML + LLVMObject LLVMSupport ) @@ -17,3 +27,4 @@ add_subdirectory(Mips) add_subdirectory(Hexagon) add_subdirectory(AArch64) add_subdirectory(ARM) +add_subdirectory(AMDGPU) diff --git a/lib/ReaderWriter/ELF/Chunk.h b/lib/ReaderWriter/ELF/Chunk.h index 2658d023b3a9..f223b6c54163 100644 --- a/lib/ReaderWriter/ELF/Chunk.h +++ b/lib/ReaderWriter/ELF/Chunk.h @@ -25,32 +25,33 @@ class ELFLinkingContext; namespace elf { class ELFWriter; - template <class ELFT> class TargetLayout; /// \brief A chunk is a contiguous region of space -template<class ELFT> -class Chunk { +template <class ELFT> class Chunk { public: - /// \brief Describes the type of Chunk - enum Kind : uint8_t{ ELFHeader, ///< ELF Header - ProgramHeader, ///< Program Header - SectionHeader, ///< Section header - ELFSegment, ///< Segment - ELFSection, ///< Section - AtomSection, ///< A section containing atoms. - Expression ///< A linker script expression + enum Kind : uint8_t { + ELFHeader, ///< ELF Header + ProgramHeader, ///< Program Header + SectionHeader, ///< Section header + ELFSegment, ///< Segment + ELFSection, ///< Section + AtomSection, ///< A section containing atoms + Expression ///< A linker script expression }; + /// \brief the ContentType of the chunk - enum ContentType : uint8_t{ Unknown, Header, Code, Data, Note, TLS }; + enum ContentType : uint8_t { Unknown, Header, Code, Data, Note, TLS }; + + Chunk(StringRef name, Kind kind, const ELFLinkingContext &ctx) + : _name(name), _kind(kind), _ctx(ctx) {} - Chunk(StringRef name, Kind kind, const ELFLinkingContext &context) - : _name(name), _kind(kind), _fsize(0), _msize(0), _alignment(0), _order(0), - _ordinal(1), _start(0), _fileoffset(0), _context(context) {} virtual ~Chunk() {} + // The name of the chunk StringRef name() const { return _name; } + // Kind of chunk Kind kind() const { return _kind; } virtual uint64_t fileSize() const { return _fsize; } @@ -59,41 +60,49 @@ public: virtual uint64_t alignment() const { return _alignment; } // The ordinal value of the chunk - uint64_t ordinal() const { return _ordinal;} - void setOrdinal(uint64_t newVal) { _ordinal = newVal;} + uint64_t ordinal() const { return _ordinal; } + void setOrdinal(uint64_t newVal) { _ordinal = newVal; } + // The order in which the chunk would appear in the output file - uint64_t order() const { return _order; } - void setOrder(uint32_t order) { _order = order; } + uint64_t order() const { return _order; } + void setOrder(uint32_t order) { _order = order; } + // Output file offset of the chunk - uint64_t fileOffset() const { return _fileoffset; } - void setFileOffset(uint64_t offset) { _fileoffset = offset; } + uint64_t fileOffset() const { return _fileoffset; } + void setFileOffset(uint64_t offset) { _fileoffset = offset; } + // Output start address of the chunk virtual void setVirtualAddr(uint64_t start) { _start = start; } virtual uint64_t virtualAddr() const { return _start; } + // Memory size of the chunk uint64_t memSize() const { return _msize; } void setMemSize(uint64_t msize) { _msize = msize; } - // Whats the contentType of the chunk? + + // Returns the ContentType of the chunk virtual int getContentType() const = 0; + // Writer the chunk virtual void write(ELFWriter *writer, TargetLayout<ELFT> &layout, llvm::FileOutputBuffer &buffer) = 0; + // Finalize the chunk before assigning offsets/virtual addresses - virtual void doPreFlight() = 0; + virtual void doPreFlight() {} + // Finalize the chunk before writing - virtual void finalize() = 0; + virtual void finalize() {} protected: StringRef _name; Kind _kind; - uint64_t _fsize; - uint64_t _msize; - uint64_t _alignment; - uint32_t _order; - uint64_t _ordinal; - uint64_t _start; - uint64_t _fileoffset; - const ELFLinkingContext &_context; + const ELFLinkingContext &_ctx; + uint64_t _fsize = 0; + uint64_t _msize = 0; + uint64_t _alignment = 1; + uint32_t _order = 0; + uint64_t _ordinal = 1; + uint64_t _start = 0; + uint64_t _fileoffset = 0; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/CreateELF.h b/lib/ReaderWriter/ELF/CreateELF.h deleted file mode 100644 index ad34dddb24d3..000000000000 --- a/lib/ReaderWriter/ELF/CreateELF.h +++ /dev/null @@ -1,118 +0,0 @@ -//===- lib/ReaderWriter/ELF/CreateELF.h -----------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief This file provides a simple way to create an object templated on -/// ELFType depending on the runtime type needed. -/// -//===----------------------------------------------------------------------===// -#ifndef LLD_READER_WRITER_ELF_CREATE_ELF_H -#define LLD_READER_WRITER_ELF_CREATE_ELF_H - -#include "llvm/Object/ELF.h" -#include "llvm/Support/Compiler.h" - -namespace { -using llvm::object::ELFType; - -/// \func createELF -/// \brief Create an object depending on the runtime attributes and alignment -/// of an ELF file. -/// -/// \param Traits -/// Traits::result_type must be a type convertable from what create returns. -/// Traits::create must be a template function which takes an ELFType and -/// returns something convertable to Traits::result_type. -/// -/// \param ident pair of EI_CLASS and EI_DATA. -/// \param maxAlignment the maximum alignment of the file. -/// \param args arguments forwarded to CreateELFTraits<T>::create. - -#define LLVM_CREATE_ELF_CreateELFTraits(endian, align, is64, ...) \ - Traits::template create<ELFType<llvm::support::endian, align, is64>>( \ - __VA_ARGS__); - -#if !LLVM_IS_UNALIGNED_ACCESS_FAST -# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \ - if (maxAlignment >= normal) \ - return LLVM_CREATE_ELF_CreateELFTraits(endian, normal, is64, __VA_ARGS__) \ - else if (maxAlignment >= low) \ - return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \ - else \ - llvm_unreachable("Invalid alignment for ELF file!"); -#else -# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \ - if (maxAlignment >= low) \ - return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \ - else \ - llvm_unreachable("Invalid alignment for ELF file!"); -#endif - -#define LLVM_CREATE_ELF_IMPL(...) \ - if (ident.first == llvm::ELF::ELFCLASS32 && \ - ident.second == llvm::ELF::ELFDATA2LSB) { \ - LLVM_CREATE_ELF_MaxAlignCheck(4, 2, little, false, __VA_ARGS__) \ - } else if (ident.first == llvm::ELF::ELFCLASS32 && \ - ident.second == llvm::ELF::ELFDATA2MSB) { \ - LLVM_CREATE_ELF_MaxAlignCheck(4, 2, big, false, __VA_ARGS__) \ - } else if (ident.first == llvm::ELF::ELFCLASS64 && \ - ident.second == llvm::ELF::ELFDATA2MSB) { \ - LLVM_CREATE_ELF_MaxAlignCheck(8, 2, big, true, __VA_ARGS__) \ - } else if (ident.first == llvm::ELF::ELFCLASS64 && \ - ident.second == llvm::ELF::ELFDATA2LSB) { \ - LLVM_CREATE_ELF_MaxAlignCheck(8, 2, little, true, __VA_ARGS__) \ - } \ - llvm_unreachable("Invalid ELF type!"); - -#if LLVM_HAS_VARIADIC_TEMPLATES -template <class Traits, class ...Args> -typename Traits::result_type createELF( - std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, - Args &&...args) { - LLVM_CREATE_ELF_IMPL(std::forward<Args>(args)...) -} -#else -template <class Traits, class T1> -typename Traits::result_type createELF( - std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, - T1 &&t1) { - LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1)) -} - -template <class Traits, class T1, class T2> -typename Traits::result_type createELF( - std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, - T1 &&t1, T2 &&t2) { - LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2)) -} - -template <class Traits, class T1, class T2, class T3> -typename Traits::result_type createELF( - std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, - T1 &&t1, T2 &&t2, T3 &&t3) { - LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2), - std::forward<T3>(t3)) -} - -template <class Traits, class T1, class T2, class T3, class T4> -typename Traits::result_type createELF( - std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, - T1 &&t1, T2 &&t2, T3 &&t3, T4 &&t4) { - LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2), - std::forward<T3>(t3), std::forward<T4>(t4)) -} - -#endif // LLVM_HAS_VARIADIC_TEMPLATES -} // end anon namespace - -#undef LLVM_CREATE_ELF_CreateELFTraits -#undef LLVM_CREATE_ELF_MaxAlignCheck -#undef LLVM_CREATE_ELF_IMPL - -#endif diff --git a/lib/ReaderWriter/ELF/DefaultLayout.h b/lib/ReaderWriter/ELF/DefaultLayout.h deleted file mode 100644 index 9af3b8eb8dc6..000000000000 --- a/lib/ReaderWriter/ELF/DefaultLayout.h +++ /dev/null @@ -1,1050 +0,0 @@ -//===- lib/ReaderWriter/ELF/DefaultLayout.h -------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H -#define LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H - -#include "Atoms.h" -#include "Chunk.h" -#include "HeaderChunks.h" -#include "Layout.h" -#include "SectionChunks.h" -#include "SegmentChunks.h" -#include "lld/Core/Instrumentation.h" -#include "lld/Core/STDExtras.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Hashing.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Format.h" -#include <map> -#include <unordered_map> - -namespace lld { -namespace elf { -/// \brief The DefaultLayout class is used by the Writer to arrange -/// sections and segments in the order determined by the target ELF -/// format. The writer creates a single instance of the DefaultLayout -/// class -template<class ELFT> -class DefaultLayout : public Layout { -public: - - // The order in which the sections appear in the output file - // If its determined, that the layout needs to change - // just changing the order of enumerations would essentially - // change the layout in the output file - // Change the enumerations so that Target can override and stick - // a section anywhere it wants to - enum DefaultSectionOrder { - ORDER_NOT_DEFINED = 0, - ORDER_INTERP = 10, - ORDER_RO_NOTE = 15, - ORDER_HASH = 30, - ORDER_DYNAMIC_SYMBOLS = 40, - ORDER_DYNAMIC_STRINGS = 50, - ORDER_DYNAMIC_RELOCS = 52, - ORDER_DYNAMIC_PLT_RELOCS = 54, - ORDER_INIT = 60, - ORDER_PLT = 70, - ORDER_TEXT = 80, - ORDER_FINI = 90, - ORDER_REL = 95, - ORDER_RODATA = 100, - ORDER_EH_FRAME = 110, - ORDER_EH_FRAMEHDR = 120, - ORDER_TDATA = 124, - ORDER_TBSS = 128, - ORDER_CTORS = 130, - ORDER_DTORS = 140, - ORDER_INIT_ARRAY = 150, - ORDER_FINI_ARRAY = 160, - ORDER_DYNAMIC = 170, - ORDER_GOT = 180, - ORDER_GOT_PLT = 190, - ORDER_DATA = 200, - ORDER_RW_NOTE = 205, - ORDER_BSS = 210, - ORDER_NOALLOC = 215, - ORDER_OTHER = 220, - ORDER_SECTION_STRINGS = 230, - ORDER_SYMBOL_TABLE = 240, - ORDER_STRING_TABLE = 250, - ORDER_SECTION_HEADERS = 260 - }; - -public: - - // The Key used for creating Sections - // The sections are created using - // SectionName, contentPermissions - struct SectionKey { - SectionKey(StringRef name, DefinedAtom::ContentPermissions perm, - StringRef path) - : _name(name), _perm(perm), _path(path) {} - - // Data members - StringRef _name; - DefinedAtom::ContentPermissions _perm; - StringRef _path; - }; - - struct SectionKeyHash { - int64_t operator()(const SectionKey &k) const { - return llvm::hash_combine(k._name, k._perm, k._path); - } - }; - - struct SectionKeyEq { - bool operator()(const SectionKey &lhs, const SectionKey &rhs) const { - return ((lhs._name == rhs._name) && (lhs._perm == rhs._perm) && - (lhs._path == rhs._path)); - } - }; - - typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; - typedef typename std::vector<Segment<ELFT> *>::iterator SegmentIter; - - // The additional segments are used to figure out - // if there is a segment by that type already created - // For example : PT_TLS, we have two sections .tdata/.tbss - // that are part of PT_TLS, we need to create this additional - // segment only once - typedef std::pair<int64_t, int64_t> AdditionalSegmentKey; - // The segments are created using - // SegmentName, Segment flags - typedef std::pair<StringRef, int64_t> SegmentKey; - - // HashKey for the Segment - class SegmentHashKey { - public: - int64_t operator() (const SegmentKey &k) const { - // k.first = SegmentName - // k.second = SegmentFlags - return llvm::hash_combine(k.first, k.second); - } - }; - - class AdditionalSegmentHashKey { - public: - int64_t operator()(const AdditionalSegmentKey &k) const { - // k.first = SegmentName - // k.second = SegmentFlags - return llvm::hash_combine(k.first, k.second); - } - }; - - // Output Sections contain the map of Sectionnames to a vector of sections, - // that have been merged to form a single section - typedef llvm::StringMap<OutputSection<ELFT> *> OutputSectionMapT; - typedef - typename std::vector<OutputSection<ELFT> *>::iterator OutputSectionIter; - - typedef std::unordered_map<SectionKey, AtomSection<ELFT> *, SectionKeyHash, - SectionKeyEq> SectionMapT; - typedef std::unordered_map<AdditionalSegmentKey, Segment<ELFT> *, - AdditionalSegmentHashKey> AdditionalSegmentMapT; - typedef std::unordered_map<SegmentKey, Segment<ELFT> *, SegmentHashKey> - SegmentMapT; - - /// \brief find a absolute atom pair given a absolute atom name - struct FindByName { - const std::string _name; - FindByName(StringRef name) : _name(name) {} - bool operator()(const lld::AtomLayout *j) { return j->_atom->name() == _name; } - }; - - typedef typename std::vector<lld::AtomLayout *>::iterator AbsoluteAtomIterT; - - typedef llvm::DenseSet<const Atom *> AtomSetT; - - DefaultLayout(ELFLinkingContext &context) - : _context(context), _linkerScriptSema(context.linkerScriptSema()) {} - - /// \brief Return the section order for a input section - SectionOrder getSectionOrder(StringRef name, int32_t contentType, - int32_t contentPermissions) override; - - /// \brief Return the name of the input section by decoding the input - /// sectionChoice. - virtual StringRef getInputSectionName(const DefinedAtom *da) const; - - /// \brief Return the name of the output section from the input section. - virtual StringRef getOutputSectionName(StringRef archivePath, - StringRef memberPath, - StringRef inputSectionName) const; - - /// \brief Gets or creates a section. - AtomSection<ELFT> * - getSection(StringRef name, int32_t contentType, - DefinedAtom::ContentPermissions contentPermissions, - const DefinedAtom *da); - - /// \brief Gets the segment for a output section - virtual Layout::SegmentType getSegmentType(Section<ELFT> *section) const; - - /// \brief Returns true/false depending on whether the section has a Output - // segment or not - static bool hasOutputSegment(Section<ELFT> *section); - - // Adds an atom to the section - ErrorOr<const lld::AtomLayout *> addAtom(const Atom *atom) override; - - /// \brief Find an output Section given a section name. - OutputSection<ELFT> *findOutputSection(StringRef name) { - auto iter = _outputSectionMap.find(name); - if (iter == _outputSectionMap.end()) - return nullptr; - return iter->second; - } - - /// \brief find a absolute atom given a name - AbsoluteAtomIterT findAbsoluteAtom(StringRef name) { - return std::find_if(_absoluteAtoms.begin(), _absoluteAtoms.end(), - FindByName(name)); - } - - // Output sections with the same name into a OutputSection - void createOutputSections(); - - /// \brief Sort the sections by their order as defined by the layout, - /// preparing all sections to be assigned to a segment. - virtual void sortInputSections(); - - /// \brief Add extra chunks to a segment just before including the input - /// section given by <archivePath, memberPath, sectionName>. This - /// is used to add linker script expressions before each section. - virtual void addExtraChunksToSegment(Segment<ELFT> *segment, - StringRef archivePath, - StringRef memberPath, - StringRef sectionName); - - void assignSectionsToSegments() override; - - void assignVirtualAddress() override; - - void assignFileOffsetsForMiscSections(); - - range<AbsoluteAtomIterT> absoluteAtoms() { return _absoluteAtoms; } - - void addSection(Chunk<ELFT> *c) { _sections.push_back(c); } - - void finalize() { - ScopedTask task(getDefaultDomain(), "Finalize layout"); - for (auto &si : _sections) - si->finalize(); - } - - void doPreFlight() { - for (auto &si : _sections) - si->doPreFlight(); - } - - const AtomLayout *findAtomLayoutByName(StringRef name) const override { - for (auto sec : _sections) - if (auto section = dyn_cast<Section<ELFT>>(sec)) - if (auto *al = section->findAtomLayoutByName(name)) - return al; - return nullptr; - } - - void setHeader(ELFHeader<ELFT> *elfHeader) { _elfHeader = elfHeader; } - - void setProgramHeader(ProgramHeader<ELFT> *p) { - _programHeader = p; - } - - range<OutputSectionIter> outputSections() { return _outputSections; } - - range<ChunkIter> sections() { return _sections; } - - range<SegmentIter> segments() { return _segments; } - - ELFHeader<ELFT> *getHeader() { return _elfHeader; } - - bool hasDynamicRelocationTable() const { return !!_dynamicRelocationTable; } - - bool hasPLTRelocationTable() const { return !!_pltRelocationTable; } - - /// \brief Get or create the dynamic relocation table. All relocations in this - /// table are processed at startup. - RelocationTable<ELFT> *getDynamicRelocationTable() { - if (!_dynamicRelocationTable) { - _dynamicRelocationTable = std::move(createRelocationTable( - _context.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn", - ORDER_DYNAMIC_RELOCS)); - addSection(_dynamicRelocationTable.get()); - } - return _dynamicRelocationTable.get(); - } - - /// \brief Get or create the PLT relocation table. Referenced by DT_JMPREL. - RelocationTable<ELFT> *getPLTRelocationTable() { - if (!_pltRelocationTable) { - _pltRelocationTable = std::move(createRelocationTable( - _context.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt", - ORDER_DYNAMIC_PLT_RELOCS)); - addSection(_pltRelocationTable.get()); - } - return _pltRelocationTable.get(); - } - - uint64_t getTLSSize() const { - for (const auto &phdr : *_programHeader) - if (phdr->p_type == llvm::ELF::PT_TLS) - return phdr->p_memsz; - return 0; - } - - bool isReferencedByDefinedAtom(const Atom *a) const { - return _referencedDynAtoms.count(a); - } - - bool isCopied(const SharedLibraryAtom *sla) const { - return _copiedDynSymNames.count(sla->name()); - } - - /// \brief Handle SORT_BY_PRIORITY. - void sortOutputSectionByPriority(StringRef outputSectionName, - StringRef prefix); - -protected: - /// \brief TargetLayouts may use these functions to reorder the input sections - /// in a order defined by their ABI. - virtual void finalizeOutputSectionLayout() {} - - /// \brief Allocate a new section. - virtual AtomSection<ELFT> *createSection( - StringRef name, int32_t contentType, - DefinedAtom::ContentPermissions contentPermissions, - SectionOrder sectionOrder); - - /// \brief Create a new relocation table. - virtual unique_bump_ptr<RelocationTable<ELFT>> - createRelocationTable(StringRef name, int32_t order) { - return unique_bump_ptr<RelocationTable<ELFT>>( - new (_allocator) RelocationTable<ELFT>(_context, name, order)); - } - -private: - /// Helper function that returns the priority value from an input section. - uint32_t getPriorityFromSectionName(StringRef sectionName) const; - -protected: - llvm::BumpPtrAllocator _allocator; - SectionMapT _sectionMap; - OutputSectionMapT _outputSectionMap; - AdditionalSegmentMapT _additionalSegmentMap; - SegmentMapT _segmentMap; - std::vector<Chunk<ELFT> *> _sections; - std::vector<Segment<ELFT> *> _segments; - std::vector<OutputSection<ELFT> *> _outputSections; - ELFHeader<ELFT> *_elfHeader; - ProgramHeader<ELFT> *_programHeader; - unique_bump_ptr<RelocationTable<ELFT>> _dynamicRelocationTable; - unique_bump_ptr<RelocationTable<ELFT>> _pltRelocationTable; - std::vector<lld::AtomLayout *> _absoluteAtoms; - AtomSetT _referencedDynAtoms; - llvm::StringSet<> _copiedDynSymNames; - ELFLinkingContext &_context; - script::Sema &_linkerScriptSema; -}; - -template <class ELFT> -Layout::SectionOrder DefaultLayout<ELFT>::getSectionOrder( - StringRef name, int32_t contentType, int32_t contentPermissions) { - switch (contentType) { - case DefinedAtom::typeResolver: - case DefinedAtom::typeCode: - return llvm::StringSwitch<Layout::SectionOrder>(name) - .StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR) - .StartsWith(".eh_frame", ORDER_EH_FRAME) - .StartsWith(".init", ORDER_INIT) - .StartsWith(".fini", ORDER_FINI) - .StartsWith(".hash", ORDER_HASH) - .Default(ORDER_TEXT); - - case DefinedAtom::typeConstant: - return ORDER_RODATA; - - case DefinedAtom::typeData: - case DefinedAtom::typeDataFast: - return llvm::StringSwitch<Layout::SectionOrder>(name) - .StartsWith(".init_array", ORDER_INIT_ARRAY) - .StartsWith(".fini_array", ORDER_FINI_ARRAY) - .StartsWith(".dynamic", ORDER_DYNAMIC) - .StartsWith(".ctors", ORDER_CTORS) - .StartsWith(".dtors", ORDER_DTORS) - .Default(ORDER_DATA); - - case DefinedAtom::typeZeroFill: - case DefinedAtom::typeZeroFillFast: - return ORDER_BSS; - - case DefinedAtom::typeGOT: - return llvm::StringSwitch<Layout::SectionOrder>(name) - .StartsWith(".got.plt", ORDER_GOT_PLT) - .Default(ORDER_GOT); - - case DefinedAtom::typeStub: - return ORDER_PLT; - - case DefinedAtom::typeRONote: - return ORDER_RO_NOTE; - - case DefinedAtom::typeRWNote: - return ORDER_RW_NOTE; - - case DefinedAtom::typeNoAlloc: - return ORDER_NOALLOC; - - case DefinedAtom::typeThreadData: - return ORDER_TDATA; - case DefinedAtom::typeThreadZeroFill: - return ORDER_TBSS; - default: - // If we get passed in a section push it to OTHER - if (contentPermissions == DefinedAtom::perm___) - return ORDER_OTHER; - - return ORDER_NOT_DEFINED; - } -} - -/// \brief This maps the input sections to the output section names -template <class ELFT> -StringRef -DefaultLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const { - if (da->sectionChoice() == DefinedAtom::sectionBasedOnContent) { - switch (da->contentType()) { - case DefinedAtom::typeCode: - return ".text"; - case DefinedAtom::typeData: - return ".data"; - case DefinedAtom::typeConstant: - return ".rodata"; - case DefinedAtom::typeZeroFill: - return ".bss"; - case DefinedAtom::typeThreadData: - return ".tdata"; - case DefinedAtom::typeThreadZeroFill: - return ".tbss"; - default: - break; - } - } - return da->customSectionName(); -} - -/// \brief This maps the input sections to the output section names. -template <class ELFT> -StringRef -DefaultLayout<ELFT>::getOutputSectionName(StringRef archivePath, - StringRef memberPath, - StringRef inputSectionName) const { - StringRef outputSectionName; - if (_linkerScriptSema.hasLayoutCommands()) { - script::Sema::SectionKey key = {archivePath, memberPath, inputSectionName}; - outputSectionName = _linkerScriptSema.getOutputSection(key); - if (!outputSectionName.empty()) - return outputSectionName; - } - return llvm::StringSwitch<StringRef>(inputSectionName) - .StartsWith(".text", ".text") - .StartsWith(".ctors", ".ctors") - .StartsWith(".dtors", ".dtors") - .StartsWith(".rodata", ".rodata") - .StartsWith(".gcc_except_table", ".gcc_except_table") - .StartsWith(".data.rel.ro", ".data.rel.ro") - .StartsWith(".data.rel.local", ".data.rel.local") - .StartsWith(".data", ".data") - .StartsWith(".tdata", ".tdata") - .StartsWith(".tbss", ".tbss") - .StartsWith(".init_array", ".init_array") - .StartsWith(".fini_array", ".fini_array") - .Default(inputSectionName); -} - -/// \brief Gets the segment for a output section -template <class ELFT> -Layout::SegmentType DefaultLayout<ELFT>::getSegmentType( - Section<ELFT> *section) const { - - switch (section->order()) { - case ORDER_INTERP: - return llvm::ELF::PT_INTERP; - - case ORDER_TEXT: - case ORDER_HASH: - case ORDER_DYNAMIC_SYMBOLS: - case ORDER_DYNAMIC_STRINGS: - case ORDER_DYNAMIC_RELOCS: - case ORDER_DYNAMIC_PLT_RELOCS: - case ORDER_REL: - case ORDER_INIT: - case ORDER_PLT: - case ORDER_FINI: - case ORDER_RODATA: - case ORDER_EH_FRAME: - case ORDER_CTORS: - case ORDER_DTORS: - return llvm::ELF::PT_LOAD; - - case ORDER_RO_NOTE: - case ORDER_RW_NOTE: - return llvm::ELF::PT_NOTE; - - case ORDER_DYNAMIC: - return llvm::ELF::PT_DYNAMIC; - - case ORDER_EH_FRAMEHDR: - return llvm::ELF::PT_GNU_EH_FRAME; - - case ORDER_GOT: - case ORDER_GOT_PLT: - case ORDER_DATA: - case ORDER_BSS: - case ORDER_INIT_ARRAY: - case ORDER_FINI_ARRAY: - return llvm::ELF::PT_LOAD; - - case ORDER_TDATA: - case ORDER_TBSS: - return llvm::ELF::PT_TLS; - - default: - return llvm::ELF::PT_NULL; - } -} - -template <class ELFT> -bool DefaultLayout<ELFT>::hasOutputSegment(Section<ELFT> *section) { - switch (section->order()) { - case ORDER_INTERP: - case ORDER_HASH: - case ORDER_DYNAMIC_SYMBOLS: - case ORDER_DYNAMIC_STRINGS: - case ORDER_DYNAMIC_RELOCS: - case ORDER_DYNAMIC_PLT_RELOCS: - case ORDER_REL: - case ORDER_INIT: - case ORDER_PLT: - case ORDER_TEXT: - case ORDER_FINI: - case ORDER_RODATA: - case ORDER_EH_FRAME: - case ORDER_EH_FRAMEHDR: - case ORDER_TDATA: - case ORDER_TBSS: - case ORDER_RO_NOTE: - case ORDER_RW_NOTE: - case ORDER_DYNAMIC: - case ORDER_CTORS: - case ORDER_DTORS: - case ORDER_GOT: - case ORDER_GOT_PLT: - case ORDER_DATA: - case ORDER_INIT_ARRAY: - case ORDER_FINI_ARRAY: - case ORDER_BSS: - case ORDER_NOALLOC: - return true; - default: - return section->hasOutputSegment(); - } -} - -template <class ELFT> -AtomSection<ELFT> *DefaultLayout<ELFT>::createSection( - StringRef sectionName, int32_t contentType, - DefinedAtom::ContentPermissions permissions, SectionOrder sectionOrder) { - return new (_allocator) AtomSection<ELFT>(_context, sectionName, contentType, - permissions, sectionOrder); -} - -template <class ELFT> -AtomSection<ELFT> * -DefaultLayout<ELFT>::getSection(StringRef sectionName, int32_t contentType, - DefinedAtom::ContentPermissions permissions, - const DefinedAtom *da) { - const SectionKey sectionKey(sectionName, permissions, da->file().path()); - SectionOrder sectionOrder = getSectionOrder(sectionName, contentType, permissions); - auto sec = _sectionMap.find(sectionKey); - if (sec != _sectionMap.end()) - return sec->second; - AtomSection<ELFT> *newSec = - createSection(sectionName, contentType, permissions, sectionOrder); - - newSec->setOutputSectionName(getOutputSectionName( - da->file().archivePath(), da->file().memberPath(), sectionName)); - newSec->setOrder(sectionOrder); - newSec->setArchiveNameOrPath(da->file().archivePath()); - newSec->setMemberNameOrPath(da->file().memberPath()); - _sections.push_back(newSec); - _sectionMap.insert(std::make_pair(sectionKey, newSec)); - return newSec; -} - -template <class ELFT> -ErrorOr<const lld::AtomLayout *> -DefaultLayout<ELFT>::addAtom(const Atom *atom) { - if (const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom)) { - // HACK: Ignore undefined atoms. We need to adjust the interface so that - // undefined atoms can still be included in the output symbol table for - // -noinhibit-exec. - if (definedAtom->contentType() == DefinedAtom::typeUnknown) - return make_error_code(llvm::errc::invalid_argument); - const DefinedAtom::ContentPermissions permissions = - definedAtom->permissions(); - const DefinedAtom::ContentType contentType = definedAtom->contentType(); - - StringRef sectionName = getInputSectionName(definedAtom); - AtomSection<ELFT> *section = - getSection(sectionName, contentType, permissions, definedAtom); - - // Add runtime relocations to the .rela section. - for (const auto &reloc : *definedAtom) { - bool isLocalReloc = true; - if (_context.isDynamicRelocation(*reloc)) { - getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc); - isLocalReloc = false; - } else if (_context.isPLTRelocation(*reloc)) { - getPLTRelocationTable()->addRelocation(*definedAtom, *reloc); - isLocalReloc = false; - } - - if (!reloc->target()) - continue; - - //Ignore undefined atoms that are not target of dynamic relocations - if (isa<UndefinedAtom>(reloc->target()) && isLocalReloc) - continue; - - if (_context.isCopyRelocation(*reloc)) { - _copiedDynSymNames.insert(definedAtom->name()); - continue; - } - - _referencedDynAtoms.insert(reloc->target()); - } - - return section->appendAtom(atom); - } else if (const AbsoluteAtom *absoluteAtom = dyn_cast<AbsoluteAtom>(atom)) { - // Absolute atoms are not part of any section, they are global for the whole - // link - _absoluteAtoms.push_back(new (_allocator) - lld::AtomLayout(absoluteAtom, 0, absoluteAtom->value())); - return _absoluteAtoms.back(); - } else { - llvm_unreachable("Only absolute / defined atoms can be added here"); - } -} - -/// Output sections with the same name into a OutputSection -template <class ELFT> void DefaultLayout<ELFT>::createOutputSections() { - OutputSection<ELFT> *outputSection; - - for (auto &si : _sections) { - Section<ELFT> *section = dyn_cast<Section<ELFT>>(si); - if (!section) - continue; - const std::pair<StringRef, OutputSection<ELFT> *> currentOutputSection( - section->outputSectionName(), nullptr); - std::pair<typename OutputSectionMapT::iterator, bool> outputSectionInsert( - _outputSectionMap.insert(currentOutputSection)); - if (!outputSectionInsert.second) { - outputSection = outputSectionInsert.first->second; - } else { - outputSection = new (_allocator.Allocate<OutputSection<ELFT>>()) - OutputSection<ELFT>(section->outputSectionName()); - _outputSections.push_back(outputSection); - outputSectionInsert.first->second = outputSection; - } - outputSection->appendSection(si); - } -} - -template <class ELFT> -uint32_t -DefaultLayout<ELFT>::getPriorityFromSectionName(StringRef sectionName) const { - StringRef priority = sectionName.drop_front().rsplit('.').second; - uint32_t prio; - if (priority.getAsInteger(10, prio)) - return std::numeric_limits<uint32_t>::max(); - return prio; -} - -template <class ELFT> -void DefaultLayout<ELFT>::sortOutputSectionByPriority( - StringRef outputSectionName, StringRef prefix) { - OutputSection<ELFT> *outputSection = findOutputSection(outputSectionName); - if (!outputSection) - return; - - auto sections = outputSection->sections(); - - std::sort(sections.begin(), sections.end(), - [&](Chunk<ELFT> *lhs, Chunk<ELFT> *rhs) { - Section<ELFT> *lhsSection = dyn_cast<Section<ELFT>>(lhs); - Section<ELFT> *rhsSection = dyn_cast<Section<ELFT>>(rhs); - if (!lhsSection || !rhsSection) - return false; - StringRef lhsSectionName = lhsSection->inputSectionName(); - StringRef rhsSectionName = rhsSection->inputSectionName(); - - if (!prefix.empty()) { - if (!lhsSectionName.startswith(prefix) || - !rhsSectionName.startswith(prefix)) - return false; - } - return getPriorityFromSectionName(lhsSectionName) < - getPriorityFromSectionName(rhsSectionName); - }); -} - -template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() { - ScopedTask task(getDefaultDomain(), "assignSectionsToSegments"); - ELFLinkingContext::OutputMagic outputMagic = _context.getOutputMagic(); - // sort the sections by their order as defined by the layout - sortInputSections(); - - // Create output sections. - createOutputSections(); - - // Finalize output section layout. - finalizeOutputSectionLayout(); - - // Set the ordinal after sorting the sections - int ordinal = 1; - for (auto osi : _outputSections) { - osi->setOrdinal(ordinal); - for (auto ai : osi->sections()) { - ai->setOrdinal(ordinal); - } - ++ordinal; - } - for (auto osi : _outputSections) { - for (auto ai : osi->sections()) { - if (auto section = dyn_cast<Section<ELFT> >(ai)) { - if (!hasOutputSegment(section)) - continue; - - osi->setLoadableSection(section->isLoadableSection()); - - // Get the segment type for the section - int64_t segmentType = getSegmentType(section); - - osi->setHasSegment(); - section->setSegmentType(segmentType); - StringRef segmentName = section->segmentKindToStr(); - - int64_t lookupSectionFlag = osi->flags(); - if ((!(lookupSectionFlag & llvm::ELF::SHF_WRITE)) && - (_context.mergeRODataToTextSegment())) - lookupSectionFlag &= ~llvm::ELF::SHF_EXECINSTR; - - // Merge string sections into Data segment itself - lookupSectionFlag &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE); - - // Merge the TLS section into the DATA segment itself - lookupSectionFlag &= ~(llvm::ELF::SHF_TLS); - - Segment<ELFT> *segment; - // We need a separate segment for sections that don't have - // the segment type to be PT_LOAD - if (segmentType != llvm::ELF::PT_LOAD) { - const AdditionalSegmentKey key(segmentType, lookupSectionFlag); - const std::pair<AdditionalSegmentKey, Segment<ELFT> *> - additionalSegment(key, nullptr); - std::pair<typename AdditionalSegmentMapT::iterator, bool> - additionalSegmentInsert( - _additionalSegmentMap.insert(additionalSegment)); - if (!additionalSegmentInsert.second) { - segment = additionalSegmentInsert.first->second; - } else { - segment = new (_allocator) - Segment<ELFT>(_context, segmentName, segmentType); - additionalSegmentInsert.first->second = segment; - _segments.push_back(segment); - } - segment->append(section); - } - if (segmentType == llvm::ELF::PT_NULL) - continue; - - // If the output magic is set to OutputMagic::NMAGIC or - // OutputMagic::OMAGIC, Place the data alongside text in one single - // segment - if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC || - outputMagic == ELFLinkingContext::OutputMagic::OMAGIC) - lookupSectionFlag = llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC | - llvm::ELF::SHF_WRITE; - - // Use the flags of the merged Section for the segment - const SegmentKey key("PT_LOAD", lookupSectionFlag); - const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key, - nullptr); - std::pair<typename SegmentMapT::iterator, bool> segmentInsert( - _segmentMap.insert(currentSegment)); - if (!segmentInsert.second) { - segment = segmentInsert.first->second; - } else { - segment = new (_allocator) - Segment<ELFT>(_context, "PT_LOAD", llvm::ELF::PT_LOAD); - segmentInsert.first->second = segment; - _segments.push_back(segment); - } - // Insert chunks with linker script expressions that occur at this - // point, just before appending a new input section - addExtraChunksToSegment(segment, section->archivePath(), - section->memberPath(), - section->inputSectionName()); - segment->append(section); - } - } - } - if (_context.isDynamic() && !_context.isDynamicLibrary()) { - Segment<ELFT> *segment = - new (_allocator) ProgramHeaderSegment<ELFT>(_context); - _segments.push_back(segment); - segment->append(_elfHeader); - segment->append(_programHeader); - } -} - -template<class ELFT> -void -DefaultLayout<ELFT>::assignVirtualAddress() { - if (_segments.empty()) - return; - - std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments); - - uint64_t baseAddress = _context.getBaseAddress(); - - // HACK: This is a super dirty hack. The elf header and program header are - // not part of a section, but we need them to be loaded at the base address - // so that AT_PHDR is set correctly by the loader and so they are accessible - // at runtime. To do this we simply prepend them to the first loadable Segment - // and let the layout logic take care of it. - Segment<ELFT> *firstLoadSegment = nullptr; - for (auto si : _segments) { - if (si->segmentType() == llvm::ELF::PT_LOAD) { - firstLoadSegment = si; - si->firstSection()->setAlign(si->alignment()); - break; - } - } - assert(firstLoadSegment != nullptr && "No loadable segment!"); - firstLoadSegment->prepend(_programHeader); - firstLoadSegment->prepend(_elfHeader); - bool newSegmentHeaderAdded = true; - bool virtualAddressAssigned = false; - bool fileOffsetAssigned = false; - while (true) { - for (auto si : _segments) { - si->finalize(); - // Don't add PT_NULL segments into the program header - if (si->segmentType() != llvm::ELF::PT_NULL) - newSegmentHeaderAdded = _programHeader->addSegment(si); - } - if (!newSegmentHeaderAdded && virtualAddressAssigned) - break; - uint64_t address = baseAddress; - // start assigning virtual addresses - for (auto &si : _segments) { - if ((si->segmentType() != llvm::ELF::PT_LOAD) && - (si->segmentType() != llvm::ELF::PT_NULL)) - continue; - - if (si->segmentType() == llvm::ELF::PT_NULL) { - si->assignVirtualAddress(0 /*non loadable*/); - } else { - if (virtualAddressAssigned && (address != baseAddress) && - (address == si->virtualAddr())) - break; - si->assignVirtualAddress(address); - } - address = si->virtualAddr() + si->memSize(); - } - uint64_t baseFileOffset = 0; - uint64_t fileoffset = baseFileOffset; - for (auto &si : _segments) { - if ((si->segmentType() != llvm::ELF::PT_LOAD) && - (si->segmentType() != llvm::ELF::PT_NULL)) - continue; - if (fileOffsetAssigned && (fileoffset != baseFileOffset) && - (fileoffset == si->fileOffset())) - break; - si->assignFileOffsets(fileoffset); - fileoffset = si->fileOffset() + si->fileSize(); - } - virtualAddressAssigned = true; - fileOffsetAssigned = true; - _programHeader->resetProgramHeaders(); - } - Section<ELFT> *section; - // Fix the offsets of all the atoms within a section - for (auto &si : _sections) { - section = dyn_cast<Section<ELFT>>(si); - if (section && DefaultLayout<ELFT>::hasOutputSegment(section)) - section->assignFileOffsets(section->fileOffset()); - } - // Set the size of the merged Sections - for (auto osi : _outputSections) { - uint64_t sectionfileoffset = 0; - uint64_t startFileOffset = 0; - uint64_t sectionsize = 0; - bool isFirstSection = true; - for (auto si : osi->sections()) { - if (isFirstSection) { - startFileOffset = si->fileOffset(); - isFirstSection = false; - } - sectionfileoffset = si->fileOffset(); - sectionsize = si->fileSize(); - } - sectionsize = (sectionfileoffset - startFileOffset) + sectionsize; - osi->setFileOffset(startFileOffset); - osi->setSize(sectionsize); - } - // Set the virtual addr of the merged Sections - for (auto osi : _outputSections) { - uint64_t sectionstartaddr = 0; - uint64_t startaddr = 0; - uint64_t sectionsize = 0; - bool isFirstSection = true; - for (auto si : osi->sections()) { - if (isFirstSection) { - startaddr = si->virtualAddr(); - isFirstSection = false; - } - sectionstartaddr = si->virtualAddr(); - sectionsize = si->memSize(); - } - sectionsize = (sectionstartaddr - startaddr) + sectionsize; - osi->setMemSize(sectionsize); - osi->setAddr(startaddr); - } -} - -template <class ELFT> -void DefaultLayout<ELFT>::assignFileOffsetsForMiscSections() { - uint64_t fileoffset = 0; - uint64_t size = 0; - for (auto si : _segments) { - // Don't calculate offsets from non loadable segments - if ((si->segmentType() != llvm::ELF::PT_LOAD) && - (si->segmentType() != llvm::ELF::PT_NULL)) - continue; - fileoffset = si->fileOffset(); - size = si->fileSize(); - } - fileoffset = fileoffset + size; - Section<ELFT> *section; - for (auto si : _sections) { - section = dyn_cast<Section<ELFT>>(si); - if (section && DefaultLayout<ELFT>::hasOutputSegment(section)) - continue; - fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment()); - si->setFileOffset(fileoffset); - si->setVirtualAddr(0); - fileoffset += si->fileSize(); - } -} - -template <class ELFT> void DefaultLayout<ELFT>::sortInputSections() { - // First, sort according to default layout's order - std::stable_sort( - _sections.begin(), _sections.end(), - [](Chunk<ELFT> *A, Chunk<ELFT> *B) { return A->order() < B->order(); }); - - if (!_linkerScriptSema.hasLayoutCommands()) - return; - - // Sort the sections by their order as defined by the linker script - std::stable_sort(this->_sections.begin(), this->_sections.end(), - [this](Chunk<ELFT> *A, Chunk<ELFT> *B) { - auto *a = dyn_cast<Section<ELFT>>(A); - auto *b = dyn_cast<Section<ELFT>>(B); - - if (a == nullptr) - return false; - if (b == nullptr) - return true; - - return _linkerScriptSema.less( - {a->archivePath(), a->memberPath(), - a->inputSectionName()}, - {b->archivePath(), b->memberPath(), - b->inputSectionName()}); - }); - // Now try to arrange sections with no mapping rules to sections with - // similar content - auto p = this->_sections.begin(); - // Find first section that has no assigned rule id - while (p != this->_sections.end()) { - auto *sect = dyn_cast<AtomSection<ELFT>>(*p); - if (!sect) - break; - - if (!_linkerScriptSema.hasMapping({sect->archivePath(), - sect->memberPath(), - sect->inputSectionName()})) - break; - - ++p; - } - // For all sections that have no assigned rule id, try to move them near a - // section with similar contents - if (p != this->_sections.begin()) { - for (; p != this->_sections.end(); ++p) { - auto q = p; - --q; - while (q != this->_sections.begin() && - (*q)->getContentType() != (*p)->getContentType()) - --q; - if ((*q)->getContentType() != (*p)->getContentType()) - continue; - ++q; - for (auto i = p; i != q;) { - auto next = i--; - std::iter_swap(i, next); - } - } - } -} - -template <class ELFT> -void DefaultLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment, - StringRef archivePath, - StringRef memberPath, - StringRef sectionName) { - if (!_linkerScriptSema.hasLayoutCommands()) - return; - - std::vector<const script::SymbolAssignment *> exprs = - _linkerScriptSema.getExprs({archivePath, memberPath, sectionName}); - for (auto expr : exprs) { - auto expChunk = - new (this->_allocator) ExpressionChunk<ELFT>(this->_context, expr); - segment->append(expChunk); - } -} - -} // end namespace elf -} // end namespace lld - -#endif diff --git a/lib/ReaderWriter/ELF/DefaultTargetHandler.h b/lib/ReaderWriter/ELF/DefaultTargetHandler.h deleted file mode 100644 index 16668f2df618..000000000000 --- a/lib/ReaderWriter/ELF/DefaultTargetHandler.h +++ /dev/null @@ -1,38 +0,0 @@ -//===- lib/ReaderWriter/ELF/DefaultTargetHandler.h ------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_ELF_DEFAULT_TARGET_HANDLER_H -#define LLD_READER_WRITER_ELF_DEFAULT_TARGET_HANDLER_H - -#include "DefaultLayout.h" -#include "DynamicLibraryWriter.h" -#include "ELFReader.h" -#include "ExecutableWriter.h" -#include "TargetHandler.h" -#include "lld/ReaderWriter/ELFLinkingContext.h" -#include "llvm/ADT/Triple.h" -#include "llvm/Support/ELF.h" - -namespace lld { -namespace elf { -template <class ELFT> -class DefaultTargetHandler : public TargetHandler<ELFT> { -public: - const TargetRelocationHandler &getRelocationHandler() const = 0; - - virtual std::unique_ptr<Reader> getObjReader() = 0; - - virtual std::unique_ptr<Reader> getDSOReader() = 0; - - virtual std::unique_ptr<Writer> getWriter() = 0; -}; - -} // end namespace elf -} // end namespace lld -#endif diff --git a/lib/ReaderWriter/ELF/DynamicFile.cpp b/lib/ReaderWriter/ELF/DynamicFile.cpp new file mode 100644 index 000000000000..5339c7d66577 --- /dev/null +++ b/lib/ReaderWriter/ELF/DynamicFile.cpp @@ -0,0 +1,146 @@ +//===- lib/ReaderWriter/ELF/DynamicFile.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DynamicFile.h" +#include "FileCommon.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Path.h" + +namespace lld { +namespace elf { + +template <class ELFT> +DynamicFile<ELFT>::DynamicFile(std::unique_ptr<MemoryBuffer> mb, + ELFLinkingContext &ctx) + : SharedLibraryFile(mb->getBufferIdentifier()), _mb(std::move(mb)), + _ctx(ctx), _useShlibUndefines(ctx.useShlibUndefines()) {} + +template <typename ELFT> +std::error_code DynamicFile<ELFT>::isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx) { + return elf::isCompatible<ELFT>(mb, ctx); +} + +template <class ELFT> +const SharedLibraryAtom *DynamicFile<ELFT>::exports(StringRef name, + bool dataSymbolOnly) const { + assert(!dataSymbolOnly && "Invalid option for ELF exports!"); + // See if we have the symbol. + auto sym = _nameToSym.find(name); + if (sym == _nameToSym.end()) + return nullptr; + // Have we already created a SharedLibraryAtom for it? + if (sym->second._atom) + return sym->second._atom; + // Create a SharedLibraryAtom for this symbol. + return sym->second._atom = new (_alloc) + ELFDynamicAtom<ELFT>(*this, name, _soname, sym->second._symbol); +} + +template <class ELFT> StringRef DynamicFile<ELFT>::getDSOName() const { + return _soname; +} + +template <class ELFT> bool DynamicFile<ELFT>::canParse(file_magic magic) { + return magic == file_magic::elf_shared_object; +} + +template <class ELFT> std::error_code DynamicFile<ELFT>::doParse() { + typedef llvm::object::ELFFile<ELFT> ELFO; + typedef typename ELFO::Elf_Shdr Elf_Shdr; + typedef typename ELFO::Elf_Dyn Elf_Dyn; + + std::error_code ec; + _objFile.reset(new ELFO(_mb->getBuffer(), ec)); + if (ec) + return ec; + + ELFO &obj = *_objFile; + + const char *base = _mb->getBuffer().data(); + const Elf_Dyn *dynStart = nullptr; + const Elf_Dyn *dynEnd = nullptr; + + const Elf_Shdr *dynSymSec = nullptr; + for (const Elf_Shdr &sec : obj.sections()) { + switch (sec.sh_type) { + case llvm::ELF::SHT_DYNAMIC: { + dynStart = reinterpret_cast<const Elf_Dyn *>(base + sec.sh_offset); + uint64_t size = sec.sh_size; + if (size % sizeof(Elf_Dyn)) + return llvm::object::object_error::parse_failed; + dynEnd = dynStart + size / sizeof(Elf_Dyn); + break; + } + case llvm::ELF::SHT_DYNSYM: + dynSymSec = &sec; + break; + } + } + + ErrorOr<StringRef> strTableOrErr = obj.getStringTableForSymtab(*dynSymSec); + if (std::error_code ec = strTableOrErr.getError()) + return ec; + StringRef stringTable = *strTableOrErr; + + for (const Elf_Dyn &dyn : llvm::make_range(dynStart, dynEnd)) { + if (dyn.d_tag == llvm::ELF::DT_SONAME) { + uint64_t offset = dyn.getVal(); + if (offset >= stringTable.size()) + return llvm::object::object_error::parse_failed; + _soname = StringRef(stringTable.data() + offset); + break; + } + } + + if (_soname.empty()) + _soname = llvm::sys::path::filename(path()); + + // Create a map from names to dynamic symbol table entries. + // TODO: This should use the object file's build in hash table instead if + // it exists. + for (auto i = obj.symbol_begin(dynSymSec), e = obj.symbol_end(dynSymSec); + i != e; ++i) { + auto name = i->getName(stringTable); + if ((ec = name.getError())) + return ec; + + // Dont add local symbols to dynamic entries. The first symbol in the + // dynamic symbol table is a local symbol. + if (i->getBinding() == llvm::ELF::STB_LOCAL) + continue; + + // TODO: Add absolute symbols + if (i->st_shndx == llvm::ELF::SHN_ABS) + continue; + + if (i->st_shndx == llvm::ELF::SHN_UNDEF) { + if (!_useShlibUndefines) + continue; + // Create an undefined atom. + if (!name->empty()) { + auto *newAtom = new (_alloc) ELFUndefinedAtom<ELFT>(*this, *name, &*i); + _undefinedAtoms.push_back(newAtom); + } + continue; + } + _nameToSym[*name]._symbol = &*i; + } + return std::error_code(); +} + +template class DynamicFile<ELF32LE>; +template class DynamicFile<ELF32BE>; +template class DynamicFile<ELF64LE>; +template class DynamicFile<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/DynamicFile.h b/lib/ReaderWriter/ELF/DynamicFile.h index c4e3e7165efd..a155900de781 100644 --- a/lib/ReaderWriter/ELF/DynamicFile.h +++ b/lib/ReaderWriter/ELF/DynamicFile.h @@ -12,96 +12,39 @@ #include "Atoms.h" #include "lld/Core/SharedLibraryFile.h" -#include "lld/ReaderWriter/ELFLinkingContext.h" -#include "llvm/Object/ELF.h" -#include "llvm/Support/Path.h" #include <unordered_map> namespace lld { +class ELFLinkingContext; + namespace elf { + template <class ELFT> class DynamicFile : public SharedLibraryFile { public: - static ErrorOr<std::unique_ptr<DynamicFile>> - create(std::unique_ptr<llvm::MemoryBuffer> mb, ELFLinkingContext &ctx); - - const SharedLibraryAtom *exports(StringRef name, - bool dataSymbolOnly) const override { - assert(!dataSymbolOnly && "Invalid option for ELF exports!"); - // See if we have the symbol. - auto sym = _nameToSym.find(name); - if (sym == _nameToSym.end()) - return nullptr; - // Have we already created a SharedLibraryAtom for it? - if (sym->second._atom) - return sym->second._atom; - // Create a SharedLibraryAtom for this symbol. - return sym->second._atom = new (_alloc) ELFDynamicAtom<ELFT>( - *this, name, _soname, sym->second._symbol); - } - - StringRef getDSOName() const override { return _soname; } - -protected: - std::error_code doParse() override { - std::error_code ec; - _objFile.reset( - new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); - if (ec) - return ec; - - llvm::object::ELFFile<ELFT> &obj = *_objFile; + DynamicFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); - _soname = obj.getLoadName(); - if (_soname.empty()) - _soname = llvm::sys::path::filename(path()); + static std::error_code isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx); - // Create a map from names to dynamic symbol table entries. - // TODO: This should use the object file's build in hash table instead if - // it exists. - for (auto i = obj.begin_dynamic_symbols(), e = obj.end_dynamic_symbols(); - i != e; ++i) { - auto name = obj.getSymbolName(i); - if ((ec = name.getError())) - return ec; + const SharedLibraryAtom *exports(StringRef name, + bool dataSymbolOnly) const override; - // Dont add local symbols to dynamic entries. The first symbol in the - // dynamic symbol table is a local symbol. - if (i->getBinding() == llvm::ELF::STB_LOCAL) - continue; + StringRef getDSOName() const override; - // TODO: Add absolute symbols - if (i->st_shndx == llvm::ELF::SHN_ABS) - continue; + static bool canParse(file_magic magic); - if (i->st_shndx == llvm::ELF::SHN_UNDEF) { - if (!_useShlibUndefines) - continue; - // Create an undefined atom. - if (!name->empty()) { - auto *newAtom = new (_alloc) ELFUndefinedAtom<ELFT>(*this, *name, &*i); - _undefinedAtoms._atoms.push_back(newAtom); - } - continue; - } - _nameToSym[*name]._symbol = &*i; - } - return std::error_code(); - } +protected: + std::error_code doParse() override; private: - DynamicFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) - : SharedLibraryFile(mb->getBufferIdentifier()), _mb(std::move(mb)), - _ctx(ctx), _useShlibUndefines(ctx.useShlibUndefines()) {} - mutable llvm::BumpPtrAllocator _alloc; std::unique_ptr<llvm::object::ELFFile<ELFT>> _objFile; /// \brief DT_SONAME StringRef _soname; struct SymAtomPair { - SymAtomPair() : _symbol(nullptr), _atom(nullptr) {} - const typename llvm::object::ELFFile<ELFT>::Elf_Sym *_symbol; - const SharedLibraryAtom *_atom; + const typename llvm::object::ELFFile<ELFT>::Elf_Sym *_symbol = nullptr; + const SharedLibraryAtom *_atom = nullptr; }; std::unique_ptr<MemoryBuffer> _mb; @@ -110,13 +53,6 @@ private: mutable std::unordered_map<StringRef, SymAtomPair> _nameToSym; }; -template <class ELFT> -ErrorOr<std::unique_ptr<DynamicFile<ELFT>>> -DynamicFile<ELFT>::create(std::unique_ptr<llvm::MemoryBuffer> mb, - ELFLinkingContext &ctx) { - return std::unique_ptr<DynamicFile>(new DynamicFile(std::move(mb), ctx)); -} - } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h index f97514b525c0..5f2c1d1a8288 100644 --- a/lib/ReaderWriter/ELF/DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h @@ -16,27 +16,19 @@ namespace elf { using namespace llvm; using namespace llvm::object; -template<class ELFT> -class DynamicLibraryWriter; - //===----------------------------------------------------------------------===// // DynamicLibraryWriter Class //===----------------------------------------------------------------------===// template<class ELFT> class DynamicLibraryWriter : public OutputELFWriter<ELFT> { public: - DynamicLibraryWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout) - : OutputELFWriter<ELFT>(context, layout), - _runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {} - -protected: - virtual void buildDynamicSymbolTable(const File &file); - virtual void addDefaultAtoms(); - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &); - virtual void finalizeDefaultAtomValues(); + DynamicLibraryWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(ctx, layout) {} protected: - std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; + void buildDynamicSymbolTable(const File &file) override; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + void finalizeDefaultAtomValues() override; }; //===----------------------------------------------------------------------===// @@ -62,30 +54,28 @@ void DynamicLibraryWriter<ELFT>::buildDynamicSymbolTable(const File &file) { OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); } -template <class ELFT> void DynamicLibraryWriter<ELFT>::addDefaultAtoms() { - _runtimeFile->addAbsoluteAtom("_end"); -} - /// \brief Hook in lld to add CRuntime file template <class ELFT> -bool DynamicLibraryWriter<ELFT>::createImplicitFiles( +void DynamicLibraryWriter<ELFT>::createImplicitFiles( std::vector<std::unique_ptr<File> > &result) { - // Add the default atoms as defined by executables - DynamicLibraryWriter<ELFT>::addDefaultAtoms(); OutputELFWriter<ELFT>::createImplicitFiles(result); - result.push_back(std::move(_runtimeFile)); - return true; + // Add the default atoms as defined by executables + auto file = llvm::make_unique<RuntimeFile<ELFT>>(this->_ctx, "C runtime"); + file->addAbsoluteAtom("_end"); + result.push_back(std::move(file)); } template <class ELFT> void DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { - auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); + OutputELFWriter<ELFT>::finalizeDefaultAtomValues(); + AtomLayout *underScoreEndAtom = this->_layout.findAbsoluteAtom("_end"); + assert(underScoreEndAtom); if (auto bssSection = this->_layout.findOutputSection(".bss")) { - (*underScoreEndAtomIter)->_virtualAddr = + underScoreEndAtom->_virtualAddr = bssSection->virtualAddr() + bssSection->memSize(); } else if (auto dataSection = this->_layout.findOutputSection(".data")) { - (*underScoreEndAtomIter)->_virtualAddr = + underScoreEndAtom->_virtualAddr = dataSection->virtualAddr() + dataSection->memSize(); } } diff --git a/lib/ReaderWriter/ELF/ELFFile.cpp b/lib/ReaderWriter/ELF/ELFFile.cpp new file mode 100644 index 000000000000..1488f1862b8d --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFFile.cpp @@ -0,0 +1,829 @@ +//===- lib/ReaderWriter/ELF/ELFFile.cpp -------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ELFFile.h" +#include "FileCommon.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace elf { + +template <typename ELFT> +ELFFile<ELFT>::ELFFile(StringRef name, ELFLinkingContext &ctx) + : SimpleFile(name), _ordinal(0), _doStringsMerge(ctx.mergeCommonStrings()), + _useWrap(false), _ctx(ctx) { + setLastError(std::error_code()); +} + +template <typename ELFT> +ELFFile<ELFT>::ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : SimpleFile(mb->getBufferIdentifier()), _mb(std::move(mb)), _ordinal(0), + _doStringsMerge(ctx.mergeCommonStrings()), + _useWrap(ctx.wrapCalls().size()), _ctx(ctx) {} + +template <typename ELFT> +std::error_code ELFFile<ELFT>::isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx) { + return elf::isCompatible<ELFT>(mb, ctx); +} + +template <typename ELFT> +Atom *ELFFile<ELFT>::findAtom(const Elf_Sym *sourceSym, + const Elf_Sym *targetSym) { + // Return the atom for targetSym if we can do so. + Atom *target = _symbolToAtomMapping.lookup(targetSym); + if (!target) + // Some realocations (R_ARM_V4BX) do not have a defined + // target. For this cases make it points to itself. + target = _symbolToAtomMapping.lookup(sourceSym); + + if (target->definition() != Atom::definitionRegular) + return target; + Atom::Scope scope = llvm::cast<DefinedAtom>(target)->scope(); + if (scope == DefinedAtom::scopeTranslationUnit) + return target; + if (!redirectReferenceUsingUndefAtom(sourceSym, targetSym)) + return target; + + // Otherwise, create a new undefined symbol and returns it. + StringRef targetName = target->name(); + auto it = _undefAtomsForGroupChild.find(targetName); + if (it != _undefAtomsForGroupChild.end()) + return it->getValue(); + auto atom = new (_readerStorage) SimpleUndefinedAtom(*this, targetName); + _undefAtomsForGroupChild[targetName] = atom; + addAtom(*atom); + return atom; +} + +template <typename ELFT> +ErrorOr<StringRef> ELFFile<ELFT>::getSectionName(const Elf_Shdr *shdr) const { + if (!shdr) + return StringRef(); + return _objFile->getSectionName(shdr); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::doParse() { + std::error_code ec; + _objFile.reset(new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); + if (ec) + return ec; + + if ((ec = createAtomsFromContext())) + return ec; + + // Read input sections from the input file that need to be converted to + // atoms + if ((ec = createAtomizableSections())) + return ec; + + // For mergeable strings, we would need to split the section into various + // atoms + if ((ec = createMergeableAtoms())) + return ec; + + // Create the necessary symbols that are part of the section that we + // created in createAtomizableSections function + if ((ec = createSymbolsFromAtomizableSections())) + return ec; + + // Create the appropriate atoms from the file + if ((ec = createAtoms())) + return ec; + return std::error_code(); +} + +template <class ELFT> Reference::KindArch ELFFile<ELFT>::kindArch() { + switch (_objFile->getHeader()->e_machine) { + case llvm::ELF::EM_X86_64: + return Reference::KindArch::x86_64; + case llvm::ELF::EM_386: + return Reference::KindArch::x86; + case llvm::ELF::EM_ARM: + return Reference::KindArch::ARM; + case llvm::ELF::EM_HEXAGON: + return Reference::KindArch::Hexagon; + case llvm::ELF::EM_MIPS: + return Reference::KindArch::Mips; + case llvm::ELF::EM_AARCH64: + return Reference::KindArch::AArch64; + } + llvm_unreachable("unsupported e_machine value"); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::createAtomizableSections() { + // Handle: SHT_REL and SHT_RELA sections: + // Increment over the sections, when REL/RELA section types are found add + // the contents to the RelocationReferences map. + // Record the number of relocs to guess at preallocating the buffer. + uint64_t totalRelocs = 0; + for (const Elf_Shdr §ion : _objFile->sections()) { + switch (section.sh_type) { + case llvm::ELF::SHT_SYMTAB: + _symtab = §ion; + continue; + case llvm::ELF::SHT_SYMTAB_SHNDX: { + ErrorOr<ArrayRef<Elf_Word>> tableOrErr = _objFile->getSHNDXTable(section); + if (std::error_code ec = tableOrErr.getError()) + return ec; + _shndxTable = *tableOrErr; + continue; + } + } + + if (isIgnoredSection(§ion)) + continue; + + if (isMergeableStringSection(§ion)) { + _mergeStringSections.push_back(§ion); + continue; + } + + if (section.sh_type == llvm::ELF::SHT_RELA) { + auto sHdrOrErr = _objFile->getSection(section.sh_info); + if (std::error_code ec = sHdrOrErr.getError()) + return ec; + auto sHdr = *sHdrOrErr; + auto rai = _objFile->rela_begin(§ion); + auto rae = _objFile->rela_end(§ion); + _relocationAddendReferences[sHdr] = make_range(rai, rae); + totalRelocs += std::distance(rai, rae); + } else if (section.sh_type == llvm::ELF::SHT_REL) { + auto sHdrOrErr = _objFile->getSection(section.sh_info); + if (std::error_code ec = sHdrOrErr.getError()) + return ec; + auto sHdr = *sHdrOrErr; + auto ri = _objFile->rel_begin(§ion); + auto re = _objFile->rel_end(§ion); + _relocationReferences[sHdr] = §ion; + totalRelocs += std::distance(ri, re); + } else { + auto sectionName = _objFile->getSectionName(§ion); + if (std::error_code ec = sectionName.getError()) + return ec; + _ctx.notifyInputSectionName(*sectionName); + _sectionSymbols[§ion]; + } + } + _references.reserve(totalRelocs); + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createMergeableAtoms() { + // Divide the section that contains mergeable strings into tokens + // TODO + // a) add resolver support to recognize multibyte chars + // b) Create a separate section chunk to write mergeable atoms + std::vector<MergeString *> tokens; + for (const Elf_Shdr *msi : _mergeStringSections) { + auto sectionName = getSectionName(msi); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto sectionContents = getSectionContents(msi); + if (std::error_code ec = sectionContents.getError()) + return ec; + + StringRef secCont(reinterpret_cast<const char *>(sectionContents->begin()), + sectionContents->size()); + + unsigned int prev = 0; + for (std::size_t i = 0, e = sectionContents->size(); i != e; ++i) { + if ((*sectionContents)[i] == '\0') { + tokens.push_back(new (_readerStorage) MergeString( + prev, secCont.slice(prev, i + 1), msi, *sectionName)); + prev = i + 1; + } + } + } + + // Create Mergeable atoms + for (const MergeString *tai : tokens) { + ArrayRef<uint8_t> content((const uint8_t *)tai->_string.data(), + tai->_string.size()); + ELFMergeAtom<ELFT> *atom = createMergedString(tai->_sectionName, tai->_shdr, + content, tai->_offset); + atom->setOrdinal(++_ordinal); + addAtom(*atom); + _mergeAtoms.push_back(atom); + } + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::createSymbolsFromAtomizableSections() { + // Increment over all the symbols collecting atoms and symbol names for + // later use. + if (!_symtab) + return std::error_code(); + + ErrorOr<StringRef> strTableOrErr = + _objFile->getStringTableForSymtab(*_symtab); + if (std::error_code ec = strTableOrErr.getError()) + return ec; + StringRef strTable = *strTableOrErr; + + auto SymI = _objFile->symbol_begin(_symtab), + SymE = _objFile->symbol_end(_symtab); + // Skip over dummy sym. + ++SymI; + + for (; SymI != SymE; ++SymI) { + ErrorOr<const Elf_Shdr *> section = + _objFile->getSection(SymI, _symtab, _shndxTable); + if (std::error_code ec = section.getError()) + return ec; + + auto symbolName = SymI->getName(strTable); + if (std::error_code ec = symbolName.getError()) + return ec; + + if (SymI->isAbsolute()) { + ELFAbsoluteAtom<ELFT> *absAtom = createAbsoluteAtom( + *symbolName, &*SymI, (int64_t)getSymbolValue(&*SymI)); + addAtom(*absAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, absAtom)); + } else if (SymI->isUndefined()) { + if (_useWrap && + (_wrapSymbolMap.find(*symbolName) != _wrapSymbolMap.end())) { + auto wrapAtom = _wrapSymbolMap.find(*symbolName); + _symbolToAtomMapping.insert( + std::make_pair(&*SymI, wrapAtom->getValue())); + continue; + } + ELFUndefinedAtom<ELFT> *undefAtom = + createUndefinedAtom(*symbolName, &*SymI); + addAtom(*undefAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, undefAtom)); + } else if (isCommonSymbol(&*SymI)) { + ELFCommonAtom<ELFT> *commonAtom = createCommonAtom(*symbolName, &*SymI); + commonAtom->setOrdinal(++_ordinal); + addAtom(*commonAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, commonAtom)); + } else if (SymI->isDefined()) { + _sectionSymbols[*section].push_back(SymI); + } else { + llvm::errs() << "Unable to create atom for: " << *symbolName << "\n"; + return llvm::object::object_error::parse_failed; + } + } + + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() { + // Holds all the atoms that are part of the section. They are the targets of + // the kindGroupChild reference. + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> atomsForSection; + + // Contains a list of comdat sections for a group. + for (auto &i : _sectionSymbols) { + const Elf_Shdr *section = i.first; + std::vector<const Elf_Sym *> &symbols = i.second; + + // Sort symbols by position. + std::stable_sort(symbols.begin(), symbols.end(), + [this](const Elf_Sym *a, const Elf_Sym *b) { + return getSymbolValue(&*a) < getSymbolValue(&*b); + }); + + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto sectionContents = getSectionContents(section); + if (std::error_code ec = sectionContents.getError()) + return ec; + + // SHT_GROUP sections are handled in the following loop. + if (isGroupSection(section)) + continue; + + bool addAtoms = (!isGnuLinkOnceSection(*sectionName) && + !isSectionMemberOfGroup(section)); + + if (handleSectionWithNoSymbols(section, symbols)) { + ELFDefinedAtom<ELFT> *newAtom = + createSectionAtom(section, *sectionName, *sectionContents); + newAtom->setOrdinal(++_ordinal); + if (addAtoms) + addAtom(*newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + continue; + } + + ELFDefinedAtom<ELFT> *previousAtom = nullptr; + ELFReference<ELFT> *anonFollowedBy = nullptr; + + if (!_symtab) + continue; + ErrorOr<StringRef> strTableOrErr = + _objFile->getStringTableForSymtab(*_symtab); + if (std::error_code ec = strTableOrErr.getError()) + return ec; + StringRef strTable = *strTableOrErr; + for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { + auto symbol = *si; + StringRef symbolName = ""; + if (symbol->getType() != llvm::ELF::STT_SECTION) { + auto symName = symbol->getName(strTable); + if (std::error_code ec = symName.getError()) + return ec; + symbolName = *symName; + } + + uint64_t contentSize = symbolContentSize( + section, &*symbol, (si + 1 == se) ? nullptr : &**(si + 1)); + + // Check to see if we need to add the FollowOn Reference + ELFReference<ELFT> *followOn = nullptr; + if (previousAtom) { + // Replace the followon atom with the anonymous atom that we created, + // so that the next symbol that we create is a followon from the + // anonymous atom. + if (anonFollowedBy) { + followOn = anonFollowedBy; + } else { + followOn = new (_readerStorage) + ELFReference<ELFT>(Reference::kindLayoutAfter); + previousAtom->addReference(followOn); + } + } + + ArrayRef<uint8_t> symbolData((const uint8_t *)sectionContents->data() + + getSymbolValue(&*symbol), + contentSize); + + // If the linker finds that a section has global atoms that are in a + // mergeable section, treat them as defined atoms as they shouldn't be + // merged away as well as these symbols have to be part of symbol + // resolution + if (isMergeableStringSection(section)) { + if (symbol->getBinding() != llvm::ELF::STB_GLOBAL) + continue; + ELFDefinedAtom<ELFT> *atom = createDefinedAtom( + symbolName, *sectionName, &**si, section, symbolData, + _references.size(), _references.size(), _references); + atom->setOrdinal(++_ordinal); + if (addAtoms) + addAtom(*atom); + else + atomsForSection[*sectionName].push_back(atom); + continue; + } + + // Don't allocate content to a weak symbol, as they may be merged away. + // Create an anonymous atom to hold the data. + ELFDefinedAtom<ELFT> *anonAtom = nullptr; + anonFollowedBy = nullptr; + if (symbol->getBinding() == llvm::ELF::STB_WEAK) { + // Create anonymous new non-weak ELF symbol that holds the symbol + // data. + auto sym = new (_readerStorage) Elf_Sym(*symbol); + sym->setBinding(llvm::ELF::STB_GLOBAL); + anonAtom = createDefinedAtomAndAssignRelocations( + "", *sectionName, sym, section, symbolData, *sectionContents); + symbolData = ArrayRef<uint8_t>(); + + // If this is the last atom, let's not create a followon reference. + if (anonAtom && (si + 1) != se) { + anonFollowedBy = new (_readerStorage) + ELFReference<ELFT>(Reference::kindLayoutAfter); + anonAtom->addReference(anonFollowedBy); + } + } + + ELFDefinedAtom<ELFT> *newAtom = createDefinedAtomAndAssignRelocations( + symbolName, *sectionName, &*symbol, section, symbolData, + *sectionContents); + newAtom->setOrdinal(++_ordinal); + + // If the atom was a weak symbol, let's create a followon reference to + // the anonymous atom that we created. + if (anonAtom) + createEdge(newAtom, anonAtom, Reference::kindLayoutAfter); + + if (previousAtom) { + // Set the followon atom to the weak atom that we have created, so + // that they would alias when the file gets written. + followOn->setTarget(anonAtom ? anonAtom : newAtom); + } + + // The previous atom is always the atom created before unless the atom + // is a weak atom. + previousAtom = anonAtom ? anonAtom : newAtom; + + if (addAtoms) + addAtom(*newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + + _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom)); + if (anonAtom) { + anonAtom->setOrdinal(++_ordinal); + if (addAtoms) + addAtom(*anonAtom); + else + atomsForSection[*sectionName].push_back(anonAtom); + } + } + } + + for (auto &i : _sectionSymbols) + if (std::error_code ec = handleSectionGroup(i.first, atomsForSection)) + return ec; + for (auto &i : _sectionSymbols) + if (std::error_code ec = handleGnuLinkOnceSection(i.first, atomsForSection)) + return ec; + + updateReferences(); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleGnuLinkOnceSection( + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection) { + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + if (!isGnuLinkOnceSection(*sectionName)) + return std::error_code(); + + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + for (auto ha : atomsForSection[*sectionName]) { + _groupChild[ha->symbol()] = std::make_pair(*sectionName, section); + auto *ref = + new (_readerStorage) ELFReference<ELFT>(Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[*sectionName].clear(); + // Create a gnu linkonce atom. + ELFDefinedAtom<ELFT> *atom = createDefinedAtom( + *sectionName, *sectionName, nullptr, section, ArrayRef<uint8_t>(), + referenceStart, _references.size(), _references); + atom->setOrdinal(++_ordinal); + addAtom(*atom); + for (auto reference : refs) + atom->addReference(reference); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleSectionGroup( + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection) { + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + if (!isGroupSection(section)) + return std::error_code(); + + auto sectionContents = getSectionContents(section); + if (std::error_code ec = sectionContents.getError()) + return ec; + + // A section of type SHT_GROUP defines a grouping of sections. The + // name of a symbol from one of the containing object's symbol tables + // provides a signature for the section group. The section header of + // the SHT_GROUP section specifies the identifying symbol entry, as + // described: the sh_link member contains the section header index of + // the symbol table section that contains the entry. The sh_info + // member contains the symbol table index of the identifying entry. + // The sh_flags member of the section header contains 0. The name of + // the section (sh_name) is not specified. + std::vector<StringRef> sectionNames; + const Elf_Word *groupMembers = + reinterpret_cast<const Elf_Word *>(sectionContents->data()); + const size_t count = section->sh_size / sizeof(Elf_Word); + for (size_t i = 1; i < count; i++) { + ErrorOr<const Elf_Shdr *> shdr = _objFile->getSection(groupMembers[i]); + if (std::error_code ec = shdr.getError()) + return ec; + ErrorOr<StringRef> sectionName = _objFile->getSectionName(*shdr); + if (std::error_code ec = sectionName.getError()) + return ec; + sectionNames.push_back(*sectionName); + } + ErrorOr<const Elf_Shdr *> symtab = _objFile->getSection(section->sh_link); + if (std::error_code ec = symtab.getError()) + return ec; + const Elf_Sym *symbol = _objFile->getSymbol(*symtab, section->sh_info); + ErrorOr<const Elf_Shdr *> strtab_sec = + _objFile->getSection((*symtab)->sh_link); + if (std::error_code ec = strtab_sec.getError()) + return ec; + ErrorOr<StringRef> strtab_or_err = _objFile->getStringTable(*strtab_sec); + if (std::error_code ec = strtab_or_err.getError()) + return ec; + StringRef strtab = *strtab_or_err; + ErrorOr<StringRef> symbolName = symbol->getName(strtab); + if (std::error_code ec = symbolName.getError()) + return ec; + + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + for (auto name : sectionNames) { + for (auto ha : atomsForSection[name]) { + _groupChild[ha->symbol()] = std::make_pair(*symbolName, section); + auto *ref = + new (_readerStorage) ELFReference<ELFT>(Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[name].clear(); + } + + // Create an atom for comdat signature. + ELFDefinedAtom<ELFT> *atom = createDefinedAtom( + *symbolName, *sectionName, nullptr, section, ArrayRef<uint8_t>(), + referenceStart, _references.size(), _references); + atom->setOrdinal(++_ordinal); + addAtom(*atom); + for (auto reference : refs) + atom->addReference(reference); + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createAtomsFromContext() { + if (!_useWrap) + return std::error_code(); + // Steps: + // a) Create an undefined atom for the symbol specified by the --wrap option, + // as that may be needed to be pulled from an archive. + // b) Create an undefined atom for __wrap_<symbolname>. + // c) All references to the symbol specified by wrap should point to + // __wrap_<symbolname> + // d) All references to __real_symbol should point to the <symbol> + for (auto &wrapsym : _ctx.wrapCalls()) { + StringRef wrapStr = wrapsym.getKey(); + // Create a undefined symbol fror the wrap symbol. + UndefinedAtom *wrapSymAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, wrapStr); + StringRef wrapCallSym = + _ctx.allocateString((llvm::Twine("__wrap_") + wrapStr).str()); + StringRef realCallSym = + _ctx.allocateString((llvm::Twine("__real_") + wrapStr).str()); + UndefinedAtom *wrapCallAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, wrapCallSym); + // Create maps, when there is call to sym, it should point to wrapCallSym. + _wrapSymbolMap.insert(std::make_pair(wrapStr, wrapCallAtom)); + // Whenever there is a reference to realCall it should point to the symbol + // created for each wrap usage. + _wrapSymbolMap.insert(std::make_pair(realCallSym, wrapSymAtom)); + addAtom(*wrapSymAtom); + addAtom(*wrapCallAtom); + } + return std::error_code(); +} + +template <class ELFT> +ELFDefinedAtom<ELFT> *ELFFile<ELFT>::createDefinedAtomAndAssignRelocations( + StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent) { + unsigned int referenceStart = _references.size(); + + // Add Rela (those with r_addend) references: + auto rari = _relocationAddendReferences.find(section); + if (rari != _relocationAddendReferences.end()) + createRelocationReferences(symbol, symContent, rari->second); + + // Add Rel references. + auto rri = _relocationReferences.find(section); + if (rri != _relocationReferences.end()) + createRelocationReferences(symbol, symContent, secContent, rri->second); + + // Create the DefinedAtom and add it to the list of DefinedAtoms. + return createDefinedAtom(symbolName, sectionName, symbol, section, symContent, + referenceStart, _references.size(), _references); +} + +template <class ELFT> +void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<const Elf_Rela *> rels) { + bool isMips64EL = _objFile->isMips64EL(); + const auto symValue = getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || symValue + content.size() <= rel.r_offset) + continue; + auto elfRelocation = new (_readerStorage) + ELFReference<ELFT>(&rel, rel.r_offset - symValue, kindArch(), + rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); + addReferenceToSymbol(elfRelocation, symbol); + _references.push_back(elfRelocation); + } +} + +template <class ELFT> +void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + const Elf_Shdr *relSec) { + auto rels = _objFile->rels(relSec); + bool isMips64EL = _objFile->isMips64EL(); + const auto symValue = getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || symValue + symContent.size() <= rel.r_offset) + continue; + auto elfRelocation = new (_readerStorage) + ELFReference<ELFT>(rel.r_offset - symValue, kindArch(), + rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); + Reference::Addend addend = getInitialAddend(symContent, symValue, rel); + elfRelocation->setAddend(addend); + addReferenceToSymbol(elfRelocation, symbol); + _references.push_back(elfRelocation); + } +} + +template <class ELFT> +void ELFFile<ELFT>::updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref, + const Elf_Sym *symbol, + const Elf_Shdr *shdr) { + // If the target atom is mergeable strefng atom, the atom might have been + // merged with other atom having the same contents. Try to find the + // merged one if that's the case. + int64_t addend = ref->addend(); + if (addend < 0) + addend = 0; + + const MergeSectionKey ms = {shdr, addend}; + auto msec = _mergedSectionMap.find(ms); + if (msec != _mergedSectionMap.end()) { + ref->setTarget(msec->second); + return; + } + + // The target atom was not merged. Mergeable atoms are not in + // _symbolToAtomMapping, so we cannot find it by calling findAtom(). We + // instead call findMergeAtom(). + if (symbol->getType() != llvm::ELF::STT_SECTION) + addend = getSymbolValue(symbol) + addend; + ELFMergeAtom<ELFT> *mergedAtom = findMergeAtom(shdr, addend); + ref->setOffset(addend - mergedAtom->offset()); + ref->setAddend(0); + ref->setTarget(mergedAtom); +} + +template <class ELFT> void ELFFile<ELFT>::updateReferences() { + for (auto &ri : _references) { + if (ri->kindNamespace() != Reference::KindNamespace::ELF) + continue; + const Elf_Sym *symbol = + _objFile->getSymbol(_symtab, ri->targetSymbolIndex()); + ErrorOr<const Elf_Shdr *> shdr = + _objFile->getSection(symbol, _symtab, _shndxTable); + + // If the atom is not in mergeable string section, the target atom is + // simply that atom. + if (isMergeableStringSection(*shdr)) + updateReferenceForMergeStringAccess(ri, symbol, *shdr); + else + ri->setTarget(findAtom(findSymbolForReference(ri), symbol)); + } +} + +template <class ELFT> +bool ELFFile<ELFT>::isIgnoredSection(const Elf_Shdr *section) { + switch (section->sh_type) { + case llvm::ELF::SHT_NULL: + case llvm::ELF::SHT_STRTAB: + case llvm::ELF::SHT_SYMTAB: + case llvm::ELF::SHT_SYMTAB_SHNDX: + return true; + default: + break; + } + return false; +} + +template <class ELFT> +bool ELFFile<ELFT>::isMergeableStringSection(const Elf_Shdr *section) { + if (_doStringsMerge && section) { + int64_t sectionFlags = section->sh_flags; + sectionFlags &= ~llvm::ELF::SHF_ALLOC; + // Mergeable string sections have both SHF_MERGE and SHF_STRINGS flags + // set. sh_entsize is the size of each character which is normally 1. + if ((section->sh_entsize < 2) && + (sectionFlags == (llvm::ELF::SHF_MERGE | llvm::ELF::SHF_STRINGS))) { + return true; + } + } + return false; +} + +template <class ELFT> +ELFDefinedAtom<ELFT> * +ELFFile<ELFT>::createSectionAtom(const Elf_Shdr *section, StringRef sectionName, + ArrayRef<uint8_t> content) { + auto *sym = new (_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->setBindingAndType(llvm::ELF::STB_LOCAL, llvm::ELF::STT_SECTION); + sym->st_other = 0; + sym->st_shndx = 0; + sym->st_value = 0; + sym->st_size = 0; + auto *newAtom = createDefinedAtomAndAssignRelocations( + "", sectionName, sym, section, content, content); + newAtom->setOrdinal(++_ordinal); + return newAtom; +} + +template <class ELFT> +uint64_t ELFFile<ELFT>::symbolContentSize(const Elf_Shdr *section, + const Elf_Sym *symbol, + const Elf_Sym *nextSymbol) { + const auto symValue = getSymbolValue(symbol); + // if this is the last symbol, take up the remaining data. + return nextSymbol ? getSymbolValue(nextSymbol) - symValue + : section->sh_size - symValue; +} + +template <class ELFT> +void ELFFile<ELFT>::createEdge(ELFDefinedAtom<ELFT> *from, + ELFDefinedAtom<ELFT> *to, uint32_t edgeKind) { + auto reference = new (_readerStorage) ELFReference<ELFT>(edgeKind); + reference->setTarget(to); + from->addReference(reference); +} + +/// Does the atom need to be redirected using a separate undefined atom? +template <class ELFT> +bool ELFFile<ELFT>::redirectReferenceUsingUndefAtom( + const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) const { + auto groupChildTarget = _groupChild.find(targetSymbol); + + // If the reference is not to a group child atom, there is no need to redirect + // using a undefined atom. Its also not needed if the source and target are + // from the same section. + if ((groupChildTarget == _groupChild.end()) || + (sourceSymbol->st_shndx == targetSymbol->st_shndx)) + return false; + + auto groupChildSource = _groupChild.find(sourceSymbol); + + // If the source symbol is not in a group, use a undefined symbol too. + if (groupChildSource == _groupChild.end()) + return true; + + // If the source and child are from the same group, we dont need the + // relocation to go through a undefined symbol. + if (groupChildSource->second.second == groupChildTarget->second.second) + return false; + return true; +} + +template <class ELFT> +void RuntimeFile<ELFT>::addAbsoluteAtom(StringRef symbolName, bool isHidden) { + assert(!symbolName.empty() && "AbsoluteAtoms must have a name"); + auto *sym = new (this->_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->st_value = 0; + sym->st_shndx = llvm::ELF::SHN_ABS; + sym->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + if (isHidden) + sym->setVisibility(llvm::ELF::STV_HIDDEN); + else + sym->setVisibility(llvm::ELF::STV_DEFAULT); + sym->st_size = 0; + ELFAbsoluteAtom<ELFT> *atom = this->createAbsoluteAtom(symbolName, sym, -1); + this->addAtom(*atom); +} + +template <class ELFT> +void RuntimeFile<ELFT>::addUndefinedAtom(StringRef symbolName) { + assert(!symbolName.empty() && "UndefinedAtoms must have a name"); + auto *sym = new (this->_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->st_value = 0; + sym->st_shndx = llvm::ELF::SHN_UNDEF; + sym->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_NOTYPE); + sym->setVisibility(llvm::ELF::STV_DEFAULT); + sym->st_size = 0; + ELFUndefinedAtom<ELFT> *atom = this->createUndefinedAtom(symbolName, sym); + this->addAtom(*atom); +} + +template class ELFFile<ELF32LE>; +template class ELFFile<ELF32BE>; +template class ELFFile<ELF64LE>; +template class ELFFile<ELF64BE>; + +template class RuntimeFile<ELF32LE>; +template class RuntimeFile<ELF32BE>; +template class RuntimeFile<ELF64LE>; +template class RuntimeFile<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFFile.h b/lib/ReaderWriter/ELF/ELFFile.h index 11f4ee4fc633..5e0c2fc75a87 100644 --- a/lib/ReaderWriter/ELF/ELFFile.h +++ b/lib/ReaderWriter/ELF/ELFFile.h @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/ELF/ELFFile.h -------------------------------------===// +//===- lib/ReaderWriter/ELF/ELFFile.h ---------------------------*- C++ -*-===// // // The LLVM Linker // @@ -11,7 +11,8 @@ #define LLD_READER_WRITER_ELF_FILE_H #include "Atoms.h" -#include <llvm/ADT/MapVector.h> +#include "FileCommon.h" +#include "llvm/ADT/MapVector.h" #include <map> #include <unordered_map> @@ -20,26 +21,20 @@ namespace lld { namespace elf { /// \brief Read a binary, find out based on the symbol table contents what kind /// of symbol it is and create corresponding atoms for it -template <class ELFT> class ELFFile : public File { - +template <class ELFT> class ELFFile : public SimpleFile { typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; - typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Iter Elf_Sym_Iter; - typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela_Iter Elf_Rela_Iter; - typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter; typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word; // A Map is used to hold the atoms that have been divided up // after reading the section that contains Merge String attributes struct MergeSectionKey { - MergeSectionKey(const Elf_Shdr *shdr, int64_t offset) - : _shdr(shdr), _offset(offset) {} - // Data members const Elf_Shdr *_shdr; int64_t _offset; }; + struct MergeSectionEq { int64_t operator()(const MergeSectionKey &k) const { return llvm::hash_combine((int64_t)(k._shdr->sh_name), @@ -71,23 +66,15 @@ template <class ELFT> class ELFFile : public File { // offset typedef std::vector<ELFMergeAtom<ELFT> *> MergeAtomsT; - /// \brief find a mergeAtom given a start offset - struct FindByOffset { - const Elf_Shdr *_shdr; - int64_t _offset; - FindByOffset(const Elf_Shdr *shdr, int64_t offset) - : _shdr(shdr), _offset(offset) {} - bool operator()(const ELFMergeAtom<ELFT> *a) { - int64_t off = a->offset(); - return (_shdr->sh_name == a->section()) && - ((_offset >= off) && (_offset <= off + (int64_t)a->size())); - } - }; - /// \brief find a merge atom given a offset - ELFMergeAtom<ELFT> *findMergeAtom(const Elf_Shdr *shdr, uint64_t offset) { + ELFMergeAtom<ELFT> *findMergeAtom(const Elf_Shdr *shdr, int64_t offset) { auto it = std::find_if(_mergeAtoms.begin(), _mergeAtoms.end(), - FindByOffset(shdr, offset)); + [=](const ELFMergeAtom<ELFT> *a) { + int64_t off = a->offset(); + return shdr->sh_name == a->section() && + offset >= off && + offset <= off + (int64_t)a->size(); + }); assert(it != _mergeAtoms.end()); return *it; } @@ -97,19 +84,15 @@ template <class ELFT> class ELFFile : public File { typedef typename MergedSectionMapT::iterator MergedSectionMapIterT; public: - ELFFile(StringRef name, ELFLinkingContext &ctx) - : File(name, kindObject), _ordinal(0), - _doStringsMerge(ctx.mergeCommonStrings()), _useWrap(false), _ctx(ctx) { - setLastError(std::error_code()); - } + ELFFile(StringRef name, ELFLinkingContext &ctx); + ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); - ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) - : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), - _ordinal(0), _doStringsMerge(ctx.mergeCommonStrings()), - _useWrap(ctx.wrapCalls().size()), _ctx(ctx) {} + static std::error_code isCompatible(MemoryBufferRef mb, + ELFLinkingContext &ctx); - static ErrorOr<std::unique_ptr<ELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); + static bool canParse(file_magic magic) { + return magic == file_magic::elf_relocatable; + } virtual Reference::KindArch kindArch(); @@ -132,41 +115,14 @@ public: /// \brief Create individual atoms std::error_code createAtoms(); - const atom_collection<DefinedAtom> &defined() const override { - return _definedAtoms; - } - - const atom_collection<UndefinedAtom> &undefined() const override { - return _undefinedAtoms; - } - - const atom_collection<SharedLibraryAtom> &sharedLibrary() const override { - return _sharedLibraryAtoms; - } - - const atom_collection<AbsoluteAtom> &absolute() const override { - return _absoluteAtoms; - } - - Atom *findAtom(const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) { - // All references to atoms inside a group are through undefined atoms. - Atom *targetAtom = _symbolToAtomMapping.lookup(targetSymbol); - StringRef targetSymbolName = targetAtom->name(); - if (targetAtom->definition() != Atom::definitionRegular) - return targetAtom; - if ((llvm::dyn_cast<DefinedAtom>(targetAtom))->scope() == - DefinedAtom::scopeTranslationUnit) - return targetAtom; - if (!redirectReferenceUsingUndefAtom(sourceSymbol, targetSymbol)) - return targetAtom; - auto undefForGroupchild = _undefAtomsForGroupChild.find(targetSymbolName); - if (undefForGroupchild != _undefAtomsForGroupChild.end()) - return undefForGroupchild->getValue(); - auto undefGroupChildAtom = - new (_readerStorage) SimpleUndefinedAtom(*this, targetSymbolName); - _undefinedAtoms._atoms.push_back(undefGroupChildAtom); - return (_undefAtomsForGroupChild[targetSymbolName] = undefGroupChildAtom); - } + // Assuming sourceSymbol has a reference to targetSym, find an atom + // for targetSym. Usually it's just the atom for targetSym. + // However, if an atom is in a section group, we may want to return an + // undefined atom for targetSym to let the resolver to resolve the + // symbol. (It's because if targetSym is in a section group A, and the + // group A is not linked in because other file already provides a + // section group B, we want to resolve references to B, not to A.) + Atom *findAtom(const Elf_Sym *sourceSym, const Elf_Sym *targetSym); protected: ELFDefinedAtom<ELFT> *createDefinedAtomAndAssignRelocations( @@ -179,13 +135,13 @@ protected: /// \brief Iterate over Elf_Rela relocations list and create references. virtual void createRelocationReferences(const Elf_Sym *symbol, ArrayRef<uint8_t> content, - range<Elf_Rela_Iter> rels); + range<const Elf_Rela *> rels); /// \brief Iterate over Elf_Rel relocations list and create references. virtual void createRelocationReferences(const Elf_Sym *symbol, ArrayRef<uint8_t> symContent, ArrayRef<uint8_t> secContent, - range<Elf_Rel_Iter> rels); + const Elf_Shdr *relSec); /// \brief After all the Atoms and References are created, update each /// Reference's target with the Atom pointer it refers to. @@ -224,11 +180,7 @@ protected: uint32_t edgeKind); /// Get the section name for a section. - ErrorOr<StringRef> getSectionName(const Elf_Shdr *shdr) const { - if (!shdr) - return StringRef(); - return _objFile->getSectionName(shdr); - } + ErrorOr<StringRef> getSectionName(const Elf_Shdr *shdr) const; /// Determines if the section occupy memory space. bool sectionOccupiesMemorySpace(const Elf_Shdr *shdr) const { @@ -242,45 +194,38 @@ protected: return _objFile->getSectionContents(shdr); } - /// Returns true if the symbol is a undefined symbol. - bool isUndefinedSymbol(const Elf_Sym *sym) const { - return (sym->st_shndx == llvm::ELF::SHN_UNDEF); - } - /// Determines if the target wants to create an atom for a section that has no /// symbol references. - bool handleSectionWithNoSymbols(const Elf_Shdr *shdr, - std::vector<Elf_Sym_Iter> &syms) const { - return shdr && (shdr->sh_type == llvm::ELF::SHT_PROGBITS) && syms.empty(); + bool + handleSectionWithNoSymbols(const Elf_Shdr *shdr, + std::vector<const Elf_Sym *> &syms) const { + return shdr && + (shdr->sh_type == llvm::ELF::SHT_PROGBITS || + shdr->sh_type == llvm::ELF::SHT_INIT_ARRAY || + shdr->sh_type == llvm::ELF::SHT_FINI_ARRAY || + shdr->sh_type == llvm::ELF::SHT_NOTE) && + syms.empty(); } /// Handle creation of atoms for .gnu.linkonce sections. std::error_code handleGnuLinkOnceSection( - StringRef sectionName, - llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, - const Elf_Shdr *shdr); + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection); - // Handle Section groups/COMDAT scetions. + // Handle COMDAT scetions. std::error_code handleSectionGroup( - StringRef signature, StringRef groupSectionName, - llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, - llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> &comdatSections, - const Elf_Shdr *shdr); + const Elf_Shdr *section, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection); /// Process the Undefined symbol and create an atom for it. - ErrorOr<ELFUndefinedAtom<ELFT> *> - handleUndefinedSymbol(StringRef symName, const Elf_Sym *sym) { + ELFUndefinedAtom<ELFT> *createUndefinedAtom(StringRef symName, + const Elf_Sym *sym) { return new (_readerStorage) ELFUndefinedAtom<ELFT>(*this, symName, sym); } - /// Returns true if the symbol is a absolute symbol. - bool isAbsoluteSymbol(const Elf_Sym *sym) const { - return (sym->st_shndx == llvm::ELF::SHN_ABS); - } - /// Process the Absolute symbol and create an atom for it. - ErrorOr<ELFAbsoluteAtom<ELFT> *> - handleAbsoluteSymbol(StringRef symName, const Elf_Sym *sym, int64_t value) { + ELFAbsoluteAtom<ELFT> *createAbsoluteAtom(StringRef symName, + const Elf_Sym *sym, int64_t value) { return new (_readerStorage) ELFAbsoluteAtom<ELFT>(*this, symName, sym, value); } @@ -316,42 +261,39 @@ protected: return symbol->st_value; } - /// Process the common symbol and create an atom for it. - virtual ErrorOr<ELFCommonAtom<ELFT> *> - handleCommonSymbol(StringRef symName, const Elf_Sym *sym) { - return new (_readerStorage) ELFCommonAtom<ELFT>(*this, symName, sym); + /// Returns initial addend + virtual Reference::Addend getInitialAddend(ArrayRef<uint8_t> symContent, + uint64_t symbolValue, + const Elf_Rel& reference) const { + return *(symContent.data() + reference.r_offset - symbolValue); } - /// Returns true if the symbol is a defined symbol. - virtual bool isDefinedSymbol(const Elf_Sym *sym) const { - return (sym->getType() == llvm::ELF::STT_NOTYPE || - sym->getType() == llvm::ELF::STT_OBJECT || - sym->getType() == llvm::ELF::STT_FUNC || - sym->getType() == llvm::ELF::STT_GNU_IFUNC || - sym->getType() == llvm::ELF::STT_SECTION || - sym->getType() == llvm::ELF::STT_FILE || - sym->getType() == llvm::ELF::STT_TLS); + /// Process the common symbol and create an atom for it. + virtual ELFCommonAtom<ELFT> *createCommonAtom(StringRef symName, + const Elf_Sym *sym) { + return new (_readerStorage) ELFCommonAtom<ELFT>(*this, symName, sym); } - /// Process the Defined symbol and create an atom for it. - virtual ErrorOr<ELFDefinedAtom<ELFT> *> - handleDefinedSymbol(StringRef symName, StringRef sectionName, - const Elf_Sym *sym, const Elf_Shdr *sectionHdr, - ArrayRef<uint8_t> contentData, - unsigned int referenceStart, unsigned int referenceEnd, - std::vector<ELFReference<ELFT> *> &referenceList) { + /// Creates an atom for a given defined symbol. + virtual ELFDefinedAtom<ELFT> * + createDefinedAtom(StringRef symName, StringRef sectionName, + const Elf_Sym *sym, const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, unsigned int referenceStart, + unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) { return new (_readerStorage) ELFDefinedAtom<ELFT>( *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, referenceEnd, referenceList); } /// Process the Merge string and create an atom for it. - ErrorOr<ELFMergeAtom<ELFT> *> - handleMergeString(StringRef sectionName, const Elf_Shdr *sectionHdr, - ArrayRef<uint8_t> contentData, unsigned int offset) { - ELFMergeAtom<ELFT> *mergeAtom = new (_readerStorage) + ELFMergeAtom<ELFT> *createMergedString(StringRef sectionName, + const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, + unsigned int offset) { + auto *mergeAtom = new (_readerStorage) ELFMergeAtom<ELFT>(*this, sectionName, sectionHdr, contentData, offset); - const MergeSectionKey mergedSectionKey(sectionHdr, offset); + const MergeSectionKey mergedSectionKey = {sectionHdr, offset}; if (_mergedSectionMap.find(mergedSectionKey) == _mergedSectionMap.end()) _mergedSectionMap.insert(std::make_pair(mergedSectionKey, mergeAtom)); return mergeAtom; @@ -380,19 +322,17 @@ protected: llvm::BumpPtrAllocator _readerStorage; std::unique_ptr<llvm::object::ELFFile<ELFT> > _objFile; - atom_collection_vector<DefinedAtom> _definedAtoms; - atom_collection_vector<UndefinedAtom> _undefinedAtoms; - atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; - atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + const Elf_Shdr *_symtab = nullptr; + ArrayRef<Elf_Word> _shndxTable; /// \brief _relocationAddendReferences and _relocationReferences contain the /// list of relocations references. In ELF, if a section named, ".text" has /// relocations will also have a section named ".rel.text" or ".rela.text" /// which will hold the entries. - std::unordered_map<StringRef, range<Elf_Rela_Iter>> - _relocationAddendReferences; + std::unordered_map<const Elf_Shdr *, range<const Elf_Rela *>> + _relocationAddendReferences; MergedSectionMapT _mergedSectionMap; - std::unordered_map<StringRef, range<Elf_Rel_Iter>> _relocationReferences; + std::unordered_map<const Elf_Shdr *, const Elf_Shdr *> _relocationReferences; std::vector<ELFReference<ELFT> *> _references; llvm::DenseMap<const Elf_Sym *, Atom *> _symbolToAtomMapping; llvm::DenseMap<const ELFReference<ELFT> *, const Elf_Sym *> @@ -409,7 +349,8 @@ protected: /// \brief the section and the symbols that are contained within it to create /// used to create atoms - llvm::MapVector<const Elf_Shdr *, std::vector<Elf_Sym_Iter>> _sectionSymbols; + llvm::MapVector<const Elf_Shdr *, std::vector<const Elf_Sym *>> + _sectionSymbols; /// \brief Sections that have merge string property std::vector<const Elf_Shdr *> _mergeStringSections; @@ -438,741 +379,16 @@ protected: template <class ELFT> class RuntimeFile : public ELFFile<ELFT> { public: typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - RuntimeFile(ELFLinkingContext &context, StringRef name) - : ELFFile<ELFT>(name, context) {} + RuntimeFile(ELFLinkingContext &ctx, StringRef name) + : ELFFile<ELFT>(name, ctx) {} /// \brief add a global absolute atom - virtual Atom *addAbsoluteAtom(StringRef symbolName) { - assert(!symbolName.empty() && "AbsoluteAtoms must have a name"); - Elf_Sym *symbol = new (this->_readerStorage) Elf_Sym; - symbol->st_name = 0; - symbol->st_value = 0; - symbol->st_shndx = llvm::ELF::SHN_ABS; - symbol->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); - symbol->setVisibility(llvm::ELF::STV_DEFAULT); - symbol->st_size = 0; - auto newAtom = this->handleAbsoluteSymbol(symbolName, symbol, -1); - this->_absoluteAtoms._atoms.push_back(*newAtom); - return *newAtom; - } + virtual void addAbsoluteAtom(StringRef symbolName, bool isHidden = false); /// \brief add an undefined atom - virtual Atom *addUndefinedAtom(StringRef symbolName) { - assert(!symbolName.empty() && "UndefinedAtoms must have a name"); - Elf_Sym *symbol = new (this->_readerStorage) Elf_Sym; - symbol->st_name = 0; - symbol->st_value = 0; - symbol->st_shndx = llvm::ELF::SHN_UNDEF; - symbol->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_NOTYPE); - symbol->setVisibility(llvm::ELF::STV_DEFAULT); - symbol->st_size = 0; - auto newAtom = this->handleUndefinedSymbol(symbolName, symbol); - this->_undefinedAtoms._atoms.push_back(*newAtom); - return *newAtom; - } - - // cannot add atoms to Runtime file - virtual void addAtom(const Atom &) { - llvm_unreachable("cannot add atoms to Runtime files"); - } + virtual void addUndefinedAtom(StringRef symbolName); }; -template <class ELFT> -ErrorOr<std::unique_ptr<ELFFile<ELFT>>> -ELFFile<ELFT>::create(std::unique_ptr<MemoryBuffer> mb, - ELFLinkingContext &ctx) { - std::unique_ptr<ELFFile<ELFT>> file(new ELFFile<ELFT>(std::move(mb), ctx)); - return std::move(file); -} - -template <class ELFT> -std::error_code ELFFile<ELFT>::doParse() { - std::error_code ec; - _objFile.reset(new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); - if (ec) - return ec; - - if ((ec = createAtomsFromContext())) - return ec; - - // Read input sections from the input file that need to be converted to - // atoms - if ((ec = createAtomizableSections())) - return ec; - - // For mergeable strings, we would need to split the section into various - // atoms - if ((ec = createMergeableAtoms())) - return ec; - - // Create the necessary symbols that are part of the section that we - // created in createAtomizableSections function - if ((ec = createSymbolsFromAtomizableSections())) - return ec; - - // Create the appropriate atoms from the file - if ((ec = createAtoms())) - return ec; - return std::error_code(); -} - -template <class ELFT> Reference::KindArch ELFFile<ELFT>::kindArch() { - switch (_objFile->getHeader()->e_machine) { - case llvm::ELF::EM_X86_64: - return Reference::KindArch::x86_64; - case llvm::ELF::EM_386: - return Reference::KindArch::x86; - case llvm::ELF::EM_ARM: - return Reference::KindArch::ARM; - case llvm::ELF::EM_HEXAGON: - return Reference::KindArch::Hexagon; - case llvm::ELF::EM_MIPS: - return Reference::KindArch::Mips; - case llvm::ELF::EM_AARCH64: - return Reference::KindArch::AArch64; - } - llvm_unreachable("unsupported e_machine value"); -} - -template <class ELFT> -std::error_code ELFFile<ELFT>::createAtomizableSections() { - // Handle: SHT_REL and SHT_RELA sections: - // Increment over the sections, when REL/RELA section types are found add - // the contents to the RelocationReferences map. - // Record the number of relocs to guess at preallocating the buffer. - uint64_t totalRelocs = 0; - for (const Elf_Shdr §ion : _objFile->sections()) { - if (isIgnoredSection(§ion)) - continue; - - if (isMergeableStringSection(§ion)) { - _mergeStringSections.push_back(§ion); - continue; - } - - if (section.sh_type == llvm::ELF::SHT_RELA) { - auto sHdr = _objFile->getSection(section.sh_info); - - auto sectionName = _objFile->getSectionName(sHdr); - if (std::error_code ec = sectionName.getError()) - return ec; - - auto rai(_objFile->begin_rela(§ion)); - auto rae(_objFile->end_rela(§ion)); - - _relocationAddendReferences[*sectionName] = make_range(rai, rae); - totalRelocs += std::distance(rai, rae); - } else if (section.sh_type == llvm::ELF::SHT_REL) { - auto sHdr = _objFile->getSection(section.sh_info); - - auto sectionName = _objFile->getSectionName(sHdr); - if (std::error_code ec = sectionName.getError()) - return ec; - - auto ri(_objFile->begin_rel(§ion)); - auto re(_objFile->end_rel(§ion)); - - _relocationReferences[*sectionName] = make_range(ri, re); - totalRelocs += std::distance(ri, re); - } else { - _sectionSymbols[§ion]; - } - } - _references.reserve(totalRelocs); - return std::error_code(); -} - -template <class ELFT> std::error_code ELFFile<ELFT>::createMergeableAtoms() { - // Divide the section that contains mergeable strings into tokens - // TODO - // a) add resolver support to recognize multibyte chars - // b) Create a separate section chunk to write mergeable atoms - std::vector<MergeString *> tokens; - for (const Elf_Shdr *msi : _mergeStringSections) { - auto sectionName = getSectionName(msi); - if (std::error_code ec = sectionName.getError()) - return ec; - - auto sectionContents = getSectionContents(msi); - if (std::error_code ec = sectionContents.getError()) - return ec; - - StringRef secCont(reinterpret_cast<const char *>(sectionContents->begin()), - sectionContents->size()); - - unsigned int prev = 0; - for (std::size_t i = 0, e = sectionContents->size(); i != e; ++i) { - if ((*sectionContents)[i] == '\0') { - tokens.push_back(new (_readerStorage) MergeString( - prev, secCont.slice(prev, i + 1), msi, *sectionName)); - prev = i + 1; - } - } - } - - // Create Mergeable atoms - for (const MergeString *tai : tokens) { - ArrayRef<uint8_t> content((const uint8_t *)tai->_string.data(), - tai->_string.size()); - ErrorOr<ELFMergeAtom<ELFT> *> mergeAtom = - handleMergeString(tai->_sectionName, tai->_shdr, content, tai->_offset); - (*mergeAtom)->setOrdinal(++_ordinal); - _definedAtoms._atoms.push_back(*mergeAtom); - _mergeAtoms.push_back(*mergeAtom); - } - return std::error_code(); -} - -template <class ELFT> -std::error_code ELFFile<ELFT>::createSymbolsFromAtomizableSections() { - // Increment over all the symbols collecting atoms and symbol names for - // later use. - auto SymI = _objFile->begin_symbols(), SymE = _objFile->end_symbols(); - - // Skip over dummy sym. - if (SymI != SymE) - ++SymI; - - for (; SymI != SymE; ++SymI) { - const Elf_Shdr *section = _objFile->getSection(&*SymI); - - auto symbolName = _objFile->getSymbolName(SymI); - if (std::error_code ec = symbolName.getError()) - return ec; - - if (isAbsoluteSymbol(&*SymI)) { - ErrorOr<ELFAbsoluteAtom<ELFT> *> absAtom = - handleAbsoluteSymbol(*symbolName, &*SymI, (int64_t)getSymbolValue(&*SymI)); - _absoluteAtoms._atoms.push_back(*absAtom); - _symbolToAtomMapping.insert(std::make_pair(&*SymI, *absAtom)); - } else if (isUndefinedSymbol(&*SymI)) { - if (_useWrap && - (_wrapSymbolMap.find(*symbolName) != _wrapSymbolMap.end())) { - auto wrapAtom = _wrapSymbolMap.find(*symbolName); - _symbolToAtomMapping.insert( - std::make_pair(&*SymI, wrapAtom->getValue())); - continue; - } - ErrorOr<ELFUndefinedAtom<ELFT> *> undefAtom = - handleUndefinedSymbol(*symbolName, &*SymI); - _undefinedAtoms._atoms.push_back(*undefAtom); - _symbolToAtomMapping.insert(std::make_pair(&*SymI, *undefAtom)); - } else if (isCommonSymbol(&*SymI)) { - ErrorOr<ELFCommonAtom<ELFT> *> commonAtom = - handleCommonSymbol(*symbolName, &*SymI); - (*commonAtom)->setOrdinal(++_ordinal); - _definedAtoms._atoms.push_back(*commonAtom); - _symbolToAtomMapping.insert(std::make_pair(&*SymI, *commonAtom)); - } else if (isDefinedSymbol(&*SymI)) { - _sectionSymbols[section].push_back(SymI); - } else { - llvm::errs() << "Unable to create atom for: " << *symbolName << "\n"; - return llvm::object::object_error::parse_failed; - } - } - - return std::error_code(); -} - -template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() { - // Holds all the atoms that are part of the section. They are the targets of - // the kindGroupChild reference. - llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> atomsForSection; - // group sections have a mapping of the section header to the - // signature/section. - llvm::DenseMap<const Elf_Shdr *, std::pair<StringRef, StringRef>> - groupSections; - // Contains a list of comdat sections for a group. - llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> comdatSections; - for (auto &i : _sectionSymbols) { - const Elf_Shdr *section = i.first; - std::vector<Elf_Sym_Iter> &symbols = i.second; - - // Sort symbols by position. - std::stable_sort(symbols.begin(), symbols.end(), - [this](Elf_Sym_Iter a, Elf_Sym_Iter b) { - return getSymbolValue(&*a) < getSymbolValue(&*b); - }); - - ErrorOr<StringRef> sectionName = this->getSectionName(section); - if (std::error_code ec = sectionName.getError()) - return ec; - - auto sectionContents = getSectionContents(section); - if (std::error_code ec = sectionContents.getError()) - return ec; - - bool addAtoms = true; - - // A section of type SHT_GROUP defines a grouping of sections. The name of a - // symbol from one of the containing object's symbol tables provides a - // signature - // for the section group. The section header of the SHT_GROUP section - // specifies - // the identifying symbol entry, as described : the sh_link member contains - // the section header index of the symbol table section that contains the - // entry. - // The sh_info member contains the symbol table index of the identifying - // entry. - // The sh_flags member of the section header contains 0. The name of the - // section - // (sh_name) is not specified. - if (isGroupSection(section)) { - const Elf_Word *groupMembers = - reinterpret_cast<const Elf_Word *>(sectionContents->data()); - const long count = (section->sh_size) / sizeof(Elf_Word); - for (int i = 1; i < count; i++) { - const Elf_Shdr *sHdr = _objFile->getSection(groupMembers[i]); - ErrorOr<StringRef> sectionName = _objFile->getSectionName(sHdr); - if (std::error_code ec = sectionName.getError()) - return ec; - comdatSections[section].push_back(*sectionName); - } - const Elf_Sym *symbol = _objFile->getSymbol(section->sh_info); - const Elf_Shdr *symtab = _objFile->getSection(section->sh_link); - ErrorOr<StringRef> symbolName = _objFile->getSymbolName(symtab, symbol); - if (std::error_code ec = symbolName.getError()) - return ec; - groupSections.insert( - std::make_pair(section, std::make_pair(*symbolName, *sectionName))); - continue; - } - - if (isGnuLinkOnceSection(*sectionName)) { - groupSections.insert( - std::make_pair(section, std::make_pair(*sectionName, *sectionName))); - addAtoms = false; - } - - if (isSectionMemberOfGroup(section)) - addAtoms = false; - - if (handleSectionWithNoSymbols(section, symbols)) { - ELFDefinedAtom<ELFT> *newAtom = - createSectionAtom(section, *sectionName, *sectionContents); - newAtom->setOrdinal(++_ordinal); - if (addAtoms) - _definedAtoms._atoms.push_back(newAtom); - else - atomsForSection[*sectionName].push_back(newAtom); - continue; - } - - ELFDefinedAtom<ELFT> *previousAtom = nullptr; - ELFReference<ELFT> *anonFollowedBy = nullptr; - - for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { - auto symbol = *si; - StringRef symbolName = ""; - if (symbol->getType() != llvm::ELF::STT_SECTION) { - auto symName = _objFile->getSymbolName(symbol); - if (std::error_code ec = symName.getError()) - return ec; - symbolName = *symName; - } - - uint64_t contentSize = symbolContentSize( - section, &*symbol, (si + 1 == se) ? nullptr : &**(si + 1)); - - // Check to see if we need to add the FollowOn Reference - ELFReference<ELFT> *followOn = nullptr; - if (previousAtom) { - // Replace the followon atom with the anonymous atom that we created, - // so that the next symbol that we create is a followon from the - // anonymous atom. - if (anonFollowedBy) { - followOn = anonFollowedBy; - } else { - followOn = new (_readerStorage) - ELFReference<ELFT>(lld::Reference::kindLayoutAfter); - previousAtom->addReference(followOn); - } - } - - ArrayRef<uint8_t> symbolData((const uint8_t *)sectionContents->data() + - getSymbolValue(&*symbol), - contentSize); - - // If the linker finds that a section has global atoms that are in a - // mergeable section, treat them as defined atoms as they shouldn't be - // merged away as well as these symbols have to be part of symbol - // resolution - if (isMergeableStringSection(section)) { - if (symbol->getBinding() == llvm::ELF::STB_GLOBAL) { - auto definedMergeAtom = handleDefinedSymbol( - symbolName, *sectionName, &**si, section, symbolData, - _references.size(), _references.size(), _references); - (*definedMergeAtom)->setOrdinal(++_ordinal); - if (addAtoms) - _definedAtoms._atoms.push_back(*definedMergeAtom); - else - atomsForSection[*sectionName].push_back(*definedMergeAtom); - } - continue; - } - - // Don't allocate content to a weak symbol, as they may be merged away. - // Create an anonymous atom to hold the data. - ELFDefinedAtom<ELFT> *anonAtom = nullptr; - anonFollowedBy = nullptr; - if (symbol->getBinding() == llvm::ELF::STB_WEAK) { - // Create anonymous new non-weak ELF symbol that holds the symbol - // data. - auto sym = new (_readerStorage) Elf_Sym(*symbol); - sym->setBinding(llvm::ELF::STB_GLOBAL); - anonAtom = createDefinedAtomAndAssignRelocations( - "", *sectionName, sym, section, symbolData, *sectionContents); - symbolData = ArrayRef<uint8_t>(); - - // If this is the last atom, let's not create a followon reference. - if (anonAtom && (si + 1) != se) { - anonFollowedBy = new (_readerStorage) - ELFReference<ELFT>(lld::Reference::kindLayoutAfter); - anonAtom->addReference(anonFollowedBy); - } - } - - ELFDefinedAtom<ELFT> *newAtom = createDefinedAtomAndAssignRelocations( - symbolName, *sectionName, &*symbol, section, symbolData, - *sectionContents); - newAtom->setOrdinal(++_ordinal); - - // If the atom was a weak symbol, let's create a followon reference to - // the anonymous atom that we created. - if (anonAtom) - createEdge(newAtom, anonAtom, Reference::kindLayoutAfter); - - if (previousAtom) { - // Set the followon atom to the weak atom that we have created, so - // that they would alias when the file gets written. - followOn->setTarget(anonAtom ? anonAtom : newAtom); - } - - // The previous atom is always the atom created before unless the atom - // is a weak atom. - previousAtom = anonAtom ? anonAtom : newAtom; - - if (addAtoms) - _definedAtoms._atoms.push_back(newAtom); - else - atomsForSection[*sectionName].push_back(newAtom); - - _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom)); - if (anonAtom) { - anonAtom->setOrdinal(++_ordinal); - if (addAtoms) - _definedAtoms._atoms.push_back(anonAtom); - else - atomsForSection[*sectionName].push_back(anonAtom); - } - } - } - - // Iterate over all the group sections to create parent atoms pointing to - // group-child atoms. - for (auto § : groupSections) { - StringRef signature = sect.second.first; - StringRef groupSectionName = sect.second.second; - if (isGnuLinkOnceSection(signature)) - handleGnuLinkOnceSection(signature, atomsForSection, sect.first); - else if (isGroupSection(sect.first)) - handleSectionGroup(signature, groupSectionName, atomsForSection, - comdatSections, sect.first); - } - - updateReferences(); - return std::error_code(); -} - -template <class ELFT> -std::error_code ELFFile<ELFT>::handleGnuLinkOnceSection( - StringRef signature, - llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, - const Elf_Shdr *shdr) { - // TODO: Check for errors. - unsigned int referenceStart = _references.size(); - std::vector<ELFReference<ELFT> *> refs; - for (auto ha : atomsForSection[signature]) { - _groupChild[ha->symbol()] = std::make_pair(signature, shdr); - ELFReference<ELFT> *ref = - new (_readerStorage) ELFReference<ELFT>(lld::Reference::kindGroupChild); - ref->setTarget(ha); - refs.push_back(ref); - } - atomsForSection[signature].clear(); - // Create a gnu linkonce atom. - auto gnuLinkOnceAtom = handleDefinedSymbol( - signature, signature, nullptr, shdr, ArrayRef<uint8_t>(), referenceStart, - _references.size(), _references); - (*gnuLinkOnceAtom)->setOrdinal(++_ordinal); - _definedAtoms._atoms.push_back(*gnuLinkOnceAtom); - for (auto reference : refs) - (*gnuLinkOnceAtom)->addReference(reference); - return std::error_code(); -} - -template <class ELFT> -std::error_code ELFFile<ELFT>::handleSectionGroup( - StringRef signature, StringRef groupSectionName, - llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, - llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> &comdatSections, - const Elf_Shdr *shdr) { - // TODO: Check for errors. - unsigned int referenceStart = _references.size(); - std::vector<ELFReference<ELFT> *> refs; - auto sectionNamesInGroup = comdatSections[shdr]; - for (auto sectionName : sectionNamesInGroup) { - for (auto ha : atomsForSection[sectionName]) { - _groupChild[ha->symbol()] = std::make_pair(signature, shdr); - ELFReference<ELFT> *ref = new (_readerStorage) - ELFReference<ELFT>(lld::Reference::kindGroupChild); - ref->setTarget(ha); - refs.push_back(ref); - } - atomsForSection[sectionName].clear(); - } - // Create a gnu linkonce atom. - auto sectionGroupAtom = handleDefinedSymbol( - signature, groupSectionName, nullptr, shdr, ArrayRef<uint8_t>(), - referenceStart, _references.size(), _references); - (*sectionGroupAtom)->setOrdinal(++_ordinal); - _definedAtoms._atoms.push_back(*sectionGroupAtom); - for (auto reference : refs) - (*sectionGroupAtom)->addReference(reference); - return std::error_code(); -} - -template <class ELFT> std::error_code ELFFile<ELFT>::createAtomsFromContext() { - if (!_useWrap) - return std::error_code(); - // Steps :- - // a) Create an undefined atom for the symbol specified by the --wrap option, - // as that - // may be needed to be pulled from an archive. - // b) Create an undefined atom for __wrap_<symbolname>. - // c) All references to the symbol specified by wrap should point to - // __wrap_<symbolname> - // d) All references to __real_symbol should point to the <symbol> - for (auto &wrapsym : _ctx.wrapCalls()) { - StringRef wrapStr = wrapsym.getKey(); - // Create a undefined symbol fror the wrap symbol. - UndefinedAtom *wrapSymAtom = - new (_readerStorage) SimpleUndefinedAtom(*this, wrapStr); - StringRef wrapCallSym = - _ctx.allocateString((llvm::Twine("__wrap_") + wrapStr).str()); - StringRef realCallSym = - _ctx.allocateString((llvm::Twine("__real_") + wrapStr).str()); - UndefinedAtom *wrapCallAtom = - new (_readerStorage) SimpleUndefinedAtom(*this, wrapCallSym); - // Create maps, when there is call to sym, it should point to wrapCallSym. - _wrapSymbolMap.insert(std::make_pair(wrapStr, wrapCallAtom)); - // Whenever there is a reference to realCall it should point to the symbol - // created for each wrap usage. - _wrapSymbolMap.insert(std::make_pair(realCallSym, wrapSymAtom)); - _undefinedAtoms._atoms.push_back(wrapSymAtom); - _undefinedAtoms._atoms.push_back(wrapCallAtom); - } - return std::error_code(); -} - -template <class ELFT> -ELFDefinedAtom<ELFT> *ELFFile<ELFT>::createDefinedAtomAndAssignRelocations( - StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, - const Elf_Shdr *section, ArrayRef<uint8_t> symContent, - ArrayRef<uint8_t> secContent) { - unsigned int referenceStart = _references.size(); - - // Add Rela (those with r_addend) references: - auto rari = _relocationAddendReferences.find(sectionName); - if (rari != _relocationAddendReferences.end()) - createRelocationReferences(symbol, symContent, rari->second); - - // Add Rel references. - auto rri = _relocationReferences.find(sectionName); - if (rri != _relocationReferences.end()) - createRelocationReferences(symbol, symContent, secContent, rri->second); - - // Create the DefinedAtom and add it to the list of DefinedAtoms. - return *handleDefinedSymbol(symbolName, sectionName, symbol, section, - symContent, referenceStart, _references.size(), - _references); -} - -template <class ELFT> -void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, - ArrayRef<uint8_t> content, - range<Elf_Rela_Iter> rels) { - bool isMips64EL = _objFile->isMips64EL(); - const auto symValue = getSymbolValue(symbol); - for (const auto &rel : rels) { - if (rel.r_offset < symValue || - symValue + content.size() <= rel.r_offset) - continue; - auto elfRelocation = new (_readerStorage) - ELFReference<ELFT>(&rel, rel.r_offset - symValue, kindArch(), - rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); - addReferenceToSymbol(elfRelocation, symbol); - _references.push_back(elfRelocation); - } -} - -template <class ELFT> -void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, - ArrayRef<uint8_t> symContent, - ArrayRef<uint8_t> secContent, - range<Elf_Rel_Iter> rels) { - bool isMips64EL = _objFile->isMips64EL(); - const auto symValue = getSymbolValue(symbol); - for (const auto &rel : rels) { - if (rel.r_offset < symValue || - symValue + symContent.size() <= rel.r_offset) - continue; - auto elfRelocation = new (_readerStorage) - ELFReference<ELFT>(rel.r_offset - symValue, kindArch(), - rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); - int32_t addend = *(symContent.data() + rel.r_offset - symValue); - elfRelocation->setAddend(addend); - addReferenceToSymbol(elfRelocation, symbol); - _references.push_back(elfRelocation); - } -} - -template <class ELFT> -void ELFFile<ELFT>::updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref, - const Elf_Sym *symbol, - const Elf_Shdr *shdr) { - // If the target atom is mergeable strefng atom, the atom might have been - // merged with other atom having the same contents. Try to find the - // merged one if that's the case. - int64_t addend = ref->addend(); - if (addend < 0) - addend = 0; - - const MergeSectionKey ms(shdr, addend); - auto msec = _mergedSectionMap.find(ms); - if (msec != _mergedSectionMap.end()) { - ref->setTarget(msec->second); - return; - } - - // The target atom was not merged. Mergeable atoms are not in - // _symbolToAtomMapping, so we cannot find it by calling findAtom(). We - // instead call findMergeAtom(). - if (symbol->getType() != llvm::ELF::STT_SECTION) - addend = getSymbolValue(symbol) + addend; - ELFMergeAtom<ELFT> *mergedAtom = findMergeAtom(shdr, addend); - ref->setOffset(addend - mergedAtom->offset()); - ref->setAddend(0); - ref->setTarget(mergedAtom); -} - -template <class ELFT> void ELFFile<ELFT>::updateReferences() { - for (auto &ri : _references) { - if (ri->kindNamespace() != lld::Reference::KindNamespace::ELF) - continue; - const Elf_Sym *symbol = _objFile->getSymbol(ri->targetSymbolIndex()); - const Elf_Shdr *shdr = _objFile->getSection(symbol); - - // If the atom is not in mergeable string section, the target atom is - // simply that atom. - if (isMergeableStringSection(shdr)) - updateReferenceForMergeStringAccess(ri, symbol, shdr); - else - ri->setTarget(findAtom(findSymbolForReference(ri), symbol)); - } -} - -template <class ELFT> -bool ELFFile<ELFT>::isIgnoredSection(const Elf_Shdr *section) { - switch (section->sh_type) { - case llvm::ELF::SHT_NULL: - case llvm::ELF::SHT_STRTAB: - case llvm::ELF::SHT_SYMTAB: - case llvm::ELF::SHT_SYMTAB_SHNDX: - return true; - default: - break; - } - return false; -} - -template <class ELFT> -bool ELFFile<ELFT>::isMergeableStringSection(const Elf_Shdr *section) { - if (_doStringsMerge && section) { - int64_t sectionFlags = section->sh_flags; - sectionFlags &= ~llvm::ELF::SHF_ALLOC; - // Mergeable string sections have both SHF_MERGE and SHF_STRINGS flags - // set. sh_entsize is the size of each character which is normally 1. - if ((section->sh_entsize < 2) && - (sectionFlags == (llvm::ELF::SHF_MERGE | llvm::ELF::SHF_STRINGS))) { - return true; - } - } - return false; -} - -template <class ELFT> -ELFDefinedAtom<ELFT> * -ELFFile<ELFT>::createSectionAtom(const Elf_Shdr *section, StringRef sectionName, - ArrayRef<uint8_t> content) { - Elf_Sym *sym = new (_readerStorage) Elf_Sym; - sym->st_name = 0; - sym->setBindingAndType(llvm::ELF::STB_LOCAL, llvm::ELF::STT_SECTION); - sym->st_other = 0; - sym->st_shndx = 0; - sym->st_value = 0; - sym->st_size = 0; - auto *newAtom = createDefinedAtomAndAssignRelocations( - "", sectionName, sym, section, content, content); - newAtom->setOrdinal(++_ordinal); - return newAtom; -} - -template <class ELFT> -uint64_t ELFFile<ELFT>::symbolContentSize(const Elf_Shdr *section, - const Elf_Sym *symbol, - const Elf_Sym *nextSymbol) { - const auto symValue = getSymbolValue(symbol); - // if this is the last symbol, take up the remaining data. - return nextSymbol ? getSymbolValue(nextSymbol) - symValue - : section->sh_size - symValue; -} - -template <class ELFT> -void ELFFile<ELFT>::createEdge(ELFDefinedAtom<ELFT> *from, - ELFDefinedAtom<ELFT> *to, uint32_t edgeKind) { - auto reference = new (_readerStorage) ELFReference<ELFT>(edgeKind); - reference->setTarget(to); - from->addReference(reference); -} - -/// Does the atom need to be redirected using a separate undefined atom? -template <class ELFT> -bool ELFFile<ELFT>::redirectReferenceUsingUndefAtom( - const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) const { - auto groupChildTarget = _groupChild.find(targetSymbol); - - // If the reference is not to a group child atom, there is no need to redirect - // using a undefined atom. Its also not needed if the source and target are - // from the same section. - if ((groupChildTarget == _groupChild.end()) || - (sourceSymbol->st_shndx == targetSymbol->st_shndx)) - return false; - - auto groupChildSource = _groupChild.find(sourceSymbol); - - // If the source symbol is not in a group, use a undefined symbol too. - if (groupChildSource == _groupChild.end()) - return true; - - // If the source and child are from the same group, we dont need the - // relocation to go through a undefined symbol. - if (groupChildSource->second.second == groupChildTarget->second.second) - return false; - - return true; -} - } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFLinkingContext.cpp b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp index c7dffda8a463..2904c7b0dae0 100644 --- a/lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -25,6 +25,9 @@ #include <cxxabi.h> #endif +using llvm::sys::fs::exists; +using llvm::sys::path::is_absolute; + namespace lld { class CommandLineUndefinedAtom : public SimpleUndefinedAtom { @@ -37,18 +40,6 @@ public: } }; -ELFLinkingContext::ELFLinkingContext( - llvm::Triple triple, std::unique_ptr<TargetHandlerBase> targetHandler) - : _outputELFType(llvm::ELF::ET_EXEC), _triple(triple), - _targetHandler(std::move(targetHandler)), _baseAddress(0), - _isStaticExecutable(false), _noInhibitExec(false), _exportDynamic(false), - _mergeCommonStrings(false), _useShlibUndefines(true), - _dynamicLinkerArg(false), _noAllowDynamicLibraries(false), - _mergeRODataToTextSegment(true), _demangle(true), - _stripSymbols(false), _alignSegments(true), _collectStats(false), - _outputMagic(OutputMagic::DEFAULT), _initFunction("_init"), - _finiFunction("_fini"), _sysrootPath(""), _linkerScriptSema() {} - void ELFLinkingContext::addPasses(PassManager &pm) { pm.add(llvm::make_unique<elf::OrderPass>()); } @@ -61,13 +52,17 @@ uint16_t ELFLinkingContext::getOutputMachine() const { return llvm::ELF::EM_X86_64; case llvm::Triple::hexagon: return llvm::ELF::EM_HEXAGON; + case llvm::Triple::mips: case llvm::Triple::mipsel: + case llvm::Triple::mips64: case llvm::Triple::mips64el: return llvm::ELF::EM_MIPS; case llvm::Triple::aarch64: return llvm::ELF::EM_AARCH64; case llvm::Triple::arm: return llvm::ELF::EM_ARM; + case llvm::Triple::amdgcn: + return llvm::ELF::EM_AMDGPU; default: llvm_unreachable("Unhandled arch"); } @@ -84,11 +79,8 @@ bool ELFLinkingContext::validateImpl(raw_ostream &diagnostics) { case LinkingContext::OutputFileType::YAML: _writer = createWriterYAML(*this); break; - case LinkingContext::OutputFileType::Native: - llvm_unreachable("Unimplemented"); - break; default: - _writer = createWriterELF(this->targetHandler()); + _writer = createWriterELF(*this); break; } @@ -116,11 +108,13 @@ Writer &ELFLinkingContext::writer() const { return *_writer; } static void buildSearchPath(SmallString<128> &path, StringRef dir, StringRef sysRoot) { - if (!dir.startswith("=/")) - path.assign(dir); - else { + if (dir.startswith("=/")) { + // If a search directory begins with "=", "=" is replaced + // with the sysroot path. path.assign(sysRoot); path.append(dir.substr(1)); + } else { + path.assign(dir); } } @@ -134,18 +128,18 @@ ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const { llvm::sys::path::append(path, hasColonPrefix ? libName.drop_front() : Twine("lib", libName) + ".so"); - if (llvm::sys::fs::exists(path.str())) - return StringRef(*new (_allocator) std::string(path.str())); + if (exists(path.str())) + return path.str().copy(_allocator); } // Search for static libraries too buildSearchPath(path, dir, _sysrootPath); llvm::sys::path::append(path, hasColonPrefix ? libName.drop_front() : Twine("lib", libName) + ".a"); - if (llvm::sys::fs::exists(path.str())) - return StringRef(*new (_allocator) std::string(path.str())); + if (exists(path.str())) + return path.str().copy(_allocator); } - if (hasColonPrefix && llvm::sys::fs::exists(libName.drop_front())) + if (hasColonPrefix && exists(libName.drop_front())) return libName.drop_front(); return make_error_code(llvm::errc::no_such_file_or_directory); @@ -154,22 +148,23 @@ ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const { ErrorOr<StringRef> ELFLinkingContext::searchFile(StringRef fileName, bool isSysRooted) const { SmallString<128> path; - if (llvm::sys::path::is_absolute(fileName) && isSysRooted) { + if (is_absolute(fileName) && isSysRooted) { path.assign(_sysrootPath); path.append(fileName); - if (llvm::sys::fs::exists(path.str())) - return StringRef(*new (_allocator) std::string(path.str())); - } else if (llvm::sys::fs::exists(fileName)) + if (exists(path.str())) + return path.str().copy(_allocator); + } else if (exists(fileName)) { return fileName; + } - if (llvm::sys::path::is_absolute(fileName)) + if (is_absolute(fileName)) return make_error_code(llvm::errc::no_such_file_or_directory); for (StringRef dir : _inputSearchPaths) { buildSearchPath(path, dir, _sysrootPath); llvm::sys::path::append(path, fileName); - if (llvm::sys::fs::exists(path.str())) - return StringRef(*new (_allocator) std::string(path.str())); + if (exists(path.str())) + return path.str().copy(_allocator); } return make_error_code(llvm::errc::no_such_file_or_directory); } @@ -227,6 +222,7 @@ void ELFLinkingContext::notifySymbolTableCoalesce(const Atom *existingAtom, } std::string ELFLinkingContext::demangle(StringRef symbolName) const { +#if defined(HAVE_CXXABI_H) if (!demangleSymbols()) return symbolName; @@ -234,21 +230,20 @@ std::string ELFLinkingContext::demangle(StringRef symbolName) const { if (!symbolName.startswith("_Z")) return symbolName; -#if defined(HAVE_CXXABI_H) SmallString<256> symBuff; StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff); const char *cstr = nullTermSym.data(); int status; char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status); - if (demangled != NULL) { - std::string result(demangled); - // __cxa_demangle() always uses a malloc'ed buffer to return the result. - free(demangled); - return result; - } -#endif - + if (!demangled) + return symbolName; + std::string result(demangled); + // __cxa_demangle() always uses a malloc'ed buffer to return the result. + free(demangled); + return result; +#else return symbolName; +#endif } void ELFLinkingContext::setUndefinesResolver(std::unique_ptr<File> resolver) { @@ -256,4 +251,15 @@ void ELFLinkingContext::setUndefinesResolver(std::unique_ptr<File> resolver) { _resolver = std::move(resolver); } +void ELFLinkingContext::notifyInputSectionName(StringRef name) { + // Save sections names which can be represented as a C identifier. + if (name.find_first_not_of("0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "_") == StringRef::npos) { + std::lock_guard<std::mutex> lock(_cidentMutex); + _cidentSections.insert(name); + } +} + } // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFReader.h b/lib/ReaderWriter/ELF/ELFReader.h index 43f218115c66..60af6dff9980 100644 --- a/lib/ReaderWriter/ELF/ELFReader.h +++ b/lib/ReaderWriter/ELF/ELFReader.h @@ -10,90 +10,35 @@ #ifndef LLD_READER_WRITER_ELF_READER_H #define LLD_READER_WRITER_ELF_READER_H -#include "CreateELF.h" #include "DynamicFile.h" #include "ELFFile.h" +#include "lld/Core/File.h" #include "lld/Core/Reader.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ELF.h" namespace lld { namespace elf { -template <typename ELFT, typename ELFTraitsT, typename ContextT> -class ELFObjectReader : public Reader { +template <typename FileT> class ELFReader : public Reader { public: - typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + ELFReader(ELFLinkingContext &ctx) : _ctx(ctx) {} - ELFObjectReader(ContextT &ctx, uint64_t machine) - : _ctx(ctx), _machine(machine) {} - - bool canParse(file_magic magic, StringRef, - const MemoryBuffer &buf) const override { - return (magic == llvm::sys::fs::file_magic::elf_relocatable && - elfHeader(buf)->e_machine == _machine); + bool canParse(file_magic magic, MemoryBufferRef mb) const override { + return FileT::canParse(magic); } - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, - std::vector<std::unique_ptr<File>> &result) const override { - std::size_t maxAlignment = - 1ULL << llvm::countTrailingZeros(uintptr_t(mb->getBufferStart())); - auto f = - createELF<ELFTraitsT>(llvm::object::getElfArchType(mb->getBuffer()), - maxAlignment, std::move(mb), _ctx); - if (std::error_code ec = f.getError()) + ErrorOr<std::unique_ptr<File>> + loadFile(std::unique_ptr<MemoryBuffer> mb, + const class Registry &) const override { + if (std::error_code ec = FileT::isCompatible(mb->getMemBufferRef(), _ctx)) return ec; - result.push_back(std::move(*f)); - return std::error_code(); - } - - const Elf_Ehdr *elfHeader(const MemoryBuffer &buf) const { - const uint8_t *data = - reinterpret_cast<const uint8_t *>(buf.getBuffer().data()); - return (reinterpret_cast<const Elf_Ehdr *>(data)); - } - -protected: - ContextT &_ctx; - uint64_t _machine; -}; - -template <typename ELFT, typename ELFTraitsT, typename ContextT> -class ELFDSOReader : public Reader { -public: - typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; - - ELFDSOReader(ContextT &ctx, uint64_t machine) - : _ctx(ctx), _machine(machine) {} - - bool canParse(file_magic magic, StringRef, - const MemoryBuffer &buf) const override { - return (magic == llvm::sys::fs::file_magic::elf_shared_object && - elfHeader(buf)->e_machine == _machine); - } - - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, - std::vector<std::unique_ptr<File>> &result) const override { - std::size_t maxAlignment = - 1ULL << llvm::countTrailingZeros(uintptr_t(mb->getBufferStart())); - auto f = - createELF<ELFTraitsT>(llvm::object::getElfArchType(mb->getBuffer()), - maxAlignment, std::move(mb), _ctx); - if (std::error_code ec = f.getError()) - return ec; - result.push_back(std::move(*f)); - return std::error_code(); - } - - const Elf_Ehdr *elfHeader(const MemoryBuffer &buf) const { - const uint8_t *data = - reinterpret_cast<const uint8_t *>(buf.getBuffer().data()); - return (reinterpret_cast<const Elf_Ehdr *>(data)); + std::unique_ptr<File> ret = llvm::make_unique<FileT>(std::move(mb), _ctx); + return std::move(ret); } -protected: - ContextT &_ctx; - uint64_t _machine; +private: + ELFLinkingContext &_ctx; }; } // namespace elf diff --git a/lib/ReaderWriter/ELF/ExecutableWriter.h b/lib/ReaderWriter/ELF/ExecutableWriter.h index 477e3920abae..9d9f4d9ce0a5 100644 --- a/lib/ReaderWriter/ELF/ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/ExecutableWriter.h @@ -16,32 +16,29 @@ namespace elf { using namespace llvm; using namespace llvm::object; -template<class ELFT> -class ExecutableWriter; - //===----------------------------------------------------------------------===// // ExecutableWriter Class //===----------------------------------------------------------------------===// template<class ELFT> class ExecutableWriter : public OutputELFWriter<ELFT> { public: - ExecutableWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout) - : OutputELFWriter<ELFT>(context, layout), - _runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {} + ExecutableWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(ctx, layout) {} protected: - virtual void buildDynamicSymbolTable(const File &file); - virtual void addDefaultAtoms(); - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &); - virtual void finalizeDefaultAtomValues(); - virtual void createDefaultSections(); + void buildDynamicSymbolTable(const File &file) override; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + void finalizeDefaultAtomValues() override; + void createDefaultSections() override; - virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const { + bool isNeededTagRequired(const SharedLibraryAtom *sla) const override { return this->_layout.isCopied(sla); } unique_bump_ptr<InterpSection<ELFT>> _interpSection; - std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; + +private: + std::unique_ptr<RuntimeFile<ELFT>> createRuntimeFile(); }; //===----------------------------------------------------------------------===// @@ -56,8 +53,8 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { if (!da) continue; if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && - !this->_context.isDynamicallyExportedSymbol(da->name()) && - !(this->_context.shouldExportDynamic() && + !this->_ctx.isDynamicallyExportedSymbol(da->name()) && + !(this->_ctx.shouldExportDynamic() && da->scope() == Atom::Scope::scopeGlobal)) continue; this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), @@ -65,7 +62,7 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { } // Put weak symbols in the dynamic symbol table. - if (this->_context.isDynamic()) { + if (this->_ctx.isDynamic()) { for (const UndefinedAtom *a : file.undefined()) { if (this->_layout.isReferencedByDefinedAtom(a) && a->canBeNull() != UndefinedAtom::canBeNullNever) @@ -76,48 +73,44 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); } -/// \brief Add absolute symbols by default. These are linker added -/// absolute symbols template<class ELFT> -void ExecutableWriter<ELFT>::addDefaultAtoms() { - OutputELFWriter<ELFT>::addDefaultAtoms(); - _runtimeFile->addUndefinedAtom(this->_context.entrySymbolName()); - _runtimeFile->addAbsoluteAtom("__bss_start"); - _runtimeFile->addAbsoluteAtom("__bss_end"); - _runtimeFile->addAbsoluteAtom("_end"); - _runtimeFile->addAbsoluteAtom("end"); - _runtimeFile->addAbsoluteAtom("__preinit_array_start"); - _runtimeFile->addAbsoluteAtom("__preinit_array_end"); - _runtimeFile->addAbsoluteAtom("__init_array_start"); - _runtimeFile->addAbsoluteAtom("__init_array_end"); - if (this->_context.isRelaOutputFormat()) { - _runtimeFile->addAbsoluteAtom("__rela_iplt_start"); - _runtimeFile->addAbsoluteAtom("__rela_iplt_end"); +std::unique_ptr<RuntimeFile<ELFT>> ExecutableWriter<ELFT>::createRuntimeFile() { + auto file = llvm::make_unique<RuntimeFile<ELFT>>(this->_ctx, "C runtime"); + file->addUndefinedAtom(this->_ctx.entrySymbolName()); + file->addAbsoluteAtom("__bss_start"); + file->addAbsoluteAtom("__bss_end"); + file->addAbsoluteAtom("_end"); + file->addAbsoluteAtom("end"); + file->addAbsoluteAtom("__preinit_array_start", true); + file->addAbsoluteAtom("__preinit_array_end", true); + file->addAbsoluteAtom("__init_array_start", true); + file->addAbsoluteAtom("__init_array_end", true); + if (this->_ctx.isRelaOutputFormat()) { + file->addAbsoluteAtom("__rela_iplt_start"); + file->addAbsoluteAtom("__rela_iplt_end"); } else { - _runtimeFile->addAbsoluteAtom("__rel_iplt_start"); - _runtimeFile->addAbsoluteAtom("__rel_iplt_end"); + file->addAbsoluteAtom("__rel_iplt_start"); + file->addAbsoluteAtom("__rel_iplt_end"); } - _runtimeFile->addAbsoluteAtom("__fini_array_start"); - _runtimeFile->addAbsoluteAtom("__fini_array_end"); + file->addAbsoluteAtom("__fini_array_start", true); + file->addAbsoluteAtom("__fini_array_end", true); + return file; } /// \brief Hook in lld to add CRuntime file template <class ELFT> -bool ExecutableWriter<ELFT>::createImplicitFiles( +void ExecutableWriter<ELFT>::createImplicitFiles( std::vector<std::unique_ptr<File> > &result) { - // Add the default atoms as defined by executables - ExecutableWriter<ELFT>::addDefaultAtoms(); OutputELFWriter<ELFT>::createImplicitFiles(result); - result.push_back(std::move(_runtimeFile)); - return true; + result.push_back(createRuntimeFile()); } template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() { OutputELFWriter<ELFT>::createDefaultSections(); - if (this->_context.isDynamic()) { + if (this->_ctx.isDynamic()) { _interpSection.reset(new (this->_alloc) InterpSection<ELFT>( - this->_context, ".interp", DefaultLayout<ELFT>::ORDER_INTERP, - this->_context.getInterpreter())); + this->_ctx, ".interp", TargetLayout<ELFT>::ORDER_INTERP, + this->_ctx.getInterpreter())); this->_layout.addSection(_interpSection.get()); } } @@ -126,53 +119,35 @@ template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() { /// created template <class ELFT> void ExecutableWriter<ELFT>::finalizeDefaultAtomValues() { OutputELFWriter<ELFT>::finalizeDefaultAtomValues(); - auto bssStartAtomIter = this->_layout.findAbsoluteAtom("__bss_start"); - auto bssEndAtomIter = this->_layout.findAbsoluteAtom("__bss_end"); - auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); - auto endAtomIter = this->_layout.findAbsoluteAtom("end"); - - auto startEnd = [&](StringRef sym, StringRef sec) -> void { - std::string start = ("__" + sym + "_start").str(); - std::string end = ("__" + sym + "_end").str(); - auto s = this->_layout.findAbsoluteAtom(start); - auto e = this->_layout.findAbsoluteAtom(end); - auto section = this->_layout.findOutputSection(sec); - if (section) { - (*s)->_virtualAddr = section->virtualAddr(); - (*e)->_virtualAddr = section->virtualAddr() + section->memSize(); - } else { - (*s)->_virtualAddr = 0; - (*e)->_virtualAddr = 0; - } - }; + AtomLayout *bssStartAtom = this->_layout.findAbsoluteAtom("__bss_start"); + AtomLayout *bssEndAtom = this->_layout.findAbsoluteAtom("__bss_end"); + AtomLayout *underScoreEndAtom = this->_layout.findAbsoluteAtom("_end"); + AtomLayout *endAtom = this->_layout.findAbsoluteAtom("end"); - startEnd("preinit_array", ".preinit_array"); - startEnd("init_array", ".init_array"); - if (this->_context.isRelaOutputFormat()) - startEnd("rela_iplt", ".rela.plt"); - else - startEnd("rel_iplt", ".rel.plt"); - startEnd("fini_array", ".fini_array"); - - assert(!(bssStartAtomIter == this->_layout.absoluteAtoms().end() || - bssEndAtomIter == this->_layout.absoluteAtoms().end() || - underScoreEndAtomIter == this->_layout.absoluteAtoms().end() || - endAtomIter == this->_layout.absoluteAtoms().end()) && + assert((bssStartAtom || bssEndAtom || underScoreEndAtom || endAtom) && "Unable to find the absolute atoms that have been added by lld"); + this->updateScopeAtomValues("preinit_array", ".preinit_array"); + this->updateScopeAtomValues("init_array", ".init_array"); + if (this->_ctx.isRelaOutputFormat()) + this->updateScopeAtomValues("rela_iplt", ".rela.plt"); + else + this->updateScopeAtomValues("rel_iplt", ".rel.plt"); + this->updateScopeAtomValues("fini_array", ".fini_array"); + auto bssSection = this->_layout.findOutputSection(".bss"); // If we don't find a bss section, then don't set these values if (bssSection) { - (*bssStartAtomIter)->_virtualAddr = bssSection->virtualAddr(); - (*bssEndAtomIter)->_virtualAddr = + bssStartAtom->_virtualAddr = bssSection->virtualAddr(); + bssEndAtom->_virtualAddr = bssSection-> |