diff options
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->virtualAddr() + bssSection->memSize(); - (*underScoreEndAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; - (*endAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; + underScoreEndAtom->_virtualAddr = bssEndAtom->_virtualAddr; + endAtom->_virtualAddr = bssEndAtom->_virtualAddr; } else if (auto dataSection = this->_layout.findOutputSection(".data")) { - (*underScoreEndAtomIter)->_virtualAddr = + underScoreEndAtom->_virtualAddr = dataSection->virtualAddr() + dataSection->memSize(); - (*endAtomIter)->_virtualAddr = (*underScoreEndAtomIter)->_virtualAddr; + endAtom->_virtualAddr = underScoreEndAtom->_virtualAddr; } } diff --git a/lib/ReaderWriter/ELF/FileCommon.cpp b/lib/ReaderWriter/ELF/FileCommon.cpp new file mode 100644 index 000000000000..c23e3f6656cd --- /dev/null +++ b/lib/ReaderWriter/ELF/FileCommon.cpp @@ -0,0 +1,66 @@ +//===- lib/ReaderWriter/ELF/FileCommon.cpp --------------------------------===// +// +// 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" + +using namespace llvm::ELF; + +namespace lld { +namespace elf { + +static const char *elf32_expected = "ELF32 expected, but got ELF64"; +static const char *elf64_expected = "ELF64 expected, but got ELF32"; +static const char *le_expected = + "Little endian files are expected, but got a big endian file."; +static const char *be_expected = + "Big endian files are expected, but got a little endian file."; + +template <> +std::error_code checkCompatibility<ELF32LE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS64) + return make_dynamic_error_code(elf32_expected); + if (endian == ELFDATA2MSB) + return make_dynamic_error_code(le_expected); + return std::error_code(); +} + +template <> +std::error_code checkCompatibility<ELF32BE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS64) + return make_dynamic_error_code(elf32_expected); + if (endian == ELFDATA2LSB) + return make_dynamic_error_code(be_expected); + return std::error_code(); +} + +template <> +std::error_code checkCompatibility<ELF64LE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS32) + return make_dynamic_error_code(elf64_expected); + if (endian == ELFDATA2MSB) + return make_dynamic_error_code(le_expected); + return std::error_code(); +} + +template <> +std::error_code checkCompatibility<ELF64BE>(unsigned char size, + unsigned char endian) { + if (size == ELFCLASS32) + return make_dynamic_error_code(elf64_expected); + if (endian == ELFDATA2LSB) + return make_dynamic_error_code(be_expected); + return std::error_code(); +} + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/FileCommon.h b/lib/ReaderWriter/ELF/FileCommon.h new file mode 100644 index 000000000000..eaff12afe72f --- /dev/null +++ b/lib/ReaderWriter/ELF/FileCommon.h @@ -0,0 +1,45 @@ +//===- lib/ReaderWriter/ELF/FileCommon.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_FILE_COMMON_H +#define LLD_READER_WRITER_ELF_FILE_COMMON_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +template <class ELFT> +std::error_code checkCompatibility(unsigned char size, unsigned char endian); + +template <typename ELFT> +std::error_code isCompatible(MemoryBufferRef mb, ELFLinkingContext &ctx) { + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + if (uintptr_t(mb.getBufferStart()) & 1) + return make_dynamic_error_code("invalid alignment"); + + auto *hdr = reinterpret_cast<const Elf_Ehdr *>(mb.getBuffer().data()); + if (hdr->e_machine != ctx.getMachineType()) + return make_dynamic_error_code("incompatible machine type"); + + unsigned char size; + unsigned char endian; + std::tie(size, endian) = llvm::object::getElfArchType(mb.getBuffer()); + if (std::error_code ec = checkCompatibility<ELFT>(size, endian)) + return ec; + return std::error_code(); +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/HeaderChunks.cpp b/lib/ReaderWriter/ELF/HeaderChunks.cpp new file mode 100644 index 000000000000..193937c18061 --- /dev/null +++ b/lib/ReaderWriter/ELF/HeaderChunks.cpp @@ -0,0 +1,205 @@ +//===- lib/ReaderWriter/ELF/HeaderChunks.cpp --------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HeaderChunks.h" +#include "TargetLayout.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace elf { + +template <class ELFT> void ELFHeader<ELFT>::finalize() { + _eh.e_ident[llvm::ELF::EI_CLASS] = + (ELFT::Is64Bits) ? llvm::ELF::ELFCLASS64 : llvm::ELF::ELFCLASS32; + _eh.e_ident[llvm::ELF::EI_DATA] = + (ELFT::TargetEndianness == llvm::support::little) + ? llvm::ELF::ELFDATA2LSB + : llvm::ELF::ELFDATA2MSB; + _eh.e_type = this->_ctx.getOutputELFType(); + _eh.e_machine = this->_ctx.getOutputMachine(); +} + +template <class ELFT> +ELFHeader<ELFT>::ELFHeader(const ELFLinkingContext &ctx) + : Chunk<ELFT>("elfhdr", Chunk<ELFT>::Kind::ELFHeader, ctx) { + this->_alignment = ELFT::Is64Bits ? 8 : 4; + this->_fsize = sizeof(Elf_Ehdr); + this->_msize = sizeof(Elf_Ehdr); + memset(_eh.e_ident, 0, llvm::ELF::EI_NIDENT); + e_ident(llvm::ELF::EI_MAG0, 0x7f); + e_ident(llvm::ELF::EI_MAG1, 'E'); + e_ident(llvm::ELF::EI_MAG2, 'L'); + e_ident(llvm::ELF::EI_MAG3, 'F'); + e_ehsize(sizeof(Elf_Ehdr)); + e_flags(0); +} + +template <class ELFT> +void ELFHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *atomContent = chunkBuffer + this->fileOffset(); + memcpy(atomContent, &_eh, fileSize()); +} + +template <class ELFT> +bool ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) { + bool allocatedNew = false; + ELFLinkingContext::OutputMagic outputMagic = this->_ctx.getOutputMagic(); + // For segments that are not a loadable segment, we + // just pick the values directly from the segment as there + // wouldnt be any slices within that + if (segment->segmentType() != llvm::ELF::PT_LOAD) { + Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); + phdr->p_type = segment->segmentType(); + phdr->p_offset = segment->fileOffset(); + phdr->p_vaddr = segment->virtualAddr(); + phdr->p_paddr = segment->virtualAddr(); + phdr->p_filesz = segment->fileSize(); + phdr->p_memsz = segment->memSize(); + phdr->p_flags = segment->flags(); + phdr->p_align = segment->alignment(); + this->_fsize = fileSize(); + this->_msize = this->_fsize; + return allocatedNew; + } + // For all other segments, use the slice + // to derive program headers + for (auto slice : segment->slices()) { + Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); + phdr->p_type = segment->segmentType(); + phdr->p_offset = slice->fileOffset(); + phdr->p_vaddr = slice->virtualAddr(); + phdr->p_paddr = slice->virtualAddr(); + phdr->p_filesz = slice->fileSize(); + phdr->p_memsz = slice->memSize(); + phdr->p_flags = segment->flags(); + phdr->p_align = slice->alignment(); + uint64_t segPageSize = segment->pageSize(); + uint64_t sliceAlign = slice->alignment(); + // Alignment of PT_LOAD segments are set to the page size, but if the + // alignment of the slice is greater than the page size, set the alignment + // of the segment appropriately. + if (outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + phdr->p_align = + (phdr->p_type == llvm::ELF::PT_LOAD) + ? (segPageSize < sliceAlign) ? sliceAlign : segPageSize + : sliceAlign; + } else + phdr->p_align = slice->alignment(); + } + this->_fsize = fileSize(); + this->_msize = this->_fsize; + + return allocatedNew; +} + +template <class ELFT> +void ProgramHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto phi : _ph) { + memcpy(dest, phi, sizeof(Elf_Phdr)); + dest += sizeof(Elf_Phdr); + } +} + +template <class ELFT> +typename ProgramHeader<ELFT>::Elf_Phdr * +ProgramHeader<ELFT>::allocateProgramHeader(bool &allocatedNew) { + Elf_Phdr *phdr; + if (_phi == _ph.end()) { + phdr = new (_allocator) Elf_Phdr; + _ph.push_back(phdr); + _phi = _ph.end(); + allocatedNew = true; + } else { + phdr = (*_phi); + ++_phi; + } + return phdr; +} + +template <class ELFT> +SectionHeader<ELFT>::SectionHeader(const ELFLinkingContext &ctx, int32_t order) + : Chunk<ELFT>("shdr", Chunk<ELFT>::Kind::SectionHeader, ctx) { + this->_fsize = 0; + this->_alignment = 8; + this->setOrder(order); + // The first element in the list is always NULL + auto *nullshdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; + ::memset(nullshdr, 0, sizeof(Elf_Shdr)); + _sectionInfo.push_back(nullshdr); + this->_fsize += sizeof(Elf_Shdr); +} + +template <class ELFT> +void SectionHeader<ELFT>::appendSection(OutputSection<ELFT> *section) { + auto *shdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; + shdr->sh_name = _stringSection->addString(section->name()); + shdr->sh_type = section->type(); + shdr->sh_flags = section->flags(); + shdr->sh_offset = section->fileOffset(); + shdr->sh_addr = section->virtualAddr(); + if (section->isLoadableSection()) + shdr->sh_size = section->memSize(); + else + shdr->sh_size = section->fileSize(); + shdr->sh_link = section->link(); + shdr->sh_info = section->shinfo(); + shdr->sh_addralign = section->alignment(); + shdr->sh_entsize = section->entsize(); + _sectionInfo.push_back(shdr); +} + +template <class ELFT> +void SectionHeader<ELFT>::updateSection(Section<ELFT> *section) { + Elf_Shdr *shdr = _sectionInfo[section->ordinal()]; + shdr->sh_type = section->getType(); + shdr->sh_flags = section->getFlags(); + shdr->sh_offset = section->fileOffset(); + shdr->sh_addr = section->virtualAddr(); + shdr->sh_size = section->fileSize(); + shdr->sh_link = section->getLink(); + shdr->sh_info = section->getInfo(); + shdr->sh_addralign = section->alignment(); + shdr->sh_entsize = section->getEntSize(); +} + +template <class ELFT> +void SectionHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto shi : _sectionInfo) { + memcpy(dest, shi, sizeof(Elf_Shdr)); + dest += sizeof(Elf_Shdr); + } + _stringSection->write(writer, layout, buffer); +} + +template class ELFHeader<ELF32LE>; +template class ELFHeader<ELF32BE>; +template class ELFHeader<ELF64LE>; +template class ELFHeader<ELF64BE>; + +template class ProgramHeader<ELF32LE>; +template class ProgramHeader<ELF32BE>; +template class ProgramHeader<ELF64LE>; +template class ProgramHeader<ELF64BE>; + +template class SectionHeader<ELF32LE>; +template class SectionHeader<ELF32BE>; +template class SectionHeader<ELF64LE>; +template class SectionHeader<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/HeaderChunks.h b/lib/ReaderWriter/ELF/HeaderChunks.h index eab132b9b2f6..51fbe38f1a00 100644 --- a/lib/ReaderWriter/ELF/HeaderChunks.h +++ b/lib/ReaderWriter/ELF/HeaderChunks.h @@ -11,18 +11,18 @@ #define LLD_READER_WRITER_ELF_HEADER_CHUNKS_H #include "SegmentChunks.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/Debug.h" #include "llvm/Support/ELF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/Format.h" /// \brief An Header represents the Elf[32/64]_Ehdr structure at the /// start of an ELF executable file. namespace lld { namespace elf { + template <class ELFT> class ELFHeader : public Chunk<ELFT> { public: typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; @@ -33,9 +33,9 @@ public: void e_type(uint16_t type) { _eh.e_type = type; } void e_machine(uint16_t machine) { _eh.e_machine = machine; } void e_version(uint32_t version) { _eh.e_version = version; } - void e_entry(int64_t entry) { _eh.e_entry = entry; } - void e_phoff(int64_t phoff) { _eh.e_phoff = phoff; } - void e_shoff(int64_t shoff) { _eh.e_shoff = shoff; } + void e_entry(int64_t entry) { _eh.e_entry = entry; } + void e_phoff(int64_t phoff) { _eh.e_phoff = phoff; } + void e_shoff(int64_t shoff) { _eh.e_shoff = shoff; } void e_flags(uint32_t flags) { _eh.e_flags = flags; } void e_ehsize(uint16_t ehsize) { _eh.e_ehsize = ehsize; } void e_phentsize(uint16_t phentsize) { _eh.e_phentsize = phentsize; } @@ -43,57 +43,25 @@ public: void e_shentsize(uint16_t shentsize) { _eh.e_shentsize = shentsize; } void e_shnum(uint16_t shnum) { _eh.e_shnum = shnum; } void e_shstrndx(uint16_t shstrndx) { _eh.e_shstrndx = shstrndx; } - uint64_t fileSize() const { return sizeof(Elf_Ehdr); } + uint64_t fileSize() const override { return sizeof(Elf_Ehdr); } static bool classof(const Chunk<ELFT> *c) { - return c->Kind() == Chunk<ELFT>::Kind::ELFHeader; + return c->kind() == Chunk<ELFT>::Kind::ELFHeader; } - int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + int getContentType() const override { + return Chunk<ELFT>::ContentType::Header; + } void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer); - - virtual void doPreFlight() {} - - void finalize() { - _eh.e_ident[llvm::ELF::EI_CLASS] = - (ELFT::Is64Bits) ? llvm::ELF::ELFCLASS64 : llvm::ELF::ELFCLASS32; - _eh.e_ident[llvm::ELF::EI_DATA] = - (ELFT::TargetEndianness == llvm::support::little) - ? llvm::ELF::ELFDATA2LSB - : llvm::ELF::ELFDATA2MSB; - _eh.e_type = this->_context.getOutputELFType(); - _eh.e_machine = this->_context.getOutputMachine(); - } + llvm::FileOutputBuffer &buffer) override; + + void finalize() override; private: Elf_Ehdr _eh; }; -template <class ELFT> -ELFHeader<ELFT>::ELFHeader(const ELFLinkingContext &context) - : Chunk<ELFT>("elfhdr", Chunk<ELFT>::Kind::ELFHeader, context) { - this->_alignment = ELFT::Is64Bits ? 8 : 4; - this->_fsize = sizeof(Elf_Ehdr); - this->_msize = sizeof(Elf_Ehdr); - memset(_eh.e_ident, 0, llvm::ELF::EI_NIDENT); - e_ident(llvm::ELF::EI_MAG0, 0x7f); - e_ident(llvm::ELF::EI_MAG1, 'E'); - e_ident(llvm::ELF::EI_MAG2, 'L'); - e_ident(llvm::ELF::EI_MAG3, 'F'); - e_ehsize(sizeof(Elf_Ehdr)); - e_flags(0); -} - -template <class ELFT> -void ELFHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *atomContent = chunkBuffer + this->fileOffset(); - memcpy(atomContent, &_eh, fileSize()); -} - /// \brief An ProgramHeader represents the Elf[32/64]_Phdr structure at the /// start of an ELF executable file. template<class ELFT> @@ -103,160 +71,43 @@ public: typedef typename std::vector<Elf_Phdr *>::iterator PhIterT; typedef typename std::reverse_iterator<PhIterT> ReversePhIterT; - /// \brief Find a program header entry, given the type of entry that - /// we are looking for - class FindPhdr { - public: - FindPhdr(uint64_t type, uint64_t flags, uint64_t flagsClear) - : _type(type) - , _flags(flags) - , _flagsClear(flagsClear) { - } - - bool operator()(const llvm::object::Elf_Phdr_Impl<ELFT> *j) const { - return ((j->p_type == _type) && - ((j->p_flags & _flags) == _flags) && - (!(j->p_flags & _flagsClear))); - } - private: - uint64_t _type; - uint64_t _flags; - uint64_t _flagsClear; - }; - - ProgramHeader(const ELFLinkingContext &context) - : Chunk<ELFT>("elfphdr", Chunk<ELFT>::Kind::ProgramHeader, context) { + ProgramHeader(const ELFLinkingContext &ctx) + : Chunk<ELFT>("elfphdr", Chunk<ELFT>::Kind::ProgramHeader, ctx) { this->_alignment = ELFT::Is64Bits ? 8 : 4; resetProgramHeaders(); } bool addSegment(Segment<ELFT> *segment); - void resetProgramHeaders() { _phi = _ph.begin(); } - - uint64_t fileSize() const { return sizeof(Elf_Phdr) * _ph.size(); } + uint64_t fileSize() const override { return sizeof(Elf_Phdr) * _ph.size(); } static bool classof(const Chunk<ELFT> *c) { - return c->Kind() == Chunk<ELFT>::Kind::ProgramHeader; + return c->kind() == Chunk<ELFT>::Kind::ProgramHeader; } void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer); - - /// \brief find a program header entry in the list of program headers - ReversePhIterT - findProgramHeader(uint64_t type, uint64_t flags, uint64_t flagClear) { - return std::find_if(_ph.rbegin(), _ph.rend(), - FindPhdr(type, flags, flagClear)); - } - - PhIterT begin() { - return _ph.begin(); - } - - PhIterT end() { - return _ph.end(); - } + llvm::FileOutputBuffer &buffer) override; + PhIterT begin() { return _ph.begin(); } + PhIterT end() { return _ph.end(); } ReversePhIterT rbegin() { return _ph.rbegin(); } - ReversePhIterT rend() { return _ph.rend(); } - virtual void doPreFlight() {} - - void finalize() {} - int64_t entsize() { return sizeof(Elf_Phdr); } + int64_t numHeaders() { return _ph.size(); } - int64_t numHeaders() { - return _ph.size(); + int getContentType() const override { + return Chunk<ELFT>::ContentType::Header; } - int getContentType() const { return Chunk<ELFT>::ContentType::Header; } - private: - Elf_Phdr *allocateProgramHeader(bool &allocatedNew) { - Elf_Phdr *phdr; - if (_phi == _ph.end()) { - phdr = new (_allocator) Elf_Phdr; - _ph.push_back(phdr); - _phi = _ph.end(); - allocatedNew = true; - } else { - phdr = (*_phi); - ++_phi; - } - return phdr; - } + Elf_Phdr *allocateProgramHeader(bool &allocatedNew); std::vector<Elf_Phdr *> _ph; PhIterT _phi; llvm::BumpPtrAllocator _allocator; }; -template <class ELFT> -bool ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) { - bool allocatedNew = false; - ELFLinkingContext::OutputMagic outputMagic = this->_context.getOutputMagic(); - // For segments that are not a loadable segment, we - // just pick the values directly from the segment as there - // wouldnt be any slices within that - if (segment->segmentType() != llvm::ELF::PT_LOAD) { - Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); - phdr->p_type = segment->segmentType(); - phdr->p_offset = segment->fileOffset(); - phdr->p_vaddr = segment->virtualAddr(); - phdr->p_paddr = segment->virtualAddr(); - phdr->p_filesz = segment->fileSize(); - phdr->p_memsz = segment->memSize(); - phdr->p_flags = segment->flags(); - phdr->p_align = segment->alignment(); - this->_fsize = fileSize(); - this->_msize = this->_fsize; - return allocatedNew; - } - // For all other segments, use the slice - // to derive program headers - for (auto slice : segment->slices()) { - Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); - phdr->p_type = segment->segmentType(); - phdr->p_offset = slice->fileOffset(); - phdr->p_vaddr = slice->virtualAddr(); - phdr->p_paddr = slice->virtualAddr(); - phdr->p_filesz = slice->fileSize(); - phdr->p_memsz = slice->memSize(); - phdr->p_flags = segment->flags(); - phdr->p_align = slice->alignment(); - uint64_t segPageSize = segment->pageSize(); - uint64_t sliceAlign = slice->alignment(); - // Alignment of PT_LOAD segments are set to the page size, but if the - // alignment of the slice is greater than the page size, set the alignment - // of the segment appropriately. - if (outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { - phdr->p_align = (phdr->p_type == llvm::ELF::PT_LOAD) - ? (segPageSize < sliceAlign) ? sliceAlign : segPageSize - : sliceAlign; - } else - phdr->p_align = slice->alignment(); - } - this->_fsize = fileSize(); - this->_msize = this->_fsize; - - return allocatedNew; -} - -template <class ELFT> -void ProgramHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - for (auto phi : _ph) { - memcpy(dest, phi, sizeof(Elf_Phdr)); - dest += sizeof(Elf_Phdr); - } -} - /// \brief An SectionHeader represents the Elf[32/64]_Shdr structure /// at the end of the file template<class ELFT> @@ -265,13 +116,11 @@ public: typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; SectionHeader(const ELFLinkingContext &, int32_t order); - void appendSection(OutputSection<ELFT> *section); - void updateSection(Section<ELFT> *section); static bool classof(const Chunk<ELFT> *c) { - return c->getChunkKind() == Chunk<ELFT>::Kind::SectionHeader; + return c->kind() == Chunk<ELFT>::Kind::SectionHeader; } void setStringSection(StringTable<ELFT> *s) { @@ -279,85 +128,26 @@ public: } void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer); + llvm::FileOutputBuffer &buffer) override; - virtual void doPreFlight() {} - - void finalize() {} - - uint64_t fileSize() const { return sizeof(Elf_Shdr) * _sectionInfo.size(); } + uint64_t fileSize() const override { + return sizeof(Elf_Shdr) * _sectionInfo.size(); + } uint64_t entsize() { return sizeof(Elf_Shdr); } - int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + int getContentType() const override { + return Chunk<ELFT>::ContentType::Header; + } uint64_t numHeaders() { return _sectionInfo.size(); } private: StringTable<ELFT> *_stringSection; - std::vector<Elf_Shdr*> _sectionInfo; - llvm::BumpPtrAllocator _sectionAllocate; + std::vector<Elf_Shdr *> _sectionInfo; + llvm::BumpPtrAllocator _sectionAllocate; }; -template <class ELFT> -SectionHeader<ELFT>::SectionHeader(const ELFLinkingContext &context, - int32_t order) - : Chunk<ELFT>("shdr", Chunk<ELFT>::Kind::SectionHeader, context) { - this->_fsize = 0; - this->_alignment = 8; - this->setOrder(order); - // The first element in the list is always NULL - Elf_Shdr *nullshdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; - ::memset(nullshdr, 0, sizeof (Elf_Shdr)); - _sectionInfo.push_back(nullshdr); - this->_fsize += sizeof (Elf_Shdr); -} - -template <class ELFT> -void SectionHeader<ELFT>::appendSection(OutputSection<ELFT> *section) { - Elf_Shdr *shdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; - shdr->sh_name = _stringSection->addString(section->name()); - shdr->sh_type = section->type(); - shdr->sh_flags = section->flags(); - shdr->sh_offset = section->fileOffset(); - shdr->sh_addr = section->virtualAddr(); - if (section->isLoadableSection()) - shdr->sh_size = section->memSize(); - else - shdr->sh_size = section->fileSize(); - shdr->sh_link = section->link(); - shdr->sh_info = section->shinfo(); - shdr->sh_addralign = section->alignment(); - shdr->sh_entsize = section->entsize(); - _sectionInfo.push_back(shdr); -} - -template<class ELFT> -void -SectionHeader<ELFT>::updateSection(Section<ELFT> *section) { - Elf_Shdr *shdr = _sectionInfo[section->ordinal()]; - shdr->sh_type = section->getType(); - shdr->sh_flags = section->getFlags(); - shdr->sh_offset = section->fileOffset(); - shdr->sh_addr = section->virtualAddr(); - shdr->sh_size = section->fileSize(); - shdr->sh_link = section->getLink(); - shdr->sh_info = section->getInfo(); - shdr->sh_addralign = section->alignment(); - shdr->sh_entsize = section->getEntSize(); -} - -template <class ELFT> -void SectionHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - for (auto shi : _sectionInfo) { - memcpy(dest, shi, sizeof(Elf_Shdr)); - dest += sizeof(Elf_Shdr); - } - _stringSection->write(writer, layout, buffer); -} } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h index e2d3193045b7..84415b273f44 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h @@ -10,67 +10,55 @@ #define HEXAGON_DYNAMIC_LIBRARY_WRITER_H #include "DynamicLibraryWriter.h" -#include "HexagonExecutableAtoms.h" #include "HexagonLinkingContext.h" namespace lld { namespace elf { -template <typename ELFT> class HexagonTargetLayout; +class HexagonTargetLayout; -template <class ELFT> -class HexagonDynamicLibraryWriter : public DynamicLibraryWriter<ELFT>, - public HexagonELFWriter<ELFT> { +class HexagonDynamicLibraryWriter : public DynamicLibraryWriter<ELF32LE> { public: - HexagonDynamicLibraryWriter(HexagonLinkingContext &context, - HexagonTargetLayout<ELFT> &layout); + HexagonDynamicLibraryWriter(HexagonLinkingContext &ctx, + HexagonTargetLayout &layout); protected: // Add any runtime files and their atoms to the output - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - virtual void finalizeDefaultAtomValues(); + void finalizeDefaultAtomValues() override; - virtual std::error_code setELFHeader() { - DynamicLibraryWriter<ELFT>::setELFHeader(); - HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + std::error_code setELFHeader() override { + DynamicLibraryWriter::setELFHeader(); + setHexagonELFHeader(*_elfHeader); return std::error_code(); } private: - void addDefaultAtoms() { - _hexagonRuntimeFile->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); - _hexagonRuntimeFile->addAbsoluteAtom("_DYNAMIC"); - } - - HexagonLinkingContext &_hexagonLinkingContext; - HexagonTargetLayout<ELFT> &_hexagonTargetLayout; - std::unique_ptr<HexagonRuntimeFile<ELFT>> _hexagonRuntimeFile; + HexagonLinkingContext &_ctx; + HexagonTargetLayout &_targetLayout; }; -template <class ELFT> -HexagonDynamicLibraryWriter<ELFT>::HexagonDynamicLibraryWriter( - HexagonLinkingContext &context, HexagonTargetLayout<ELFT> &layout) - : DynamicLibraryWriter<ELFT>(context, layout), - HexagonELFWriter<ELFT>(context, layout), _hexagonLinkingContext(context), - _hexagonTargetLayout(layout), - _hexagonRuntimeFile(new HexagonRuntimeFile<ELFT>(context)) {} +HexagonDynamicLibraryWriter::HexagonDynamicLibraryWriter( + HexagonLinkingContext &ctx, HexagonTargetLayout &layout) + : DynamicLibraryWriter(ctx, layout), _ctx(ctx), _targetLayout(layout) {} -template <class ELFT> -bool HexagonDynamicLibraryWriter<ELFT>::createImplicitFiles( +void HexagonDynamicLibraryWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + DynamicLibraryWriter::createImplicitFiles(result); // Add the default atoms as defined for hexagon - addDefaultAtoms(); - result.push_back(std::move(_hexagonRuntimeFile)); - return true; + auto file = + llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "Hexagon runtime file"); + file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + file->addAbsoluteAtom("_DYNAMIC"); + result.push_back(std::move(file)); } -template <class ELFT> -void HexagonDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { +void HexagonDynamicLibraryWriter::finalizeDefaultAtomValues() { // Finalize the atom values that are part of the parent. - DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); - HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); + DynamicLibraryWriter::finalizeDefaultAtomValues(); + if (_ctx.isDynamic()) + finalizeHexagonRuntimeAtomValues(_targetLayout); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h index ab0b9b432b43..3d0d38f387dd 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h @@ -16,53 +16,46 @@ namespace lld { namespace elf { -template <class ELFT> class HexagonELFFile; +class HexagonELFFile; -template <class ELFT> -class HexagonELFDefinedAtom : public ELFDefinedAtom<ELFT> { - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; +class HexagonELFDefinedAtom : public ELFDefinedAtom<ELF32LE> { + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> Elf_Shdr; public: - HexagonELFDefinedAtom(const HexagonELFFile<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) {} - - virtual DefinedAtom::ContentType contentType() const { - if (this->_contentType != DefinedAtom::typeUnknown) - return this->_contentType; - else if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) { - if (this->_section->sh_type == llvm::ELF::SHT_NOBITS) - return (this->_contentType = DefinedAtom::typeZeroFillFast); - else - return (this->_contentType = DefinedAtom::typeDataFast); + template <typename... T> + HexagonELFDefinedAtom(T &&... args) + : ELFDefinedAtom(std::forward<T>(args)...) {} + + DefinedAtom::ContentType contentType() const override { + if (_contentType != DefinedAtom::typeUnknown) + return _contentType; + if (_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) { + if (_section->sh_type == llvm::ELF::SHT_NOBITS) + return (_contentType = DefinedAtom::typeZeroFillFast); + return (_contentType = DefinedAtom::typeDataFast); } - return ELFDefinedAtom<ELFT>::contentType(); + return ELFDefinedAtom::contentType(); } - virtual DefinedAtom::ContentPermissions permissions() const { - if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) + DefinedAtom::ContentPermissions permissions() const override { + if (_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) return DefinedAtom::permRW_; - return ELFDefinedAtom<ELFT>::permissions(); + return ELFDefinedAtom::permissions(); } }; -template <class ELFT> class HexagonELFCommonAtom : public ELFCommonAtom<ELFT> { - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; +class HexagonELFCommonAtom : public ELFCommonAtom<ELF32LE> { + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> Elf_Shdr; public: - HexagonELFCommonAtom(const HexagonELFFile<ELFT> &file, StringRef symbolName, + HexagonELFCommonAtom(const ELFFile<ELF32LE> &file, StringRef symbolName, const Elf_Sym *symbol) - : ELFCommonAtom<ELFT>(file, symbolName, symbol) {} + : ELFCommonAtom(file, symbolName, symbol) {} virtual bool isSmallCommonSymbol() const { - switch (this->_symbol->st_shndx) { + switch (_symbol->st_shndx) { // Common symbols case llvm::ELF::SHN_HEXAGON_SCOMMON: case llvm::ELF::SHN_HEXAGON_SCOMMON_1: @@ -76,52 +69,46 @@ public: return false; } - virtual uint64_t size() const { + uint64_t size() const override { if (isSmallCommonSymbol()) - return this->_symbol->st_size; - return ELFCommonAtom<ELFT>::size(); + return _symbol->st_size; + return ELFCommonAtom::size(); } - virtual DefinedAtom::Merge merge() const { - if (this->_symbol->getBinding() == llvm::ELF::STB_WEAK) + DefinedAtom::Merge merge() const override { + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) return DefinedAtom::mergeAsWeak; if (isSmallCommonSymbol()) return DefinedAtom::mergeAsTentative; - return ELFCommonAtom<ELFT>::merge(); + return ELFCommonAtom::merge(); } - virtual DefinedAtom::ContentType contentType() const { + DefinedAtom::ContentType contentType() const override { if (isSmallCommonSymbol()) return DefinedAtom::typeZeroFillFast; - return ELFCommonAtom<ELFT>::contentType(); + return ELFCommonAtom::contentType(); } - virtual DefinedAtom::Alignment alignment() const { + DefinedAtom::Alignment alignment() const override { if (isSmallCommonSymbol()) - return DefinedAtom::Alignment(llvm::Log2_64(this->_symbol->st_value)); - return ELFCommonAtom<ELFT>::alignment(); + return DefinedAtom::Alignment(_symbol->st_value); + return 1; } - virtual DefinedAtom::ContentPermissions permissions() const { + DefinedAtom::ContentPermissions permissions() const override { if (isSmallCommonSymbol()) return DefinedAtom::permRW_; - return ELFCommonAtom<ELFT>::permissions(); + return ELFCommonAtom::permissions(); } }; -template <class ELFT> class HexagonELFFile : public ELFFile<ELFT> { - typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; +class HexagonELFFile : public ELFFile<ELF32LE> { + typedef llvm::object::Elf_Sym_Impl<ELF32LE> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELF32LE> Elf_Shdr; public: - HexagonELFFile(std::unique_ptr<MemoryBuffer> mb, HexagonLinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} - - static ErrorOr<std::unique_ptr<HexagonELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, HexagonLinkingContext &ctx) { - return std::unique_ptr<HexagonELFFile<ELFT>>( - new HexagonELFFile<ELFT>(std::move(mb), ctx)); - } + HexagonELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : ELFFile(std::move(mb), ctx) {} bool isCommonSymbol(const Elf_Sym *symbol) const override { switch (symbol->st_shndx) { @@ -135,35 +122,27 @@ public: default: break; } - return ELFFile<ELFT>::isCommonSymbol(symbol); + return ELFFile::isCommonSymbol(symbol); } /// 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) HexagonELFDefinedAtom<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 { + return new (_readerStorage) HexagonELFDefinedAtom( *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, referenceEnd, referenceList); } /// Process the Common symbol and create an atom for it. - ErrorOr<ELFCommonAtom<ELFT> *> - handleCommonSymbol(StringRef symName, const Elf_Sym *sym) override { - return new (this->_readerStorage) - HexagonELFCommonAtom<ELFT>(*this, symName, sym); + ELFCommonAtom<ELF32LE> *createCommonAtom(StringRef symName, + const Elf_Sym *sym) override { + return new (_readerStorage) HexagonELFCommonAtom(*this, symName, sym); } }; -template <class ELFT> class HexagonDynamicFile : public DynamicFile<ELFT> { -public: - HexagonDynamicFile(const HexagonLinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} -}; - } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h deleted file mode 100644 index 1a4f891df799..000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- lib/ReaderWriter/ELF/HexagonELFReader.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_HEXAGON_ELF_READER_H -#define LLD_READER_WRITER_HEXAGON_ELF_READER_H - -#include "ELFReader.h" -#include "HexagonELFFile.h" - -namespace lld { -namespace elf { - -typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; - -struct HexagonDynamicFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - HexagonLinkingContext &ctx) { - return lld::elf::HexagonDynamicFile<ELFT>::create(std::move(mb), ctx); - } -}; - -struct HexagonELFFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - HexagonLinkingContext &ctx) { - return lld::elf::HexagonELFFile<ELFT>::create(std::move(mb), ctx); - } -}; - -class HexagonELFObjectReader - : public ELFObjectReader<HexagonELFType, HexagonELFFileCreateELFTraits, - HexagonLinkingContext> { -public: - HexagonELFObjectReader(HexagonLinkingContext &ctx) - : ELFObjectReader<HexagonELFType, HexagonELFFileCreateELFTraits, - HexagonLinkingContext>(ctx, llvm::ELF::EM_HEXAGON) {} -}; - -class HexagonELFDSOReader - : public ELFDSOReader<HexagonELFType, HexagonDynamicFileCreateELFTraits, - HexagonLinkingContext> { -public: - HexagonELFDSOReader(HexagonLinkingContext &ctx) - : ELFDSOReader<HexagonELFType, HexagonDynamicFileCreateELFTraits, - HexagonLinkingContext>(ctx, llvm::ELF::EM_HEXAGON) {} -}; - -} // namespace elf -} // namespace lld - -#endif // LLD_READER_WRITER_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h deleted file mode 100644 index 96c74f72222d..000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h +++ /dev/null @@ -1,61 +0,0 @@ -//===- lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h -------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef HEXAGON_ELF_WRITERS_H -#define HEXAGON_ELF_WRITERS_H - -#include "HexagonLinkingContext.h" -#include "OutputELFWriter.h" - -namespace lld { -namespace elf { - -template <class ELFT> class HexagonTargetLayout; - -template <typename ELFT> class HexagonELFWriter { -public: - HexagonELFWriter(HexagonLinkingContext &context, - HexagonTargetLayout<ELFT> &targetLayout) - : _hexagonLinkingContext(context), _hexagonTargetLayout(targetLayout) {} - -protected: - bool setELFHeader(ELFHeader<ELFT> &elfHeader) { - elfHeader.e_ident(llvm::ELF::EI_VERSION, 1); - elfHeader.e_ident(llvm::ELF::EI_OSABI, 0); - elfHeader.e_version(1); - elfHeader.e_flags(0x3); - return true; - } - - void finalizeHexagonRuntimeAtomValues() { - if (_hexagonLinkingContext.isDynamic()) { - auto gotAtomIter = - _hexagonTargetLayout.findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); - auto gotpltSection = _hexagonTargetLayout.findOutputSection(".got.plt"); - if (gotpltSection) - (*gotAtomIter)->_virtualAddr = gotpltSection->virtualAddr(); - else - (*gotAtomIter)->_virtualAddr = 0; - auto dynamicAtomIter = _hexagonTargetLayout.findAbsoluteAtom("_DYNAMIC"); - auto dynamicSection = _hexagonTargetLayout.findOutputSection(".dynamic"); - if (dynamicSection) - (*dynamicAtomIter)->_virtualAddr = dynamicSection->virtualAddr(); - else - (*dynamicAtomIter)->_virtualAddr = 0; - } - } - -private: - HexagonLinkingContext &_hexagonLinkingContext; - HexagonTargetLayout<ELFT> &_hexagonTargetLayout; -}; - -} // elf -} // lld -#endif // HEXAGON_ELF_WRITERS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h index 3e12786704a2..6af43d88afbb 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h @@ -6,7 +6,27 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" + +namespace lld { +namespace elf { + +/// \brief Applying fixup on Hexagon requires the relocator to fetch the fixup +/// mask from the instruction. To fetch the fixup encoding, the linker uses a +/// static array that contains the instruction mask, the compare mask and the +/// relocation mask. +typedef struct { + uint32_t insnMask; // Instruction mask. + uint32_t insnCmpMask; // Compare mask. + uint32_t insnBitMask; // Relocation mask. + bool isDuplex; // Indicates if the instruction is a duplex instruction. +} Instruction; + Instruction insn_encodings[] = { + // InsnMask CompareMask BitMask IsDuplexInstruction { 0xffe00004, 0x40000000, 0x20f8, 0x0 }, { 0xffe03080, 0x9ca03080, 0xf60, 0x0 }, { 0xf9e00000, 0x48c00000, 0x61f20ff, 0x0 }, @@ -599,3 +619,20 @@ Instruction insn_encodings[] = { { 0xff602060, 0x3f000060, 0x1f80, 0x0 }, { 0xf7c02000, 0x11000000, 0x3000fe, 0x0 }, }; + +/// \brief finds the scatter Bits that need to be used to apply relocations +inline uint32_t findv4bitmask(uint8_t *location) { + uint32_t insn = llvm::support::endian::read32le(location); + for (int32_t i = 0, e = llvm::array_lengthof(insn_encodings); i < e; i++) { + if ((insn & 0xc000) == 0 && !insn_encodings[i].isDuplex) + continue; + if ((insn & 0xc000) != 0 && insn_encodings[i].isDuplex) + continue; + if ((insn_encodings[i].insnMask & insn) == insn_encodings[i].insnCmpMask) + return insn_encodings[i].insnBitMask; + } + llvm_unreachable("found unknown Hexagon instruction"); +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h deleted file mode 100644 index a2505aa460c5..000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h +++ /dev/null @@ -1,29 +0,0 @@ -//===- lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.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_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H -#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H - -#include "ELFFile.h" - -namespace lld { -namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; -class HexagonLinkingContext; - -template <class HexagonELFType> class HexagonRuntimeFile - : public RuntimeFile<HexagonELFType> { -public: - HexagonRuntimeFile(HexagonLinkingContext &context) - : RuntimeFile<HexagonELFType>(context, "Hexagon runtime file") {} -}; -} // elf -} // lld - -#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h index 0848e64166fa..390694954a75 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h @@ -10,74 +10,61 @@ #define HEXAGON_EXECUTABLE_WRITER_H #include "ExecutableWriter.h" -#include "HexagonELFWriters.h" -#include "HexagonExecutableAtoms.h" #include "HexagonLinkingContext.h" +#include "HexagonTargetHandler.h" namespace lld { namespace elf { -template <typename ELFT> class HexagonTargetLayout; +class HexagonTargetLayout; -template <class ELFT> -class HexagonExecutableWriter : public ExecutableWriter<ELFT>, - public HexagonELFWriter<ELFT> { +class HexagonExecutableWriter : public ExecutableWriter<ELF32LE> { public: - HexagonExecutableWriter(HexagonLinkingContext &context, - HexagonTargetLayout<ELFT> &layout); + HexagonExecutableWriter(HexagonLinkingContext &ctx, + HexagonTargetLayout &layout); protected: // Add any runtime files and their atoms to the output - virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - virtual void finalizeDefaultAtomValues(); + void finalizeDefaultAtomValues() override; - virtual std::error_code setELFHeader() { - ExecutableWriter<ELFT>::setELFHeader(); - HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + std::error_code setELFHeader() override { + ExecutableWriter::setELFHeader(); + setHexagonELFHeader(*_elfHeader); return std::error_code(); } private: - void addDefaultAtoms() { - _hexagonRuntimeFile->addAbsoluteAtom("_SDA_BASE_"); - if (this->_context.isDynamic()) { - _hexagonRuntimeFile->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); - _hexagonRuntimeFile->addAbsoluteAtom("_DYNAMIC"); - } - } - - HexagonLinkingContext &_hexagonLinkingContext; - HexagonTargetLayout<ELFT> &_hexagonTargetLayout; - std::unique_ptr<HexagonRuntimeFile<ELFT>> _hexagonRuntimeFile; + HexagonLinkingContext &_ctx; + HexagonTargetLayout &_targetLayout; }; -template <class ELFT> -HexagonExecutableWriter<ELFT>::HexagonExecutableWriter( - HexagonLinkingContext &context, HexagonTargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(context, layout), - HexagonELFWriter<ELFT>(context, layout), _hexagonLinkingContext(context), - _hexagonTargetLayout(layout), - _hexagonRuntimeFile(new HexagonRuntimeFile<ELFT>(context)) {} +HexagonExecutableWriter::HexagonExecutableWriter(HexagonLinkingContext &ctx, + HexagonTargetLayout &layout) + : ExecutableWriter(ctx, layout), _ctx(ctx), _targetLayout(layout) {} -template <class ELFT> -bool HexagonExecutableWriter<ELFT>::createImplicitFiles( +void HexagonExecutableWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); + ExecutableWriter::createImplicitFiles(result); // Add the default atoms as defined for hexagon - addDefaultAtoms(); - result.push_back(std::move(_hexagonRuntimeFile)); - return true; + auto file = + llvm::make_unique<RuntimeFile<ELF32LE>>(_ctx, "Hexagon runtime file"); + file->addAbsoluteAtom("_SDA_BASE_"); + if (_ctx.isDynamic()) { + file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + file->addAbsoluteAtom("_DYNAMIC"); + } + result.push_back(std::move(file)); } -template <class ELFT> -void HexagonExecutableWriter<ELFT>::finalizeDefaultAtomValues() { +void HexagonExecutableWriter::finalizeDefaultAtomValues() { // Finalize the atom values that are part of the parent. - ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); - auto sdabaseAtomIter = _hexagonTargetLayout.findAbsoluteAtom("_SDA_BASE_"); - (*sdabaseAtomIter)->_virtualAddr = - _hexagonTargetLayout.getSDataSection()->virtualAddr(); - HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); + ExecutableWriter::finalizeDefaultAtomValues(); + AtomLayout *sdabaseAtom = _targetLayout.findAbsoluteAtom("_SDA_BASE_"); + sdabaseAtom->_virtualAddr = _targetLayout.getSDataSection()->virtualAddr(); + if (_ctx.isDynamic()) + finalizeHexagonRuntimeAtomValues(_targetLayout); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp index 7eacb2b44c3b..11eabf7e26fc 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp @@ -10,16 +10,38 @@ #include "HexagonLinkingContext.h" #include "HexagonTargetHandler.h" -using namespace lld::elf; +namespace lld { +namespace elf { -std::unique_ptr<lld::ELFLinkingContext> -HexagonLinkingContext::create(llvm::Triple triple) { +std::unique_ptr<ELFLinkingContext> +createHexagonLinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::hexagon) - return std::unique_ptr<lld::ELFLinkingContext>( - new HexagonLinkingContext(triple)); + return llvm::make_unique<HexagonLinkingContext>(triple); return nullptr; } HexagonLinkingContext::HexagonLinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + : ELFLinkingContext(triple, std::unique_ptr<TargetHandler>( new HexagonTargetHandler(*this))) {} + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/Hexagon.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void HexagonLinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Hexagon, kindStrings); +} + +void setHexagonELFHeader(ELFHeader<ELF32LE> &elfHeader) { + elfHeader.e_ident(llvm::ELF::EI_VERSION, 1); + elfHeader.e_ident(llvm::ELF::EI_OSABI, 0); + elfHeader.e_version(1); + elfHeader.e_flags(0x3); +} + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h index c920cdf153aa..ab91e405fd56 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h @@ -10,6 +10,7 @@ #ifndef LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H #define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H +#include "OutputELFWriter.h" #include "lld/ReaderWriter/ELFLinkingContext.h" #include "llvm/Object/ELF.h" #include "llvm/Support/ELF.h" @@ -17,14 +18,13 @@ namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; - class HexagonLinkingContext final : public ELFLinkingContext { public: - static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_HEXAGON; } HexagonLinkingContext(llvm::Triple triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; bool isDynamicRelocation(const Reference &r) const override { if (r.kindNamespace() != Reference::KindNamespace::ELF) @@ -41,12 +41,7 @@ public: bool isPLTRelocation(const Reference &r) const override { if (r.kindNamespace() != Reference::KindNamespace::ELF) return false; - switch (r.kindValue()) { - case llvm::ELF::R_HEX_JMP_SLOT: - return true; - default: - return false; - } + return r.kindValue() == llvm::ELF::R_HEX_JMP_SLOT; } /// \brief Hexagon has only one relative relocation @@ -54,15 +49,12 @@ public: bool isRelativeReloc(const Reference &r) const override { if (r.kindNamespace() != Reference::KindNamespace::ELF) return false; - switch (r.kindValue()) { - case llvm::ELF::R_HEX_RELATIVE: - return true; - default: - return false; - } + return r.kindValue() == llvm::ELF::R_HEX_RELATIVE; } }; +void setHexagonELFHeader(ELFHeader<ELF32LE> &elfHeader); + } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h deleted file mode 100644 index 2b9e25ce363b..000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h +++ /dev/null @@ -1,49 +0,0 @@ -//===- HexagonRelocationFunction.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_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H -#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H - -namespace lld { -namespace elf { - -/// \brief HexagonInstruction which is used to store various values -typedef struct { - uint32_t insnMask; - uint32_t insnCmpMask; - uint32_t insnBitMask; - bool isDuplex; -} Instruction; - -#include "HexagonEncodings.h" - -#define FINDV4BITMASK(INSN) \ - findBitMask((uint32_t) * ((llvm::support::ulittle32_t *) INSN), \ - insn_encodings, \ - sizeof(insn_encodings) / sizeof(Instruction)) - -/// \brief finds the scatter Bits that need to be used to apply relocations -inline uint32_t -findBitMask(uint32_t insn, Instruction *encodings, int32_t numInsns) { - for (int32_t i = 0; i < numInsns; i++) { - if (((insn & 0xc000) == 0) && !(encodings[i].isDuplex)) - continue; - - if (((insn & 0xc000) != 0) && (encodings[i].isDuplex)) - continue; - - if (((encodings[i].insnMask) & insn) == encodings[i].insnCmpMask) - return encodings[i].insnBitMask; - } - llvm_unreachable("found unknown instruction"); -} - -} // elf -} // lld - -#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp index 21967d356a31..0a201b32b5f1 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp @@ -7,10 +7,10 @@ // //===----------------------------------------------------------------------===// +#include "HexagonEncodings.h" #include "HexagonLinkingContext.h" -#include "HexagonRelocationFunctions.h" -#include "HexagonTargetHandler.h" #include "HexagonRelocationHandler.h" +#include "HexagonTargetHandler.h" #include "llvm/Support/Endian.h" using namespace lld; @@ -18,263 +18,250 @@ using namespace lld::elf; using namespace llvm::ELF; using namespace llvm::support::endian; -#define APPLY_RELOC(result) \ - write32le(location, result | read32le(location)); +// Scatter val's bits as specified by the mask. Example: +// +// Val: 0bABCDEFG +// Mask: 0b10111100001011 +// Output: 0b00ABCD0000E0FG +static uint32_t scatterBits(uint32_t val, uint32_t mask) { + uint32_t result = 0; + size_t off = 0; + for (size_t bit = 0; bit < 32; ++bit) { + if ((mask >> bit) & 1) { + uint32_t valBit = (val >> off) & 1; + result |= valBit << bit; + ++off; + } + } + return result; +} -static int relocBNPCREL(uint8_t *location, uint64_t P, uint64_t S, uint64_t A, - int32_t nBits) { - int32_t result = (uint32_t)(((S + A) - P) >> 2); +static void relocBNPCREL(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + int32_t nBits) { + int32_t result = (S + A - P) >> 2; int32_t range = 1 << nBits; if (result < range && result > -range) { - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - return 1; } /// \brief Word32_LO: 0x00c03fff : (S + A) : Truncate -static int relocLO16(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - uint32_t result = (uint32_t)(S + A); - result = lld::scatterBits<int32_t>(result, 0x00c03fff); - APPLY_RELOC(result); - return 0; +static void relocLO16(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = S + A; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \brief Word32_LO: 0x00c03fff : (S + A) >> 16 : Truncate -static int relocHI16(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - uint32_t result = (uint32_t)((S + A) >> 16); - result = lld::scatterBits<int32_t>(result, 0x00c03fff); - APPLY_RELOC(result); - return 0; +static void relocHI16(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (S + A) >> 16; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \brief Word32: 0xffffffff : (S + A) : Truncate -static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - uint32_t result = (uint32_t)(S + A); - APPLY_RELOC(result); - return 0; +static void reloc32(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = S + A; + write32le(loc, result | read32le(loc)); } -static int reloc32_6_X(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - int64_t result = ((S + A) >> 6); - int64_t range = ((int64_t)1) << 32; +static void reloc32_6_X(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + int64_t result = (S + A) >> 6; + int64_t range = int64_t(1) << 32; if (result > range) - return 1; - result = lld::scatterBits<int32_t>(result, 0xfff3fff); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, 0xfff3fff); + write32le(loc, result | read32le(loc)); } // R_HEX_B32_PCREL_X -static int relocHexB32PCRELX(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A) { - int64_t result = ((S + A - P) >> 6); - result = lld::scatterBits<int32_t>(result, 0xfff3fff); - APPLY_RELOC(result); - return 0; +static void relocHexB32PCRELX(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A) { + int64_t result = (S + A - P) >> 6; + result = scatterBits(result, 0xfff3fff); + write32le(loc, result | read32le(loc)); } // R_HEX_BN_PCREL_X -static int relocHexBNPCRELX(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A, int nbits) { - int32_t result = ((S + A - P) & 0x3f); +static void relocHexBNPCRELX(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + int nbits) { + int32_t result = (S + A - P) & 0x3f; int32_t range = 1 << nbits; if (result < range && result > -range) { - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - return 1; } // R_HEX_6_PCREL_X -static int relocHex6PCRELX(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A) { - int32_t result = (S + A - P); - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; +static void relocHex6PCRELX(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + int32_t result = S + A - P; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } // R_HEX_N_X : Word32_U6 : (S + A) : Unsigned Truncate -static int relocHex_N_X(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - uint32_t result = (S + A); - result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; +static void relocHex_N_X(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = S + A; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -// GP REL relocations -static int relocHexGPRELN(uint8_t *location, uint64_t P, uint64_t S, uint64_t A, - uint64_t GP, int nShiftBits) { - int32_t result = (int64_t)((S + A - GP) >> nShiftBits); +// GP REL relocs +static void relocHexGPRELN(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + uint64_t GP, int nShiftBits) { + int32_t result = (S + A - GP) >> nShiftBits; int32_t range = 1L << 16; if (result <= range) { - result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - return 1; } /// \brief Word32_LO: 0x00c03fff : (G) : Truncate -static int relocHexGOTLO16(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(A-GOT); - result = lld::scatterBits<int32_t>(result, 0x00c03fff); - APPLY_RELOC(result); - return 0; +static void relocHexGOTLO16(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = A - GOT; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \brief Word32_LO: 0x00c03fff : (G) >> 16 : Truncate -static int relocHexGOTHI16(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)((A-GOT) >> 16); - result = lld::scatterBits<int32_t>(result, 0x00c03fff); - APPLY_RELOC(result); - return 0; +static void relocHexGOTHI16(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = (A - GOT) >> 16; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } /// \brief Word32: 0xffffffff : (G) : Truncate -static int relocHexGOT32(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(GOT - A); - APPLY_RELOC(result); - return 0; +static void relocHexGOT32(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = GOT - A; + write32le(loc, result | read32le(loc)); } /// \brief Word32_U16 : (G) : Truncate -static int relocHexGOT16(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(GOT-A); +static void relocHexGOT16(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = GOT - A; int32_t range = 1L << 16; if (result <= range) { - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - return 1; } -static int relocHexGOT32_6_X(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)((A-GOT) >> 6); - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; +static void relocHexGOT32_6_X(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = (A - GOT) >> 6; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -static int relocHexGOT16_X(uint8_t *location, uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(A-GOT); +static void relocHexGOT16_X(uint8_t *loc, uint64_t A, uint64_t GOT) { + int32_t result = A - GOT; int32_t range = 1L << 6; if (result <= range) { - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } - return 1; } -static int relocHexGOT11_X(uint8_t *location, uint64_t A, uint64_t GOT) { - uint32_t result = (uint32_t)(A-GOT); - result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; +static void relocHexGOT11_X(uint8_t *loc, uint64_t A, uint64_t GOT) { + uint32_t result = A - GOT; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -static int relocHexGOTRELSigned(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A, uint64_t GOT, int shiftBits = 0) { - int32_t result = (int32_t)((S + A - GOT) >> shiftBits); - result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; +static void relocHexGOTRELSigned(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits) { + int32_t result = (S + A - GOT) >> shiftBits; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -static int relocHexGOTRELUnsigned(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A, uint64_t GOT, int shiftBits = 0) { - uint32_t result = (uint32_t)((S + A - GOT) >> shiftBits); - result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); - APPLY_RELOC(result); - return 0; +static void relocHexGOTRELUnsigned(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT) { + uint32_t result = S + A - GOT; + result = scatterBits(result, findv4bitmask(loc)); + write32le(loc, result | read32le(loc)); } -static int relocHexGOTREL_HILO16(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A, uint64_t GOT, int shiftBits = 0) { - int32_t result = (int32_t)((S + A - GOT) >> shiftBits); - result = lld::scatterBits<int32_t>(result, 0x00c03fff); - APPLY_RELOC(result); - return 0; +static void relocHexGOTREL_HILO16(uint8_t *loc, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits) { + int32_t result = (S + A - GOT) >> shiftBits; + result = scatterBits(result, 0x00c03fff); + write32le(loc, result | read32le(loc)); } -static int relocHexGOTREL_32(uint8_t *location, uint64_t P, uint64_t S, - uint64_t A, uint64_t GOT) { - int32_t result = (int32_t)(S + A - GOT); - APPLY_RELOC(result); - return 0; +static void relocHexGOTREL_32(uint8_t *loc, uint64_t P, uint64_t S, uint64_t A, + uint64_t GOT) { + int32_t result = S + A - GOT; + write32le(loc, result | read32le(loc)); } std::error_code HexagonTargetRelocationHandler::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::Hexagon); switch (ref.kindValue()) { case R_HEX_B22_PCREL: - relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 21); + relocBNPCREL(loc, reloc, target, ref.addend(), 21); break; case R_HEX_B15_PCREL: - relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 14); + relocBNPCREL(loc, reloc, target, ref.addend(), 14); break; case R_HEX_B9_PCREL: - relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 8); + relocBNPCREL(loc, reloc, target, ref.addend(), 8); break; case R_HEX_LO16: - relocLO16(location, relocVAddress, targetVAddress, ref.addend()); + relocLO16(loc, reloc, target, ref.addend()); break; case R_HEX_HI16: - relocHI16(location, relocVAddress, targetVAddress, ref.addend()); + relocHI16(loc, reloc, target, ref.addend()); break; case R_HEX_32: - reloc32(location, relocVAddress, targetVAddress, ref.addend()); + reloc32(loc, reloc, target, ref.addend()); break; case R_HEX_32_6_X: - reloc32_6_X(location, relocVAddress, targetVAddress, ref.addend()); + reloc32_6_X(loc, reloc, target, ref.addend()); break; case R_HEX_B32_PCREL_X: - relocHexB32PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + relocHexB32PCRELX(loc, reloc, target, ref.addend()); break; case R_HEX_B22_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 21); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 21); break; case R_HEX_B15_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 14); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 14); break; case R_HEX_B13_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 12); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 12); break; case R_HEX_B9_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 8); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 8); break; case R_HEX_B7_PCREL_X: - relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 6); + relocHexBNPCRELX(loc, reloc, target, ref.addend(), 6); break; case R_HEX_GPREL16_0: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 0); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 0); break; case R_HEX_GPREL16_1: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 1); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 1); break; case R_HEX_GPREL16_2: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 2); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 2); break; case R_HEX_GPREL16_3: - relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getSDataSection()->virtualAddr(), 3); + relocHexGPRELN(loc, reloc, target, ref.addend(), + _targetLayout.getSDataSection()->virtualAddr(), 3); break; case R_HEX_16_X: case R_HEX_12_X: @@ -284,62 +271,55 @@ std::error_code HexagonTargetRelocationHandler::applyRelocation( case R_HEX_8_X: case R_HEX_7_X: case R_HEX_6_X: - relocHex_N_X(location, relocVAddress, targetVAddress, ref.addend()); + relocHex_N_X(loc, reloc, target, ref.addend()); break; case R_HEX_6_PCREL_X: - relocHex6PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + relocHex6PCRELX(loc, reloc, target, ref.addend()); break; case R_HEX_JMP_SLOT: case R_HEX_GLOB_DAT: break; case R_HEX_GOTREL_32: - relocHexGOTREL_32(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTREL_32(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr()); break; case R_HEX_GOTREL_LO16: - relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTREL_HILO16(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr(), 0); break; case R_HEX_GOTREL_HI16: - relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr(), 16); + relocHexGOTREL_HILO16(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr(), 16); break; case R_HEX_GOT_LO16: - relocHexGOTLO16(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTLO16(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_HI16: - relocHexGOTHI16(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTHI16(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_32: - relocHexGOT32(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT32(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_16: - relocHexGOT16(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT16(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_32_6_X: - relocHexGOT32_6_X(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT32_6_X(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_16_X: - relocHexGOT16_X(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT16_X(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOT_11_X: - relocHexGOT11_X(location, targetVAddress, - _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOT11_X(loc, target, _targetLayout.getGOTSymAddr()); break; case R_HEX_GOTREL_32_6_X: - relocHexGOTRELSigned(location, relocVAddress, targetVAddress, ref.addend(), - _hexagonTargetLayout.getGOTSymAddr(), 6); + relocHexGOTRELSigned(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr(), 6); break; case R_HEX_GOTREL_16_X: case R_HEX_GOTREL_11_X: - relocHexGOTRELUnsigned(location, relocVAddress, targetVAddress, - ref.addend(), _hexagonTargetLayout.getGOTSymAddr()); + relocHexGOTRELUnsigned(loc, reloc, target, ref.addend(), + _targetLayout.getGOTSymAddr()); break; default: diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h index 4795d0264b9c..6afba0ddb8f3 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h @@ -9,26 +9,24 @@ #ifndef LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_HANDLER_H #define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_HANDLER_H -#include "HexagonSectionChunks.h" -#include "HexagonTargetHandler.h" -#include "lld/ReaderWriter/RelocationHelperFunctions.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { - class HexagonTargetHandler; +class HexagonTargetLayout; class HexagonTargetRelocationHandler final : public TargetRelocationHandler { public: - HexagonTargetRelocationHandler(HexagonTargetLayout<HexagonELFType> &layout) - : _hexagonTargetLayout(layout) {} + HexagonTargetRelocationHandler(HexagonTargetLayout &layout) + : _targetLayout(layout) {} std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; private: - HexagonTargetLayout<HexagonELFType> &_hexagonTargetLayout; + HexagonTargetLayout &_targetLayout; }; } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h b/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h deleted file mode 100644 index 5b3fbbbd899b..000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h +++ /dev/null @@ -1,86 +0,0 @@ -//===- lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h-----------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#ifndef HEXAGON_SECTION_CHUNKS_H -#define HEXAGON_SECTION_CHUNKS_H - -#include "HexagonTargetHandler.h" - -namespace lld { -namespace elf { -template <typename ELFT> class HexagonTargetLayout; -class HexagonLinkingContext; - -/// \brief Handle Hexagon SData section -template <class HexagonELFType> -class SDataSection : public AtomSection<HexagonELFType> { -public: - SDataSection(const HexagonLinkingContext &context) - : AtomSection<HexagonELFType>( - context, ".sdata", DefinedAtom::typeDataFast, 0, - HexagonTargetLayout<HexagonELFType>::ORDER_SDATA) { - this->_type = SHT_PROGBITS; - this->_flags = SHF_ALLOC | SHF_WRITE; - this->_alignment = 4096; - } - - /// \brief Finalize the section contents before writing - virtual void doPreFlight(); - - /// \brief Does this section have an output segment. - virtual bool hasOutputSegment() { return true; } - - const lld::AtomLayout *appendAtom(const Atom *atom) { - const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); - DefinedAtom::Alignment atomAlign = definedAtom->alignment(); - uint64_t alignment = 1u << atomAlign.powerOf2; - this->_atoms.push_back(new (this->_alloc) lld::AtomLayout(atom, 0, 0)); - // Set the section alignment to the largest alignment - // std::max doesn't support uint64_t - if (this->_alignment < alignment) - this->_alignment = alignment; - return (this->_atoms.back()); - } - -}; // SDataSection - -template <class HexagonELFType> -void SDataSection<HexagonELFType>::doPreFlight() { - // sort the atoms on the alignments they have been set - std::stable_sort(this->_atoms.begin(), this->_atoms.end(), - [](const lld::AtomLayout * A, - const lld::AtomLayout * B) { - const DefinedAtom *definedAtomA = cast<DefinedAtom>(A->_atom); - const DefinedAtom *definedAtomB = cast<DefinedAtom>(B->_atom); - int64_t alignmentA = 1 << definedAtomA->alignment().powerOf2; - int64_t alignmentB = 1 << definedAtomB->alignment().powerOf2; - if (alignmentA == alignmentB) { - if (definedAtomA->merge() == DefinedAtom::mergeAsTentative) - return false; - if (definedAtomB->merge() == DefinedAtom::mergeAsTentative) - return true; - } - return alignmentA < alignmentB; - }); - - // Set the fileOffset, and the appropriate size of the section - for (auto &ai : this->_atoms) { - const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); - DefinedAtom::Alignment atomAlign = definedAtom->alignment(); - uint64_t fOffset = this->alignOffset(this->fileSize(), atomAlign); - uint64_t mOffset = this->alignOffset(this->memSize(), atomAlign); - ai->_fileOffset = fOffset; - this->_fsize = fOffset + definedAtom->size(); - this->_msize = mOffset + definedAtom->size(); - } -} // finalize - -} // elf -} // lld - -#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_SECTION_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp index 9b10c2f160f4..6c0360c310f4 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp @@ -12,29 +12,23 @@ #include "HexagonLinkingContext.h" #include "HexagonTargetHandler.h" -using namespace lld; -using namespace elf; using namespace llvm::ELF; using llvm::makeArrayRef; -HexagonTargetHandler::HexagonTargetHandler(HexagonLinkingContext &context) - : _hexagonLinkingContext(context), - _hexagonRuntimeFile(new HexagonRuntimeFile<HexagonELFType>(context)), - _hexagonTargetLayout(new HexagonTargetLayout<HexagonELFType>(context)), - _hexagonRelocationHandler(new HexagonTargetRelocationHandler( - *_hexagonTargetLayout.get())) {} +namespace lld { +namespace elf { + +HexagonTargetHandler::HexagonTargetHandler(HexagonLinkingContext &ctx) + : _ctx(ctx), _targetLayout(new HexagonTargetLayout(ctx)), + _relocationHandler(new HexagonTargetRelocationHandler(*_targetLayout)) {} std::unique_ptr<Writer> HexagonTargetHandler::getWriter() { - switch (_hexagonLinkingContext.getOutputELFType()) { + switch (_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new elf::HexagonExecutableWriter<HexagonELFType>( - _hexagonLinkingContext, *_hexagonTargetLayout.get())); + return llvm::make_unique<HexagonExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new elf::HexagonDynamicLibraryWriter<HexagonELFType>( - _hexagonLinkingContext, *_hexagonTargetLayout.get())); + return llvm::make_unique<HexagonDynamicLibraryWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_REL: llvm_unreachable("TODO: support -r mode"); default: @@ -77,7 +71,7 @@ public: return makeArrayRef(hexagonGotAtomContent); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } }; class HexagonGOTPLTAtom : public GOTAtom { @@ -88,7 +82,7 @@ public: return makeArrayRef(hexagonGotPltAtomContent); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } }; class HexagonGOTPLT0Atom : public GOTAtom { @@ -99,7 +93,7 @@ public: return makeArrayRef(hexagonGotPlt0AtomContent); } - Alignment alignment() const override { return Alignment(3); } + Alignment alignment() const override { return 8; } }; class HexagonPLT0Atom : public PLT0Atom { @@ -165,8 +159,7 @@ protected: } public: - GOTPLTPass(const ELFLinkingContext &ctx) - : _file(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr) {} + GOTPLTPass(const ELFLinkingContext &ctx) : _file(ctx) {} /// \brief Do the pass. /// @@ -176,34 +169,36 @@ 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 { // Process all references. - for (const auto &atom : mf->defined()) + for (const auto &atom : mf.defined()) for (const auto &ref : *atom) handleReference(*atom, *ref); // 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 (_got0) { _got0->setOrdinal(ordinal++); - mf->addAtom(*_got0); + mf.addAtom(*_got0); } for (auto &got : _gotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } + + return std::error_code(); } protected: @@ -221,19 +216,19 @@ protected: std::vector<PLTAtom *> _pltVector; /// \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; + PLT0Atom *_plt0 = nullptr; + GOTAtom *_got0 = nullptr; /// @} }; class DynamicGOTPLTPass final : public GOTPLTPass<DynamicGOTPLTPass> { public: - DynamicGOTPLTPass(const elf::HexagonLinkingContext &ctx) : GOTPLTPass(ctx) { + DynamicGOTPLTPass(const HexagonLinkingContext &ctx) : GOTPLTPass(ctx) { _got0 = new (_file._alloc) HexagonGOTPLT0Atom(_file); #ifndef NDEBUG _got0->_name = "__got0"; @@ -241,14 +236,14 @@ public: } const PLT0Atom *getPLT0() { - if (_PLT0) - return _PLT0; - _PLT0 = new (_file._alloc) HexagonPLT0Atom(_file); - _PLT0->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, _got0, 0); - _PLT0->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, _got0, 4); + if (_plt0) + return _plt0; + _plt0 = new (_file._alloc) HexagonPLT0Atom(_file); + _plt0->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, _got0, 0); + _plt0->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, _got0, 4); DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[ PLT0/GOT0 ] " << "Adding plt0/got0 \n"); - return _PLT0; + return _plt0; } const PLTAtom *getPLTEntry(const Atom *a) { @@ -313,22 +308,75 @@ public: } }; -void elf::HexagonLinkingContext::addPasses(PassManager &pm) { +void HexagonLinkingContext::addPasses(PassManager &pm) { if (isDynamic()) pm.add(llvm::make_unique<DynamicGOTPLTPass>(*this)); ELFLinkingContext::addPasses(pm); } -void HexagonTargetHandler::registerRelocationNames(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, - Reference::KindArch::Hexagon, kindStrings); +void SDataSection::doPreFlight() { + // sort the atoms on the alignments they have been set + std::stable_sort(_atoms.begin(), _atoms.end(), [](const AtomLayout *A, + const AtomLayout *B) { + const DefinedAtom *definedAtomA = cast<DefinedAtom>(A->_atom); + const DefinedAtom *definedAtomB = cast<DefinedAtom>(B->_atom); + int64_t alignmentA = definedAtomA->alignment().value; + int64_t alignmentB = definedAtomB->alignment().value; + if (alignmentA == alignmentB) { + if (definedAtomA->merge() == DefinedAtom::mergeAsTentative) + return false; + if (definedAtomB->merge() == DefinedAtom::mergeAsTentative) + return true; + } + return alignmentA < alignmentB; + }); + + // Set the fileOffset, and the appropriate size of the section + for (auto &ai : _atoms) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t fOffset = alignOffset(fileSize(), atomAlign); + uint64_t mOffset = alignOffset(memSize(), atomAlign); + ai->_fileOffset = fOffset; + _fsize = fOffset + definedAtom->size(); + _msize = mOffset + definedAtom->size(); + } +} // finalize + +SDataSection::SDataSection(const HexagonLinkingContext &ctx) + : AtomSection(ctx, ".sdata", DefinedAtom::typeDataFast, 0, + HexagonTargetLayout::ORDER_SDATA) { + _type = SHT_PROGBITS; + _flags = SHF_ALLOC | SHF_WRITE; + _alignment = 4096; } -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +const AtomLayout *SDataSection::appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = atomAlign.value; + _atoms.push_back(new (_alloc) AtomLayout(atom, 0, 0)); + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (_alignment < alignment) + _alignment = alignment; + return _atoms.back(); +} -const Registry::KindStrings HexagonTargetHandler::kindStrings[] = { -#include "llvm/Support/ELFRelocs/Hexagon.def" - LLD_KIND_STRING_END -}; +void finalizeHexagonRuntimeAtomValues(HexagonTargetLayout &layout) { + AtomLayout *gotAtom = layout.findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + OutputSection<ELF32LE> *gotpltSection = layout.findOutputSection(".got.plt"); + if (gotpltSection) + gotAtom->_virtualAddr = gotpltSection->virtualAddr(); + else + gotAtom->_virtualAddr = 0; + AtomLayout *dynamicAtom = layout.findAbsoluteAtom("_DYNAMIC"); + OutputSection<ELF32LE> *dynamicSection = layout.findOutputSection(".dynamic"); + if (dynamicSection) + dynamicAtom->_virtualAddr = dynamicSection->virtualAddr(); + else + dynamicAtom->_virtualAddr = 0; +} -#undef ELF_RELOC +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h index f4315f710ec7..b1366bed09ea 100644 --- a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h @@ -10,44 +10,51 @@ #ifndef HEXAGON_TARGET_HANDLER_H #define HEXAGON_TARGET_HANDLER_H -#include "DefaultTargetHandler.h" -#include "HexagonELFReader.h" -#include "HexagonExecutableAtoms.h" +#include "ELFReader.h" +#include "HexagonELFFile.h" #include "HexagonRelocationHandler.h" -#include "HexagonSectionChunks.h" #include "TargetLayout.h" namespace lld { namespace elf { class HexagonLinkingContext; +/// \brief Handle Hexagon SData section +class SDataSection : public AtomSection<ELF32LE> { +public: + SDataSection(const HexagonLinkingContext &ctx); + + /// \brief Finalize the section contents before writing + void doPreFlight() override; + + /// \brief Does this section have an output segment. + bool hasOutputSegment() const override { return true; } + + const AtomLayout *appendAtom(const Atom *atom) override; +}; + /// \brief TargetLayout for Hexagon -template <class HexagonELFType> -class HexagonTargetLayout final : public TargetLayout<HexagonELFType> { +class HexagonTargetLayout final : public TargetLayout<ELF32LE> { public: enum HexagonSectionOrder { ORDER_SDATA = 205 }; - HexagonTargetLayout(HexagonLinkingContext &hti) - : TargetLayout<HexagonELFType>(hti), _sdataSection(nullptr), - _gotSymAtom(nullptr), _cachedGotSymAtom(false) { - _sdataSection = new (_alloc) SDataSection<HexagonELFType>(hti); - } + HexagonTargetLayout(HexagonLinkingContext &ctx) + : TargetLayout(ctx), _sdataSection(ctx) {} /// \brief Return the section order for a input section - virtual Layout::SectionOrder getSectionOrder( - StringRef name, int32_t contentType, int32_t contentPermissions) { - if ((contentType == DefinedAtom::typeDataFast) || - (contentType == DefinedAtom::typeZeroFillFast)) + TargetLayout::SectionOrder + getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override { + if (contentType == DefinedAtom::typeDataFast || + contentType == DefinedAtom::typeZeroFillFast) return ORDER_SDATA; - - return DefaultLayout<HexagonELFType>::getSectionOrder(name, contentType, - contentPermissions); + return TargetLayout::getSectionOrder(name, contentType, contentPermissions); } /// \brief Return the appropriate input section name. - virtual StringRef getInputSectionName(const DefinedAtom *da) const { + StringRef getInputSectionName(const DefinedAtom *da) const override { switch (da->contentType()) { case DefinedAtom::typeDataFast: case DefinedAtom::typeZeroFillFast: @@ -55,88 +62,72 @@ public: default: break; } - return DefaultLayout<HexagonELFType>::getInputSectionName(da); + return TargetLayout::getInputSectionName(da); } /// \brief Gets or creates a section. - virtual AtomSection<HexagonELFType> * + AtomSection<ELF32LE> * createSection(StringRef name, int32_t contentType, DefinedAtom::ContentPermissions contentPermissions, - Layout::SectionOrder sectionOrder) { - if ((contentType == DefinedAtom::typeDataFast) || - (contentType == DefinedAtom::typeZeroFillFast)) - return _sdataSection; - return DefaultLayout<HexagonELFType>::createSection( - name, contentType, contentPermissions, sectionOrder); + TargetLayout::SectionOrder sectionOrder) override { + if (contentType == DefinedAtom::typeDataFast || + contentType == DefinedAtom::typeZeroFillFast) + return &_sdataSection; + return TargetLayout::createSection(name, contentType, contentPermissions, + sectionOrder); } /// \brief get the segment type for the section thats defined by the target - virtual Layout::SegmentType - getSegmentType(Section<HexagonELFType> *section) const { + TargetLayout::SegmentType + getSegmentType(const Section<ELF32LE> *section) const override { if (section->order() == ORDER_SDATA) return PT_LOAD; - - return DefaultLayout<HexagonELFType>::getSegmentType(section); + return TargetLayout::getSegmentType(section); } - Section<HexagonELFType> *getSDataSection() const { - return _sdataSection; - } + Section<ELF32LE> *getSDataSection() { return &_sdataSection; } uint64_t getGOTSymAddr() { - if (!_cachedGotSymAtom) { - auto gotAtomIter = this->findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); - _gotSymAtom = (*gotAtomIter); - _cachedGotSymAtom = true; - } - if (_gotSymAtom) - return _gotSymAtom->_virtualAddr; - return 0; + std::call_once(_gotOnce, [this]() { + if (AtomLayout *got = findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_")) + _gotAddr = got->_virtualAddr; + }); + return _gotAddr; } private: - llvm::BumpPtrAllocator _alloc; - SDataSection<HexagonELFType> *_sdataSection; - AtomLayout *_gotSymAtom; - bool _cachedGotSymAtom; + SDataSection _sdataSection; + uint64_t _gotAddr = 0; + std::once_flag _gotOnce; }; /// \brief TargetHandler for Hexagon -class HexagonTargetHandler final : - public DefaultTargetHandler<HexagonELFType> { +class HexagonTargetHandler final : public TargetHandler { public: HexagonTargetHandler(HexagonLinkingContext &targetInfo); - void registerRelocationNames(Registry ®istry) override; - - const HexagonTargetRelocationHandler &getRelocationHandler() const override { - return *(_hexagonRelocationHandler.get()); - } - - HexagonTargetLayout<HexagonELFType> &getTargetLayout() override { - return *(_hexagonTargetLayout.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>( - new HexagonELFObjectReader(_hexagonLinkingContext)); + return llvm::make_unique<ELFReader<HexagonELFFile>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>( - new HexagonELFDSOReader(_hexagonLinkingContext)); + return llvm::make_unique<ELFReader<DynamicFile<ELF32LE>>>(_ctx); } std::unique_ptr<Writer> getWriter() override; private: - llvm::BumpPtrAllocator _alloc; - static const Registry::KindStrings kindStrings[]; - HexagonLinkingContext &_hexagonLinkingContext; - std::unique_ptr<HexagonRuntimeFile<HexagonELFType> > _hexagonRuntimeFile; - std::unique_ptr<HexagonTargetLayout<HexagonELFType>> _hexagonTargetLayout; - std::unique_ptr<HexagonTargetRelocationHandler> _hexagonRelocationHandler; + HexagonLinkingContext &_ctx; + std::unique_ptr<HexagonTargetLayout> _targetLayout; + std::unique_ptr<HexagonTargetRelocationHandler> _relocationHandler; }; + +void finalizeHexagonRuntimeAtomValues(HexagonTargetLayout &layout); + } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/Hexagon/Makefile b/lib/ReaderWriter/ELF/Hexagon/Makefile deleted file mode 100644 index 8d6f1a0a3b1e..000000000000 --- a/lib/ReaderWriter/ELF/Hexagon/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/Hexagon/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 := lldHexagonELFTarget -USEDLIBS = lldCore.a - -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/Hexagon -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Layout.h b/lib/ReaderWriter/ELF/Layout.h deleted file mode 100644 index 826cf5035d59..000000000000 --- a/lib/ReaderWriter/ELF/Layout.h +++ /dev/null @@ -1,59 +0,0 @@ -//===- lib/ReaderWriter/ELF/Layout.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_LAYOUT_H -#define LLD_READER_WRITER_ELF_LAYOUT_H - -#include "lld/Core/DefinedAtom.h" -#include "lld/ReaderWriter/AtomLayout.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Object/ELF.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/ELF.h" -#include "llvm/Support/ErrorOr.h" - -namespace lld { -namespace elf { - -/// \brief The ELFLayout is an abstract class for managing the final layout for -/// the kind of binaries(Shared Libraries / Relocatables / Executables 0 -/// Each architecture (Hexagon, MIPS) would have a concrete -/// subclass derived from Layout for generating each binary thats -// needed by the lld linker -class Layout { -public: - typedef uint32_t SectionOrder; - typedef uint32_t SegmentType; - typedef uint32_t Flags; - -public: - /// Return the order the section would appear in the output file - virtual SectionOrder getSectionOrder(StringRef name, int32_t contentType, - int32_t contentPerm) = 0; - /// \brief Append the Atom to the layout and create appropriate sections. - /// \returns A reference to the atom layout or an error. The atom layout will - /// be updated as linking progresses. - virtual ErrorOr<const lld::AtomLayout *> addAtom(const Atom *atom) = 0; - /// find the Atom in the current layout - virtual const AtomLayout *findAtomLayoutByName(StringRef name) const = 0; - /// associates a section to a segment - virtual void assignSectionsToSegments() = 0; - /// associates a virtual address to the segment, section, and the atom - virtual void assignVirtualAddress() = 0; - -public: - Layout() {} - - virtual ~Layout() { } -}; -} // end namespace elf -} // end namespace lld - -#endif diff --git a/lib/ReaderWriter/ELF/Makefile b/lib/ReaderWriter/ELF/Makefile deleted file mode 100644 index 5791ecb9733d..000000000000 --- a/lib/ReaderWriter/ELF/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/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 := lldELF - -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -# these link against this lib -PARALLEL_DIRS := Hexagon X86 X86_64 Mips AArch64 ARM - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Mips/CMakeLists.txt b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt index d982508b7ddc..434e310640bd 100644 --- a/lib/ReaderWriter/ELF/Mips/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt @@ -1,10 +1,14 @@ add_llvm_library(lldMipsELFTarget + MipsAbiInfoHandler.cpp MipsCtorsOrderPass.cpp - MipsELFFlagsMerger.cpp + MipsELFFile.cpp + MipsELFWriters.cpp MipsLinkingContext.cpp MipsRelocationHandler.cpp MipsRelocationPass.cpp + MipsSectionChunks.cpp MipsTargetHandler.cpp + MipsTargetLayout.cpp LINK_LIBS lldELF lldReaderWriter diff --git a/lib/ReaderWriter/ELF/Mips/Makefile b/lib/ReaderWriter/ELF/Mips/Makefile deleted file mode 100644 index 0b2f4ff82279..000000000000 --- a/lib/ReaderWriter/ELF/Mips/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/Mips/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 := lldMipsELFTarget -USEDLIBS = lldCore.a -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp new file mode 100644 index 000000000000..ad4e62e64680 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp @@ -0,0 +1,675 @@ +//===- lib/ReaderWriter/ELF/MipsAbiInfoHandler.cpp ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsAbiInfoHandler.h" +#include "lld/Core/Error.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/MipsABIFlags.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::Mips; + +namespace { + +// The joined set of MIPS ISAs and MIPS ISA extensions. +enum MipsISAs { + ArchNone, + + // General ISAs + Arch1, + Arch2, + Arch3, + Arch4, + Arch5, + Arch32, + Arch32r2, + Arch32r3, + Arch32r5, + Arch32r6, + Arch64, + Arch64r2, + Arch64r3, + Arch64r5, + Arch64r6, + + // CPU specific ISAs + Arch3900, + Arch4010, + Arch4100, + Arch4111, + Arch4120, + Arch4650, + Arch5400, + Arch5500, + Arch5900, + Arch9000, + Arch10000, + ArchLs2e, + ArchLs2f, + ArchLs3a, + ArchOcteon, + ArchOcteonP, + ArchOcteon2, + ArchOcteon3, + ArchSB1, + ArchXLR +}; + +struct MipsISATreeEdge { + MipsISAs child; + MipsISAs parent; +}; + +struct ElfArchPair { + uint32_t _elfFlag; + MipsISAs _arch; +}; + +struct AbiIsaArchPair { + uint8_t _isaLevel; + uint8_t _isaRev; + uint8_t _isaExt; + MipsISAs _arch; +}; +} + +static const MipsISATreeEdge isaTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + + // MIPS64R2 extensions. + {ArchOcteon3, ArchOcteon2}, + {ArchOcteon2, ArchOcteonP}, + {ArchOcteonP, ArchOcteon}, + {ArchOcteon, Arch64r2}, + {ArchLs3a, Arch64r2}, + + // MIPS64 extensions. + {Arch64r2, Arch64}, + {ArchSB1, Arch64}, + {ArchXLR, Arch64}, + + // MIPS V extensions. + {Arch64, Arch5}, + + // R5000 extensions. + {Arch5500, Arch5400}, + + // MIPS IV extensions. + {Arch5, Arch4}, + {Arch5400, Arch4}, + {Arch9000, Arch4}, + + // VR4100 extensions. + {Arch4120, Arch4100}, + {Arch4111, Arch4100}, + + // MIPS III extensions. + {ArchLs2e, Arch3}, + {ArchLs2f, Arch3}, + {Arch4650, Arch3}, + {Arch4100, Arch3}, + {Arch4010, Arch3}, + {Arch5900, Arch3}, + {Arch4, Arch3}, + + // MIPS32 extensions. + {Arch32r2, Arch32}, + + // MIPS II extensions. + {Arch3, Arch2}, + {Arch32, Arch2}, + + // MIPS I extensions. + {Arch3900, Arch1}, + {Arch2, Arch1}, +}; + +// Conversion ELF arch flags => MipsISAs +static const ElfArchPair elfArchPairs[] = { + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, Arch3900}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, Arch4010}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, Arch4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, Arch4111}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, Arch4120}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, Arch4650}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, Arch5400}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, Arch5500}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, Arch5900}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, Arch9000}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, ArchLs2e}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, ArchLs2f}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, ArchLs3a}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteon}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, ArchOcteon2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, ArchOcteon3}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchSB1}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, ArchXLR}, + {EF_MIPS_ARCH_1, Arch1}, + {EF_MIPS_ARCH_2, Arch2}, + {EF_MIPS_ARCH_3, Arch3}, + {EF_MIPS_ARCH_4, Arch4}, + {EF_MIPS_ARCH_5, Arch5}, + {EF_MIPS_ARCH_32, Arch32}, + {EF_MIPS_ARCH_32R2, Arch32r2}, + {EF_MIPS_ARCH_32R6, Arch32r6}, + {EF_MIPS_ARCH_64, Arch64}, + {EF_MIPS_ARCH_64R2, Arch64r2}, + {EF_MIPS_ARCH_64R6, Arch64r6} +}; + +// Conversion MipsISAs => ELF arch flags +static const ElfArchPair archElfPairs[] = { + {EF_MIPS_ARCH_1, Arch1}, + {EF_MIPS_ARCH_2, Arch2}, + {EF_MIPS_ARCH_3, Arch3}, + {EF_MIPS_ARCH_4, Arch4}, + {EF_MIPS_ARCH_5, Arch5}, + {EF_MIPS_ARCH_32, Arch32}, + {EF_MIPS_ARCH_32R2, Arch32r2}, + {EF_MIPS_ARCH_32R2, Arch32r3}, + {EF_MIPS_ARCH_32R2, Arch32r5}, + {EF_MIPS_ARCH_32R6, Arch32r6}, + {EF_MIPS_ARCH_64, Arch64}, + {EF_MIPS_ARCH_64R2, Arch64r2}, + {EF_MIPS_ARCH_64R2, Arch64r3}, + {EF_MIPS_ARCH_64R2, Arch64r5}, + {EF_MIPS_ARCH_64R6, Arch64r6}, + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, Arch3900}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, Arch4010}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, Arch4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, Arch4111}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, Arch4120}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, Arch4650}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, Arch5400}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, Arch5500}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, Arch5900}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, Arch9000}, + {EF_MIPS_ARCH_4, Arch10000}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, ArchLs2e}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, ArchLs2f}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, ArchLs3a}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteon}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteonP}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, ArchOcteon2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, ArchOcteon3}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchSB1}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchXLR} +}; + +// Conversion .MIPS.abiflags isa/level/extension <=> MipsISAs +static const AbiIsaArchPair abiIsaArchPair[] = { + { 0, 0, 0, ArchNone}, + { 1, 0, 0, Arch1}, + { 2, 0, 0, Arch2}, + { 3, 0, 0, Arch3}, + { 4, 0, 0, Arch4}, + { 5, 0, 0, Arch5}, + {32, 1, 0, Arch32}, + {32, 2, 0, Arch32r2}, + {32, 3, 0, Arch32r3}, + {32, 5, 0, Arch32r5}, + {32, 6, 0, Arch32r6}, + {64, 1, 0, Arch64}, + {64, 2, 0, Arch64r2}, + {64, 3, 0, Arch64r3}, + {64, 5, 0, Arch64r5}, + {64, 6, 0, Arch64r6}, + { 1, 0, AFL_EXT_3900, Arch3900}, + { 3, 0, AFL_EXT_4010, Arch4010}, + { 3, 0, AFL_EXT_4100, Arch4100}, + { 3, 0, AFL_EXT_4111, Arch4111}, + { 3, 0, AFL_EXT_4120, Arch4120}, + { 3, 0, AFL_EXT_4650, Arch4650}, + { 4, 0, AFL_EXT_5400, Arch5400}, + { 4, 0, AFL_EXT_5500, Arch5500}, + { 3, 0, AFL_EXT_5900, Arch5900}, + { 4, 0, AFL_EXT_10000, Arch10000}, + { 3, 0, AFL_EXT_LOONGSON_2E, ArchLs2e}, + { 3, 0, AFL_EXT_LOONGSON_2F, ArchLs2f}, + {64, 2, AFL_EXT_LOONGSON_3A, ArchLs3a}, + {64, 2, AFL_EXT_OCTEON, ArchOcteon}, + {64, 2, AFL_EXT_OCTEON2, ArchOcteon2}, + {64, 2, AFL_EXT_OCTEON3, ArchOcteon3}, + {64, 1, AFL_EXT_SB1, ArchSB1}, + {64, 1, AFL_EXT_XLR, ArchXLR} +}; + +static bool matchMipsISA(MipsISAs base, MipsISAs ext) { + if (base == ext) + return true; + if (base == Arch32 && matchMipsISA(Arch64, ext)) + return true; + if (base == Arch32r2 && matchMipsISA(Arch64r2, ext)) + return true; + for (const auto &edge : isaTree) { + if (ext == edge.child) { + ext = edge.parent; + if (ext == base) + return true; + } + } + return false; +} + +static bool is32BitElfFlags(unsigned flags) { + if (flags & EF_MIPS_32BITMODE) + return true; + + unsigned arch = flags & EF_MIPS_ARCH; + if (arch == EF_MIPS_ARCH_1 || arch == EF_MIPS_ARCH_2 || + arch == EF_MIPS_ARCH_32 || arch == EF_MIPS_ARCH_32R2 || + arch == EF_MIPS_ARCH_32R6) + return true; + + unsigned abi = flags & EF_MIPS_ABI; + if (abi == EF_MIPS_ABI_O32 || abi == EF_MIPS_ABI_EABI32) + return true; + + return false; +} + +static ErrorOr<MipsISAs> headerFlagsToIsa(uint32_t flags) { + uint32_t arch = flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + for (const auto &p : elfArchPairs) + if (p._elfFlag == arch) + return p._arch; + return make_dynamic_error_code( + StringRef("Unknown EF_MIPS_ARCH | EF_MIPS_MACH flags (0x") + + Twine::utohexstr(arch) + ")"); +} + +static uint32_t isaToHeaderFlags(unsigned isa) { + for (const auto &p : archElfPairs) + if (p._arch == isa) + return p._elfFlag; + llvm_unreachable("Unknown MIPS ISA"); +} + +static ErrorOr<uint32_t> flagsToAses(uint32_t flags) { + uint32_t ases = flags & EF_MIPS_ARCH_ASE; + switch (ases) { + case 0: + return 0; + case EF_MIPS_MICROMIPS: + return AFL_ASE_MICROMIPS; + case EF_MIPS_ARCH_ASE_M16: + return AFL_ASE_MIPS16; + case EF_MIPS_ARCH_ASE_MDMX: + return AFL_ASE_MDMX; + default: + return make_dynamic_error_code( + StringRef("Unknown EF_MIPS_ARCH_ASE flag (0x") + + Twine::utohexstr(ases) + ")"); + } +} + +static uint32_t asesToFlags(uint32_t ases) { + switch (ases) { + case AFL_ASE_MICROMIPS: + return EF_MIPS_MICROMIPS; + case AFL_ASE_MIPS16: + return EF_MIPS_ARCH_ASE_M16; + case AFL_ASE_MDMX: + return EF_MIPS_ARCH_ASE_MDMX; + default: + return 0; + } +} + +static ErrorOr<MipsISAs> sectionFlagsToIsa(uint8_t isaLevel, uint8_t isaRev, + uint8_t isaExt) { + for (const auto &p : abiIsaArchPair) + if (p._isaLevel == isaLevel && p._isaRev == isaRev && p._isaExt == isaExt) + return p._arch; + return make_dynamic_error_code( + StringRef("Unknown ISA level/revision/extension ") + Twine(isaLevel) + + "/" + Twine(isaRev) + "/" + Twine(isaExt)); +} + +static std::tuple<uint8_t, uint8_t, uint32_t> isaToSectionFlags(unsigned isa) { + for (const auto &p : abiIsaArchPair) + if (p._arch == isa) + return std::make_tuple(p._isaLevel, p._isaRev, p._isaExt); + llvm_unreachable("Unknown MIPS ISA"); +} + +static bool checkCompatibility(const MipsAbiFlags &hdr, + const MipsAbiFlags &sec) { + uint32_t secIsa = ArchNone; + switch (sec._isa) { + case Arch32r3: + case Arch32r5: + secIsa = Arch32r2; + break; + case Arch64r3: + case Arch64r5: + secIsa = Arch64r2; + break; + default: + secIsa = sec._isa; + break; + } + if (secIsa != hdr._isa) { + llvm::errs() << "inconsistent ISA between .MIPS.abiflags " + "and ELF header e_flags field\n"; + return false; + } + if ((sec._ases & hdr._ases) != hdr._ases) { + llvm::errs() << "inconsistent ASEs between .MIPS.abiflags " + "and ELF header e_flags field\n"; + return false; + } + return true; +} + +static int compareFpAbi(uint32_t fpA, uint32_t fpB) { + if (fpA == fpB) + return 0; + if (fpB == Val_GNU_MIPS_ABI_FP_ANY) + return 1; + if (fpB == Val_GNU_MIPS_ABI_FP_64A && fpA == Val_GNU_MIPS_ABI_FP_64) + return 1; + if (fpB != Val_GNU_MIPS_ABI_FP_XX) + return -1; + if (fpA == Val_GNU_MIPS_ABI_FP_DOUBLE || fpA == Val_GNU_MIPS_ABI_FP_64 || + fpA == Val_GNU_MIPS_ABI_FP_64A) + return 1; + return -1; +} + +static StringRef getFpAbiName(uint32_t fpAbi) { + switch (fpAbi) { + case Val_GNU_MIPS_ABI_FP_ANY: + return "<any>"; + case Val_GNU_MIPS_ABI_FP_DOUBLE: + return "-mdouble-float"; + case Val_GNU_MIPS_ABI_FP_SINGLE: + return "-msingle-float"; + case Val_GNU_MIPS_ABI_FP_SOFT: + return "-msoft-float"; + case Val_GNU_MIPS_ABI_FP_OLD_64: + return "-mips32r2 -mfp64 (old)"; + case Val_GNU_MIPS_ABI_FP_XX: + return "-mfpxx"; + case Val_GNU_MIPS_ABI_FP_64: + return "-mgp32 -mfp64"; + case Val_GNU_MIPS_ABI_FP_64A: + return "-mgp32 -mfp64 -mno-odd-spreg"; + default: + return "<unknown>"; + } +} + +static uint32_t selectFpAbiFlag(uint32_t oldFp, uint32_t newFp) { + if (compareFpAbi(newFp, oldFp) >= 0) + return newFp; + if (compareFpAbi(oldFp, newFp) < 0) + llvm::errs() << "FP ABI " << getFpAbiName(oldFp) << " is incompatible with " + << getFpAbiName(newFp) << "\n"; + return oldFp; +} + +namespace lld { +namespace elf { + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isMicroMips() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_ases & AFL_ASE_MICROMIPS; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isMipsR6() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_isa == Arch32r6 || _abiFlags->_isa == Arch64r6; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isFp64() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_fpAbi == Val_GNU_MIPS_ABI_FP_64 || + _abiFlags->_fpAbi == Val_GNU_MIPS_ABI_FP_64A; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isCPicOnly() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_isCPic && !_abiFlags->_isPic; +} + +template <class ELFT> uint32_t MipsAbiInfoHandler<ELFT>::getFlags() const { + std::lock_guard<std::mutex> lock(_mutex); + uint32_t flags = 0; + if (_abiFlags.hasValue()) { + flags |= isaToHeaderFlags(_abiFlags->_isa); + flags |= asesToFlags(_abiFlags->_ases); + flags |= _abiFlags->_abi; + flags |= _abiFlags->_isPic ? EF_MIPS_PIC : 0u; + flags |= _abiFlags->_isCPic ? EF_MIPS_CPIC : 0u; + flags |= _abiFlags->_isNoReorder ? EF_MIPS_NOREORDER : 0u; + flags |= _abiFlags->_is32BitMode ? EF_MIPS_32BITMODE : 0u; + flags |= _abiFlags->_isNan2008 ? EF_MIPS_NAN2008 : 0u; + } + return flags; +} + +template <class ELFT> +llvm::Optional<typename MipsAbiInfoHandler<ELFT>::Elf_Mips_RegInfo> +MipsAbiInfoHandler<ELFT>::getRegistersMask() const { + std::lock_guard<std::mutex> lock(_mutex); + return _regMask; +} + +template <class ELFT> +llvm::Optional<typename MipsAbiInfoHandler<ELFT>::Elf_Mips_ABIFlags> +MipsAbiInfoHandler<ELFT>::getAbiFlags() const { + std::lock_guard<std::mutex> lock(_mutex); + if (!_hasAbiSection) + return llvm::Optional<Elf_Mips_ABIFlags>(); + + Elf_Mips_ABIFlags sec; + sec.version = 0; + std::tie(sec.isa_level, sec.isa_rev, sec.isa_ext) = + isaToSectionFlags(_abiFlags->_isa); + sec.gpr_size = _abiFlags->_gprSize; + sec.cpr1_size = _abiFlags->_cpr1Size; + sec.cpr2_size = _abiFlags->_cpr2Size; + sec.fp_abi = _abiFlags->_fpAbi; + sec.ases = _abiFlags->_ases; + sec.flags1 = _abiFlags->_flags1; + sec.flags2 = 0; + return sec; +} + +template <class ELFT> MipsAbi MipsAbiInfoHandler<ELFT>::getAbi() const { + if (!_abiFlags.hasValue()) + return ELFT::Is64Bits ? MipsAbi::N64 : MipsAbi::O32; + switch (_abiFlags->_abi & (EF_MIPS_ABI_O32 | EF_MIPS_ABI2)) { + case EF_MIPS_ABI_O32: + return MipsAbi::O32; + case EF_MIPS_ABI2: + return MipsAbi::N32; + case 0: + return MipsAbi::N64; + default: + llvm_unreachable("Unknown ABI flag"); + } +} + +template <class ELFT> +std::error_code +MipsAbiInfoHandler<ELFT>::mergeFlags(uint32_t newFlags, + const Elf_Mips_ABIFlags *newSec) { + std::lock_guard<std::mutex> lock(_mutex); + + ErrorOr<MipsAbiFlags> abiFlags = createAbiFlags(newFlags, newSec); + if (auto ec = abiFlags.getError()) + return ec; + + // We support three ABI: O32, N32, and N64. The last one does not have + // the corresponding ELF flag. + if (ELFT::Is64Bits) { + if (abiFlags->_abi) + return make_dynamic_error_code("Unsupported ABI"); + } else { + if (!(abiFlags->_abi & (EF_MIPS_ABI_O32 | EF_MIPS_ABI2))) + return make_dynamic_error_code("Unsupported ABI"); + } + + // ... and still do not support MIPS-16 extension. + if (abiFlags->_ases & AFL_ASE_MIPS16) + return make_dynamic_error_code("Unsupported extension: MIPS16"); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + // Ensure that this flag will exist in the linked file. + if (abiFlags->_isPic) + abiFlags->_isCPic = true; + + // If the old set of flags is empty, use the new one as a result. + if (!_abiFlags.hasValue()) { + _abiFlags = *abiFlags; + return std::error_code(); + } + + // Check ABI compatibility. + if (abiFlags->_abi != _abiFlags->_abi) + return make_dynamic_error_code("Linking modules with incompatible ABI"); + + // Check PIC / CPIC flags compatibility. + if (abiFlags->_isCPic != _abiFlags->_isCPic) + llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n"; + + if (!abiFlags->_isPic) + _abiFlags->_isPic = false; + if (abiFlags->_isCPic) + _abiFlags->_isCPic = true; + + // Check mixing -mnan=2008 / -mnan=legacy modules. + if (abiFlags->_isNan2008 != _abiFlags->_isNan2008) + return make_dynamic_error_code( + "Linking -mnan=2008 and -mnan=legacy modules"); + + // Check ISA compatibility and update the extension flag. + if (!matchMipsISA(MipsISAs(abiFlags->_isa), MipsISAs(_abiFlags->_isa))) { + if (!matchMipsISA(MipsISAs(_abiFlags->_isa), MipsISAs(abiFlags->_isa))) + return make_dynamic_error_code("Linking modules with incompatible ISA"); + _abiFlags->_isa = abiFlags->_isa; + } + + _abiFlags->_ases |= abiFlags->_ases; + _abiFlags->_isNoReorder = _abiFlags->_isNoReorder || abiFlags->_isNoReorder; + _abiFlags->_is32BitMode = _abiFlags->_is32BitMode || abiFlags->_is32BitMode; + + _abiFlags->_fpAbi = selectFpAbiFlag(_abiFlags->_fpAbi, abiFlags->_fpAbi); + _abiFlags->_gprSize = std::max(_abiFlags->_gprSize, abiFlags->_gprSize); + _abiFlags->_cpr1Size = std::max(_abiFlags->_cpr1Size, abiFlags->_cpr1Size); + _abiFlags->_cpr2Size = std::max(_abiFlags->_cpr2Size, abiFlags->_cpr2Size); + _abiFlags->_flags1 |= abiFlags->_flags1; + + return std::error_code(); +} + +template <class ELFT> +void MipsAbiInfoHandler<ELFT>::mergeRegistersMask( + const Elf_Mips_RegInfo &info) { + std::lock_guard<std::mutex> lock(_mutex); + if (!_regMask.hasValue()) { + _regMask = info; + return; + } + _regMask->ri_gprmask = _regMask->ri_gprmask | info.ri_gprmask; + _regMask->ri_cprmask[0] = _regMask->ri_cprmask[0] | info.ri_cprmask[0]; + _regMask->ri_cprmask[1] = _regMask->ri_cprmask[1] | info.ri_cprmask[1]; + _regMask->ri_cprmask[2] = _regMask->ri_cprmask[2] | info.ri_cprmask[2]; + _regMask->ri_cprmask[3] = _regMask->ri_cprmask[3] | info.ri_cprmask[3]; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFlags(uint32_t flags, + const Elf_Mips_ABIFlags *sec) { + ErrorOr<MipsAbiFlags> hdrFlags = createAbiFromHeaderFlags(flags); + if (auto ec = hdrFlags.getError()) + return ec; + if (!sec) + return *hdrFlags; + ErrorOr<MipsAbiFlags> secFlags = createAbiFromSection(*sec); + if (auto ec = secFlags.getError()) + return ec; + if (!checkCompatibility(*hdrFlags, *secFlags)) + return *hdrFlags; + + _hasAbiSection = true; + + secFlags->_abi = hdrFlags->_abi; + secFlags->_isPic = hdrFlags->_isPic; + secFlags->_isCPic = hdrFlags->_isCPic; + secFlags->_isNoReorder = hdrFlags->_isNoReorder; + secFlags->_is32BitMode = hdrFlags->_is32BitMode; + secFlags->_isNan2008 = hdrFlags->_isNan2008; + return *secFlags; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFromHeaderFlags(uint32_t flags) { + MipsAbiFlags abi; + ErrorOr<MipsISAs> isa = headerFlagsToIsa(flags); + if (auto ec = isa.getError()) + return ec; + abi._isa = *isa; + + abi._fpAbi = Val_GNU_MIPS_ABI_FP_ANY; + abi._cpr1Size = AFL_REG_NONE; + abi._cpr2Size = AFL_REG_NONE; + abi._gprSize = is32BitElfFlags(flags) ? AFL_REG_32 : AFL_REG_64; + + ErrorOr<uint32_t> ases = flagsToAses(flags); + if (auto ec = ases.getError()) + return ec; + abi._ases = *ases; + abi._flags1 = 0; + abi._abi = flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + abi._isPic = flags & EF_MIPS_PIC; + abi._isCPic = flags & EF_MIPS_CPIC; + abi._isNoReorder = flags & EF_MIPS_NOREORDER; + abi._is32BitMode = flags & EF_MIPS_32BITMODE; + abi._isNan2008 = flags & EF_MIPS_NAN2008; + return abi; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFromSection(const Elf_Mips_ABIFlags &sec) { + MipsAbiFlags abi; + ErrorOr<MipsISAs> isa = + sectionFlagsToIsa(sec.isa_level, sec.isa_rev, sec.isa_ext); + if (auto ec = isa.getError()) + return ec; + abi._isa = *isa; + abi._fpAbi = sec.fp_abi; + abi._cpr1Size = sec.cpr1_size; + abi._cpr2Size = sec.cpr2_size; + abi._gprSize = sec.gpr_size; + abi._ases = sec.ases; + abi._flags1 = sec.flags1; + if (sec.flags2 != 0) + return make_dynamic_error_code("unexpected non-zero 'flags2' value"); + return abi; +} + +template class MipsAbiInfoHandler<ELF32BE>; +template class MipsAbiInfoHandler<ELF32LE>; +template class MipsAbiInfoHandler<ELF64BE>; +template class MipsAbiInfoHandler<ELF64LE>; + +} +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.h b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.h new file mode 100644 index 000000000000..44da29f09214 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.h @@ -0,0 +1,83 @@ +//===- lib/ReaderWriter/ELF/MipsAbiInfoHandler.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_MIPS_MIPS_ABI_INFO_HANDLER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ABI_INFO_HANDLER_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/ErrorOr.h" +#include <mutex> +#include <system_error> + +namespace lld { +namespace elf { + +enum class MipsAbi { O32, N32, N64 }; + +struct MipsAbiFlags { + unsigned _isa = 0; + unsigned _fpAbi = 0; + unsigned _ases = 0; + unsigned _flags1 = 0; + unsigned _gprSize = 0; + unsigned _cpr1Size = 0; + unsigned _cpr2Size = 0; + + unsigned _abi = 0; + + bool _isPic = false; + bool _isCPic = false; + bool _isNoReorder = false; + bool _is32BitMode = false; + bool _isNan2008 = false; +}; + +template <class ELFT> class MipsAbiInfoHandler { +public: + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags; + + MipsAbiInfoHandler() = default; + + bool hasMipsAbiSection() const { return _hasAbiSection; } + bool isMicroMips() const; + bool isMipsR6() const; + bool isFp64() const; + bool isCPicOnly() const; + + uint32_t getFlags() const; + llvm::Optional<Elf_Mips_RegInfo> getRegistersMask() const; + llvm::Optional<Elf_Mips_ABIFlags> getAbiFlags() const; + + MipsAbi getAbi() const; + + /// \brief Merge saved ELF header flags and the new set of flags. + std::error_code mergeFlags(uint32_t newFlags, + const Elf_Mips_ABIFlags *newAbi); + + /// \brief Merge saved and new sets of registers usage masks. + void mergeRegistersMask(const Elf_Mips_RegInfo &info); + +private: + mutable std::mutex _mutex; + bool _hasAbiSection = false; + llvm::Optional<MipsAbiFlags> _abiFlags; + llvm::Optional<Elf_Mips_RegInfo> _regMask; + + llvm::ErrorOr<MipsAbiFlags> createAbiFlags(uint32_t flags, + const Elf_Mips_ABIFlags *sec); + static llvm::ErrorOr<MipsAbiFlags> createAbiFromHeaderFlags(uint32_t flags); + static llvm::ErrorOr<MipsAbiFlags> + createAbiFromSection(const Elf_Mips_ABIFlags &sec); +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp index 8bf80257fc89..a7062813df42 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "MipsCtorsOrderPass.h" +#include "lld/Core/Simple.h" #include <algorithm> #include <climits> @@ -48,8 +49,8 @@ static int32_t getSectionPriority(StringRef path, StringRef sectionName) { return priority; } -void MipsCtorsOrderPass::perform(std::unique_ptr<MutableFile> &f) { - auto definedAtoms = f->definedAtoms(); +std::error_code MipsCtorsOrderPass::perform(SimpleFile &f) { + auto definedAtoms = f.definedAtoms(); auto last = std::stable_partition(definedAtoms.begin(), definedAtoms.end(), [](const DefinedAtom *atom) { @@ -70,4 +71,6 @@ void MipsCtorsOrderPass::perform(std::unique_ptr<MutableFile> &f) { return leftPriority < rightPriority; }); + + return std::error_code(); } diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h index eeb1a194f9c7..5b12b7de0fa2 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h @@ -17,7 +17,7 @@ namespace elf { /// \brief This pass sorts atoms in .{ctors,dtors}.<priority> sections. class MipsCtorsOrderPass : public Pass { public: - void perform(std::unique_ptr<MutableFile> &mergedFile) override; + std::error_code perform(SimpleFile &mergedFile) override; }; } } diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h deleted file mode 100644 index 30b5b0ba6dae..000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h +++ /dev/null @@ -1,101 +0,0 @@ -//===- lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.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_MIPS_MIPS_DYNAMIC_LIBRARY_WRITER_H -#define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_LIBRARY_WRITER_H - -#include "DynamicLibraryWriter.h" -#include "MipsDynamicTable.h" -#include "MipsELFWriters.h" -#include "MipsLinkingContext.h" - -namespace lld { -namespace elf { - -template <typename ELFT> class MipsSymbolTable; -template <typename ELFT> class MipsDynamicSymbolTable; -template <typename ELFT> class MipsTargetLayout; - -template <class ELFT> -class MipsDynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { -public: - MipsDynamicLibraryWriter(MipsLinkingContext &ctx, - MipsTargetLayout<ELFT> &layout); - -protected: - // Add any runtime files and their atoms to the output - bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - - void finalizeDefaultAtomValues() override; - - std::error_code setELFHeader() override { - DynamicLibraryWriter<ELFT>::setELFHeader(); - _writeHelper.setELFHeader(*this->_elfHeader); - return std::error_code(); - } - - unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; - unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; - - unique_bump_ptr<DynamicSymbolTable<ELFT>> - createDynamicSymbolTable() override; - -private: - MipsELFWriter<ELFT> _writeHelper; - MipsTargetLayout<ELFT> &_mipsTargetLayout; -}; - -template <class ELFT> -MipsDynamicLibraryWriter<ELFT>::MipsDynamicLibraryWriter( - MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) - : DynamicLibraryWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout), - _mipsTargetLayout(layout) {} - -template <class ELFT> -bool MipsDynamicLibraryWriter<ELFT>::createImplicitFiles( - std::vector<std::unique_ptr<File>> &result) { - DynamicLibraryWriter<ELFT>::createImplicitFiles(result); - result.push_back(std::move(_writeHelper.createRuntimeFile())); - return true; -} - -template <class ELFT> -void MipsDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { - // Finalize the atom values that are part of the parent. - DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); - _writeHelper.finalizeMipsRuntimeAtomValues(); -} - -template <class ELFT> -unique_bump_ptr<SymbolTable<ELFT>> - MipsDynamicLibraryWriter<ELFT>::createSymbolTable() { - return unique_bump_ptr<SymbolTable<ELFT>>(new ( - this->_alloc) MipsSymbolTable<ELFT>(this->_context)); -} - -/// \brief create dynamic table -template <class ELFT> -unique_bump_ptr<DynamicTable<ELFT>> - MipsDynamicLibraryWriter<ELFT>::createDynamicTable() { - return unique_bump_ptr<DynamicTable<ELFT>>(new ( - this->_alloc) MipsDynamicTable<ELFT>(this->_context, _mipsTargetLayout)); -} - -/// \brief create dynamic symbol table -template <class ELFT> -unique_bump_ptr<DynamicSymbolTable<ELFT>> - MipsDynamicLibraryWriter<ELFT>::createDynamicSymbolTable() { - return unique_bump_ptr<DynamicSymbolTable<ELFT>>( - new (this->_alloc) MipsDynamicSymbolTable<ELFT>( - this->_context, _mipsTargetLayout)); -} - -} // namespace elf -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h index 2b9562f42b57..480c69cf4600 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h +++ b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h @@ -9,83 +9,89 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_TABLE_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_TABLE_H -#include "DefaultLayout.h" +#include "TargetLayout.h" #include "SectionChunks.h" namespace lld { namespace elf { -template <class ELFType> class MipsTargetLayout; +template <class ELFT> class MipsTargetLayout; -template <class MipsELFType> -class MipsDynamicTable : public DynamicTable<MipsELFType> { +template <class ELFT> class MipsDynamicTable : public DynamicTable<ELFT> { public: - MipsDynamicTable(const ELFLinkingContext &ctx, - MipsTargetLayout<MipsELFType> &layout) - : DynamicTable<MipsELFType>(ctx, layout, ".dynamic", - DefaultLayout<MipsELFType>::ORDER_DYNAMIC), - _mipsTargetLayout(layout) {} + MipsDynamicTable(const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : DynamicTable<ELFT>(ctx, layout, ".dynamic", + TargetLayout<ELFT>::ORDER_DYNAMIC), + _targetLayout(layout) {} void createDefaultEntries() override { - DynamicTable<MipsELFType>::createDefaultEntries(); - - typename DynamicTable<MipsELFType>::Elf_Dyn dyn; + DynamicTable<ELFT>::createDefaultEntries(); // Version id for the Runtime Linker Interface. - dyn.d_un.d_val = 1; - dyn.d_tag = DT_MIPS_RLD_VERSION; - this->addEntry(dyn); + this->addEntry(DT_MIPS_RLD_VERSION, 1); + + // The .rld_map section address. + if (this->_ctx.getOutputELFType() == ET_EXEC) { + _dt_rldmap = this->addEntry(DT_MIPS_RLD_MAP, 0); + _dt_rldmaprel = this->addEntry(DT_MIPS_RLD_MAP_REL, 0); + } // MIPS flags. - dyn.d_un.d_val = RHF_NOTPOT; - dyn.d_tag = DT_MIPS_FLAGS; - this->addEntry(dyn); + this->addEntry(DT_MIPS_FLAGS, RHF_NOTPOT); // The base address of the segment. - dyn.d_un.d_ptr = 0; - dyn.d_tag = DT_MIPS_BASE_ADDRESS; - _dt_baseaddr = this->addEntry(dyn); + _dt_baseaddr = this->addEntry(DT_MIPS_BASE_ADDRESS, 0); // Number of local global offset table entries. - dyn.d_un.d_val = 0; - dyn.d_tag = DT_MIPS_LOCAL_GOTNO; - _dt_localgot = this->addEntry(dyn); + _dt_localgot = this->addEntry(DT_MIPS_LOCAL_GOTNO, 0); // Number of entries in the .dynsym section. - dyn.d_un.d_val = 0; - dyn.d_tag = DT_MIPS_SYMTABNO; - _dt_symtabno = this->addEntry(dyn); + _dt_symtabno = this->addEntry(DT_MIPS_SYMTABNO, 0); // The index of the first dynamic symbol table entry that corresponds // to an entry in the global offset table. - dyn.d_un.d_val = 0; - dyn.d_tag = DT_MIPS_GOTSYM; - _dt_gotsym = this->addEntry(dyn); + _dt_gotsym = this->addEntry(DT_MIPS_GOTSYM, 0); // Address of the .got section. - dyn.d_un.d_val = 0; - dyn.d_tag = DT_PLTGOT; - _dt_pltgot = this->addEntry(dyn); + _dt_pltgot = this->addEntry(DT_PLTGOT, 0); + } + + void doPreFlight() override { + DynamicTable<ELFT>::doPreFlight(); + + if (_targetLayout.findOutputSection(".MIPS.options")) { + _dt_options = this->addEntry(DT_MIPS_OPTIONS, 0); + } } void updateDynamicTable() override { - DynamicTable<MipsELFType>::updateDynamicTable(); + DynamicTable<ELFT>::updateDynamicTable(); // Assign the minimum segment address to the DT_MIPS_BASE_ADDRESS tag. auto baseAddr = std::numeric_limits<uint64_t>::max(); - for (auto si : _mipsTargetLayout.segments()) + for (auto si : _targetLayout.segments()) if (si->segmentType() != llvm::ELF::PT_NULL) baseAddr = std::min(baseAddr, si->virtualAddr()); this->_entries[_dt_baseaddr].d_un.d_val = baseAddr; - auto &got = _mipsTargetLayout.getGOTSection(); + auto &got = _targetLayout.getGOTSection(); this->_entries[_dt_symtabno].d_un.d_val = this->getSymbolTable()->size(); this->_entries[_dt_gotsym].d_un.d_val = - this-> getSymbolTable()->size() - got.getGlobalCount(); + this->getSymbolTable()->size() - got.getGlobalCount(); this->_entries[_dt_localgot].d_un.d_val = got.getLocalCount(); - this->_entries[_dt_pltgot].d_un.d_ptr = - _mipsTargetLayout.findOutputSection(".got")->virtualAddr(); + this->_entries[_dt_pltgot].d_un.d_ptr = got.virtualAddr(); + + if (const auto *sec = _targetLayout.findOutputSection(".MIPS.options")) + this->_entries[_dt_options].d_un.d_ptr = sec->virtualAddr(); + + if (const auto *sec = _targetLayout.findOutputSection(".rld_map")) { + this->_entries[_dt_rldmap].d_un.d_ptr = sec->virtualAddr(); + this->_entries[_dt_rldmaprel].d_un.d_ptr = + sec->virtualAddr() - + (this->virtualAddr() + + _dt_rldmaprel * sizeof(typename DynamicTable<ELFT>::Elf_Dyn)); + } } int64_t getGotPltTag() override { return DT_MIPS_PLTGOT; } @@ -106,7 +112,10 @@ private: std::size_t _dt_gotsym; std::size_t _dt_pltgot; std::size_t _dt_baseaddr; - MipsTargetLayout<MipsELFType> &_mipsTargetLayout; + std::size_t _dt_options; + std::size_t _dt_rldmap; + std::size_t _dt_rldmaprel; + MipsTargetLayout<ELFT> &_targetLayout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFile.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFile.cpp new file mode 100644 index 000000000000..b081b63d77f7 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFile.cpp @@ -0,0 +1,348 @@ +//===- lib/ReaderWriter/ELF/MipsELFFile.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsELFFile.h" +#include "MipsTargetHandler.h" +#include "llvm/ADT/StringExtras.h" + +namespace lld { +namespace elf { + +template <class ELFT> +MipsELFDefinedAtom<ELFT>::MipsELFDefinedAtom( + const MipsELFFile<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) {} + +template <class ELFT> +const MipsELFFile<ELFT> &MipsELFDefinedAtom<ELFT>::file() const { + return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile); +} + +template <class ELFT> +DefinedAtom::CodeModel MipsELFDefinedAtom<ELFT>::codeModel() const { + switch (this->_symbol->st_other & llvm::ELF::STO_MIPS_MIPS16) { + case llvm::ELF::STO_MIPS_MIPS16: + return DefinedAtom::codeMips16; + case llvm::ELF::STO_MIPS_PIC: + return DefinedAtom::codeMipsPIC; + case llvm::ELF::STO_MIPS_MICROMIPS: + return DefinedAtom::codeMipsMicro; + case llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC: + return DefinedAtom::codeMipsMicroPIC; + default: + return DefinedAtom::codeNA; + } +} + +template <class ELFT> bool MipsELFDefinedAtom<ELFT>::isPIC() const { + return file().isPIC() || codeModel() == DefinedAtom::codeMipsMicroPIC || + codeModel() == DefinedAtom::codeMipsPIC; +} + +template class MipsELFDefinedAtom<ELF32BE>; +template class MipsELFDefinedAtom<ELF32LE>; +template class MipsELFDefinedAtom<ELF64BE>; +template class MipsELFDefinedAtom<ELF64LE>; + +template <class ELFT> static bool isMips64EL() { + return ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; +} + +template <class ELFT, bool isRela> +static uint32_t +extractTag(const llvm::object::Elf_Rel_Impl<ELFT, isRela> &rel) { + return (rel.getType(isMips64EL<ELFT>()) & 0xffffff00) >> 8; +} + +template <class ELFT> +MipsELFReference<ELFT>::MipsELFReference(uint64_t symValue, const Elf_Rela &rel) + : ELFReference<ELFT>(&rel, rel.r_offset - symValue, + Reference::KindArch::Mips, + rel.getType(isMips64EL<ELFT>()) & 0xff, + rel.getSymbol(isMips64EL<ELFT>())), + _tag(extractTag(rel)) {} + +template <class ELFT> +MipsELFReference<ELFT>::MipsELFReference(uint64_t symValue, const Elf_Rel &rel) + : ELFReference<ELFT>(rel.r_offset - symValue, Reference::KindArch::Mips, + rel.getType(isMips64EL<ELFT>()) & 0xff, + rel.getSymbol(isMips64EL<ELFT>())), + _tag(extractTag(rel)) {} + +template class MipsELFReference<ELF32BE>; +template class MipsELFReference<ELF32LE>; +template class MipsELFReference<ELF64BE>; +template class MipsELFReference<ELF64LE>; + +template <class ELFT> +MipsELFFile<ELFT>::MipsELFFile(std::unique_ptr<MemoryBuffer> mb, + ELFLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + +template <class ELFT> bool MipsELFFile<ELFT>::isPIC() const { + return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; +} + +template <class ELFT> std::error_code MipsELFFile<ELFT>::doParse() { + if (std::error_code ec = ELFFile<ELFT>::doParse()) + return ec; + // Retrieve some auxiliary data like GP value, TLS section address etc + // from the object file. + return readAuxData(); +} + +template <class ELFT> +ELFDefinedAtom<ELFT> *MipsELFFile<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 (this->_readerStorage) MipsELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, referenceStart, + referenceEnd, referenceList); +} + +template <class ELFT> +const typename MipsELFFile<ELFT>::Elf_Shdr * +MipsELFFile<ELFT>::findSectionByType(uint64_t type) const { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_type == type) + return §ion; + return nullptr; +} + +template <class ELFT> +const typename MipsELFFile<ELFT>::Elf_Shdr * +MipsELFFile<ELFT>::findSectionByFlags(uint64_t flags) const { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_flags & flags) + return §ion; + return nullptr; +} + +template <class ELFT> +ErrorOr<const typename MipsELFFile<ELFT>::Elf_Mips_RegInfo *> +MipsELFFile<ELFT>::findRegInfoSec() const { + using namespace llvm::ELF; + if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_OPTIONS)) { + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + while (!raw.empty()) { + if (raw.size() < sizeof(Elf_Mips_Options)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_OPTIONS section")); + + const auto *opt = reinterpret_cast<const Elf_Mips_Options *>(raw.data()); + if (opt->kind == ODK_REGINFO) + return &opt->getRegInfo(); + raw = raw.slice(opt->size); + } + } else if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_REGINFO)) { + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + if (raw.size() != sizeof(Elf_Mips_RegInfo)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_REGINFO section")); + + return reinterpret_cast<const Elf_Mips_RegInfo *>(raw.data()); + } + return nullptr; +} + +template <class ELFT> +ErrorOr<const typename MipsELFFile<ELFT>::Elf_Mips_ABIFlags *> +MipsELFFile<ELFT>::findAbiFlagsSec() const { + const Elf_Shdr *sec = findSectionByType(SHT_MIPS_ABIFLAGS); + if (!sec) + return nullptr; + + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + if (raw.size() != sizeof(Elf_Mips_ABIFlags)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_ABIFLAGS section")); + + const auto *abi = reinterpret_cast<const Elf_Mips_ABIFlags *>(raw.data()); + if (abi->version != 0) + return make_dynamic_error_code( + StringRef(".MIPS.abiflags section has unsupported version '") + + llvm::utostr(abi->version) + "'"); + + return abi; +} + +template <class ELFT> std::error_code MipsELFFile<ELFT>::readAuxData() { + using namespace llvm::ELF; + if (const Elf_Shdr *sec = findSectionByFlags(SHF_TLS)) { + _tpOff = sec->sh_addr + TP_OFFSET; + _dtpOff = sec->sh_addr + DTP_OFFSET; + } + + auto &handler = + static_cast<MipsTargetHandler<ELFT> &>(this->_ctx.getTargetHandler()); + auto &abi = handler.getAbiInfoHandler(); + + ErrorOr<const Elf_Mips_RegInfo *> regInfoSec = findRegInfoSec(); + if (auto ec = regInfoSec.getError()) + return ec; + if (const Elf_Mips_RegInfo *regInfo = regInfoSec.get()) { + abi.mergeRegistersMask(*regInfo); + _gp0 = regInfo->ri_gp_value; + } + + ErrorOr<const Elf_Mips_ABIFlags *> abiFlagsSec = findAbiFlagsSec(); + if (auto ec = abiFlagsSec.getError()) + return ec; + + const Elf_Ehdr *hdr = this->_objFile->getHeader(); + if (std::error_code ec = abi.mergeFlags(hdr->e_flags, abiFlagsSec.get())) + return ec; + + return std::error_code(); +} + +template <class ELFT> +void MipsELFFile<ELFT>::createRelocationReferences( + const Elf_Sym *symbol, ArrayRef<uint8_t> content, + range<const Elf_Rela *> rels) { + const auto value = this->getSymbolValue(symbol); + unsigned numInGroup = 0; + for (const auto &rel : rels) { + if (rel.r_offset < value || value + content.size() <= rel.r_offset) { + numInGroup = 0; + continue; + } + if (numInGroup > 0) { + auto &last = + *static_cast<MipsELFReference<ELFT> *>(this->_references.back()); + if (last.offsetInAtom() + value == rel.r_offset) { + last.setTag(last.tag() | + (rel.getType(isMips64EL<ELFT>()) << 8 * (numInGroup - 1))); + ++numInGroup; + continue; + } + } + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, rel); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + numInGroup = 1; + } +} + +template <class ELFT> +void MipsELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + const Elf_Shdr *relSec) { + const Elf_Shdr *symtab = *this->_objFile->getSection(relSec->sh_link); + auto rels = this->_objFile->rels(relSec); + const auto value = this->getSymbolValue(symbol); + for (const Elf_Rel *rit = rels.begin(), *eit = rels.end(); rit != eit; + ++rit) { + if (rit->r_offset < value || value + symContent.size() <= rit->r_offset) + continue; + + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, *rit); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + + auto addend = readAddend(*rit, secContent); + auto pairRelType = getPairRelocation(symtab, *rit); + if (pairRelType != llvm::ELF::R_MIPS_NONE) { + addend <<= 16; + auto mit = findMatchingRelocation(pairRelType, rit, eit); + if (mit != eit) + addend += int16_t(readAddend(*mit, secContent)); + else + // FIXME (simon): Show detailed warning. + llvm::errs() << "lld warning: cannot matching LO16 relocation\n"; + } + this->_references.back()->setAddend(addend); + } +} + +template <class ELFT> +static uint8_t +getPrimaryType(const llvm::object::Elf_Rel_Impl<ELFT, false> &rel) { + return rel.getType(isMips64EL<ELFT>()) & 0xff; +} + +template <class ELFT> +Reference::Addend +MipsELFFile<ELFT>::readAddend(const Elf_Rel &ri, + const ArrayRef<uint8_t> content) const { + return readMipsRelocAddend<ELFT>(getPrimaryType(ri), + content.data() + ri.r_offset); +} + +template <class ELFT> +uint32_t MipsELFFile<ELFT>::getPairRelocation(const Elf_Shdr *symtab, + const Elf_Rel &rel) const { + switch (getPrimaryType(rel)) { + case llvm::ELF::R_MIPS_HI16: + return llvm::ELF::R_MIPS_LO16; + case llvm::ELF::R_MIPS_PCHI16: + return llvm::ELF::R_MIPS_PCLO16; + case llvm::ELF::R_MIPS_GOT16: + if (isLocalBinding(symtab, rel)) + return llvm::ELF::R_MIPS_LO16; + break; + case llvm::ELF::R_MICROMIPS_HI16: + return llvm::ELF::R_MICROMIPS_LO16; + case llvm::ELF::R_MICROMIPS_GOT16: + if (isLocalBinding(symtab, rel)) + return llvm::ELF::R_MICROMIPS_LO16; + break; + default: + // Nothing to do. + break; + } + return llvm::ELF::R_MIPS_NONE; +} + +template <class ELFT> +const typename MipsELFFile<ELFT>::Elf_Rel * +MipsELFFile<ELFT>::findMatchingRelocation(uint32_t pairRelType, + const Elf_Rel *rit, + const Elf_Rel *eit) const { + return std::find_if(rit, eit, [&](const Elf_Rel &rel) { + return getPrimaryType(rel) == pairRelType && + rel.getSymbol(isMips64EL<ELFT>()) == + rit->getSymbol(isMips64EL<ELFT>()); + }); +} + +template <class ELFT> +bool MipsELFFile<ELFT>::isLocalBinding(const Elf_Shdr *symtab, + const Elf_Rel &rel) const { + return this->_objFile->getSymbol(symtab, rel.getSymbol(isMips64EL<ELFT>())) + ->getBinding() == llvm::ELF::STB_LOCAL; +} + +template class MipsELFFile<ELF32BE>; +template class MipsELFFile<ELF32LE>; +template class MipsELFFile<ELF64BE>; +template class MipsELFFile<ELF64LE>; + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFile.h b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h index 7381c7e977bf..934934b539cc 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsELFFile.h +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h @@ -12,42 +12,7 @@ #include "ELFReader.h" #include "MipsLinkingContext.h" #include "MipsRelocationHandler.h" - -namespace llvm { -namespace object { - -template <class ELFT> -struct Elf_RegInfo; - -template <llvm::support::endianness TargetEndianness, std::size_t MaxAlign> -struct Elf_RegInfo<ELFType<TargetEndianness, MaxAlign, false>> { - LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, false) - Elf_Word ri_gprmask; // bit-mask of used general registers - Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers - Elf_Addr ri_gp_value; // gp register value -}; - -template <llvm::support::endianness TargetEndianness, std::size_t MaxAlign> -struct Elf_RegInfo<ELFType<TargetEndianness, MaxAlign, true>> { - LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, true) - Elf_Word ri_gprmask; // bit-mask of used general registers - Elf_Word ri_pad; // unused padding field - Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers - Elf_Addr ri_gp_value; // gp register value -}; - -template <class ELFT> struct Elf_Mips_Options { - LLVM_ELF_IMPORT_TYPES(ELFT::TargetEndianness, ELFT::MaxAlignment, - ELFT::Is64Bits) - uint8_t kind; // Determines interpretation of variable part of descriptor - uint8_t size; // Byte size of descriptor, including this header - Elf_Half section; // Section header index of section affected, - // or 0 for global options - Elf_Word info; // Kind-specific information -}; - -} // end namespace object. -} // end namespace llvm. +#include "llvm/ADT/STLExtras.h" namespace lld { namespace elf { @@ -64,50 +29,21 @@ public: 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) {} + std::vector<ELFReference<ELFT> *> &referenceList); - const MipsELFFile<ELFT>& file() const override { - return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile); - } + const MipsELFFile<ELFT>& file() const override; + DefinedAtom::CodeModel codeModel() const override; - DefinedAtom::CodeModel codeModel() const override { - switch (this->_symbol->st_other & llvm::ELF::STO_MIPS_MIPS16) { - case llvm::ELF::STO_MIPS_MIPS16: - return DefinedAtom::codeMips16; - case llvm::ELF::STO_MIPS_PIC: - return DefinedAtom::codeMipsPIC; - case llvm::ELF::STO_MIPS_MICROMIPS: - return DefinedAtom::codeMipsMicro; - case llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC: - return DefinedAtom::codeMipsMicroPIC; - default: - return DefinedAtom::codeNA; - } - } + bool isPIC() const; }; template <class ELFT> class MipsELFReference : public ELFReference<ELFT> { typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; - static const bool _isMips64EL = - ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; - public: - MipsELFReference(uint64_t symValue, const Elf_Rela &rel) - : ELFReference<ELFT>( - &rel, rel.r_offset - symValue, Reference::KindArch::Mips, - rel.getType(_isMips64EL) & 0xff, rel.getSymbol(_isMips64EL)), - _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} - - MipsELFReference(uint64_t symValue, const Elf_Rel &rel) - : ELFReference<ELFT>(rel.r_offset - symValue, Reference::KindArch::Mips, - rel.getType(_isMips64EL) & 0xff, - rel.getSymbol(_isMips64EL)), - _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} + MipsELFReference(uint64_t symValue, const Elf_Rela &rel); + MipsELFReference(uint64_t symValue, const Elf_Rel &rel); uint32_t tag() const override { return _tag; } void setTag(uint32_t tag) { _tag = tag; } @@ -118,211 +54,70 @@ private: template <class ELFT> class MipsELFFile : public ELFFile<ELFT> { public: - MipsELFFile(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} + MipsELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); - static ErrorOr<std::unique_ptr<MipsELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) { - return std::unique_ptr<MipsELFFile<ELFT>>( - new MipsELFFile<ELFT>(std::move(mb), ctx)); - } - - bool isPIC() const { - return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; - } + bool isPIC() const; /// \brief gp register value stored in the .reginfo section. - int64_t getGP0() const { return _gp0 ? *_gp0 : 0; } + int64_t getGP0() const { return _gp0; } /// \brief .tdata section address plus fixed offset. - uint64_t getTPOffset() const { return *_tpOff; } - uint64_t getDTPOffset() const { return *_dtpOff; } + uint64_t getTPOffset() const { return _tpOff; } + uint64_t getDTPOffset() const { return _dtpOff; } protected: - std::error_code doParse() override { - if (std::error_code ec = ELFFile<ELFT>::doParse()) - return ec; - // Retrieve some auxiliary data like GP value, TLS section address etc - // from the object file. - return readAuxData(); - } + std::error_code doParse() override; private: 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 typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter; - typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela_Iter Elf_Rela_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela; enum { TP_OFFSET = 0x7000, DTP_OFFSET = 0x8000 }; - static const bool _isMips64EL = - ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; - - llvm::Optional<int64_t> _gp0; - llvm::Optional<uint64_t> _tpOff; - llvm::Optional<uint64_t> _dtpOff; + int64_t _gp0 = 0; + uint64_t _tpOff = 0; + uint64_t _dtpOff = 0; - 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) MipsELFDefinedAtom<ELFT>( - *this, symName, sectionName, sym, sectionHdr, contentData, - referenceStart, referenceEnd, referenceList); - } - - const Elf_Shdr *findSectionByType(uint64_t type) { - for (const Elf_Shdr §ion : this->_objFile->sections()) - if (section.sh_type == type) - return §ion; - return nullptr; - } - - const Elf_Shdr *findSectionByFlags(uint64_t flags) { - for (const Elf_Shdr §ion : this->_objFile->sections()) - if (section.sh_flags & flags) - return §ion; - return nullptr; - } - - std::error_code readAuxData() { - using namespace llvm::ELF; - if (const Elf_Shdr *sec = findSectionByFlags(SHF_TLS)) { - _tpOff = sec->sh_addr + TP_OFFSET; - _dtpOff = sec->sh_addr + DTP_OFFSET; - } - - typedef llvm::object::Elf_RegInfo<ELFT> Elf_RegInfo; - typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; - - if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_OPTIONS)) { - auto contents = this->getSectionContents(sec); - if (std::error_code ec = contents.getError()) - return ec; - - ArrayRef<uint8_t> raw = contents.get(); - while (!raw.empty()) { - if (raw.size() < sizeof(Elf_Mips_Options)) - return make_dynamic_error_code( - StringRef("Invalid size of MIPS_OPTIONS section")); - - const auto *opt = reinterpret_cast<const Elf_Mips_Options *>(raw.data()); - if (opt->kind == ODK_REGINFO) { - _gp0 = reinterpret_cast<const Elf_RegInfo *>(opt + 1)->ri_gp_value; - break; - } - raw = raw.slice(opt->size); - } - } else if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_REGINFO)) { - auto contents = this->getSectionContents(sec); - if (std::error_code ec = contents.getError()) - return ec; - - ArrayRef<uint8_t> raw = contents.get(); - if (raw.size() != sizeof(Elf_RegInfo)) - return make_dynamic_error_code( - StringRef("Invalid size of MIPS_REGINFO section")); - - _gp0 = reinterpret_cast<const Elf_RegInfo *>(raw.data())->ri_gp_value; - } - return std::error_code(); - } + 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) override; void createRelocationReferences(const Elf_Sym *symbol, ArrayRef<uint8_t> content, - range<Elf_Rela_Iter> rels) override { - const auto value = this->getSymbolValue(symbol); - for (const auto &rel : rels) { - if (rel.r_offset < value || value + content.size() <= rel.r_offset) - continue; - auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, rel); - this->addReferenceToSymbol(r, symbol); - this->_references.push_back(r); - } - } - + range<const Elf_Rela *> rels) override; void createRelocationReferences(const Elf_Sym *symbol, ArrayRef<uint8_t> symContent, ArrayRef<uint8_t> secContent, - range<Elf_Rel_Iter> rels) override { - const auto value = this->getSymbolValue(symbol); - for (Elf_Rel_Iter rit = rels.begin(), eit = rels.end(); rit != eit; ++rit) { - if (rit->r_offset < value || value + symContent.size() <= rit->r_offset) - continue; + const Elf_Shdr *RelSec) override; - auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, *rit); - this->addReferenceToSymbol(r, symbol); - this->_references.push_back(r); + const Elf_Shdr *findSectionByType(uint64_t type) const; + const Elf_Shdr *findSectionByFlags(uint64_t flags) const; - auto addend = readAddend(*rit, secContent); - auto pairRelType = getPairRelocation(*rit); - if (pairRelType != llvm::ELF::R_MIPS_NONE) { - addend <<= 16; - auto mit = findMatchingRelocation(pairRelType, rit, eit); - if (mit != eit) - addend += int16_t(readAddend(*mit, secContent)); - else - // FIXME (simon): Show detailed warning. - llvm::errs() << "lld warning: cannot matching LO16 relocation\n"; - } - this->_references.back()->setAddend(addend); - } - } + typedef typename llvm::object::ELFFile<ELFT>::Elf_Ehdr Elf_Ehdr; + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; + typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags; - Reference::Addend readAddend(const Elf_Rel &ri, - const ArrayRef<uint8_t> content) const { - const auto &rh = - this->_ctx.template getTargetHandler<ELFT>().getRelocationHandler(); - return static_cast<const MipsRelocationHandler &>(rh) - .readAddend(getPrimaryType(ri), content.data() + ri.r_offset); - } + ErrorOr<const Elf_Mips_RegInfo *> findRegInfoSec() const; + ErrorOr<const Elf_Mips_ABIFlags*> findAbiFlagsSec() const; - uint32_t getPairRelocation(const Elf_Rel &rel) const { - switch (getPrimaryType(rel)) { - case llvm::ELF::R_MIPS_HI16: - return llvm::ELF::R_MIPS_LO16; - case llvm::ELF::R_MIPS_PCHI16: - return llvm::ELF::R_MIPS_PCLO16; - case llvm::ELF::R_MIPS_GOT16: - if (isLocalBinding(rel)) - return llvm::ELF::R_MIPS_LO16; - break; - case llvm::ELF::R_MICROMIPS_HI16: - return llvm::ELF::R_MICROMIPS_LO16; - case llvm::ELF::R_MICROMIPS_GOT16: - if (isLocalBinding(rel)) - return llvm::ELF::R_MICROMIPS_LO16; - break; - default: - // Nothing to do. - break; - } - return llvm::ELF::R_MIPS_NONE; - } + std::error_code readAuxData(); - Elf_Rel_Iter findMatchingRelocation(uint32_t pairRelType, Elf_Rel_Iter rit, - Elf_Rel_Iter eit) const { - return std::find_if(rit, eit, [&](const Elf_Rel &rel) { - return getPrimaryType(rel) == pairRelType && - rel.getSymbol(_isMips64EL) == rit->getSymbol(_isMips64EL); - }); - } + Reference::Addend readAddend(const Elf_Rel &ri, + const ArrayRef<uint8_t> content) const; - static uint8_t getPrimaryType(const Elf_Rel &rel) { - return rel.getType(_isMips64EL) & 0xff; - } - bool isLocalBinding(const Elf_Rel &rel) const { - return this->_objFile->getSymbol(rel.getSymbol(_isMips64EL)) - ->getBinding() == llvm::ELF::STB_LOCAL; - } -}; + uint32_t getPairRelocation(const Elf_Shdr *Symtab, const Elf_Rel &rel) const; -template <class ELFT> class MipsDynamicFile : public DynamicFile<ELFT> { -public: - MipsDynamicFile(const MipsLinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} + const Elf_Rel *findMatchingRelocation(uint32_t pairRelType, + const Elf_Rel *rit, + const Elf_Rel *eit) const; + + bool isLocalBinding(const Elf_Shdr *Symtab, const Elf_Rel &rel) const; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp deleted file mode 100644 index 0ef2c70b8156..000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp +++ /dev/null @@ -1,149 +0,0 @@ -//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.cpp ------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "MipsELFFlagsMerger.h" -#include "lld/Core/Error.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/ELF.h" -#include "llvm/Support/raw_ostream.h" - -using namespace lld; -using namespace lld::elf; -using namespace llvm::ELF; - -struct MipsISATreeEdge { - unsigned child; - unsigned parent; -}; - -static MipsISATreeEdge isaTree[] = { - // MIPS32R6 and MIPS64R6 are not compatible with other extensions - - // MIPS64 extensions. - {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, - // MIPS V extensions. - {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, - // MIPS IV extensions. - {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, - // MIPS III extensions. - {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3}, - // MIPS32 extensions. - {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32}, - // MIPS II extensions. - {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2}, - {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2}, - // MIPS I extensions. - {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, -}; - -static bool matchMipsISA(unsigned base, unsigned ext) { - if (base == ext) - return true; - if (base == EF_MIPS_ARCH_32 && matchMipsISA(EF_MIPS_ARCH_64, ext)) - return true; - if (base == EF_MIPS_ARCH_32R2 && matchMipsISA(EF_MIPS_ARCH_64R2, ext)) - return true; - for (const auto &edge : isaTree) { - if (ext == edge.child) { - ext = edge.parent; - if (ext == base) - return true; - } - } - return false; -} - -MipsELFFlagsMerger::MipsELFFlagsMerger(bool is64Bits) - : _is64Bit(is64Bits), _flags(0) {} - -uint32_t MipsELFFlagsMerger::getMergedELFFlags() const { return _flags; } - -std::error_code MipsELFFlagsMerger::merge(uint8_t newClass, uint32_t newFlags) { - // Check bitness. - if (_is64Bit != (newClass == ELFCLASS64)) - return make_dynamic_error_code( - Twine("Bitness is incompatible with that of the selected target")); - - // We support two ABI: O32 and N64. The last one does not have - // the corresponding ELF flag. - uint32_t inAbi = newFlags & EF_MIPS_ABI; - uint32_t supportedAbi = _is64Bit ? 0 : uint32_t(EF_MIPS_ABI_O32); - if (inAbi != supportedAbi) - return make_dynamic_error_code(Twine("Unsupported ABI")); - - // ... and reduced set of architectures ... - uint32_t newArch = newFlags & EF_MIPS_ARCH; - switch (newArch) { - case EF_MIPS_ARCH_1: - case EF_MIPS_ARCH_2: - case EF_MIPS_ARCH_3: - case EF_MIPS_ARCH_4: - case EF_MIPS_ARCH_5: - case EF_MIPS_ARCH_32: - case EF_MIPS_ARCH_64: - case EF_MIPS_ARCH_32R2: - case EF_MIPS_ARCH_64R2: - case EF_MIPS_ARCH_32R6: - case EF_MIPS_ARCH_64R6: - break; - default: - return make_dynamic_error_code(Twine("Unsupported instruction set")); - } - - // ... and still do not support MIPS-16 extension. - if (newFlags & EF_MIPS_ARCH_ASE_M16) - return make_dynamic_error_code(Twine("Unsupported extension: MIPS16")); - - // PIC code is inherently CPIC and may not set CPIC flag explicitly. - // Ensure that this flag will exist in the linked file. - if (newFlags & EF_MIPS_PIC) - newFlags |= EF_MIPS_CPIC; - - std::lock_guard<std::mutex> lock(_mutex); - - // If the old set of flags is empty, use the new one as a result. - if (!_flags) { - _flags = newFlags; - return std::error_code(); - } - - // Check PIC / CPIC flags compatibility. - uint32_t newPic = newFlags & (EF_MIPS_PIC | EF_MIPS_CPIC); - uint32_t oldPic = _flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - - if ((newPic != 0) != (oldPic != 0)) - llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n"; - - if (!(newPic & EF_MIPS_PIC)) - _flags &= ~EF_MIPS_PIC; - if (newPic) - _flags |= EF_MIPS_CPIC; - - // Check mixing -mnan=2008 / -mnan=legacy modules. - if ((newFlags & EF_MIPS_NAN2008) != (_flags & EF_MIPS_NAN2008)) - return make_dynamic_error_code( - Twine("Linking -mnan=2008 and -mnan=legacy modules")); - - // Check ISA compatibility and update the extension flag. - uint32_t oldArch = _flags & EF_MIPS_ARCH; - if (!matchMipsISA(newArch, oldArch)) { - if (!matchMipsISA(oldArch, newArch)) - return make_dynamic_error_code( - Twine("Linking modules with incompatible ISA")); - _flags &= ~EF_MIPS_ARCH; - _flags |= newArch; - } - - _flags |= newFlags & EF_MIPS_NOREORDER; - _flags |= newFlags & EF_MIPS_MICROMIPS; - _flags |= newFlags & EF_MIPS_NAN2008; - _flags |= newFlags & EF_MIPS_32BITMODE; - - return std::error_code(); -} diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h deleted file mode 100644 index 6ade86f0163c..000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h +++ /dev/null @@ -1,36 +0,0 @@ -//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.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_MIPS_MIPS_ELF_FLAGS_MERGER_H -#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_FLAGS_MERGER_H - -#include <mutex> -#include <system_error> - -namespace lld { -namespace elf { - -class MipsELFFlagsMerger { -public: - MipsELFFlagsMerger(bool is64Bits); - - uint32_t getMergedELFFlags() const; - - /// \brief Merge saved ELF header flags and the new set of flags. - std::error_code merge(uint8_t newClass, uint32_t newFlags); - -private: - const bool _is64Bit; - std::mutex _mutex; - uint32_t _flags; -}; - -} // namespace elf -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFReader.h b/lib/ReaderWriter/ELF/Mips/MipsELFReader.h deleted file mode 100644 index 8b325b38bb52..000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsELFReader.h +++ /dev/null @@ -1,93 +0,0 @@ -//===- lib/ReaderWriter/ELF/MipsELFReader.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_MIPS_MIPS_ELF_READER_H -#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_READER_H - -#include "ELFReader.h" -#include "MipsELFFile.h" -#include "MipsELFFlagsMerger.h" -#include "MipsLinkingContext.h" - -namespace lld { -namespace elf { - -struct MipsELFFileCreateTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - MipsLinkingContext &ctx) { - return lld::elf::MipsELFFile<ELFT>::create(std::move(mb), ctx); - } -}; - -struct MipsDynamicFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - MipsLinkingContext &ctx) { - return lld::elf::MipsDynamicFile<ELFT>::create(std::move(mb), ctx); - } -}; - -template <class ELFT> -class MipsELFObjectReader - : public ELFObjectReader<ELFT, MipsELFFileCreateTraits, - MipsLinkingContext> { - typedef ELFObjectReader<ELFT, MipsELFFileCreateTraits, MipsLinkingContext> - BaseReaderType; - -public: - MipsELFObjectReader(MipsLinkingContext &ctx) - : BaseReaderType(ctx, llvm::ELF::EM_MIPS), - _flagMerger(ctx.getELFFlagsMerger()) {} - - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, - std::vector<std::unique_ptr<File>> &result) const override { - auto &hdr = *this->elfHeader(*mb); - if (std::error_code ec = _flagMerger.merge(hdr.getFileClass(), hdr.e_flags)) - return ec; - return BaseReaderType::loadFile(std::move(mb), registry, result); - } - -private: - MipsELFFlagsMerger &_flagMerger; -}; - -template <class ELFT> -class MipsELFDSOReader - : public ELFDSOReader<ELFT, MipsDynamicFileCreateELFTraits, - MipsLinkingContext> { - typedef ELFDSOReader<ELFT, MipsDynamicFileCreateELFTraits, MipsLinkingContext> - BaseReaderType; - -public: - MipsELFDSOReader(MipsLinkingContext &ctx) - : BaseReaderType(ctx, llvm::ELF::EM_MIPS), - _flagMerger(ctx.getELFFlagsMerger()) {} - - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, - std::vector<std::unique_ptr<File>> &result) const override { - auto &hdr = *this->elfHeader(*mb); - if (std::error_code ec = _flagMerger.merge(hdr.getFileClass(), hdr.e_flags)) - return ec; - return BaseReaderType::loadFile(std::move(mb), registry, result); - } - -private: - MipsELFFlagsMerger &_flagMerger; -}; - -} // namespace elf -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp new file mode 100644 index 000000000000..b97a4f5a9070 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp @@ -0,0 +1,292 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsELFWriters.cpp -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsDynamicTable.h" +#include "MipsELFWriters.h" +#include "MipsLinkingContext.h" +#include "MipsTargetHandler.h" +#include "MipsTargetLayout.h" + +namespace { +class MipsDynamicAtom : public lld::elf::DynamicAtom { +public: + MipsDynamicAtom(const lld::File &f) : DynamicAtom(f) {} + + ContentPermissions permissions() const override { return permR__; } +}; +} + +namespace lld { +namespace elf { + +template <class ELFT> +MipsELFWriter<ELFT>::MipsELFWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const MipsAbiInfoHandler<ELFT> &abiInfo) + : _ctx(ctx), _targetLayout(targetLayout), _abiInfo(abiInfo) {} + +template <class ELFT> +void MipsELFWriter<ELFT>::setELFHeader(ELFHeader<ELFT> &elfHeader) { + elfHeader.e_version(1); + elfHeader.e_ident(llvm::ELF::EI_VERSION, llvm::ELF::EV_CURRENT); + elfHeader.e_ident(llvm::ELF::EI_OSABI, llvm::ELF::ELFOSABI_NONE); + + unsigned char abiVer = 0; + if (_ctx.getOutputELFType() == ET_EXEC && _abiInfo.isCPicOnly()) + abiVer = 1; + if (_abiInfo.isFp64()) + abiVer = 3; + + elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, abiVer); + elfHeader.e_flags(_abiInfo.getFlags()); +} + +template <class ELFT> +void MipsELFWriter<ELFT>::finalizeMipsRuntimeAtomValues() { + auto gotSection = _targetLayout.findOutputSection(".got"); + auto got = gotSection ? gotSection->virtualAddr() : 0; + auto gp = gotSection ? got + _targetLayout.getGPOffset() : 0; + + setAtomValue("_gp", gp); + setAtomValue("_gp_disp", gp); + setAtomValue("__gnu_local_gp", gp); + + if (_ctx.isDynamic() && _ctx.getOutputELFType() == ET_EXEC) + setAtomValue("_DYNAMIC_LINKING", 1); +} + +template <class ELFT> +std::unique_ptr<RuntimeFile<ELFT>> MipsELFWriter<ELFT>::createRuntimeFile() { + auto file = llvm::make_unique<RuntimeFile<ELFT>>(_ctx, "Mips runtime file"); + file->addAbsoluteAtom("_gp"); + file->addAbsoluteAtom("_gp_disp"); + file->addAbsoluteAtom("__gnu_local_gp"); + if (_ctx.isDynamic()) { + file->addAtom(*new (file->allocator()) MipsDynamicAtom(*file)); + if (_ctx.getOutputELFType() == ET_EXEC) + file->addAbsoluteAtom("_DYNAMIC_LINKING"); + } + return file; +} + +template <class ELFT> +unique_bump_ptr<Section<ELFT>> +MipsELFWriter<ELFT>::createOptionsSection(llvm::BumpPtrAllocator &alloc) { + typedef unique_bump_ptr<Section<ELFT>> Ptr; + const auto ®Mask = _abiInfo.getRegistersMask(); + if (!regMask.hasValue()) + return Ptr(); + return ELFT::Is64Bits + ? Ptr(new (alloc) + MipsOptionsSection<ELFT>(_ctx, _targetLayout, *regMask)) + : Ptr(new (alloc) + MipsReginfoSection<ELFT>(_ctx, _targetLayout, *regMask)); +} + +template <class ELFT> +unique_bump_ptr<Section<ELFT>> +MipsELFWriter<ELFT>::createAbiFlagsSection(llvm::BumpPtrAllocator &alloc) { + typedef unique_bump_ptr<Section<ELFT>> Ptr; + const auto &abi = _abiInfo.getAbiFlags(); + if (!abi.hasValue()) + return Ptr(); + return Ptr(new (alloc) MipsAbiFlagsSection<ELFT>(_ctx, _targetLayout, *abi)); +} + +template <class ELFT> +void MipsELFWriter<ELFT>::setAtomValue(StringRef name, uint64_t value) { + AtomLayout *atom = _targetLayout.findAbsoluteAtom(name); + assert(atom); + atom->_virtualAddr = value; +} + +template <class ELFT> +MipsDynamicLibraryWriter<ELFT>::MipsDynamicLibraryWriter( + MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo) + : DynamicLibraryWriter<ELFT>(ctx, layout), + _writeHelper(ctx, layout, abiInfo), _targetLayout(layout) {} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + result.push_back(_writeHelper.createRuntimeFile()); +} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::createDefaultSections() { + DynamicLibraryWriter<ELFT>::createDefaultSections(); + _reginfo = _writeHelper.createOptionsSection(this->_alloc); + if (_reginfo) + this->_layout.addSection(_reginfo.get()); + _abiFlags = _writeHelper.createAbiFlagsSection(this->_alloc); + if (_abiFlags) + this->_layout.addSection(_abiFlags.get()); +} + +template <class ELFT> +std::error_code MipsDynamicLibraryWriter<ELFT>::setELFHeader() { + DynamicLibraryWriter<ELFT>::setELFHeader(); + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> +MipsDynamicLibraryWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>( + new (this->_alloc) MipsSymbolTable<ELFT>(this->_ctx)); +} + +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> +MipsDynamicLibraryWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>( + new (this->_alloc) MipsDynamicTable<ELFT>(this->_ctx, _targetLayout)); +} + +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> +MipsDynamicLibraryWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>(new ( + this->_alloc) MipsDynamicSymbolTable<ELFT>(this->_ctx, _targetLayout)); +} + +template class MipsDynamicLibraryWriter<ELF32BE>; +template class MipsDynamicLibraryWriter<ELF32LE>; +template class MipsDynamicLibraryWriter<ELF64BE>; +template class MipsDynamicLibraryWriter<ELF64LE>; + +template <class ELFT> +MipsExecutableWriter<ELFT>::MipsExecutableWriter( + MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo) + : ExecutableWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout, abiInfo), + _targetLayout(layout) {} + +template <class ELFT> +std::error_code MipsExecutableWriter<ELFT>::setELFHeader() { + std::error_code ec = ExecutableWriter<ELFT>::setELFHeader(); + if (ec) + return ec; + + StringRef entryName = this->_ctx.entrySymbolName(); + if (const AtomLayout *al = this->_layout.findAtomLayoutByName(entryName)) { + const auto *ea = cast<DefinedAtom>(al->_atom); + if (ea->codeModel() == DefinedAtom::codeMipsMicro || + ea->codeModel() == DefinedAtom::codeMipsMicroPIC) + // Adjust entry symbol value if this symbol is microMIPS encoded. + this->_elfHeader->e_entry(al->_virtualAddr | 1); + } + + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + // MIPS ABI requires to add to dynsym even undefined symbols + // if they have a corresponding entries in a global part of GOT. + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + if (_targetLayout.getGOTSection().hasGlobalGOTEntry(atom->_atom)) { + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + continue; + } + + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (!da) + continue; + + if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && + !this->_ctx.isDynamicallyExportedSymbol(da->name()) && + !(this->_ctx.shouldExportDynamic() && + da->scope() == Atom::Scope::scopeGlobal)) + continue; + + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + for (const UndefinedAtom *a : file.undefined()) + // FIXME (simon): Consider to move this check to the + // MipsELFUndefinedAtom class method. That allows to + // handle more complex coditions in the future. + if (_targetLayout.getGOTSection().hasGlobalGOTEntry(a)) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + + // Skip our immediate parent class method + // ExecutableWriter<ELFT>::buildDynamicSymbolTable because we replaced it + // with our own version. Call OutputELFWriter directly. + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + result.push_back(_writeHelper.createRuntimeFile()); +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> void MipsExecutableWriter<ELFT>::createDefaultSections() { + ExecutableWriter<ELFT>::createDefaultSections(); + _reginfo = _writeHelper.createOptionsSection(this->_alloc); + if (_reginfo) + this->_layout.addSection(_reginfo.get()); + _abiFlags = _writeHelper.createAbiFlagsSection(this->_alloc); + if (_abiFlags) + this->_layout.addSection(_abiFlags.get()); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> +MipsExecutableWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>( + new (this->_alloc) MipsSymbolTable<ELFT>(this->_ctx)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> +MipsExecutableWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>( + new (this->_alloc) MipsDynamicTable<ELFT>(this->_ctx, _targetLayout)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> +MipsExecutableWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>(new ( + this->_alloc) MipsDynamicSymbolTable<ELFT>(this->_ctx, _targetLayout)); +} + +template class MipsExecutableWriter<ELF32BE>; +template class MipsExecutableWriter<ELF32LE>; +template class MipsExecutableWriter<ELF64BE>; +template class MipsExecutableWriter<ELF64LE>; + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h index d94dd757a0f3..31b84f947c95 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h +++ b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h @@ -9,71 +9,91 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_WRITERS_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_WRITERS_H +#include "DynamicLibraryWriter.h" +#include "ExecutableWriter.h" +#include "MipsAbiInfoHandler.h" #include "MipsLinkingContext.h" -#include "OutputELFWriter.h" namespace lld { namespace elf { -template <class ELFT> class MipsRuntimeFile; - template <class ELFT> class MipsTargetLayout; template <typename ELFT> class MipsELFWriter { public: - MipsELFWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout) - : _ctx(ctx), _targetLayout(targetLayout) {} - - void setELFHeader(ELFHeader<ELFT> &elfHeader) { - elfHeader.e_version(1); - elfHeader.e_ident(llvm::ELF::EI_VERSION, llvm::ELF::EV_CURRENT); - elfHeader.e_ident(llvm::ELF::EI_OSABI, llvm::ELF::ELFOSABI_NONE); - if (_targetLayout.findOutputSection(".got.plt")) - elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, 1); - else - elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, 0); - - elfHeader.e_flags(_ctx.getMergedELFFlags()); - } - - void finalizeMipsRuntimeAtomValues() { - if (!_ctx.isDynamic()) - return; - - auto gotSection = _targetLayout.findOutputSection(".got"); - auto got = gotSection ? gotSection->virtualAddr() : 0; - auto gp = gotSection ? got + _targetLayout.getGPOffset() : 0; - - setAtomValue("_GLOBAL_OFFSET_TABLE_", got); - setAtomValue("_gp", gp); - setAtomValue("_gp_disp", gp); - setAtomValue("__gnu_local_gp", gp); - } - - bool hasGlobalGOTEntry(const Atom *a) const { - return _targetLayout.getGOTSection().hasGlobalGOTEntry(a); - } - - std::unique_ptr<MipsRuntimeFile<ELFT>> createRuntimeFile() { - auto file = llvm::make_unique<MipsRuntimeFile<ELFT>>(_ctx); - if (_ctx.isDynamic()) { - file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); - file->addAbsoluteAtom("_gp"); - file->addAbsoluteAtom("_gp_disp"); - file->addAbsoluteAtom("__gnu_local_gp"); - } - return file; - } + MipsELFWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const MipsAbiInfoHandler<ELFT> &abiInfo); + + void setELFHeader(ELFHeader<ELFT> &elfHeader); + + void finalizeMipsRuntimeAtomValues(); + + std::unique_ptr<RuntimeFile<ELFT>> createRuntimeFile(); + unique_bump_ptr<Section<ELFT>> + createOptionsSection(llvm::BumpPtrAllocator &alloc); + unique_bump_ptr<Section<ELFT>> + createAbiFlagsSection(llvm::BumpPtrAllocator &alloc); private: MipsLinkingContext &_ctx; MipsTargetLayout<ELFT> &_targetLayout; + const MipsAbiInfoHandler<ELFT> &_abiInfo; + + void setAtomValue(StringRef name, uint64_t value); +}; + +template <class ELFT> +class MipsDynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + MipsDynamicLibraryWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo); + +protected: + // Add any runtime files and their atoms to the output + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + void createDefaultSections() override; + + std::error_code setELFHeader() override; + + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; + unique_bump_ptr<DynamicSymbolTable<ELFT>> createDynamicSymbolTable() override; + +private: + MipsELFWriter<ELFT> _writeHelper; + MipsTargetLayout<ELFT> &_targetLayout; + unique_bump_ptr<Section<ELFT>> _reginfo; + unique_bump_ptr<Section<ELFT>> _abiFlags; +}; + +template <class ELFT> +class MipsExecutableWriter : public ExecutableWriter<ELFT> { +public: + MipsExecutableWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout, + const MipsAbiInfoHandler<ELFT> &abiInfo); - void setAtomValue(StringRef name, uint64_t value) { - auto atom = _targetLayout.findAbsoluteAtom(name); - assert(atom != _targetLayout.absoluteAtoms().end()); - (*atom)->_virtualAddr = value; - } +protected: + void buildDynamicSymbolTable(const File &file) override; + + // Add any runtime files and their atoms to the output + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + void createDefaultSections() override; + std::error_code setELFHeader() override; + + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; + unique_bump_ptr<DynamicSymbolTable<ELFT>> createDynamicSymbolTable() override; + +private: + MipsELFWriter<ELFT> _writeHelper; + MipsTargetLayout<ELFT> &_targetLayout; + unique_bump_ptr<Section<ELFT>> _reginfo; + unique_bump_ptr<Section<ELFT>> _abiFlags; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h b/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h deleted file mode 100644 index 1a85bba3bd0f..000000000000 --- a/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h +++ /dev/null @@ -1,154 +0,0 @@ -//===- lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.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_MIPS_MIPS_EXECUTABLE_WRITER_H -#define LLD_READER_WRITER_ELF_MIPS_MIPS_EXECUTABLE_WRITER_H - -#include "ExecutableWriter.h" -#include "MipsDynamicTable.h" -#include "MipsELFWriters.h" -#include "MipsLinkingContext.h" - -namespace lld { -namespace elf { - -template <typename ELFT> class MipsTargetLayout; - -template <class ELFT> -class MipsExecutableWriter : public ExecutableWriter<ELFT> { -public: - MipsExecutableWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout); - -protected: - void buildDynamicSymbolTable(const File &file) override; - - // Add any runtime files and their atoms to the output - bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; - - void finalizeDefaultAtomValues() override; - std::error_code setELFHeader() override; - - unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; - unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; - - unique_bump_ptr<DynamicSymbolTable<ELFT>> - createDynamicSymbolTable() override; - -private: - MipsELFWriter<ELFT> _writeHelper; - MipsTargetLayout<ELFT> &_mipsTargetLayout; -}; - -template <class ELFT> -MipsExecutableWriter<ELFT>::MipsExecutableWriter(MipsLinkingContext &ctx, - MipsTargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout), - _mipsTargetLayout(layout) {} - -template <class ELFT> -std::error_code MipsExecutableWriter<ELFT>::setELFHeader() { - std::error_code ec = ExecutableWriter<ELFT>::setELFHeader(); - if (ec) - return ec; - - StringRef entryName = this->_context.entrySymbolName(); - if (const AtomLayout *al = this->_layout.findAtomLayoutByName(entryName)) { - const auto *ea = cast<DefinedAtom>(al->_atom); - if (ea->codeModel() == DefinedAtom::codeMipsMicro || - ea->codeModel() == DefinedAtom::codeMipsMicroPIC) - // Adjust entry symbol value if this symbol is microMIPS encoded. - this->_elfHeader->e_entry(al->_virtualAddr | 1); - } - - _writeHelper.setELFHeader(*this->_elfHeader); - return std::error_code(); -} - -template <class ELFT> -void MipsExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { - // MIPS ABI requires to add to dynsym even undefined symbols - // if they have a corresponding entries in a global part of GOT. - for (auto sec : this->_layout.sections()) - if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) - for (const auto &atom : section->atoms()) { - if (_writeHelper.hasGlobalGOTEntry(atom->_atom)) { - this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), - atom->_virtualAddr, atom); - continue; - } - - const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); - if (!da) - continue; - - if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && - !this->_context.isDynamicallyExportedSymbol(da->name()) && - !(this->_context.shouldExportDynamic() && - da->scope() == Atom::Scope::scopeGlobal)) - continue; - - this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), - atom->_virtualAddr, atom); - } - - for (const UndefinedAtom *a : file.undefined()) - // FIXME (simon): Consider to move this check to the - // MipsELFUndefinedAtom class method. That allows to - // handle more complex coditions in the future. - if (_writeHelper.hasGlobalGOTEntry(a)) - this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); - - // Skip our immediate parent class method - // ExecutableWriter<ELFT>::buildDynamicSymbolTable because we replaced it - // with our own version. Call OutputELFWriter directly. - OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); -} - -template <class ELFT> -bool MipsExecutableWriter<ELFT>::createImplicitFiles( - std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); - result.push_back(std::move(_writeHelper.createRuntimeFile())); - return true; -} - -template <class ELFT> -void MipsExecutableWriter<ELFT>::finalizeDefaultAtomValues() { - // Finalize the atom values that are part of the parent. - ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); - _writeHelper.finalizeMipsRuntimeAtomValues(); -} - -template <class ELFT> -unique_bump_ptr<SymbolTable<ELFT>> - MipsExecutableWriter<ELFT>::createSymbolTable() { - return unique_bump_ptr<SymbolTable<ELFT>>(new ( - this->_alloc) MipsSymbolTable<ELFT>(this->_context)); -} - -/// \brief create dynamic table -template <class ELFT> -unique_bump_ptr<DynamicTable<ELFT>> - MipsExecutableWriter<ELFT>::createDynamicTable() { - return unique_bump_ptr<DynamicTable<ELFT>>(new ( - this->_alloc) MipsDynamicTable<ELFT>(this->_context, _mipsTargetLayout)); -} - -/// \brief create dynamic symbol table -template <class ELFT> -unique_bump_ptr<DynamicSymbolTable<ELFT>> - MipsExecutableWriter<ELFT>::createDynamicSymbolTable() { - return unique_bump_ptr<DynamicSymbolTable<ELFT>>( - new (this->_alloc) MipsDynamicSymbolTable<ELFT>( - this->_context, _mipsTargetLayout)); -} - -} // namespace elf -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp index 7bffcbeb5c08..b6cdd5c1487c 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp @@ -17,43 +17,46 @@ using namespace lld; using namespace lld::elf; std::unique_ptr<ELFLinkingContext> -MipsLinkingContext::create(llvm::Triple triple) { - if (triple.getArch() == llvm::Triple::mipsel || +elf::createMipsLinkingContext(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::mips || + triple.getArch() == llvm::Triple::mipsel || + triple.getArch() == llvm::Triple::mips64 || triple.getArch() == llvm::Triple::mips64el) - return std::unique_ptr<ELFLinkingContext>(new MipsLinkingContext(triple)); + return llvm::make_unique<MipsLinkingContext>(triple); return nullptr; } -typedef std::unique_ptr<TargetHandlerBase> TargetHandlerBasePtr; - -static TargetHandlerBasePtr createTarget(llvm::Triple triple, - MipsLinkingContext &ctx) { +static std::unique_ptr<TargetHandler> createTarget(llvm::Triple triple, + MipsLinkingContext &ctx) { switch (triple.getArch()) { + case llvm::Triple::mips: + return llvm::make_unique<MipsTargetHandler<ELF32BE>>(ctx); case llvm::Triple::mipsel: - return TargetHandlerBasePtr(new MipsTargetHandler<Mips32ELType>(ctx)); + return llvm::make_unique<MipsTargetHandler<ELF32LE>>(ctx); + case llvm::Triple::mips64: + return llvm::make_unique<MipsTargetHandler<ELF64BE>>(ctx); case llvm::Triple::mips64el: - return TargetHandlerBasePtr(new MipsTargetHandler<Mips64ELType>(ctx)); + return llvm::make_unique<MipsTargetHandler<ELF64LE>>(ctx); default: llvm_unreachable("Unhandled arch"); } } MipsLinkingContext::MipsLinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, createTarget(triple, *this)), - _flagsMerger(triple.isArch64Bit()) {} - -uint32_t MipsLinkingContext::getMergedELFFlags() const { - return _flagsMerger.getMergedELFFlags(); -} - -MipsELFFlagsMerger &MipsLinkingContext::getELFFlagsMerger() { - return _flagsMerger; -} + : ELFLinkingContext(triple, createTarget(triple, *this)) {} uint64_t MipsLinkingContext::getBaseAddress() const { - if (_baseAddress == 0 && getOutputELFType() == llvm::ELF::ET_EXEC) - return getTriple().isArch64Bit() ? 0x120000000 : 0x400000; - return _baseAddress; + if (_baseAddress != 0 || getOutputELFType() != llvm::ELF::ET_EXEC) + return _baseAddress; + switch (getAbi()) { + case MipsAbi::O32: + return 0x0400000; + case MipsAbi::N32: + return 0x10000000; + case MipsAbi::N64: + return 0x120000000; + } + llvm_unreachable("unknown MIPS ABI flag"); } StringRef MipsLinkingContext::entrySymbolName() const { @@ -63,7 +66,15 @@ StringRef MipsLinkingContext::entrySymbolName() const { } StringRef MipsLinkingContext::getDefaultInterpreter() const { - return getTriple().isArch64Bit() ? "/lib64/ld.so.1" : "/lib/ld.so.1"; + switch (getAbi()) { + case MipsAbi::O32: + return "/lib/ld.so.1"; + case MipsAbi::N32: + return "/lib32/ld.so.1"; + case MipsAbi::N64: + return "/lib64/ld.so.1"; + } + llvm_unreachable("unknown MIPS ABI flag"); } void MipsLinkingContext::addPasses(PassManager &pm) { @@ -81,13 +92,14 @@ bool MipsLinkingContext::isDynamicRelocation(const Reference &r) const { switch (r.kindValue()) { case llvm::ELF::R_MIPS_COPY: case llvm::ELF::R_MIPS_REL32: + return true; case llvm::ELF::R_MIPS_TLS_DTPMOD32: case llvm::ELF::R_MIPS_TLS_DTPREL32: case llvm::ELF::R_MIPS_TLS_TPREL32: case llvm::ELF::R_MIPS_TLS_DTPMOD64: case llvm::ELF::R_MIPS_TLS_DTPREL64: case llvm::ELF::R_MIPS_TLS_TPREL64: - return true; + return isDynamic(); default: return false; } @@ -113,3 +125,40 @@ bool MipsLinkingContext::isPLTRelocation(const Reference &r) const { return false; } } + +bool MipsLinkingContext::isRelativeReloc(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + switch (r.kindValue()) { + case llvm::ELF::R_MIPS_REL32: + case llvm::ELF::R_MIPS_GPREL16: + case llvm::ELF::R_MIPS_GPREL32: + return true; + default: + return false; + } +} + +MipsAbi MipsLinkingContext::getAbi() const { + auto &handler = static_cast<MipsBaseTargetHandler &>(getTargetHandler()); + return handler.getAbi(); +} + +const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/Mips.def" +#undef ELF_RELOC + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_GOT), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_32_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_64_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_26), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_STO_PLT), + LLD_KIND_STRING_ENTRY(LLD_R_MICROMIPS_GLOBAL_26_S1), + LLD_KIND_STRING_END +}; + +void MipsLinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Mips, kindStrings); +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h index 824605f5fa7f..414d2c785e17 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h @@ -9,7 +9,7 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_LINKING_CONTEXT_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_LINKING_CONTEXT_H -#include "MipsELFFlagsMerger.h" +#include "MipsAbiInfoHandler.h" #include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { @@ -23,10 +23,6 @@ enum { LLD_R_MIPS_32_HI16 = 1025, /// \brief The same as R_MIPS_26 but for global symbols. LLD_R_MIPS_GLOBAL_26 = 1026, - /// \brief Setup hi 16 bits using the symbol this reference refers to. - LLD_R_MIPS_HI16 = 1027, - /// \brief Setup low 16 bits using the symbol this reference refers to. - LLD_R_MIPS_LO16 = 1028, /// \brief Represents a reference between PLT and dynamic symbol. LLD_R_MIPS_STO_PLT = 1029, /// \brief The same as R_MICROMIPS_26_S1 but for global symbols. @@ -35,20 +31,12 @@ enum { LLD_R_MIPS_64_HI16 = 1031, }; -typedef llvm::object::ELFType<llvm::support::little, 2, false> Mips32ELType; -typedef llvm::object::ELFType<llvm::support::little, 2, true> Mips64ELType; -typedef llvm::object::ELFType<llvm::support::big, 2, false> Mips32BEType; -typedef llvm::object::ELFType<llvm::support::big, 2, true> Mips64BEType; - class MipsLinkingContext final : public ELFLinkingContext { public: - static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); MipsLinkingContext(llvm::Triple triple); - uint32_t getMergedELFFlags() const; - MipsELFFlagsMerger &getELFFlagsMerger(); - - // ELFLinkingContext + void registerRelocationNames(Registry &r) override; + int getMachineType() const override { return llvm::ELF::EM_MIPS; } uint64_t getBaseAddress() const override; StringRef entrySymbolName() const override; StringRef getDefaultInterpreter() const override; @@ -57,9 +45,9 @@ public: bool isDynamicRelocation(const Reference &r) const override; bool isCopyRelocation(const Reference &r) const override; bool isPLTRelocation(const Reference &r) const override; + bool isRelativeReloc(const Reference &r) const override; -private: - MipsELFFlagsMerger _flagsMerger; + MipsAbi getAbi() const; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp index 173ce0e6b1a8..c55a7a4116e6 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp @@ -7,9 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "MipsTargetHandler.h" #include "MipsLinkingContext.h" #include "MipsRelocationHandler.h" +#include "MipsTargetLayout.h" +#include "llvm/Support/Format.h" using namespace lld; using namespace elf; @@ -20,306 +21,265 @@ namespace { enum class CrossJumpMode { None, // Not a jump or non-isa-cross jump ToRegular, // cross isa jump to regular symbol - ToMicro // cross isa jump to microMips symbol + ToMicro, // cross isa jump to microMips symbol + ToMicroJalr// cross isa jump to microMips symbol referenced by R_MIPS_JALR }; +typedef std::function<std::error_code(int64_t, bool)> OverflowChecker; + +static std::error_code dummyCheck(int64_t, bool) { + return std::error_code(); +} + +template <int BITS> static std::error_code signedCheck(int64_t res, bool) { + if (llvm::isInt<BITS>(res)) + return std::error_code(); + return make_out_of_range_reloc_error(); +} + +template <int BITS> +static std::error_code gpDispCheck(int64_t res, bool isGpDisp) { + if (!isGpDisp || llvm::isInt<BITS>(res)) + return std::error_code(); + return make_out_of_range_reloc_error(); +} + struct MipsRelocationParams { uint8_t _size; // Relocations's size in bytes uint64_t _mask; // Read/write mask of relocation uint8_t _shift; // Relocation's addendum left shift size bool _shuffle; // Relocation's addendum/result needs to be shuffled + OverflowChecker _overflow; // Check the relocation result }; -template <class ELFT> class RelocationHandler : public MipsRelocationHandler { +template <class ELFT> class RelocationHandler : public TargetRelocationHandler { public: - RelocationHandler(MipsLinkingContext &ctx) : _ctx(ctx) {} + RelocationHandler(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : _ctx(ctx), _targetLayout(layout) {} std::error_code applyRelocation(ELFWriter &writer, llvm::FileOutputBuffer &buf, - const lld::AtomLayout &atom, + const AtomLayout &atom, const Reference &ref) const override; - Reference::Addend readAddend(Reference::KindValue kind, - const uint8_t *content) const override; - private: MipsLinkingContext &_ctx; + MipsTargetLayout<ELFT> &_targetLayout; }; } static MipsRelocationParams getRelocationParams(uint32_t rType) { switch (rType) { case R_MIPS_NONE: - return {4, 0x0, 0, false}; + return {4, 0x0, 0, false, dummyCheck}; case R_MIPS_64: case R_MIPS_SUB: - return {8, 0xffffffffffffffffull, 0, false}; + return {8, 0xffffffffffffffffull, 0, false, dummyCheck}; + case R_MICROMIPS_SUB: + return {8, 0xffffffffffffffffull, 0, true, dummyCheck}; case R_MIPS_32: case R_MIPS_GPREL32: + case R_MIPS_REL32: case R_MIPS_PC32: - return {4, 0xffffffff, 0, false}; + case R_MIPS_EH: + return {4, 0xffffffff, 0, false, dummyCheck}; case LLD_R_MIPS_32_HI16: - return {4, 0xffff0000, 0, false}; + return {4, 0xffff0000, 0, false, dummyCheck}; case LLD_R_MIPS_64_HI16: - return {8, 0xffffffffffff0000ull, 0, false}; + return {8, 0xffffffffffff0000ull, 0, false, dummyCheck}; case R_MIPS_26: case LLD_R_MIPS_GLOBAL_26: - return {4, 0x3ffffff, 2, false}; + return {4, 0x3ffffff, 2, false, dummyCheck}; + case R_MIPS_PC16: + return {4, 0xffff, 2, false, signedCheck<18>}; case R_MIPS_PC18_S3: - return {4, 0x3ffff, 3, false}; + return {4, 0x3ffff, 3, false, signedCheck<21>}; case R_MIPS_PC19_S2: - return {4, 0x7ffff, 2, false}; + return {4, 0x7ffff, 2, false, signedCheck<21>}; case R_MIPS_PC21_S2: - return {4, 0x1fffff, 2, false}; + return {4, 0x1fffff, 2, false, signedCheck<23>}; case R_MIPS_PC26_S2: - return {4, 0x3ffffff, 2, false}; + return {4, 0x3ffffff, 2, false, signedCheck<28>}; case R_MIPS_HI16: + return {4, 0xffff, 0, false, gpDispCheck<16>}; case R_MIPS_LO16: + case R_MIPS_HIGHER: + case R_MIPS_HIGHEST: + return {4, 0xffff, 0, false, dummyCheck}; + case R_MIPS_16: case R_MIPS_PCHI16: case R_MIPS_PCLO16: - case R_MIPS_GPREL16: case R_MIPS_GOT16: + case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: case R_MIPS_GOT_OFST: + case R_MIPS_GPREL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + case R_MIPS_LITERAL: + return {4, 0xffff, 0, false, signedCheck<16>}; + case R_MIPS_GOT_HI16: + case R_MIPS_GOT_LO16: + case R_MIPS_CALL_HI16: + case R_MIPS_CALL_LO16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: - case LLD_R_MIPS_HI16: - case LLD_R_MIPS_LO16: - return {4, 0xffff, 0, false}; + return {4, 0xffff, 0, false, dummyCheck}; + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_LITERAL: + return {4, 0xffff, 0, true, signedCheck<16>}; + case R_MICROMIPS_GPREL7_S2: + return {4, 0x7f, 2, false, signedCheck<9>}; + case R_MICROMIPS_GOT_HI16: + case R_MICROMIPS_GOT_LO16: + case R_MICROMIPS_CALL_HI16: + case R_MICROMIPS_CALL_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: - return {4, 0xffff, 0, true}; + return {4, 0xffff, 0, true, dummyCheck}; case R_MICROMIPS_26_S1: case LLD_R_MICROMIPS_GLOBAL_26_S1: - return {4, 0x3ffffff, 1, true}; + return {4, 0x3ffffff, 1, true, dummyCheck}; case R_MICROMIPS_HI16: + return {4, 0xffff, 0, true, gpDispCheck<16>}; case R_MICROMIPS_LO16: - case R_MICROMIPS_GOT16: - return {4, 0xffff, 0, true}; + case R_MICROMIPS_HI0_LO16: + case R_MICROMIPS_HIGHER: + case R_MICROMIPS_HIGHEST: + return {4, 0xffff, 0, true, dummyCheck}; case R_MICROMIPS_PC16_S1: - return {4, 0xffff, 1, true}; + return {4, 0xffff, 1, true, signedCheck<17>}; case R_MICROMIPS_PC7_S1: - return {4, 0x7f, 1, false}; + return {4, 0x7f, 1, false, signedCheck<8>}; case R_MICROMIPS_PC10_S1: - return {4, 0x3ff, 1, false}; + return {4, 0x3ff, 1, false, signedCheck<11>}; case R_MICROMIPS_PC23_S2: - return {4, 0x7fffff, 2, true}; - case R_MIPS_CALL16: - case R_MIPS_TLS_GD: - case R_MIPS_TLS_LDM: - case R_MIPS_TLS_GOTTPREL: - return {4, 0xffff, 0, false}; + return {4, 0x7fffff, 2, true, signedCheck<25>}; + case R_MICROMIPS_PC18_S3: + return {4, 0x3ffff, 3, true, signedCheck<21>}; + case R_MICROMIPS_PC19_S2: + return {4, 0x7ffff, 2, true, signedCheck<21>}; + case R_MICROMIPS_PC21_S2: + return {4, 0x1fffff, 2, true, signedCheck<23>}; + case R_MICROMIPS_PC26_S2: + return {4, 0x3ffffff, 2, true, signedCheck<28>}; + case R_MICROMIPS_GOT16: case R_MICROMIPS_CALL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: case R_MICROMIPS_TLS_GOTTPREL: - return {4, 0xffff, 0, true}; + case R_MICROMIPS_GOT_DISP: + case R_MICROMIPS_GOT_PAGE: + case R_MICROMIPS_GOT_OFST: + return {4, 0xffff, 0, true, signedCheck<16>}; case R_MIPS_JALR: - return {4, 0x0, 0, false}; + return {4, 0xffffffff, 0, false, dummyCheck}; case R_MICROMIPS_JALR: - return {4, 0x0, 0, true}; - case R_MIPS_REL32: + return {4, 0x0, 0, true, dummyCheck}; case R_MIPS_JUMP_SLOT: case R_MIPS_COPY: case R_MIPS_TLS_DTPMOD32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - // Ignore runtime relocations. - return {4, 0x0, 0, false}; + return {4, 0xffffffff, 0, false, dummyCheck}; case R_MIPS_TLS_DTPMOD64: case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_TPREL64: - return {8, 0x0, 0, false}; + return {8, 0xffffffffffffffffull, 0, false, dummyCheck}; case LLD_R_MIPS_GLOBAL_GOT: case LLD_R_MIPS_STO_PLT: // Do nothing. - return {4, 0x0, 0, false}; + return {4, 0x0, 0, false, dummyCheck}; default: llvm_unreachable("Unknown relocation"); } } -/// \brief R_MIPS_32 -/// local/external: word32 S + A (truncate) -static uint32_t reloc32(uint64_t S, int64_t A) { return S + A; } - -/// \brief R_MIPS_64 -/// local/external: word64 S + A (truncate) -static uint64_t reloc64(uint64_t S, int64_t A) { return S + A; } - -/// \brief R_MIPS_SUB -/// local/external: word64 S - A (truncate) -static uint64_t relocSub(uint64_t S, int64_t A) { return S - A; } - -/// \brief R_MIPS_PC32 -/// local/external: word32 S + A i- P (truncate) -static uint32_t relocpc32(uint64_t P, uint64_t S, int64_t A) { - return S + A - P; -} - -/// \brief R_MIPS_26, R_MICROMIPS_26_S1 -/// local : ((A | ((P + 4) & 0x3F000000)) + S) >> 2 -static uint32_t reloc26loc(uint64_t P, uint64_t S, int32_t A, uint32_t shift) { - uint32_t result = (A | ((P + 4) & (0xfc000000 << shift))) + S; - return result >> shift; -} - -/// \brief LLD_R_MIPS_GLOBAL_26, LLD_R_MICROMIPS_GLOBAL_26_S1 -/// external: (sign-extend(A) + S) >> 2 -static uint32_t reloc26ext(uint64_t S, int32_t A, uint32_t shift) { - int32_t result = - shift == 1 ? llvm::SignExtend32<27>(A) : llvm::SignExtend32<28>(A); - return (result + S) >> shift; -} - -/// \brief R_MIPS_HI16, R_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_TPREL_HI16, -/// R_MICROMIPS_HI16, R_MICROMIPS_TLS_DTPREL_HI16, R_MICROMIPS_TLS_TPREL_HI16, -/// LLD_R_MIPS_HI16 -/// local/external: hi16 (AHL + S) - (short)(AHL + S) (truncate) -/// _gp_disp : hi16 (AHL + GP - P) - (short)(AHL + GP - P) (verify) -static uint32_t relocHi16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp) { - int32_t result = isGPDisp ? AHL + S - P : AHL + S; - return (result + 0x8000) >> 16; -} +template <class ELFT> +static uint64_t relocRead(const MipsRelocationParams ¶ms, + const uint8_t *loc); -/// \brief R_MIPS_PCHI16 -/// local/external: hi16 (S + AHL - P) -static uint32_t relocPcHi16(uint64_t P, uint64_t S, int64_t AHL) { - int32_t result = S + AHL - P; - return (result + 0x8000) >> 16; +static int64_t getHi16(int64_t value) { + return ((value + 0x8000) >> 16) & 0xffff; } -/// \brief R_MIPS_LO16, R_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_TPREL_LO16, -/// R_MICROMIPS_LO16, R_MICROMIPS_TLS_DTPREL_LO16, R_MICROMIPS_TLS_TPREL_LO16, -/// LLD_R_MIPS_LO16 -/// local/external: lo16 AHL + S (truncate) -/// _gp_disp : lo16 AHL + GP - P + 4 (verify) -static uint32_t relocLo16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp, - bool micro) { - int32_t result = isGPDisp ? AHL + S - P + (micro ? 3 : 4) : AHL + S; - return result; +static int64_t getHigher16(int64_t value) { + return ((value + 0x80008000ull) >> 32) & 0xffff; } -/// \brief R_MIPS_PCLO16 -/// local/external: lo16 (S + AHL - P) -static uint32_t relocPcLo16(uint64_t P, uint64_t S, int64_t AHL) { - AHL = llvm::SignExtend32<16>(AHL); - int32_t result = S + AHL - P; - return result; +static int64_t getHighest16(int64_t value) { + return ((value + 0x800080008000ull) >> 48) & 0xffff; } -/// \brief R_MIPS_GOT16, R_MIPS_CALL16, R_MICROMIPS_GOT16, R_MICROMIPS_CALL16 -/// rel16 G (verify) -static uint64_t relocGOT(uint64_t S, uint64_t GP) { - int64_t G = (int64_t)(S - GP); - return G; +static int64_t maskLow16(int64_t value) { + return (value + 0x8000) & ~0xffff; } -/// R_MIPS_GOT_OFST +/// R_MIPS_GOT_OFST, R_MICROMIPS_GOT_OFST /// rel16 offset of (S+A) from the page pointer (verify) -static uint32_t relocGOTOfst(uint64_t S, int64_t A) { - uint64_t page = (S + A + 0x8000) & ~0xffff; +static int32_t relocGOTOfst(uint64_t S, int64_t A) { + int64_t page = maskLow16(S + A); return S + A - page; } -/// \brief R_MIPS_GPREL16 -/// local: sign-extend(A) + S + GP0 - GP -/// external: sign-extend(A) + S - GP -static uint64_t relocGPRel16(uint64_t S, int64_t A, uint64_t GP) { - // We added GP0 to addendum for a local symbol during a Relocation pass. - return llvm::SignExtend32<16>(A) + S - GP; -} - -/// \brief R_MIPS_GPREL32 -/// local: rel32 A + S + GP0 - GP (truncate) -static uint64_t relocGPRel32(uint64_t S, int64_t A, uint64_t GP) { - // We added GP0 to addendum for a local symbol during a Relocation pass. - return A + S - GP; -} - -/// \brief R_MIPS_PC18_S3 -/// local/external: (S + A - P) >> 3 (P with cleared 3 less significant bits) -static uint32_t relocPc18(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<21>(A); - // FIXME (simon): Check that S + A has 8-byte alignment - int32_t result = S + A - ((P | 7) ^ 7); - return result >> 3; -} - -/// \brief R_MIPS_PC19_S2 +/// \brief R_MIPS_PC16 /// local/external: (S + A - P) >> 2 -static uint32_t relocPc19(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<21>(A); - // FIXME (simon): Check that S + A has 4-byte alignment - int32_t result = S + A - P; - return result >> 2; +static ErrorOr<int64_t> relocPc16(uint64_t P, uint64_t S, int64_t A) { + if ((S + A) & 3) + return make_unaligned_range_reloc_error(); + return S + A - P; } -/// \brief R_MIPS_PC21_S2 -/// local/external: (S + A - P) >> 2 -static uint32_t relocPc21(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<23>(A); - // FIXME (simon): Check that S + A has 4-byte alignment - int32_t result = S + A - P; - return result >> 2; +/// \brief R_MIPS_PC18_S3, R_MICROMIPS_PC18_S3 +/// local/external: (S + A - P) >> 3 (P with cleared 3 less significant bits) +static ErrorOr<int64_t> relocPc18(uint64_t P, uint64_t S, int64_t A) { + if ((S + A) & 6) + return make_unaligned_range_reloc_error(); + return S + A - ((P | 7) ^ 7); } -/// \brief R_MIPS_PC26_S2 +/// \brief R_MIPS_PC19_S2, R_MICROMIPS_PC19_S2, R_MIPS_PC21_S2, +/// R_MICROMIPS_PC21_S2, R_MIPS_PC26_S2, R_MICROMIPS_PC26_S2 /// local/external: (S + A - P) >> 2 -static uint32_t relocPc26(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<28>(A); - // FIXME (simon): Check that S + A has 4-byte alignment - int32_t result = S + A - P; - return result >> 2; -} - -/// \brief R_MICROMIPS_PC7_S1 -static uint32_t relocPc7(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<8>(A); - int32_t result = S + A - P; - return result >> 1; -} - -/// \brief R_MICROMIPS_PC10_S1 -static uint32_t relocPc10(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<11>(A); - int32_t result = S + A - P; - return result >> 1; -} - -/// \brief R_MICROMIPS_PC16_S1 -static uint32_t relocPc16(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<17>(A); - int32_t result = S + A - P; - return result >> 1; -} - -/// \brief R_MICROMIPS_PC23_S2 -static uint32_t relocPc23(uint64_t P, uint64_t S, int64_t A) { - A = llvm::SignExtend32<25>(A); - int32_t result = S + A - P; - - // Check addiupc 16MB range. - if (result + 0x1000000 >= 0x2000000) - llvm::errs() << "The addiupc instruction immediate " - << llvm::format_hex(result, 10) << " is out of range.\n"; - - return result >> 2; +static ErrorOr<int64_t> relocPcS2(uint64_t P, uint64_t S, int64_t A) { + if ((S + A) & 2) + return make_unaligned_range_reloc_error(); + return S + A - P; } -/// \brief LLD_R_MIPS_32_HI16, LLD_R_MIPS_64_HI16 -static uint64_t relocMaskLow16(uint64_t S, int64_t A) { - return S + A + 0x8000; +template <class ELFT> +static ErrorOr<int64_t> relocJalr(uint64_t P, uint64_t S, bool isCrossJump, + uint8_t *location) { + uint64_t ins = relocRead<ELFT>(getRelocationParams(R_MIPS_JALR), location); + if (isCrossJump) + return ins; + int64_t off = S - P - 4; + if (!llvm::isInt<18>(off)) + return ins; + if (ins == 0x0320f809) // jalr t9 + return 0x04110000 | ((off >> 2) & 0xffff); + if (ins == 0x03200008) // jr t9 + return 0x10000000 | ((off >> 2) & 0xffff); + return ins; +} + +static int64_t relocRel32(uint64_t S, int64_t A, bool isLocal) { + // If output relocation format is REL and the input one is RELA, the only + // method to transfer the relocation addend from the input relocation + // to the output dynamic relocation is to save this addend to the location + // modified by R_MIPS_REL32. + return isLocal ? S + A : A; } static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, CrossJumpMode mode) { - if (mode == CrossJumpMode::None) + if (mode == CrossJumpMode::None || mode == CrossJumpMode::ToMicroJalr) return std::error_code(); bool toMicro = mode == CrossJumpMode::ToMicro; @@ -327,8 +287,7 @@ static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, uint32_t opCross = toMicro ? 0x1d : 0x3c; if ((tgt & 1) != toMicro) - return make_dynamic_error_code( - Twine("Incorrect bit 0 for the jalx target")); + return make_dynamic_error_code("Incorrect bit 0 for the jalx target"); if (tgt & 2) return make_dynamic_error_code(Twine("The jalx target 0x") + @@ -356,6 +315,8 @@ static CrossJumpMode getCrossJumpMode(const Reference &ref) { return CrossJumpMode::None; bool isTgtMicro = isMicroMipsAtom(ref.target()); switch (ref.kindValue()) { + case R_MIPS_JALR: + return isTgtMicro ? CrossJumpMode::ToMicroJalr : CrossJumpMode::None; case R_MIPS_26: case LLD_R_MIPS_GLOBAL_26: return isTgtMicro ? CrossJumpMode::ToMicro : CrossJumpMode::None; @@ -367,44 +328,64 @@ static CrossJumpMode getCrossJumpMode(const Reference &ref) { } } -static uint32_t microShuffle(uint32_t ins) { - return ((ins & 0xffff) << 16) | ((ins & 0xffff0000) >> 16); -} - -static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, - Reference::Addend addend, - uint64_t tgtAddr, uint64_t relAddr, - uint64_t gpAddr, bool isGP, - CrossJumpMode jumpMode) { - bool isCrossJump = jumpMode != CrossJumpMode::None; +template <class ELFT> +static ErrorOr<int64_t> +calculateRelocation(Reference::KindValue kind, Reference::Addend addend, + uint64_t tgtAddr, uint64_t relAddr, uint64_t gpAddr, + uint8_t *location, bool isGP, bool isCrossJump, + bool isDynamic, bool isLocalSym) { switch (kind) { case R_MIPS_NONE: return 0; + case R_MIPS_16: case R_MIPS_32: - return reloc32(tgtAddr, addend); case R_MIPS_64: - return reloc64(tgtAddr, addend); + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_LO16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_LO16: + case LLD_R_MIPS_GLOBAL_26: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return tgtAddr + addend; case R_MIPS_SUB: - return relocSub(tgtAddr, addend); + case R_MICROMIPS_SUB: + return tgtAddr - addend; case R_MIPS_26: - return reloc26loc(relAddr, tgtAddr, addend, 2); + return tgtAddr + (addend | (relAddr & 0xf0000000)); case R_MICROMIPS_26_S1: - return reloc26loc(relAddr, tgtAddr, addend, isCrossJump ? 2 : 1); + return tgtAddr + (addend | (relAddr & 0xf8000000)); case R_MIPS_HI16: case R_MICROMIPS_HI16: - return relocHi16(relAddr, tgtAddr, addend, isGP); + return getHi16(tgtAddr + addend - (isGP ? relAddr : 0)); case R_MIPS_PCHI16: - return relocPcHi16(relAddr, tgtAddr, addend); + return getHi16(tgtAddr + addend - relAddr); case R_MIPS_LO16: - return relocLo16(relAddr, tgtAddr, addend, isGP, false); - case R_MIPS_PCLO16: - return relocPcLo16(relAddr, tgtAddr, addend); + return tgtAddr + addend - (isGP ? relAddr - 4 : 0); case R_MICROMIPS_LO16: - return relocLo16(relAddr, tgtAddr, addend, isGP, true); + case R_MICROMIPS_HI0_LO16: + return tgtAddr + addend - (isGP ? relAddr - 3 : 0); + case R_MIPS_GOT_HI16: + case R_MIPS_CALL_HI16: + case R_MICROMIPS_GOT_HI16: + case R_MICROMIPS_CALL_HI16: + return getHi16(tgtAddr - gpAddr); + case R_MIPS_HIGHER: + case R_MICROMIPS_HIGHER: + return getHigher16(tgtAddr + addend); + case R_MIPS_HIGHEST: + case R_MICROMIPS_HIGHEST: + return getHighest16(tgtAddr + addend); + case R_MIPS_GOT_LO16: + case R_MIPS_CALL_LO16: + case R_MICROMIPS_GOT_LO16: + case R_MICROMIPS_CALL_LO16: + case R_MIPS_EH: case R_MIPS_GOT16: case R_MIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: + case R_MICROMIPS_GOT_DISP: + case R_MICROMIPS_GOT_PAGE: case R_MICROMIPS_GOT16: case R_MICROMIPS_CALL16: case R_MIPS_TLS_GD: @@ -413,71 +394,66 @@ static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: case R_MICROMIPS_TLS_GOTTPREL: - return relocGOT(tgtAddr, gpAddr); + return tgtAddr - gpAddr; + case R_MIPS_GPREL16: + case R_MIPS_GPREL32: + case R_MIPS_LITERAL: + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_GPREL7_S2: + case R_MICROMIPS_LITERAL: + return tgtAddr + addend - gpAddr; case R_MIPS_GOT_OFST: + case R_MICROMIPS_GOT_OFST: return relocGOTOfst(tgtAddr, addend); + case R_MIPS_PC16: + return relocPc16(relAddr, tgtAddr, addend); case R_MIPS_PC18_S3: + case R_MICROMIPS_PC18_S3: return relocPc18(relAddr, tgtAddr, addend); case R_MIPS_PC19_S2: - return relocPc19(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC19_S2: case R_MIPS_PC21_S2: - return relocPc21(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC21_S2: case R_MIPS_PC26_S2: - return relocPc26(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC26_S2: + return relocPcS2(relAddr, tgtAddr, addend); + case R_MIPS_PC32: + case R_MIPS_PCLO16: case R_MICROMIPS_PC7_S1: - return relocPc7(relAddr, tgtAddr, addend); case R_MICROMIPS_PC10_S1: - return relocPc10(relAddr, tgtAddr, addend); case R_MICROMIPS_PC16_S1: - return relocPc16(relAddr, tgtAddr, addend); case R_MICROMIPS_PC23_S2: - return relocPc23(relAddr, tgtAddr, addend); + return tgtAddr + addend - relAddr; case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_TPREL_HI16: - return relocHi16(0, tgtAddr, addend, false); - case R_MIPS_TLS_DTPREL_LO16: - case R_MIPS_TLS_TPREL_LO16: - return relocLo16(0, tgtAddr, addend, false, false); - case R_MICROMIPS_TLS_DTPREL_LO16: - case R_MICROMIPS_TLS_TPREL_LO16: - return relocLo16(0, tgtAddr, addend, false, true); - case R_MIPS_GPREL16: - return relocGPRel16(tgtAddr, addend, gpAddr); - case R_MIPS_GPREL32: - return relocGPRel32(tgtAddr, addend, gpAddr); + return getHi16(tgtAddr + addend); case R_MIPS_JALR: + return relocJalr<ELFT>(relAddr, tgtAddr, isCrossJump, location); case R_MICROMIPS_JALR: // We do not do JALR optimization now. return 0; case R_MIPS_REL32: + return relocRel32(tgtAddr, addend, isLocalSym); case R_MIPS_JUMP_SLOT: case R_MIPS_COPY: + // Ignore runtime relocations. + return 0; case R_MIPS_TLS_DTPMOD32: - case R_MIPS_TLS_DTPREL32: - case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_DTPMOD64: + return isDynamic ? 0 : 1; + case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_DTPREL64: + return isDynamic ? 0 : tgtAddr + addend - 0x8000; + case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_TPREL64: - // Ignore runtime relocations. - return 0; - case R_MIPS_PC32: - return relocpc32(relAddr, tgtAddr, addend); - case LLD_R_MIPS_GLOBAL_GOT: - // Do nothing. + return isDynamic ? 0 : tgtAddr + addend - 0x7000; case LLD_R_MIPS_32_HI16: case LLD_R_MIPS_64_HI16: - return relocMaskLow16(tgtAddr, addend); - case LLD_R_MIPS_GLOBAL_26: - return reloc26ext(tgtAddr, addend, 2); - case LLD_R_MICROMIPS_GLOBAL_26_S1: - return reloc26ext(tgtAddr, addend, isCrossJump ? 2 : 1); - case LLD_R_MIPS_HI16: - return relocHi16(0, tgtAddr, 0, false); - case LLD_R_MIPS_LO16: - return relocLo16(0, tgtAddr, 0, false, false); + return maskLow16(tgtAddr + addend); case LLD_R_MIPS_STO_PLT: + case LLD_R_MIPS_GLOBAL_GOT: // Do nothing. return 0; default: @@ -488,27 +464,29 @@ static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, template <class ELFT> static uint64_t relocRead(const MipsRelocationParams ¶ms, const uint8_t *loc) { - uint64_t data; + assert((params._size == 4 || params._size == 8) && "Unexpected size"); + uint64_t data = 0; + memcpy(&data, loc, params._size); + if (params._shuffle) { + using namespace endian; + auto p = reinterpret_cast<const uint8_t *>(&data); + uint32_t a = readNext<uint16_t, ELFT::TargetEndianness, unaligned>(p); + uint32_t b = read<uint16_t, ELFT::TargetEndianness, unaligned>(p); + write<uint32_t, ELFT::TargetEndianness, unaligned>(&data, a << 16 | b); + } switch (params._size) { case 4: - data = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); - break; + return endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(&data); case 8: - data = endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(loc); - break; + return endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(&data); default: llvm_unreachable("Unexpected size"); } - if (params._shuffle) - data = microShuffle(data); - return data; } template <class ELFT> static void relocWrite(uint64_t data, const MipsRelocationParams ¶ms, uint8_t *loc) { - if (params._shuffle) - data = microShuffle(data); switch (params._size) { case 4: endian::write<uint32_t, ELFT::TargetEndianness, unaligned>(loc, data); @@ -519,24 +497,50 @@ static void relocWrite(uint64_t data, const MipsRelocationParams ¶ms, default: llvm_unreachable("Unexpected size"); } + if (params._shuffle) { + uint32_t v = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); + uint16_t a = v >> 16; + uint16_t b = v & 0xffff; + endian::write<uint16_t, ELFT::TargetEndianness, unaligned>(loc, a); + endian::write<uint16_t, ELFT::TargetEndianness, unaligned>(loc + 2, b); + } +} + +static uint32_t getRelKind(const Reference &ref, size_t num) { + if (num == 0) + return ref.kindValue(); + if (num > 2) + return R_MIPS_NONE; + return (ref.tag() >> (8 * (num - 1))) & 0xff; +} + +static uint8_t getRelShift(Reference::KindValue kind, + const MipsRelocationParams ¶ms, + bool isCrossJump) { + uint8_t shift = params._shift; + if (isCrossJump && + (kind == R_MICROMIPS_26_S1 || kind == LLD_R_MICROMIPS_GLOBAL_26_S1)) + return 2; + return shift; +} + +static bool isLocalTarget(const Atom *a) { + if (auto *da = dyn_cast<DefinedAtom>(a)) + return da->scope() == Atom::scopeTranslationUnit; + return false; } template <class ELFT> std::error_code RelocationHandler<ELFT>::applyRelocation( - ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom, const Reference &ref) const { - if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); assert(ref.kindArch() == Reference::KindArch::Mips); - auto &targetLayout = static_cast<MipsTargetLayout<ELFT> &>( - _ctx.getTargetHandler<ELFT>().getTargetLayout()); - - AtomLayout *gpAtom = targetLayout.getGP(); - uint64_t gpAddr = gpAtom ? gpAtom->_virtualAddr : 0; - - AtomLayout *gpDispAtom = targetLayout.getGPDisp(); - bool isGpDisp = gpDispAtom && ref.target() == gpDispAtom->_atom; + uint64_t gpAddr = _targetLayout.getGPAddr(); + bool isGpDisp = ref.target()->name() == "_gp_disp"; + bool isLocalSym = isLocalTarget(ref.target()); uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; uint8_t *location = atomContent + ref.offsetInAtom(); @@ -547,26 +551,37 @@ std::error_code RelocationHandler<ELFT>::applyRelocation( tgtAddr |= 1; CrossJumpMode jumpMode = getCrossJumpMode(ref); - - ErrorOr<uint64_t> res = - calculateRelocation(ref.kindValue(), ref.addend(), tgtAddr, relAddr, - gpAddr, isGpDisp, jumpMode); - if (auto ec = res.getError()) - return ec; - - Reference::KindValue op = ref.kindValue(); - - // FIXME (simon): Handle r_ssym value. - for (auto tag = (ref.tag() & 0xffff); tag & 0xff; tag >>= 8) { - op = tag & 0xff; - res = calculateRelocation(op, *res, 0, relAddr, gpAddr, isGpDisp, jumpMode); + bool isCrossJump = jumpMode != CrossJumpMode::None; + + uint64_t sym = tgtAddr; + ErrorOr<int64_t> res = ref.addend(); + Reference::KindValue lastRel = R_MIPS_NONE; + + for (size_t relNum = 0; relNum < 3; ++relNum) { + Reference::KindValue kind = getRelKind(ref, relNum); + if (kind == R_MIPS_NONE) + break; + auto params = getRelocationParams(kind); + res = calculateRelocation<ELFT>(kind, *res, sym, relAddr, gpAddr, location, + isGpDisp, isCrossJump, _ctx.isDynamic(), + isLocalSym); if (auto ec = res.getError()) return ec; + // Check result for the last relocation only. + if (getRelKind(ref, relNum + 1) == R_MIPS_NONE) { + if (auto ec = params._overflow(*res, isGpDisp)) + return ec; + } + res = *res >> getRelShift(kind, params, isCrossJump); + // FIXME (simon): Handle r_ssym value. + sym = 0; + isGpDisp = false; + isCrossJump = false; + lastRel = kind; } - auto params = getRelocationParams(op); + auto params = getRelocationParams(lastRel); uint64_t ins = relocRead<ELFT>(params, location); - if (auto ec = adjustJumpOpCode(ins, tgtAddr, jumpMode)) return ec; @@ -576,31 +591,97 @@ std::error_code RelocationHandler<ELFT>::applyRelocation( return std::error_code(); } -template <class ELFT> -Reference::Addend -RelocationHandler<ELFT>::readAddend(Reference::KindValue kind, - const uint8_t *content) const { - auto params = getRelocationParams(kind); - uint64_t ins = relocRead<ELFT>(params, content); - return (ins & params._mask) << params._shift; -} - namespace lld { namespace elf { template <> std::unique_ptr<TargetRelocationHandler> -createMipsRelocationHandler<Mips32ELType>(MipsLinkingContext &ctx) { - return std::unique_ptr<TargetRelocationHandler>( - new RelocationHandler<Mips32ELType>(ctx)); +createMipsRelocationHandler<ELF32BE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF32BE> &layout) { + return llvm::make_unique<RelocationHandler<ELF32BE>>(ctx, layout); } template <> std::unique_ptr<TargetRelocationHandler> -createMipsRelocationHandler<Mips64ELType>(MipsLinkingContext &ctx) { - return std::unique_ptr<TargetRelocationHandler>( - new RelocationHandler<Mips64ELType>(ctx)); +createMipsRelocationHandler<ELF32LE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF32LE> &layout) { + return llvm::make_unique<RelocationHandler<ELF32LE>>(ctx, layout); } +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<ELF64BE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF64BE> &layout) { + return llvm::make_unique<RelocationHandler<ELF64BE>>(ctx, layout); +} + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<ELF64LE>(MipsLinkingContext &ctx, + MipsTargetLayout<ELF64LE> &layout) { + return llvm::make_unique<RelocationHandler<ELF64LE>>(ctx, layout); +} + +template <class ELFT> +Reference::Addend readMipsRelocAddend(Reference::KindValue kind, + const uint8_t *content) { + auto params = getRelocationParams(kind); + uint64_t ins = relocRead<ELFT>(params, content); + int64_t res = (ins & params._mask) << params._shift; + switch (kind) { + case R_MIPS_GPREL16: + case R_MICROMIPS_GPREL16: + case R_MIPS_PCLO16: + case R_MIPS_LITERAL: + case R_MICROMIPS_LITERAL: + return llvm::SignExtend32<16>(res); + case R_MIPS_PC16: + return llvm::SignExtend32<18>(res); + case R_MICROMIPS_GPREL7_S2: + return llvm::SignExtend32<9>(res); + case R_MICROMIPS_PC7_S1: + return llvm::SignExtend32<8>(res); + case R_MICROMIPS_PC10_S1: + return llvm::SignExtend32<11>(res); + case R_MIPS_16: + return llvm::SignExtend32<16>(res); + case R_MICROMIPS_PC16_S1: + return llvm::SignExtend32<17>(res); + case R_MIPS_PC18_S3: + case R_MIPS_PC19_S2: + case R_MICROMIPS_PC18_S3: + case R_MICROMIPS_PC19_S2: + return llvm::SignExtend32<21>(res); + case R_MIPS_PC21_S2: + case R_MICROMIPS_PC21_S2: + return llvm::SignExtend32<23>(res); + case R_MICROMIPS_PC23_S2: + return llvm::SignExtend32<25>(res); + case R_MICROMIPS_26_S1: + return llvm::SignExtend32<27>(res); + case R_MIPS_26: + case R_MIPS_PC26_S2: + case R_MICROMIPS_PC26_S2: + return llvm::SignExtend32<28>(res); + default: + // Nothing to do + break; + } + return res; +} + +template +Reference::Addend readMipsRelocAddend<ELF32BE>(Reference::KindValue kind, + const uint8_t *content); +template +Reference::Addend readMipsRelocAddend<ELF32LE>(Reference::KindValue kind, + const uint8_t *content); +template +Reference::Addend readMipsRelocAddend<ELF64BE>(Reference::KindValue kind, + const uint8_t *content); +template +Reference::Addend readMipsRelocAddend<ELF64LE>(Reference::KindValue kind, + const uint8_t *content); + } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h index 87066b2b5c10..62a7aee34496 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h @@ -9,22 +9,22 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_RELOCATION_HANDLER_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_RELOCATION_HANDLER_H -#include "TargetHandler.h" #include "lld/Core/Reference.h" namespace lld { namespace elf { -class MipsRelocationHandler : public TargetRelocationHandler { -public: - virtual Reference::Addend readAddend(Reference::KindValue kind, - const uint8_t *content) const = 0; -}; +class MipsLinkingContext; +template<typename ELFT> class MipsTargetLayout; template <class ELFT> std::unique_ptr<TargetRelocationHandler> -createMipsRelocationHandler(MipsLinkingContext &ctx); +createMipsRelocationHandler(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout); +template <class ELFT> +Reference::Addend readMipsRelocAddend(Reference::KindValue kind, + const uint8_t *content); } // elf } // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp index a1b3530dfcdf..b47c7d2210db 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp @@ -37,20 +37,69 @@ static const uint8_t mipsGotTlsGdAtomContent[] = { 0x00, 0x00, 0x00, 0x00 }; -// Regular PLT0 entry -static const uint8_t mipsPlt0AtomContent[] = { +// Regular big-endian PLT0 entry +static const uint8_t mipsBePlt0AtomContent[] = { + 0x3c, 0x1c, 0x00, 0x00, // lui $28, %hi(&GOTPLT[0]) + 0x8f, 0x99, 0x00, 0x00, // lw $25, %lo(&GOTPLT[0])($28) + 0x27, 0x9c, 0x00, 0x00, // addiu $28, $28, %lo(&GOTPLT[0]) + 0x03, 0x1c, 0xc0, 0x23, // subu $24, $24, $28 + 0x03, 0xe0, 0x78, 0x25, // move $15, $31 + 0x00, 0x18, 0xc0, 0x82, // srl $24, $24, 2 + 0x03, 0x20, 0xf8, 0x09, // jalr $25 + 0x27, 0x18, 0xff, 0xfe // subu $24, $24, 2 +}; + +// Regular little-endian PLT0 entry +static const uint8_t mipsLePlt0AtomContent[] = { 0x00, 0x00, 0x1c, 0x3c, // lui $28, %hi(&GOTPLT[0]) 0x00, 0x00, 0x99, 0x8f, // lw $25, %lo(&GOTPLT[0])($28) 0x00, 0x00, 0x9c, 0x27, // addiu $28, $28, %lo(&GOTPLT[0]) 0x23, 0xc0, 0x1c, 0x03, // subu $24, $24, $28 - 0x21, 0x78, 0xe0, 0x03, // move $15, $31 + 0x25, 0x78, 0xe0, 0x03, // move $15, $31 0x82, 0xc0, 0x18, 0x00, // srl $24, $24, 2 0x09, 0xf8, 0x20, 0x03, // jalr $25 0xfe, 0xff, 0x18, 0x27 // subu $24, $24, 2 }; -// microMIPS PLT0 entry -static const uint8_t micromipsPlt0AtomContent[] = { +// N32 big-endian PLT0 entry +static const uint8_t mipsN32BePlt0AtomContent[] = { + 0x3c, 0x0e, 0x00, 0x00, // lui $14, %hi(&GOTPLT[0]) + 0x8d, 0xd9, 0x00, 0x00, // lw $25, %lo(&GOTPLT[0])($14) + 0x25, 0xce, 0x00, 0x00, // addiu $14, $14, %lo(&GOTPLT[0]) + 0x03, 0x0e, 0xc0, 0x23, // subu $24, $24, $14 + 0x03, 0xe0, 0x78, 0x25, // move $15, $31 + 0x00, 0x18, 0xc0, 0x82, // srl $24, $24, 2 + 0x03, 0x20, 0xf8, 0x09, // jalr $25 + 0x27, 0x18, 0xff, 0xfe // subu $24, $24, 2 +}; + +// N32 little-endian PLT0 entry +static const uint8_t mipsN32LePlt0AtomContent[] = { + 0x00, 0x00, 0x0e, 0x3c, // lui $14, %hi(&GOTPLT[0]) + 0x00, 0x00, 0xd9, 0x8d, // lw $25, %lo(&GOTPLT[0])($14) + 0x00, 0x00, 0xce, 0x25, // addiu $14, $14, %lo(&GOTPLT[0]) + 0x23, 0xc0, 0x0e, 0x03, // subu $24, $24, $14 + 0x25, 0x78, 0xe0, 0x03, // move $15, $31 + 0x82, 0xc0, 0x18, 0x00, // srl $24, $24, 2 + 0x09, 0xf8, 0x20, 0x03, // jalr $25 + 0xfe, 0xff, 0x18, 0x27 // subu $24, $24, 2 +}; + +// microMIPS big-endian PLT0 entry +static const uint8_t microMipsBePlt0AtomContent[] = { + 0x79, 0x80, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . + 0xff, 0x23, 0x00, 0x00, // lw $25, 0($3) + 0x05, 0x35, // subu $2, $2, $3 + 0x25, 0x25, // srl $2, $2, 2 + 0x33, 0x02, 0xff, 0xfe, // subu $24, $2, 2 + 0x0d, 0xff, // move $15, $31 + 0x45, 0xf9, // jalrs $25 + 0x0f, 0x83, // move $28, $3 + 0x0c, 0x00 // nop +}; + +// microMIPS little-endian PLT0 entry +static const uint8_t microMipsLePlt0AtomContent[] = { 0x80, 0x79, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . 0x23, 0xff, 0x00, 0x00, // lw $25, 0($3) 0x35, 0x05, // subu $2, $2, $3 @@ -62,40 +111,80 @@ static const uint8_t micromipsPlt0AtomContent[] = { 0x00, 0x0c // nop }; -// Regular PLT entry -static const uint8_t mipsPltAAtomContent[] = { +// Regular big-endian PLT entry +static const uint8_t mipsBePltAAtomContent[] = { + 0x3c, 0x0f, 0x00, 0x00, // lui $15, %hi(.got.plt entry) + 0x8d, 0xf9, 0x00, 0x00, // l[wd] $25, %lo(.got.plt entry)($15) + 0x03, 0x20, 0x00, 0x08, // jr $25 + 0x25, 0xf8, 0x00, 0x00 // addiu $24, $15, %lo(.got.plt entry) +}; + +// Regular little-endian PLT entry +static const uint8_t mipsLePltAAtomContent[] = { 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) 0x08, 0x00, 0x20, 0x03, // jr $25 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) }; -// microMIPS PLT entry -static const uint8_t micromipsPltAtomContent[] = { +// microMIPS big-endian PLT entry +static const uint8_t microMipsBePltAAtomContent[] = { + 0x79, 0x00, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . + 0xff, 0x22, 0x00, 0x00, // lw $25, 0($2) + 0x45, 0x99, // jr $25 + 0x0f, 0x02 // move $24, $2 +}; + +// microMIPS little-endian PLT entry +static const uint8_t microMipsLePltAAtomContent[] = { 0x00, 0x79, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . 0x22, 0xff, 0x00, 0x00, // lw $25, 0($2) 0x99, 0x45, // jr $25 0x02, 0x0f // move $24, $2 }; -// R6 PLT entry -static const uint8_t mipsR6PltAAtomContent[] = { +// R6 big-endian PLT entry +static const uint8_t mipsR6BePltAAtomContent[] = { + 0x3c, 0x0f, 0x00, 0x00, // lui $15, %hi(.got.plt entry) + 0x8d, 0xf9, 0x00, 0x00, // l[wd] $25, %lo(.got.plt entry)($15) + 0x03, 0x20, 0x00, 0x09, // jr $25 + 0x25, 0xf8, 0x00, 0x00 // addiu $24, $15, %lo(.got.plt entry) +}; + +// R6 little-endian PLT entry +static const uint8_t mipsR6LePltAAtomContent[] = { 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) 0x09, 0x00, 0x20, 0x03, // jr $25 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) }; -// LA25 stub entry -static const uint8_t mipsLA25AtomContent[] = { +// LA25 big-endian stub entry +static const uint8_t mipsBeLA25AtomContent[] = { + 0x3c, 0x19, 0x00, 0x00, // lui $25, %hi(func) + 0x08, 0x00, 0x00, 0x00, // j func + 0x27, 0x39, 0x00, 0x00, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +// LA25 little-endian stub entry +static const uint8_t mipsLeLA25AtomContent[] = { 0x00, 0x00, 0x19, 0x3c, // lui $25, %hi(func) 0x00, 0x00, 0x00, 0x08, // j func 0x00, 0x00, 0x39, 0x27, // addiu $25, $25, %lo(func) 0x00, 0x00, 0x00, 0x00 // nop }; -// microMIPS LA25 stub entry -static const uint8_t micromipsLA25AtomContent[] = { +// microMIPS LA25 big-endian stub entry +static const uint8_t microMipsBeLA25AtomContent[] = { + 0x41, 0xbe, 0x00, 0x00, // lui $25, %hi(func) + 0xd4, 0x00, 0x00, 0x00, // j func + 0x33, 0x39, 0x00, 0x00, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +// microMIPS LA25 little-endian stub entry +static const uint8_t microMipsLeLA25AtomContent[] = { 0xb9, 0x41, 0x00, 0x00, // lui $25, %hi(func) 0x00, 0xd4, 0x00, 0x00, // j func 0x39, 0x33, 0x00, 0x00, // addiu $25, $25, %lo(func) @@ -109,7 +198,7 @@ class MipsGOTAtom : public GOTAtom { public: MipsGOTAtom(const File &f) : GOTAtom(f, ".got") {} - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } }; /// \brief MIPS GOT entry initialized by zero. @@ -120,10 +209,16 @@ public: ArrayRef<uint8_t> rawContent() const override; }; -template <> ArrayRef<uint8_t> GOT0Atom<Mips32ELType>::rawContent() const { +template <> ArrayRef<uint8_t> GOT0Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); +} +template <> ArrayRef<uint8_t> GOT0Atom<ELF32LE>::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } -template <> ArrayRef<uint8_t> GOT0Atom<Mips64ELType>::rawContent() const { +template <> ArrayRef<uint8_t> GOT0Atom<ELF64BE>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent); +} +template <> ArrayRef<uint8_t> GOT0Atom<ELF64LE>::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent); } @@ -136,11 +231,19 @@ public: }; template <> -ArrayRef<uint8_t> GOTModulePointerAtom<Mips32ELType>::rawContent() const { +ArrayRef<uint8_t> GOTModulePointerAtom<ELF32BE>::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); } template <> -ArrayRef<uint8_t> GOTModulePointerAtom<Mips64ELType>::rawContent() const { +ArrayRef<uint8_t> GOTModulePointerAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); +} +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<ELF64BE>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent); +} +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<ELF64LE>::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent); } @@ -152,12 +255,17 @@ public: ArrayRef<uint8_t> rawContent() const override; }; -template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips32ELType>::rawContent() const { - return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); } - -template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips64ELType>::rawContent() const { - return llvm::makeArrayRef(mipsGotTlsGdAtomContent); +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); +} +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF64BE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent); +} +template <> ArrayRef<uint8_t> GOTTLSGdAtom<ELF64LE>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent); } class GOTPLTAtom : public GOTAtom { @@ -173,28 +281,56 @@ public: addReferenceELF_Mips(R_MIPS_32, 0, plt0, 0); } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } ArrayRef<uint8_t> rawContent() const override { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } }; -class PLT0Atom : public PLTAtom { +template <class ELFT> class PLT0Atom : public PLTAtom { public: PLT0Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT0 entry. - addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); - addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); - addReferenceELF_Mips(LLD_R_MIPS_LO16, 8, got, 0); + addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 8, got, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + llvm_unreachable("PLT0 is not applicable for this target"); + } +}; + +template <> ArrayRef<uint8_t> PLT0Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsBePlt0AtomContent); +} +template <> ArrayRef<uint8_t> PLT0Atom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsLePlt0AtomContent); +} + +template <class ELFT> class PLT0N32Atom : public PLTAtom { +public: + PLT0N32Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT0 entry. + addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 8, got, 0); } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsPlt0AtomContent); + llvm_unreachable("PLT0 is not applicable for this target"); } }; -class PLT0MicroAtom : public PLTAtom { +template <> ArrayRef<uint8_t> PLT0N32Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsN32BePlt0AtomContent); +} +template <> ArrayRef<uint8_t> PLT0N32Atom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsN32LePlt0AtomContent); +} + +template <class ELFT> class PLT0MicroAtom : public PLTAtom { public: PLT0MicroAtom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT0 entry. @@ -204,54 +340,87 @@ public: CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(micromipsPlt0AtomContent); + llvm_unreachable("PLT0 is not applicable for this target"); } }; +template <> ArrayRef<uint8_t> PLT0MicroAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(microMipsBePlt0AtomContent); +} +template <> ArrayRef<uint8_t> PLT0MicroAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(microMipsLePlt0AtomContent); +} + class PLTAAtom : public PLTAtom { public: PLTAAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT entry. - addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); - addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); - addReferenceELF_Mips(LLD_R_MIPS_LO16, 12, got, 0); + addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(R_MIPS_LO16, 12, got, 0); } +}; + +template <class ELFT> class PLTARegAtom : public PLTAAtom { +public: + PLTARegAtom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsPltAAtomContent); + llvm_unreachable("PLT is not applicable for this target"); } }; -class PLTR6Atom : public PLTAAtom { +template <> ArrayRef<uint8_t> PLTARegAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsBePltAAtomContent); +} +template <> ArrayRef<uint8_t> PLTARegAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsLePltAAtomContent); +} + +template <class ELFT> class PLTR6Atom : public PLTAAtom { public: PLTR6Atom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsR6PltAAtomContent); + llvm_unreachable("PLT is not applicable for this target"); } }; -class PLTMicroAtom : public PLTAtom { +template <> ArrayRef<uint8_t> PLTR6Atom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsR6BePltAAtomContent); +} +template <> ArrayRef<uint8_t> PLTR6Atom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsR6LePltAAtomContent); +} + +template <class ELFT> class PLTMicroAtom : public PLTAtom { public: PLTMicroAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the microMIPS PLT entry. addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); } - Alignment alignment() const override { return Alignment(1); } + Alignment alignment() const override { return 2; } CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(micromipsPltAtomContent); + llvm_unreachable("PLT is not applicable for this target"); } }; +template <> ArrayRef<uint8_t> PLTMicroAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(microMipsBePltAAtomContent); +} +template <> ArrayRef<uint8_t> PLTMicroAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(microMipsLePltAAtomContent); +} + class LA25Atom : public PLTAtom { public: LA25Atom(const File &f) : PLTAtom(f, ".text") {} }; -class LA25RegAtom : public LA25Atom { +template <typename ELFT> class LA25RegAtom : public LA25Atom { public: LA25RegAtom(const Atom *a, const File &f) : LA25Atom(f) { // Setup reference to fixup the LA25 stub entry. @@ -261,11 +430,18 @@ public: } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(mipsLA25AtomContent); + llvm_unreachable("LA25 stubs are not applicable for this target"); } }; -class LA25MicroAtom : public LA25Atom { +template <> ArrayRef<uint8_t> LA25RegAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(mipsBeLA25AtomContent); +} +template <> ArrayRef<uint8_t> LA25RegAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(mipsLeLA25AtomContent); +} + +template <typename ELFT> class LA25MicroAtom : public LA25Atom { public: LA25MicroAtom(const Atom *a, const File &f) : LA25Atom(f) { // Setup reference to fixup the microMIPS LA25 stub entry. @@ -277,7 +453,39 @@ public: CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(micromipsLA25AtomContent); + llvm_unreachable("LA25 stubs are not applicable for this target"); + } +}; + +template <> ArrayRef<uint8_t> LA25MicroAtom<ELF32BE>::rawContent() const { + return llvm::makeArrayRef(microMipsBeLA25AtomContent); +} +template <> ArrayRef<uint8_t> LA25MicroAtom<ELF32LE>::rawContent() const { + return llvm::makeArrayRef(microMipsLeLA25AtomContent); +} + +class MipsGlobalOffsetTableAtom : public GlobalOffsetTableAtom { +public: + MipsGlobalOffsetTableAtom(const File &f) : GlobalOffsetTableAtom(f) {} + + StringRef customSectionName() const override { return ".got"; } +}; + +template <typename ELFT> class MipsRldAtom : public SimpleELFDefinedAtom { +public: + MipsRldAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + Scope scope() const override { return scopeGlobal; } + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return ".rld_map"; } + ContentType contentType() const override { return typeData; } + uint64_t size() const override { return rawContent().size(); } + ContentPermissions permissions() const override { return permRW_; } + Alignment alignment() const override { return rawContent().size(); } + StringRef name() const override { return "__RLD_MAP"; } + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsGot0AtomContent) + .slice(ELFT::Is64Bits ? 0 : 4); } }; @@ -295,7 +503,7 @@ template <typename ELFT> class RelocationPass : public Pass { public: RelocationPass(MipsLinkingContext &ctx); - void perform(std::unique_ptr<MutableFile> &mf) override; + std::error_code perform(SimpleFile &mf) override; private: /// \brief Reference to the linking context. @@ -319,7 +527,7 @@ private: llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap; /// \brief GOT entry for the R_xxxMIPS_TLS_LDM relocations. - GOTTLSGdAtom<ELFT> *_gotLDMEntry; + GOTTLSGdAtom<ELFT> *_gotLDMEntry = nullptr; /// \brief the list of local GOT atoms. std::vector<GOTAtom *> _localGotVector; @@ -335,14 +543,14 @@ private: /// \brief Map Atoms to their PLT entries. llvm::DenseMap<const Atom *, PLTAAtom *> _pltRegMap; - llvm::DenseMap<const Atom *, PLTMicroAtom *> _pltMicroMap; + llvm::DenseMap<const Atom *, PLTMicroAtom<ELFT> *> _pltMicroMap; /// \brief Map Atoms to their Object entries. llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; /// \brief Map Atoms to their LA25 entries. - llvm::DenseMap<const Atom *, LA25RegAtom *> _la25RegMap; - llvm::DenseMap<const Atom *, LA25MicroAtom *> _la25MicroMap; + llvm::DenseMap<const Atom *, LA25Atom *> _la25RegMap; + llvm::DenseMap<const Atom *, LA25Atom *> _la25MicroMap; /// \brief Atoms referenced by static relocations. llvm::DenseSet<const Atom *> _hasStaticRelocations; @@ -372,24 +580,27 @@ private: /// \brief Collect information about the reference to use it /// later in the handleReference() routine. - void collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, - Reference &ref); + std::error_code collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref); + + /// \brief Check that the relocation is valid for the current linking mode. + std::error_code validateRelocation(const DefinedAtom &atom, + const Reference &ref) const; void handlePlain(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); - void handle26(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + void handleBranch(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); void handleGOT(Reference &ref); const GOTAtom *getLocalGOTEntry(const Reference &ref); const GOTAtom *getLocalGOTPageEntry(const Reference &ref); const GOTAtom *getGlobalGOTEntry(const Atom *a); - const GOTAtom *getTLSGOTEntry(const Atom *a); - const GOTAtom *getTLSGdGOTEntry(const Atom *a); + const GOTAtom *getTLSGOTEntry(const Atom *a, Reference::Addend addend); + const GOTAtom *getTLSGdGOTEntry(const Atom *a, Reference::Addend addend); const GOTAtom *getTLSLdmGOTEntry(const Atom *a); const GOTPLTAtom *getGOTPLTEntry(const Atom *a); const PLTAtom *getPLTEntry(const Atom *a); const PLTAtom *getPLTRegEntry(const Atom *a); const PLTAtom *getPLTMicroEntry(const Atom *a); - const LA25Atom *getLA25Entry(const Atom *target, bool isMicroMips); const LA25Atom *getLA25RegEntry(const Atom *a); const LA25Atom *getLA25MicroEntry(const Atom *a); const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a); @@ -399,40 +610,47 @@ private: bool isLocal(const Atom *a) const; bool isLocalCall(const Atom *a) const; bool isDynamic(const Atom *atom) const; - bool requireLA25Stub(const Atom *a) const; + bool requireLA25Stub(const MipsELFDefinedAtom<ELFT> &atom, + const Reference &ref) const; bool requirePLTEntry(const Atom *a) const; bool requireCopy(const Atom *a) const; bool mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, Reference::KindValue refKind) const; bool hasPLTEntry(const Atom *atom) const; - bool isR6Target() const; + /// \brief Linked files contain microMIPS code. + bool isMicroMips(); + /// \brief Linked files contain MIPS R6 code. + bool isMipsR6(); }; template <typename ELFT> RelocationPass<ELFT>::RelocationPass(MipsLinkingContext &ctx) - : _ctx(ctx), _file(ctx), _gotLDMEntry(nullptr) { + : _ctx(ctx), _file(ctx) { _localGotVector.push_back(new (_file._alloc) GOT0Atom<ELFT>(_file)); _localGotVector.push_back(new (_file._alloc) GOTModulePointerAtom<ELFT>(_file)); } template <typename ELFT> -void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { - for (const auto &atom : mf->defined()) - for (const auto &ref : *atom) - collectReferenceInfo(*cast<MipsELFDefinedAtom<ELFT>>(atom), - const_cast<Reference &>(*ref)); +std::error_code RelocationPass<ELFT>::perform(SimpleFile &mf) { + for (const auto &atom : mf.defined()) + for (const auto &ref : *atom) { + const auto &da = *cast<MipsELFDefinedAtom<ELFT>>(atom); + if (auto ec = collectReferenceInfo(da, const_cast<Reference &>(*ref))) + return ec; + } // Process all references. - for (const auto &atom : mf->defined()) + for (const auto &atom : mf.defined()) for (const auto &ref : *atom) handleReference(*cast<MipsELFDefinedAtom<ELFT>>(atom), const_cast<Reference &>(*ref)); // Create R_MIPS_REL32 relocations. for (auto *ref : _rel32Candidates) { - if (!isDynamic(ref->target()) || hasPLTEntry(ref->target())) + bool forceRel = isLocal(ref->target()) && _ctx.getOutputELFType() == ET_DYN; + if (!forceRel && (!isDynamic(ref->target()) || hasPLTEntry(ref->target()))) continue; ref->setKindValue(R_MIPS_REL32); if (ELFT::Is64Bits) @@ -443,19 +661,32 @@ void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { uint64_t ordinal = 0; + if (_ctx.isDynamic() && _ctx.getOutputELFType() == ET_EXEC) { + auto rlda = new (_file._alloc) MipsRldAtom<ELFT>(_file); + rlda->setOrdinal(ordinal++); + mf.addAtom(*rlda); + } + + if (!_localGotVector.empty() || !_globalGotVector.empty() || + !_tlsGotVector.empty()) { + SimpleDefinedAtom *ga = new (_file._alloc) MipsGlobalOffsetTableAtom(_file); + ga->setOrdinal(ordinal++); + mf.addAtom(*ga); + } + for (auto &got : _localGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto &got : _globalGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto &got : _tlsGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } // Create and emit PLT0 entry. @@ -467,19 +698,19 @@ void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { if (plt0Atom) { plt0Atom->setOrdinal(ordinal++); - mf->addAtom(*plt0Atom); + mf.addAtom(*plt0Atom); } // Emit regular PLT entries firts. for (auto &plt : _pltRegVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } // microMIPS PLT entries come after regular ones. for (auto &plt : _pltMicroVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } // Assign PLT0 to GOTPLT entries. @@ -489,18 +720,96 @@ void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { for (auto &gotplt : _gotpltVector) { gotplt->setOrdinal(ordinal++); - mf->addAtom(*gotplt); + mf.addAtom(*gotplt); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } for (auto la25 : _la25Vector) { la25->setOrdinal(ordinal++); - mf->addAtom(*la25); + mf.addAtom(*la25); } + + return std::error_code(); +} + +static bool isMicroMipsReloc(Reference::KindValue kind) { + return R_MICROMIPS_26_S1 <= kind && kind <= R_MICROMIPS_PC19_S2; +} + +static bool isHiLo16Reloc(Reference::KindValue kind) { + return kind == R_MIPS_HI16 || kind == R_MIPS_LO16 || kind == R_MIPS_PCHI16 || + kind == R_MIPS_PCLO16 || kind == R_MICROMIPS_HI16 || + kind == R_MICROMIPS_LO16 || kind == R_MICROMIPS_HI0_LO16; +} + +static bool isBranchReloc(Reference::KindValue kind) { + return kind == R_MIPS_26 || kind == R_MICROMIPS_26_S1 || + kind == R_MIPS_PC16 || kind == R_MIPS_PC21_S2 || + kind == R_MIPS_PC26_S2 || kind == R_MICROMIPS_PC7_S1 || + kind == R_MICROMIPS_PC10_S1 || kind == R_MICROMIPS_PC16_S1 || + kind == R_MICROMIPS_PC23_S2; +} + +static bool isGotReloc(Reference::KindValue kind) { + return kind == R_MIPS_GOT16 || kind == R_MICROMIPS_GOT16; +} + +static bool isAllGotReloc(Reference::KindValue kind) { + return isGotReloc(kind) || kind == R_MIPS_GOT_HI16 || + kind == R_MIPS_GOT_LO16 || kind == R_MICROMIPS_GOT_HI16 || + kind == R_MICROMIPS_GOT_LO16; +} + +static bool isCallReloc(Reference::KindValue kind) { + return kind == R_MIPS_CALL16 || kind == R_MICROMIPS_CALL16; +} + +static bool isAllCallReloc(Reference::KindValue kind) { + return isCallReloc(kind) || kind == R_MIPS_CALL_HI16 || + kind == R_MIPS_CALL_LO16 || kind == R_MICROMIPS_CALL_HI16 || + kind == R_MICROMIPS_CALL_LO16; +} + +static bool isGotDispReloc(Reference::KindValue kind) { + return kind == R_MIPS_GOT_DISP || kind == R_MICROMIPS_GOT_DISP; +} + +static bool isGotPageReloc(Reference::KindValue kind) { + return kind == R_MIPS_GOT_PAGE || kind == R_MICROMIPS_GOT_PAGE; +} + +static bool isTlsDtpReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_DTPREL_HI16 || kind == R_MIPS_TLS_DTPREL_LO16 || + kind == R_MICROMIPS_TLS_DTPREL_HI16 || + kind == R_MICROMIPS_TLS_DTPREL_LO16; +} + +static bool isTlsTpReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_TPREL_HI16 || kind == R_MIPS_TLS_TPREL_LO16 || + kind == R_MICROMIPS_TLS_TPREL_HI16 || + kind == R_MICROMIPS_TLS_TPREL_LO16; +} + +static bool isTlsGdReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_GD || kind == R_MICROMIPS_TLS_GD; +} + +static bool isTlsLdmReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_LDM || kind == R_MICROMIPS_TLS_LDM; +} + +static bool isTlsGotTpReloc(Reference::KindValue kind) { + return kind == R_MIPS_TLS_GOTTPREL || kind == R_MICROMIPS_TLS_GOTTPREL; +} + +static bool isGpRelReloc(Reference::KindValue kind) { + return kind == R_MIPS_GPREL32 || kind == R_MIPS_GPREL16 || + kind == R_MICROMIPS_GPREL16 || kind == R_MICROMIPS_GPREL7_S2 || + kind == R_MIPS_LITERAL || kind == R_MICROMIPS_LITERAL; } template <typename ELFT> @@ -508,67 +817,33 @@ void RelocationPass<ELFT>::handleReference(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref) { if (!ref.target()) return; - if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + if (ref.kindNamespace() != Reference::KindNamespace::ELF) return; + assert(ref.kindArch() == Reference::KindArch::Mips); - switch (ref.kindValue()) { - case R_MIPS_32: - case R_MIPS_PC32: - case R_MIPS_HI16: - case R_MIPS_LO16: - case R_MIPS_PCHI16: - case R_MIPS_PCLO16: - case R_MICROMIPS_HI16: - case R_MICROMIPS_LO16: - // FIXME (simon): Handle dynamic/static linking differently. + Reference::KindValue kind = ref.kindValue(); + if (isHiLo16Reloc(kind) || kind == R_MIPS_32 || kind == R_MIPS_PC32) handlePlain(atom, ref); - break; - case R_MIPS_26: - case R_MICROMIPS_26_S1: - handle26(atom, ref); - break; - case R_MIPS_GOT16: - case R_MIPS_CALL16: - case R_MICROMIPS_GOT16: - case R_MICROMIPS_CALL16: - case R_MIPS_GOT_DISP: - case R_MIPS_GOT_PAGE: + else if (isBranchReloc(kind)) + handleBranch(atom, ref); + else if (isAllGotReloc(kind) || isAllCallReloc(kind) || + isGotDispReloc(kind) || isGotPageReloc(kind) || kind == R_MIPS_EH) handleGOT(ref); - break; - case R_MIPS_GOT_OFST: - // Nothing to do. We create GOT page entry in the R_MIPS_GOT_PAGE handler. - break; - case R_MIPS_GPREL16: - if (isLocal(ref.target())) - ref.setAddend(ref.addend() + atom.file().getGP0()); - break; - case R_MIPS_GPREL32: - ref.setAddend(ref.addend() + atom.file().getGP0()); - break; - case R_MIPS_TLS_DTPREL_HI16: - case R_MIPS_TLS_DTPREL_LO16: - case R_MICROMIPS_TLS_DTPREL_HI16: - case R_MICROMIPS_TLS_DTPREL_LO16: + else if (isTlsDtpReloc(kind)) ref.setAddend(ref.addend() - atom.file().getDTPOffset()); - break; - case R_MIPS_TLS_TPREL_HI16: - case R_MIPS_TLS_TPREL_LO16: - case R_MICROMIPS_TLS_TPREL_HI16: - case R_MICROMIPS_TLS_TPREL_LO16: + else if (isTlsTpReloc(kind)) ref.setAddend(ref.addend() - atom.file().getTPOffset()); - break; - case R_MIPS_TLS_GD: - case R_MICROMIPS_TLS_GD: - ref.setTarget(getTLSGdGOTEntry(ref.target())); - break; - case R_MIPS_TLS_LDM: - case R_MICROMIPS_TLS_LDM: + else if (isTlsGdReloc(kind)) + ref.setTarget(getTLSGdGOTEntry(ref.target(), ref.addend())); + else if (isTlsLdmReloc(kind)) ref.setTarget(getTLSLdmGOTEntry(ref.target())); - break; - case R_MIPS_TLS_GOTTPREL: - case R_MICROMIPS_TLS_GOTTPREL: - ref.setTarget(getTLSGOTEntry(ref.target())); - break; + else if (isTlsGotTpReloc(kind)) + ref.setTarget(getTLSGOTEntry(ref.target(), ref.addend())); + else if (kind == R_MIPS_GPREL32 || (isLocal(ref.target()) && isGpRelReloc(kind))) + ref.setAddend(ref.addend() + atom.file().getGP0()); + else if (kind == R_MIPS_JALR) { + if (_ctx.getOutputELFType() != ET_EXEC || !isLocalCall(ref.target())) + ref.setKindValue(R_MIPS_NONE); } } @@ -583,6 +858,10 @@ static bool isConstrainSym(const MipsELFDefinedAtom<ELFT> &atom, case R_MICROMIPS_JALR: case R_MIPS_GPREL16: case R_MIPS_GPREL32: + case R_MICROMIPS_GPREL16: + case R_MICROMIPS_GPREL7_S2: + case R_MIPS_LITERAL: + case R_MICROMIPS_LITERAL: return false; default: return true; @@ -590,25 +869,96 @@ static bool isConstrainSym(const MipsELFDefinedAtom<ELFT> &atom, } template <typename ELFT> -void RelocationPass<ELFT>::collectReferenceInfo( - const MipsELFDefinedAtom<ELFT> &atom, Reference &ref) { +std::error_code +RelocationPass<ELFT>::collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { if (!ref.target()) - return; - if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) - return; + return std::error_code(); + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); auto refKind = ref.kindValue(); + if (refKind == R_MIPS_EH && this->_ctx.mipsPcRelEhRel()) + ref.setKindValue(R_MIPS_PC32); + + if (auto ec = validateRelocation(atom, ref)) + return ec; + if (!isConstrainSym(atom, refKind)) - return; + return std::error_code(); - if (mightBeDynamic(atom, refKind)) - _rel32Candidates.push_back(&ref); - else + if (!mightBeDynamic(atom, refKind)) _hasStaticRelocations.insert(ref.target()); + else if (refKind == R_MIPS_32 || refKind == R_MIPS_64) + _rel32Candidates.push_back(&ref); - if (refKind != R_MIPS_CALL16 && refKind != R_MICROMIPS_CALL16 && - refKind != R_MIPS_26 && refKind != R_MICROMIPS_26_S1) + if (!isBranchReloc(refKind) && !isAllCallReloc(refKind) && + refKind != R_MIPS_EH) _requiresPtrEquality.insert(ref.target()); + + return std::error_code(); +} + +static std::error_code +make_reject_for_shared_lib_reloc_error(const ELFLinkingContext &ctx, + const DefinedAtom &atom, + const Reference &ref) { + StringRef kindValStr = "unknown"; + ctx.registry().referenceKindToString(ref.kindNamespace(), ref.kindArch(), + ref.kindValue(), kindValStr); + + return make_dynamic_error_code(Twine(kindValStr) + " (" + + Twine(ref.kindValue()) + + ") relocation cannot be used " + "when making a shared object, recompile " + + atom.file().path() + " with -fPIC"); +} + +static std::error_code +make_local_call16_reloc_error(const ELFLinkingContext &ctx, + const DefinedAtom &atom, const Reference &ref) { + return make_dynamic_error_code("R_MIPS_CALL16 (11) relocation cannot be used " + "against local symbol " + + ref.target()->name() + " in file " + + atom.file().path()); +} + +template <typename ELFT> +std::error_code +RelocationPass<ELFT>::validateRelocation(const DefinedAtom &atom, + const Reference &ref) const { + if (!ref.target()) + return std::error_code(); + + if (isCallReloc(ref.kindValue()) && isLocal(ref.target())) + return make_local_call16_reloc_error(this->_ctx, atom, ref); + + if (this->_ctx.getOutputELFType() != ET_DYN) + return std::error_code(); + + switch (ref.kindValue()) { + case R_MIPS16_HI16: + case R_MIPS_HI16: + case R_MIPS_HIGHER: + case R_MIPS_HIGHEST: + case R_MICROMIPS_HI16: + case R_MICROMIPS_HIGHER: + case R_MICROMIPS_HIGHEST: + // For shared object we accepts "high" relocations + // against the "_gp_disp" symbol only. + if (ref.target()->name() != "_gp_disp") + return make_reject_for_shared_lib_reloc_error(this->_ctx, atom, ref); + break; + case R_MIPS16_26: + case R_MIPS_26: + case R_MICROMIPS_26_S1: + // These relocations are position dependent + // and not acceptable in a shared object. + return make_reject_for_shared_lib_reloc_error(this->_ctx, atom, ref); + default: + break; + } + return std::error_code(); } template <typename ELFT> @@ -635,8 +985,7 @@ static bool isMipsReadonly(const MipsELFDefinedAtom<ELFT> &atom) { template <typename ELFT> bool RelocationPass<ELFT>::mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, Reference::KindValue refKind) const { - if (refKind == R_MIPS_CALL16 || refKind == R_MIPS_GOT16 || - refKind == R_MICROMIPS_CALL16 || refKind == R_MICROMIPS_GOT16) + if (isAllGotReloc(refKind) || isAllCallReloc(refKind)) return true; if (refKind != R_MIPS_32 && refKind != R_MIPS_64) @@ -648,7 +997,7 @@ bool RelocationPass<ELFT>::mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, return true; if (!isMipsReadonly(atom)) return true; - if (atom.file().isPIC()) + if (atom.isPIC()) return true; return false; @@ -659,14 +1008,18 @@ bool RelocationPass<ELFT>::hasPLTEntry(const Atom *atom) const { return _pltRegMap.count(atom) || _pltMicroMap.count(atom); } -template <typename ELFT> bool RelocationPass<ELFT>::isR6Target() const { - switch (_ctx.getMergedELFFlags() & EF_MIPS_ARCH) { - case EF_MIPS_ARCH_32R6: - case EF_MIPS_ARCH_64R6: - return true; - default: - return false; - } +template <typename ELFT> bool RelocationPass<ELFT>::isMicroMips() { + TargetHandler &handler = this->_ctx.getTargetHandler(); + return static_cast<MipsTargetHandler<ELFT> &>(handler) + .getAbiInfoHandler() + .isMicroMips(); +} + +template <typename ELFT> bool RelocationPass<ELFT>::isMipsR6() { + TargetHandler &handler = this->_ctx.getTargetHandler(); + return static_cast<MipsTargetHandler<ELFT> &>(handler) + .getAbiInfoHandler() + .isMipsR6(); } template <typename ELFT> @@ -697,21 +1050,13 @@ bool RelocationPass<ELFT>::isDynamic(const Atom *atom) const { const auto *da = dyn_cast<const DefinedAtom>(atom); if (da && da->dynamicExport() == DefinedAtom::dynamicExportAlways) return true; - - const auto *sa = dyn_cast<SharedLibraryAtom>(atom); - if (sa) + if (isa<SharedLibraryAtom>(atom)) return true; - - if (_ctx.getOutputELFType() == ET_DYN) { - if (da && da->scope() != DefinedAtom::scopeTranslationUnit) - return true; - - const auto *ua = dyn_cast<UndefinedAtom>(atom); - if (ua) - return true; - } - - return false; + if (_ctx.getOutputELFType() != ET_DYN) + return false; + if (da && da->scope() != DefinedAtom::scopeTranslationUnit) + return true; + return isa<UndefinedAtom>(atom); } template <typename ELFT> @@ -721,17 +1066,9 @@ static bool isMicroMips(const MipsELFDefinedAtom<ELFT> &atom) { } template <typename ELFT> -const LA25Atom *RelocationPass<ELFT>::getLA25Entry(const Atom *target, - bool isMicroMips) { - return isMicroMips ? getLA25MicroEntry(target) : getLA25RegEntry(target); -} - -template <typename ELFT> const PLTAtom *RelocationPass<ELFT>::getPLTEntry(const Atom *a) { - bool hasMicroCode = _ctx.getMergedELFFlags() & EF_MIPS_MICROMIPS; - // If file contains microMIPS code try to reuse compressed PLT entry... - if (hasMicroCode) { + if (isMicroMips()) { auto microPLT = _pltMicroMap.find(a); if (microPLT != _pltMicroMap.end()) return microPLT->second; @@ -743,7 +1080,7 @@ const PLTAtom *RelocationPass<ELFT>::getPLTEntry(const Atom *a) { return regPLT->second; // ... and finally prefer to create new compressed PLT entry. - return hasMicroCode ? getPLTMicroEntry(a) : getPLTRegEntry(a); + return isMicroMips() ? getPLTMicroEntry(a) : getPLTRegEntry(a); } template <typename ELFT> @@ -759,37 +1096,34 @@ void RelocationPass<ELFT>::handlePlain(const MipsELFDefinedAtom<ELFT> &atom, } template <typename ELFT> -void RelocationPass<ELFT>::handle26(const MipsELFDefinedAtom<ELFT> &atom, - Reference &ref) { - bool isMicro = ref.kindValue() == R_MICROMIPS_26_S1; - assert((isMicro || ref.kindValue() == R_MIPS_26) && "Unexpected relocation"); - - const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target()); - if (sla && sla->type() == SharedLibraryAtom::Type::Code) - ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); - - if (requireLA25Stub(ref.target())) - ref.setTarget(getLA25Entry(ref.target(), isMicro)); +void RelocationPass<ELFT>::handleBranch(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + bool isMicro = isMicroMipsReloc(ref.kindValue()); + if (const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Code) + ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); + } else if (requireLA25Stub(atom, ref)) { + if (isMicro) + ref.setTarget(getLA25MicroEntry(ref.target())); + else + ref.setTarget(getLA25RegEntry(ref.target())); + } if (!isLocal(ref.target())) { - if (isMicro) + if (ref.kindValue() == R_MICROMIPS_26_S1) ref.setKindValue(LLD_R_MICROMIPS_GLOBAL_26_S1); - else + else if (ref.kindValue() == R_MIPS_26) ref.setKindValue(LLD_R_MIPS_GLOBAL_26); } } template <typename ELFT> void RelocationPass<ELFT>::handleGOT(Reference &ref) { - if (!isLocalCall(ref.target())) { + if (!isLocalCall(ref.target())) ref.setTarget(getGlobalGOTEntry(ref.target())); - return; - } - - if (ref.kindValue() == R_MIPS_GOT_PAGE) + else if (isGotPageReloc(ref.kindValue())) ref.setTarget(getLocalGOTPageEntry(ref)); - else if (ref.kindValue() == R_MIPS_GOT_DISP) - ref.setTarget(getLocalGOTEntry(ref)); - else if (isLocal(ref.target())) + else if (isLocal(ref.target()) && + (isCallReloc(ref.kindValue()) || isGotReloc(ref.kindValue()))) ref.setTarget(getLocalGOTPageEntry(ref)); else ref.setTarget(getLocalGOTEntry(ref)); @@ -817,11 +1151,12 @@ bool RelocationPass<ELFT>::isLocalCall(const Atom *a) const { } template <typename ELFT> -bool RelocationPass<ELFT>::requireLA25Stub(const Atom *a) const { - if (isLocal(a)) +bool RelocationPass<ELFT>::requireLA25Stub(const MipsELFDefinedAtom<ELFT> &atom, + const Reference &ref) const { + if (atom.file().isPIC()) return false; - if (auto *da = dyn_cast<DefinedAtom>(a)) - return static_cast<const MipsELFDefinedAtom<ELFT> *>(da)->file().isPIC(); + if (auto *da = dyn_cast<DefinedAtom>(ref.target())) + return static_cast<const MipsELFDefinedAtom<ELFT> *>(da)->isPIC(); return false; } @@ -886,7 +1221,8 @@ const GOTAtom *RelocationPass<ELFT>::getGlobalGOTEntry(const Atom *a) { } template <typename ELFT> -const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) { +const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a, + Reference::Addend addend) { auto got = _gotTLSMap.find(a); if (got != _gotTLSMap.end()) return got->second; @@ -897,13 +1233,15 @@ const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) { _tlsGotVector.push_back(ga); Reference::KindValue relKind = ELFT::Is64Bits ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32; - ga->addReferenceELF_Mips(relKind, 0, a, 0); + ga->addReferenceELF_Mips(relKind, 0, a, addend); return ga; } template <typename ELFT> -const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) { +const GOTAtom * +RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a, + Reference::Addend addend) { auto got = _gotTLSGdMap.find(a); if (got != _gotTLSGdMap.end()) return got->second; @@ -913,11 +1251,11 @@ const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) { _tlsGotVector.push_back(ga); if (ELFT::Is64Bits) { - ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, a, 0); - ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, a, addend); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, addend); } else { - ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, 0); - ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, addend); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, addend); } return ga; @@ -946,9 +1284,10 @@ PLTAtom *RelocationPass<ELFT>::createPLTHeader(bool isMicroMips) { _gotpltVector.insert(_gotpltVector.begin(), ga0); if (isMicroMips) - return new (_file._alloc) PLT0MicroAtom(ga0, _file); - else - return new (_file._alloc) PLT0Atom(ga0, _file); + return new (_file._alloc) PLT0MicroAtom<ELFT>(ga0, _file); + if (_ctx.getAbi() == MipsAbi::N32) + return new (_file._alloc) PLT0N32Atom<ELFT>(ga0, _file); + return new (_file._alloc) PLT0Atom<ELFT>(ga0, _file); } template <typename ELFT> @@ -969,9 +1308,11 @@ const PLTAtom *RelocationPass<ELFT>::getPLTRegEntry(const Atom *a) { if (plt != _pltRegMap.end()) return plt->second; - PLTAAtom *pa = isR6Target() - ? new (_file._alloc) PLTR6Atom(getGOTPLTEntry(a), _file) - : new (_file._alloc) PLTAAtom(getGOTPLTEntry(a), _file); + PLTAAtom *pa = nullptr; + if (isMipsR6()) + pa = new (_file._alloc) PLTR6Atom<ELFT>(getGOTPLTEntry(a), _file); + else + pa = new (_file._alloc) PLTARegAtom<ELFT>(getGOTPLTEntry(a), _file); _pltRegMap[a] = pa; _pltRegVector.push_back(pa); @@ -988,7 +1329,7 @@ const PLTAtom *RelocationPass<ELFT>::getPLTMicroEntry(const Atom *a) { if (plt != _pltMicroMap.end()) return plt->second; - auto pa = new (_file._alloc) PLTMicroAtom(getGOTPLTEntry(a), _file); + auto pa = new (_file._alloc) PLTMicroAtom<ELFT>(getGOTPLTEntry(a), _file); _pltMicroMap[a] = pa; _pltMicroVector.push_back(pa); @@ -1005,7 +1346,7 @@ const LA25Atom *RelocationPass<ELFT>::getLA25RegEntry(const Atom *a) { if (la25 != _la25RegMap.end()) return la25->second; - auto sa = new (_file._alloc) LA25RegAtom(a, _file); + auto sa = new (_file._alloc) LA25RegAtom<ELFT>(a, _file); _la25RegMap[a] = sa; _la25Vector.push_back(sa); @@ -1018,7 +1359,7 @@ const LA25Atom *RelocationPass<ELFT>::getLA25MicroEntry(const Atom *a) { if (la25 != _la25MicroMap.end()) return la25->second; - auto sa = new (_file._alloc) LA25MicroAtom(a, _file); + auto sa = new (_file._alloc) LA25MicroAtom<ELFT>(a, _file); _la25MicroMap[a] = sa; _la25Vector.push_back(sa); @@ -1047,10 +1388,14 @@ RelocationPass<ELFT>::getObjectEntry(const SharedLibraryAtom *a) { static std::unique_ptr<Pass> createPass(MipsLinkingContext &ctx) { switch (ctx.getTriple().getArch()) { + case llvm::Triple::mips: + return llvm::make_unique<RelocationPass<ELF32BE>>(ctx); case llvm::Triple::mipsel: - return llvm::make_unique<RelocationPass<Mips32ELType>>(ctx); + return llvm::make_unique<RelocationPass<ELF32LE>>(ctx); + case llvm::Triple::mips64: + return llvm::make_unique<RelocationPass<ELF64BE>>(ctx); case llvm::Triple::mips64el: - return llvm::make_unique<RelocationPass<Mips64ELType>>(ctx); + return llvm::make_unique<RelocationPass<ELF64LE>>(ctx); default: llvm_unreachable("Unhandled arch"); } diff --git a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp new file mode 100644 index 000000000000..98cc059787ef --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp @@ -0,0 +1,264 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsSectionChunks.cpp --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsLinkingContext.h" +#include "MipsSectionChunks.h" +#include "MipsTargetLayout.h" + +namespace lld { +namespace elf { + +template <class ELFT> +MipsReginfoSection<ELFT>::MipsReginfoSection( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info) + : Section<ELFT>(ctx, ".reginfo", "MipsReginfo"), _reginfo(reginfo), + _targetLayout(targetLayout) { + this->setOrder(MipsTargetLayout<ELFT>::ORDER_MIPS_REGINFO); + this->_entSize = sizeof(Elf_Mips_RegInfo); + this->_fsize = sizeof(Elf_Mips_RegInfo); + this->_msize = sizeof(Elf_Mips_RegInfo); + this->_alignment = 4; + this->_type = SHT_MIPS_REGINFO; + this->_flags = SHF_ALLOC; +} + +template <class ELFT> +void MipsReginfoSection<ELFT>::write(ELFWriter *writer, + TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *dest = buffer.getBufferStart() + this->fileOffset(); + std::memcpy(dest, &_reginfo, this->_fsize); +} + +template <class ELFT> void MipsReginfoSection<ELFT>::finalize() { + _reginfo.ri_gp_value = _targetLayout.getGPAddr(); + + if (this->_outputSection) + this->_outputSection->setType(this->_type); +} + +template class MipsReginfoSection<ELF32BE>; +template class MipsReginfoSection<ELF32LE>; +template class MipsReginfoSection<ELF64BE>; +template class MipsReginfoSection<ELF64LE>; + +template <class ELFT> +MipsOptionsSection<ELFT>::MipsOptionsSection( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info) + : Section<ELFT>(ctx, ".MIPS.options", "MipsOptions"), _reginfo(reginfo), + _targetLayout(targetLayout) { + this->setOrder(MipsTargetLayout<ELFT>::ORDER_MIPS_OPTIONS); + this->_entSize = 1; + this->_alignment = 8; + this->_fsize = llvm::RoundUpToAlignment( + sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo), this->_alignment); + this->_msize = this->_fsize; + this->_type = SHT_MIPS_OPTIONS; + this->_flags = SHF_ALLOC | SHF_MIPS_NOSTRIP; + + _header.kind = ODK_REGINFO; + _header.size = this->_fsize; + _header.section = 0; + _header.info = 0; +} + +template <class ELFT> +void MipsOptionsSection<ELFT>::write(ELFWriter *writer, + TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *dest = buffer.getBufferStart() + this->fileOffset(); + std::memset(dest, 0, this->_fsize); + std::memcpy(dest, &_header, sizeof(_header)); + std::memcpy(dest + sizeof(_header), &_reginfo, sizeof(_reginfo)); +} + +template <class ELFT> void MipsOptionsSection<ELFT>::finalize() { + _reginfo.ri_gp_value = _targetLayout.getGPAddr(); + + if (this->_outputSection) + this->_outputSection->setType(this->_type); +} + +template class MipsOptionsSection<ELF32BE>; +template class MipsOptionsSection<ELF32LE>; +template class MipsOptionsSection<ELF64BE>; +template class MipsOptionsSection<ELF64LE>; + +template <class ELFT> +MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_ABIFlags &abiFlags) + : Section<ELFT>(ctx, ".MIPS.abiflags", "MipsAbiFlags"), _abiFlags(abiFlags), + _targetLayout(targetLayout) { + this->setOrder(MipsTargetLayout<ELFT>::ORDER_MIPS_ABI_FLAGS); + this->_alignment = 8; + this->_fsize = llvm::RoundUpToAlignment(sizeof(_abiFlags), this->_alignment); + this->_msize = this->_fsize; + this->_entSize = this->_fsize; + this->_type = SHT_MIPS_ABIFLAGS; + this->_flags = SHF_ALLOC; +} + +template <class ELFT> +void MipsAbiFlagsSection<ELFT>::write(ELFWriter *writer, + TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *dest = buffer.getBufferStart() + this->fileOffset(); + std::memcpy(dest, &_abiFlags, this->_fsize); +} + +template <class ELFT> void MipsAbiFlagsSection<ELFT>::finalize() { + if (this->_outputSection) + this->_outputSection->setType(this->_type); +} + +template class MipsAbiFlagsSection<ELF32BE>; +template class MipsAbiFlagsSection<ELF32LE>; +template class MipsAbiFlagsSection<ELF64BE>; +template class MipsAbiFlagsSection<ELF64LE>; + +template <class ELFT> +MipsGOTSection<ELFT>::MipsGOTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFT>(ctx, ".got", DefinedAtom::typeGOT, DefinedAtom::permRW_, + MipsTargetLayout<ELFT>::ORDER_GOT), + _hasNonLocal(false), _localCount(0) { + this->_flags |= SHF_MIPS_GPREL; + this->_alignment = 4; +} + +template <class ELFT> +bool MipsGOTSection<ELFT>::compare(const Atom *a, const Atom *b) const { + auto ia = _posMap.find(a); + auto ib = _posMap.find(b); + + if (ia != _posMap.end() && ib != _posMap.end()) + return ia->second < ib->second; + + return ia == _posMap.end() && ib != _posMap.end(); +} + +template <class ELFT> +const AtomLayout *MipsGOTSection<ELFT>::appendAtom(const Atom *atom) { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + if (atom->name() == "_GLOBAL_OFFSET_TABLE_") + return AtomSection<ELFT>::appendAtom(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::Mips); + switch (r->kindValue()) { + case LLD_R_MIPS_GLOBAL_GOT: + _hasNonLocal = true; + _posMap[r->target()] = _posMap.size(); + return AtomSection<ELFT>::appendAtom(atom); + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL64: + case R_MIPS_TLS_DTPREL64: + _hasNonLocal = true; + _tlsMap[r->target()] = _tlsMap.size(); + return AtomSection<ELFT>::appendAtom(atom); + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + _hasNonLocal = true; + break; + } + } + + if (!_hasNonLocal) + ++_localCount; + + return AtomSection<ELFT>::appendAtom(atom); +} + +template class MipsGOTSection<ELF32BE>; +template class MipsGOTSection<ELF32LE>; +template class MipsGOTSection<ELF64BE>; +template class MipsGOTSection<ELF64LE>; + +template <class ELFT> +MipsPLTSection<ELFT>::MipsPLTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFT>(ctx, ".plt", DefinedAtom::typeGOT, DefinedAtom::permR_X, + MipsTargetLayout<ELFT>::ORDER_PLT) {} + +template <class ELFT> +const AtomLayout *MipsPLTSection<ELFT>::findPLTLayout(const Atom *plt) const { + auto it = _pltLayoutMap.find(plt); + return it != _pltLayoutMap.end() ? it->second : nullptr; +} + +template <class ELFT> +const AtomLayout *MipsPLTSection<ELFT>::appendAtom(const Atom *atom) { + const auto *layout = AtomSection<ELFT>::appendAtom(atom); + + const DefinedAtom *da = cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::Mips); + if (r->kindValue() == LLD_R_MIPS_STO_PLT) { + _pltLayoutMap[r->target()] = layout; + break; + } + } + + return layout; +} + +template class MipsPLTSection<ELF32BE>; +template class MipsPLTSection<ELF32LE>; +template class MipsPLTSection<ELF64BE>; +template class MipsPLTSection<ELF64LE>; + +template <class ELFT> static bool isMips64EL() { + return ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; +} + +template <class ELFT> +MipsRelocationTable<ELFT>::MipsRelocationTable(const ELFLinkingContext &ctx, + StringRef str, int32_t order) + : RelocationTable<ELFT>(ctx, str, order) {} + +template <class ELFT> +void MipsRelocationTable<ELFT>::writeRela(ELFWriter *writer, Elf_Rela &r, + const DefinedAtom &atom, + const Reference &ref) { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, + isMips64EL<ELFT>()); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_ctx.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; +} + +template <class ELFT> +void MipsRelocationTable<ELFT>::writeRel(ELFWriter *writer, Elf_Rel &r, + const DefinedAtom &atom, + const Reference &ref) { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, + isMips64EL<ELFT>()); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); +} + +template class MipsRelocationTable<ELF32BE>; +template class MipsRelocationTable<ELF32LE>; +template class MipsRelocationTable<ELF64BE>; +template class MipsRelocationTable<ELF64LE>; + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h index de9390f2b307..e545f65dc419 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h +++ b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h @@ -9,23 +9,81 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_SECTION_CHUNKS_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_SECTION_CHUNKS_H +#include "SectionChunks.h" + namespace lld { namespace elf { template <typename ELFT> class MipsTargetLayout; class MipsLinkingContext; +/// \brief Handle Mips .reginfo section +template <class ELFT> class MipsReginfoSection : public Section<ELFT> { +public: + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + + MipsReginfoSection(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info); + + StringRef segmentKindToStr() const override { return "REGINFO"; } + bool hasOutputSegment() const override { return true; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + void finalize() override; + +private: + Elf_Mips_RegInfo _reginfo; + MipsTargetLayout<ELFT> &_targetLayout; +}; + +/// \brief Handle .MIPS.options section +template <class ELFT> class MipsOptionsSection : public Section<ELFT> { +public: + typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; + typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo; + + MipsOptionsSection(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_RegInfo ®info); + + bool hasOutputSegment() const override { return true; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + void finalize() override; + +private: + Elf_Mips_Options _header; + Elf_Mips_RegInfo _reginfo; + MipsTargetLayout<ELFT> &_targetLayout; +}; + +/// \brief Handle .MIPS.abiflags section +template <class ELFT> class MipsAbiFlagsSection : public Section<ELFT> { +public: + typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags; + + MipsAbiFlagsSection(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &targetLayout, + const Elf_Mips_ABIFlags &abiFlags); + + bool hasOutputSegment() const override { return true; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + void finalize() override; + +private: + Elf_Mips_ABIFlags _abiFlags; + MipsTargetLayout<ELFT> &_targetLayout; +}; + /// \brief Handle Mips GOT section -template <class ELFType> class MipsGOTSection : public AtomSection<ELFType> { +template <class ELFT> class MipsGOTSection : public AtomSection<ELFT> { public: - MipsGOTSection(const MipsLinkingContext &ctx) - : AtomSection<ELFType>(ctx, ".got", DefinedAtom::typeGOT, - DefinedAtom::permRW_, - MipsTargetLayout<ELFType>::ORDER_GOT), - _hasNonLocal(false), _localCount(0) { - this->_flags |= SHF_MIPS_GPREL; - this->_alignment = 4; - } + MipsGOTSection(const MipsLinkingContext &ctx); /// \brief Number of local GOT entries. std::size_t getLocalCount() const { return _localCount; } @@ -39,47 +97,9 @@ public: } /// \brief Compare two atoms accordingly theirs positions in the GOT. - bool compare(const Atom *a, const Atom *b) const { - auto ia = _posMap.find(a); - auto ib = _posMap.find(b); + bool compare(const Atom *a, const Atom *b) const; - if (ia != _posMap.end() && ib != _posMap.end()) - return ia->second < ib->second; - - return ia == _posMap.end() && ib != _posMap.end(); - } - - const lld::AtomLayout *appendAtom(const Atom *atom) override { - const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); - - for (const auto &r : *da) { - if (r->kindNamespace() != lld::Reference::KindNamespace::ELF) - continue; - assert(r->kindArch() == Reference::KindArch::Mips); - switch (r->kindValue()) { - case LLD_R_MIPS_GLOBAL_GOT: - _hasNonLocal = true; - _posMap[r->target()] = _posMap.size(); - return AtomSection<ELFType>::appendAtom(atom); - case R_MIPS_TLS_TPREL32: - case R_MIPS_TLS_DTPREL32: - case R_MIPS_TLS_TPREL64: - case R_MIPS_TLS_DTPREL64: - _hasNonLocal = true; - _tlsMap[r->target()] = _tlsMap.size(); - return AtomSection<ELFType>::appendAtom(atom); - case R_MIPS_TLS_DTPMOD32: - case R_MIPS_TLS_DTPMOD64: - _hasNonLocal = true; - break; - } - } - - if (!_hasNonLocal) - ++_localCount; - - return AtomSection<ELFType>::appendAtom(atom); - } + const AtomLayout *appendAtom(const Atom *atom) override; private: /// \brief True if the GOT contains non-local entries. @@ -96,35 +116,13 @@ private: }; /// \brief Handle Mips PLT section -template <class ELFType> class MipsPLTSection : public AtomSection<ELFType> { +template <class ELFT> class MipsPLTSection : public AtomSection<ELFT> { public: - MipsPLTSection(const MipsLinkingContext &ctx) - : AtomSection<ELFType>(ctx, ".plt", DefinedAtom::typeGOT, - DefinedAtom::permR_X, - MipsTargetLayout<ELFType>::ORDER_PLT) {} - - const AtomLayout *findPLTLayout(const Atom *plt) const { - auto it = _pltLayoutMap.find(plt); - return it != _pltLayoutMap.end() ? it->second : nullptr; - } - - const lld::AtomLayout *appendAtom(const Atom *atom) override { - const auto *layout = AtomSection<ELFType>::appendAtom(atom); + MipsPLTSection(const MipsLinkingContext &ctx); - const DefinedAtom *da = cast<DefinedAtom>(atom); + const AtomLayout *findPLTLayout(const Atom *plt) const; - for (const auto &r : *da) { - if (r->kindNamespace() != lld::Reference::KindNamespace::ELF) - continue; - assert(r->kindArch() == Reference::KindArch::Mips); - if (r->kindValue() == LLD_R_MIPS_STO_PLT) { - _pltLayoutMap[r->target()] = layout; - break; - } - } - - return layout; - } + const AtomLayout *appendAtom(const Atom *atom) override; private: /// \brief Map PLT Atoms to their layouts. @@ -135,33 +133,15 @@ template <class ELFT> class MipsRelocationTable : public RelocationTable<ELFT> { typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; - static const bool _isMips64EL = - ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; - public: - MipsRelocationTable(const ELFLinkingContext &context, StringRef str, - int32_t order) - : RelocationTable<ELFT>(context, str, order) {} + MipsRelocationTable(const ELFLinkingContext &ctx, StringRef str, + int32_t order); protected: void writeRela(ELFWriter *writer, Elf_Rela &r, const DefinedAtom &atom, - const Reference &ref) override { - uint32_t rType = ref.kindValue() | (ref.tag() << 8); - r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, _isMips64EL); - r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); - // The addend is used only by relative relocations - if (this->_context.isRelativeReloc(ref)) - r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); - else - r.r_addend = 0; - } - + const Reference &ref) override; void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom, - const Reference &ref) override { - uint32_t rType = ref.kindValue() | (ref.tag() << 8); - r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, _isMips64EL); - r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); - } + const Reference &ref) override; }; } // elf diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp index f60ab63c6af7..817e29444666 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp --------------------===// +//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler32EL.cpp ----------------===// // // The LLVM Linker // @@ -7,29 +7,160 @@ // //===----------------------------------------------------------------------===// +#include "ELFReader.h" +#include "MipsELFFile.h" +#include "MipsELFWriters.h" #include "MipsTargetHandler.h" -using namespace lld; -using namespace elf; +namespace lld { +namespace elf { -void MipsRelocationStringTable::registerTable(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, - Reference::KindArch::Mips, kindStrings); +template <class ELFT> +MipsTargetHandler<ELFT>::MipsTargetHandler(MipsLinkingContext &ctx) + : _ctx(ctx), _targetLayout(new MipsTargetLayout<ELFT>(ctx, _abiInfoHandler)), + _relocationHandler( + createMipsRelocationHandler<ELFT>(ctx, *_targetLayout)) {} + +template <class ELFT> +std::unique_ptr<Reader> MipsTargetHandler<ELFT>::getObjReader() { + return llvm::make_unique<ELFReader<MipsELFFile<ELFT>>>(_ctx); +} + +template <class ELFT> +std::unique_ptr<Reader> MipsTargetHandler<ELFT>::getDSOReader() { + return llvm::make_unique<ELFReader<DynamicFile<ELFT>>>(_ctx); +} + +template <class ELFT> +const TargetRelocationHandler & +MipsTargetHandler<ELFT>::getRelocationHandler() const { + return *_relocationHandler; +} + +template <class ELFT> +std::unique_ptr<Writer> MipsTargetHandler<ELFT>::getWriter() { + switch (_ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return llvm::make_unique<MipsExecutableWriter<ELFT>>(_ctx, *_targetLayout, + _abiInfoHandler); + case llvm::ELF::ET_DYN: + return llvm::make_unique<MipsDynamicLibraryWriter<ELFT>>( + _ctx, *_targetLayout, _abiInfoHandler); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +template <class ELFT> MipsAbi MipsTargetHandler<ELFT>::getAbi() const { + return _abiInfoHandler.getAbi(); +} + +template class MipsTargetHandler<ELF32BE>; +template class MipsTargetHandler<ELF32LE>; +template class MipsTargetHandler<ELF64BE>; +template class MipsTargetHandler<ELF64LE>; + +template <class ELFT> +MipsSymbolTable<ELFT>::MipsSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable<ELFT>(ctx, ".symtab", + TargetLayout<ELFT>::ORDER_SYMBOL_TABLE) {} + +template <class ELFT> +void MipsSymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); + + switch (da->codeModel()) { + case DefinedAtom::codeMipsMicro: + sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS; + break; + case DefinedAtom::codeMipsMicroPIC: + sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC; + break; + default: + break; + } } -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +template <class ELFT> void MipsSymbolTable<ELFT>::finalize(bool sort) { + SymbolTable<ELFT>::finalize(sort); -const Registry::KindStrings MipsRelocationStringTable::kindStrings[] = { -#include "llvm/Support/ELFRelocs/Mips.def" - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_GOT), - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_32_HI16), - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_64_HI16), - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_26), - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_HI16), - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_LO16), - LLD_KIND_STRING_ENTRY(LLD_R_MIPS_STO_PLT), - LLD_KIND_STRING_ENTRY(LLD_R_MICROMIPS_GLOBAL_26_S1), - LLD_KIND_STRING_END -}; + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + continue; + if (const auto *da = dyn_cast<DefinedAtom>(ste._atom)) { + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) { + // Adjust dynamic microMIPS symbol value. That allows a dynamic + // linker to recognize and handle this symbol correctly. + ste._symbol.st_value = ste._symbol.st_value | 1; + } + } + } +} + +template class MipsSymbolTable<ELF32BE>; +template class MipsSymbolTable<ELF32LE>; +template class MipsSymbolTable<ELF64BE>; +template class MipsSymbolTable<ELF64LE>; + +template <class ELFT> +MipsDynamicSymbolTable<ELFT>::MipsDynamicSymbolTable( + const ELFLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : DynamicSymbolTable<ELFT>(ctx, layout, ".dynsym", + TargetLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS), + _targetLayout(layout) {} + +template <class ELFT> void MipsDynamicSymbolTable<ELFT>::sortSymbols() { + typedef typename DynamicSymbolTable<ELFT>::SymbolEntry SymbolEntry; + std::stable_sort(this->_symbolTable.begin(), this->_symbolTable.end(), + [this](const SymbolEntry &A, const SymbolEntry &B) { + if (A._symbol.getBinding() != STB_GLOBAL && + B._symbol.getBinding() != STB_GLOBAL) + return A._symbol.getBinding() < B._symbol.getBinding(); + + return _targetLayout.getGOTSection().compare(A._atom, + B._atom); + }); +} + +template <class ELFT> void MipsDynamicSymbolTable<ELFT>::finalize() { + DynamicSymbolTable<ELFT>::finalize(); -#undef ELF_RELOC + const auto &pltSection = _targetLayout.getPLTSection(); + + for (auto &ste : this->_symbolTable) { + const Atom *a = ste._atom; + if (!a) + continue; + if (auto *layout = pltSection.findPLTLayout(a)) { + a = layout->_atom; + // Under some conditions a dynamic symbol table record should hold + // a symbol value of the corresponding PLT entry. For details look + // at the PLT entry creation code in the class MipsRelocationPass. + // Let's update atomLayout fields for such symbols. + assert(!ste._atomLayout); + ste._symbol.st_value = layout->_virtualAddr; + ste._symbol.st_other |= ELF::STO_MIPS_PLT; + } + + if (const auto *da = dyn_cast<DefinedAtom>(a)) { + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) { + // Adjust dynamic microMIPS symbol value. That allows a dynamic + // linker to recognize and handle this symbol correctly. + ste._symbol.st_value = ste._symbol.st_value | 1; + } + } + } +} + +template class MipsDynamicSymbolTable<ELF32BE>; +template class MipsDynamicSymbolTable<ELF32LE>; +template class MipsDynamicSymbolTable<ELF64BE>; +template class MipsDynamicSymbolTable<ELF64LE>; + +} +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h index 79509addf40b..e4a35bdd323d 100644 --- a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h @@ -9,146 +9,36 @@ #ifndef LLD_READER_WRITER_ELF_MIPS_MIPS_TARGET_HANDLER_H #define LLD_READER_WRITER_ELF_MIPS_MIPS_TARGET_HANDLER_H -#include "DefaultTargetHandler.h" -#include "MipsDynamicLibraryWriter.h" -#include "MipsELFReader.h" -#include "MipsExecutableWriter.h" +#include "MipsAbiInfoHandler.h" #include "MipsLinkingContext.h" -#include "MipsRelocationHandler.h" -#include "MipsSectionChunks.h" -#include "TargetLayout.h" -#include "llvm/ADT/DenseSet.h" +#include "MipsTargetLayout.h" +#include "TargetHandler.h" namespace lld { namespace elf { -/// \brief TargetLayout for Mips -template <class ELFT> class MipsTargetLayout final : public TargetLayout<ELFT> { +class MipsBaseTargetHandler : public TargetHandler { public: - MipsTargetLayout(MipsLinkingContext &ctx) - : TargetLayout<ELFT>(ctx), - _gotSection(new (this->_allocator) MipsGOTSection<ELFT>(ctx)), - _pltSection(new (this->_allocator) MipsPLTSection<ELFT>(ctx)) {} - - const MipsGOTSection<ELFT> &getGOTSection() const { return *_gotSection; } - const MipsPLTSection<ELFT> &getPLTSection() const { return *_pltSection; } - - AtomSection<ELFT> *createSection(StringRef name, int32_t type, - DefinedAtom::ContentPermissions permissions, - Layout::SectionOrder order) override { - if (type == DefinedAtom::typeGOT && name == ".got") - return _gotSection; - if (type == DefinedAtom::typeStub && name == ".plt") - return _pltSection; - return DefaultLayout<ELFT>::createSection(name, type, permissions, order); - } - - /// \brief GP offset relative to .got section. - uint64_t getGPOffset() const { return 0x7FF0; } - - /// \brief Get '_gp' symbol atom layout. - AtomLayout *getGP() { - if (!_gpAtom.hasValue()) { - auto atom = this->findAbsoluteAtom("_gp"); - _gpAtom = atom != this->absoluteAtoms().end() ? *atom : nullptr; - } - return *_gpAtom; - } - - /// \brief Get '_gp_disp' symbol atom layout. - AtomLayout *getGPDisp() { - if (!_gpDispAtom.hasValue()) { - auto atom = this->findAbsoluteAtom("_gp_disp"); - _gpDispAtom = atom != this->absoluteAtoms().end() ? *atom : nullptr; - } - return *_gpDispAtom; - } - - /// \brief Return the section order for a input section - Layout::SectionOrder getSectionOrder(StringRef name, int32_t contentType, - int32_t contentPermissions) override { - if ((contentType == DefinedAtom::typeStub) && (name.startswith(".text"))) - return DefaultLayout<ELFT>::ORDER_TEXT; - - return DefaultLayout<ELFT>::getSectionOrder(name, contentType, - contentPermissions); - } - -protected: - unique_bump_ptr<RelocationTable<ELFT>> - createRelocationTable(StringRef name, int32_t order) override { - return unique_bump_ptr<RelocationTable<ELFT>>( - new (this->_allocator) - MipsRelocationTable<ELFT>(this->_context, name, order)); - } - -private: - MipsGOTSection<ELFT> *_gotSection; - MipsPLTSection<ELFT> *_pltSection; - llvm::Optional<AtomLayout *> _gpAtom; - llvm::Optional<AtomLayout *> _gpDispAtom; -}; - -/// \brief Mips Runtime file. -template <class ELFT> class MipsRuntimeFile final : public RuntimeFile<ELFT> { -public: - MipsRuntimeFile(MipsLinkingContext &ctx) - : RuntimeFile<ELFT>(ctx, "Mips runtime file") {} -}; - -/// \brief Auxiliary class holds relocation's names table. -class MipsRelocationStringTable { - static const Registry::KindStrings kindStrings[]; - -public: - static void registerTable(Registry ®istry); + virtual MipsAbi getAbi() const = 0; }; /// \brief TargetHandler for Mips template <class ELFT> -class MipsTargetHandler final : public DefaultTargetHandler<ELFT> { +class MipsTargetHandler final : public MipsBaseTargetHandler { public: - MipsTargetHandler(MipsLinkingContext &ctx) - : _ctx(ctx), _runtimeFile(new MipsRuntimeFile<ELFT>(ctx)), - _targetLayout(new MipsTargetLayout<ELFT>(ctx)), - _relocationHandler(createMipsRelocationHandler<ELFT>(ctx)) {} - - MipsTargetLayout<ELFT> &getTargetLayout() override { return *_targetLayout; } - - std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new MipsELFObjectReader<ELFT>(_ctx)); - } + MipsTargetHandler(MipsLinkingContext &ctx); - std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new MipsELFDSOReader<ELFT>(_ctx)); - } + MipsAbiInfoHandler<ELFT> &getAbiInfoHandler() { return _abiInfoHandler; } - const TargetRelocationHandler &getRelocationHandler() const override { - return *_relocationHandler; - } - - std::unique_ptr<Writer> getWriter() override { - switch (_ctx.getOutputELFType()) { - case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new MipsExecutableWriter<ELFT>(_ctx, *_targetLayout)); - case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new MipsDynamicLibraryWriter<ELFT>(_ctx, *_targetLayout)); - case llvm::ELF::ET_REL: - llvm_unreachable("TODO: support -r mode"); - default: - llvm_unreachable("unsupported output type"); - } - } - - void registerRelocationNames(Registry ®istry) override { - MipsRelocationStringTable::registerTable(registry); - } + std::unique_ptr<Reader> getObjReader() override; + std::unique_ptr<Reader> getDSOReader() override; + const TargetRelocationHandler &getRelocationHandler() const override; + std::unique_ptr<Writer> getWriter() override; + MipsAbi getAbi() const override; private: MipsLinkingContext &_ctx; - std::unique_ptr<MipsRuntimeFile<ELFT>> _runtimeFile; + MipsAbiInfoHandler<ELFT> _abiInfoHandler; std::unique_ptr<MipsTargetLayout<ELFT>> _targetLayout; std::unique_ptr<TargetRelocationHandler> _relocationHandler; }; @@ -157,95 +47,21 @@ template <class ELFT> class MipsSymbolTable : public SymbolTable<ELFT> { public: typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - MipsSymbolTable(const ELFLinkingContext &ctx) - : SymbolTable<ELFT>(ctx, ".symtab", - DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE) {} + MipsSymbolTable(const ELFLinkingContext &ctx); void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, - int64_t addr) override { - SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); - - switch (da->codeModel()) { - case DefinedAtom::codeMipsMicro: - sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS; - break; - case DefinedAtom::codeMipsMicroPIC: - sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC; - break; - default: - break; - } - } - - void finalize(bool sort) override { - SymbolTable<ELFT>::finalize(sort); - - for (auto &ste : this->_symbolTable) { - if (!ste._atom) - continue; - if (const auto *da = dyn_cast<DefinedAtom>(ste._atom)) { - if (da->codeModel() == DefinedAtom::codeMipsMicro || - da->codeModel() == DefinedAtom::codeMipsMicroPIC) { - // Adjust dynamic microMIPS symbol value. That allows a dynamic - // linker to recognize and handle this symbol correctly. - ste._symbol.st_value = ste._symbol.st_value | 1; - } - } - } - } + int64_t addr) override; + void finalize(bool sort) override; }; template <class ELFT> class MipsDynamicSymbolTable : public DynamicSymbolTable<ELFT> { public: MipsDynamicSymbolTable(const ELFLinkingContext &ctx, - MipsTargetLayout<ELFT> &layout) - : DynamicSymbolTable<ELFT>(ctx, layout, ".dynsym", - DefaultLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS), - _targetLayout(layout) {} - - void sortSymbols() override { - typedef typename DynamicSymbolTable<ELFT>::SymbolEntry SymbolEntry; - std::stable_sort(this->_symbolTable.begin(), this->_symbolTable.end(), - [this](const SymbolEntry &A, const SymbolEntry &B) { - if (A._symbol.getBinding() != STB_GLOBAL && - B._symbol.getBinding() != STB_GLOBAL) - return A._symbol.getBinding() < B._symbol.getBinding(); - - return _targetLayout.getGOTSection().compare(A._atom, B._atom); - }); - } - - void finalize() override { - DynamicSymbolTable<ELFT>::finalize(); - - const auto &pltSection = _targetLayout.getPLTSection(); - - for (auto &ste : this->_symbolTable) { - const Atom *a = ste._atom; - if (!a) - continue; - if (auto *layout = pltSection.findPLTLayout(a)) { - a = layout->_atom; - // Under some conditions a dynamic symbol table record should hold - // a symbol value of the corresponding PLT entry. For details look - // at the PLT entry creation code in the class MipsRelocationPass. - // Let's update atomLayout fields for such symbols. - assert(!ste._atomLayout); - ste._symbol.st_value = layout->_virtualAddr; - ste._symbol.st_other |= ELF::STO_MIPS_PLT; - } + MipsTargetLayout<ELFT> &layout); - if (const auto *da = dyn_cast<DefinedAtom>(a)) { - if (da->codeModel() == DefinedAtom::codeMipsMicro || - da->codeModel() == DefinedAtom::codeMipsMicroPIC) { - // Adjust dynamic microMIPS symbol value. That allows a dynamic - // linker to recognize and handle this symbol correctly. - ste._symbol.st_value = ste._symbol.st_value | 1; - } - } - } - } + void sortSymbols() override; + void finalize() override; private: MipsTargetLayout<ELFT> &_targetLayout; diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp new file mode 100644 index 000000000000..710f8320a8b9 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp @@ -0,0 +1,111 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetLayout.cpp ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsLinkingContext.h" +#include "MipsTargetLayout.h" + +namespace lld { +namespace elf { + +template <class ELFT> +MipsTargetLayout<ELFT>::MipsTargetLayout(MipsLinkingContext &ctx, + MipsAbiInfoHandler<ELFT> &abi) + : TargetLayout<ELFT>(ctx), _abiInfo(abi), + _gotSection(new (this->_allocator) MipsGOTSection<ELFT>(ctx)), + _pltSection(new (this->_allocator) MipsPLTSection<ELFT>(ctx)) {} + +template <class ELFT> +AtomSection<ELFT> *MipsTargetLayout<ELFT>::createSection( + StringRef name, int32_t type, DefinedAtom::ContentPermissions permissions, + typename TargetLayout<ELFT>::SectionOrder order) { + if (type == DefinedAtom::typeGOT && name == ".got") + return _gotSection; + if (type == DefinedAtom::typeStub && name == ".plt") + return _pltSection; + return TargetLayout<ELFT>::createSection(name, type, permissions, order); +} + +template <class ELFT> +typename TargetLayout<ELFT>::SegmentType +MipsTargetLayout<ELFT>::getSegmentType(const Section<ELFT> *section) const { + switch (section->order()) { + case ORDER_MIPS_REGINFO: + return _abiInfo.hasMipsAbiSection() ? llvm::ELF::PT_LOAD + : llvm::ELF::PT_MIPS_REGINFO; + case ORDER_MIPS_OPTIONS: + return llvm::ELF::PT_LOAD; + case ORDER_MIPS_ABI_FLAGS: + return llvm::ELF::PT_MIPS_ABIFLAGS; + default: + return TargetLayout<ELFT>::getSegmentType(section); + } +} + +template <class ELFT> uint64_t MipsTargetLayout<ELFT>::getGPAddr() { + std::call_once(_gpOnce, [this]() { + if (AtomLayout *a = this->findAbsoluteAtom("_gp")) + _gpAddr = a->_virtualAddr; + }); + return _gpAddr; +} + +template <class ELFT> +typename TargetLayout<ELFT>::SectionOrder +MipsTargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) { + if ((contentType == DefinedAtom::typeStub) && (name.startswith(".text"))) + return TargetLayout<ELFT>::ORDER_TEXT; + + return TargetLayout<ELFT>::getSectionOrder(name, contentType, + contentPermissions); +} + +template <class ELFT> +unique_bump_ptr<RelocationTable<ELFT>> +MipsTargetLayout<ELFT>::createRelocationTable(StringRef name, int32_t order) { + return unique_bump_ptr<RelocationTable<ELFT>>(new ( + this->_allocator) MipsRelocationTable<ELFT>(this->_ctx, name, order)); +} + +template <class ELFT> +uint64_t MipsTargetLayout<ELFT>::getLookupSectionFlags( + const OutputSection<ELFT> *os) const { + uint64_t flags = TargetLayout<ELFT>::getLookupSectionFlags(os); + return flags & ~llvm::ELF::SHF_MIPS_NOSTRIP; +} + +template <class ELFT> void MipsTargetLayout<ELFT>::sortSegments() { + using namespace llvm::ELF; + TargetLayout<ELFT>::sortSegments(); + // Move PT_MIPS_ABIFLAGS or PT_MIPS_REGINFO right after PT_INTERP. + auto abiIt = + std::find_if(this->_segments.begin(), this->_segments.end(), + [](const Segment<ELFT> *s) { + auto typ = s->segmentType(); + return typ == PT_MIPS_ABIFLAGS || typ == PT_MIPS_REGINFO; + }); + if (abiIt == this->_segments.end()) + return; + Segment<ELFT> *abiSeg = *abiIt; + this->_segments.erase(abiIt); + auto outIt = std::find_if(this->_segments.begin(), this->_segments.end(), + [](const Segment<ELFT> *s) { + auto typ = s->segmentType(); + return typ != PT_PHDR && typ != PT_INTERP; + }); + this->_segments.insert(outIt, abiSeg); +} + +template class MipsTargetLayout<ELF32BE>; +template class MipsTargetLayout<ELF32LE>; +template class MipsTargetLayout<ELF64BE>; +template class MipsTargetLayout<ELF64LE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.h b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.h new file mode 100644 index 000000000000..08855438d20e --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetLayout.h @@ -0,0 +1,71 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetLayout.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_MIPS_MIPS_TARGET_LAYOUT_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_TARGET_LAYOUT_H + +#include "MipsAbiInfoHandler.h" +#include "MipsSectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +class MipsLinkingContext; + +/// \brief TargetLayout for Mips +template <class ELFT> class MipsTargetLayout final : public TargetLayout<ELFT> { +public: + enum MipsSectionOrder { + ORDER_MIPS_ABI_FLAGS = TargetLayout<ELFT>::ORDER_RO_NOTE + 1, + ORDER_MIPS_REGINFO, + ORDER_MIPS_OPTIONS, + }; + + MipsTargetLayout(MipsLinkingContext &ctx, MipsAbiInfoHandler<ELFT> &abi); + + const MipsGOTSection<ELFT> &getGOTSection() const { return *_gotSection; } + const MipsPLTSection<ELFT> &getPLTSection() const { return *_pltSection; } + + AtomSection<ELFT> * + createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + typename TargetLayout<ELFT>::SectionOrder order) override; + + typename TargetLayout<ELFT>::SegmentType + getSegmentType(const Section<ELFT> *section) const override; + + /// \brief GP offset relative to .got section. + uint64_t getGPOffset() const { return 0x7FF0; } + + /// \brief Get '_gp' symbol address. + uint64_t getGPAddr(); + + /// \brief Return the section order for a input section + typename TargetLayout<ELFT>::SectionOrder + getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override; + +protected: + unique_bump_ptr<RelocationTable<ELFT>> + createRelocationTable(StringRef name, int32_t order) override; + uint64_t getLookupSectionFlags(const OutputSection<ELFT> *os) const override; + void sortSegments() override; + +private: + MipsAbiInfoHandler<ELFT> &_abiInfo; + MipsGOTSection<ELFT> *_gotSection; + MipsPLTSection<ELFT> *_pltSection; + uint64_t _gpAddr = 0; + std::once_flag _gpOnce; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/OrderPass.h b/lib/ReaderWriter/ELF/OrderPass.h index d126b830db96..11f88056c8c4 100644 --- a/lib/ReaderWriter/ELF/OrderPass.h +++ b/lib/ReaderWriter/ELF/OrderPass.h @@ -19,9 +19,10 @@ namespace elf { /// \brief This pass sorts atoms by file and atom ordinals. class OrderPass : public Pass { public: - void perform(std::unique_ptr<MutableFile> &file) override { - parallel_sort(file->definedAtoms().begin(), file->definedAtoms().end(), + std::error_code perform(SimpleFile &file) override { + parallel_sort(file.definedAtoms().begin(), file.definedAtoms().end(), DefinedAtom::compareByPosition); + return std::error_code(); } }; } diff --git a/lib/ReaderWriter/ELF/OutputELFWriter.cpp b/lib/ReaderWriter/ELF/OutputELFWriter.cpp new file mode 100644 index 000000000000..4f8b0eac655f --- /dev/null +++ b/lib/ReaderWriter/ELF/OutputELFWriter.cpp @@ -0,0 +1,514 @@ +//===- lib/ReaderWriter/ELF/OutputELFWriter.cpp --------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "OutputELFWriter.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Support/Path.h" + +namespace lld { +namespace elf { + +namespace { + +template <class ELFT> class SymbolFile : public RuntimeFile<ELFT> { +public: + SymbolFile(ELFLinkingContext &ctx) + : RuntimeFile<ELFT>(ctx, "Dynamic absolute symbols") {} + + void addUndefinedAtom(StringRef) override { + llvm_unreachable("Cannot add undefined atoms to resolve undefined symbols"); + } + + bool hasAtoms() const { return this->absolute().size(); } +}; + +template <class ELFT> +class DynamicSymbolFile : public SimpleArchiveLibraryFile { + typedef std::function<void(StringRef, RuntimeFile<ELFT> &)> Resolver; + +public: + DynamicSymbolFile(ELFLinkingContext &ctx, Resolver resolver) + : SimpleArchiveLibraryFile("Dynamically added runtime symbols"), + _ctx(ctx), _resolver(resolver) {} + + File *find(StringRef sym, bool dataSymbolOnly) override { + if (!_file) + _file.reset(new (_alloc) SymbolFile<ELFT>(_ctx)); + + assert(!_file->hasAtoms() && "The file shouldn't have atoms yet"); + _resolver(sym, *_file); + + if (!_file->hasAtoms()) + return nullptr; + + // If atoms were added - return the file but also store it for later + // destruction. + File *result = _file.get(); + _returnedFiles.push_back(std::move(_file)); + return result; + } + +private: + ELFLinkingContext &_ctx; + Resolver _resolver; + + // The allocator should go before bump pointers because of + // reversed destruction order. + llvm::BumpPtrAllocator _alloc; + unique_bump_ptr<SymbolFile<ELFT>> _file; + std::vector<unique_bump_ptr<SymbolFile<ELFT>>> _returnedFiles; +}; + +} // end anon namespace + +template <class ELFT> +OutputELFWriter<ELFT>::OutputELFWriter(ELFLinkingContext &ctx, + TargetLayout<ELFT> &layout) + : _ctx(ctx), _targetHandler(ctx.getTargetHandler()), _layout(layout) {} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildChunks(const File &file) { + ScopedTask task(getDefaultDomain(), "buildChunks"); + for (const DefinedAtom *definedAtom : file.defined()) { + DefinedAtom::ContentType contentType = definedAtom->contentType(); + // Dont add COMDAT group atoms and GNU linkonce atoms, as they are used for + // symbol resolution. + // TODO: handle partial linking. + if (contentType == DefinedAtom::typeGroupComdat || + contentType == DefinedAtom::typeGnuLinkOnce) + continue; + _layout.addAtom(definedAtom); + } + for (const AbsoluteAtom *absoluteAtom : file.absolute()) + _layout.addAtom(absoluteAtom); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildStaticSymbolTable(const File &file) { + ScopedTask task(getDefaultDomain(), "buildStaticSymbolTable"); + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) + _symtab->addSymbol(atom->_atom, section->ordinal(), atom->_virtualAddr); + for (auto &atom : _layout.absoluteAtoms()) + _symtab->addSymbol(atom->_atom, ELF::SHN_ABS, atom->_virtualAddr); + for (const UndefinedAtom *a : file.undefined()) + _symtab->addSymbol(a, ELF::SHN_UNDEF); +} + +// Returns the DSO name for a given input file if it's a shared library +// file and not marked as --as-needed. +template <class ELFT> +StringRef OutputELFWriter<ELFT>::maybeGetSOName(Node *node) { + if (auto *fnode = dyn_cast<FileNode>(node)) + if (!fnode->asNeeded()) + if (auto *file = dyn_cast<SharedLibraryFile>(fnode->getFile())) + return file->getDSOName(); + return ""; +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + ScopedTask task(getDefaultDomain(), "buildDynamicSymbolTable"); + for (const auto &sla : file.sharedLibrary()) { + if (isDynSymEntryRequired(sla)) { + _dynamicSymbolTable->addSymbol(sla, ELF::SHN_UNDEF); + _soNeeded.insert(sla->loadName()); + continue; + } + if (isNeededTagRequired(sla)) + _soNeeded.insert(sla->loadName()); + } + for (const std::unique_ptr<Node> &node : _ctx.getNodes()) { + StringRef soname = maybeGetSOName(node.get()); + if (!soname.empty()) + _soNeeded.insert(soname); + } + // Never mark the dynamic linker as DT_NEEDED + _soNeeded.erase(sys::path::filename(_ctx.getInterpreter())); + for (const auto &loadName : _soNeeded) + _dynamicTable->addEntry(DT_NEEDED, + _dynamicStringTable->addString(loadName.getKey())); + const auto &rpathList = _ctx.getRpathList(); + if (!rpathList.empty()) { + auto rpath = + new (_alloc) std::string(join(rpathList.begin(), rpathList.end(), ":")); + _dynamicTable->addEntry(_ctx.getEnableNewDtags() ? DT_RUNPATH : DT_RPATH, + _dynamicStringTable->addString(*rpath)); + } + StringRef soname = _ctx.sharedObjectName(); + if (!soname.empty() && _ctx.getOutputELFType() == llvm::ELF::ET_DYN) + _dynamicTable->addEntry(DT_SONAME, _dynamicStringTable->addString(soname)); + + // Add DT_FLAGS/DT_FLAGS_1 entries if necessary. + uint32_t dtflags = 0, dt1flags = 0; + if (_ctx.getDTFlag(ELFLinkingContext::DTFlag::DT_NOW)) { + dtflags |= DF_BIND_NOW; + dt1flags |= DF_1_NOW; + } + if (_ctx.getDTFlag(ELFLinkingContext::DTFlag::DT_ORIGIN)) { + dtflags |= DF_ORIGIN; + dt1flags |= DF_1_ORIGIN; + } + if (dtflags != 0) + _dynamicTable->addEntry(DT_FLAGS, dtflags); + if (dt1flags != 0) + _dynamicTable->addEntry(DT_FLAGS_1, dt1flags); + + // The dynamic symbol table need to be sorted earlier because the hash + // table needs to be built using the dynamic symbol table. It would be + // late to sort the symbols due to that in finalize. In the dynamic symbol + // table finalize, we call the symbol table finalize and we don't want to + // sort again + _dynamicSymbolTable->sortSymbols(); + + // Add the dynamic symbols into the hash table + _dynamicSymbolTable->addSymbolsToHashTable(); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildAtomToAddressMap(const File &file) { + ScopedTask task(getDefaultDomain(), "buildAtomToAddressMap"); + int64_t totalAbsAtoms = _layout.absoluteAtoms().size(); + int64_t totalUndefinedAtoms = file.undefined().size(); + int64_t totalDefinedAtoms = 0; + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) { + totalDefinedAtoms += section->atoms().size(); + for (const auto &atom : section->atoms()) + _atomToAddressMap[atom->_atom] = atom->_virtualAddr; + } + // build the atomToAddressMap that contains absolute symbols too + for (auto &atom : _layout.absoluteAtoms()) + _atomToAddressMap[atom->_atom] = atom->_virtualAddr; + + // Set the total number of atoms in the symbol table, so that appropriate + // resizing of the string table can be done. + // There's no such thing as symbol table if we're stripping all the symbols + if (!_ctx.stripSymbols()) + _symtab->setNumEntries(totalDefinedAtoms + totalAbsAtoms + + totalUndefinedAtoms); +} + +template <class ELFT> void OutputELFWriter<ELFT>::buildSectionHeaderTable() { + ScopedTask task(getDefaultDomain(), "buildSectionHeaderTable"); + for (auto outputSection : _layout.outputSections()) { + if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && + outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) + continue; + if (outputSection->hasSegment()) + _shdrtab->appendSection(outputSection); + } +} + +template <class ELFT> +void OutputELFWriter<ELFT>::assignSectionsWithNoSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsWithNoSegments"); + for (auto outputSection : _layout.outputSections()) { + if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && + outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) + continue; + if (!outputSection->hasSegment()) + _shdrtab->appendSection(outputSection); + } + _layout.assignFileOffsetsForMiscSections(); + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (!TargetLayout<ELFT>::hasOutputSegment(section)) + _shdrtab->updateSection(section); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + // Add the virtual archive to resolve undefined symbols. + // The file will be added later in the linking context. + auto callback = [this](StringRef sym, RuntimeFile<ELFT> &file) { + processUndefinedSymbol(sym, file); + }; + _ctx.setUndefinesResolver( + llvm::make_unique<DynamicSymbolFile<ELFT>>(_ctx, std::move(callback))); + // Add script defined symbols + auto file = + llvm::make_unique<RuntimeFile<ELFT>>(_ctx, "Linker script runtime"); + for (auto &sym : this->_ctx.linkerScriptSema().getScriptDefinedSymbols()) + file->addAbsoluteAtom(sym.getKey()); + result.push_back(std::move(file)); +} + +template <class ELFT> void OutputELFWriter<ELFT>::finalizeDefaultAtomValues() { + const llvm::StringSet<> &symbols = + _ctx.linkerScriptSema().getScriptDefinedSymbols(); + for (auto &sym : symbols) { + uint64_t res = + _ctx.linkerScriptSema().getLinkerScriptExprValue(sym.getKey()); + AtomLayout *a = _layout.findAbsoluteAtom(sym.getKey()); + assert(a); + a->_virtualAddr = res; + } + // If there is a section named XXX, and XXX is a valid C identifier, + // and there are undefined or weak __start_XXX/__stop_XXX symbols, + // set the symbols values to the begin/end of the XXX section + // correspondingly. + for (const auto &name : _ctx.cidentSectionNames()) + updateScopeAtomValues((Twine("__start_") + name.getKey()).str(), + (Twine("__stop_") + name.getKey()).str(), + name.getKey()); +} + +template <class ELFT> void OutputELFWriter<ELFT>::createDefaultSections() { + _elfHeader.reset(new (_alloc) ELFHeader<ELFT>(_ctx)); + _programHeader.reset(new (_alloc) ProgramHeader<ELFT>(_ctx)); + _layout.setHeader(_elfHeader.get()); + _layout.setProgramHeader(_programHeader.get()); + + // Don't create .symtab and .strtab sections if we're going to + // strip all the symbols. + if (!_ctx.stripSymbols()) { + _symtab = this->createSymbolTable(); + _strtab.reset(new (_alloc) StringTable<ELFT>( + _ctx, ".strtab", TargetLayout<ELFT>::ORDER_STRING_TABLE)); + _layout.addSection(_symtab.get()); + _layout.addSection(_strtab.get()); + _symtab->setStringSection(_strtab.get()); + } + + _shstrtab.reset(new (_alloc) StringTable<ELFT>( + _ctx, ".shstrtab", TargetLayout<ELFT>::ORDER_SECTION_STRINGS)); + _shdrtab.reset(new (_alloc) SectionHeader<ELFT>( + _ctx, TargetLayout<ELFT>::ORDER_SECTION_HEADERS)); + _layout.addSection(_shstrtab.get()); + _shdrtab->setStringSection(_shstrtab.get()); + _layout.addSection(_shdrtab.get()); + + for (auto sec : _layout.sections()) { + // TODO: use findOutputSection + auto section = dyn_cast<Section<ELFT>>(sec); + if (!section || section->outputSectionName() != ".eh_frame") + continue; + _ehFrameHeader.reset(new (_alloc) EHFrameHeader<ELFT>( + _ctx, ".eh_frame_hdr", _layout, TargetLayout<ELFT>::ORDER_EH_FRAMEHDR)); + _layout.addSection(_ehFrameHeader.get()); + break; + } + + if (_ctx.isDynamic()) { + _dynamicTable = createDynamicTable(); + _dynamicStringTable.reset(new (_alloc) StringTable<ELFT>( + _ctx, ".dynstr", TargetLayout<ELFT>::ORDER_DYNAMIC_STRINGS, true)); + _dynamicSymbolTable = createDynamicSymbolTable(); + _hashTable.reset(new (_alloc) HashSection<ELFT>( + _ctx, ".hash", TargetLayout<ELFT>::ORDER_HASH)); + // Set the hash table in the dynamic symbol table so that the entries in the + // hash table can be created + _dynamicSymbolTable->setHashTable(_hashTable.get()); + _hashTable->setSymbolTable(_dynamicSymbolTable.get()); + _layout.addSection(_dynamicTable.get()); + _layout.addSection(_dynamicStringTable.get()); + _layout.addSection(_dynamicSymbolTable.get()); + _layout.addSection(_hashTable.get()); + _dynamicSymbolTable->setStringSection(_dynamicStringTable.get()); + _dynamicTable->setSymbolTable(_dynamicSymbolTable.get()); + _dynamicTable->setHashTable(_hashTable.get()); + if (_layout.hasDynamicRelocationTable()) + _layout.getDynamicRelocationTable()->setSymbolTable( + _dynamicSymbolTable.get()); + if (_layout.hasPLTRelocationTable()) + _layout.getPLTRelocationTable()->setSymbolTable( + _dynamicSymbolTable.get()); + } +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> OutputELFWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new (_alloc) SymbolTable<ELFT>( + this->_ctx, ".symtab", TargetLayout<ELFT>::ORDER_SYMBOL_TABLE)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> +OutputELFWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>(new (_alloc) DynamicTable<ELFT>( + this->_ctx, _layout, ".dynamic", TargetLayout<ELFT>::ORDER_DYNAMIC)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> +OutputELFWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (_alloc) + DynamicSymbolTable<ELFT>(this->_ctx, _layout, ".dynsym", + TargetLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS)); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::buildOutput(const File &file) { + ScopedTask buildTask(getDefaultDomain(), "ELF Writer buildOutput"); + buildChunks(file); + + // Create the default sections like the symbol table, string table, and the + // section string table + createDefaultSections(); + + // Set the Layout + _layout.assignSectionsToSegments(); + + // Create the dynamic table entries + if (_ctx.isDynamic()) { + _dynamicTable->createDefaultEntries(); + buildDynamicSymbolTable(file); + } + + // Call the preFlight callbacks to modify the sections and the atoms + // contained in them, in anyway the targets may want + _layout.doPreFlight(); + + _layout.assignVirtualAddress(); + + // Finalize the default value of symbols that the linker adds + finalizeDefaultAtomValues(); + + // Build the Atom To Address map for applying relocations + buildAtomToAddressMap(file); + + // Create symbol table and section string table + // Do it only if -s is not specified. + if (!_ctx.stripSymbols()) + buildStaticSymbolTable(file); + + // Finalize the layout by calling the finalize() functions + _layout.finalize(); + + // build Section Header table + buildSectionHeaderTable(); + + // assign Offsets and virtual addresses + // for sections with no segments + assignSectionsWithNoSegments(); + + if (_ctx.isDynamic()) + _dynamicTable->updateDynamicTable(); + + return std::error_code(); +} + +template <class ELFT> std::error_code OutputELFWriter<ELFT>::setELFHeader() { + _elfHeader->e_type(_ctx.getOutputELFType()); + _elfHeader->e_machine(_ctx.getOutputMachine()); + _elfHeader->e_ident(ELF::EI_VERSION, 1); + _elfHeader->e_ident(ELF::EI_OSABI, 0); + _elfHeader->e_version(1); + _elfHeader->e_phoff(_programHeader->fileOffset()); + _elfHeader->e_shoff(_shdrtab->fileOffset()); + _elfHeader->e_phentsize(_programHeader->entsize()); + _elfHeader->e_phnum(_programHeader->numHeaders()); + _elfHeader->e_shentsize(_shdrtab->entsize()); + _elfHeader->e_shnum(_shdrtab->numHeaders()); + _elfHeader->e_shstrndx(_shstrtab->ordinal()); + if (const auto *al = _layout.findAtomLayoutByName(_ctx.entrySymbolName())) + _elfHeader->e_entry(al->_virtualAddr); + else + _elfHeader->e_entry(0); + + return std::error_code(); +} + +template <class ELFT> uint64_t OutputELFWriter<ELFT>::outputFileSize() const { + return _shdrtab->fileOffset() + _shdrtab->fileSize(); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::writeOutput(const File &file, + StringRef path) { + + ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); + ErrorOr<std::unique_ptr<FileOutputBuffer>> bufferOrErr = + FileOutputBuffer::create(path, outputFileSize(), + FileOutputBuffer::F_executable); + if (std::error_code ec = bufferOrErr.getError()) + return ec; + std::unique_ptr<FileOutputBuffer> &buffer = *bufferOrErr; + createOutputTask.end(); + + ScopedTask writeTask(getDefaultDomain(), "ELF Writer write to memory"); + + // HACK: We have to write out the header and program header here even though + // they are a member of a segment because only sections are written in the + // following loop. + + // Finalize ELF Header / Program Headers. + _elfHeader->finalize(); + _programHeader->finalize(); + + _elfHeader->write(this, _layout, *buffer); + _programHeader->write(this, _layout, *buffer); + + auto sections = _layout.sections(); + parallel_for_each( + sections.begin(), sections.end(), + [&](Chunk<ELFT> *section) { section->write(this, _layout, *buffer); }); + writeTask.end(); + + ScopedTask commitTask(getDefaultDomain(), "ELF Writer commit to disk"); + return buffer->commit(); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::writeFile(const File &file, + StringRef path) { + if (std::error_code ec = buildOutput(file)) + return ec; + if (std::error_code ec = setELFHeader()) + return ec; + return writeOutput(file, path); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::processUndefinedSymbol( + StringRef symName, RuntimeFile<ELFT> &file) const { + if (symName.startswith("__start_")) { + if (_ctx.cidentSectionNames().count(symName.drop_front(8))) + file.addAbsoluteAtom(symName); + } else if (symName.startswith("__stop_")) { + if (_ctx.cidentSectionNames().count(symName.drop_front(7))) + file.addAbsoluteAtom(symName); + } +} + +template <class ELFT> +void OutputELFWriter<ELFT>::updateScopeAtomValues(StringRef sym, + StringRef sec) { + updateScopeAtomValues(("__" + sym + "_start").str().c_str(), + ("__" + sym + "_end").str().c_str(), sec); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::updateScopeAtomValues(StringRef start, + StringRef end, + StringRef sec) { + AtomLayout *s = _layout.findAbsoluteAtom(start); + AtomLayout *e = _layout.findAbsoluteAtom(end); + const OutputSection<ELFT> *section = _layout.findOutputSection(sec); + if (s) + s->_virtualAddr = section ? section->virtualAddr() : 0; + if (e) + e->_virtualAddr = section ? section->virtualAddr() + section->memSize() : 0; +} + +template class OutputELFWriter<ELF32LE>; +template class OutputELFWriter<ELF32BE>; +template class OutputELFWriter<ELF64LE>; +template class OutputELFWriter<ELF64BE>; + +} // namespace elf +} // namespace lld diff --git a/lib/ReaderWriter/ELF/OutputELFWriter.h b/lib/ReaderWriter/ELF/OutputELFWriter.h index c137905b936b..bb3901010634 100644 --- a/lib/ReaderWriter/ELF/OutputELFWriter.h +++ b/lib/ReaderWriter/ELF/OutputELFWriter.h @@ -6,88 +6,24 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// + #ifndef LLD_READER_WRITER_ELF_OUTPUT_WRITER_H #define LLD_READER_WRITER_ELF_OUTPUT_WRITER_H -#include "DefaultLayout.h" #include "ELFFile.h" #include "TargetLayout.h" -#include "lld/Core/Instrumentation.h" -#include "lld/Core/Parallel.h" -#include "lld/Core/SharedLibraryFile.h" -#include "lld/ReaderWriter/ELFLinkingContext.h" -#include "lld/Core/Simple.h" #include "lld/Core/Writer.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSet.h" -#include "llvm/Support/Path.h" namespace lld { +class ELFLinkingContext; + namespace elf { using namespace llvm; using namespace llvm::object; -template <class ELFT> class OutputELFWriter; -template <class ELFT> class TargetLayout; - -namespace { - -template<class ELFT> -class SymbolFile : public RuntimeFile<ELFT> { -public: - SymbolFile(ELFLinkingContext &context) - : RuntimeFile<ELFT>(context, "Dynamic absolute symbols"), - _atomsAdded(false) {} - - Atom *addAbsoluteAtom(StringRef symbolName) override { - auto *a = RuntimeFile<ELFT>::addAbsoluteAtom(symbolName); - if (a) _atomsAdded = true; - return a; - } - - Atom *addUndefinedAtom(StringRef) override { - llvm_unreachable("Cannot add undefined atoms to resolve undefined symbols"); - } - - bool hasAtoms() const { return _atomsAdded; } - -private: - bool _atomsAdded; -}; - -template<class ELFT> -class DynamicSymbolFile : public SimpleArchiveLibraryFile { - typedef std::function<void(StringRef, RuntimeFile<ELFT> &)> Resolver; -public: - DynamicSymbolFile(ELFLinkingContext &context, Resolver resolver) - : SimpleArchiveLibraryFile("Dynamically added runtime symbols"), - _context(context), _resolver(resolver) {} - - File *find(StringRef sym, bool dataSymbolOnly) override { - if (!_file) - _file.reset(new (_alloc) SymbolFile<ELFT>(_context)); - - assert(!_file->hasAtoms() && "The file shouldn't have atoms yet"); - _resolver(sym, *_file); - // If atoms were added - release the file to the caller. - return _file->hasAtoms() ? _file.release() : nullptr; - } - -private: - ELFLinkingContext &_context; - Resolver _resolver; - - // The allocator should go before bump pointers because of - // reversed destruction order. - llvm::BumpPtrAllocator _alloc; - unique_bump_ptr<SymbolFile<ELFT>> _file; -}; - -} // end anon namespace - -//===----------------------------------------------------------------------===// // OutputELFWriter Class -//===----------------------------------------------------------------------===// +// /// \brief This acts as the base class for all the ELF writers that are output /// for emitting an ELF output file. This class also acts as a common class for /// creating static and dynamic executables. All the function in this class @@ -99,7 +35,7 @@ public: typedef Elf_Sym_Impl<ELFT> Elf_Sym; typedef Elf_Dyn_Impl<ELFT> Elf_Dyn; - OutputELFWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout); + OutputELFWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout); protected: // build the sections that need to be created @@ -140,11 +76,8 @@ protected: // section header table, string table etc virtual void assignSectionsWithNoSegments(); - // Add default atoms that need to be present in the output file - virtual void addDefaultAtoms(); - // Add any runtime files and their atoms to the output - bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; // Finalize the default atom values virtual void finalizeDefaultAtomValues(); @@ -180,12 +113,15 @@ protected: /// \brief Process undefined symbols that left after resolution step. virtual void processUndefinedSymbol(StringRef symName, - RuntimeFile<ELFT> &file) const {} + RuntimeFile<ELFT> &file) const; + + /// \brief Assign addresses to atoms marking section's start and end. + void updateScopeAtomValues(StringRef sym, StringRef sec); llvm::BumpPtrAllocator _alloc; - ELFLinkingContext &_context; - TargetHandler<ELFT> &_targetHandler; + ELFLinkingContext &_ctx; + TargetHandler &_targetHandler; typedef llvm::DenseMap<const Atom *, uint64_t> AtomToAddress; AtomToAddress _atomToAddressMap; @@ -205,410 +141,12 @@ protected: unique_bump_ptr<HashSection<ELFT>> _hashTable; llvm::StringSet<> _soNeeded; /// @} - std::unique_ptr<RuntimeFile<ELFT>> _scriptFile; private: static StringRef maybeGetSOName(Node *node); + void updateScopeAtomValues(StringRef start, StringRef end, StringRef sec); }; -//===----------------------------------------------------------------------===// -// OutputELFWriter -//===----------------------------------------------------------------------===// -template <class ELFT> -OutputELFWriter<ELFT>::OutputELFWriter(ELFLinkingContext &context, - TargetLayout<ELFT> &layout) - : _context(context), _targetHandler(context.getTargetHandler<ELFT>()), - _layout(layout), - _scriptFile(new RuntimeFile<ELFT>(context, "Linker script runtime")) {} - -template <class ELFT> -void OutputELFWriter<ELFT>::buildChunks(const File &file) { - ScopedTask task(getDefaultDomain(), "buildChunks"); - for (const DefinedAtom *definedAtom : file.defined()) { - DefinedAtom::ContentType contentType = definedAtom->contentType(); - // Dont add COMDAT group atoms and GNU linkonce atoms, as they are used for - // symbol resolution. - // TODO: handle partial linking. - if (contentType == DefinedAtom::typeGroupComdat || - contentType == DefinedAtom::typeGnuLinkOnce) - continue; - _layout.addAtom(definedAtom); - } - for (const AbsoluteAtom *absoluteAtom : file.absolute()) - _layout.addAtom(absoluteAtom); -} - -template <class ELFT> -void OutputELFWriter<ELFT>::buildStaticSymbolTable(const File &file) { - ScopedTask task(getDefaultDomain(), "buildStaticSymbolTable"); - for (auto sec : _layout.sections()) - if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) - for (const auto &atom : section->atoms()) - _symtab->addSymbol(atom->_atom, section->ordinal(), atom->_virtualAddr); - for (auto &atom : _layout.absoluteAtoms()) - _symtab->addSymbol(atom->_atom, ELF::SHN_ABS, atom->_virtualAddr); - for (const UndefinedAtom *a : file.undefined()) - _symtab->addSymbol(a, ELF::SHN_UNDEF); -} - -// Returns the DSO name for a given input file if it's a shared library -// file and not marked as --as-needed. -template <class ELFT> -StringRef OutputELFWriter<ELFT>::maybeGetSOName(Node *node) { - if (auto *fnode = dyn_cast<FileNode>(node)) - if (!fnode->asNeeded()) - if (auto *file = dyn_cast<SharedLibraryFile>(fnode->getFile())) - return file->getDSOName(); - return ""; -} - -template <class ELFT> -void OutputELFWriter<ELFT>::buildDynamicSymbolTable(const File &file) { - ScopedTask task(getDefaultDomain(), "buildDynamicSymbolTable"); - for (const auto &sla : file.sharedLibrary()) { - if (isDynSymEntryRequired(sla)) { - _dynamicSymbolTable->addSymbol(sla, ELF::SHN_UNDEF); - _soNeeded.insert(sla->loadName()); - continue; - } - if (isNeededTagRequired(sla)) - _soNeeded.insert(sla->loadName()); - } - for (const std::unique_ptr<Node> &node : _context.getNodes()) { - StringRef soname = maybeGetSOName(node.get()); - if (!soname.empty()) - _soNeeded.insert(soname); - } - // Never mark the dynamic linker as DT_NEEDED - _soNeeded.erase(sys::path::filename(_context.getInterpreter())); - for (const auto &loadName : _soNeeded) { - Elf_Dyn dyn; - dyn.d_tag = DT_NEEDED; - dyn.d_un.d_val = _dynamicStringTable->addString(loadName.getKey()); - _dynamicTable->addEntry(dyn); - } - const auto &rpathList = _context.getRpathList(); - if (!rpathList.empty()) { - auto rpath = new (_alloc) std::string(join(rpathList.begin(), - rpathList.end(), ":")); - Elf_Dyn dyn; - dyn.d_tag = DT_RPATH; - dyn.d_un.d_val = _dynamicStringTable->addString(*rpath); - _dynamicTable->addEntry(dyn); - } - StringRef soname = _context.sharedObjectName(); - if (!soname.empty() && _context.getOutputELFType() == llvm::ELF::ET_DYN) { - Elf_Dyn dyn; - dyn.d_tag = DT_SONAME; - dyn.d_un.d_val = _dynamicStringTable->addString(soname); - _dynamicTable->addEntry(dyn); - } - // The dynamic symbol table need to be sorted earlier because the hash - // table needs to be built using the dynamic symbol table. It would be - // late to sort the symbols due to that in finalize. In the dynamic symbol - // table finalize, we call the symbol table finalize and we don't want to - // sort again - _dynamicSymbolTable->sortSymbols(); - - // Add the dynamic symbols into the hash table - _dynamicSymbolTable->addSymbolsToHashTable(); -} - -template <class ELFT> -void OutputELFWriter<ELFT>::buildAtomToAddressMap(const File &file) { - ScopedTask task(getDefaultDomain(), "buildAtomToAddressMap"); - int64_t totalAbsAtoms = _layout.absoluteAtoms().size(); - int64_t totalUndefinedAtoms = file.undefined().size(); - int64_t totalDefinedAtoms = 0; - for (auto sec : _layout.sections()) - if (auto section = dyn_cast<AtomSection<ELFT> >(sec)) { - totalDefinedAtoms += section->atoms().size(); - for (const auto &atom : section->atoms()) - _atomToAddressMap[atom->_atom] = atom->_virtualAddr; - } - // build the atomToAddressMap that contains absolute symbols too - for (auto &atom : _layout.absoluteAtoms()) - _atomToAddressMap[atom->_atom] = atom->_virtualAddr; - - // Set the total number of atoms in the symbol table, so that appropriate - // resizing of the string table can be done - _symtab->setNumEntries(totalDefinedAtoms + totalAbsAtoms + - totalUndefinedAtoms); -} - -template<class ELFT> -void OutputELFWriter<ELFT>::buildSectionHeaderTable() { - ScopedTask task(getDefaultDomain(), "buildSectionHeaderTable"); - for (auto outputSection : _layout.outputSections()) { - if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && - outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) - continue; - if (outputSection->hasSegment()) - _shdrtab->appendSection(outputSection); - } -} - -template<class ELFT> -void OutputELFWriter<ELFT>::assignSectionsWithNoSegments() { - ScopedTask task(getDefaultDomain(), "assignSectionsWithNoSegments"); - for (auto outputSection : _layout.outputSections()) { - if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && - outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) - continue; - if (!outputSection->hasSegment()) - _shdrtab->appendSection(outputSection); - } - _layout.assignFileOffsetsForMiscSections(); - for (auto sec : _layout.sections()) - if (auto section = dyn_cast<Section<ELFT>>(sec)) - if (!DefaultLayout<ELFT>::hasOutputSegment(section)) - _shdrtab->updateSection(section); -} - -template <class ELFT> void OutputELFWriter<ELFT>::addDefaultAtoms() { - const llvm::StringSet<> &symbols = - _context.linkerScriptSema().getScriptDefinedSymbols(); - for (auto &sym : symbols) - _scriptFile->addAbsoluteAtom(sym.getKey()); -} - -template <class ELFT> -bool OutputELFWriter<ELFT>::createImplicitFiles( - std::vector<std::unique_ptr<File>> &result) { - // Add the virtual archive to resolve undefined symbols. - // The file will be added later in the linking context. - auto callback = [this](StringRef sym, RuntimeFile<ELFT> &file) { - processUndefinedSymbol(sym, file); - }; - auto &ctx = const_cast<ELFLinkingContext &>(_context); - ctx.setUndefinesResolver( - llvm::make_unique<DynamicSymbolFile<ELFT>>(ctx, std::move(callback))); - // Add script defined symbols - result.push_back(std::move(_scriptFile)); - return true; -} - -template <class ELFT> -void OutputELFWriter<ELFT>::finalizeDefaultAtomValues() { - const llvm::StringSet<> &symbols = - _context.linkerScriptSema().getScriptDefinedSymbols(); - for (auto &sym : symbols) { - uint64_t res = - _context.linkerScriptSema().getLinkerScriptExprValue(sym.getKey()); - auto a = _layout.findAbsoluteAtom(sym.getKey()); - (*a)->_virtualAddr = res; - } -} - -template <class ELFT> void OutputELFWriter<ELFT>::createDefaultSections() { - _elfHeader.reset(new (_alloc) ELFHeader<ELFT>(_context)); - _programHeader.reset(new (_alloc) ProgramHeader<ELFT>(_context)); - _layout.setHeader(_elfHeader.get()); - _layout.setProgramHeader(_programHeader.get()); - - _symtab = std::move(this->createSymbolTable()); - _strtab.reset(new (_alloc) StringTable<ELFT>( - _context, ".strtab", DefaultLayout<ELFT>::ORDER_STRING_TABLE)); - _shstrtab.reset(new (_alloc) StringTable<ELFT>( - _context, ".shstrtab", DefaultLayout<ELFT>::ORDER_SECTION_STRINGS)); - _shdrtab.reset(new (_alloc) SectionHeader<ELFT>( - _context, DefaultLayout<ELFT>::ORDER_SECTION_HEADERS)); - _layout.addSection(_symtab.get()); - _layout.addSection(_strtab.get()); - _layout.addSection(_shstrtab.get()); - _shdrtab->setStringSection(_shstrtab.get()); - _symtab->setStringSection(_strtab.get()); - _layout.addSection(_shdrtab.get()); - - for (auto sec : _layout.sections()) { - // TODO: use findOutputSection - auto section = dyn_cast<Section<ELFT>>(sec); - if (!section || section->outputSectionName() != ".eh_frame") - continue; - _ehFrameHeader.reset(new (_alloc) EHFrameHeader<ELFT>( - _context, ".eh_frame_hdr", _layout, - DefaultLayout<ELFT>::ORDER_EH_FRAMEHDR)); - _layout.addSection(_ehFrameHeader.get()); - break; - } - - if (_context.isDynamic()) { - _dynamicTable = std::move(createDynamicTable()); - _dynamicStringTable.reset(new (_alloc) StringTable<ELFT>( - _context, ".dynstr", DefaultLayout<ELFT>::ORDER_DYNAMIC_STRINGS, true)); - _dynamicSymbolTable = std::move(createDynamicSymbolTable()); - _hashTable.reset(new (_alloc) HashSection<ELFT>( - _context, ".hash", DefaultLayout<ELFT>::ORDER_HASH)); - // Set the hash table in the dynamic symbol table so that the entries in the - // hash table can be created - _dynamicSymbolTable->setHashTable(_hashTable.get()); - _hashTable->setSymbolTable(_dynamicSymbolTable.get()); - _layout.addSection(_dynamicTable.get()); - _layout.addSection(_dynamicStringTable.get()); - _layout.addSection(_dynamicSymbolTable.get()); - _layout.addSection(_hashTable.get()); - _dynamicSymbolTable->setStringSection(_dynamicStringTable.get()); - _dynamicTable->setSymbolTable(_dynamicSymbolTable.get()); - _dynamicTable->setHashTable(_hashTable.get()); - if (_layout.hasDynamicRelocationTable()) - _layout.getDynamicRelocationTable()->setSymbolTable( - _dynamicSymbolTable.get()); - if (_layout.hasPLTRelocationTable()) - _layout.getPLTRelocationTable()->setSymbolTable( - _dynamicSymbolTable.get()); - } -} - -template <class ELFT> -unique_bump_ptr<SymbolTable<ELFT>> - OutputELFWriter<ELFT>::createSymbolTable() { - return unique_bump_ptr<SymbolTable<ELFT>>(new (_alloc) SymbolTable<ELFT>( - this->_context, ".symtab", DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE)); -} - -/// \brief create dynamic table -template <class ELFT> -unique_bump_ptr<DynamicTable<ELFT>> - OutputELFWriter<ELFT>::createDynamicTable() { - return unique_bump_ptr<DynamicTable<ELFT>>( - new (_alloc) DynamicTable<ELFT>( - this->_context, _layout, ".dynamic", DefaultLayout<ELFT>::ORDER_DYNAMIC)); -} - -/// \brief create dynamic symbol table -template <class ELFT> -unique_bump_ptr<DynamicSymbolTable<ELFT>> - OutputELFWriter<ELFT>::createDynamicSymbolTable() { - return unique_bump_ptr<DynamicSymbolTable<ELFT>>( - new (_alloc) DynamicSymbolTable<ELFT>( - this->_context, _layout, ".dynsym", - DefaultLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS)); -} - -template <class ELFT> -std::error_code OutputELFWriter<ELFT>::buildOutput(const File &file) { - ScopedTask buildTask(getDefaultDomain(), "ELF Writer buildOutput"); - buildChunks(file); - - // Create the default sections like the symbol table, string table, and the - // section string table - createDefaultSections(); - - // Set the Layout - _layout.assignSectionsToSegments(); - - // Create the dynamic table entries - if (_context.isDynamic()) { - _dynamicTable->createDefaultEntries(); - buildDynamicSymbolTable(file); - } - - // Call the preFlight callbacks to modify the sections and the atoms - // contained in them, in anyway the targets may want - _layout.doPreFlight(); - - _layout.assignVirtualAddress(); - - // Finalize the default value of symbols that the linker adds - finalizeDefaultAtomValues(); - - // Build the Atom To Address map for applying relocations - buildAtomToAddressMap(file); - - // Create symbol table and section string table - // Do it only if -s is not specified. - if (!_context.stripSymbols()) - buildStaticSymbolTable(file); - - // Finalize the layout by calling the finalize() functions - _layout.finalize(); - - // build Section Header table - buildSectionHeaderTable(); - - // assign Offsets and virtual addresses - // for sections with no segments - assignSectionsWithNoSegments(); - - if (_context.isDynamic()) - _dynamicTable->updateDynamicTable(); - - return std::error_code(); -} - -template <class ELFT> std::error_code OutputELFWriter<ELFT>::setELFHeader() { - _elfHeader->e_type(_context.getOutputELFType()); - _elfHeader->e_machine(_context.getOutputMachine()); - _elfHeader->e_ident(ELF::EI_VERSION, 1); - _elfHeader->e_ident(ELF::EI_OSABI, 0); - _elfHeader->e_version(1); - _elfHeader->e_phoff(_programHeader->fileOffset()); - _elfHeader->e_shoff(_shdrtab->fileOffset()); - _elfHeader->e_phentsize(_programHeader->entsize()); - _elfHeader->e_phnum(_programHeader->numHeaders()); - _elfHeader->e_shentsize(_shdrtab->entsize()); - _elfHeader->e_shnum(_shdrtab->numHeaders()); - _elfHeader->e_shstrndx(_shstrtab->ordinal()); - if (const auto *al = _layout.findAtomLayoutByName(_context.entrySymbolName())) - _elfHeader->e_entry(al->_virtualAddr); - else - _elfHeader->e_entry(0); - - return std::error_code(); -} - -template <class ELFT> uint64_t OutputELFWriter<ELFT>::outputFileSize() const { - return _shdrtab->fileOffset() + _shdrtab->fileSize(); -} - -template <class ELFT> -std::error_code OutputELFWriter<ELFT>::writeOutput(const File &file, - StringRef path) { - std::unique_ptr<FileOutputBuffer> buffer; - ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); - std::error_code ec = FileOutputBuffer::create(path, outputFileSize(), buffer, - FileOutputBuffer::F_executable); - createOutputTask.end(); - - if (ec) - return ec; - - ScopedTask writeTask(getDefaultDomain(), "ELF Writer write to memory"); - - // HACK: We have to write out the header and program header here even though - // they are a member of a segment because only sections are written in the - // following loop. - - // Finalize ELF Header / Program Headers. - _elfHeader->finalize(); - _programHeader->finalize(); - - _elfHeader->write(this, _layout, *buffer); - _programHeader->write(this, _layout, *buffer); - - auto sections = _layout.sections(); - parallel_for_each( - sections.begin(), sections.end(), - [&](Chunk<ELFT> *section) { section->write(this, _layout, *buffer); }); - writeTask.end(); - - ScopedTask commitTask(getDefaultDomain(), "ELF Writer commit to disk"); - return buffer->commit(); -} - -template <class ELFT> -std::error_code OutputELFWriter<ELFT>::writeFile(const File &file, - StringRef path) { - std::error_code ec = buildOutput(file); - if (ec) - return ec; - - ec = setELFHeader(); - if (ec) - return ec; - - return writeOutput(file, path); -} } // namespace elf } // namespace lld diff --git a/lib/ReaderWriter/ELF/Reader.cpp b/lib/ReaderWriter/ELF/Reader.cpp index fc113d478913..801f1abaed7a 100644 --- a/lib/ReaderWriter/ELF/Reader.cpp +++ b/lib/ReaderWriter/ELF/Reader.cpp @@ -29,15 +29,15 @@ namespace lld { void Registry::addSupportELFObjects(ELFLinkingContext &ctx) { // Tell registry about the ELF object file parser. - add(std::move(ctx.targetHandler()->getObjReader())); + add(ctx.getTargetHandler().getObjReader()); // Tell registry about the relocation name to number mapping for this arch. - ctx.targetHandler()->registerRelocationNames(*this); + ctx.registerRelocationNames(*this); } void Registry::addSupportELFDynamicSharedObjects(ELFLinkingContext &ctx) { // Tell registry about the ELF dynamic shared library file parser. - add(ctx.targetHandler()->getDSOReader()); + add(ctx.getTargetHandler().getDSOReader()); } } // end namespace lld diff --git a/lib/ReaderWriter/ELF/SectionChunks.cpp b/lib/ReaderWriter/ELF/SectionChunks.cpp new file mode 100644 index 000000000000..520fbe24af3b --- /dev/null +++ b/lib/ReaderWriter/ELF/SectionChunks.cpp @@ -0,0 +1,996 @@ +//===- lib/ReaderWriter/ELF/SectionChunks.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SectionChunks.h" +#include "TargetLayout.h" +#include "lld/Core/Parallel.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Dwarf.h" + +namespace lld { +namespace elf { + +template <class ELFT> +Section<ELFT>::Section(const ELFLinkingContext &ctx, StringRef sectionName, + StringRef chunkName, typename Chunk<ELFT>::Kind k) + : Chunk<ELFT>(chunkName, k, ctx), _inputSectionName(sectionName), + _outputSectionName(sectionName) {} + +template <class ELFT> int Section<ELFT>::getContentType() const { + if (_flags & llvm::ELF::SHF_EXECINSTR) + return Chunk<ELFT>::ContentType::Code; + else if (_flags & llvm::ELF::SHF_WRITE) + return Chunk<ELFT>::ContentType::Data; + else if (_flags & llvm::ELF::SHF_ALLOC) + return Chunk<ELFT>::ContentType::Code; + else + return Chunk<ELFT>::ContentType::Unknown; +} + +template <class ELFT> +AtomSection<ELFT>::AtomSection(const ELFLinkingContext &ctx, + StringRef sectionName, int32_t contentType, + int32_t permissions, int32_t order) + : Section<ELFT>(ctx, sectionName, "AtomSection", + Chunk<ELFT>::Kind::AtomSection), + _contentType(contentType), _contentPermissions(permissions) { + this->setOrder(order); + + switch (contentType) { + case DefinedAtom::typeCode: + case DefinedAtom::typeDataFast: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + case DefinedAtom::typeThreadData: + this->_type = SHT_PROGBITS; + break; + + case DefinedAtom::typeThreadZeroFill: + case DefinedAtom::typeZeroFillFast: + case DefinedAtom::typeZeroFill: + this->_type = SHT_NOBITS; + break; + + case DefinedAtom::typeRONote: + case DefinedAtom::typeRWNote: + this->_type = SHT_NOTE; + break; + + case DefinedAtom::typeNoAlloc: + this->_type = SHT_PROGBITS; + this->_isLoadedInMemory = false; + break; + } + + switch (permissions) { + case DefinedAtom::permR__: + this->_flags = SHF_ALLOC; + break; + case DefinedAtom::permR_X: + this->_flags = SHF_ALLOC | SHF_EXECINSTR; + break; + case DefinedAtom::permRW_: + case DefinedAtom::permRW_L: + this->_flags = SHF_ALLOC | SHF_WRITE; + if (_contentType == DefinedAtom::typeThreadData || + _contentType == DefinedAtom::typeThreadZeroFill) + this->_flags |= SHF_TLS; + break; + case DefinedAtom::permRWX: + this->_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; + break; + case DefinedAtom::perm___: + this->_flags = 0; + break; + } +} + +template <class ELFT> +void AtomSection<ELFT>::assignVirtualAddress(uint64_t addr) { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_virtualAddr = addr + ai->_fileOffset; + }); +} + +template <class ELFT> +void AtomSection<ELFT>::assignFileOffsets(uint64_t offset) { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_fileOffset = offset + ai->_fileOffset; + }); +} + +template <class ELFT> +const AtomLayout * +AtomSection<ELFT>::findAtomLayoutByName(StringRef name) const { + for (auto ai : _atoms) + if (ai->_atom->name() == name) + return ai; + return nullptr; +} + +template <class ELFT> +std::string AtomSection<ELFT>::formatError(const std::string &errorStr, + const AtomLayout &atom, + const Reference &ref) const { + StringRef kindValStr; + if (!this->_ctx.registry().referenceKindToString( + ref.kindNamespace(), ref.kindArch(), ref.kindValue(), kindValStr)) { + kindValStr = "unknown"; + } + + return + (Twine(errorStr) + " in file " + atom._atom->file().path() + + ": reference from " + atom._atom->name() + "+" + + Twine(ref.offsetInAtom()) + " to " + ref.target()->name() + "+" + + Twine(ref.addend()) + " of type " + Twine(ref.kindValue()) + " (" + + kindValStr + ")\n") + .str(); +} + +/// Align the offset to the required modulus defined by the atom alignment +template <class ELFT> +uint64_t AtomSection<ELFT>::alignOffset(uint64_t offset, + DefinedAtom::Alignment &atomAlign) { + uint64_t requiredModulus = atomAlign.modulus; + uint64_t alignment = atomAlign.value; + uint64_t currentModulus = (offset % alignment); + uint64_t retOffset = offset; + if (currentModulus != requiredModulus) { + if (requiredModulus > currentModulus) + retOffset += requiredModulus - currentModulus; + else + retOffset += alignment + requiredModulus - currentModulus; + } + return retOffset; +} + +// \brief Append an atom to a Section. The atom gets pushed into a vector +// contains the atom, the atom file offset, the atom virtual address +// the atom file offset is aligned appropriately as set by the Reader +template <class ELFT> +const AtomLayout *AtomSection<ELFT>::appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = atomAlign.value; + // Align the atom to the required modulus/ align the file offset and the + // memory offset separately this is required so that BSS symbols are handled + // properly as the BSS symbols only occupy memory size and not file size + uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = alignOffset(this->memSize(), atomAlign); + switch (definedAtom->contentType()) { + case DefinedAtom::typeCode: + case DefinedAtom::typeConstant: + case DefinedAtom::typeData: + case DefinedAtom::typeDataFast: + case DefinedAtom::typeZeroFillFast: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + case DefinedAtom::typeThreadData: + case DefinedAtom::typeRONote: + case DefinedAtom::typeRWNote: + _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"); + break; + case DefinedAtom::typeNoAlloc: + _atoms.push_back(new (_alloc) AtomLayout(atom, fOffset, 0)); + this->_fsize = fOffset + definedAtom->size(); + DEBUG_WITH_TYPE("Section", llvm::dbgs() + << "[" << this->name() << " " << this << "] " + << "Adding atom: " << atom->name() << "@" + << fOffset << "\n"); + break; + case DefinedAtom::typeThreadZeroFill: + case DefinedAtom::typeZeroFill: + _atoms.push_back(new (_alloc) AtomLayout(atom, mOffset, 0)); + this->_msize = mOffset + definedAtom->size(); + break; + default: + llvm::dbgs() << definedAtom->contentType() << "\n"; + llvm_unreachable("Uexpected content type."); + } + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (this->_alignment < alignment) + this->_alignment = alignment; + + if (_atoms.size()) + return _atoms.back(); + return nullptr; +} + +/// \brief convert the segment type to a String for diagnostics +/// and printing purposes +template <class ELFT> StringRef Section<ELFT>::segmentKindToStr() const { + switch (_segmentType) { + case llvm::ELF::PT_DYNAMIC: + return "DYNAMIC"; + case llvm::ELF::PT_INTERP: + return "INTERP"; + case llvm::ELF::PT_LOAD: + return "LOAD"; + case llvm::ELF::PT_GNU_EH_FRAME: + return "EH_FRAME"; + case llvm::ELF::PT_GNU_RELRO: + return "GNU_RELRO"; + case llvm::ELF::PT_NOTE: + return "NOTE"; + case llvm::ELF::PT_NULL: + return "NULL"; + case llvm::ELF::PT_TLS: + return "TLS"; + default: + return "UNKNOWN"; + } +} + +/// \brief Write the section and the atom contents to the buffer +template <class ELFT> +void AtomSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + bool success = true; + + // parallel_for_each() doesn't have deterministic order. To guarantee + // deterministic error output, collect errors in this vector and sort it + // by atom file offset before printing all errors. + std::vector<std::pair<size_t, std::string>> errors; + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + DEBUG_WITH_TYPE("Section", llvm::dbgs() + << "Writing atom: " << ai->_atom->name() + << " | " << ai->_fileOffset << "\n"); + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + if (!definedAtom->occupiesDiskSpace()) + return; + // Copy raw content of atom to file buffer. + ArrayRef<uint8_t> content = definedAtom->rawContent(); + uint64_t contentSize = content.size(); + if (contentSize == 0) + return; + uint8_t *atomContent = chunkBuffer + ai->_fileOffset; + std::memcpy(atomContent, content.data(), contentSize); + const TargetRelocationHandler &relHandler = + this->_ctx.getTargetHandler().getRelocationHandler(); + for (const auto ref : *definedAtom) { + if (std::error_code ec = + relHandler.applyRelocation(*writer, buffer, *ai, *ref)) { + std::lock_guard<std::mutex> lock(_outputMutex); + errors.push_back(std::make_pair(ai->_fileOffset, + formatError(ec.message(), *ai, *ref))); + success = false; + } + } + }); + if (!success) { + std::sort(errors.begin(), errors.end()); + for (auto &&error : errors) + llvm::errs() << error.second; + llvm::report_fatal_error("relocating output"); + } +} + +template <class ELFT> +void OutputSection<ELFT>::appendSection(Section<ELFT> *section) { + if (section->alignment() > _alignment) + _alignment = section->alignment(); + assert(!_link && "Section already has a link!"); + _link = section->getLink(); + _shInfo = section->getInfo(); + _entSize = section->getEntSize(); + _type = section->getType(); + if (_flags < section->getFlags()) + _flags = section->getFlags(); + section->setOutputSection(this, (_sections.size() == 0)); + _kind = section->kind(); + _sections.push_back(section); +} + +template <class ELFT> +StringTable<ELFT>::StringTable(const ELFLinkingContext &ctx, const char *str, + int32_t order, bool dynamic) + : Section<ELFT>(ctx, str, "StringTable") { + // the string table has a NULL entry for which + // add an empty string + _strings.push_back(""); + this->_fsize = 1; + this->_alignment = 1; + this->setOrder(order); + this->_type = SHT_STRTAB; + if (dynamic) { + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; + } +} + +template <class ELFT> uint64_t StringTable<ELFT>::addString(StringRef symname) { + if (symname.empty()) + return 0; + StringMapTIter stringIter = _stringMap.find(symname); + if (stringIter == _stringMap.end()) { + _strings.push_back(symname); + uint64_t offset = this->_fsize; + this->_fsize += symname.size() + 1; + if (this->_flags & SHF_ALLOC) + this->_msize = this->_fsize; + _stringMap[symname] = offset; + return offset; + } + return stringIter->second; +} + +template <class ELFT> +void StringTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto si : _strings) { + memcpy(dest, si.data(), si.size()); + dest += si.size(); + memcpy(dest, "", 1); + dest += 1; + } +} + +/// ELF Symbol Table +template <class ELFT> +SymbolTable<ELFT>::SymbolTable(const ELFLinkingContext &ctx, const char *str, + int32_t order) + : Section<ELFT>(ctx, str, "SymbolTable") { + this->setOrder(order); + Elf_Sym symbol; + std::memset(&symbol, 0, sizeof(Elf_Sym)); + _symbolTable.push_back(SymbolEntry(nullptr, symbol, nullptr)); + this->_entSize = sizeof(Elf_Sym); + this->_fsize = sizeof(Elf_Sym); + this->_alignment = sizeof(Elf_Addr); + this->_type = SHT_SYMTAB; +} + +template <class ELFT> +void SymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + unsigned char binding = 0, type = 0; + sym.st_size = da->size(); + DefinedAtom::ContentType ct; + switch (ct = da->contentType()) { + case DefinedAtom::typeCode: + case DefinedAtom::typeStub: + sym.st_value = addr; + type = llvm::ELF::STT_FUNC; + break; + case DefinedAtom::typeResolver: + sym.st_value = addr; + type = llvm::ELF::STT_GNU_IFUNC; + break; + case DefinedAtom::typeDataFast: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + sym.st_value = addr; + type = llvm::ELF::STT_OBJECT; + break; + case DefinedAtom::typeGOT: + sym.st_value = addr; + type = llvm::ELF::STT_NOTYPE; + break; + case DefinedAtom::typeZeroFill: + case DefinedAtom::typeZeroFillFast: + type = llvm::ELF::STT_OBJECT; + sym.st_value = addr; + break; + case DefinedAtom::typeThreadData: + case DefinedAtom::typeThreadZeroFill: + type = llvm::ELF::STT_TLS; + sym.st_value = addr; + break; + default: + type = llvm::ELF::STT_NOTYPE; + } + if (da->customSectionName() == da->name()) + type = llvm::ELF::STT_SECTION; + + if (da->scope() == DefinedAtom::scopeTranslationUnit) + binding = llvm::ELF::STB_LOCAL; + else + binding = llvm::ELF::STB_GLOBAL; + + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, + int64_t addr) { + unsigned char binding = 0, type = 0; + type = llvm::ELF::STT_OBJECT; + sym.st_shndx = llvm::ELF::SHN_ABS; + switch (aa->scope()) { + case AbsoluteAtom::scopeLinkageUnit: + sym.setVisibility(llvm::ELF::STV_HIDDEN); + binding = llvm::ELF::STB_LOCAL; + break; + case AbsoluteAtom::scopeTranslationUnit: + binding = llvm::ELF::STB_LOCAL; + break; + case AbsoluteAtom::scopeGlobal: + binding = llvm::ELF::STB_GLOBAL; + break; + } + sym.st_value = addr; + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addSharedLibAtom(Elf_Sym &sym, + const SharedLibraryAtom *aa) { + unsigned char binding = 0, type = 0; + if (aa->type() == SharedLibraryAtom::Type::Data) { + type = llvm::ELF::STT_OBJECT; + sym.st_size = aa->size(); + } else + type = llvm::ELF::STT_FUNC; + sym.st_shndx = llvm::ELF::SHN_UNDEF; + binding = llvm::ELF::STB_GLOBAL; + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addUndefinedAtom(Elf_Sym &sym, + const UndefinedAtom *ua) { + unsigned char binding = 0, type = 0; + sym.st_value = 0; + type = llvm::ELF::STT_NOTYPE; + if (ua->canBeNull()) + binding = llvm::ELF::STB_WEAK; + else + binding = llvm::ELF::STB_GLOBAL; + sym.setBindingAndType(binding, type); +} + +/// Add a symbol to the symbol Table, definedAtoms which get added to the symbol +/// section don't have their virtual addresses set at the time of adding the +/// symbol to the symbol table(Example: dynamic symbols), the addresses needs +/// to be updated in the table before writing the dynamic symbol table +/// information +template <class ELFT> +void SymbolTable<ELFT>::addSymbol(const Atom *atom, int32_t sectionIndex, + uint64_t addr, const AtomLayout *atomLayout) { + Elf_Sym symbol; + + if (atom->name().empty()) + return; + + symbol.st_name = _stringSection->addString(atom->name()); + symbol.st_size = 0; + symbol.st_shndx = sectionIndex; + symbol.st_value = 0; + symbol.st_other = 0; + symbol.setVisibility(llvm::ELF::STV_DEFAULT); + + // Add all the atoms + if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom)) + addDefinedAtom(symbol, da, addr); + else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(atom)) + addAbsoluteAtom(symbol, aa, addr); + else if (isa<const SharedLibraryAtom>(atom)) + addSharedLibAtom(symbol, dyn_cast<SharedLibraryAtom>(atom)); + else + addUndefinedAtom(symbol, dyn_cast<UndefinedAtom>(atom)); + + // If --discard-all is on, don't add to the symbol table + // symbols with local binding. + if (this->_ctx.discardLocals() && symbol.getBinding() == llvm::ELF::STB_LOCAL) + return; + + // Temporary locals are all the symbols which name starts with .L. + // This is defined by the ELF standard. + if (this->_ctx.discardTempLocals() && atom->name().startswith(".L")) + return; + + _symbolTable.push_back(SymbolEntry(atom, symbol, atomLayout)); + this->_fsize += sizeof(Elf_Sym); + if (this->_flags & SHF_ALLOC) + this->_msize = this->_fsize; +} + +template <class ELFT> void SymbolTable<ELFT>::finalize(bool sort) { + // sh_info should be one greater than last symbol with STB_LOCAL binding + // we sort the symbol table to keep all local symbols at the beginning + if (sort) + sortSymbols(); + + uint16_t shInfo = 0; + for (const auto &i : _symbolTable) { + if (i._symbol.getBinding() != llvm::ELF::STB_LOCAL) + break; + shInfo++; + } + this->_info = shInfo; + this->_link = _stringSection->ordinal(); + if (this->_outputSection) { + this->_outputSection->setInfo(this->_info); + this->_outputSection->setLink(this->_link); + } +} + +template <class ELFT> +void SymbolTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &sti : _symbolTable) { + memcpy(dest, &sti._symbol, sizeof(Elf_Sym)); + dest += sizeof(Elf_Sym); + } +} + +template <class ELFT> +DynamicSymbolTable<ELFT>::DynamicSymbolTable(const ELFLinkingContext &ctx, + TargetLayout<ELFT> &layout, + const char *str, int32_t order) + : SymbolTable<ELFT>(ctx, str, order), _layout(layout) { + this->_type = SHT_DYNSYM; + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; +} + +template <class ELFT> void DynamicSymbolTable<ELFT>::addSymbolsToHashTable() { + int index = 0; + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + _hashTable->addSymbol("", index); + else + _hashTable->addSymbol(ste._atom->name(), index); + ++index; + } +} + +template <class ELFT> void DynamicSymbolTable<ELFT>::finalize() { + // Defined symbols which have been added into the dynamic symbol table + // don't have their addresses known until addresses have been assigned + // so let's update the symbol values after they have got assigned + for (auto &ste : this->_symbolTable) { + const AtomLayout *atomLayout = ste._atomLayout; + if (!atomLayout) + continue; + ste._symbol.st_value = atomLayout->_virtualAddr; + } + + // Don't sort the symbols + SymbolTable<ELFT>::finalize(false); +} + +template <class ELFT> +RelocationTable<ELFT>::RelocationTable(const ELFLinkingContext &ctx, + StringRef str, int32_t order) + : Section<ELFT>(ctx, str, "RelocationTable") { + this->setOrder(order); + this->_flags = SHF_ALLOC; + // Set the alignment properly depending on the target architecture + this->_alignment = ELFT::Is64Bits ? 8 : 4; + if (ctx.isRelaOutputFormat()) { + this->_entSize = sizeof(Elf_Rela); + this->_type = SHT_RELA; + } else { + this->_entSize = sizeof(Elf_Rel); + this->_type = SHT_REL; + } +} + +template <class ELFT> +uint32_t RelocationTable<ELFT>::addRelocation(const DefinedAtom &da, + const Reference &r) { + _relocs.emplace_back(&da, &r); + this->_fsize = _relocs.size() * this->_entSize; + this->_msize = this->_fsize; + return _relocs.size() - 1; +} + +template <class ELFT> +bool RelocationTable<ELFT>::getRelocationIndex(const Reference &r, + uint32_t &res) { + auto rel = std::find_if( + _relocs.begin(), _relocs.end(), + [&](const std::pair<const DefinedAtom *, const Reference *> &p) { + if (p.second == &r) + return true; + return false; + }); + if (rel == _relocs.end()) + return false; + res = std::distance(_relocs.begin(), rel); + return true; +} + +template <class ELFT> +bool RelocationTable<ELFT>::canModifyReadonlySection() const { + for (const auto &rel : _relocs) { + const DefinedAtom *atom = rel.first; + if ((atom->permissions() & DefinedAtom::permRW_) != DefinedAtom::permRW_) + return true; + } + return false; +} + +template <class ELFT> void RelocationTable<ELFT>::finalize() { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); +} + +template <class ELFT> +void RelocationTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &rel : _relocs) { + if (this->_ctx.isRelaOutputFormat()) { + auto &r = *reinterpret_cast<Elf_Rela *>(dest); + writeRela(writer, r, *rel.first, *rel.second); + DEBUG_WITH_TYPE("ELFRelocationTable", + llvm::dbgs() + << rel.second->kindValue() << " relocation at " + << rel.first->name() << "@" << r.r_offset << " to " + << rel.second->target()->name() << "@" << r.r_addend + << "\n";); + } else { + auto &r = *reinterpret_cast<Elf_Rel *>(dest); + writeRel(writer, r, *rel.first, *rel.second); + DEBUG_WITH_TYPE("ELFRelocationTable", + llvm::dbgs() << rel.second->kindValue() + << " relocation at " << rel.first->name() + << "@" << r.r_offset << " to " + << rel.second->target()->name() << "\n";); + } + dest += this->_entSize; + } +} + +template <class ELFT> +void RelocationTable<ELFT>::writeRela(ELFWriter *writer, Elf_Rela &r, + const DefinedAtom &atom, + const Reference &ref) { + r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_ctx.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; +} + +template <class ELFT> +void RelocationTable<ELFT>::writeRel(ELFWriter *writer, Elf_Rel &r, + const DefinedAtom &atom, + const Reference &ref) { + r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); +} + +template <class ELFT> +uint32_t RelocationTable<ELFT>::getSymbolIndex(const Atom *a) { + return _symbolTable ? _symbolTable->getSymbolTableIndex(a) + : (uint32_t)STN_UNDEF; +} + +template <class ELFT> +DynamicTable<ELFT>::DynamicTable(const ELFLinkingContext &ctx, + TargetLayout<ELFT> &layout, StringRef str, + int32_t order) + : Section<ELFT>(ctx, str, "DynamicSection"), _layout(layout) { + this->setOrder(order); + this->_entSize = sizeof(Elf_Dyn); + this->_alignment = ELFT::Is64Bits ? 8 : 4; + // Reserve space for the DT_NULL entry. + this->_fsize = sizeof(Elf_Dyn); + this->_msize = sizeof(Elf_Dyn); + this->_type = SHT_DYNAMIC; + this->_flags = SHF_ALLOC; +} + +template <class ELFT> +std::size_t DynamicTable<ELFT>::addEntry(int64_t tag, uint64_t val) { + Elf_Dyn dyn; + dyn.d_tag = tag; + dyn.d_un.d_val = val; + _entries.push_back(dyn); + this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn); + this->_msize = this->_fsize; + return _entries.size() - 1; +} + +template <class ELFT> +void DynamicTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + // Add the null entry. + Elf_Dyn d; + d.d_tag = 0; + d.d_un.d_val = 0; + _entries.push_back(d); + std::memcpy(dest, _entries.data(), this->_fsize); +} + +template <class ELFT> void DynamicTable<ELFT>::createDefaultEntries() { + bool isRela = this->_ctx.isRelaOutputFormat(); + _dt_hash = addEntry(DT_HASH, 0); + _dt_strtab = addEntry(DT_STRTAB, 0); + _dt_symtab = addEntry(DT_SYMTAB, 0); + _dt_strsz = addEntry(DT_STRSZ, 0); + _dt_syment = addEntry(DT_SYMENT, 0); + if (_layout.hasDynamicRelocationTable()) { + _dt_rela = addEntry(isRela ? DT_RELA : DT_REL, 0); + _dt_relasz = addEntry(isRela ? DT_RELASZ : DT_RELSZ, 0); + _dt_relaent = addEntry(isRela ? DT_RELAENT : DT_RELENT, 0); + if (_layout.getDynamicRelocationTable()->canModifyReadonlySection()) + _dt_textrel = addEntry(DT_TEXTREL, 0); + } + if (_layout.hasPLTRelocationTable()) { + _dt_pltrelsz = addEntry(DT_PLTRELSZ, 0); + _dt_pltgot = addEntry(getGotPltTag(), 0); + _dt_pltrel = addEntry(DT_PLTREL, isRela ? DT_RELA : DT_REL); + _dt_jmprel = addEntry(DT_JMPREL, 0); + } +} + +template <class ELFT> void DynamicTable<ELFT>::doPreFlight() { + auto initArray = _layout.findOutputSection(".init_array"); + auto finiArray = _layout.findOutputSection(".fini_array"); + if (initArray) { + _dt_init_array = addEntry(DT_INIT_ARRAY, 0); + _dt_init_arraysz = addEntry(DT_INIT_ARRAYSZ, 0); + } + if (finiArray) { + _dt_fini_array = addEntry(DT_FINI_ARRAY, 0); + _dt_fini_arraysz = addEntry(DT_FINI_ARRAYSZ, 0); + } + if (getInitAtomLayout()) + _dt_init = addEntry(DT_INIT, 0); + if (getFiniAtomLayout()) + _dt_fini = addEntry(DT_FINI, 0); +} + +template <class ELFT> void DynamicTable<ELFT>::finalize() { + StringTable<ELFT> *dynamicStringTable = _dynamicSymbolTable->getStringTable(); + this->_link = dynamicStringTable->ordinal(); + if (this->_outputSection) { + this->_outputSection->setType(this->_type); + this->_outputSection->setInfo(this->_info); + this->_outputSection->setLink(this->_link); + } +} + +template <class ELFT> void DynamicTable<ELFT>::updateDynamicTable() { + StringTable<ELFT> *dynamicStringTable = _dynamicSymbolTable->getStringTable(); + _entries[_dt_hash].d_un.d_val = _hashTable->virtualAddr(); + _entries[_dt_strtab].d_un.d_val = dynamicStringTable->virtualAddr(); + _entries[_dt_symtab].d_un.d_val = _dynamicSymbolTable->virtualAddr(); + _entries[_dt_strsz].d_un.d_val = dynamicStringTable->memSize(); + _entries[_dt_syment].d_un.d_val = _dynamicSymbolTable->getEntSize(); + auto initArray = _layout.findOutputSection(".init_array"); + if (initArray) { + _entries[_dt_init_array].d_un.d_val = initArray->virtualAddr(); + _entries[_dt_init_arraysz].d_un.d_val = initArray->memSize(); + } + auto finiArray = _layout.findOutputSection(".fini_array"); + if (finiArray) { + _entries[_dt_fini_array].d_un.d_val = finiArray->virtualAddr(); + _entries[_dt_fini_arraysz].d_un.d_val = finiArray->memSize(); + } + if (const auto *al = getInitAtomLayout()) + _entries[_dt_init].d_un.d_val = getAtomVirtualAddress(al); + if (const auto *al = getFiniAtomLayout()) + _entries[_dt_fini].d_un.d_val = getAtomVirtualAddress(al); + if (_layout.hasDynamicRelocationTable()) { + auto relaTbl = _layout.getDynamicRelocationTable(); + _entries[_dt_rela].d_un.d_val = relaTbl->virtualAddr(); + _entries[_dt_relasz].d_un.d_val = relaTbl->memSize(); + _entries[_dt_relaent].d_un.d_val = relaTbl->getEntSize(); + } + if (_layout.hasPLTRelocationTable()) { + auto relaTbl = _layout.getPLTRelocationTable(); + _entries[_dt_jmprel].d_un.d_val = relaTbl->virtualAddr(); + _entries[_dt_pltrelsz].d_un.d_val = relaTbl->memSize(); + auto gotplt = _layout.findOutputSection(".got.plt"); + _entries[_dt_pltgot].d_un.d_val = gotplt->virtualAddr(); + } +} + +template <class ELFT> +const AtomLayout *DynamicTable<ELFT>::getInitAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_ctx.initFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; +} + +template <class ELFT> +const AtomLayout *DynamicTable<ELFT>::getFiniAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_ctx.finiFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; +} + +template <class ELFT> +InterpSection<ELFT>::InterpSection(const ELFLinkingContext &ctx, StringRef str, + int32_t order, StringRef interp) + : Section<ELFT>(ctx, str, "Dynamic:Interp"), _interp(interp) { + this->setOrder(order); + this->_alignment = 1; + // + 1 for null term. + this->_fsize = interp.size() + 1; + this->_msize = this->_fsize; + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC; +} + +template <class ELFT> +void InterpSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + std::memcpy(dest, _interp.data(), _interp.size()); +} + +template <class ELFT> +HashSection<ELFT>::HashSection(const ELFLinkingContext &ctx, StringRef name, + int32_t order) + : Section<ELFT>(ctx, name, "Dynamic:Hash") { + this->setOrder(order); + this->_entSize = 4; + this->_type = SHT_HASH; + this->_flags = SHF_ALLOC; + this->_alignment = ELFT::Is64Bits ? 8 : 4; + this->_fsize = 0; + this->_msize = 0; +} + +template <class ELFT> +void HashSection<ELFT>::addSymbol(StringRef name, uint32_t index) { + SymbolTableEntry ste; + ste._name = name; + ste._index = index; + _entries.push_back(ste); +} + +/// \brief Set the dynamic symbol table +template <class ELFT> +void HashSection<ELFT>::setSymbolTable( + const DynamicSymbolTable<ELFT> *symbolTable) { + _symbolTable = symbolTable; +} + +template <class ELFT> void HashSection<ELFT>::doPreFlight() { + // The number of buckets to use for a certain number of symbols. + // If there are less than 3 symbols, 1 bucket will be used. If + // there are less than 17 symbols, 3 buckets will be used, and so + // forth. The bucket numbers are defined by GNU ld. We use the + // same rules here so we generate hash sections with the same + // size as those generated by GNU ld. + uint32_t hashBuckets[] = {1, 3, 17, 37, 67, 97, 131, + 197, 263, 521, 1031, 2053, 4099, 8209, + 16411, 32771, 65537, 131101, 262147}; + int hashBucketsCount = sizeof(hashBuckets) / sizeof(uint32_t); + + unsigned int bucketsCount = 0; + unsigned int dynSymCount = _entries.size(); + + // Get the number of buckes that we want to use + for (int i = 0; i < hashBucketsCount; ++i) { + if (dynSymCount < hashBuckets[i]) + break; + bucketsCount = hashBuckets[i]; + } + _buckets.resize(bucketsCount); + _chains.resize(_entries.size()); + + // Create the hash table for the dynamic linker + for (auto ai : _entries) { + unsigned int dynsymIndex = ai._index; + unsigned int bucketpos = llvm::object::elf_hash(ai._name) % bucketsCount; + _chains[dynsymIndex] = _buckets[bucketpos]; + _buckets[bucketpos] = dynsymIndex; + } + + this->_fsize = (2 + _chains.size() + _buckets.size()) * sizeof(uint32_t); + this->_msize = this->_fsize; +} + +template <class ELFT> void HashSection<ELFT>::finalize() { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); +} + +template <class ELFT> +void HashSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + Elf_Word bucketChainCounts[2]; + bucketChainCounts[0] = _buckets.size(); + bucketChainCounts[1] = _chains.size(); + std::memcpy(dest, bucketChainCounts, sizeof(bucketChainCounts)); + dest += sizeof(bucketChainCounts); + // write bucket values + std::memcpy(dest, _buckets.data(), _buckets.size() * sizeof(Elf_Word)); + dest += _buckets.size() * sizeof(Elf_Word); + // write chain values + std::memcpy(dest, _chains.data(), _chains.size() * sizeof(Elf_Word)); +} + +template <class ELFT> +EHFrameHeader<ELFT>::EHFrameHeader(const ELFLinkingContext &ctx, StringRef name, + TargetLayout<ELFT> &layout, int32_t order) + : Section<ELFT>(ctx, name, "EHFrameHeader"), _layout(layout) { + this->setOrder(order); + this->_entSize = 0; + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC; + this->_alignment = ELFT::Is64Bits ? 8 : 4; + // Minimum size for empty .eh_frame_hdr. + this->_fsize = 1 + 1 + 1 + 1 + 4; + this->_msize = this->_fsize; +} + +template <class ELFT> void EHFrameHeader<ELFT>::doPreFlight() { + // TODO: Generate a proper binary search table. +} + +template <class ELFT> void EHFrameHeader<ELFT>::finalize() { + OutputSection<ELFT> *s = _layout.findOutputSection(".eh_frame"); + OutputSection<ELFT> *h = _layout.findOutputSection(".eh_frame_hdr"); + if (s && h) + _ehFrameOffset = s->virtualAddr() - (h->virtualAddr() + 4); +} + +template <class ELFT> +void EHFrameHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + int pos = 0; + dest[pos++] = 1; // version + dest[pos++] = llvm::dwarf::DW_EH_PE_pcrel | + llvm::dwarf::DW_EH_PE_sdata4; // eh_frame_ptr_enc + dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // fde_count_enc + dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // table_enc + *reinterpret_cast<typename llvm::object::ELFFile<ELFT>::Elf_Sword *>( + dest + pos) = _ehFrameOffset; +} + +#define INSTANTIATE(klass) \ + template class klass<ELF32LE>; \ + template class klass<ELF32BE>; \ + template class klass<ELF64LE>; \ + template class klass<ELF64BE> + +INSTANTIATE(AtomSection); +INSTANTIATE(DynamicSymbolTable); +INSTANTIATE(DynamicTable); +INSTANTIATE(EHFrameHeader); +INSTANTIATE(HashSection); +INSTANTIATE(InterpSection); +INSTANTIATE(OutputSection); +INSTANTIATE(RelocationTable); +INSTANTIATE(Section); +INSTANTIATE(StringTable); +INSTANTIATE(SymbolTable); + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/SectionChunks.h b/lib/ReaderWriter/ELF/SectionChunks.h index 03bdb59e6568..b10ba05237ff 100644 --- a/lib/ReaderWriter/ELF/SectionChunks.h +++ b/lib/ReaderWriter/ELF/SectionChunks.h @@ -11,19 +11,16 @@ #define LLD_READER_WRITER_ELF_SECTION_CHUNKS_H #include "Chunk.h" -#include "Layout.h" #include "TargetHandler.h" #include "Writer.h" #include "lld/Core/DefinedAtom.h" -#include "lld/Core/Parallel.h" #include "lld/Core/range.h" -#include "llvm/ADT/ArrayRef.h" +#include "lld/ReaderWriter/AtomLayout.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Dwarf.h" #include "llvm/Support/ELF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" @@ -35,29 +32,22 @@ namespace elf { template <class> class OutputSection; using namespace llvm::ELF; template <class ELFT> class Segment; +template <class ELFT> class TargetLayout; /// \brief An ELF section. template <class ELFT> class Section : public Chunk<ELFT> { public: - Section(const ELFLinkingContext &context, StringRef sectionName, + Section(const ELFLinkingContext &ctx, StringRef sectionName, StringRef chunkName, - typename Chunk<ELFT>::Kind k = Chunk<ELFT>::Kind::ELFSection) - : Chunk<ELFT>(chunkName, k, context), _outputSection(nullptr), _flags(0), - _entSize(0), _type(0), _link(0), _info(0), - _isFirstSectionInOutputSection(false), _segmentType(SHT_NULL), - _inputSectionName(sectionName), _outputSectionName(sectionName) {} + typename Chunk<ELFT>::Kind k = Chunk<ELFT>::Kind::ELFSection); /// \brief Modify the section contents before assigning virtual addresses // or assigning file offsets - void doPreFlight() override {} /// \brief Finalize the section contents before writing - void finalize() override {} /// \brief Does this section have an output segment. - virtual bool hasOutputSegment() { - return false; - } + virtual bool hasOutputSegment() const { return false; } /// Return if the section is a loadable section that occupies memory virtual bool isLoadableSection() const { return false; } @@ -73,26 +63,21 @@ public: uint32_t getType() const { return _type; } uint32_t getLink() const { return _link; } uint32_t getInfo() const { return _info; } - Layout::SegmentType getSegmentType() const { return _segmentType; } - /// \brief Return the type of content that the section contains - virtual int getContentType() const override { - if (_flags & llvm::ELF::SHF_EXECINSTR) - return Chunk<ELFT>::ContentType::Code; - else if (_flags & llvm::ELF::SHF_WRITE) - return Chunk<ELFT>::ContentType::Data; - else if (_flags & llvm::ELF::SHF_ALLOC) - return Chunk<ELFT>::ContentType::Code; - else - return Chunk<ELFT>::ContentType::Unknown; + typename TargetLayout<ELFT>::SegmentType getSegmentType() const { + return _segmentType; } + /// \brief Return the type of content that the section contains + int getContentType() const override; + /// \brief convert the segment type to a String for diagnostics and printing /// purposes - StringRef segmentKindToStr() const; + virtual StringRef segmentKindToStr() const; /// \brief Records the segmentType, that this section belongs to - void setSegmentType(const Layout::SegmentType segmentType) { + void + setSegmentType(const typename TargetLayout<ELFT>::SegmentType segmentType) { this->_segmentType = segmentType; } @@ -100,6 +85,10 @@ public: return nullptr; } + const OutputSection<ELFT> *getOutputSection() const { + return _outputSection; + } + void setOutputSection(OutputSection<ELFT> *os, bool isFirst = false) { _outputSection = os; _isFirstSectionInOutputSection = isFirst; @@ -133,21 +122,21 @@ public: protected: /// \brief OutputSection this Section is a member of, or nullptr. - OutputSection<ELFT> *_outputSection; + OutputSection<ELFT> *_outputSection = nullptr; /// \brief ELF SHF_* flags. - uint64_t _flags; + uint64_t _flags = 0; /// \brief The size of each entity. - uint64_t _entSize; + uint64_t _entSize = 0; /// \brief ELF SHT_* type. - uint32_t _type; + uint32_t _type = 0; /// \brief sh_link field. - uint32_t _link; + uint32_t _link = 0; /// \brief the sh_info field. - uint32_t _info; + uint32_t _info = 0; /// \brief Is this the first section in the output section. - bool _isFirstSectionInOutputSection; + bool _isFirstSectionInOutputSection = false; /// \brief the output ELF segment type of this section. - Layout::SegmentType _segmentType; + typename TargetLayout<ELFT>::SegmentType _segmentType = SHT_NULL; /// \brief Input section name. StringRef _inputSectionName; /// \brief Output section name. @@ -159,65 +148,8 @@ protected: /// \brief A section containing atoms. template <class ELFT> class AtomSection : public Section<ELFT> { public: - AtomSection(const ELFLinkingContext &context, StringRef sectionName, - int32_t contentType, int32_t permissions, int32_t order) - : Section<ELFT>(context, sectionName, "AtomSection", - Chunk<ELFT>::Kind::AtomSection), - _contentType(contentType), _contentPermissions(permissions), - _isLoadedInMemory(true) { - this->setOrder(order); - - switch (contentType) { - case DefinedAtom::typeCode: - case DefinedAtom::typeDataFast: - case DefinedAtom::typeData: - case DefinedAtom::typeConstant: - case DefinedAtom::typeGOT: - case DefinedAtom::typeStub: - case DefinedAtom::typeResolver: - case DefinedAtom::typeThreadData: - this->_type = SHT_PROGBITS; - break; - - case DefinedAtom::typeThreadZeroFill: - case DefinedAtom::typeZeroFillFast: - case DefinedAtom::typeZeroFill: - this->_type = SHT_NOBITS; - break; - - case DefinedAtom::typeRONote: - case DefinedAtom::typeRWNote: - this->_type = SHT_NOTE; - break; - - case DefinedAtom::typeNoAlloc: - this->_type = SHT_PROGBITS; - this->_isLoadedInMemory = false; - break; - } - - switch (permissions) { - case DefinedAtom::permR__: - this->_flags = SHF_ALLOC; - break; - case DefinedAtom::permR_X: - this->_flags = SHF_ALLOC | SHF_EXECINSTR; - break; - case DefinedAtom::permRW_: - case DefinedAtom::permRW_L: - this->_flags = SHF_ALLOC | SHF_WRITE; - if (_contentType == DefinedAtom::typeThreadData || - _contentType == DefinedAtom::typeThreadZeroFill) - this->_flags |= SHF_TLS; - break; - case DefinedAtom::permRWX: - this->_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; - break; - case DefinedAtom::perm___: - this->_flags = 0; - break; - } - } + AtomSection(const ELFLinkingContext &ctx, StringRef sectionName, + int32_t contentType, int32_t permissions, int32_t order); /// Align the offset to the required modulus defined by the atom alignment uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign); @@ -228,40 +160,27 @@ public: // \brief Append an atom to a Section. The atom gets pushed into a vector // contains the atom, the atom file offset, the atom virtual address // the atom file offset is aligned appropriately as set by the Reader - virtual const lld::AtomLayout *appendAtom(const Atom *atom); + virtual const AtomLayout *appendAtom(const Atom *atom); /// \brief Set the virtual address of each Atom in the Section. This /// routine gets called after the linker fixes up the virtual address /// of the section - virtual void assignVirtualAddress(uint64_t addr) override { - parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { - ai->_virtualAddr = addr + ai->_fileOffset; - }); - } + void assignVirtualAddress(uint64_t addr) override; /// \brief Set the file offset of each Atom in the section. This routine /// gets called after the linker fixes up the section offset - void assignFileOffsets(uint64_t offset) override { - parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { - ai->_fileOffset = offset + ai->_fileOffset; - }); - } + void assignFileOffsets(uint64_t offset) override; /// \brief Find the Atom address given a name, this is needed to properly /// apply relocation. The section class calls this to find the atom address /// to fix the relocation - const AtomLayout *findAtomLayoutByName(StringRef name) const override { - for (auto ai : _atoms) - if (ai->_atom->name() == name) - return ai; - return nullptr; - } + const AtomLayout *findAtomLayoutByName(StringRef name) const override; /// \brief Return the raw flags, we need this to sort segments int64_t atomflags() const { return _contentPermissions; } /// Atom Iterators - typedef typename std::vector<lld::AtomLayout *>::iterator atom_iter; + typedef typename std::vector<AtomLayout *>::iterator atom_iter; range<atom_iter> atoms() { return _atoms; } @@ -276,185 +195,27 @@ protected: llvm::BumpPtrAllocator _alloc; int32_t _contentType; int32_t _contentPermissions; - bool _isLoadedInMemory; - std::vector<lld::AtomLayout *> _atoms; + bool _isLoadedInMemory = true; + std::vector<AtomLayout *> _atoms; mutable std::mutex _outputMutex; - void printError(const std::string &errorStr, const AtomLayout &atom, - const Reference &ref) const { - StringRef kindValStr; - if (!this->_context.registry().referenceKindToString(ref.kindNamespace(), - ref.kindArch(), - ref.kindValue(), - kindValStr)) { - kindValStr = "unknown"; - } - - std::string errStr = (Twine(errorStr) + " in file " + - atom._atom->file().path() + - ": reference from " + atom._atom->name() + - "+" + Twine(ref.offsetInAtom()) + - " to " + ref.target()->name() + - "+" + Twine(ref.addend()) + - " of type " + Twine(ref.kindValue()) + - " (" + kindValStr + ")\n").str(); - - // Take the lock to prevent output getting interleaved between threads - std::lock_guard<std::mutex> lock(_outputMutex); - llvm::errs() << errStr; - } + std::string formatError(const std::string &errorStr, const AtomLayout &atom, + const Reference &ref) const; }; -/// Align the offset to the required modulus defined by the atom alignment -template <class ELFT> -uint64_t AtomSection<ELFT>::alignOffset(uint64_t offset, - DefinedAtom::Alignment &atomAlign) { - uint64_t requiredModulus = atomAlign.modulus; - uint64_t alignment = 1u << atomAlign.powerOf2; - uint64_t currentModulus = (offset % alignment); - uint64_t retOffset = offset; - if (currentModulus != requiredModulus) { - if (requiredModulus > currentModulus) - retOffset += requiredModulus - currentModulus; - else - retOffset += alignment + requiredModulus - currentModulus; - } - return retOffset; -} - -// \brief Append an atom to a Section. The atom gets pushed into a vector -// contains the atom, the atom file offset, the atom virtual address -// the atom file offset is aligned appropriately as set by the Reader -template <class ELFT> -const lld::AtomLayout *AtomSection<ELFT>::appendAtom(const Atom *atom) { - const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); - - DefinedAtom::Alignment atomAlign = definedAtom->alignment(); - uint64_t alignment = 1u << atomAlign.powerOf2; - // Align the atom to the required modulus/ align the file offset and the - // memory offset separately this is required so that BSS symbols are handled - // properly as the BSS symbols only occupy memory size and not file size - uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); - uint64_t mOffset = alignOffset(this->memSize(), atomAlign); - switch(definedAtom->contentType()) { - case DefinedAtom::typeCode: - case DefinedAtom::typeConstant: - case DefinedAtom::typeData: - case DefinedAtom::typeDataFast: - case DefinedAtom::typeZeroFillFast: - case DefinedAtom::typeGOT: - case DefinedAtom::typeStub: - case DefinedAtom::typeResolver: - case DefinedAtom::typeThreadData: - case DefinedAtom::typeRONote: - case DefinedAtom::typeRWNote: - _atoms.push_back(new (_alloc) lld::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"); - break; - case DefinedAtom::typeNoAlloc: - _atoms.push_back(new (_alloc) lld::AtomLayout(atom, fOffset, 0)); - this->_fsize = fOffset + definedAtom->size(); - DEBUG_WITH_TYPE("Section", llvm::dbgs() << "[" << this->name() << " " - << this << "] " - << "Adding atom: " << atom->name() - << "@" << fOffset << "\n"); - break; - case DefinedAtom::typeThreadZeroFill: - case DefinedAtom::typeZeroFill: - _atoms.push_back(new (_alloc) lld::AtomLayout(atom, mOffset, 0)); - this->_msize = mOffset + definedAtom->size(); - break; - default: - llvm::dbgs() << definedAtom->contentType() << "\n"; - llvm_unreachable("Uexpected content type."); - } - // Set the section alignment to the largest alignment - // std::max doesn't support uint64_t - if (this->_alignment < alignment) - this->_alignment = alignment; - - if (_atoms.size()) - return _atoms.back(); - return nullptr; -} - -/// \brief convert the segment type to a String for diagnostics -/// and printing purposes -template <class ELFT> StringRef Section<ELFT>::segmentKindToStr() const { - switch(_segmentType) { - case llvm::ELF::PT_DYNAMIC: - return "DYNAMIC"; - case llvm::ELF::PT_INTERP: - return "INTERP"; - case llvm::ELF::PT_LOAD: - return "LOAD"; - case llvm::ELF::PT_GNU_EH_FRAME: - return "EH_FRAME"; - case llvm::ELF::PT_GNU_RELRO: - return "GNU_RELRO"; - case llvm::ELF::PT_NOTE: - return "NOTE"; - case llvm::ELF::PT_NULL: - return "NULL"; - case llvm::ELF::PT_TLS: - return "TLS"; - default: - return "UNKNOWN"; - } -} - -/// \brief Write the section and the atom contents to the buffer -template <class ELFT> -void AtomSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - bool success = true; - parallel_for_each(_atoms.begin(), _atoms.end(), [&](lld::AtomLayout * ai) { - DEBUG_WITH_TYPE("Section", - llvm::dbgs() << "Writing atom: " << ai->_atom->name() - << " | " << ai->_fileOffset << "\n"); - const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); - if (!definedAtom->occupiesDiskSpace()) - return; - // Copy raw content of atom to file buffer. - ArrayRef<uint8_t> content = definedAtom->rawContent(); - uint64_t contentSize = content.size(); - if (contentSize == 0) - return; - uint8_t *atomContent = chunkBuffer + ai->_fileOffset; - std::memcpy(atomContent, content.data(), contentSize); - const TargetRelocationHandler &relHandler = - this->_context.template getTargetHandler<ELFT>().getRelocationHandler(); - for (const auto ref : *definedAtom) { - if (std::error_code ec = relHandler.applyRelocation(*writer, buffer, - *ai, *ref)) { - printError(ec.message(), *ai, *ref); - success = false; - } - } - }); - if (!success) - llvm::report_fatal_error("relocating output"); -} - /// \brief A OutputSection represents a set of sections grouped by the same /// name. The output file that gets written by the linker has sections grouped /// by similar names template <class ELFT> class OutputSection { public: // Iterators - typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; + typedef typename std::vector<Section<ELFT> *>::iterator SectionIter; - OutputSection(StringRef name); + OutputSection(StringRef name) : _name(name) {} // Appends a section into the list of sections that are part of this Output // Section - void appendSection(Chunk<ELFT> *c); + void appendSection(Section<ELFT> *c); // Set the OutputSection is associated with a segment void setHasSegment() { _hasSegment = true; } @@ -484,90 +245,48 @@ public: } void setLink(uint64_t link) { _link = link; } - void setInfo(uint64_t info) { _shInfo = info; } - void setFlag(uint64_t flags) { _flags = flags; } - - void setType(int16_t type) { _type = type; } - - range<ChunkIter> sections() { return _sections; } + void setType(int64_t type) { _type = type; } + range<SectionIter> sections() { return _sections; } // The below functions returns the properties of the OutputSection. bool hasSegment() const { return _hasSegment; } - StringRef name() const { return _name; } - int64_t shinfo() const { return _shInfo; } - uint64_t alignment() const { return _alignment; } - int64_t link() const { return _link; } - int64_t type() const { return _type; } - uint64_t virtualAddr() const { return _virtualAddr; } - int64_t ordinal() const { return _ordinal; } - int64_t kind() const { return _kind; } - uint64_t fileSize() const { return _size; } - int64_t entsize() const { return _entSize; } - uint64_t fileOffset() const { return _fileOffset; } - - int64_t flags() const { return _flags; } - - uint64_t memSize() { return _memSize; } + uint64_t flags() const { return _flags; } + uint64_t memSize() const { return _memSize; } private: StringRef _name; - bool _hasSegment; - uint64_t _ordinal; - uint64_t _flags; - uint64_t _size; - uint64_t _memSize; - uint64_t _fileOffset; - uint64_t _virtualAddr; - int64_t _shInfo; - int64_t _entSize; - int64_t _link; - uint64_t _alignment; - int64_t _kind; - int64_t _type; - bool _isLoadableSection; - std::vector<Chunk<ELFT> *> _sections; + bool _hasSegment = false; + uint64_t _ordinal = 0; + uint64_t _flags = 0; + uint64_t _size = 0; + uint64_t _memSize = 0; + uint64_t _fileOffset = 0; + uint64_t _virtualAddr = 0; + int64_t _shInfo = 0; + int64_t _entSize = 0; + int64_t _link = 0; + uint64_t _alignment = 1; + int64_t _kind = 0; + int64_t _type = 0; + bool _isLoadableSection = false; + std::vector<Section<ELFT> *> _sections; }; -/// OutputSection -template <class ELFT> -OutputSection<ELFT>::OutputSection(StringRef name) - : _name(name), _hasSegment(false), _ordinal(0), _flags(0), _size(0), - _memSize(0), _fileOffset(0), _virtualAddr(0), _shInfo(0), _entSize(0), - _link(0), _alignment(0), _kind(0), _type(0), _isLoadableSection(false) {} - -template <class ELFT> void OutputSection<ELFT>::appendSection(Chunk<ELFT> *c) { - if (c->alignment() > _alignment) - _alignment = c->alignment(); - if (const auto section = dyn_cast<Section<ELFT>>(c)) { - assert(!_link && "Section already has a link!"); - _link = section->getLink(); - _shInfo = section->getInfo(); - _entSize = section->getEntSize(); - _type = section->getType(); - if (_flags < section->getFlags()) - _flags = section->getFlags(); - section->setOutputSection(this, (_sections.size() == 0)); - } - _kind = c->kind(); - _sections.push_back(c); -} - /// \brief The class represents the ELF String Table -template<class ELFT> -class StringTable : public Section<ELFT> { +template <class ELFT> class StringTable : public Section<ELFT> { public: StringTable(const ELFLinkingContext &, const char *str, int32_t order, bool dynamic = false); @@ -592,68 +311,21 @@ private: return lhs.equals(rhs); } }; - typedef typename llvm::DenseMap<StringRef, uint64_t, - StringRefMappingInfo> StringMapT; + typedef typename llvm::DenseMap<StringRef, uint64_t, StringRefMappingInfo> + StringMapT; typedef typename StringMapT::iterator StringMapTIter; StringMapT _stringMap; }; -template <class ELFT> -StringTable<ELFT>::StringTable(const ELFLinkingContext &context, - const char *str, int32_t order, bool dynamic) - : Section<ELFT>(context, str, "StringTable") { - // the string table has a NULL entry for which - // add an empty string - _strings.push_back(""); - this->_fsize = 1; - this->_alignment = 1; - this->setOrder(order); - this->_type = SHT_STRTAB; - if (dynamic) { - this->_flags = SHF_ALLOC; - this->_msize = this->_fsize; - } -} - -template <class ELFT> uint64_t StringTable<ELFT>::addString(StringRef symname) { - if (symname.empty()) - return 0; - StringMapTIter stringIter = _stringMap.find(symname); - if (stringIter == _stringMap.end()) { - _strings.push_back(symname); - uint64_t offset = this->_fsize; - this->_fsize += symname.size() + 1; - if (this->_flags & SHF_ALLOC) - this->_msize = this->_fsize; - _stringMap[symname] = offset; - return offset; - } - return stringIter->second; -} - -template <class ELFT> -void StringTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - for (auto si : _strings) { - memcpy(dest, si.data(), si.size()); - dest += si.size(); - memcpy(dest, "", 1); - dest += 1; - } -} - /// \brief The SymbolTable class represents the symbol table in a ELF file -template<class ELFT> -class SymbolTable : public Section<ELFT> { - typedef typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Addr - Elf_Addr; +template <class ELFT> class SymbolTable : public Section<ELFT> { + typedef + typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Addr Elf_Addr; public: typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; - SymbolTable(const ELFLinkingContext &context, const char *str, int32_t order); + SymbolTable(const ELFLinkingContext &ctx, const char *str, int32_t order); /// \brief set the number of entries that would exist in the symbol /// table for the current link @@ -666,7 +338,7 @@ public: std::size_t size() const { return _symbolTable.size(); } void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0, - const lld::AtomLayout *layout = nullptr); + const AtomLayout *layout = nullptr); /// \brief Get the symbol table index for an Atom. If it's not in the symbol /// table, return STN_UNDEF. @@ -681,9 +353,9 @@ public: virtual void sortSymbols() { std::stable_sort(_symbolTable.begin(), _symbolTable.end(), - [](const SymbolEntry & A, const SymbolEntry & B) { - return A._symbol.getBinding() < B._symbol.getBinding(); - }); + [](const SymbolEntry &A, const SymbolEntry &B) { + return A._symbol.getBinding() < B._symbol.getBinding(); + }); } virtual void addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, @@ -707,12 +379,11 @@ public: protected: struct SymbolEntry { - SymbolEntry(const Atom *a, const Elf_Sym &sym, - const lld::AtomLayout *layout) + SymbolEntry(const Atom *a, const Elf_Sym &sym, const AtomLayout *layout) : _atom(a), _atomLayout(layout), _symbol(sym) {} const Atom *_atom; - const lld::AtomLayout *_atomLayout; + const AtomLayout *_atomLayout; Elf_Sym _symbol; }; @@ -721,233 +392,23 @@ protected: std::vector<SymbolEntry> _symbolTable; }; -/// ELF Symbol Table -template <class ELFT> -SymbolTable<ELFT>::SymbolTable(const ELFLinkingContext &context, - const char *str, int32_t order) - : Section<ELFT>(context, str, "SymbolTable") { - this->setOrder(order); - Elf_Sym symbol; - std::memset(&symbol, 0, sizeof(Elf_Sym)); - _symbolTable.push_back(SymbolEntry(nullptr, symbol, nullptr)); - this->_entSize = sizeof(Elf_Sym); - this->_fsize = sizeof(Elf_Sym); - this->_alignment = sizeof(Elf_Addr); - this->_type = SHT_SYMTAB; -} - -template <class ELFT> -void SymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, - int64_t addr) { - unsigned char binding = 0, type = 0; - sym.st_size = da->size(); - DefinedAtom::ContentType ct; - switch (ct = da->contentType()) { - case DefinedAtom::typeCode: - case DefinedAtom::typeStub: - sym.st_value = addr; - type = llvm::ELF::STT_FUNC; - break; - case DefinedAtom::typeResolver: - sym.st_value = addr; - type = llvm::ELF::STT_GNU_IFUNC; - break; - case DefinedAtom::typeDataFast: - case DefinedAtom::typeData: - case DefinedAtom::typeConstant: - sym.st_value = addr; - type = llvm::ELF::STT_OBJECT; - break; - case DefinedAtom::typeGOT: - sym.st_value = addr; - type = llvm::ELF::STT_NOTYPE; - break; - case DefinedAtom::typeZeroFill: - case DefinedAtom::typeZeroFillFast: - type = llvm::ELF::STT_OBJECT; - sym.st_value = addr; - break; - case DefinedAtom::typeThreadData: - case DefinedAtom::typeThreadZeroFill: - type = llvm::ELF::STT_TLS; - sym.st_value = addr; - break; - default: - type = llvm::ELF::STT_NOTYPE; - } - if (da->customSectionName() == da->name()) - type = llvm::ELF::STT_SECTION; - - if (da->scope() == DefinedAtom::scopeTranslationUnit) - binding = llvm::ELF::STB_LOCAL; - else - binding = llvm::ELF::STB_GLOBAL; - - sym.setBindingAndType(binding, type); -} - -template <class ELFT> -void SymbolTable<ELFT>::addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, - int64_t addr) { - unsigned char binding = 0, type = 0; - type = llvm::ELF::STT_OBJECT; - sym.st_shndx = llvm::ELF::SHN_ABS; - switch (aa->scope()) { - case AbsoluteAtom::scopeLinkageUnit: - sym.setVisibility(llvm::ELF::STV_HIDDEN); - binding = llvm::ELF::STB_LOCAL; - break; - case AbsoluteAtom::scopeTranslationUnit: - binding = llvm::ELF::STB_LOCAL; - break; - case AbsoluteAtom::scopeGlobal: - binding = llvm::ELF::STB_GLOBAL; - break; - } - sym.st_value = addr; - sym.setBindingAndType(binding, type); -} - -template <class ELFT> -void SymbolTable<ELFT>::addSharedLibAtom(Elf_Sym &sym, - const SharedLibraryAtom *aa) { - unsigned char binding = 0, type = 0; - if (aa->type() == SharedLibraryAtom::Type::Data) { - type = llvm::ELF::STT_OBJECT; - sym.st_size = aa->size(); - } else - type = llvm::ELF::STT_FUNC; - sym.st_shndx = llvm::ELF::SHN_UNDEF; - binding = llvm::ELF::STB_GLOBAL; - sym.setBindingAndType(binding, type); -} - -template <class ELFT> -void SymbolTable<ELFT>::addUndefinedAtom(Elf_Sym &sym, - const UndefinedAtom *ua) { - unsigned char binding = 0, type = 0; - sym.st_value = 0; - type = llvm::ELF::STT_NOTYPE; - if (ua->canBeNull()) - binding = llvm::ELF::STB_WEAK; - else - binding = llvm::ELF::STB_GLOBAL; - sym.setBindingAndType(binding, type); -} - -/// Add a symbol to the symbol Table, definedAtoms which get added to the symbol -/// section don't have their virtual addresses set at the time of adding the -/// symbol to the symbol table(Example: dynamic symbols), the addresses needs -/// to be updated in the table before writing the dynamic symbol table -/// information -template <class ELFT> -void SymbolTable<ELFT>::addSymbol(const Atom *atom, int32_t sectionIndex, - uint64_t addr, - const lld::AtomLayout *atomLayout) { - Elf_Sym symbol; - - if (atom->name().empty()) - return; - - symbol.st_name = _stringSection->addString(atom->name()); - symbol.st_size = 0; - symbol.st_shndx = sectionIndex; - symbol.st_value = 0; - symbol.st_other = 0; - symbol.setVisibility(llvm::ELF::STV_DEFAULT); - - // Add all the atoms - if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom)) - addDefinedAtom(symbol, da, addr); - else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(atom)) - addAbsoluteAtom(symbol, aa, addr); - else if (isa<const SharedLibraryAtom>(atom)) - addSharedLibAtom(symbol, dyn_cast<SharedLibraryAtom>(atom)); - else - addUndefinedAtom(symbol, dyn_cast<UndefinedAtom>(atom)); - - _symbolTable.push_back(SymbolEntry(atom, symbol, atomLayout)); - this->_fsize += sizeof(Elf_Sym); - if (this->_flags & SHF_ALLOC) - this->_msize = this->_fsize; -} - -template <class ELFT> void SymbolTable<ELFT>::finalize(bool sort) { - // sh_info should be one greater than last symbol with STB_LOCAL binding - // we sort the symbol table to keep all local symbols at the beginning - if (sort) - sortSymbols(); - - uint16_t shInfo = 0; - for (const auto &i : _symbolTable) { - if (i._symbol.getBinding() != llvm::ELF::STB_LOCAL) - break; - shInfo++; - } - this->_info = shInfo; - this->_link = _stringSection->ordinal(); - if (this->_outputSection) { - this->_outputSection->setInfo(this->_info); - this->_outputSection->setLink(this->_link); - } -} - -template <class ELFT> -void SymbolTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - for (const auto &sti : _symbolTable) { - memcpy(dest, &sti._symbol, sizeof(Elf_Sym)); - dest += sizeof(Elf_Sym); - } -} - template <class ELFT> class HashSection; template <class ELFT> class DynamicSymbolTable : public SymbolTable<ELFT> { public: - DynamicSymbolTable(const ELFLinkingContext &context, - TargetLayout<ELFT> &layout, const char *str, int32_t order) - : SymbolTable<ELFT>(context, str, order), _hashTable(nullptr), - _layout(layout) { - this->_type = SHT_DYNSYM; - this->_flags = SHF_ALLOC; - this->_msize = this->_fsize; - } + DynamicSymbolTable(const ELFLinkingContext &ctx, TargetLayout<ELFT> &layout, + const char *str, int32_t order); // Set the dynamic hash table for symbols to be added into void setHashTable(HashSection<ELFT> *hashTable) { _hashTable = hashTable; } // Add all the dynamic symbos to the hash table - void addSymbolsToHashTable() { - int index = 0; - for (auto &ste : this->_symbolTable) { - if (!ste._atom) - _hashTable->addSymbol("", index); - else - _hashTable->addSymbol(ste._atom->name(), index); - ++index; - } - } + void addSymbolsToHashTable(); - void finalize() override { - // Defined symbols which have been added into the dynamic symbol table - // don't have their addresses known until addresses have been assigned - // so let's update the symbol values after they have got assigned - for (auto &ste: this->_symbolTable) { - const lld::AtomLayout *atomLayout = ste._atomLayout; - if (!atomLayout) - continue; - ste._symbol.st_value = atomLayout->_virtualAddr; - } - - // Don't sort the symbols - SymbolTable<ELFT>::finalize(false); - } + void finalize() override; protected: - HashSection<ELFT> *_hashTable; + HashSection<ELFT> *_hashTable = nullptr; TargetLayout<ELFT> &_layout; }; @@ -956,118 +417,36 @@ public: typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; - RelocationTable(const ELFLinkingContext &context, StringRef str, - int32_t order) - : Section<ELFT>(context, str, "RelocationTable"), _symbolTable(nullptr) { - this->setOrder(order); - this->_flags = SHF_ALLOC; - // Set the alignment properly depending on the target architecture - this->_alignment = ELFT::Is64Bits ? 8 : 4; - if (context.isRelaOutputFormat()) { - this->_entSize = sizeof(Elf_Rela); - this->_type = SHT_RELA; - } else { - this->_entSize = sizeof(Elf_Rel); - this->_type = SHT_REL; - } - } + RelocationTable(const ELFLinkingContext &ctx, StringRef str, int32_t order); /// \returns the index of the relocation added. - uint32_t addRelocation(const DefinedAtom &da, const Reference &r) { - _relocs.emplace_back(&da, &r); - this->_fsize = _relocs.size() * this->_entSize; - this->_msize = this->_fsize; - return _relocs.size() - 1; - } + uint32_t addRelocation(const DefinedAtom &da, const Reference &r); - bool getRelocationIndex(const Reference &r, uint32_t &res) { - auto rel = std::find_if( - _relocs.begin(), _relocs.end(), - [&](const std::pair<const DefinedAtom *, const Reference *> &p) { - if (p.second == &r) - return true; - return false; - }); - if (rel == _relocs.end()) - return false; - res = std::distance(_relocs.begin(), rel); - return true; - } + bool getRelocationIndex(const Reference &r, uint32_t &res); void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { _symbolTable = symbolTable; } /// \brief Check if any relocation modifies a read-only section. - bool canModifyReadonlySection() const { - for (const auto &rel : _relocs) { - const DefinedAtom *atom = rel.first; - if ((atom->permissions() & DefinedAtom::permRW_) != DefinedAtom::permRW_) - return true; - } - return false; - } + bool canModifyReadonlySection() const; - void finalize() override { - this->_link = _symbolTable ? _symbolTable->ordinal() : 0; - if (this->_outputSection) - this->_outputSection->setLink(this->_link); - } + void finalize() override; void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) override { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - for (const auto &rel : _relocs) { - if (this->_context.isRelaOutputFormat()) { - auto &r = *reinterpret_cast<Elf_Rela *>(dest); - writeRela(writer, r, *rel.first, *rel.second); - DEBUG_WITH_TYPE("ELFRelocationTable", - llvm::dbgs() - << rel.second->kindValue() << " relocation at " - << rel.first->name() << "@" << r.r_offset << " to " - << rel.second->target()->name() << "@" << r.r_addend - << "\n";); - } else { - auto &r = *reinterpret_cast<Elf_Rel *>(dest); - writeRel(writer, r, *rel.first, *rel.second); - DEBUG_WITH_TYPE("ELFRelocationTable", - llvm::dbgs() << rel.second->kindValue() - << " relocation at " << rel.first->name() - << "@" << r.r_offset << " to " - << rel.second->target()->name() << "\n";); - } - dest += this->_entSize; - } - } + llvm::FileOutputBuffer &buffer) override; protected: - const DynamicSymbolTable<ELFT> *_symbolTable; + const DynamicSymbolTable<ELFT> *_symbolTable = nullptr; virtual void writeRela(ELFWriter *writer, Elf_Rela &r, - const DefinedAtom &atom, const Reference &ref) { - r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); - r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); - // The addend is used only by relative relocations - if (this->_context.isRelativeReloc(ref)) - r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); - else - r.r_addend = 0; - } - + const DefinedAtom &atom, const Reference &ref); virtual void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom, - const Reference &ref) { - r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); - r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); - } - - uint32_t getSymbolIndex(const Atom *a) { - return _symbolTable ? _symbolTable->getSymbolTableIndex(a) - : (uint32_t)STN_UNDEF; - } + const Reference &ref); + uint32_t getSymbolIndex(const Atom *a); private: - std::vector<std::pair<const DefinedAtom *, const Reference *> > _relocs; + std::vector<std::pair<const DefinedAtom *, const Reference *>> _relocs; }; template <class ELFT> class HashSection; @@ -1077,125 +456,25 @@ public: typedef llvm::object::Elf_Dyn_Impl<ELFT> Elf_Dyn; typedef std::vector<Elf_Dyn> EntriesT; - DynamicTable(const ELFLinkingContext &context, TargetLayout<ELFT> &layout, - StringRef str, int32_t order) - : Section<ELFT>(context, str, "DynamicSection"), _layout(layout) { - this->setOrder(order); - this->_entSize = sizeof(Elf_Dyn); - this->_alignment = ELFT::Is64Bits ? 8 : 4; - // Reserve space for the DT_NULL entry. - this->_fsize = sizeof(Elf_Dyn); - this->_msize = sizeof(Elf_Dyn); - this->_type = SHT_DYNAMIC; - this->_flags = SHF_ALLOC; - } + DynamicTable(const ELFLinkingContext &ctx, TargetLayout<ELFT> &layout, + StringRef str, int32_t order); range<typename EntriesT::iterator> entries() { return _entries; } /// \returns the index of the entry. - std::size_t addEntry(Elf_Dyn e) { - _entries.push_back(e); - this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn); - this->_msize = this->_fsize; - return _entries.size() - 1; - } + std::size_t addEntry(int64_t tag, uint64_t val); void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) override { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - // Add the null entry. - Elf_Dyn d; - d.d_tag = 0; - d.d_un.d_val = 0; - _entries.push_back(d); - std::memcpy(dest, _entries.data(), this->_fsize); - } - - virtual void createDefaultEntries() { - bool isRela = this->_context.isRelaOutputFormat(); - - Elf_Dyn dyn; - dyn.d_un.d_val = 0; - - dyn.d_tag = DT_HASH; - _dt_hash = addEntry(dyn); - dyn.d_tag = DT_STRTAB; - _dt_strtab = addEntry(dyn); - dyn.d_tag = DT_SYMTAB; - _dt_symtab = addEntry(dyn); - dyn.d_tag = DT_STRSZ; - _dt_strsz = addEntry(dyn); - dyn.d_tag = DT_SYMENT; - _dt_syment = addEntry(dyn); - if (_layout.hasDynamicRelocationTable()) { - dyn.d_tag = isRela ? DT_RELA : DT_REL; - _dt_rela = addEntry(dyn); - dyn.d_tag = isRela ? DT_RELASZ : DT_RELSZ; - _dt_relasz = addEntry(dyn); - dyn.d_tag = isRela ? DT_RELAENT : DT_RELENT; - _dt_relaent = addEntry(dyn); - - if (_layout.getDynamicRelocationTable()->canModifyReadonlySection()) { - dyn.d_tag = DT_TEXTREL; - _dt_textrel = addEntry(dyn); - } - } - if (_layout.hasPLTRelocationTable()) { - dyn.d_tag = DT_PLTRELSZ; - _dt_pltrelsz = addEntry(dyn); - dyn.d_tag = getGotPltTag(); - _dt_pltgot = addEntry(dyn); - dyn.d_tag = DT_PLTREL; - dyn.d_un.d_val = isRela ? DT_RELA : DT_REL; - _dt_pltrel = addEntry(dyn); - dyn.d_un.d_val = 0; - dyn.d_tag = DT_JMPREL; - _dt_jmprel = addEntry(dyn); - } - } + llvm::FileOutputBuffer &buffer) override; - void doPreFlight() override { - Elf_Dyn dyn; - dyn.d_un.d_val = 0; - auto initArray = _layout.findOutputSection(".init_array"); - auto finiArray = _layout.findOutputSection(".fini_array"); - if (initArray) { - dyn.d_tag = DT_INIT_ARRAY; - _dt_init_array = addEntry(dyn); - dyn.d_tag = DT_INIT_ARRAYSZ; - _dt_init_arraysz = addEntry(dyn); - } - if (finiArray) { - dyn.d_tag = DT_FINI_ARRAY; - _dt_fini_array = addEntry(dyn); - dyn.d_tag = DT_FINI_ARRAYSZ; - _dt_fini_arraysz = addEntry(dyn); - } - if (getInitAtomLayout()) { - dyn.d_tag = DT_INIT; - _dt_init = addEntry(dyn); - } - if (getFiniAtomLayout()) { - dyn.d_tag = DT_FINI; - _dt_fini = addEntry(dyn); - } - } + virtual void createDefaultEntries(); + void doPreFlight() override; /// \brief Dynamic table tag for .got.plt section referencing. /// Usually but not always targets use DT_PLTGOT for that. virtual int64_t getGotPltTag() { return DT_PLTGOT; } - void finalize() override { - StringTable<ELFT> *dynamicStringTable = - _dynamicSymbolTable->getStringTable(); - this->_link = dynamicStringTable->ordinal(); - if (this->_outputSection) { - this->_outputSection->setType(this->_type); - this->_outputSection->setInfo(this->_info); - this->_outputSection->setLink(this->_link); - } - } + void finalize() override; void setSymbolTable(DynamicSymbolTable<ELFT> *dynsym) { _dynamicSymbolTable = dynsym; @@ -1207,42 +486,7 @@ public: void setHashTable(HashSection<ELFT> *hsh) { _hashTable = hsh; } - virtual void updateDynamicTable() { - StringTable<ELFT> *dynamicStringTable = - _dynamicSymbolTable->getStringTable(); - _entries[_dt_hash].d_un.d_val = _hashTable->virtualAddr(); - _entries[_dt_strtab].d_un.d_val = dynamicStringTable->virtualAddr(); - _entries[_dt_symtab].d_un.d_val = _dynamicSymbolTable->virtualAddr(); - _entries[_dt_strsz].d_un.d_val = dynamicStringTable->memSize(); - _entries[_dt_syment].d_un.d_val = _dynamicSymbolTable->getEntSize(); - auto initArray = _layout.findOutputSection(".init_array"); - if (initArray) { - _entries[_dt_init_array].d_un.d_val = initArray->virtualAddr(); - _entries[_dt_init_arraysz].d_un.d_val = initArray->memSize(); - } - auto finiArray = _layout.findOutputSection(".fini_array"); - if (finiArray) { - _entries[_dt_fini_array].d_un.d_val = finiArray->virtualAddr(); - _entries[_dt_fini_arraysz].d_un.d_val = finiArray->memSize(); - } - if (const auto *al = getInitAtomLayout()) - _entries[_dt_init].d_un.d_val = getAtomVirtualAddress(al); - if (const auto *al = getFiniAtomLayout()) - _entries[_dt_fini].d_un.d_val = getAtomVirtualAddress(al); - if (_layout.hasDynamicRelocationTable()) { - auto relaTbl = _layout.getDynamicRelocationTable(); - _entries[_dt_rela].d_un.d_val = relaTbl->virtualAddr(); - _entries[_dt_relasz].d_un.d_val = relaTbl->memSize(); - _entries[_dt_relaent].d_un.d_val = relaTbl->getEntSize(); - } - if (_layout.hasPLTRelocationTable()) { - auto relaTbl = _layout.getPLTRelocationTable(); - _entries[_dt_jmprel].d_un.d_val = relaTbl->virtualAddr(); - _entries[_dt_pltrelsz].d_un.d_val = relaTbl->memSize(); - auto gotplt = _layout.findOutputSection(".got.plt"); - _entries[_dt_pltgot].d_un.d_val = gotplt->virtualAddr(); - } - } + virtual void updateDynamicTable(); protected: EntriesT _entries; @@ -1279,41 +523,18 @@ private: DynamicSymbolTable<ELFT> *_dynamicSymbolTable; HashSection<ELFT> *_hashTable; - const AtomLayout *getInitAtomLayout() { - auto al = _layout.findAtomLayoutByName(this->_context.initFunction()); - if (al && isa<DefinedAtom>(al->_atom)) - return al; - return nullptr; - } + const AtomLayout *getInitAtomLayout(); - const AtomLayout *getFiniAtomLayout() { - auto al = _layout.findAtomLayoutByName(this->_context.finiFunction()); - if (al && isa<DefinedAtom>(al->_atom)) - return al; - return nullptr; - } + const AtomLayout *getFiniAtomLayout(); }; template <class ELFT> class InterpSection : public Section<ELFT> { public: - InterpSection(const ELFLinkingContext &context, StringRef str, int32_t order, - StringRef interp) - : Section<ELFT>(context, str, "Dynamic:Interp"), _interp(interp) { - this->setOrder(order); - this->_alignment = 1; - // + 1 for null term. - this->_fsize = interp.size() + 1; - this->_msize = this->_fsize; - this->_type = SHT_PROGBITS; - this->_flags = SHF_ALLOC; - } + InterpSection(const ELFLinkingContext &ctx, StringRef str, int32_t order, + StringRef interp); void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - std::memcpy(dest, _interp.data(), _interp.size()); - } + llvm::FileOutputBuffer &buffer) override; private: StringRef _interp; @@ -1338,7 +559,6 @@ private: /// * Take the value from the buckets[hash % nbuckets] as the index of symbol /// * Compare the symbol's name, if true return, if false, look through the /// * array since there was a collision - template <class ELFT> class HashSection : public Section<ELFT> { struct SymbolTableEntry { StringRef _name; @@ -1346,153 +566,51 @@ template <class ELFT> class HashSection : public Section<ELFT> { }; public: - HashSection(const ELFLinkingContext &context, StringRef name, int32_t order) - : Section<ELFT>(context, name, "Dynamic:Hash"), _symbolTable(nullptr) { - this->setOrder(order); - this->_entSize = 4; - this->_type = SHT_HASH; - this->_flags = SHF_ALLOC; - this->_alignment = ELFT::Is64Bits ? 8 : 4; - this->_fsize = 0; - this->_msize = 0; - } + HashSection(const ELFLinkingContext &ctx, StringRef name, int32_t order); /// \brief add the dynamic symbol into the table so that the /// hash could be calculated - void addSymbol(StringRef name, uint32_t index) { - SymbolTableEntry ste; - ste._name = name; - ste._index = index; - _entries.push_back(ste); - } + void addSymbol(StringRef name, uint32_t index); /// \brief Set the dynamic symbol table - void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { - _symbolTable = symbolTable; - } + void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable); // The size of the section has to be determined so that fileoffsets // may be properly assigned. Let's calculate the buckets and the chains // and fill the chains and the buckets hash table used by the dynamic // linker and update the filesize and memory size accordingly - void doPreFlight() override { - // The number of buckets to use for a certain number of symbols. - // If there are less than 3 symbols, 1 bucket will be used. If - // there are less than 17 symbols, 3 buckets will be used, and so - // forth. The bucket numbers are defined by GNU ld. We use the - // same rules here so we generate hash sections with the same - // size as those generated by GNU ld. - uint32_t hashBuckets[] = { 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, - 2053, 4099, 8209, 16411, 32771, 65537, 131101, - 262147 }; - int hashBucketsCount = sizeof(hashBuckets) / sizeof(uint32_t); - - unsigned int bucketsCount = 0; - unsigned int dynSymCount = _entries.size(); - - // Get the number of buckes that we want to use - for (int i = 0; i < hashBucketsCount; ++i) { - if (dynSymCount < hashBuckets[i]) - break; - bucketsCount = hashBuckets[i]; - } - _buckets.resize(bucketsCount); - _chains.resize(_entries.size()); - - // Create the hash table for the dynamic linker - for (auto ai : _entries) { - unsigned int dynsymIndex = ai._index; - unsigned int bucketpos = llvm::object::elf_hash(ai._name) % bucketsCount; - _chains[dynsymIndex] = _buckets[bucketpos]; - _buckets[bucketpos] = dynsymIndex; - } + void doPreFlight() override; - this->_fsize = (2 + _chains.size() + _buckets.size()) * sizeof(uint32_t); - this->_msize = this->_fsize; - } - - void finalize() override { - this->_link = _symbolTable ? _symbolTable->ordinal() : 0; - if (this->_outputSection) - this->_outputSection->setLink(this->_link); - } + void finalize() override; void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) override { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - uint32_t bucketChainCounts[2]; - bucketChainCounts[0] = _buckets.size(); - bucketChainCounts[1] = _chains.size(); - std::memcpy(dest, (char *)bucketChainCounts, sizeof(bucketChainCounts)); - dest += sizeof(bucketChainCounts); - // write bucket values - for (auto bi : _buckets) { - uint32_t val = (bi); - std::memcpy(dest, &val, sizeof(uint32_t)); - dest += sizeof(uint32_t); - } - // write chain values - for (auto ci : _chains) { - uint32_t val = (ci); - std::memcpy(dest, &val, sizeof(uint32_t)); - dest += sizeof(uint32_t); - } - } + llvm::FileOutputBuffer &buffer) override; private: + typedef + typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Word Elf_Word; + std::vector<SymbolTableEntry> _entries; - std::vector<uint32_t> _buckets; - std::vector<uint32_t> _chains; - const DynamicSymbolTable<ELFT> *_symbolTable; + std::vector<Elf_Word> _buckets; + std::vector<Elf_Word> _chains; + const DynamicSymbolTable<ELFT> *_symbolTable = nullptr; }; template <class ELFT> class EHFrameHeader : public Section<ELFT> { public: - EHFrameHeader(const ELFLinkingContext &context, StringRef name, - TargetLayout<ELFT> &layout, int32_t order) - : Section<ELFT>(context, name, "EHFrameHeader"), _ehFrameOffset(0), - _layout(layout) { - this->setOrder(order); - this->_entSize = 0; - this->_type = SHT_PROGBITS; - this->_flags = SHF_ALLOC; - this->_alignment = ELFT::Is64Bits ? 8 : 4; - // Minimum size for empty .eh_frame_hdr. - this->_fsize = 1 + 1 + 1 + 1 + 4; - this->_msize = this->_fsize; - } - - void doPreFlight() override { - // TODO: Generate a proper binary search table. - } - - void finalize() override { - OutputSection<ELFT> *s = _layout.findOutputSection(".eh_frame"); - OutputSection<ELFT> *h = _layout.findOutputSection(".eh_frame_hdr"); - if (s && h) - _ehFrameOffset = s->virtualAddr() - (h->virtualAddr() + 4); - } - + EHFrameHeader(const ELFLinkingContext &ctx, StringRef name, + TargetLayout<ELFT> &layout, int32_t order); + void doPreFlight() override; + void finalize() override; void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) override { - uint8_t *chunkBuffer = buffer.getBufferStart(); - uint8_t *dest = chunkBuffer + this->fileOffset(); - int pos = 0; - dest[pos++] = 1; // version - dest[pos++] = llvm::dwarf::DW_EH_PE_pcrel | - llvm::dwarf::DW_EH_PE_sdata4; // eh_frame_ptr_enc - dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // fde_count_enc - dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // table_enc - *reinterpret_cast<typename llvm::object::ELFFile<ELFT>::Elf_Sword *>( - dest + pos) = _ehFrameOffset; - } + llvm::FileOutputBuffer &buffer) override; private: - int32_t _ehFrameOffset; + int32_t _ehFrameOffset = 0; TargetLayout<ELFT> &_layout; }; + } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_SECTION_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/SegmentChunks.cpp b/lib/ReaderWriter/ELF/SegmentChunks.cpp new file mode 100644 index 000000000000..99449f7d45aa --- /dev/null +++ b/lib/ReaderWriter/ELF/SegmentChunks.cpp @@ -0,0 +1,519 @@ +//===- lib/ReaderWriter/ELF/SegmentChunks.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SegmentChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +template <class ELFT> +bool SegmentSlice<ELFT>::compare_slices(SegmentSlice<ELFT> *a, + SegmentSlice<ELFT> *b) { + return a->startSection() < b->startSection(); +} + +template <class ELFT> +Segment<ELFT>::Segment(const ELFLinkingContext &ctx, StringRef name, + const typename TargetLayout<ELFT>::SegmentType type) + : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, ctx), _segmentType(type), + _flags(0), _atomflags(0), _segmentFlags(false) { + this->_alignment = 1; + this->_fsize = 0; + _outputMagic = ctx.getOutputMagic(); +} + +// This function actually is used, but not in all instantiations of Segment. +LLVM_ATTRIBUTE_UNUSED +static DefinedAtom::ContentPermissions toAtomPerms(uint64_t flags) { + switch (flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) { + case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR: + return DefinedAtom::permRWX; + case SHF_ALLOC | SHF_EXECINSTR: + return DefinedAtom::permR_X; + case SHF_ALLOC: + return DefinedAtom::permR__; + case SHF_ALLOC | SHF_WRITE: + return DefinedAtom::permRW_; + default: + return DefinedAtom::permUnknown; + } +} + +// This function actually is used, but not in all instantiations of Segment. +LLVM_ATTRIBUTE_UNUSED +static DefinedAtom::ContentPermissions toAtomPermsSegment(uint64_t flags) { + switch (flags & (llvm::ELF::PF_R | llvm::ELF::PF_W | llvm::ELF::PF_X)) { + case llvm::ELF::PF_R | llvm::ELF::PF_W | llvm::ELF::PF_X: + return DefinedAtom::permRWX; + case llvm::ELF::PF_R | llvm::ELF::PF_X: + return DefinedAtom::permR_X; + case llvm::ELF::PF_R: + return DefinedAtom::permR__; + case llvm::ELF::PF_R | llvm::ELF::PF_W: + return DefinedAtom::permRW_; + default: + return DefinedAtom::permUnknown; + } +} + +template <class ELFT> void Segment<ELFT>::append(Chunk<ELFT> *chunk) { + _sections.push_back(chunk); + Section<ELFT> *section = dyn_cast<Section<ELFT>>(chunk); + if (!section) + return; + if (this->_alignment < section->alignment()) + this->_alignment = section->alignment(); + + if (_segmentFlags) + return; + if (_flags < section->getFlags()) + _flags |= section->getFlags(); + if (_atomflags < toAtomPerms(_flags)) + _atomflags = toAtomPerms(_flags); +} + +template <class ELFT> +bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) { + int64_t type1 = sega->segmentType(); + int64_t type2 = segb->segmentType(); + + if (type1 == type2) + return sega->atomflags() < segb->atomflags(); + + // The single PT_PHDR segment is required to precede any loadable + // segment. We simply make it always first. + if (type1 == llvm::ELF::PT_PHDR) + return true; + if (type2 == llvm::ELF::PT_PHDR) + return false; + + // The single PT_INTERP segment is required to precede any loadable + // segment. We simply make it always second. + if (type1 == llvm::ELF::PT_INTERP) + return true; + if (type2 == llvm::ELF::PT_INTERP) + return false; + + // We then put PT_LOAD segments before any other segments. + if (type1 == llvm::ELF::PT_LOAD) + return true; + if (type2 == llvm::ELF::PT_LOAD) + return false; + + // We put the PT_GNU_RELRO segment last, because that is where the + // dynamic linker expects to find it + if (type1 == llvm::ELF::PT_GNU_RELRO) + return false; + if (type2 == llvm::ELF::PT_GNU_RELRO) + return true; + + // We put the PT_TLS segment last except for the PT_GNU_RELRO + // segment, because that is where the dynamic linker expects to find + if (type1 == llvm::ELF::PT_TLS) + return false; + if (type2 == llvm::ELF::PT_TLS) + return true; + + // Otherwise compare the types to establish an arbitrary ordering. + // FIXME: Should figure out if we should just make all other types compare + // equal, but if so, we should probably do the same for atom flags and change + // users of this to use stable_sort. + return type1 < type2; +} + +template <class ELFT> +void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) { + uint64_t fileOffset = startOffset; + uint64_t curSliceFileOffset = fileOffset; + bool isDataPageAlignedForNMagic = false; + bool alignSegments = this->_ctx.alignSegments(); + uint64_t p_align = this->_ctx.getPageSize(); + uint64_t lastVirtualAddress = 0; + + this->setFileOffset(startOffset); + bool changeOffset = false; + uint64_t newOffset = 0; + for (auto &slice : slices()) { + bool isFirstSection = true; + for (auto section : slice->sections()) { + // Handle linker script expressions, which may change the offset + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) { + if (!isFirstSection) { + changeOffset = true; + newOffset = fileOffset + expr->virtualAddr() - lastVirtualAddress; + } + continue; + } + if (changeOffset) { + changeOffset = false; + fileOffset = newOffset; + } + // Align fileoffset to the alignment of the section. + fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data + // to a page boundary + if (isFirstSection && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + if (alignSegments) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); + // Align according to ELF spec. + // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf + uint64_t virtualAddress = slice->virtualAddr(); + Section<ELFT> *sect = dyn_cast<Section<ELFT>>(section); + if (sect && sect->isLoadableSection() && + ((virtualAddress & (p_align - 1)) != (fileOffset & (p_align - 1)))) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align) + + (virtualAddress % p_align); + } else if (!isDataPageAlignedForNMagic && needAlign(section)) { + fileOffset = + llvm::RoundUpToAlignment(fileOffset, this->_ctx.getPageSize()); + isDataPageAlignedForNMagic = true; + } + if (isFirstSection) { + slice->setFileOffset(fileOffset); + isFirstSection = false; + curSliceFileOffset = fileOffset; + } + section->setFileOffset(fileOffset); + fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); + } + changeOffset = false; + slice->setFileSize(fileOffset - curSliceFileOffset); + } + this->setFileSize(fileOffset - startOffset); +} + +/// \brief Assign virtual addresses to the slices +template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) { + int startSection = 0; + int currSection = 0; + SectionIter startSectionIter; + + // slice align is set to the max alignment of the chunks that are + // contained in the slice + uint64_t sliceAlign = 0; + // Current slice size + uint64_t curSliceSize = 0; + // Current Slice File Offset + uint64_t curSliceAddress = 0; + + startSectionIter = _sections.begin(); + startSection = 0; + bool isDataPageAlignedForNMagic = false; + uint64_t startAddr = addr; + SegmentSlice<ELFT> *slice = nullptr; + uint64_t tlsStartAddr = 0; + bool alignSegments = this->_ctx.alignSegments(); + StringRef prevOutputSectionName = StringRef(); + uint64_t tbssMemsize = 0; + + // If this is first section in the segment, page align the section start + // address. The linker needs to align the data section to a page boundary + // only if NMAGIC is set. + auto si = _sections.begin(); + if (si != _sections.end()) { + if (alignSegments && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + startAddr = llvm::RoundUpToAlignment(startAddr, this->_ctx.getPageSize()); + } else if (needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data to a page boundary. + startAddr = llvm::RoundUpToAlignment(startAddr, this->_ctx.getPageSize()); + isDataPageAlignedForNMagic = true; + } + // align the startOffset to the section alignment + uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + curSliceAddress = newAddr; + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(curSliceAddress); + + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of any + // segment. If we see a tbss section, don't add memory size to addr The + // fileOffset is automatically taken care of since TBSS section does not + // end up using file size + if ((*si)->order() != TargetLayout<ELFT>::ORDER_TBSS) { + curSliceSize = (*si)->memSize(); + tbssMemsize = 0; + } else { + tbssMemsize = (*si)->memSize(); + } + ++currSection; + ++si; + } + + uint64_t scriptAddr = 0; + bool forceScriptAddr = false; + for (auto e = _sections.end(); si != e; ++si) { + uint64_t curAddr = curSliceAddress + curSliceSize; + if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data + // to a page boundary + curAddr = llvm::RoundUpToAlignment(curAddr, this->_ctx.getPageSize()); + isDataPageAlignedForNMagic = true; + } + uint64_t newAddr = llvm::RoundUpToAlignment( + forceScriptAddr ? scriptAddr : curAddr, (*si)->alignment()); + forceScriptAddr = false; + + // Handle linker script expressions, which may force an address change if + // the expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) { + uint64_t oldAddr = newAddr; + expr->evalExpr(newAddr); + if (oldAddr != newAddr) { + forceScriptAddr = true; + scriptAddr = newAddr; + } + (*si)->setVirtualAddr(newAddr); + continue; + } + Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); + StringRef curOutputSectionName = + sec ? sec->outputSectionName() : (*si)->name(); + bool autoCreateSlice = true; + if (curOutputSectionName == prevOutputSectionName) + autoCreateSlice = false; + // If the newAddress computed is more than a page away, let's create + // a separate segment, so that memory is not used up while running. + // Dont create a slice, if the new section falls in the same output + // section as the previous section. + if (autoCreateSlice && ((newAddr - curAddr) > this->_ctx.getPageSize()) && + (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) { + auto sliceIter = + std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = *sliceIter; + } + slice->setStart(startSection); + slice->setSections(make_range(startSectionIter, si)); + slice->setMemSize(curSliceSize); + slice->setAlign(sliceAlign); + slice->setVirtualAddr(curSliceAddress); + // Start new slice + curSliceAddress = newAddr; + if ((*si)->order() == TargetLayout<ELFT>::ORDER_TBSS) + curSliceAddress += tbssMemsize; + (*si)->setVirtualAddr(curSliceAddress); + startSectionIter = si; + startSection = currSection; + if (auto section = dyn_cast<Section<ELFT>>(*si)) + section->assignVirtualAddress(newAddr); + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + sliceAlign = (*si)->alignment(); + } else { + if (sliceAlign < (*si)->alignment()) + sliceAlign = (*si)->alignment(); + if ((*si)->order() == TargetLayout<ELFT>::ORDER_TBSS) + newAddr += tbssMemsize; + (*si)->setVirtualAddr(newAddr); + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of + // any segment. If we see a tbss section, don't add memory size to addr + // The fileOffset is automatically taken care of since TBSS section does + // not end up using file size. + if ((*si)->order() != TargetLayout<ELFT>::ORDER_TBSS) { + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + tbssMemsize = 0; + } else { + // Although TBSS section does not contribute to memory of any segment, + // we still need to keep track its total size to correct write it + // down. Since it is done based on curSliceAddress, we need to add + // add it to virtual address. + tbssMemsize = (*si)->memSize(); + } + } + prevOutputSectionName = curOutputSectionName; + ++currSection; + } + + auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = *sliceIter; + } + + slice->setStart(startSection); + slice->setVirtualAddr(curSliceAddress); + slice->setMemSize(curSliceSize); + slice->setSections(make_range(startSectionIter, _sections.end())); + slice->setAlign(sliceAlign); + + // Set the segment memory size and the virtual address. + this->setMemSize(curSliceAddress - startAddr + curSliceSize); + this->setVirtualAddr(startAddr); + std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), + SegmentSlice<ELFT>::compare_slices); +} + +// Write the Segment +template <class ELFT> +void Segment<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + for (auto slice : slices()) + for (auto section : slice->sections()) + section->write(writer, layout, buffer); +} + +template <class ELFT> int64_t Segment<ELFT>::flags() const { + if (_segmentFlags) + return (int64_t)_flags; + + int64_t fl = 0; + if (_flags & llvm::ELF::SHF_ALLOC) + fl |= llvm::ELF::PF_R; + if (_flags & llvm::ELF::SHF_WRITE) + fl |= llvm::ELF::PF_W; + if (_flags & llvm::ELF::SHF_EXECINSTR) + fl |= llvm::ELF::PF_X; + return fl; +} + +template <class ELFT> void Segment<ELFT>::setSegmentFlags(uint64_t flags) { + assert(!_segmentFlags && "Segment flags have already been set"); + _segmentFlags = true; + _flags = flags; + _atomflags = toAtomPermsSegment(flags); +} + +template <class ELFT> void Segment<ELFT>::finalize() { + // We want to finalize the segment values for now only for non loadable + // segments, since those values are not set in the Layout + if (_segmentType == llvm::ELF::PT_LOAD) + return; + // The size is the difference of the + // last section to the first section, especially for TLS because + // the TLS segment contains both .tdata/.tbss + this->setFileOffset(_sections.front()->fileOffset()); + this->setVirtualAddr(_sections.front()->virtualAddr()); + size_t startFileOffset = _sections.front()->fileOffset(); + size_t startAddr = _sections.front()->virtualAddr(); + for (auto ai : _sections) { + this->_fsize = ai->fileOffset() + ai->fileSize() - startFileOffset; + this->_msize = ai->virtualAddr() + ai->memSize() - startAddr; + } +} + +template <class ELFT> int Segment<ELFT>::getContentType() const { + int64_t fl = flags(); + switch (_segmentType) { + case llvm::ELF::PT_LOAD: { + if (fl && llvm::ELF::PF_X) + return Chunk<ELFT>::ContentType::Code; + if (fl && llvm::ELF::PF_W) + return Chunk<ELFT>::ContentType::Data; + } + case llvm::ELF::PT_TLS: + return Chunk<ELFT>::ContentType::TLS; + case llvm::ELF::PT_NOTE: + return Chunk<ELFT>::ContentType::Note; + default: + return Chunk<ELFT>::ContentType::Unknown; + } +} + +template <class ELFT> int64_t Segment<ELFT>::atomflags() const { + switch (_atomflags) { + case DefinedAtom::permUnknown: + return permUnknown; + case DefinedAtom::permRWX: + return permRWX; + case DefinedAtom::permR_X: + return permRX; + case DefinedAtom::permR__: + return permR; + case DefinedAtom::permRW_L: + return permRWL; + case DefinedAtom::permRW_: + return permRW; + case DefinedAtom::perm___: + default: + return permNonAccess; + } +} + +/// \brief Check if the chunk needs to be aligned +template <class ELFT> bool Segment<ELFT>::needAlign(Chunk<ELFT> *chunk) const { + if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data && + _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) + return true; + return false; +} + +template <class ELFT> void ProgramHeaderSegment<ELFT>::finalize() { + // If the segment is of type Program Header, then the values fileOffset + // and the fileSize need to be picked up from the last section, the first + // section points to the ELF header and the second chunk points to the + // actual program headers + this->setFileOffset(this->_sections.back()->fileOffset()); + this->setVirtualAddr(this->_sections.back()->virtualAddr()); + this->_fsize = this->_sections.back()->fileSize(); + this->_msize = this->_sections.back()->memSize(); +} + +#define INSTANTIATE(klass) \ + template class klass<ELF32LE>; \ + template class klass<ELF32BE>; \ + template class klass<ELF64LE>; \ + template class klass<ELF64BE> + +INSTANTIATE(ExpressionChunk); +INSTANTIATE(ProgramHeaderSegment); +INSTANTIATE(Segment); +INSTANTIATE(SegmentSlice); + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/SegmentChunks.h b/lib/ReaderWriter/ELF/SegmentChunks.h index f2a975aaeed0..b444f44c04eb 100644 --- a/lib/ReaderWriter/ELF/SegmentChunks.h +++ b/lib/ReaderWriter/ELF/SegmentChunks.h @@ -11,16 +11,13 @@ #define LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H #include "Chunk.h" -#include "Layout.h" #include "SectionChunks.h" #include "Writer.h" #include "lld/Core/range.h" #include "lld/Core/Writer.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/Debug.h" #include "llvm/Support/ELF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" @@ -29,7 +26,7 @@ namespace lld { namespace elf { -template <typename ELFT> class DefaultLayout; +template <typename ELFT> class TargetLayout; /// \brief A segment can be divided into segment slices /// depending on how the segments can be split @@ -38,8 +35,6 @@ class SegmentSlice { public: typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; - SegmentSlice() { } - /// Set the start of the slice. void setStart(int32_t s) { _startSection = s; } @@ -49,12 +44,10 @@ public: // Return the fileOffset of the slice uint64_t fileOffset() const { return _offset; } - void setFileOffset(uint64_t offset) { _offset = offset; } // Return the size of the slice uint64_t fileSize() const { return _fsize; } - void setFileSize(uint64_t filesz) { _fsize = filesz; } // Return the start of the slice @@ -75,9 +68,7 @@ public: void setAlign(uint64_t align) { _alignment = align; } - static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) { - return a->startSection() < b->startSection(); - } + static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b); range<SectionIter> sections() { return _sections; } @@ -100,8 +91,8 @@ public: typedef typename std::vector<SegmentSlice<ELFT> *>::iterator SliceIter; typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; - Segment(const ELFLinkingContext &context, StringRef name, - const Layout::SegmentType type); + Segment(const ELFLinkingContext &ctx, StringRef name, + const typename TargetLayout<ELFT>::SegmentType type); /// \brief the Order of segments that appear in the output file enum SegmentOrder { @@ -144,37 +135,21 @@ public: // Write the Segment void write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer); + llvm::FileOutputBuffer &buffer) override; int64_t flags() const; + // Set segment flags directly. + void setSegmentFlags(uint64_t flags); + /// Prepend a generic chunk to the segment. void prepend(Chunk<ELFT> *c) { _sections.insert(_sections.begin(), c); } - /// Finalize the segment before assigning File Offsets / Virtual addresses - void doPreFlight() {} - /// Finalize the segment, before we want to write the segment header /// information - void finalize() { - // We want to finalize the segment values for now only for non loadable - // segments, since those values are not set in the Layout - if (_segmentType == llvm::ELF::PT_LOAD) - return; - // The size is the difference of the - // last section to the first section, especially for TLS because - // the TLS segment contains both .tdata/.tbss - this->setFileOffset(_sections.front()->fileOffset()); - this->setVirtualAddr(_sections.front()->virtualAddr()); - size_t startFileOffset = _sections.front()->fileOffset(); - size_t startAddr = _sections.front()->virtualAddr(); - for (auto ai : _sections) { - this->_fsize = ai->fileOffset() + ai->fileSize() - startFileOffset; - this->_msize = ai->virtualAddr() + ai->memSize() - startAddr; - } - } + void finalize() override; // For LLVM RTTI static bool classof(const Chunk<ELFT> *c) { @@ -185,76 +160,26 @@ public: int32_t sectionCount() const { return _sections.size(); } /// \brief, this function returns the type of segment (PT_*) - Layout::SegmentType segmentType() { return _segmentType; } + typename TargetLayout<ELFT>::SegmentType segmentType() const { + return _segmentType; + } /// \brief return the segment type depending on the content, /// If the content corresponds to Code, this will return Segment::Code /// If the content corresponds to Data, this will return Segment::Data /// If the content corresponds to TLS, this will return Segment::TLS - virtual int getContentType() const { - int64_t fl = flags(); - switch (_segmentType) { - case llvm::ELF::PT_LOAD: { - if (fl && llvm::ELF::PF_X) - return Chunk<ELFT>::ContentType::Code; - if (fl && llvm::ELF::PF_W) - return Chunk<ELFT>::ContentType::Data; - } - case llvm::ELF::PT_TLS: - return Chunk<ELFT>::ContentType::TLS; - case llvm::ELF::PT_NOTE: - return Chunk<ELFT>::ContentType::Note; - default: - return Chunk<ELFT>::ContentType::Unknown; - } - } - - int pageSize() const { return this->_context.getPageSize(); } + int getContentType() const override; + int pageSize() const { return this->_ctx.getPageSize(); } int rawflags() const { return _atomflags; } - - int64_t atomflags() const { - switch (_atomflags) { - - case DefinedAtom::permUnknown: - return permUnknown; - - case DefinedAtom::permRWX: - return permRWX; - - case DefinedAtom::permR_X: - return permRX; - - case DefinedAtom::permR__: - return permR; - - case DefinedAtom::permRW_L: - return permRWL; - - case DefinedAtom::permRW_: - return permRW; - - case DefinedAtom::perm___: - default: - return permNonAccess; - } - } - + int64_t atomflags() const; int64_t numSlices() const { return _segmentSlices.size(); } - range<SliceIter> slices() { return _segmentSlices; } - Chunk<ELFT> *firstSection() { return _sections[0]; } private: - /// \brief Check if the chunk needs to be aligned - bool needAlign(Chunk<ELFT> *chunk) const { - if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data && - _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) - return true; - return false; - } + bool needAlign(Chunk<ELFT> *chunk) const; // Cached value of outputMagic ELFLinkingContext::OutputMagic _outputMagic; @@ -263,9 +188,10 @@ protected: /// \brief Section or some other chunk type. std::vector<Chunk<ELFT> *> _sections; std::vector<SegmentSlice<ELFT> *> _segmentSlices; - Layout::SegmentType _segmentType; + typename TargetLayout<ELFT>::SegmentType _segmentType; uint64_t _flags; int64_t _atomflags; + bool _segmentFlags; llvm::BumpPtrAllocator _segmentAllocate; }; @@ -286,10 +212,9 @@ public: int getContentType() const override { return Chunk<ELFT>::ContentType::Unknown; } + void write(ELFWriter *, TargetLayout<ELFT> &, llvm::FileOutputBuffer &) override {} - void doPreFlight() override {} - void finalize() override {} std::error_code evalExpr(uint64_t &curPos) { return _linkerScriptSema.evalExpr(_expr, curPos); @@ -304,383 +229,18 @@ private: /// The segment doesn't contain any slice template <class ELFT> class ProgramHeaderSegment : public Segment<ELFT> { public: - ProgramHeaderSegment(const ELFLinkingContext &context) - : Segment<ELFT>(context, "PHDR", llvm::ELF::PT_PHDR) { + ProgramHeaderSegment(const ELFLinkingContext &ctx) + : Segment<ELFT>(ctx, "PHDR", llvm::ELF::PT_PHDR) { this->_alignment = 8; this->_flags = (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR); } /// Finalize the segment, before we want to write the segment header /// information - void finalize() { - // If the segment is of type Program Header, then the values fileOffset - // and the fileSize need to be picked up from the last section, the first - // section points to the ELF header and the second chunk points to the - // actual program headers - this->setFileOffset(this->_sections.back()->fileOffset()); - this->setVirtualAddr(this->_sections.back()->virtualAddr()); - this->_fsize = this->_sections.back()->fileSize(); - this->_msize = this->_sections.back()->memSize(); - } - + void finalize() override; }; -template <class ELFT> -Segment<ELFT>::Segment(const ELFLinkingContext &context, StringRef name, - const Layout::SegmentType type) - : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, context), - _segmentType(type), _flags(0), _atomflags(0) { - this->_alignment = 0; - this->_fsize = 0; - _outputMagic = context.getOutputMagic(); -} - -// This function actually is used, but not in all instantiations of Segment. -LLVM_ATTRIBUTE_UNUSED -static DefinedAtom::ContentPermissions toAtomPerms(uint64_t flags) { - switch (flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) { - case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR: - return DefinedAtom::permRWX; - case SHF_ALLOC | SHF_EXECINSTR: - return DefinedAtom::permR_X; - case SHF_ALLOC: - return DefinedAtom::permR__; - case SHF_ALLOC | SHF_WRITE: - return DefinedAtom::permRW_; - default: - return DefinedAtom::permUnknown; - } -} - -template <class ELFT> void Segment<ELFT>::append(Chunk<ELFT> *chunk) { - _sections.push_back(chunk); - Section<ELFT> *section = dyn_cast<Section<ELFT>>(chunk); - if (!section) - return; - if (_flags < section->getFlags()) - _flags |= section->getFlags(); - if (_atomflags < toAtomPerms(_flags)) - _atomflags = toAtomPerms(_flags); - if (this->_alignment < section->alignment()) - this->_alignment = section->alignment(); -} - -template <class ELFT> -bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) { - int64_t type1 = sega->segmentType(); - int64_t type2 = segb->segmentType(); - - if (type1 == type2) - return sega->atomflags() < segb->atomflags(); - - // The single PT_PHDR segment is required to precede any loadable - // segment. We simply make it always first. - if (type1 == llvm::ELF::PT_PHDR) - return true; - if (type2 == llvm::ELF::PT_PHDR) - return false; - - // The single PT_INTERP segment is required to precede any loadable - // segment. We simply make it always second. - if (type1 == llvm::ELF::PT_INTERP) - return true; - if (type2 == llvm::ELF::PT_INTERP) - return false; - - // We then put PT_LOAD segments before any other segments. - if (type1 == llvm::ELF::PT_LOAD) - return true; - if (type2 == llvm::ELF::PT_LOAD) - return false; - - // We put the PT_GNU_RELRO segment last, because that is where the - // dynamic linker expects to find it - if (type1 == llvm::ELF::PT_GNU_RELRO) - return false; - if (type2 == llvm::ELF::PT_GNU_RELRO) - return true; - - // We put the PT_TLS segment last except for the PT_GNU_RELRO - // segment, because that is where the dynamic linker expects to find - if (type1 == llvm::ELF::PT_TLS) - return false; - if (type2 == llvm::ELF::PT_TLS) - return true; - - // Otherwise compare the types to establish an arbitrary ordering. - // FIXME: Should figure out if we should just make all other types compare - // equal, but if so, we should probably do the same for atom flags and change - // users of this to use stable_sort. - return type1 < type2; -} - -template <class ELFT> -void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) { - uint64_t fileOffset = startOffset; - uint64_t curSliceFileOffset = fileOffset; - bool isDataPageAlignedForNMagic = false; - bool alignSegments = this->_context.alignSegments(); - uint64_t p_align = this->_context.getPageSize(); - uint64_t lastVirtualAddress = 0; - - this->setFileOffset(startOffset); - for (auto &slice : slices()) { - bool isFirstSection = true; - for (auto section : slice->sections()) { - // Handle linker script expressions, which may change the offset - if (!isFirstSection) - if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) - fileOffset += expr->virtualAddr() - lastVirtualAddress; - // Align fileoffset to the alignment of the section. - fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); - // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data - // to a page boundary - if (isFirstSection && - _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { - // Align to a page only if the output is not - // OutputMagic::NMAGIC/OutputMagic::OMAGIC - if (alignSegments) - fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); - else { - // Align according to ELF spec. - // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf - uint64_t virtualAddress = slice->virtualAddr(); - Section<ELFT> *sect = dyn_cast<Section<ELFT>>(section); - if (sect && sect->isLoadableSection() && - ((virtualAddress & (p_align - 1)) != - (fileOffset & (p_align - 1)))) - fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align) + - (virtualAddress % p_align); - } - } else if (!isDataPageAlignedForNMagic && needAlign(section)) { - fileOffset = - llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize()); - isDataPageAlignedForNMagic = true; - } - if (isFirstSection) { - slice->setFileOffset(fileOffset); - isFirstSection = false; - curSliceFileOffset = fileOffset; - } - section->setFileOffset(fileOffset); - fileOffset += section->fileSize(); - lastVirtualAddress = section->virtualAddr() + section->memSize(); - } - slice->setFileSize(fileOffset - curSliceFileOffset); - } - this->setFileSize(fileOffset - startOffset); -} - -/// \brief Assign virtual addresses to the slices -template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) { - int startSection = 0; - int currSection = 0; - SectionIter startSectionIter; - - // slice align is set to the max alignment of the chunks that are - // contained in the slice - uint64_t sliceAlign = 0; - // Current slice size - uint64_t curSliceSize = 0; - // Current Slice File Offset - uint64_t curSliceAddress = 0; - - startSectionIter = _sections.begin(); - startSection = 0; - bool isFirstSection = true; - bool isDataPageAlignedForNMagic = false; - uint64_t startAddr = addr; - SegmentSlice<ELFT> *slice = nullptr; - uint64_t tlsStartAddr = 0; - bool alignSegments = this->_context.alignSegments(); - StringRef prevOutputSectionName = StringRef(); - - for (auto si = _sections.begin(); si != _sections.end(); ++si) { - // If this is first section in the segment, page align the section start - // address. The linker needs to align the data section to a page boundary - // only if NMAGIC is set. - if (isFirstSection) { - isFirstSection = false; - if (alignSegments && - _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) - // Align to a page only if the output is not - // OutputMagic::NMAGIC/OutputMagic::OMAGIC - startAddr = - llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); - else if (!isDataPageAlignedForNMagic && needAlign(*si)) { - // If the linker outputmagic is set to OutputMagic::NMAGIC, align the - // Data to a page boundary. - startAddr = - llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); - isDataPageAlignedForNMagic = true; - } - // align the startOffset to the section alignment - uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); - // Handle linker script expressions, which *may update newAddr* if the - // expression assigns to "." - if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) - expr->evalExpr(newAddr); - curSliceAddress = newAddr; - sliceAlign = (*si)->alignment(); - (*si)->setVirtualAddr(curSliceAddress); - - // Handle TLS. - if (auto section = dyn_cast<Section<ELFT>>(*si)) { - if (section->getSegmentType() == llvm::ELF::PT_TLS) { - tlsStartAddr = - llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); - section->assignVirtualAddress(tlsStartAddr); - tlsStartAddr += (*si)->memSize(); - } else { - section->assignVirtualAddress(newAddr); - } - } - // TBSS section is special in that it doesn't contribute to memory of any - // segment. If we see a tbss section, don't add memory size to addr The - // fileOffset is automatically taken care of since TBSS section does not - // end up using file size - if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) - curSliceSize = (*si)->memSize(); - } else { - uint64_t curAddr = curSliceAddress + curSliceSize; - if (!isDataPageAlignedForNMagic && needAlign(*si)) { - // If the linker outputmagic is set to OutputMagic::NMAGIC, align the - // Data - // to a page boundary - curAddr = - llvm::RoundUpToAlignment(curAddr, this->_context.getPageSize()); - isDataPageAlignedForNMagic = true; - } - uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment()); - // Handle linker script expressions, which *may update newAddr* if the - // expression assigns to "." - if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) - expr->evalExpr(newAddr); - Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); - StringRef curOutputSectionName; - if (sec) - curOutputSectionName = sec->outputSectionName(); - else { - // If this is a linker script expression, propagate the name of the - // previous section instead - if (isa<ExpressionChunk<ELFT>>(*si)) - curOutputSectionName = prevOutputSectionName; - else - curOutputSectionName = (*si)->name(); - } - bool autoCreateSlice = true; - if (curOutputSectionName == prevOutputSectionName) - autoCreateSlice = false; - // If the newAddress computed is more than a page away, let's create - // a separate segment, so that memory is not used up while running. - // Dont create a slice, if the new section falls in the same output - // section as the previous section. - if (autoCreateSlice && - ((newAddr - curAddr) > this->_context.getPageSize()) && - (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) { - auto sliceIter = - std::find_if(_segmentSlices.begin(), _segmentSlices.end(), - [startSection](SegmentSlice<ELFT> *s) -> bool { - return s->startSection() == startSection; - }); - if (sliceIter == _segmentSlices.end()) { - slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) - SegmentSlice<ELFT>(); - _segmentSlices.push_back(slice); - } else { - slice = (*sliceIter); - } - slice->setStart(startSection); - slice->setSections(make_range(startSectionIter, si)); - slice->setMemSize(curSliceSize); - slice->setAlign(sliceAlign); - slice->setVirtualAddr(curSliceAddress); - // Start new slice - curSliceAddress = newAddr; - (*si)->setVirtualAddr(curSliceAddress); - startSectionIter = si; - startSection = currSection; - if (auto section = dyn_cast<Section<ELFT>>(*si)) - section->assignVirtualAddress(newAddr); - curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); - sliceAlign = (*si)->alignment(); - } else { - if (sliceAlign < (*si)->alignment()) - sliceAlign = (*si)->alignment(); - (*si)->setVirtualAddr(newAddr); - // Handle TLS. - if (auto section = dyn_cast<Section<ELFT>>(*si)) { - if (section->getSegmentType() == llvm::ELF::PT_TLS) { - tlsStartAddr = - llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); - section->assignVirtualAddress(tlsStartAddr); - tlsStartAddr += (*si)->memSize(); - } else { - section->assignVirtualAddress(newAddr); - } - } - // TBSS section is special in that it doesn't contribute to memory of - // any segment. If we see a tbss section, don't add memory size to addr - // The fileOffset is automatically taken care of since TBSS section does - // not end up using file size. - if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) - curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); - else - curSliceSize = newAddr - curSliceAddress; - } - prevOutputSectionName = curOutputSectionName; - } - currSection++; - } - auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), - [startSection](SegmentSlice<ELFT> *s) -> bool { - return s->startSection() == startSection; - }); - if (sliceIter == _segmentSlices.end()) { - slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) - SegmentSlice<ELFT>(); - _segmentSlices.push_back(slice); - } else { - slice = (*sliceIter); - } - slice->setStart(startSection); - slice->setVirtualAddr(curSliceAddress); - slice->setMemSize(curSliceSize); - slice->setSections(make_range(startSectionIter, _sections.end())); - slice->setAlign(sliceAlign); - - // Set the segment memory size and the virtual address. - this->setMemSize(curSliceAddress - startAddr + curSliceSize); - this->setVirtualAddr(curSliceAddress); - std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), - SegmentSlice<ELFT>::compare_slices); -} - -// Write the Segment -template <class ELFT> -void Segment<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, - llvm::FileOutputBuffer &buffer) { - for (auto slice : slices()) - for (auto section : slice->sections()) - section->write(writer, layout, buffer); -} - -template<class ELFT> -int64_t -Segment<ELFT>::flags() const { - int64_t fl = 0; - if (_flags & llvm::ELF::SHF_ALLOC) - fl |= llvm::ELF::PF_R; - if (_flags & llvm::ELF::SHF_WRITE) - fl |= llvm::ELF::PF_W; - if (_flags & llvm::ELF::SHF_EXECINSTR) - fl |= llvm::ELF::PF_X; - return fl; -} } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/TargetHandler.h b/lib/ReaderWriter/ELF/TargetHandler.h index ca7a442276d1..406ac670fc83 100644 --- a/lib/ReaderWriter/ELF/TargetHandler.h +++ b/lib/ReaderWriter/ELF/TargetHandler.h @@ -6,78 +6,27 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -/// -/// \file -/// \brief These interfaces provide target specific hooks to change the linker's -/// behaivor. -/// -//===----------------------------------------------------------------------===// #ifndef LLD_READER_WRITER_ELF_TARGET_HANDLER_H #define LLD_READER_WRITER_ELF_TARGET_HANDLER_H -#include "Layout.h" -#include "lld/Core/Atom.h" -#include "lld/Core/LLVM.h" -#include "lld/Core/LinkingContext.h" -#include "lld/Core/STDExtras.h" -#include "lld/ReaderWriter/ELFLinkingContext.h" -#include "llvm/ADT/Hashing.h" -#include "llvm/ADT/StringRef.h" +#include "lld/Core/Error.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FileOutputBuffer.h" -#include <memory> -#include <vector> namespace lld { namespace elf { -template <class ELFT> class DynamicTable; -template <class ELFT> class DynamicSymbolTable; -template <class ELFT> class ELFDefinedAtom; -template <class ELFT> class ELFReference; -class ELFWriter; -template <class ELFT> class ELFHeader; -template <class ELFT> class Section; -template <class ELFT> class TargetLayout; - -class TargetRelocationHandler { -public: - /// Constructor - TargetRelocationHandler() {} - virtual ~TargetRelocationHandler() {} - - virtual std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, - const Reference &) const = 0; -}; - -/// \brief TargetHandler contains all the information responsible to handle a -/// a particular target on ELF. A target might wish to override implementation -/// of creating atoms and how the atoms are written to the output file. -template <class ELFT> class TargetHandler : public TargetHandlerBase { -public: - /// The layout determined completely by the Target. - virtual TargetLayout<ELFT> &getTargetLayout() = 0; - - /// Determine how relocations need to be applied. - virtual const TargetRelocationHandler &getRelocationHandler() const = 0; - - /// How does the target deal with reading input files. - virtual std::unique_ptr<Reader> getObjReader() = 0; - - /// How does the target deal with reading dynamic libraries. - virtual std::unique_ptr<Reader> getDSOReader() = 0; - - /// How does the target deal with writing ELF output. - virtual std::unique_ptr<Writer> getWriter() = 0; -}; inline std::error_code make_unhandled_reloc_error() { - return make_dynamic_error_code(Twine("Unhandled reference type")); + return make_dynamic_error_code("Unhandled reference type"); } inline std::error_code make_out_of_range_reloc_error() { - return make_dynamic_error_code(Twine("Relocation out of range")); + return make_dynamic_error_code("Relocation out of range"); +} + +inline std::error_code make_unaligned_range_reloc_error() { + return make_dynamic_error_code("Relocation not aligned"); } } // end namespace elf diff --git a/lib/ReaderWriter/ELF/TargetLayout.cpp b/lib/ReaderWriter/ELF/TargetLayout.cpp new file mode 100644 index 000000000000..09c49f62d64f --- /dev/null +++ b/lib/ReaderWriter/ELF/TargetLayout.cpp @@ -0,0 +1,747 @@ +//===- lib/ReaderWriter/ELF/TargetLayout.cpp ------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TargetLayout.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" + +namespace lld { +namespace elf { + +template <class ELFT> +typename TargetLayout<ELFT>::SectionOrder +TargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) { + switch (contentType) { + case DefinedAtom::typeResolver: + case DefinedAtom::typeCode: + return llvm::StringSwitch<typename TargetLayout<ELFT>::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<typename TargetLayout<ELFT>::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<typename TargetLayout<ELFT>::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 TargetLayout<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 +TargetLayout<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> +typename TargetLayout<ELFT>::SegmentType +TargetLayout<ELFT>::getSegmentType(const 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 TargetLayout<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> * +TargetLayout<ELFT>::createSection(StringRef sectionName, int32_t contentType, + DefinedAtom::ContentPermissions permissions, + SectionOrder sectionOrder) { + return new (_allocator) AtomSection<ELFT>(_ctx, sectionName, contentType, + permissions, sectionOrder); +} + +template <class ELFT> +AtomSection<ELFT> * +TargetLayout<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 AtomLayout *> TargetLayout<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 (_ctx.isDynamicRelocation(*reloc)) { + getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc); + isLocalReloc = false; + } else if (_ctx.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 (_ctx.isCopyRelocation(*reloc)) { + _copiedDynSymNames.insert(definedAtom->name()); + continue; + } + + _referencedDynAtoms.insert(reloc->target()); + } + return section->appendAtom(atom); + } + + const AbsoluteAtom *absoluteAtom = cast<AbsoluteAtom>(atom); + // Absolute atoms are not part of any section, they are global for the whole + // link + _absoluteAtoms.push_back( + new (_allocator) AtomLayout(absoluteAtom, 0, absoluteAtom->value())); + return _absoluteAtoms.back(); +} + +/// Output sections with the same name into a OutputSection +template <class ELFT> void TargetLayout<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(section); + } +} + +template <class ELFT> +std::vector<typename TargetLayout<ELFT>::SegmentKey> +TargetLayout<ELFT>::getSegmentsForSection(const OutputSection<ELFT> *os, + const Section<ELFT> *sec) const { + std::vector<SegmentKey> segKeys; + auto phdrs = _linkerScriptSema.getPHDRsForOutputSection(os->name()); + if (!phdrs.empty()) { + if (phdrs.size() == 1 && phdrs[0]->isNone()) { + segKeys.emplace_back("NONE", llvm::ELF::PT_NULL, 0, false); + return segKeys; + } + + for (auto phdr : phdrs) { + segKeys.emplace_back(phdr->name(), phdr->type(), phdr->flags(), true); + } + return segKeys; + } + + uint64_t flags = getLookupSectionFlags(os); + int64_t segmentType = getSegmentType(sec); + StringRef segmentName = sec->segmentKindToStr(); + + // We need a separate segment for sections that don't have + // the segment type to be PT_LOAD + if (segmentType != llvm::ELF::PT_LOAD) + segKeys.emplace_back(segmentName, segmentType, flags, false); + + if (segmentType == llvm::ELF::PT_NULL) + return segKeys; + + // If the output magic is set to OutputMagic::NMAGIC or + // OutputMagic::OMAGIC, Place the data alongside text in one single + // segment + ELFLinkingContext::OutputMagic outputMagic = _ctx.getOutputMagic(); + if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC || + outputMagic == ELFLinkingContext::OutputMagic::OMAGIC) + flags = + llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE; + + segKeys.emplace_back("LOAD", llvm::ELF::PT_LOAD, flags, false); + return segKeys; +} + +template <class ELFT> +uint64_t +TargetLayout<ELFT>::getLookupSectionFlags(const OutputSection<ELFT> *os) const { + uint64_t flags = os->flags(); + if (!(flags & llvm::ELF::SHF_WRITE) && _ctx.mergeRODataToTextSegment()) + flags &= ~llvm::ELF::SHF_EXECINSTR; + + // Merge string sections into Data segment itself + flags &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE); + + // Merge the TLS section into the DATA segment itself + flags &= ~(llvm::ELF::SHF_TLS); + return flags; +} + +template <class ELFT> void TargetLayout<ELFT>::assignSectionsToSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsToSegments"); + // 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 section : osi->sections()) { + if (!hasOutputSegment(section)) + continue; + + osi->setLoadableSection(section->isLoadableSection()); + osi->setHasSegment(); + + auto segKeys = getSegmentsForSection(osi, section); + assert(!segKeys.empty() && "Must always be at least one segment"); + section->setSegmentType(segKeys[0]._type); + + for (auto key : segKeys) { + // Try to find non-load (real) segment type if possible + if (key._type != llvm::ELF::PT_LOAD) + section->setSegmentType(key._type); + + const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key, + nullptr); + std::pair<typename SegmentMapT::iterator, bool> segmentInsert( + _segmentMap.insert(currentSegment)); + Segment<ELFT> *segment; + if (!segmentInsert.second) { + segment = segmentInsert.first->second; + } else { + segment = new (_allocator) Segment<ELFT>(_ctx, key._name, key._type); + if (key._segmentFlags) + segment->setSegmentFlags(key._flags); + segmentInsert.first->second = segment; + _segments.push_back(segment); + } + if (key._type == llvm::ELF::PT_LOAD) { + // 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); + } + } + } + + // Default values if no linker script is available + bool hasProgramSegment = _ctx.isDynamic() && !_ctx.isDynamicLibrary(); + bool hasElfHeader = true; + bool hasProgramHeader = true; + uint64_t segmentFlags = 0; + + // Check if linker script has PHDRS and program segment defined + if (_linkerScriptSema.hasPHDRs()) { + if (auto p = _linkerScriptSema.getProgramPHDR()) { + hasProgramSegment = true; + hasElfHeader = p->hasFileHdr(); + hasProgramHeader = p->hasPHDRs(); + segmentFlags = p->flags(); + } else { + hasProgramSegment = false; + hasElfHeader = false; + hasProgramHeader = false; + } + } + + if (hasProgramSegment) { + Segment<ELFT> *segment = new (_allocator) ProgramHeaderSegment<ELFT>(_ctx); + _segments.push_back(segment); + if (segmentFlags) + segment->setSegmentFlags(segmentFlags); + if (hasElfHeader) + segment->append(_elfHeader); + if (hasProgramHeader) + segment->append(_programHeader); + } +} + +template <class ELFT> void TargetLayout<ELFT>::sortSegments() { + std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments); +} + +template <class ELFT> void TargetLayout<ELFT>::assignVirtualAddress() { + if (_segments.empty()) + return; + + sortSegments(); + + uint64_t baseAddress = _ctx.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 && TargetLayout<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 TargetLayout<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 && TargetLayout<ELFT>::hasOutputSegment(section)) + continue; + fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment()); + si->setFileOffset(fileoffset); + si->setVirtualAddr(0); + fileoffset += si->fileSize(); + } +} + +template <class ELFT> void TargetLayout<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> +const AtomLayout * +TargetLayout<ELFT>::findAtomLayoutByName(StringRef name) const { + for (auto sec : _sections) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (auto *al = section->findAtomLayoutByName(name)) + return al; + return nullptr; +} + +template <class ELFT> +void TargetLayout<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->_ctx, expr); + segment->append(expChunk); + } +} + +template <class ELFT> +RelocationTable<ELFT> *TargetLayout<ELFT>::getDynamicRelocationTable() { + if (!_dynamicRelocationTable) { + _dynamicRelocationTable = createRelocationTable( + _ctx.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn", + ORDER_DYNAMIC_RELOCS); + addSection(_dynamicRelocationTable.get()); + } + return _dynamicRelocationTable.get(); +} + +template <class ELFT> +RelocationTable<ELFT> *TargetLayout<ELFT>::getPLTRelocationTable() { + if (!_pltRelocationTable) { + _pltRelocationTable = createRelocationTable( + _ctx.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt", + ORDER_DYNAMIC_PLT_RELOCS); + addSection(_pltRelocationTable.get()); + } + return _pltRelocationTable.get(); +} + +template <class ELFT> uint64_t TargetLayout<ELFT>::getTLSSize() const { + for (const auto &phdr : *_programHeader) + if (phdr->p_type == llvm::ELF::PT_TLS) + return phdr->p_memsz; + return 0; +} + +template class TargetLayout<ELF32LE>; +template class TargetLayout<ELF32BE>; +template class TargetLayout<ELF64LE>; +template class TargetLayout<ELF64BE>; + +} // end namespace elf +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/TargetLayout.h b/lib/ReaderWriter/ELF/TargetLayout.h index ab7a7890a274..52512f8e279e 100644 --- a/lib/ReaderWriter/ELF/TargetLayout.h +++ b/lib/ReaderWriter/ELF/TargetLayout.h @@ -7,21 +7,320 @@ // //===----------------------------------------------------------------------===// -#ifndef LLD_READER_WRITER_ELF_TARGET_LAYOUT_H -#define LLD_READER_WRITER_ELF_TARGET_LAYOUT_H +#ifndef LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H +#define LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H -#include "DefaultLayout.h" -#include "lld/Core/LLVM.h" +#include "Atoms.h" +#include "HeaderChunks.h" +#include "SectionChunks.h" +#include "SegmentChunks.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include <unordered_map> namespace lld { namespace elf { -/// \brief The target can override certain functions in the DefaultLayout -/// class so that the order, the name of the section and the segment type could -/// be changed in the final layout -template <class ELFT> class TargetLayout : public DefaultLayout<ELFT> { + +/// \brief The TargetLayout 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 TargetLayout +/// class +template <class ELFT> class TargetLayout { +public: + typedef uint32_t SectionOrder; + typedef uint32_t SegmentType; + + // 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: - TargetLayout(ELFLinkingContext &context) : DefaultLayout<ELFT>(context) {} + + // 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; + + // Properties used during segment creation + struct SegmentKey { + SegmentKey(StringRef name, int64_t type, uint64_t flags, bool segFlags) + : _name(name), _type(type), _flags(flags), + _segmentFlags(segFlags && flags != 0) {} + StringRef _name = ""; + int64_t _type = 0; + uint64_t _flags = 0; + bool _segmentFlags = false; + }; + + struct SegmentKeyHash { + int64_t operator()(const SegmentKey &k) const { + return llvm::hash_combine(k._name, k._type, k._flags); + } + }; + + struct SegmentKeyEq { + bool operator()(const SegmentKey &lhs, const SegmentKey &rhs) const { + return ((lhs._name == rhs._name) && (lhs._type == rhs._type) && + (lhs._flags == rhs._flags)); + } + }; + + // Output Sections contain the map of Section names 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<SegmentKey, Segment<ELFT> *, SegmentKeyHash, + SegmentKeyEq> SegmentMapT; + + typedef typename std::vector<AtomLayout *>::iterator AbsoluteAtomIterT; + + typedef llvm::DenseSet<const Atom *> AtomSetT; + + TargetLayout(ELFLinkingContext &ctx) + : _ctx(ctx), _linkerScriptSema(ctx.linkerScriptSema()) {} + + virtual ~TargetLayout() = default; + + /// \brief Return the section order for a input section + virtual SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions); + + /// \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 SegmentType getSegmentType(const 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); + + /// \brief Append the Atom to the layout and create appropriate sections. + /// \returns A reference to the atom layout or an error. The atom layout will + /// be updated as linking progresses. + virtual ErrorOr<const AtomLayout *> addAtom(const Atom *atom); + + /// \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 + AtomLayout *findAbsoluteAtom(StringRef name) { + auto iter = std::find_if( + _absoluteAtoms.begin(), _absoluteAtoms.end(), + [=](const AtomLayout *a) { return a->_atom->name() == name; }); + if (iter == _absoluteAtoms.end()) + return nullptr; + return *iter; + } + + // Output sections with the same name into a OutputSection + void createOutputSections(); + + // Query for segments based on output and input sections + std::vector<SegmentKey> getSegmentsForSection(const OutputSection<ELFT> *os, + const Section<ELFT> *sec) const; + + /// \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); + + /// \brief associates a section to a segment + virtual void assignSectionsToSegments(); + + /// \brief associates a virtual address to the segment, section, and the atom + virtual void assignVirtualAddress(); + + 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(); + } + + /// \brief find the Atom in the current layout + virtual const AtomLayout *findAtomLayoutByName(StringRef name) const; + + 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(); + + /// \brief Get or create the PLT relocation table. Referenced by DT_JMPREL. + RelocationTable<ELFT> *getPLTRelocationTable(); + + uint64_t getTLSSize() const; + + bool isReferencedByDefinedAtom(const Atom *a) const { + return _referencedDynAtoms.count(a); + } + + bool isCopied(const SharedLibraryAtom *sla) const { + return _copiedDynSymNames.count(sla->name()); + } + +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>(_ctx, name, order)); + } + + virtual uint64_t getLookupSectionFlags(const OutputSection<ELFT> *os) const; + + /// \brief Sort segements stored in the _segments + virtual void sortSegments(); + +protected: + llvm::BumpPtrAllocator _allocator; + SectionMapT _sectionMap; + OutputSectionMapT _outputSectionMap; + 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<AtomLayout *> _absoluteAtoms; + AtomSetT _referencedDynAtoms; + llvm::StringSet<> _copiedDynSymNames; + ELFLinkingContext &_ctx; + script::Sema &_linkerScriptSema; }; + } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/Writer.cpp b/lib/ReaderWriter/ELF/Writer.cpp index 3071827e07d0..1c5d9766e9c5 100644 --- a/lib/ReaderWriter/ELF/Writer.cpp +++ b/lib/ReaderWriter/ELF/Writer.cpp @@ -16,8 +16,8 @@ using namespace llvm::object; namespace lld { -std::unique_ptr<Writer> createWriterELF(TargetHandlerBase *handler) { - return std::move(handler->getWriter()); +std::unique_ptr<Writer> createWriterELF(const ELFLinkingContext &ctx) { + return ctx.getTargetHandler().getWriter(); } } // namespace lld diff --git a/lib/ReaderWriter/ELF/Writer.h b/lib/ReaderWriter/ELF/Writer.h index 1e819467c558..8b3e8f90638a 100644 --- a/lib/ReaderWriter/ELF/Writer.h +++ b/lib/ReaderWriter/ELF/Writer.h @@ -19,15 +19,12 @@ namespace elf { /// various kinds of ELF files. class ELFWriter : public Writer { public: - ELFWriter() { } - -public: /// \brief builds the chunks that needs to be written to the output /// ELF file virtual void buildChunks(const File &file) = 0; /// \brief Writes the chunks into the output file specified by path - virtual std::error_code writeFile(const File &file, StringRef path) = 0; + std::error_code writeFile(const File &file, StringRef path) override = 0; /// \brief Get the virtual address of \p atom after layout. virtual uint64_t addressOfAtom(const Atom *atom) = 0; @@ -35,4 +32,4 @@ public: } // end namespace elf } // end namespace lld -#endif +#endif // LLD_READER_WRITER_ELF_WRITER_H diff --git a/lib/ReaderWriter/ELF/X86/Makefile b/lib/ReaderWriter/ELF/X86/Makefile deleted file mode 100644 index 058d5133eaba..000000000000 --- a/lib/ReaderWriter/ELF/X86/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/X86/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 := lldX86ELFTarget -USEDLIBS = lldCore.a -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h index 86376295bec4..dd2184d7201e 100644 --- a/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h @@ -15,50 +15,27 @@ namespace lld { namespace elf { -template <class ELFT> -class X86DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +class X86DynamicLibraryWriter : public DynamicLibraryWriter<ELF32LE> { public: - X86DynamicLibraryWriter(X86LinkingContext &context, - X86TargetLayout<ELFT> &layout); + X86DynamicLibraryWriter(X86LinkingContext &ctx, + TargetLayout<ELF32LE> &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; - X86LinkingContext &_context; - X86TargetLayout<ELFT> &_x86Layout; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; -template <class ELFT> -X86DynamicLibraryWriter<ELFT>::X86DynamicLibraryWriter( - X86LinkingContext &context, X86TargetLayout<ELFT> &layout) - : DynamicLibraryWriter<ELFT>(context, layout), - _gotFile(new GOTFile(context)), _context(context), _x86Layout(layout) {} +X86DynamicLibraryWriter::X86DynamicLibraryWriter(X86LinkingContext &ctx, + TargetLayout<ELF32LE> &layout) + : DynamicLibraryWriter(ctx, layout) {} -template <class ELFT> -bool X86DynamicLibraryWriter<ELFT>::createImplicitFiles( +void X86DynamicLibraryWriter::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/X86/X86ELFFile.h b/lib/ReaderWriter/ELF/X86/X86ELFFile.h deleted file mode 100644 index 621c38c43505..000000000000 --- a/lib/ReaderWriter/ELF/X86/X86ELFFile.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- lib/ReaderWriter/ELF/X86/X86ELFFile.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_X86_X86_ELF_FILE_H -#define LLD_READER_WRITER_ELF_X86_X86_ELF_FILE_H - -#include "ELFReader.h" - -namespace lld { -namespace elf { - -class X86LinkingContext; - -template <class ELFT> class X86ELFFile : public ELFFile<ELFT> { -public: - X86ELFFile(std::unique_ptr<MemoryBuffer> mb, X86LinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} - - static ErrorOr<std::unique_ptr<X86ELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, X86LinkingContext &ctx) { - return std::unique_ptr<X86ELFFile<ELFT>>( - new X86ELFFile<ELFT>(std::move(mb), ctx)); - } -}; - -template <class ELFT> class X86DynamicFile : public DynamicFile<ELFT> { -public: - X86DynamicFile(const X86LinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} -}; - -} // elf -} // lld - -#endif // LLD_READER_WRITER_ELF_X86_X86_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/X86/X86ELFReader.h b/lib/ReaderWriter/ELF/X86/X86ELFReader.h deleted file mode 100644 index 96186c5eb024..000000000000 --- a/lib/ReaderWriter/ELF/X86/X86ELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- lib/ReaderWriter/ELF/X86/X86ELFReader.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_X86_X86_ELF_READER_H -#define LLD_READER_WRITER_X86_X86_ELF_READER_H - -#include "ELFReader.h" -#include "X86ELFFile.h" - -namespace lld { -namespace elf { - -typedef llvm::object::ELFType<llvm::support::little, 2, false> X86ELFType; - -struct X86DynamicFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - X86LinkingContext &ctx) { - return lld::elf::X86DynamicFile<ELFT>::create(std::move(mb), ctx); - } -}; - -struct X86ELFFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - X86LinkingContext &ctx) { - return lld::elf::X86ELFFile<ELFT>::create(std::move(mb), ctx); - } -}; - -class X86ELFObjectReader - : public ELFObjectReader<X86ELFType, X86ELFFileCreateELFTraits, - X86LinkingContext> { -public: - X86ELFObjectReader(X86LinkingContext &ctx) - : ELFObjectReader<X86ELFType, X86ELFFileCreateELFTraits, - X86LinkingContext>(ctx, llvm::ELF::EM_386) {} -}; - -class X86ELFDSOReader - : public ELFDSOReader<X86ELFType, X86DynamicFileCreateELFTraits, - X86LinkingContext> { -public: - X86ELFDSOReader(X86LinkingContext &ctx) - : ELFDSOReader<X86ELFType, X86DynamicFileCreateELFTraits, - X86LinkingContext>(ctx, llvm::ELF::EM_386) {} -}; - -} // namespace elf -} // namespace lld - -#endif // LLD_READER_WRITER_X86_X86_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h index 68acc06c2261..70aabde3ad2c 100644 --- a/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h @@ -15,40 +15,22 @@ namespace lld { namespace elf { -template <class ELFT> -class X86ExecutableWriter : public ExecutableWriter<ELFT> { +class X86ExecutableWriter : public ExecutableWriter<ELF32LE> { public: - X86ExecutableWriter(X86LinkingContext &context, - X86TargetLayout<ELFT> &layout); + X86ExecutableWriter(X86LinkingContext &ctx, TargetLayout<ELF32LE> &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 ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); - } - - virtual void addDefaultAtoms() { - return ExecutableWriter<ELFT>::addDefaultAtoms(); - } - -private: - X86LinkingContext &_context; - X86TargetLayout<ELFT> &_x86Layout; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; -template <class ELFT> -X86ExecutableWriter<ELFT>::X86ExecutableWriter(X86LinkingContext &context, - X86TargetLayout<ELFT> &layout) - : ExecutableWriter<ELFT>(context, layout), _context(context), - _x86Layout(layout) {} +X86ExecutableWriter::X86ExecutableWriter(X86LinkingContext &ctx, + TargetLayout<ELF32LE> &layout) + : ExecutableWriter(ctx, layout) {} -template <class ELFT> -bool X86ExecutableWriter<ELFT>::createImplicitFiles( +void X86ExecutableWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { - ExecutableWriter<ELFT>::createImplicitFiles(result); - return true; + ExecutableWriter::createImplicitFiles(result); } } // namespace elf diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp index 26d715cf2953..dc45efc390fd 100644 --- a/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp @@ -14,15 +14,26 @@ #include "llvm/Support/ErrorOr.h" using namespace lld; +using namespace lld::elf; std::unique_ptr<ELFLinkingContext> -elf::X86LinkingContext::create(llvm::Triple triple) { +elf::createX86LinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::x86) - return std::unique_ptr<ELFLinkingContext>( - new elf::X86LinkingContext(triple)); + return llvm::make_unique<X86LinkingContext>(triple); return nullptr; } -elf::X86LinkingContext::X86LinkingContext(llvm::Triple triple) - : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( - new X86TargetHandler(*this))) {} +X86LinkingContext::X86LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, llvm::make_unique<X86TargetHandler>(*this)) {} + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/i386.def" +#undef ELF_RELOC + LLD_KIND_STRING_END +}; + +void X86LinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::x86, + kindStrings); +} diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.h b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h index ff424f411aae..f6ab3e980d7d 100644 --- a/lib/ReaderWriter/ELF/X86/X86LinkingContext.h +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h @@ -19,7 +19,9 @@ namespace elf { class X86LinkingContext final : public ELFLinkingContext { public: static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_386; } X86LinkingContext(llvm::Triple); + void registerRelocationNames(Registry &r) override; /// \brief X86 has only two relative relocation /// a) for supporting IFUNC relocs - R_386_IRELATIVE diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp index da5a24c6ec37..15774bc33123 100644 --- a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp @@ -15,7 +15,6 @@ using namespace lld; using namespace lld::elf; using namespace llvm::support::endian; -namespace { /// \brief R_386_32 - word32: S + A static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { int32_t result = (uint32_t)(S + A); @@ -25,33 +24,31 @@ static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { /// \brief R_386_PC32 - word32: S + A - P static int relocPC32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - uint32_t result = (uint32_t)((S + A) - P); + uint32_t result = (uint32_t)(S + A - P); write32le(location, result + read32le(location)); return 0; } -} std::error_code X86TargetRelocationHandler::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::x86); switch (ref.kindValue()) { case R_386_32: - reloc32(location, relocVAddress, targetVAddress, ref.addend()); + reloc32(loc, reloc, target, ref.addend()); break; case R_386_PC32: - relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + relocPC32(loc, reloc, target, ref.addend()); break; default: return make_unhandled_reloc_error(); } - return std::error_code(); } diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h index f161cdd55983..1131635c6735 100644 --- a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h @@ -10,16 +10,15 @@ #ifndef X86_X86_RELOCATION_HANDLER_H #define X86_X86_RELOCATION_HANDLER_H -#include "X86TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, false> X86ELFType; class X86TargetRelocationHandler final : public TargetRelocationHandler { public: std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; }; diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp index 22d918231424..c01ed7258f1c 100644 --- a/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp @@ -19,13 +19,11 @@ using namespace elf; using namespace llvm::ELF; std::unique_ptr<Writer> X86TargetHandler::getWriter() { - switch (_x86LinkingContext.getOutputELFType()) { + switch (_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>(new X86ExecutableWriter<X86ELFType>( - _x86LinkingContext, *_x86TargetLayout.get())); + return llvm::make_unique<X86ExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>(new X86DynamicLibraryWriter<X86ELFType>( - _x86LinkingContext, *_x86TargetLayout.get())); + return llvm::make_unique<X86DynamicLibraryWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_REL: llvm_unreachable("TODO: support -r mode"); default: @@ -33,21 +31,6 @@ std::unique_ptr<Writer> X86TargetHandler::getWriter() { } } -#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), - -const Registry::KindStrings X86TargetHandler::kindStrings[] = { -#include "llvm/Support/ELFRelocs/i386.def" - LLD_KIND_STRING_END -}; - -#undef ELF_RELOC - -void X86TargetHandler::registerRelocationNames(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::x86, - kindStrings); -} - -X86TargetHandler::X86TargetHandler(X86LinkingContext &context) - : _x86LinkingContext(context), - _x86TargetLayout(new X86TargetLayout<X86ELFType>(context)), - _x86RelocationHandler(new X86TargetRelocationHandler()) {} +X86TargetHandler::X86TargetHandler(X86LinkingContext &ctx) + : _ctx(ctx), _targetLayout(new TargetLayout<ELF32LE>(ctx)), + _relocationHandler(new X86TargetRelocationHandler()) {} diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.h b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h index 6c4026735419..fecf9abfc678 100644 --- a/lib/ReaderWriter/ELF/X86/X86TargetHandler.h +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h @@ -10,10 +10,8 @@ #ifndef LLD_READER_WRITER_ELF_X86_TARGET_HANDLER_H #define LLD_READER_WRITER_ELF_X86_TARGET_HANDLER_H -#include "DefaultTargetHandler.h" #include "TargetLayout.h" -#include "X86ELFFile.h" -#include "X86ELFReader.h" +#include "ELFReader.h" #include "X86RelocationHandler.h" namespace lld { @@ -21,41 +19,28 @@ namespace elf { class X86LinkingContext; -template <class ELFT> class X86TargetLayout : public TargetLayout<ELFT> { +class X86TargetHandler final : public TargetHandler { public: - X86TargetLayout(X86LinkingContext &context) : TargetLayout<ELFT>(context) {} -}; - -class X86TargetHandler final - : public DefaultTargetHandler<X86ELFType> { -public: - X86TargetHandler(X86LinkingContext &context); - - X86TargetLayout<X86ELFType> &getTargetLayout() override { - return *(_x86TargetLayout.get()); - } - - void registerRelocationNames(Registry ®istry) override; + X86TargetHandler(X86LinkingContext &ctx); - const X86TargetRelocationHandler &getRelocationHandler() const override { - return *(_x86RelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new X86ELFObjectReader(_x86LinkingContext)); + return llvm::make_unique<ELFReader<ELFFile<ELF32LE>>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new X86ELFDSOReader(_x86LinkingContext)); + return llvm::make_unique<ELFReader<DynamicFile<ELF32LE>>>(_ctx); } std::unique_ptr<Writer> getWriter() override; protected: - static const Registry::KindStrings kindStrings[]; - X86LinkingContext &_x86LinkingContext; - std::unique_ptr<X86TargetLayout<X86ELFType>> _x86TargetLayout; - std::unique_ptr<X86TargetRelocationHandler> _x86RelocationHandler; + X86LinkingContext &_ctx; + std::unique_ptr<TargetLayout<ELF32LE>> _targetLayout; + std::unique_ptr<X86TargetRelocationHandler> _relocationHandler; }; } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt index a85d2b504630..36ea839aa674 100644 --- a/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt +++ b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt @@ -3,6 +3,7 @@ add_llvm_library(lldX86_64ELFTarget X86_64TargetHandler.cpp X86_64RelocationHandler.cpp X86_64RelocationPass.cpp + X86_64SectionChunks.cpp LINK_LIBS lldELF lldReaderWriter diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp index dbbb3ad3bc90..cb3e819aff6b 100644 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp @@ -14,14 +14,14 @@ using namespace lld; using namespace elf; std::unique_ptr<ELFLinkingContext> -ExampleLinkingContext::create(llvm::Triple triple) { +elf::createExampleLinkingContext(llvm::Triple triple) { if (triple.getVendorName() == "example") return llvm::make_unique<ExampleLinkingContext>(triple); return nullptr; } ExampleLinkingContext::ExampleLinkingContext(llvm::Triple triple) - : X86_64LinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + : X86_64LinkingContext(triple, std::unique_ptr<TargetHandler>( new ExampleTargetHandler(*this))) { _outputELFType = llvm::ELF::ET_LOPROC; } diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp index b66b0d903f6a..89ec6671f3a0 100644 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp @@ -15,9 +15,8 @@ using namespace lld; using namespace elf; ExampleTargetHandler::ExampleTargetHandler(ExampleLinkingContext &c) - : X86_64TargetHandler(c), _exampleContext(c) {} + : X86_64TargetHandler(c), _ctx(c) {} std::unique_ptr<Writer> ExampleTargetHandler::getWriter() { - return std::unique_ptr<Writer>( - new X86_64ExecutableWriter(_exampleContext, *_x86_64TargetLayout)); + return llvm::make_unique<X86_64ExecutableWriter>(_ctx, *_targetLayout); } diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h index 19a642113359..46eade5864f9 100644 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h @@ -23,7 +23,7 @@ public: std::unique_ptr<Writer> getWriter() override; private: - ExampleLinkingContext &_exampleContext; + ExampleLinkingContext &_ctx; }; } // end namespace elf } // end namespace lld diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile deleted file mode 100644 index 8f0b0fead1f6..000000000000 --- a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/X86_64/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 := lldExampleSubTarget -USEDLIBS = lldX86_64ELFTarget.a -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86_64/Makefile b/lib/ReaderWriter/ELF/X86_64/Makefile deleted file mode 100644 index dbeb4d227050..000000000000 --- a/lib/ReaderWriter/ELF/X86_64/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- lld/lib/ReaderWriter/ELF/X86_64/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 := lldX86_64ELFTarget -USEDLIBS = lldCore.a - -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/X86_64/ - -PARALLEL_DIRS := ExampleSubTarget - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h index b996186115b6..f84f85223bfb 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h @@ -10,51 +10,33 @@ #define X86_64_DYNAMIC_LIBRARY_WRITER_H #include "DynamicLibraryWriter.h" -#include "X86_64ElfType.h" #include "X86_64LinkingContext.h" #include "X86_64TargetHandler.h" namespace lld { namespace elf { -class X86_64DynamicLibraryWriter : public DynamicLibraryWriter<X86_64ELFType> { +class X86_64DynamicLibraryWriter : public DynamicLibraryWriter<ELF64LE> { public: - X86_64DynamicLibraryWriter(X86_64LinkingContext &context, + X86_64DynamicLibraryWriter(X86_64LinkingContext &ctx, X86_64TargetLayout &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::finalizeDefaultAtomValues(); - } - - virtual void addDefaultAtoms() { - return DynamicLibraryWriter::addDefaultAtoms(); - } - -private: - class GOTFile : public SimpleFile { - public: - GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} - llvm::BumpPtrAllocator _alloc; - }; - - std::unique_ptr<GOTFile> _gotFile; + void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; }; X86_64DynamicLibraryWriter::X86_64DynamicLibraryWriter( - X86_64LinkingContext &context, X86_64TargetLayout &layout) - : DynamicLibraryWriter(context, layout), _gotFile(new GOTFile(context)) {} + X86_64LinkingContext &ctx, X86_64TargetLayout &layout) + : DynamicLibraryWriter(ctx, layout) {} -bool X86_64DynamicLibraryWriter::createImplicitFiles( +void X86_64DynamicLibraryWriter::createImplicitFiles( std::vector<std::unique_ptr<File>> &result) { DynamicLibraryWriter::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; + 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/X86_64/X86_64ELFFile.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h deleted file mode 100644 index d43840a63e7e..000000000000 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.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_X86_64_ELF_FILE_H -#define LLD_READER_WRITER_ELF_X86_64_ELF_FILE_H - -#include "ELFReader.h" - -namespace lld { -namespace elf { - -class X86_64LinkingContext; - -template <class ELFT> class X86_64ELFFile : public ELFFile<ELFT> { -public: - X86_64ELFFile(std::unique_ptr<MemoryBuffer> mb, X86_64LinkingContext &ctx) - : ELFFile<ELFT>(std::move(mb), ctx) {} - - static ErrorOr<std::unique_ptr<X86_64ELFFile>> - create(std::unique_ptr<MemoryBuffer> mb, X86_64LinkingContext &ctx) { - return std::unique_ptr<X86_64ELFFile<ELFT>>( - new X86_64ELFFile<ELFT>(std::move(mb), ctx)); - } -}; - -template <class ELFT> class X86_64DynamicFile : public DynamicFile<ELFT> { -public: - X86_64DynamicFile(const X86_64LinkingContext &context, StringRef name) - : DynamicFile<ELFT>(context, name) {} -}; - -} // elf -} // lld - -#endif // LLD_READER_WRITER_ELF_X86_64_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h deleted file mode 100644 index 9b1284c6dfa8..000000000000 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h +++ /dev/null @@ -1,62 +0,0 @@ -//===- lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.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_X86_64_X86_64_ELF_READER_H -#define LLD_READER_WRITER_X86_64_X86_64_ELF_READER_H - -#include "ELFReader.h" -#include "X86_64ELFFile.h" - -namespace lld { -namespace elf { - -typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; - -struct X86_64DynamicFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - X86_64LinkingContext &ctx) { - return lld::elf::X86_64DynamicFile<ELFT>::create(std::move(mb), ctx); - } -}; - -struct X86_64ELFFileCreateELFTraits { - typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; - - template <class ELFT> - static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, - X86_64LinkingContext &ctx) { - return lld::elf::X86_64ELFFile<ELFT>::create(std::move(mb), ctx); - } -}; - -class X86_64ELFObjectReader - : public ELFObjectReader<X86_64ELFType, X86_64ELFFileCreateELFTraits, - X86_64LinkingContext> { -public: - X86_64ELFObjectReader(X86_64LinkingContext &ctx) - : ELFObjectReader<X86_64ELFType, X86_64ELFFileCreateELFTraits, - X86_64LinkingContext>(ctx, llvm::ELF::EM_X86_64) {} -}; - -class X86_64ELFDSOReader - : public ELFDSOReader<X86_64ELFType, X86_64DynamicFileCreateELFTraits, - X86_64LinkingContext> { -public: - X86_64ELFDSOReader(X86_64LinkingContext &ctx) - : ELFDSOReader<X86_64ELFType, X86_64DynamicFileCreateELFTraits, - X86_64LinkingContext>(ctx, llvm::ELF::EM_X86_64) {} -}; - -} // namespace elf -} // namespace lld - -#endif // LLD_READER_WRITER_ELF_X86_64_X86_64_READER_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h b/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h deleted file mode 100644 index 0b982e7754e2..000000000000 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h +++ /dev/null @@ -1,21 +0,0 @@ -//===- lib/ReaderWriter/ELF/X86_64/X86_64ElfType.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_X86_64_X86_64_ELF_TYPE_H -#define LLD_READER_WRITER_ELF_X86_64_X86_64_ELF_TYPE_H - -#include "llvm/Object/ELF.h" - -namespace lld { -namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; -} -} - -#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h index f549ed6dcfcb..930a2de2a9e8 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h @@ -10,49 +10,45 @@ #define X86_64_EXECUTABLE_WRITER_H #include "ExecutableWriter.h" -#include "X86_64ElfType.h" #include "X86_64LinkingContext.h" namespace lld { namespace elf { -class X86_64ExecutableWriter : public ExecutableWriter<X86_64ELFType> { +class X86_64ExecutableWriter : public ExecutableWriter<ELF64LE> { public: - X86_64ExecutableWriter(X86_64LinkingContext &context, - X86_64TargetLayout &layout) - : ExecutableWriter(context, layout), _gotFile(new GOTFile(context)), - _context(context) {} + X86_64ExecutableWriter(X86_64LinkingContext &ctx, X86_64TargetLayout &layout) + : ExecutableWriter(ctx, layout), _targetLayout(layout) {} protected: // Add any runtime files and their atoms to the output - virtual bool - createImplicitFiles(std::vector<std::unique_ptr<File>> &result) { + void + createImplicitFiles(std::vector<std::unique_ptr<File>> &result) override { ExecutableWriter::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; + 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)); } - virtual void finalizeDefaultAtomValues() { - return ExecutableWriter::finalizeDefaultAtomValues(); + void buildDynamicSymbolTable(const File &file) override { + for (auto sec : this->_layout.sections()) { + if (auto section = dyn_cast<AtomSection<ELF64LE>>(sec)) { + for (const auto &atom : section->atoms()) { + if (_targetLayout.getGOTSection().hasGlobalGOTEntry(atom->_atom)) { + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + } + } + } + + ExecutableWriter<ELF64LE>::buildDynamicSymbolTable(file); } - virtual void addDefaultAtoms() { - return ExecutableWriter::addDefaultAtoms(); - } - -private: - class GOTFile : public SimpleFile { - public: - GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} - llvm::BumpPtrAllocator _alloc; - }; - - std::unique_ptr<GOTFile> _gotFile; - X86_64LinkingContext &_context; + X86_64TargetLayout &_targetLayout; }; } // namespace elf diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp index 6a8ce8bd6496..0dcd6ac6fbed 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp @@ -12,10 +12,10 @@ #include "X86_64TargetHandler.h" using namespace lld; -using namespace elf; +using namespace lld::elf; X86_64LinkingContext::X86_64LinkingContext( - llvm::Triple triple, std::unique_ptr<TargetHandlerBase> handler) + llvm::Triple triple, std::unique_ptr<TargetHandler> handler) : ELFLinkingContext(triple, std::move(handler)) {} X86_64LinkingContext::X86_64LinkingContext(llvm::Triple triple) @@ -30,9 +30,21 @@ void X86_64LinkingContext::addPasses(PassManager &pm) { } std::unique_ptr<ELFLinkingContext> -X86_64LinkingContext::create(llvm::Triple triple) { +elf::createX86_64LinkingContext(llvm::Triple triple) { if (triple.getArch() == llvm::Triple::x86_64) - return std::unique_ptr<ELFLinkingContext>( - new elf::X86_64LinkingContext(triple)); + return llvm::make_unique<X86_64LinkingContext>(triple); return nullptr; } + +static const Registry::KindStrings kindStrings[] = { +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), +#include "llvm/Support/ELFRelocs/x86_64.def" +#undef ELF_RELOC + LLD_KIND_STRING_ENTRY(LLD_R_X86_64_GOTRELINDEX), + LLD_KIND_STRING_END +}; + +void X86_64LinkingContext::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::x86_64, kindStrings); +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h index 2cc799a9c810..a5a7b3d21f4e 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h @@ -26,12 +26,15 @@ enum { class X86_64LinkingContext : public ELFLinkingContext { protected: - X86_64LinkingContext(llvm::Triple, std::unique_ptr<TargetHandlerBase>); + X86_64LinkingContext(llvm::Triple, std::unique_ptr<TargetHandler>); + public: static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + int getMachineType() const override { return llvm::ELF::EM_X86_64; } X86_64LinkingContext(llvm::Triple); void addPasses(PassManager &) override; + void registerRelocationNames(Registry &r) override; uint64_t getBaseAddress() const override { if (_baseAddress == 0) @@ -65,7 +68,7 @@ public: return false; } - virtual bool isPLTRelocation(const Reference &r) const override { + bool isPLTRelocation(const Reference &r) const override { if (r.kindNamespace() != Reference::KindNamespace::ELF) return false; assert(r.kindArch() == Reference::KindArch::x86_64); diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp index 8fd74f43bbd2..d56983d1e382 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp @@ -23,7 +23,7 @@ static void reloc64(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { /// \brief R_X86_64_PC32 - word32: S + A - P static void relocPC32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { - uint32_t result = (uint32_t)((S + A) - P); + uint32_t result = (uint32_t)(S + A - P); write32le(location, result + read32le(location)); } @@ -50,24 +50,24 @@ static void reloc16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { /// \brief R_X86_64_PC16 - word16: S + A - P static void relocPC16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { - uint16_t result = (uint16_t)((S + A) - P); + uint16_t result = (uint16_t)(S + A - P); write16le(location, result | read16le(location)); // TODO: Check for overflow. } /// \brief R_X86_64_PC64 - word64: S + A - P static void relocPC64(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { - int64_t result = (uint64_t)((S + A) - P); + int64_t result = (uint64_t)(S + A - P); write64le(location, result | read64le(location)); } std::error_code X86_64TargetRelocationHandler::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(); @@ -76,38 +76,34 @@ std::error_code X86_64TargetRelocationHandler::applyRelocation( case R_X86_64_NONE: break; case R_X86_64_64: - reloc64(location, relocVAddress, targetVAddress, ref.addend()); + reloc64(loc, reloc, target, ref.addend()); break; case R_X86_64_PC32: case R_X86_64_GOTPCREL: - relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + relocPC32(loc, reloc, target, ref.addend()); break; case R_X86_64_32: - reloc32(location, relocVAddress, targetVAddress, ref.addend()); + reloc32(loc, reloc, target, ref.addend()); break; case R_X86_64_32S: - reloc32S(location, relocVAddress, targetVAddress, ref.addend()); + reloc32S(loc, reloc, target, ref.addend()); break; case R_X86_64_16: - reloc16(location, relocVAddress, targetVAddress, ref.addend()); + reloc16(loc, reloc, target, ref.addend()); break; case R_X86_64_PC16: - relocPC16(location, relocVAddress, targetVAddress, ref.addend()); + relocPC16(loc, reloc, target, ref.addend()); break; - case R_X86_64_TPOFF64: case R_X86_64_DTPOFF32: - case R_X86_64_TPOFF32: { - _tlsSize = _x86_64Layout.getTLSSize(); - if (ref.kindValue() == R_X86_64_TPOFF32 || - ref.kindValue() == R_X86_64_DTPOFF32) { - write32le(location, targetVAddress - _tlsSize); - } else { - write64le(location, targetVAddress - _tlsSize); - } + case R_X86_64_TPOFF32: + _tlsSize = _layout.getTLSSize(); + write32le(loc, target - _tlsSize); + break; + case R_X86_64_GOTTPOFF: + relocPC32(loc, reloc, target, ref.addend()); break; - } case R_X86_64_TLSGD: { - relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + relocPC32(loc, reloc, target, ref.addend()); break; } case R_X86_64_TLSLD: { @@ -115,21 +111,20 @@ std::error_code X86_64TargetRelocationHandler::applyRelocation( // next relocation is a PC32 to __tls_get_addr... static uint8_t instr[] = { 0x66, 0x66, 0x66, 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00 }; - std::memcpy(location - 3, instr, sizeof(instr)); + std::memcpy(loc - 3, instr, sizeof(instr)); break; } case R_X86_64_PC64: - relocPC64(location, relocVAddress, targetVAddress, ref.addend()); + relocPC64(loc, reloc, target, ref.addend()); break; case LLD_R_X86_64_GOTRELINDEX: { const DefinedAtom *target = cast<const DefinedAtom>(ref.target()); for (const Reference *r : *target) { if (r->kindValue() == R_X86_64_JUMP_SLOT) { uint32_t index; - if (!_x86_64Layout.getPLTRelocationTable()->getRelocationIndex(*r, - index)) + if (!_layout.getPLTRelocationTable()->getRelocationIndex(*r, index)) llvm_unreachable("Relocation doesn't exist"); - reloc32(location, 0, index, 0); + reloc32(loc, 0, index, 0); break; } } @@ -142,6 +137,7 @@ std::error_code X86_64TargetRelocationHandler::applyRelocation( case R_X86_64_GLOB_DAT: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: + case R_X86_64_TPOFF64: break; default: return make_unhandled_reloc_error(); diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h index 9e2c2171015d..26382804549b 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h @@ -10,27 +10,25 @@ #ifndef X86_64_RELOCATION_HANDLER_H #define X86_64_RELOCATION_HANDLER_H -#include "X86_64TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" namespace lld { namespace elf { -typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; - class X86_64TargetLayout; class X86_64TargetRelocationHandler final : public TargetRelocationHandler { public: X86_64TargetRelocationHandler(X86_64TargetLayout &layout) - : _tlsSize(0), _x86_64Layout(layout) {} + : _tlsSize(0), _layout(layout) {} std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, - const lld::AtomLayout &, + const AtomLayout &, const Reference &) const override; private: // Cached size of the TLS segment. mutable uint64_t _tlsSize; - X86_64TargetLayout &_x86_64Layout; + X86_64TargetLayout &_layout; }; } // end namespace elf diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp index 0703927fd56c..a2f10dc08a4e 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp @@ -188,11 +188,12 @@ 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_X86_64_PC32); + /// \brief Create a TPOFF64 GOT entry. + std::error_code handleGOTTPOFF(const Reference &ref) { + if (isa<DefinedAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); + } + return std::error_code(); } /// \brief Create a TLS GOT entry with DTPMOD64/DTPOFF64 dynamic relocations. @@ -243,9 +244,7 @@ protected: } public: - RelocationPass(const ELFLinkingContext &ctx) - : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), - _got1(nullptr) {} + RelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} /// \brief Do the pass. /// @@ -255,45 +254,46 @@ 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(), "X86-64 GOT/PLT Pass"); // Process all references. - for (const auto &atom : mf->defined()) + for (const auto &atom : mf.defined()) for (const auto &ref : *atom) handleReference(*atom, *ref); // 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); } for (auto &got : _tlsGotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } + return std::error_code(); } protected: @@ -322,14 +322,14 @@ protected: std::vector<GOTAtom *> _tlsGotVector; /// \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; /// @} }; @@ -379,20 +379,20 @@ public: : RelocationPass(ctx) {} const PLT0Atom *getPLT0() { - if (_PLT0) - return _PLT0; + if (_plt0) + return _plt0; // Fill in the null entry. getNullGOT(); - _PLT0 = new (_file._alloc) X86_64PLT0Atom(_file); + _plt0 = new (_file._alloc) X86_64PLT0Atom(_file); _got0 = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); _got1 = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); - _PLT0->addReferenceELF_x86_64(R_X86_64_PC32, 2, _got0, -4); - _PLT0->addReferenceELF_x86_64(R_X86_64_PC32, 8, _got1, -4); + _plt0->addReferenceELF_x86_64(R_X86_64_PC32, 2, _got0, -4); + _plt0->addReferenceELF_x86_64(R_X86_64_PC32, 8, _got1, -4); #ifndef NDEBUG _got0->_name = "__got0"; _got1->_name = "__got1"; #endif - return _PLT0; + return _plt0; } const PLTAtom *getPLTEntry(const Atom *a) { diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp new file mode 100644 index 000000000000..28eb3e4244b6 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp @@ -0,0 +1,37 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86_64SectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +X86_64GOTSection::X86_64GOTSection(const ELFLinkingContext &ctx) + : AtomSection<ELF64LE>(ctx, ".got", DefinedAtom::typeGOT, DefinedAtom::permRW_, + TargetLayout<ELF64LE>::ORDER_GOT) { + this->_alignment = 8; +} + +const AtomLayout *X86_64GOTSection::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::x86_64); + if (r->kindValue() == R_X86_64_TPOFF64) + _tlsMap[r->target()] = _tlsMap.size(); + } + + return AtomSection<ELF64LE>::appendAtom(atom); +} + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.h b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.h new file mode 100644 index 000000000000..5208491eee55 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.h @@ -0,0 +1,36 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64SectionChunks.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_X86_64_X86_64_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_SECTION_CHUNKS_H + +#include "TargetLayout.h" + +namespace lld { +namespace elf { + +class X86_64GOTSection : public AtomSection<ELF64LE> { +public: + X86_64GOTSection(const ELFLinkingContext &ctx); + + 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/X86_64/X86_64TargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp index f35330eb25c0..599077ac33c5 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp @@ -16,37 +16,19 @@ using namespace lld; using namespace elf; -X86_64TargetHandler::X86_64TargetHandler(X86_64LinkingContext &context) - : _context(context), _x86_64TargetLayout(new X86_64TargetLayout(context)), - _x86_64RelocationHandler( - new X86_64TargetRelocationHandler(*_x86_64TargetLayout.get())) {} - -void X86_64TargetHandler::registerRelocationNames(Registry ®istry) { - registry.addKindTable(Reference::KindNamespace::ELF, - Reference::KindArch::x86_64, kindStrings); -} +X86_64TargetHandler::X86_64TargetHandler(X86_64LinkingContext &ctx) + : _ctx(ctx), _targetLayout(new X86_64TargetLayout(ctx)), + _relocationHandler(new X86_64TargetRelocationHandler(*_targetLayout)) {} std::unique_ptr<Writer> X86_64TargetHandler::getWriter() { - switch (this->_context.getOutputELFType()) { + switch (this->_ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: - return std::unique_ptr<Writer>( - new X86_64ExecutableWriter(_context, *_x86_64TargetLayout.get())); + return llvm::make_unique<X86_64ExecutableWriter>(_ctx, *_targetLayout); case llvm::ELF::ET_DYN: - return std::unique_ptr<Writer>( - new X86_64DynamicLibraryWriter(_context, *_x86_64TargetLayout.get())); + return llvm::make_unique<X86_64DynamicLibraryWriter>(_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 X86_64TargetHandler::kindStrings[] = { -#include "llvm/Support/ELFRelocs/x86_64.def" - LLD_KIND_STRING_ENTRY(LLD_R_X86_64_GOTRELINDEX), - LLD_KIND_STRING_END -}; - -#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h index 57da7bca01e6..6e3e58f8aed6 100644 --- a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h @@ -10,57 +10,92 @@ #ifndef LLD_READER_WRITER_ELF_X86_64_X86_64_TARGET_HANDLER_H #define LLD_READER_WRITER_ELF_X86_64_X86_64_TARGET_HANDLER_H -#include "DefaultTargetHandler.h" +#include "ELFReader.h" #include "TargetLayout.h" -#include "X86_64ELFFile.h" -#include "X86_64ELFReader.h" #include "X86_64LinkingContext.h" #include "X86_64RelocationHandler.h" +#include "X86_64SectionChunks.h" #include "lld/Core/Simple.h" namespace lld { namespace elf { -class X86_64TargetLayout : public TargetLayout<X86_64ELFType> { + + +class X86_64TargetLayout : public TargetLayout<ELF64LE> { public: - X86_64TargetLayout(X86_64LinkingContext &context) - : TargetLayout(context) {} + X86_64TargetLayout(X86_64LinkingContext &ctx) : TargetLayout(ctx), + _gotSection(new (this->_allocator) X86_64GOTSection(ctx)) {} + + AtomSection<ELF64LE> * + createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + TargetLayout<ELF64LE>::SectionOrder order) override { + if (type == DefinedAtom::typeGOT && name == ".got") + return _gotSection; + return TargetLayout<ELF64LE>::createSection(name, type, permissions, order); + } void finalizeOutputSectionLayout() override { - sortOutputSectionByPriority(".init_array", ".init_array"); - sortOutputSectionByPriority(".fini_array", ".fini_array"); + sortOutputSectionByPriority<ELF64LE>(".init_array"); + sortOutputSectionByPriority<ELF64LE>(".fini_array"); } -}; -class X86_64TargetHandler - : public DefaultTargetHandler<X86_64ELFType> { -public: - X86_64TargetHandler(X86_64LinkingContext &context); + const X86_64GOTSection &getGOTSection() const { return *_gotSection; } - X86_64TargetLayout &getTargetLayout() override { - return *(_x86_64TargetLayout.get()); +private: + uint32_t getPriority(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; } - void registerRelocationNames(Registry ®istry) override; + template <typename T> void sortOutputSectionByPriority(StringRef prefix) { + OutputSection<T> *section = findOutputSection(prefix); + if (!section) + return; + auto sections = section->sections(); + std::sort(sections.begin(), sections.end(), + [&](Chunk<T> *lhs, Chunk<T> *rhs) { + Section<T> *lhsSection = dyn_cast<Section<T>>(lhs); + Section<T> *rhsSection = dyn_cast<Section<T>>(rhs); + if (!lhsSection || !rhsSection) + return false; + StringRef lhsName = lhsSection->inputSectionName(); + StringRef rhsName = rhsSection->inputSectionName(); + if (!lhsName.startswith(prefix) || !rhsName.startswith(prefix)) + return false; + return getPriority(lhsName) < getPriority(rhsName); + }); + } + +private: + X86_64GOTSection *_gotSection; +}; + +class X86_64TargetHandler : public TargetHandler { +public: + X86_64TargetHandler(X86_64LinkingContext &ctx); - const X86_64TargetRelocationHandler &getRelocationHandler() const override { - return *(_x86_64RelocationHandler.get()); + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; } std::unique_ptr<Reader> getObjReader() override { - return std::unique_ptr<Reader>(new X86_64ELFObjectReader(_context)); + return llvm::make_unique<ELFReader<ELFFile<ELF64LE>>>(_ctx); } std::unique_ptr<Reader> getDSOReader() override { - return std::unique_ptr<Reader>(new X86_64ELFDSOReader(_context)); + return llvm::make_unique<ELFReader<DynamicFile<ELF64LE>>>(_ctx); } std::unique_ptr<Writer> getWriter() override; protected: - static const Registry::KindStrings kindStrings[]; - X86_64LinkingContext &_context; - std::unique_ptr<X86_64TargetLayout> _x86_64TargetLayout; - std::unique_ptr<X86_64TargetRelocationHandler> _x86_64RelocationHandler; + X86_64LinkingContext &_ctx; + std::unique_ptr<X86_64TargetLayout> _targetLayout; + std::unique_ptr<X86_64TargetRelocationHandler> _relocationHandler; }; } // end namespace elf diff --git a/lib/ReaderWriter/FileArchive.cpp b/lib/ReaderWriter/FileArchive.cpp index 3f38814ae18e..a09923f57d07 100644 --- a/lib/ReaderWriter/FileArchive.cpp +++ b/lib/ReaderWriter/FileArchive.cpp @@ -11,6 +11,7 @@ #include "lld/Core/LLVM.h" #include "lld/Core/LinkingContext.h" #include "lld/Core/Parallel.h" +#include "lld/Driver/Driver.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/Archive.h" @@ -48,9 +49,14 @@ public: if (member == _symbolMemberMap.end()) return nullptr; Archive::child_iterator ci = member->second; + if (ci->getError()) + return nullptr; // Don't return a member already returned - const char *memberStart = ci->getBuffer().data(); + ErrorOr<StringRef> buf = (*ci)->getBuffer(); + if (!buf) + return nullptr; + const char *memberStart = buf->data(); if (_membersInstantiated.count(memberStart)) return nullptr; if (dataSymbolOnly && !isDataSymbol(ci, name)) @@ -73,8 +79,11 @@ public: if (instantiateMember(ci, result)) return nullptr; - // give up the pointer so that this object no longer manages it - return result.release(); + File *file = result.get(); + _filesReturned.push_back(std::move(result)); + + // Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive + return file; } // Instantiate a member file containing a given symbol name. @@ -83,9 +92,14 @@ public: if (member == _symbolMemberMap.end()) return; Archive::child_iterator ci = member->second; + if (ci->getError()) + return; // Do nothing if a member is already instantiated. - const char *memberStart = ci->getBuffer().data(); + ErrorOr<StringRef> buf = (*ci)->getBuffer(); + if (!buf) + return; + const char *memberStart = buf->data(); if (_membersInstantiated.count(memberStart)) return; @@ -119,20 +133,20 @@ public: return std::error_code(); } - const atom_collection<DefinedAtom> &defined() const override { - return _definedAtoms; + const AtomVector<DefinedAtom> &defined() const override { + return _noDefinedAtoms; } - const atom_collection<UndefinedAtom> &undefined() const override { - return _undefinedAtoms; + const AtomVector<UndefinedAtom> &undefined() const override { + return _noUndefinedAtoms; } - const atom_collection<SharedLibraryAtom> &sharedLibrary() const override { - return _sharedLibraryAtoms; + const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { + return _noSharedLibraryAtoms; } - const atom_collection<AbsoluteAtom> &absolute() const override { - return _absoluteAtoms; + const AtomVector<AbsoluteAtom> &absolute() const override { + return _noAbsoluteAtoms; } /// Returns a set of all defined symbols in the archive. @@ -157,10 +171,12 @@ protected: } private: - std::error_code - instantiateMember(Archive::child_iterator member, - std::unique_ptr<File> &result) const { - ErrorOr<llvm::MemoryBufferRef> mbOrErr = member->getMemoryBufferRef(); + std::error_code instantiateMember(Archive::child_iterator cOrErr, + std::unique_ptr<File> &result) const { + if (std::error_code ec = cOrErr->getError()) + return ec; + Archive::child_iterator member = cOrErr->get(); + ErrorOr<llvm::MemoryBufferRef> mbOrErr = (*member)->getMemoryBufferRef(); if (std::error_code ec = mbOrErr.getError()) return ec; llvm::MemoryBufferRef mb = mbOrErr.get(); @@ -173,11 +189,11 @@ private: std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer( mb.getBuffer(), mb.getBufferIdentifier(), false)); - std::vector<std::unique_ptr<File>> files; - if (std::error_code ec = _registry.loadFile(std::move(memberMB), files)) + ErrorOr<std::unique_ptr<File>> fileOrErr = + _registry.loadFile(std::move(memberMB)); + if (std::error_code ec = fileOrErr.getError()) return ec; - assert(files.size() == 1); - result = std::move(files[0]); + result = std::move(fileOrErr.get()); if (std::error_code ec = result->parse()) return ec; result->setArchivePath(_archive->getFileName()); @@ -191,8 +207,11 @@ private: // Parses the given memory buffer as an object file, and returns true // code if the given symbol is a data symbol. If the symbol is not a data // symbol or does not exist, returns false. - bool isDataSymbol(Archive::child_iterator member, StringRef symbol) const { - ErrorOr<llvm::MemoryBufferRef> buf = member->getMemoryBufferRef(); + bool isDataSymbol(Archive::child_iterator cOrErr, StringRef symbol) const { + if (cOrErr->getError()) + return false; + Archive::child_iterator member = cOrErr->get(); + ErrorOr<llvm::MemoryBufferRef> buf = (*member)->getMemoryBufferRef(); if (buf.getError()) return false; std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer( @@ -205,19 +224,17 @@ private: for (SymbolRef sym : obj->symbols()) { // Skip until we find the symbol. - StringRef name; - if (sym.getName(name)) + ErrorOr<StringRef> name = sym.getName(); + if (!name) return false; - if (name != symbol) + if (*name != symbol) continue; uint32_t flags = sym.getFlags(); if (flags <= SymbolRef::SF_Undefined) continue; // Returns true if it's a data symbol. - SymbolRef::Type type; - if (sym.getType(type)) - return false; + SymbolRef::Type type = sym.getType(); if (type == SymbolRef::ST_Data) return true; } @@ -234,11 +251,12 @@ private: if (std::error_code ec = memberOrErr.getError()) return ec; Archive::child_iterator member = memberOrErr.get(); - DEBUG_WITH_TYPE( - "FileArchive", - llvm::dbgs() << llvm::format("0x%08llX ", member->getBuffer().data()) - << "'" << name << "'\n"); - _symbolMemberMap[name] = member; + DEBUG_WITH_TYPE("FileArchive", + llvm::dbgs() + << llvm::format("0x%08llX ", + (*member)->getBuffer()->data()) + << "'" << name << "'\n"); + _symbolMemberMap.insert(std::make_pair(name, member)); } return std::error_code(); } @@ -251,33 +269,27 @@ private: std::unique_ptr<Archive> _archive; MemberMap _symbolMemberMap; InstantiatedSet _membersInstantiated; - atom_collection_vector<DefinedAtom> _definedAtoms; - atom_collection_vector<UndefinedAtom> _undefinedAtoms; - atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; - atom_collection_vector<AbsoluteAtom> _absoluteAtoms; bool _logLoading; std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers; std::map<const char *, std::unique_ptr<Future<File *>>> _preloaded; std::mutex _mutex; + FileVector _filesReturned; }; class ArchiveReader : public Reader { public: ArchiveReader(bool logLoading) : _logLoading(logLoading) {} - bool canParse(file_magic magic, StringRef, - const MemoryBuffer &) const override { - return (magic == llvm::sys::fs::file_magic::archive); + bool canParse(file_magic magic, MemoryBufferRef) const override { + return magic == llvm::sys::fs::file_magic::archive; } - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®, - std::vector<std::unique_ptr<File>> &result) const override { + ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb, + const Registry ®) const override { StringRef path = mb->getBufferIdentifier(); - std::unique_ptr<FileArchive> file( - new FileArchive(std::move(mb), reg, path, _logLoading)); - result.push_back(std::move(file)); - return std::error_code(); + std::unique_ptr<File> ret = + llvm::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading); + return std::move(ret); } private: diff --git a/lib/ReaderWriter/LinkerScript.cpp b/lib/ReaderWriter/LinkerScript.cpp index 56194cae5e72..67822dc48fe6 100644 --- a/lib/ReaderWriter/LinkerScript.cpp +++ b/lib/ReaderWriter/LinkerScript.cpp @@ -1,4 +1,4 @@ -//===- ReaderWriter/LinkerScript.cpp --------------------------------------===// +//===- ReaderWriter/LinkerScript.cpp ----------------------------*- C++ -*-===// // // The LLVM Linker // @@ -14,6 +14,11 @@ #include "lld/ReaderWriter/LinkerScript.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ELF.h" + namespace lld { namespace script { void Token::dump(raw_ostream &os) const { @@ -63,6 +68,9 @@ void Token::dump(raw_ostream &os) const { CASE(kw_entry) CASE(kw_exclude_file) CASE(kw_extern) + CASE(kw_filehdr) + CASE(kw_fill) + CASE(kw_flags) CASE(kw_group) CASE(kw_hidden) CASE(kw_input) @@ -70,6 +78,7 @@ void Token::dump(raw_ostream &os) const { CASE(kw_length) CASE(kw_memory) CASE(kw_origin) + CASE(kw_phdrs) CASE(kw_provide) CASE(kw_provide_hidden) CASE(kw_only_if_ro) @@ -100,7 +109,7 @@ static llvm::ErrorOr<uint64_t> parseDecimal(StringRef str) { for (auto &c : str) { res *= 10; if (c < '0' || c > '9') - return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); res += c - '0'; } return res; @@ -111,7 +120,7 @@ static llvm::ErrorOr<uint64_t> parseOctal(StringRef str) { for (auto &c : str) { res <<= 3; if (c < '0' || c > '7') - return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); res += c - '0'; } return res; @@ -122,7 +131,7 @@ static llvm::ErrorOr<uint64_t> parseBinary(StringRef str) { for (auto &c : str) { res <<= 1; if (c != '0' && c != '1') - return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); res += c - '0'; } return res; @@ -139,7 +148,7 @@ static llvm::ErrorOr<uint64_t> parseHex(StringRef str) { else if (c >= 'A' && c <= 'F') res += c - 'A' + 10; else - return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); } return res; } @@ -469,6 +478,9 @@ void Lexer::lex(Token &tok) { .Case("ENTRY", Token::kw_entry) .Case("EXCLUDE_FILE", Token::kw_exclude_file) .Case("EXTERN", Token::kw_extern) + .Case("FILEHDR", Token::kw_filehdr) + .Case("FILL", Token::kw_fill) + .Case("FLAGS", Token::kw_flags) .Case("GROUP", Token::kw_group) .Case("HIDDEN", Token::kw_hidden) .Case("INPUT", Token::kw_input) @@ -486,6 +498,7 @@ void Lexer::lex(Token &tok) { .Case("OUTPUT_ARCH", Token::kw_output_arch) .Case("OUTPUT_FORMAT", Token::kw_output_format) .Case("OVERLAY", Token::kw_overlay) + .Case("PHDRS", Token::kw_phdrs) .Case("PROVIDE", Token::kw_provide) .Case("PROVIDE_HIDDEN", Token::kw_provide_hidden) .Case("SEARCH_DIR", Token::kw_search_dir) @@ -544,14 +557,14 @@ void Lexer::skipWhitespace() { // Constant functions void Constant::dump(raw_ostream &os) const { os << _num; } -ErrorOr<int64_t> Constant::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr<int64_t> Constant::evalExpr(const SymbolTableTy &symbolTable) const { return _num; } // Symbol functions void Symbol::dump(raw_ostream &os) const { os << _name; } -ErrorOr<int64_t> Symbol::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr<int64_t> Symbol::evalExpr(const SymbolTableTy &symbolTable) const { auto it = symbolTable.find(_name); if (it == symbolTable.end()) return LinkerScriptReaderError::unknown_symbol_in_expr; @@ -569,7 +582,8 @@ void FunctionCall::dump(raw_ostream &os) const { os << ")"; } -ErrorOr<int64_t> FunctionCall::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr<int64_t> +FunctionCall::evalExpr(const SymbolTableTy &symbolTable) const { return LinkerScriptReaderError::unrecognized_function_in_expr; } @@ -584,7 +598,7 @@ void Unary::dump(raw_ostream &os) const { os << ")"; } -ErrorOr<int64_t> Unary::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr<int64_t> Unary::evalExpr(const SymbolTableTy &symbolTable) const { auto child = _child->evalExpr(symbolTable); if (child.getError()) return child.getError(); @@ -654,7 +668,7 @@ void BinOp::dump(raw_ostream &os) const { os << ")"; } -ErrorOr<int64_t> BinOp::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr<int64_t> BinOp::evalExpr(const SymbolTableTy &symbolTable) const { auto lhs = _lhs->evalExpr(symbolTable); if (lhs.getError()) return lhs.getError(); @@ -695,7 +709,7 @@ void TernaryConditional::dump(raw_ostream &os) const { } ErrorOr<int64_t> -TernaryConditional::evalExpr(SymbolTableTy &symbolTable) const { +TernaryConditional::evalExpr(const SymbolTableTy &symbolTable) const { auto conditional = _conditional->evalExpr(symbolTable); if (conditional.getError()) return conditional.getError(); @@ -857,6 +871,12 @@ void InputSectionsCmd::dump(raw_ostream &os) const { os << ")"; } +void FillCmd::dump(raw_ostream &os) const { + os << "FILL("; + dumpByteStream(os, StringRef((const char *)_bytes.begin(), _bytes.size())); + os << ")"; +} + // OutputSectionDescription functions void OutputSectionDescription::dump(raw_ostream &os) const { if (_discard) @@ -909,6 +929,9 @@ void OutputSectionDescription::dump(raw_ostream &os) const { } os << " }"; + for (auto && phdr : _phdrs) + os << " : " << phdr; + if (_fillStream.size() > 0) { os << " ="; dumpByteStream(os, _fillStream); @@ -918,6 +941,37 @@ void OutputSectionDescription::dump(raw_ostream &os) const { } } +// Special header that discards output sections assigned to it. +static const PHDR PHDR_NONE("NONE", 0, false, false, nullptr, 0); + +bool PHDR::isNone() const { + return this == &PHDR_NONE; +} + +void PHDR::dump(raw_ostream &os) const { + os << _name << " " << _type; + if (_includeFileHdr) + os << " FILEHDR"; + if (_includePHDRs) + os << " PHDRS"; + if (_at) { + os << " AT ("; + _at->dump(os); + os << ")"; + } + if (_flags) + os << " FLAGS (" << _flags << ")"; + os << ";\n"; +} + +void PHDRS::dump(raw_ostream &os) const { + os << "PHDRS\n{\n"; + for (auto &&phdr : _phdrs) { + phdr->dump(os); + } + os << "}\n"; +} + // Sections functions void Sections::dump(raw_ostream &os) const { os << "SECTIONS\n{\n"; @@ -965,7 +1019,6 @@ void Extern::dump(raw_ostream &os) const { os << ")\n"; } - // Parser functions std::error_code Parser::parse() { // Get the first token. @@ -1024,6 +1077,13 @@ std::error_code Parser::parse() { _script._commands.push_back(entry); break; } + case Token::kw_phdrs: { + PHDRS *phdrs = parsePHDRS(); + if (!phdrs) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(phdrs); + break; + } case Token::kw_search_dir: { SearchDir *searchDir = parseSearchDir(); if (!searchDir) @@ -1122,7 +1182,7 @@ const Expression *Parser::parseExprOperand() { case Token::identifier: { if (peek()._kind== Token::l_paren) return parseFunctionCall(); - Symbol *sym = new (_alloc) Symbol(*this, _tok._range); + auto *sym = new (_alloc) Symbol(*this, _tok._range); consumeToken(); return sym; } @@ -1140,7 +1200,7 @@ const Expression *Parser::parseExprOperand() { error(_tok, "Unrecognized number constant"); return nullptr; } - Constant *c = new (_alloc) Constant(*this, *val); + auto *c = new (_alloc) Constant(*this, *val); consumeToken(); return c; } @@ -1588,7 +1648,7 @@ llvm::ErrorOr<InputSectionsCmd::VectorTy> Parser::parseExcludeFile() { if (!expectAndConsume(Token::l_paren, "expected (")) return llvm::ErrorOr<InputSectionsCmd::VectorTy>( - std::make_error_code(std::errc::io_error)); + make_error_code(llvm::errc::io_error)); while (_tok._kind == Token::identifier) { res.push_back(new (_alloc) InputSectionName(*this, _tok._range, true)); @@ -1597,7 +1657,7 @@ llvm::ErrorOr<InputSectionsCmd::VectorTy> Parser::parseExcludeFile() { if (!expectAndConsume(Token::r_paren, "expected )")) return llvm::ErrorOr<InputSectionsCmd::VectorTy>( - std::make_error_code(std::errc::io_error)); + make_error_code(llvm::errc::io_error)); return llvm::ErrorOr<InputSectionsCmd::VectorTy>(std::move(res)); } @@ -1793,6 +1853,46 @@ const InputSectionsCmd *Parser::parseInputSectionsCmd() { archiveSortMode, inputSections); } +const FillCmd *Parser::parseFillCmd() { + assert(_tok._kind == Token::kw_fill && "Expected FILL!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + SmallVector<uint8_t, 8> storage; + + // If the expression is just a number, it's arbitrary length. + if (_tok._kind == Token::number && peek()._kind == Token::r_paren) { + if (_tok._range.size() > 2 && _tok._range.startswith("0x")) { + StringRef num = _tok._range.substr(2); + for (char c : num) { + unsigned nibble = llvm::hexDigitValue(c); + if (nibble == -1u) + goto not_simple_hex; + storage.push_back(nibble); + } + + if (storage.size() % 2 != 0) + storage.insert(storage.begin(), 0); + + // Collapse nibbles. + for (std::size_t i = 0, e = storage.size() / 2; i != e; ++i) + storage[i] = (storage[i * 2] << 4) + storage[(i * 2) + 1]; + + storage.resize(storage.size() / 2); + } + } +not_simple_hex: + + const Expression *expr = parseExpression(); + if (!expr) + return nullptr; + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + + return new(getAllocator()) FillCmd(*this, storage); +} + const OutputSectionDescription *Parser::parseOutputSectionDescription() { assert((_tok._kind == Token::kw_discard || _tok._kind == Token::identifier) && "Expected /DISCARD/ or identifier!"); @@ -1892,6 +1992,12 @@ const OutputSectionDescription *Parser::parseOutputSectionDescription() { break; } break; + case Token::kw_fill: + if (const Command *cmd = parseFillCmd()) + outputSectionCommands.push_back(cmd); + else + return nullptr; + break; case Token::kw_keep: case Token::star: case Token::colon: @@ -1921,6 +2027,17 @@ const OutputSectionDescription *Parser::parseOutputSectionDescription() { if (!expectAndConsume(Token::r_brace, "expected }")) return nullptr; + SmallVector<StringRef, 2> phdrs; + while (_tok._kind == Token::colon) { + consumeToken(); + if (_tok._kind != Token::identifier) { + error(_tok, "expected program header name"); + return nullptr; + } + phdrs.push_back(_tok._range); + consumeToken(); + } + if (_tok._kind == Token::equal) { consumeToken(); if (_tok._kind != Token::number || !_tok._range.startswith_lower("0x")) { @@ -1945,7 +2062,7 @@ const OutputSectionDescription *Parser::parseOutputSectionDescription() { return new (_alloc) OutputSectionDescription( *this, sectionName, address, align, subAlign, at, fillExpr, fillStream, - alignWithInput, discard, constraint, outputSectionCommands); + alignWithInput, discard, constraint, outputSectionCommands, phdrs); } const Overlay *Parser::parseOverlay() { @@ -1954,6 +2071,141 @@ const Overlay *Parser::parseOverlay() { return nullptr; } +const PHDR *Parser::parsePHDR() { + assert(_tok._kind == Token::identifier && "Expected identifier!"); + + StringRef name = _tok._range; + consumeToken(); + + uint64_t type; + + switch (_tok._kind) { + case Token::identifier: + case Token::number: + case Token::l_paren: { + const Expression *expr = parseExpression(); + if (!expr) + return nullptr; + Expression::SymbolTableTy PHDRTypes; +#define PHDR_INSERT(x) PHDRTypes.insert(std::make_pair(#x, llvm::ELF::x)) + PHDR_INSERT(PT_NULL); + PHDR_INSERT(PT_LOAD); + PHDR_INSERT(PT_DYNAMIC); + PHDR_INSERT(PT_INTERP); + PHDR_INSERT(PT_NOTE); + PHDR_INSERT(PT_SHLIB); + PHDR_INSERT(PT_PHDR); + PHDR_INSERT(PT_TLS); + PHDR_INSERT(PT_LOOS); + PHDR_INSERT(PT_GNU_EH_FRAME); + PHDR_INSERT(PT_GNU_STACK); + PHDR_INSERT(PT_GNU_RELRO); + PHDR_INSERT(PT_SUNW_EH_FRAME); + PHDR_INSERT(PT_SUNW_UNWIND); + PHDR_INSERT(PT_HIOS); + PHDR_INSERT(PT_LOPROC); + PHDR_INSERT(PT_ARM_ARCHEXT); + PHDR_INSERT(PT_ARM_EXIDX); + PHDR_INSERT(PT_ARM_UNWIND); + PHDR_INSERT(PT_MIPS_REGINFO); + PHDR_INSERT(PT_MIPS_RTPROC); + PHDR_INSERT(PT_MIPS_OPTIONS); + PHDR_INSERT(PT_MIPS_ABIFLAGS); + PHDR_INSERT(PT_HIPROC); +#undef PHDR_INSERT + auto t = expr->evalExpr(PHDRTypes); + if (t == LinkerScriptReaderError::unknown_symbol_in_expr) { + error(_tok, "Unknown type"); + return nullptr; + } + if (!t) + return nullptr; + type = *t; + break; + } + default: + error(_tok, "expected identifier or expression"); + return nullptr; + } + + uint64_t flags = 0; + const Expression *flagsExpr = nullptr; + bool includeFileHdr = false; + bool includePHDRs = false; + + while (_tok._kind != Token::semicolon) { + switch (_tok._kind) { + case Token::kw_filehdr: + if (includeFileHdr) { + error(_tok, "Duplicate FILEHDR attribute"); + return nullptr; + } + includeFileHdr = true; + consumeToken(); + break; + case Token::kw_phdrs: + if (includePHDRs) { + error(_tok, "Duplicate PHDRS attribute"); + return nullptr; + } + includePHDRs = true; + consumeToken(); + break; + case Token::kw_flags: { + if (flagsExpr) { + error(_tok, "Duplicate FLAGS attribute"); + return nullptr; + } + consumeToken(); + if (!expectAndConsume(Token::l_paren, "Expected (")) + return nullptr; + flagsExpr = parseExpression(); + if (!flagsExpr) + return nullptr; + auto f = flagsExpr->evalExpr(); + if (!f) + return nullptr; + flags = *f; + if (!expectAndConsume(Token::r_paren, "Expected )")) + return nullptr; + } break; + default: + error(_tok, "Unexpected token"); + return nullptr; + } + } + + if (!expectAndConsume(Token::semicolon, "Expected ;")) + return nullptr; + + return new (getAllocator()) + PHDR(name, type, includeFileHdr, includePHDRs, nullptr, flags); +} + +PHDRS *Parser::parsePHDRS() { + assert(_tok._kind == Token::kw_phdrs && "Expected PHDRS!"); + consumeToken(); + if (!expectAndConsume(Token::l_brace, "expected {")) + return nullptr; + + SmallVector<const PHDR *, 8> phdrs; + + while (true) { + if (_tok._kind == Token::identifier) { + const PHDR *phdr = parsePHDR(); + if (!phdr) + return nullptr; + phdrs.push_back(phdr); + } else + break; + } + + if (!expectAndConsume(Token::r_brace, "expected }")) + return nullptr; + + return new (getAllocator()) PHDRS(*this, phdrs); +} + Sections *Parser::parseSections() { assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!"); consumeToken(); @@ -2101,8 +2353,7 @@ Memory *Parser::parseMemory() { if (!length) return nullptr; - MemoryBlock *block = - new (_alloc) MemoryBlock(name, attrs, origin, length); + auto *block = new (_alloc) MemoryBlock(name, attrs, origin, length); blocks.push_back(block); } else { unrecognizedToken = true; @@ -2142,14 +2393,22 @@ Extern *Parser::parseExtern() { } // Sema member functions -Sema::Sema() - : _scripts(), _layoutCommands(), _memberToLayoutOrder(), - _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), - _deliveredExprs(), _symbolTable() {} +Sema::Sema() : _programPHDR(nullptr) {} + +std::error_code Sema::perform() { + llvm::StringMap<const PHDR *> phdrs; -void Sema::perform() { - for (auto &parser : _scripts) - perform(parser->get()); + for (auto &parser : _scripts) { + for (const Command *c : parser->get()->_commands) { + if (const auto *sec = dyn_cast<Sections>(c)) { + linearizeAST(sec); + } else if (const auto *ph = dyn_cast<PHDRS>(c)) { + if (auto ec = collectPHDRs(ph, phdrs)) + return ec; + } + } + } + return buildSectionToPHDR(phdrs); } bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const { @@ -2254,6 +2513,15 @@ uint64_t Sema::getLinkerScriptExprValue(StringRef name) const { return it->second; } +bool Sema::hasPHDRs() const { return !_sectionToPHDR.empty(); } + +std::vector<const PHDR *> Sema::getPHDRsForOutputSection(StringRef name) const { + auto vec = _sectionToPHDR.lookup(name); + return std::vector<const PHDR *>(std::begin(vec), std::end(vec)); +} + +const PHDR *Sema::getProgramPHDR() const { return _programPHDR; } + void Sema::dump() const { raw_ostream &os = llvm::outs(); os << "Linker script semantics dump\n"; @@ -2287,7 +2555,7 @@ static bool wildcardMatch(StringRef pattern, StringRef name) { switch (*j) { case '*': while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1), - name.drop_front(i - name.begin() + 1))) { + name.drop_front(i - name.begin()))) { if (i == name.end()) return false; ++i; @@ -2295,6 +2563,7 @@ static bool wildcardMatch(StringRef pattern, StringRef name) { break; case '?': // Matches any character + ++i; break; case '[': { // Matches a range of characters specified between brackets @@ -2307,20 +2576,22 @@ static bool wildcardMatch(StringRef pattern, StringRef name) { return false; j = pattern.begin() + end; + ++i; break; } case '\\': ++j; if (*j != *i) return false; + ++i; break; default: // No wildcard character means we must match exactly the same char if (*j != *i) return false; + ++i; break; } - ++i; } // If our pattern has't consumed the entire string, it is not a match @@ -2376,11 +2647,10 @@ int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const { // If we still couldn't find a rule for this input section, try to match // wildcards - for (auto I = _memberNameWildcards.begin(), E = _memberNameWildcards.end(); - I != E; ++I) { - if (!wildcardMatch(I->first, key.memberPath)) + for (const auto &I : _memberNameWildcards) { + if (!wildcardMatch(I.first, key.memberPath)) continue; - int order = I->second; + int order = I.second; int exprOrder = -1; if ((exprOrder = matchSectionName(order, key)) >= 0) { @@ -2490,6 +2760,74 @@ bool Sema::localCompare(int order, const SectionKey &lhs, return false; } +std::error_code Sema::collectPHDRs(const PHDRS *ph, + llvm::StringMap<const PHDR *> &phdrs) { + bool loadFound = false; + for (auto *p : *ph) { + phdrs[p->name()] = p; + + switch (p->type()) { + case llvm::ELF::PT_PHDR: + if (_programPHDR != nullptr) + return LinkerScriptReaderError::extra_program_phdr; + if (loadFound) + return LinkerScriptReaderError::misplaced_program_phdr; + if (!p->hasPHDRs()) + return LinkerScriptReaderError::program_phdr_wrong_phdrs; + _programPHDR = p; + break; + case llvm::ELF::PT_LOAD: + // Program header, if available, should have program header table + // mapped in the first loadable segment. + if (!loadFound && _programPHDR && !p->hasPHDRs()) + return LinkerScriptReaderError::program_phdr_wrong_phdrs; + loadFound = true; + break; + } + } + return std::error_code(); +} + +std::error_code Sema::buildSectionToPHDR(llvm::StringMap<const PHDR *> &phdrs) { + const bool noPhdrs = phdrs.empty(); + + // Add NONE header to the map provided there's no user-defined + // header with the same name. + if (!phdrs.count(PHDR_NONE.name())) + phdrs[PHDR_NONE.name()] = &PHDR_NONE; + + // Match output sections to available headers. + llvm::SmallVector<const PHDR *, 2> phdrsCur, phdrsLast { &PHDR_NONE }; + for (const Command *cmd : _layoutCommands) { + auto osd = dyn_cast<OutputSectionDescription>(cmd); + if (!osd || osd->isDiscarded()) + continue; + + phdrsCur.clear(); + for (StringRef name : osd->PHDRs()) { + auto it = phdrs.find(name); + if (it == phdrs.end()) { + return LinkerScriptReaderError::unknown_phdr_ids; + } + phdrsCur.push_back(it->second); + } + + // If no headers and no errors - insert empty headers set. + // If the current set of headers is empty, then use the last non-empty + // set. Otherwise mark the current set to be the last non-empty set for + // successors. + if (noPhdrs) + phdrsCur.clear(); + else if (phdrsCur.empty()) + phdrsCur = phdrsLast; + else + phdrsLast = phdrsCur; + + _sectionToPHDR[osd->name()] = phdrsCur; + } + return std::error_code(); +} + static bool hasWildcard(StringRef name) { for (auto ch : name) if (ch == '*' || ch == '?' || ch == '[' || ch == '\\') @@ -2553,12 +2891,5 @@ void Sema::linearizeAST(const Sections *sections) { } } -void Sema::perform(const LinkerScript *ls) { - for (const Command *c : ls->_commands) { - if (const Sections *sec = dyn_cast<Sections>(c)) - linearizeAST(sec); - } -} - -} // End namespace script +} // end namespace script } // end namespace lld diff --git a/lib/ReaderWriter/MachO/ArchHandler.h b/lib/ReaderWriter/MachO/ArchHandler.h index 7f0961ebc807..120f5dfd4cd2 100644 --- a/lib/ReaderWriter/MachO/ArchHandler.h +++ b/lib/ReaderWriter/MachO/ArchHandler.h @@ -49,6 +49,12 @@ public: return false; } + /// Used by TLVPass to locate TLV References. + virtual bool isTLVAccess(const Reference &) const { return false; } + + /// Used by the TLVPass to update TLV References. + virtual void updateReferenceToTLV(const Reference *) {} + /// Used by ShimPass to insert shims in branches that switch mode. virtual bool isNonCallBranch(const Reference &) = 0; diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp index 43f88a1d30d8..4e15a2d434c6 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -28,8 +28,8 @@ using llvm::support::little32_t; class ArchHandler_arm : public ArchHandler { public: - ArchHandler_arm(); - virtual ~ArchHandler_arm(); + ArchHandler_arm() = default; + ~ArchHandler_arm() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } @@ -202,10 +202,6 @@ private: // ArchHandler_arm //===----------------------------------------------------------------------===// -ArchHandler_arm::ArchHandler_arm() { } - -ArchHandler_arm::~ArchHandler_arm() { } - const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(modeThumbCode), @@ -463,7 +459,6 @@ bool ArchHandler_arm::isArmMovt(uint32_t instruction) { return (instruction & 0x0FF00000) == 0x03400000; } - uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { assert(isThumbMovw(instruction) || isThumbMovt(instruction)); uint32_t i = ((instruction & 0x00000400) >> 10); @@ -480,7 +475,6 @@ uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { return (imm4 << 12) | imm12; } - uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { assert(isThumbMovw(instr) || isThumbMovt(instr)); uint32_t imm4 = (word & 0xF000) >> 12; @@ -497,7 +491,6 @@ uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; } - uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { // The assembler often adds one to the address of a thumb function. // We need to undo that so it does not look like an addend. @@ -619,7 +612,7 @@ std::error_code ArchHandler_arm::getReferenceInfo( *addend += (clearThumbBit(instruction, *target) - reloc.value); return std::error_code(); default: - return make_dynamic_error_code(Twine("unsupported arm relocation type")); + return make_dynamic_error_code("unsupported arm relocation type"); } return std::error_code(); } @@ -777,7 +770,7 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, pointerDiff = true; break; default: - return make_dynamic_error_code(Twine("unsupported arm relocation pair")); + return make_dynamic_error_code("unsupported arm relocation pair"); } const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; std::error_code ec; @@ -800,8 +793,8 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, if (ec) return ec; if (scatterable && (fromTarget != inAtom)) - return make_dynamic_error_code(Twine("SECTDIFF relocation where " - "subtrahend label is not in atom")); + return make_dynamic_error_code( + "SECTDIFF relocation where subtrahend label is not in atom"); *kind = delta32; value = clearThumbBit(instruction, *target); *addend = (int32_t)(value - (toAddress - fixupAddress)); @@ -815,29 +808,28 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, if (ec) return ec; if (fromTarget != inAtom) - return make_dynamic_error_code( - Twine("ARM_RELOC_HALF_SECTDIFF relocation " - "where subtrahend label is not in atom")); + return make_dynamic_error_code("ARM_RELOC_HALF_SECTDIFF relocation " + "where subtrahend label is not in atom"); other16 = (reloc2.offset & 0xFFFF); if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) - return make_dynamic_error_code(Twine("expected movt instruction")); + return make_dynamic_error_code("expected movt instruction"); } else { if (!isThumbMovw(instruction)) - return make_dynamic_error_code(Twine("expected movw instruction")); + return make_dynamic_error_code("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) - return make_dynamic_error_code(Twine("expected movt instruction")); + return make_dynamic_error_code("expected movt instruction"); } else { if (!isArmMovw(instruction)) - return make_dynamic_error_code(Twine("expected movw instruction")); + return make_dynamic_error_code("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } @@ -854,22 +846,22 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) - return make_dynamic_error_code(Twine("expected movt instruction")); + return make_dynamic_error_code("expected movt instruction"); } else { if (!isThumbMovw(instruction)) - return make_dynamic_error_code(Twine("expected movw instruction")); + return make_dynamic_error_code("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) - return make_dynamic_error_code(Twine("expected movt instruction")); + return make_dynamic_error_code("expected movt instruction"); } else { if (!isArmMovw(instruction)) - return make_dynamic_error_code(Twine("expected movw instruction")); + return make_dynamic_error_code("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } @@ -1041,7 +1033,6 @@ void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, } } - bool ArchHandler_arm::useExternalRelocationTo(const Atom &target) { // Undefined symbols are referenced via external relocations. if (isa<UndefinedAtom>(&target)) @@ -1410,7 +1401,6 @@ bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { return false; } - class Thumb2ToArmShimAtom : public SimpleDefinedAtom { public: Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName, @@ -1433,9 +1423,7 @@ public: return DefinedAtom::typeCode; } - Alignment alignment() const override { - return Alignment(2); - } + Alignment alignment() const override { return 4; } uint64_t size() const override { return 12; @@ -1458,7 +1446,6 @@ private: StringRef _name; }; - class ArmToThumbShimAtom : public SimpleDefinedAtom { public: ArmToThumbShimAtom(MachOFile &file, StringRef targetName, @@ -1479,9 +1466,7 @@ public: return DefinedAtom::typeCode; } - Alignment alignment() const override { - return Alignment(2); - } + Alignment alignment() const override { return 4; } uint64_t size() const override { return 16; @@ -1515,7 +1500,6 @@ const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); } - std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() { return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm()); } diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp index fd9984b89ce6..5e5a426b310b 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp @@ -31,8 +31,8 @@ using llvm::support::little64_t; class ArchHandler_arm64 : public ArchHandler { public: - ArchHandler_arm64(); - virtual ~ArchHandler_arm64(); + ArchHandler_arm64() = default; + ~ArchHandler_arm64() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } @@ -50,6 +50,9 @@ public: case gotOffset12: canBypassGOT = true; return true; + case delta32ToGOT: + canBypassGOT = false; + return true; case imageOffsetGot: canBypassGOT = false; return true; @@ -74,6 +77,9 @@ public: const_cast<Reference *>(ref)->setKindValue(targetNowGOT ? offset12scale8 : addOffset12); break; + case delta32ToGOT: + const_cast<Reference *>(ref)->setKindValue(delta32); + break; case imageOffsetGot: const_cast<Reference *>(ref)->setKindValue(imageOffset); break; @@ -213,10 +219,6 @@ private: static uint32_t setImm12(uint32_t instr, uint32_t offset); }; -ArchHandler_arm64::ArchHandler_arm64() {} - -ArchHandler_arm64::~ArchHandler_arm64() {} - const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch26), @@ -437,7 +439,7 @@ std::error_code ArchHandler_arm64::getReferenceInfo( *addend = 0; return std::error_code(); default: - return make_dynamic_error_code(Twine("unsupported arm64 relocation type")); + return make_dynamic_error_code("unsupported arm64 relocation type"); } } @@ -480,6 +482,9 @@ std::error_code ArchHandler_arm64::getPairReferenceInfo( *kind = delta64; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; + // The offsets of the 2 relocations must match + if (reloc1.offset != reloc2.offset) + return make_dynamic_error_code("paired relocs must have the same offset"); *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; return std::error_code(); case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | @@ -491,7 +496,7 @@ std::error_code ArchHandler_arm64::getPairReferenceInfo( *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; return std::error_code(); default: - return make_dynamic_error_code(Twine("unsupported arm64 relocation pair")); + return make_dynamic_error_code("unsupported arm64 relocation pair"); } } @@ -669,7 +674,7 @@ void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref, *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; case negDelta32: - *loc32 = fixupAddress - inAtomAddress + ref.addend(); + *loc32 = fixupAddress - targetAddress + ref.addend(); return; case pointer64ToGOT: *loc64 = 0; diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp index 19c8780e707a..7aac2584d078 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -30,8 +30,8 @@ using llvm::support::little32_t; class ArchHandler_x86 : public ArchHandler { public: - ArchHandler_x86(); - virtual ~ArchHandler_x86(); + ArchHandler_x86() = default; + ~ArchHandler_x86() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } @@ -49,9 +49,11 @@ public: bool needsCompactUnwind() override { return false; } + Reference::KindValue imageOffsetKind() override { return invalid; } + Reference::KindValue imageOffsetKindIndirect() override { return invalid; } @@ -68,7 +70,6 @@ public: return invalid; } - uint32_t dwarfCompactUnwindType() override { return 0x04000000U; } @@ -169,10 +170,6 @@ private: // ArchHandler_x86 //===----------------------------------------------------------------------===// -ArchHandler_x86::ArchHandler_x86() {} - -ArchHandler_x86::~ArchHandler_x86() { } - const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(modeCode), @@ -334,7 +331,7 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc, *addend = *(const ulittle32_t *)fixupContent - reloc.value; break; default: - return make_dynamic_error_code(Twine("unsupported i386 relocation type")); + return make_dynamic_error_code("unsupported i386 relocation type"); } return std::error_code(); } @@ -376,8 +373,8 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, return ec; if (fromTarget != inAtom) { if (*target != inAtom) - return make_dynamic_error_code(Twine("SECTDIFF relocation where " - "neither target is in atom")); + return make_dynamic_error_code( + "SECTDIFF relocation where neither target is in atom"); *kind = negDelta32; *addend = toAddress - value - fromAddress; *target = fromTarget; @@ -400,7 +397,7 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, return std::error_code(); break; default: - return make_dynamic_error_code(Twine("unsupported i386 relocation type")); + return make_dynamic_error_code("unsupported i386 relocation type"); } } @@ -545,7 +542,6 @@ bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) { return false; } - void ArchHandler_x86::appendSectionRelocations( const DefinedAtom &atom, uint64_t atomSectionOffset, @@ -633,7 +629,6 @@ void ArchHandler_x86::appendSectionRelocations( } } - std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() { return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86()); } diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index 81fe1af42d7e..c57a00359e33 100644 --- a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -30,8 +30,8 @@ using llvm::support::little64_t; class ArchHandler_x86_64 : public ArchHandler { public: - ArchHandler_x86_64(); - virtual ~ArchHandler_x86_64(); + ArchHandler_x86_64() = default; + ~ArchHandler_x86_64() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } @@ -59,6 +59,19 @@ public: } } + bool isTLVAccess(const Reference &ref) const override { + assert(ref.kindNamespace() == Reference::KindNamespace::mach_o); + assert(ref.kindArch() == Reference::KindArch::x86_64); + return ref.kindValue() == ripRel32Tlv; + } + + void updateReferenceToTLV(const Reference *ref) override { + assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); + assert(ref->kindArch() == Reference::KindArch::x86_64); + assert(ref->kindValue() == ripRel32Tlv); + const_cast<Reference*>(ref)->setKindValue(ripRel32); + } + /// Used by GOTPass to update GOT References void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); @@ -82,9 +95,11 @@ public: bool needsCompactUnwind() override { return true; } + Reference::KindValue imageOffsetKind() override { return imageOffset; } + Reference::KindValue imageOffsetKindIndirect() override { return imageOffsetGot; } @@ -168,8 +183,12 @@ private: ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip) ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip) ripRel32Anon, /// ex: movq L1(%rip), %rax + ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip) + ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip) + ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip) ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip) + ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi pointer64, /// ex: .quad _foo pointer64Anon, /// ex: .quad L1 delta64, /// ex: .quad _foo - . @@ -192,6 +211,8 @@ private: /// relocatable object (yay for implicit contracts!). unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to /// refer to __eh_frame entry. + tlvInitSectionOffset /// Location contains offset tlv init-value atom + /// within the __thread_data section. }; Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); @@ -209,18 +230,18 @@ private: uint64_t inAtomAddress); }; - -ArchHandler_x86_64::ArchHandler_x86_64() { } - -ArchHandler_x86_64::~ArchHandler_x86_64() { } - const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch32), LLD_KIND_STRING_ENTRY(ripRel32), LLD_KIND_STRING_ENTRY(ripRel32Minus1), LLD_KIND_STRING_ENTRY(ripRel32Minus2), LLD_KIND_STRING_ENTRY(ripRel32Minus4), - LLD_KIND_STRING_ENTRY(ripRel32Anon), LLD_KIND_STRING_ENTRY(ripRel32GotLoad), + LLD_KIND_STRING_ENTRY(ripRel32Anon), + LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon), + LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon), + LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon), + LLD_KIND_STRING_ENTRY(ripRel32GotLoad), LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea), - LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(lazyPointer), + LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(ripRel32Tlv), + LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), @@ -229,6 +250,7 @@ const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), + LLD_KIND_STRING_ENTRY(tlvInitSectionOffset), LLD_KIND_STRING_END }; @@ -301,14 +323,22 @@ ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) { return ripRel32Anon; case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4: return ripRel32Minus1; + case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4: + return ripRel32Minus1Anon; case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4: return ripRel32Minus2; + case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4: + return ripRel32Minus2Anon; case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4: return ripRel32Minus4; + case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4: + return ripRel32Minus4Anon; case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4: return ripRel32GotLoad; case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4: return ripRel32Got; + case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4: + return ripRel32Tlv; case X86_64_RELOC_UNSIGNED | rExtern | rLength8: return pointer64; case X86_64_RELOC_UNSIGNED | rLength8: @@ -331,7 +361,7 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, typedef std::error_code E; *kind = kindFromReloc(reloc); if (*kind == invalid) - return make_dynamic_error_code(Twine("unknown type")); + return make_dynamic_error_code("unknown type"); const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; switch (*kind) { @@ -359,16 +389,34 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, case ripRel32Anon: targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); + case ripRel32Minus1Anon: + targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + case ripRel32Minus2Anon: + targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + case ripRel32Minus4Anon: + targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32GotLoad: case ripRel32Got: + case ripRel32Tlv: if (E ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; return std::error_code(); + case tlvInitSectionOffset: case pointer64: if (E ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; - *addend = *(const little64_t *)fixupContent; + // If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's + // initial value) we need to handle it specially. + if (inAtom->contentType() == DefinedAtom::typeThunkTLV && + offsetInAtom == 16) { + *kind = tlvInitSectionOffset; + assert(*addend == 0 && "TLV-init has non-zero addend?"); + } else + *addend = *(const little64_t *)fixupContent; return std::error_code(); case pointer64Anon: targetAddress = *(const little64_t *)fixupContent; @@ -413,7 +461,7 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, Reference::Addend *addend) { *kind = kindFromRelocPair(reloc1, reloc2); if (*kind == invalid) - return make_dynamic_error_code(Twine("unknown pair")); + return make_dynamic_error_code("unknown pair"); const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; typedef std::error_code E; uint64_t targetAddress; @@ -421,7 +469,7 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, if (E ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget)) return ec; if (fromTarget != inAtom) - return make_dynamic_error_code(Twine("pointer diff not in base atom")); + return make_dynamic_error_code("pointer diff not in base atom"); switch (*kind) { case delta64: if (E ec = atomFromSymbolIndex(reloc2.symbol, target)) @@ -486,19 +534,26 @@ void ArchHandler_x86_64::applyFixupFinal( case ripRel32Anon: case ripRel32Got: case ripRel32GotLoad: + case ripRel32Tlv: *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); return; case pointer64: case pointer64Anon: *loc64 = targetAddress + ref.addend(); return; + case tlvInitSectionOffset: + *loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); + return; case ripRel32Minus1: + case ripRel32Minus1Anon: *loc32 = targetAddress - (fixupAddress + 5) + ref.addend(); return; case ripRel32Minus2: + case ripRel32Minus2Anon: *loc32 = targetAddress - (fixupAddress + 6) + ref.addend(); return; case ripRel32Minus4: + case ripRel32Minus4Anon: *loc32 = targetAddress - (fixupAddress + 8) + ref.addend(); return; case delta32: @@ -542,7 +597,6 @@ void ArchHandler_x86_64::applyFixupFinal( llvm_unreachable("invalid x86_64 Reference Kind"); } - void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, @@ -558,11 +612,13 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, case ripRel32: case ripRel32Got: case ripRel32GotLoad: + case ripRel32Tlv: *loc32 = ref.addend(); return; case ripRel32Anon: *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); return; + case tlvInitSectionOffset: case pointer64: *loc64 = ref.addend(); return; @@ -572,12 +628,21 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, case ripRel32Minus1: *loc32 = ref.addend() - 1; return; + case ripRel32Minus1Anon: + *loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend(); + return; case ripRel32Minus2: *loc32 = ref.addend() - 2; return; + case ripRel32Minus2Anon: + *loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend(); + return; case ripRel32Minus4: *loc32 = ref.addend() - 4; return; + case ripRel32Minus4Anon: + *loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend(); + return; case delta32: *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; @@ -638,7 +703,7 @@ void ArchHandler_x86_64::appendSectionRelocations( return; case ripRel32Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, - X86_64_RELOC_SIGNED | rPcRel | rLength4 ); + X86_64_RELOC_SIGNED | rPcRel | rLength4 ); return; case ripRel32Got: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, @@ -648,6 +713,11 @@ void ArchHandler_x86_64::appendSectionRelocations( appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 ); return; + case ripRel32Tlv: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 ); + return; + case tlvInitSectionOffset: case pointer64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8); @@ -660,14 +730,26 @@ void ArchHandler_x86_64::appendSectionRelocations( appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 ); return; + case ripRel32Minus1Anon: + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 ); + return; case ripRel32Minus2: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 ); return; + case ripRel32Minus2Anon: + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 ); + return; case ripRel32Minus4: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 ); return; + case ripRel32Minus4Anon: + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 ); + return; case delta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); @@ -714,7 +796,6 @@ void ArchHandler_x86_64::appendSectionRelocations( llvm_unreachable("unknown x86_64 Reference Kind"); } - std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86_64() { return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86_64()); } diff --git a/lib/ReaderWriter/MachO/Atoms.h b/lib/ReaderWriter/MachO/Atoms.h index 8d60c1a163a6..9f2e5acad99a 100644 --- a/lib/ReaderWriter/MachO/Atoms.h +++ b/lib/ReaderWriter/MachO/Atoms.h @@ -25,11 +25,11 @@ public: // Constructor for zero-fill content MachODefinedAtom(const File &f, const StringRef name, Scope scope, - uint64_t size, bool noDeadStrip, Alignment align) + ContentType type, uint64_t size, bool noDeadStrip, + Alignment align) : SimpleDefinedAtom(f), _name(name), _content(ArrayRef<uint8_t>(nullptr, size)), _align(align), - _contentType(DefinedAtom::typeZeroFill), - _scope(scope), _merge(mergeNo), _thumb(false), + _contentType(type), _scope(scope), _merge(mergeNo), _thumb(false), _noDeadStrip(noDeadStrip) {} uint64_t size() const override { return _content.size(); } @@ -103,7 +103,6 @@ private: StringRef _sectionName; }; - class MachOTentativeDefAtom : public SimpleDefinedAtom { public: MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope, @@ -126,7 +125,7 @@ public: ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } private: - const StringRef _name; + const std::string _name; const Scope _scope; const uint64_t _size; const DefinedAtom::Alignment _align; @@ -138,32 +137,26 @@ public: StringRef dylibInstallName, bool weakDef) : SharedLibraryAtom(), _file(file), _name(name), _dylibInstallName(dylibInstallName) {} - virtual ~MachOSharedLibraryAtom() {} + ~MachOSharedLibraryAtom() override = default; - virtual StringRef loadName() const override { - return _dylibInstallName; - } + StringRef loadName() const override { return _dylibInstallName; } - virtual bool canBeNullAtRuntime() const override { + bool canBeNullAtRuntime() const override { // FIXME: this may actually be changeable. For now, all symbols are strongly // defined though. return false; } - virtual const File& file() const override { - return _file; - } + const File &file() const override { return _file; } - virtual StringRef name() const override { - return _name; - } + StringRef name() const override { return _name; } - virtual Type type() const override { + Type type() const override { // Unused in MachO (I think). return Type::Unknown; } - virtual uint64_t size() const override { + uint64_t size() const override { // Unused in MachO (I think) return 0; } @@ -174,8 +167,7 @@ private: StringRef _dylibInstallName; }; +} // namespace mach_o +} // namespace lld -} // mach_o -} // lld - -#endif +#endif // LLD_READER_WRITER_MACHO_ATOMS_H diff --git a/lib/ReaderWriter/MachO/CMakeLists.txt b/lib/ReaderWriter/MachO/CMakeLists.txt index e396537c63c8..7ce782af6f99 100644 --- a/lib/ReaderWriter/MachO/CMakeLists.txt +++ b/lib/ReaderWriter/MachO/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_library(lldMachO MachONormalizedFileYAML.cpp ShimPass.cpp StubsPass.cpp + TLVPass.cpp WriterMachO.cpp LINK_LIBS lldCore diff --git a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp index fc8608383e5d..4b8644a67e70 100644 --- a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp +++ b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -----------------------===// +//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -------------*- C++ -*-===// // // The LLVM Linker // @@ -92,7 +92,7 @@ public: return DefinedAtom::typeProcessedUnwindInfo; } - Alignment alignment() const override { return Alignment(2); } + Alignment alignment() const override { return 4; } uint64_t size() const override { return _contents.size(); } @@ -272,12 +272,12 @@ private: class CompactUnwindPass : public Pass { public: CompactUnwindPass(const MachOLinkingContext &context) - : _context(context), _archHandler(_context.archHandler()), + : _ctx(context), _archHandler(_ctx.archHandler()), _file("<mach-o Compact Unwind Pass>"), - _isBig(MachOLinkingContext::isBigEndian(_context.arch())) {} + _isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) {} private: - void perform(std::unique_ptr<MutableFile> &mergedFile) override { + std::error_code perform(SimpleFile &mergedFile) override { DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n"); std::map<const Atom *, CompactUnwindEntry> unwindLocs; @@ -294,7 +294,7 @@ private: // Skip rest of pass if no unwind info. if (unwindLocs.empty() && dwarfFrames.empty()) - return; + return std::error_code(); // FIXME: if there are more than 4 personality functions then we need to // defer to DWARF info for the ones we don't put in the list. They should @@ -310,6 +310,9 @@ private: std::vector<CompactUnwindEntry> unwindInfos = createUnwindInfoEntries( mergedFile, unwindLocs, personalities, dwarfFrames); + // Remove any unused eh-frame atoms. + pruneUnusedEHFrames(mergedFile, unwindInfos, unwindLocs, dwarfFrames); + // Finally, we can start creating pages based on these entries. DEBUG(llvm::dbgs() << " Splitting entries into pages\n"); @@ -339,24 +342,26 @@ private: << " has " << entriesInPage << " entries\n"); } while (pageStart < unwindInfos.size()); - UnwindInfoAtom *unwind = new (_file.allocator()) + auto *unwind = new (_file.allocator()) UnwindInfoAtom(_archHandler, _file, _isBig, personalities, commonEncodings, pages, numLSDAs); - mergedFile->addAtom(*unwind); + mergedFile.addAtom(*unwind); // Finally, remove all __compact_unwind atoms now that we've processed them. - mergedFile->removeDefinedAtomsIf([](const DefinedAtom *atom) { + mergedFile.removeDefinedAtomsIf([](const DefinedAtom *atom) { return atom->contentType() == DefinedAtom::typeCompactUnwindInfo; }); + + return std::error_code(); } void collectCompactUnwindEntries( - std::unique_ptr<MutableFile> &mergedFile, + const SimpleFile &mergedFile, std::map<const Atom *, CompactUnwindEntry> &unwindLocs, std::vector<const Atom *> &personalities, uint32_t &numLSDAs) { DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n"); - for (const DefinedAtom *atom : mergedFile->defined()) { + for (const DefinedAtom *atom : mergedFile.defined()) { if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo) continue; @@ -422,9 +427,9 @@ private: } void - collectDwarfFrameEntries(std::unique_ptr<MutableFile> &mergedFile, + collectDwarfFrameEntries(const SimpleFile &mergedFile, std::map<const Atom *, const Atom *> &dwarfFrames) { - for (const DefinedAtom *ehFrameAtom : mergedFile->defined()) { + for (const DefinedAtom *ehFrameAtom : mergedFile.defined()) { if (ehFrameAtom->contentType() != DefinedAtom::typeCFI) continue; if (ArchHandler::isDwarfCIE(_isBig, ehFrameAtom)) @@ -442,7 +447,7 @@ private: /// + A synthesised reference to __eh_frame if there's no __compact_unwind /// or too many personality functions to be accommodated. std::vector<CompactUnwindEntry> createUnwindInfoEntries( - const std::unique_ptr<MutableFile> &mergedFile, + const SimpleFile &mergedFile, const std::map<const Atom *, CompactUnwindEntry> &unwindLocs, const std::vector<const Atom *> &personalities, const std::map<const Atom *, const Atom *> &dwarfFrames) { @@ -452,7 +457,7 @@ private: // The final order in the __unwind_info section must be derived from the // order of typeCode atoms, since that's how they'll be put into the object // file eventually (yuck!). - for (const DefinedAtom *atom : mergedFile->defined()) { + for (const DefinedAtom *atom : mergedFile.defined()) { if (atom->contentType() != DefinedAtom::typeCode) continue; @@ -468,6 +473,53 @@ private: return unwindInfos; } + /// Remove unused EH frames. + /// + /// An EH frame is considered unused if there is a corresponding compact + /// unwind atom that doesn't require the EH frame. + void pruneUnusedEHFrames( + SimpleFile &mergedFile, + const std::vector<CompactUnwindEntry> &unwindInfos, + const std::map<const Atom *, CompactUnwindEntry> &unwindLocs, + const std::map<const Atom *, const Atom *> &dwarfFrames) { + + // Worklist of all 'used' FDEs. + std::vector<const DefinedAtom *> usedDwarfWorklist; + + // We have to check two conditions when building the worklist: + // (1) EH frames used by compact unwind entries. + for (auto &entry : unwindInfos) + if (entry.ehFrame) + usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.ehFrame)); + + // (2) EH frames that reference functions with no corresponding compact + // unwind info. + for (auto &entry : dwarfFrames) + if (!unwindLocs.count(entry.first)) + usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.second)); + + // Add all transitively referenced CFI atoms by processing the worklist. + std::set<const Atom *> usedDwarfFrames; + while (!usedDwarfWorklist.empty()) { + const DefinedAtom *cfiAtom = usedDwarfWorklist.back(); + usedDwarfWorklist.pop_back(); + usedDwarfFrames.insert(cfiAtom); + for (const auto *ref : *cfiAtom) { + const DefinedAtom *cfiTarget = dyn_cast<DefinedAtom>(ref->target()); + if (cfiTarget->contentType() == DefinedAtom::typeCFI) + usedDwarfWorklist.push_back(cfiTarget); + } + } + + // Finally, delete all unreferenced CFI atoms. + mergedFile.removeDefinedAtomsIf([&](const DefinedAtom *atom) { + if ((atom->contentType() == DefinedAtom::typeCFI) && + !usedDwarfFrames.count(atom)) + return true; + return false; + }); + } + CompactUnwindEntry finalizeUnwindInfoEntryForAtom( const DefinedAtom *function, const std::map<const Atom *, CompactUnwindEntry> &unwindLocs, @@ -496,7 +548,6 @@ private: } } - auto personality = std::find(personalities.begin(), personalities.end(), entry.personalityFunction); uint32_t personalityIdx = personality == personalities.end() @@ -515,7 +566,7 @@ private: return entry; } - const MachOLinkingContext &_context; + const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; MachOFile _file; bool _isBig; diff --git a/lib/ReaderWriter/MachO/ExecutableAtoms.hpp b/lib/ReaderWriter/MachO/ExecutableAtoms.h index cd562de216d9..2e99af903dbd 100644 --- a/lib/ReaderWriter/MachO/ExecutableAtoms.hpp +++ b/lib/ReaderWriter/MachO/ExecutableAtoms.h @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/MachO/ExecutableAtoms.hpp -------------------------===// +//===- lib/ReaderWriter/MachO/ExecutableAtoms.h ---------------------------===// // // The LLVM Linker // @@ -97,36 +97,33 @@ public: File *find(StringRef sym, bool dataSymbolOnly) override { if (sym.equals("___dso_handle") || sym.equals(_machHeaderSymbolName)) { - _definedAtoms._atoms.push_back(new (allocator()) MachODefinedAtom( + _definedAtoms.push_back(new (allocator()) MachODefinedAtom( *this, sym, DefinedAtom::scopeLinkageUnit, DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false, false, - ArrayRef<uint8_t>(), DefinedAtom::Alignment(12,0))); + ArrayRef<uint8_t>(), DefinedAtom::Alignment(4096))); return this; } return nullptr; } - const atom_collection<DefinedAtom> &defined() const override { + const AtomVector<DefinedAtom> &defined() const override { return _definedAtoms; } - const atom_collection<UndefinedAtom> &undefined() const override { - return _undefinedAtoms; + const AtomVector<UndefinedAtom> &undefined() const override { + return _noUndefinedAtoms; } - const atom_collection<SharedLibraryAtom> &sharedLibrary() const override { - return _sharedLibraryAtoms; + const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { + return _noSharedLibraryAtoms; } - const atom_collection<AbsoluteAtom> &absolute() const override { - return _absoluteAtoms; + const AtomVector<AbsoluteAtom> &absolute() const override { + return _noAbsoluteAtoms; } private: - mutable atom_collection_vector<DefinedAtom> _definedAtoms; - atom_collection_vector<UndefinedAtom> _undefinedAtoms; - atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; - atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + mutable AtomVector<DefinedAtom> _definedAtoms; StringRef _machHeaderSymbolName; }; diff --git a/lib/ReaderWriter/MachO/File.h b/lib/ReaderWriter/MachO/File.h index 913644ec1fc0..c97dfa142b8d 100644 --- a/lib/ReaderWriter/MachO/File.h +++ b/lib/ReaderWriter/MachO/File.h @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/MachO/File.h --------------------------------------===// +//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===// // // The LLVM Linker // @@ -44,8 +44,8 @@ public: } DefinedAtom::Alignment align( inSection->alignment, - sectionOffset % ((uint64_t)1 << inSection->alignment)); - MachODefinedAtom *atom = + sectionOffset % inSection->alignment); + auto *atom = new (allocator()) MachODefinedAtom(*this, name, scope, type, merge, thumb, noDeadStrip, content, align); addAtomForSection(inSection, atom, sectionOffset); @@ -67,8 +67,8 @@ public: } DefinedAtom::Alignment align( inSection->alignment, - sectionOffset % ((uint64_t)1 << inSection->alignment)); - MachODefinedCustomSectionAtom *atom = + sectionOffset % inSection->alignment); + auto *atom = new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type, merge, thumb, noDeadStrip, content, @@ -86,10 +86,23 @@ public: } DefinedAtom::Alignment align( inSection->alignment, - sectionOffset % ((uint64_t)1 << inSection->alignment)); - MachODefinedAtom *atom = - new (allocator()) MachODefinedAtom(*this, name, scope, size, noDeadStrip, - align); + sectionOffset % inSection->alignment); + + DefinedAtom::ContentType type = DefinedAtom::typeUnknown; + switch (inSection->type) { + case llvm::MachO::S_ZEROFILL: + type = DefinedAtom::typeZeroFill; + break; + case llvm::MachO::S_THREAD_LOCAL_ZEROFILL: + type = DefinedAtom::typeTLVInitialZeroFill; + break; + default: + llvm_unreachable("Unrecognized zero-fill section"); + } + + auto *atom = + new (allocator()) MachODefinedAtom(*this, name, scope, type, size, + noDeadStrip, align); addAtomForSection(inSection, atom, sectionOffset); } @@ -98,8 +111,7 @@ public: // Make a copy of the atom's name that is owned by this file. name = name.copy(allocator()); } - SimpleUndefinedAtom *atom = - new (allocator()) SimpleUndefinedAtom(*this, name); + auto *atom = new (allocator()) SimpleUndefinedAtom(*this, name); addAtom(*atom); _undefAtoms[name] = atom; } @@ -110,7 +122,7 @@ public: // Make a copy of the atom's name that is owned by this file. name = name.copy(allocator()); } - MachOTentativeDefAtom *atom = + auto *atom = new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align); addAtom(*atom); _undefAtoms[name] = atom; @@ -200,7 +212,6 @@ private: addAtom(*atom); } - typedef llvm::DenseMap<const normalized::Section *, std::vector<SectionOffsetAndAtom>> SectionToAtoms; typedef llvm::StringMap<const lld::Atom *> NameToAtom; @@ -298,7 +309,6 @@ private: return nullptr; } - struct ReExportedDylib { ReExportedDylib(StringRef p) : path(p), file(nullptr) { } StringRef path; @@ -324,4 +334,4 @@ private: } // end namespace mach_o } // end namespace lld -#endif +#endif // LLD_READER_WRITER_MACHO_FILE_H diff --git a/lib/ReaderWriter/MachO/FlatNamespaceFile.h b/lib/ReaderWriter/MachO/FlatNamespaceFile.h new file mode 100644 index 000000000000..6c6a9262ba2e --- /dev/null +++ b/lib/ReaderWriter/MachO/FlatNamespaceFile.h @@ -0,0 +1,61 @@ +//===- lib/ReaderWriter/MachO/FlatNamespaceFile.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_MACHO_FLAT_NAMESPACE_FILE_H +#define LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H + +#include "lld/Core/SharedLibraryFile.h" +#include "llvm/Support/Debug.h" + +namespace lld { +namespace mach_o { + +// +// A FlateNamespaceFile instance may be added as a resolution source of last +// resort, depending on how -flat_namespace and -undefined are set. +// +class FlatNamespaceFile : public SharedLibraryFile { +public: + FlatNamespaceFile(const MachOLinkingContext &context) + : SharedLibraryFile("flat namespace") { } + + const SharedLibraryAtom *exports(StringRef name, + bool dataSymbolOnly) const override { + _sharedLibraryAtoms.push_back( + new (allocator()) MachOSharedLibraryAtom(*this, name, getDSOName(), + false)); + + return _sharedLibraryAtoms.back(); + } + + StringRef getDSOName() const override { return "flat-namespace"; } + + const AtomVector<DefinedAtom> &defined() const override { + return _noDefinedAtoms; + } + const AtomVector<UndefinedAtom> &undefined() const override { + return _noUndefinedAtoms; + } + + const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { + return _sharedLibraryAtoms; + } + + const AtomVector<AbsoluteAtom> &absolute() const override { + return _noAbsoluteAtoms; + } + +private: + mutable AtomVector<SharedLibraryAtom> _sharedLibraryAtoms; +}; + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp index 1ddec4003cbd..a5816277dd71 100644 --- a/lib/ReaderWriter/MachO/GOTPass.cpp +++ b/lib/ReaderWriter/MachO/GOTPass.cpp @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/MachO/GOTPass.cpp ---------------------------------===// +//===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===// // // The LLVM Linker // @@ -46,7 +46,6 @@ namespace lld { namespace mach_o { - // // GOT Entry Atom created by the GOT pass. // @@ -60,7 +59,7 @@ public: } Alignment alignment() const override { - return Alignment(_is64 ? 3 : 2); + return _is64 ? 8 : 4; } uint64_t size() const override { @@ -86,20 +85,18 @@ private: StringRef _name; }; - /// Pass for instantiating and optimizing GOT slots. /// class GOTPass : public Pass { public: GOTPass(const MachOLinkingContext &context) - : _context(context), _archHandler(_context.archHandler()), - _file("<mach-o GOT Pass>") { } + : _ctx(context), _archHandler(_ctx.archHandler()), + _file("<mach-o GOT Pass>") {} private: - - void perform(std::unique_ptr<MutableFile> &mergedFile) override { + std::error_code perform(SimpleFile &mergedFile) override { // Scan all references in all atoms. - for (const DefinedAtom *atom : mergedFile->defined()) { + for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { // Look at instructions accessing the GOT. bool canBypassGOT; @@ -131,7 +128,9 @@ private: return (left->slotName().compare(right->slotName()) < 0); }); for (const GOTEntryAtom *slot : entries) - mergedFile->addAtom(*slot); + mergedFile.addAtom(*slot); + + return std::error_code(); } bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) { @@ -154,8 +153,8 @@ private: const DefinedAtom *makeGOTEntry(const Atom *target) { auto pos = _targetToGOT.find(target); if (pos == _targetToGOT.end()) { - GOTEntryAtom *gotEntry = new (_file.allocator()) - GOTEntryAtom(_file, _context.is64Bit(), target->name()); + auto *gotEntry = new (_file.allocator()) + GOTEntryAtom(_file, _ctx.is64Bit(), target->name()); _targetToGOT[target] = gotEntry; const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo(). nonLazyPointerReferenceToBinder; @@ -166,20 +165,16 @@ private: return pos->second; } - - const MachOLinkingContext &_context; + const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; MachOFile _file; llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT; }; - - void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) { assert(ctx.needsGOTPass()); pm.add(llvm::make_unique<GOTPass>(ctx)); } - } // end namesapce mach_o } // end namesapce lld diff --git a/lib/ReaderWriter/MachO/LayoutPass.cpp b/lib/ReaderWriter/MachO/LayoutPass.cpp index 2d096e4c1a6a..0c14ee9d39ab 100644 --- a/lib/ReaderWriter/MachO/LayoutPass.cpp +++ b/lib/ReaderWriter/MachO/LayoutPass.cpp @@ -133,7 +133,7 @@ static void checkReachabilityFromRoot(AtomToAtomT &followOnRoots, } } -static void printDefinedAtoms(const MutableFile::DefinedAtomRange &atomRange) { +static void printDefinedAtoms(const SimpleFile::DefinedAtomRange &atomRange) { for (const DefinedAtom *atom : atomRange) { llvm::dbgs() << " file=" << atom->file().path() << ", name=" << atom->name() @@ -146,7 +146,7 @@ static void printDefinedAtoms(const MutableFile::DefinedAtomRange &atomRange) { /// Verify that the followon chain is sane. Should not be called in /// release binary. -void LayoutPass::checkFollowonChain(MutableFile::DefinedAtomRange &range) { +void LayoutPass::checkFollowonChain(SimpleFile::DefinedAtomRange &range) { ScopedTask task(getDefaultDomain(), "LayoutPass::checkFollowonChain"); // Verify that there's no cycle in follow-on chain. @@ -329,7 +329,7 @@ void LayoutPass::setChainRoot(const DefinedAtom *targetAtom, /// d) If the targetAtom is part of a different chain and the root of the /// targetAtom until the targetAtom has all atoms of size 0, then chain the /// targetAtoms and its tree to the current chain -void LayoutPass::buildFollowOnTable(MutableFile::DefinedAtomRange &range) { +void LayoutPass::buildFollowOnTable(SimpleFile::DefinedAtomRange &range) { ScopedTask task(getDefaultDomain(), "LayoutPass::buildFollowOnTable"); // Set the initial size of the followon and the followonNext hash to the // number of atoms that we have. @@ -397,7 +397,7 @@ void LayoutPass::buildFollowOnTable(MutableFile::DefinedAtomRange &range) { /// assigning ordinals to each atom, if the atoms have their ordinals /// already assigned skip the atom and move to the next. This is the /// main map thats used to sort the atoms while comparing two atoms together -void LayoutPass::buildOrdinalOverrideMap(MutableFile::DefinedAtomRange &range) { +void LayoutPass::buildOrdinalOverrideMap(SimpleFile::DefinedAtomRange &range) { ScopedTask task(getDefaultDomain(), "LayoutPass::buildOrdinalOverrideMap"); uint64_t index = 0; for (const DefinedAtom *ai : range) { @@ -407,7 +407,7 @@ void LayoutPass::buildOrdinalOverrideMap(MutableFile::DefinedAtomRange &range) { AtomToAtomT::iterator start = _followOnRoots.find(atom); if (start == _followOnRoots.end()) continue; - for (const DefinedAtom *nextAtom = start->second; nextAtom != NULL; + for (const DefinedAtom *nextAtom = start->second; nextAtom; nextAtom = _followOnNexts[nextAtom]) { AtomToOrdinalT::iterator pos = _ordinalOverrideMap.find(nextAtom); if (pos == _ordinalOverrideMap.end()) @@ -417,7 +417,7 @@ void LayoutPass::buildOrdinalOverrideMap(MutableFile::DefinedAtomRange &range) { } std::vector<LayoutPass::SortKey> -LayoutPass::decorate(MutableFile::DefinedAtomRange &atomRange) const { +LayoutPass::decorate(SimpleFile::DefinedAtomRange &atomRange) const { std::vector<SortKey> ret; for (const DefinedAtom *atom : atomRange) { auto ri = _followOnRoots.find(atom); @@ -429,7 +429,7 @@ LayoutPass::decorate(MutableFile::DefinedAtomRange &atomRange) const { return ret; } -void LayoutPass::undecorate(MutableFile::DefinedAtomRange &atomRange, +void LayoutPass::undecorate(SimpleFile::DefinedAtomRange &atomRange, std::vector<SortKey> &keys) const { size_t i = 0; for (SortKey &k : keys) @@ -437,10 +437,11 @@ void LayoutPass::undecorate(MutableFile::DefinedAtomRange &atomRange, } /// Perform the actual pass -void LayoutPass::perform(std::unique_ptr<MutableFile> &mergedFile) { +std::error_code LayoutPass::perform(SimpleFile &mergedFile) { + DEBUG(llvm::dbgs() << "******** Laying out atoms:\n"); // sort the atoms ScopedTask task(getDefaultDomain(), "LayoutPass"); - MutableFile::DefinedAtomRange atomRange = mergedFile->definedAtoms(); + SimpleFile::DefinedAtomRange atomRange = mergedFile.definedAtoms(); // Build follow on tables buildFollowOnTable(atomRange); @@ -468,6 +469,9 @@ void LayoutPass::perform(std::unique_ptr<MutableFile> &mergedFile) { llvm::dbgs() << "sorted atoms:\n"; printDefinedAtoms(atomRange); }); + + DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n"); + return std::error_code(); } void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) { diff --git a/lib/ReaderWriter/MachO/LayoutPass.h b/lib/ReaderWriter/MachO/LayoutPass.h index 186f29be0719..d6072b0ca4fb 100644 --- a/lib/ReaderWriter/MachO/LayoutPass.h +++ b/lib/ReaderWriter/MachO/LayoutPass.h @@ -13,6 +13,7 @@ #include "lld/Core/File.h" #include "lld/Core/Pass.h" #include "lld/Core/Reader.h" +#include "lld/Core/Simple.h" #include "llvm/ADT/DenseMap.h" #include <map> #include <string> @@ -20,7 +21,7 @@ namespace lld { class DefinedAtom; -class MutableFile; +class SimpleFile; namespace mach_o { @@ -45,17 +46,17 @@ public: LayoutPass(const Registry ®istry, SortOverride sorter); /// Sorts atoms in mergedFile by content type then by command line order. - void perform(std::unique_ptr<MutableFile> &mergedFile) override; + std::error_code perform(SimpleFile &mergedFile) override; - virtual ~LayoutPass() {} + ~LayoutPass() override = default; private: // Build the followOn atoms chain as specified by the kindLayoutAfter // reference type - void buildFollowOnTable(MutableFile::DefinedAtomRange &range); + void buildFollowOnTable(SimpleFile::DefinedAtomRange &range); // Build a map of Atoms to ordinals for sorting the atoms - void buildOrdinalOverrideMap(MutableFile::DefinedAtomRange &range); + void buildOrdinalOverrideMap(SimpleFile::DefinedAtomRange &range); const Registry &_registry; SortOverride _customSorter; @@ -83,12 +84,12 @@ private: void setChainRoot(const DefinedAtom *targetAtom, const DefinedAtom *root); - std::vector<SortKey> decorate(MutableFile::DefinedAtomRange &atomRange) const; - void undecorate(MutableFile::DefinedAtomRange &atomRange, + std::vector<SortKey> decorate(SimpleFile::DefinedAtomRange &atomRange) const; + void undecorate(SimpleFile::DefinedAtomRange &atomRange, std::vector<SortKey> &keys) const; // Check if the follow-on graph is a correct structure. For debugging only. - void checkFollowonChain(MutableFile::DefinedAtomRange &range); + void checkFollowonChain(SimpleFile::DefinedAtomRange &range); }; } // namespace mach_o diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp index 92385cf3e820..1c97c5a39d3f 100644 --- a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -10,8 +10,10 @@ #include "lld/ReaderWriter/MachOLinkingContext.h" #include "ArchHandler.h" #include "File.h" +#include "FlatNamespaceFile.h" #include "MachONormalizedFile.h" #include "MachOPasses.h" +#include "SectCreateFile.h" #include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/PassManager.h" #include "lld/Core/Reader.h" @@ -73,7 +75,6 @@ bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) { return false; } - MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = { { "x86_64", arch_x86_64, true, CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL }, { "i386", arch_x86, true, CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL }, @@ -133,8 +134,7 @@ bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) { return mach_o::normalized::isThinObjectFile(path, arch); } -bool MachOLinkingContext::sliceFromFatFile(const MemoryBuffer &mb, - uint32_t &offset, +bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, uint32_t &size) { return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size); } @@ -143,11 +143,13 @@ MachOLinkingContext::MachOLinkingContext() : _outputMachOType(MH_EXECUTE), _outputMachOTypeStatic(false), _doNothing(false), _pie(false), _arch(arch_unknown), _os(OS::macOSX), _osMinVersion(0), _pageZeroSize(0), _pageSize(4096), _baseAddress(0), - _compatibilityVersion(0), _currentVersion(0), _deadStrippableDylib(false), - _printAtoms(false), _testingFileUsage(false), _keepPrivateExterns(false), - _demangle(false), _archHandler(nullptr), + _stackSize(0), _compatibilityVersion(0), _currentVersion(0), + _flatNamespace(false), _undefinedMode(UndefinedMode::error), + _deadStrippableDylib(false), _printAtoms(false), _testingFileUsage(false), + _keepPrivateExterns(false), _demangle(false), _archHandler(nullptr), _exportMode(ExportMode::globals), - _debugInfoMode(DebugInfoMode::addDebugMap), _orderFileEntries(0) {} + _debugInfoMode(DebugInfoMode::addDebugMap), _orderFileEntries(0), + _flatNamespaceFile(nullptr) {} MachOLinkingContext::~MachOLinkingContext() {} @@ -196,6 +198,9 @@ void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os, _pageZeroSize = 0x1000; } + // Initial base address is __PAGEZERO size. + _baseAddress = _pageZeroSize; + // Make PIE by default when targetting newer OSs. switch (os) { case OS::macOSX: @@ -268,8 +273,6 @@ bool MachOLinkingContext::isBigEndian(Arch arch) { llvm_unreachable("Unknown arch type"); } - - bool MachOLinkingContext::is64Bit() const { return is64Bit(_arch); } @@ -337,13 +340,21 @@ bool MachOLinkingContext::needsShimPass() const { } } +bool MachOLinkingContext::needsTLVPass() const { + switch (_outputMachOType) { + case MH_BUNDLE: + case MH_EXECUTE: + case MH_DYLIB: + return true; + default: + return false; + } +} + StringRef MachOLinkingContext::binderSymbolName() const { return archHandler().stubInfo().binderSymbolName; } - - - bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const { uint32_t parsedVersion; switch (_os) { @@ -473,7 +484,6 @@ void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath, _frameworkDirs.push_back(fwPath); } - ErrorOr<StringRef> MachOLinkingContext::searchDirForLibrary(StringRef path, StringRef libName) const { @@ -502,8 +512,6 @@ MachOLinkingContext::searchDirForLibrary(StringRef path, return make_error_code(llvm::errc::no_such_file_or_directory); } - - ErrorOr<StringRef> MachOLinkingContext::searchLibrary(StringRef libName) const { SmallString<256> path; for (StringRef dir : searchDirs()) { @@ -515,7 +523,6 @@ ErrorOr<StringRef> MachOLinkingContext::searchLibrary(StringRef libName) const { return make_error_code(llvm::errc::no_such_file_or_directory); } - ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) const{ SmallString<256> fullPath; for (StringRef dir : frameworkDirs()) { @@ -589,6 +596,8 @@ void MachOLinkingContext::addPasses(PassManager &pm) { mach_o::addCompactUnwindPass(pm, *this); if (needsGOTPass()) mach_o::addGOTPass(pm, *this); + if (needsTLVPass()) + mach_o::addTLVPass(pm, *this); if (needsShimPass()) mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass. } @@ -613,7 +622,7 @@ MachOLinkingContext::getMemoryBuffer(StringRef path) { // and switch buffer to point to just that required slice. uint32_t offset; uint32_t size; - if (sliceFromFatFile(*mb, offset, size)) + if (sliceFromFatFile(mb->getMemBufferRef(), offset, size)) return MemoryBuffer::getFileSlice(path, size, offset); return std::move(mb); } @@ -623,18 +632,18 @@ MachODylibFile* MachOLinkingContext::loadIndirectDylib(StringRef path) { if (mbOrErr.getError()) return nullptr; - std::vector<std::unique_ptr<File>> files; - if (registry().loadFile(std::move(mbOrErr.get()), files)) + ErrorOr<std::unique_ptr<File>> fileOrErr = + registry().loadFile(std::move(mbOrErr.get())); + if (!fileOrErr) return nullptr; - assert(files.size() == 1 && "expected one file in dylib"); - files[0]->parse(); - MachODylibFile* result = reinterpret_cast<MachODylibFile*>(files[0].get()); + std::unique_ptr<File> &file = fileOrErr.get(); + file->parse(); + MachODylibFile *result = reinterpret_cast<MachODylibFile *>(file.get()); // Node object now owned by _indirectDylibs vector. - _indirectDylibs.push_back(std::move(files[0])); + _indirectDylibs.push_back(std::move(file)); return result; } - MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) { // See if already loaded. auto pos = _pathToDylibMap.find(path); @@ -685,7 +694,7 @@ uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const { return 0x1000; // 1.0 } -bool MachOLinkingContext::createImplicitFiles( +void MachOLinkingContext::createImplicitFiles( std::vector<std::unique_ptr<File> > &result) { // Add indirect dylibs by asking each linked dylib to add its indirects. // Iterate until no more dylibs get loaded. @@ -699,12 +708,19 @@ bool MachOLinkingContext::createImplicitFiles( } // Let writer add output type specific extras. - return writer().createImplicitFiles(result); -} + writer().createImplicitFiles(result); + // If undefinedMode is != error, add a FlatNamespaceFile instance. This will + // provide a SharedLibraryAtom for symbols that aren't defined elsewhere. + if (undefinedMode() != UndefinedMode::error) { + result.emplace_back(new mach_o::FlatNamespaceFile(*this)); + _flatNamespaceFile = result.back().get(); + } +} void MachOLinkingContext::registerDylib(MachODylibFile *dylib, bool upward) const { + std::lock_guard<std::mutex> lock(_dylibsMutex); _allDylibs.insert(dylib); _pathToDylibMap[dylib->installName()] = dylib; // If path is different than install name, register path too. @@ -714,7 +730,6 @@ void MachOLinkingContext::registerDylib(MachODylibFile *dylib, _upwardDylibs.insert(dylib); } - bool MachOLinkingContext::isUpwardDylib(StringRef installName) const { for (MachODylibFile *dylib : _upwardDylibs) { if (dylib->installName().equals(installName)) @@ -729,28 +744,37 @@ ArchHandler &MachOLinkingContext::archHandler() const { return *_archHandler; } - void MachOLinkingContext::addSectionAlignment(StringRef seg, StringRef sect, - uint8_t align2) { - SectionAlign entry; - entry.segmentName = seg; - entry.sectionName = sect; - entry.align2 = align2; + uint16_t align) { + SectionAlign entry = { seg, sect, align }; _sectAligns.push_back(entry); } +void MachOLinkingContext::addSectCreateSection( + StringRef seg, StringRef sect, + std::unique_ptr<MemoryBuffer> content) { + + if (!_sectCreateFile) { + auto sectCreateFile = llvm::make_unique<mach_o::SectCreateFile>(); + _sectCreateFile = sectCreateFile.get(); + getNodes().push_back(llvm::make_unique<FileNode>(std::move(sectCreateFile))); + } + + assert(_sectCreateFile && "sectcreate file does not exist."); + _sectCreateFile->addSection(seg, sect, std::move(content)); +} + bool MachOLinkingContext::sectionAligned(StringRef seg, StringRef sect, - uint8_t &align2) const { + uint16_t &align) const { for (const SectionAlign &entry : _sectAligns) { if (seg.equals(entry.segmentName) && sect.equals(entry.sectionName)) { - align2 = entry.align2; + align = entry.align; return true; } } return false; } - void MachOLinkingContext::addExportSymbol(StringRef sym) { // Support old crufty export lists with bogus entries. if (sym.endswith(".eh") || sym.startswith(".objc_category_name_")) { @@ -805,7 +829,7 @@ std::string MachOLinkingContext::demangle(StringRef symbolName) const { const char *cstr = nullTermSym.data() + 1; int status; char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status); - if (demangled != NULL) { + if (demangled) { std::string result(demangled); // __cxa_demangle() always uses a malloc'ed buffer to return the result. free(demangled); diff --git a/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lib/ReaderWriter/MachO/MachONormalizedFile.h index 70bcde2dea22..cccf180f1043 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFile.h +++ b/lib/ReaderWriter/MachO/MachONormalizedFile.h @@ -45,6 +45,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MachO.h" #include "llvm/Support/YAMLTraits.h" @@ -108,17 +109,26 @@ LLVM_YAML_STRONG_TYPEDEF(uint32_t, SectionAttr) /// can support either kind. struct Section { Section() : type(llvm::MachO::S_REGULAR), - attributes(0), alignment(0), address(0) { } + attributes(0), alignment(1), address(0) { } StringRef segmentName; StringRef sectionName; SectionType type; SectionAttr attributes; - uint32_t alignment; + uint16_t alignment; Hex64 address; ArrayRef<uint8_t> content; Relocations relocations; IndirectSymbols indirectSymbols; + +#ifndef NDEBUG + raw_ostream& operator<<(raw_ostream &OS) const { + dump(OS); + return OS; + } + + void dump(raw_ostream &OS = llvm::dbgs()) const; +#endif }; @@ -142,6 +152,14 @@ struct Symbol { Hex64 value; }; +/// Check whether the given section type indicates a zero-filled section. +// FIXME: Utility functions of this kind should probably be moved into +// llvm/Support. +inline bool isZeroFillSection(SectionType T) { + return (T == llvm::MachO::S_ZEROFILL || + T == llvm::MachO::S_THREAD_LOCAL_ZEROFILL); +} + /// A typedef so that YAML I/O can (de/en)code the protection bits of a segment. LLVM_YAML_STRONG_TYPEDEF(uint32_t, VMProtect) @@ -210,15 +228,9 @@ LLVM_YAML_STRONG_TYPEDEF(uint32_t, FileFlags) /// struct NormalizedFile { - NormalizedFile() : arch(MachOLinkingContext::arch_unknown), - fileType(llvm::MachO::MH_OBJECT), - flags(0), - hasUUID(false), - os(MachOLinkingContext::OS::unknown) { } - - MachOLinkingContext::Arch arch; - HeaderFileType fileType; - FileFlags flags; + MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown; + HeaderFileType fileType = llvm::MachO::MH_OBJECT; + FileFlags flags = 0; std::vector<Segment> segments; // Not used in object files. std::vector<Section> sections; @@ -229,19 +241,20 @@ struct NormalizedFile { // Maps to load commands with no LINKEDIT content (final linked images only). std::vector<DependentDylib> dependentDylibs; - StringRef installName; // dylibs only - PackedVersion compatVersion; // dylibs only - PackedVersion currentVersion; // dylibs only - bool hasUUID; + StringRef installName; // dylibs only + PackedVersion compatVersion = 0; // dylibs only + PackedVersion currentVersion = 0; // dylibs only + bool hasUUID = false; std::vector<StringRef> rpaths; - Hex64 entryAddress; - MachOLinkingContext::OS os; - Hex64 sourceVersion; - PackedVersion minOSverson; - PackedVersion sdkVersion; + Hex64 entryAddress = 0; + Hex64 stackSize = 0; + MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown; + Hex64 sourceVersion = 0; + PackedVersion minOSverson = 0; + PackedVersion sdkVersion = 0; // Maps to load commands with LINKEDIT content (final linked images only). - Hex32 pageSize; + Hex32 pageSize = 0; std::vector<RebaseLocation> rebasingInfo; std::vector<BindLocation> bindingInfo; std::vector<BindLocation> weakBindingInfo; @@ -264,8 +277,8 @@ bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch); /// If the buffer is a fat file with the request arch, then this function /// returns true with 'offset' and 'size' set to location of the arch slice /// within the buffer. Otherwise returns false; -bool sliceFromFatFile(const MemoryBuffer &mb, MachOLinkingContext::Arch arch, - uint32_t &offset, uint32_t &size); +bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch, + uint32_t &offset, uint32_t &size); /// Reads a mach-o file and produces an in-memory normalized view. ErrorOr<std::unique_ptr<NormalizedFile>> diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp index 07a6dbfe569b..1013d3ddaef3 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp @@ -30,6 +30,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/Object/MachO.h" #include "llvm/Support/Casting.h" @@ -169,9 +170,8 @@ bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) { return true; } - -bool sliceFromFatFile(const MemoryBuffer &mb, MachOLinkingContext::Arch arch, - uint32_t &offset, uint32_t &size) { +bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch, + uint32_t &offset, uint32_t &size) { const char *start = mb.getBufferStart(); const llvm::MachO::fat_header *fh = reinterpret_cast<const llvm::MachO::fat_header *>(start); @@ -211,7 +211,7 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, uint32_t sliceOffset; uint32_t sliceSize; - if (sliceFromFatFile(*mb, arch, sliceOffset, sliceSize)) { + if (sliceFromFatFile(mb->getMemBufferRef(), arch, sliceOffset, sliceSize)) { start = &start[sliceOffset]; objSize = sliceSize; mh = reinterpret_cast<const mach_header *>(start); @@ -297,7 +297,7 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, section.type = (SectionType)(read32(§->flags, isBig) & SECTION_TYPE); section.attributes = read32(§->flags, isBig) & SECTION_ATTRIBUTES; - section.alignment = read32(§->align, isBig); + section.alignment = 1 << read32(§->align, isBig); section.address = read64(§->addr, isBig); const uint8_t *content = (const uint8_t *)start + read32(§->offset, isBig); @@ -341,7 +341,7 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb, SECTION_TYPE); section.attributes = read32((const uint8_t *)§->flags, isBig) & SECTION_ATTRIBUTES; - section.alignment = read32(§->align, isBig); + section.alignment = 1 << read32(§->align, isBig); section.address = read32(§->addr, isBig); const uint8_t *content = (const uint8_t *)start + read32(§->offset, isBig); @@ -516,22 +516,17 @@ class MachOObjectReader : public Reader { public: MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {} - bool canParse(file_magic magic, StringRef ext, - const MemoryBuffer &mb) const override { - switch (magic) { - case llvm::sys::fs::file_magic::macho_object: - return (mb.getBufferSize() > 32); - default: - return false; - } + bool canParse(file_magic magic, MemoryBufferRef mb) const override { + return (magic == llvm::sys::fs::file_magic::macho_object && + mb.getBufferSize() > 32); } - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, - std::vector<std::unique_ptr<File>> &result) const override { - auto *file = new MachOFile(std::move(mb), &_ctx); - result.push_back(std::unique_ptr<MachOFile>(file)); - return std::error_code(); + ErrorOr<std::unique_ptr<File>> + loadFile(std::unique_ptr<MemoryBuffer> mb, + const Registry ®istry) const override { + std::unique_ptr<File> ret = + llvm::make_unique<MachOFile>(std::move(mb), &_ctx); + return std::move(ret); } private: @@ -542,23 +537,22 @@ class MachODylibReader : public Reader { public: MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {} - bool canParse(file_magic magic, StringRef ext, - const MemoryBuffer &mb) const override { + bool canParse(file_magic magic, MemoryBufferRef mb) const override { switch (magic) { case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib: case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib_stub: - return (mb.getBufferSize() > 32); + return mb.getBufferSize() > 32; default: return false; } } - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, - std::vector<std::unique_ptr<File>> &result) const override { - auto *file = new MachODylibFile(std::move(mb), &_ctx); - result.push_back(std::unique_ptr<MachODylibFile>(file)); - return std::error_code(); + ErrorOr<std::unique_ptr<File>> + loadFile(std::unique_ptr<MemoryBuffer> mb, + const Registry ®istry) const override { + std::unique_ptr<File> ret = + llvm::make_unique<MachODylibFile>(std::move(mb), &_ctx); + return std::move(ret); } private: diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp index be7acf9d4d60..4ecfece0629e 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp @@ -149,6 +149,7 @@ private: const uint8_t *bytes() { return reinterpret_cast<const uint8_t*>(_ostream.str().data()); } + private: SmallVector<char, 128> _bytes; // Stream ivar must be after SmallVector ivar to construct properly. @@ -159,7 +160,6 @@ private: struct TrieEdge { TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {} - ~TrieEdge() {} StringRef _subString; struct TrieNode *_child; @@ -169,14 +169,14 @@ private: TrieNode(StringRef s) : _cummulativeString(s), _address(0), _flags(0), _other(0), _trieOffset(0), _hasExportInfo(false) {} - ~TrieNode() {} + ~TrieNode() = default; void addSymbol(const Export &entry, BumpPtrAllocator &allocator, std::vector<TrieNode *> &allNodes); bool updateOffset(uint32_t &offset); void appendToByteBuffer(ByteBuffer &out); -private: + private: StringRef _cummulativeString; std::list<TrieEdge> _children; uint64_t _address; @@ -263,7 +263,6 @@ size_t MachOFileLayout::headerAndLoadCommandsSize() const { return _endOfLoadCommands; } - MachOFileLayout::MachOFileLayout(const NormalizedFile &file) : _file(file), _is64(MachOLinkingContext::is64Bit(file.arch)), @@ -290,12 +289,12 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file) unsigned relocCount = 0; uint64_t offset = _startOfSectionsContent; for (const Section § : file.sections) { - if (sect.type != llvm::MachO::S_ZEROFILL) { - offset = llvm::RoundUpToAlignment(offset, 1 << sect.alignment); + if (isZeroFillSection(sect.type)) + _sectInfo[§].fileOffset = 0; + else { + offset = llvm::RoundUpToAlignment(offset, sect.alignment); _sectInfo[§].fileOffset = offset; offset += sect.content.size(); - } else { - _sectInfo[§].fileOffset = 0; } relocCount += sect.relocations.size(); } @@ -430,7 +429,7 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { // Add LC_RPATH for (const StringRef &path : _file.rpaths) { - size += sizeof(rpath_command) + pointerAlign(path.size()+1); + size += pointerAlign(sizeof(rpath_command) + path.size() + 1); ++count; } @@ -531,7 +530,7 @@ void MachOFileLayout::buildFileOffsets() { for (const Section *s : _segInfo[&sg].sections) { uint32_t sectOffset = s->address - sg.address; uint32_t sectFileSize = - s->type == llvm::MachO::S_ZEROFILL ? 0 : s->content.size(); + isZeroFillSection(s->type) ? 0 : s->content.size(); segFileSize = std::max(segFileSize, sectOffset + sectFileSize); _sectInfo[s].fileOffset = _segInfo[&sg].fileOffset + sectOffset; @@ -549,7 +548,6 @@ void MachOFileLayout::buildFileOffsets() { _startOfLinkEdit = fileOffset; } - size_t MachOFileLayout::size() const { return _endOfSymbolStrings; } @@ -613,7 +611,7 @@ std::error_code MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { sout->addr = sin.address; sout->size = sin.content.size(); sout->offset = _sectInfo[&sin].fileOffset; - sout->align = sin.alignment; + sout->align = llvm::Log2_32(sin.alignment); sout->reloff = sin.relocations.empty() ? 0 : relOffset; sout->nreloc = sin.relocations.size(); sout->flags = sin.type | sin.attributes; @@ -657,11 +655,11 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { setString16(section->segmentName, sect->segname); sect->addr = section->address; sect->size = section->content.size(); - if (section->type == llvm::MachO::S_ZEROFILL) + if (isZeroFillSection(section->type)) sect->offset = 0; else sect->offset = section->address - seg.address + segInfo.fileOffset; - sect->align = section->alignment; + sect->align = llvm::Log2_32(section->alignment); sect->reloff = 0; sect->nreloc = 0; sect->flags = section->type | section->attributes; @@ -829,7 +827,7 @@ std::error_code MachOFileLayout::writeLoadCommands() { ep->cmd = LC_MAIN; ep->cmdsize = sizeof(entry_point_command); ep->entryoff = _file.entryAddress - _seg1addr; - ep->stacksize = 0; + ep->stacksize = _file.stackSize; if (_swap) swapStruct(*ep); lc += sizeof(entry_point_command); @@ -856,7 +854,7 @@ std::error_code MachOFileLayout::writeLoadCommands() { // Add LC_RPATH for (const StringRef &path : _file.rpaths) { rpath_command *rpc = reinterpret_cast<rpath_command *>(lc); - uint32_t size = sizeof(rpath_command) + pointerAlign(path.size()+1); + uint32_t size = pointerAlign(sizeof(rpath_command) + path.size() + 1); rpc->cmd = LC_RPATH; rpc->cmdsize = size; rpc->path = sizeof(rpath_command); // offset @@ -882,11 +880,10 @@ std::error_code MachOFileLayout::writeLoadCommands() { return ec; } - void MachOFileLayout::writeSectionContent() { for (const Section &s : _file.sections) { // Copy all section content to output buffer. - if (s.type == llvm::MachO::S_ZEROFILL) + if (isZeroFillSection(s.type)) continue; if (s.content.empty()) continue; @@ -909,7 +906,6 @@ void MachOFileLayout::writeRelocations() { } } - void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols, uint32_t &symOffset, uint32_t &strOffset) { for (const Symbol &sym : symbols) { @@ -1043,7 +1039,12 @@ void MachOFileLayout::buildBindInfo() { _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | entry.segIndex); _bindingInfo.append_uleb128(entry.segOffset); - _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | entry.ordinal); + if (entry.ordinal > 0) + _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | + (entry.ordinal & 0xF)); + else + _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | + (entry.ordinal & 0xF)); _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); _bindingInfo.append_string(entry.symbolName); if (entry.addend != lastAddend) { @@ -1063,7 +1064,12 @@ void MachOFileLayout::buildLazyBindInfo() { _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | entry.segIndex); _lazyBindingInfo.append_uleb128Fixed(entry.segOffset, 5); - _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | entry.ordinal); + if (entry.ordinal > 0) + _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | + (entry.ordinal & 0xF)); + else + _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | + (entry.ordinal & 0xF)); _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); _lazyBindingInfo.append_string(entry.symbolName); _lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND); @@ -1090,7 +1096,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, // Splice in new node: was A -> C, now A -> B -> C StringRef bNodeStr = edge._child->_cummulativeString; bNodeStr = bNodeStr.drop_back(edgeStr.size()-n).copy(allocator); - TrieNode* bNode = new (allocator) TrieNode(bNodeStr); + auto *bNode = new (allocator) TrieNode(bNodeStr); allNodes.push_back(bNode); TrieNode* cNode = edge._child; StringRef abEdgeStr = edgeStr.substr(0,n).copy(allocator); @@ -1103,7 +1109,7 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, TrieEdge& abEdge = edge; abEdge._subString = abEdgeStr; abEdge._child = bNode; - TrieEdge *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode); + auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode); bNode->_children.push_back(std::move(*bcEdge)); bNode->addSymbol(entry, allocator, allNodes); return; @@ -1117,8 +1123,8 @@ void MachOFileLayout::TrieNode::addSymbol(const Export& entry, assert(entry.otherOffset != 0); } // No commonality with any existing child, make a new edge. - TrieNode* newNode = new (allocator) TrieNode(entry.name.copy(allocator)); - TrieEdge *newEdge = new (allocator) TrieEdge(partialStr, newNode); + auto *newNode = new (allocator) TrieNode(entry.name.copy(allocator)); + auto *newEdge = new (allocator) TrieEdge(partialStr, newNode); _children.push_back(std::move(*newEdge)); DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() << "new TrieNode('" << entry.name << "') with edge '" @@ -1230,7 +1236,7 @@ void MachOFileLayout::buildExportTrie() { BumpPtrAllocator allocator; // Build trie of all exported symbols. - TrieNode* rootNode = new (allocator) TrieNode(StringRef()); + auto *rootNode = new (allocator) TrieNode(StringRef()); std::vector<TrieNode*> allNodes; allNodes.reserve(_file.exportInfo.size()*2); allNodes.push_back(rootNode); @@ -1257,14 +1263,13 @@ void MachOFileLayout::buildExportTrie() { _exportTrie.align(_is64 ? 8 : 4); } - void MachOFileLayout::computeSymbolTableSizes() { // MachO symbol tables have three ranges: locals, globals, and undefines const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist)); _symbolTableSize = nlistSize * (_file.localSymbols.size() + _file.globalSymbols.size() + _file.undefinedSymbols.size()); - _symbolStringPoolSize = 0; + _symbolStringPoolSize = 1; // Always reserve 1-byte for the empty string. for (const Symbol &sym : _file.localSymbols) { _symbolStringPoolSize += (sym.name.size()+1); } @@ -1310,19 +1315,18 @@ std::error_code MachOFileLayout::writeBinary(StringRef path) { if (_ec) return _ec; // Create FileOutputBuffer with calculated size. - std::unique_ptr<llvm::FileOutputBuffer> fob; unsigned flags = 0; if (_file.fileType != llvm::MachO::MH_OBJECT) flags = llvm::FileOutputBuffer::F_executable; - std::error_code ec; - ec = llvm::FileOutputBuffer::create(path, size(), fob, flags); - if (ec) + ErrorOr<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr = + llvm::FileOutputBuffer::create(path, size(), flags); + if (std::error_code ec = fobOrErr.getError()) return ec; - + std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr; // Write content. _buffer = fob->getBufferStart(); writeMachHeader(); - ec = writeLoadCommands(); + std::error_code ec = writeLoadCommands(); if (ec) return ec; writeSectionContent(); @@ -1332,15 +1336,12 @@ std::error_code MachOFileLayout::writeBinary(StringRef path) { return std::error_code(); } - /// Takes in-memory normalized view and writes a mach-o object file. std::error_code writeBinary(const NormalizedFile &file, StringRef path) { MachOFileLayout layout(file); return layout.writeBinary(path); } - } // namespace normalized } // namespace mach_o } // namespace lld - diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index 4d6183f71df7..f80e2ac467fc 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -58,7 +58,7 @@ struct SectionInfo { uint32_t attributes; uint64_t address; uint64_t size; - uint32_t alignment; + uint16_t alignment; std::vector<AtomInfo> atomsAndOffsets; uint32_t normalizedSectionIndex; uint32_t finalSectionIndex; @@ -67,9 +67,9 @@ struct SectionInfo { SectionInfo::SectionInfo(StringRef sg, StringRef sct, SectionType t, const MachOLinkingContext &ctxt, uint32_t attrs) : segmentName(sg), sectionName(sct), type(t), attributes(attrs), - address(0), size(0), alignment(0), + address(0), size(0), alignment(1), normalizedSectionIndex(0), finalSectionIndex(0) { - uint8_t align; + uint16_t align = 1; if (ctxt.sectionAligned(segmentName, sectionName, align)) { alignment = align; } @@ -90,11 +90,11 @@ SegmentInfo::SegmentInfo(StringRef n) : name(n), address(0), size(0), access(0), normalizedSegmentIndex(0) { } - class Util { public: Util(const MachOLinkingContext &ctxt) - : _context(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr) {} + : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr), + _hasTLVDescriptors(false) {} ~Util(); void assignAtomsToSections(const lld::File &atomFile); @@ -142,7 +142,6 @@ private: void appendSection(SectionInfo *si, NormalizedFile &file); uint32_t sectionIndexForAtom(const Atom *atom); - static uint64_t alignTo(uint64_t value, uint8_t align2); typedef llvm::DenseMap<const Atom*, uint32_t> AtomToIndex; struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; }; struct AtomSorter { @@ -157,7 +156,7 @@ private: static unsigned weight(const SectionInfo *); }; - const MachOLinkingContext &_context; + const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; llvm::BumpPtrAllocator _allocator; std::vector<SectionInfo*> _sectionInfos; @@ -169,6 +168,7 @@ private: const DefinedAtom *_entryAtom; AtomToIndex _atomToSymbolIndex; std::vector<const Atom *> _machHeaderAliasAtoms; + bool _hasTLVDescriptors; }; Util::~Util() { @@ -206,9 +206,8 @@ SectionInfo *Util::getRelocatableSection(DefinedAtom::ContentType type) { } } // Otherwise allocate new SectionInfo object. - SectionInfo *sect = new (_allocator) SectionInfo(segmentName, sectionName, - sectionType, _context, - sectionAttrs); + auto *sect = new (_allocator) + SectionInfo(segmentName, sectionName, sectionType, _ctx, sectionAttrs); _sectionInfos.push_back(sect); _sectionMap[type] = sect; return sect; @@ -248,12 +247,19 @@ const MachOFinalSectionFromAtomType sectsToAtomType[] = { typeTerminatorPtr), ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, typeGOT), + ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, + typeThunkTLV), + ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, + typeTLVInitialData), + ENTRY("__DATA", "__thread_ptrs", S_THREAD_LOCAL_VARIABLE_POINTERS, + typeTLVInitializerPtr), + ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL, + typeTLVInitialZeroFill), ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), }; #undef ENTRY - SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { for (auto &p : sectsToAtomType) { if (p.atomType != atomType) @@ -265,6 +271,9 @@ SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { case DefinedAtom::typeStubHelper: sectionAttrs = S_ATTR_PURE_INSTRUCTIONS; break; + case DefinedAtom::typeThunkTLV: + _hasTLVDescriptors = true; + break; default: break; } @@ -277,11 +286,8 @@ SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { } } // Otherwise allocate new SectionInfo object. - SectionInfo *sect = new (_allocator) SectionInfo(p.segmentName, - p.sectionName, - p.sectionType, - _context, - sectionAttrs); + auto *sect = new (_allocator) SectionInfo( + p.segmentName, p.sectionName, p.sectionType, _ctx, sectionAttrs); _sectionInfos.push_back(sect); _sectionMap[atomType] = sect; return sect; @@ -289,8 +295,6 @@ SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { llvm_unreachable("content type not yet supported"); } - - SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) { if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) { // Section for this atom is derived from content type. @@ -298,7 +302,7 @@ SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) { auto pos = _sectionMap.find(type); if ( pos != _sectionMap.end() ) return pos->second; - bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); return rMode ? getRelocatableSection(type) : getFinalSection(type); } else { // This atom needs to be in a custom section. @@ -315,31 +319,30 @@ SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) { assert(seperatorIndex != StringRef::npos); StringRef segName = customName.slice(0, seperatorIndex); StringRef sectName = customName.drop_front(seperatorIndex + 1); - SectionInfo *sect = new (_allocator) SectionInfo(segName, sectName, - S_REGULAR, _context); + auto *sect = + new (_allocator) SectionInfo(segName, sectName, S_REGULAR, _ctx); _customSections.push_back(sect); _sectionInfos.push_back(sect); return sect; } } - void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) { // Figure out offset for atom in this section given alignment constraints. uint64_t offset = sect->size; DefinedAtom::Alignment atomAlign = atom->alignment(); - uint64_t align2 = 1 << atomAlign.powerOf2; + uint64_t align = atomAlign.value; uint64_t requiredModulus = atomAlign.modulus; - uint64_t currentModulus = (offset % align2); + uint64_t currentModulus = (offset % align); if ( currentModulus != requiredModulus ) { if ( requiredModulus > currentModulus ) offset += requiredModulus-currentModulus; else - offset += align2+requiredModulus-currentModulus; + offset += align+requiredModulus-currentModulus; } // Record max alignment of any atom in this section. - if ( atomAlign.powerOf2 > sect->alignment ) - sect->alignment = atomAlign.powerOf2; + if (align > sect->alignment) + sect->alignment = atomAlign.value; // Assign atom to this section with this offset. AtomInfo ai = {atom, offset}; sect->atomsAndOffsets.push_back(ai); @@ -361,7 +364,7 @@ SegmentInfo *Util::segmentForName(StringRef segName) { if ( si->name.equals(segName) ) return si; } - SegmentInfo *info = new (_allocator) SegmentInfo(segName); + auto *info = new (_allocator) SegmentInfo(segName); if (segName.equals("__TEXT")) info->access = VM_PROT_READ | VM_PROT_EXECUTE; else if (segName.equals("__DATA")) @@ -402,16 +405,15 @@ bool Util::TextSectionSorter::operator()(const SectionInfo *left, return (weight(left) < weight(right)); } - void Util::organizeSections() { - if (_context.outputMachOType() == llvm::MachO::MH_OBJECT) { + if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) { // Leave sections ordered as normalized file specified. uint32_t sectionIndex = 1; for (SectionInfo *si : _sectionInfos) { si->finalSectionIndex = sectionIndex++; } } else { - switch (_context.outputMachOType()){ + switch (_ctx.outputMachOType()) { case llvm::MachO::MH_EXECUTE: // Main executables, need a zero-page segment segmentForName("__PAGEZERO"); @@ -450,24 +452,17 @@ void Util::organizeSections() { } } } - } -uint64_t Util::alignTo(uint64_t value, uint8_t align2) { - return llvm::RoundUpToAlignment(value, 1 << align2); -} - - void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) { seg->address = addr; for (SectionInfo *sect : seg->sections) { - sect->address = alignTo(addr, sect->alignment); + sect->address = llvm::RoundUpToAlignment(addr, sect->alignment); addr = sect->address + sect->size; } - seg->size = llvm::RoundUpToAlignment(addr - seg->address,_context.pageSize()); + seg->size = llvm::RoundUpToAlignment(addr - seg->address, _ctx.pageSize()); } - // __TEXT segment lays out backwards so padding is at front after load commands. void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, uint64_t &addr) { @@ -477,39 +472,38 @@ void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) { SectionInfo *sect = *it; taddr -= sect->size; - taddr = taddr & (0 - (1 << sect->alignment)); + taddr = taddr & (0 - sect->alignment); } int64_t padding = taddr - hlcSize; while (padding < 0) - padding += _context.pageSize(); + padding += _ctx.pageSize(); // Start assigning section address starting at padded offset. addr += (padding + hlcSize); for (SectionInfo *sect : seg->sections) { - sect->address = alignTo(addr, sect->alignment); + sect->address = llvm::RoundUpToAlignment(addr, sect->alignment); addr = sect->address + sect->size; } - seg->size = llvm::RoundUpToAlignment(addr - seg->address,_context.pageSize()); + seg->size = llvm::RoundUpToAlignment(addr - seg->address, _ctx.pageSize()); } - void Util::assignAddressesToSections(const NormalizedFile &file) { size_t hlcSize = headerAndLoadCommandsSize(file); uint64_t address = 0; - if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) { + if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) { for (SegmentInfo *seg : _segmentInfos) { if (seg->name.equals("__PAGEZERO")) { - seg->size = _context.pageZeroSize(); + seg->size = _ctx.pageZeroSize(); address += seg->size; } else if (seg->name.equals("__TEXT")) { - // _context.baseAddress() == 0 implies it was either unspecified or + // _ctx.baseAddress() == 0 implies it was either unspecified or // pageZeroSize is also 0. In either case resetting address is safe. - address = _context.baseAddress() ? _context.baseAddress() : address; + address = _ctx.baseAddress() ? _ctx.baseAddress() : address; layoutSectionsInTextSegment(hlcSize, seg, address); } else layoutSectionsInSegment(seg, address); - address = llvm::RoundUpToAlignment(address, _context.pageSize()); + address = llvm::RoundUpToAlignment(address, _ctx.pageSize()); } DEBUG_WITH_TYPE("WriterMachO-norm", llvm::dbgs() << "assignAddressesToSections()\n"; @@ -528,7 +522,7 @@ void Util::assignAddressesToSections(const NormalizedFile &file) { ); } else { for (SectionInfo *sect : _sectionInfos) { - sect->address = alignTo(address, sect->alignment); + sect->address = llvm::RoundUpToAlignment(address, sect->alignment); address = sect->address + sect->size; } DEBUG_WITH_TYPE("WriterMachO-norm", @@ -543,7 +537,6 @@ void Util::assignAddressesToSections(const NormalizedFile &file) { } } - void Util::copySegmentInfo(NormalizedFile &file) { for (SegmentInfo *sgi : _segmentInfos) { Segment seg; @@ -572,7 +565,7 @@ void Util::appendSection(SectionInfo *si, NormalizedFile &file) { } void Util::copySectionContent(NormalizedFile &file) { - const bool r = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + const bool r = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); // Utility function for ArchHandler to find address of atom in output file. auto addrForAtom = [&] (const Atom &atom) -> uint64_t { @@ -591,7 +584,7 @@ void Util::copySectionContent(NormalizedFile &file) { for (SectionInfo *si : _sectionInfos) { Section *normSect = &file.sections[si->normalizedSectionIndex]; - if (si->type == llvm::MachO::S_ZEROFILL) { + if (isZeroFillSection(si->type)) { const uint8_t *empty = nullptr; normSect->content = llvm::makeArrayRef(empty, si->size); continue; @@ -603,17 +596,16 @@ void Util::copySectionContent(NormalizedFile &file) { uint8_t *atomContent = reinterpret_cast<uint8_t*> (§ionContent[ai.offsetInSection]); _archHandler.generateAtomContent(*ai.atom, r, addrForAtom, - sectionAddrForAtom, - _context.baseAddress(), atomContent); + sectionAddrForAtom, _ctx.baseAddress(), + atomContent); } } } - void Util::copySectionInfo(NormalizedFile &file) { file.sections.reserve(_sectionInfos.size()); // For final linked images, write sections grouped by segment. - if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) { + if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) { for (SegmentInfo *sgi : _segmentInfos) { for (SectionInfo *si : sgi->sections) { appendSection(si, file); @@ -629,7 +621,7 @@ void Util::copySectionInfo(NormalizedFile &file) { void Util::updateSectionInfo(NormalizedFile &file) { file.sections.reserve(_sectionInfos.size()); - if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) { + if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) { // For final linked images, sections grouped by segment. for (SegmentInfo *sgi : _segmentInfos) { Segment *normSeg = &file.segments[sgi->normalizedSegmentIndex]; @@ -650,7 +642,7 @@ void Util::updateSectionInfo(NormalizedFile &file) { } void Util::copyEntryPointAddress(NormalizedFile &nFile) { - if (_context.outputTypeHasEntry()) { + if (_ctx.outputTypeHasEntry()) { if (_archHandler.isThumbFunction(*_entryAtom)) nFile.entryAddress = (_atomToAddress[_entryAtom] | 1); else @@ -661,13 +653,13 @@ void Util::copyEntryPointAddress(NormalizedFile &nFile) { void Util::buildAtomToAddressMap() { DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << "assign atom addresses:\n"); - const bool lookForEntry = _context.outputTypeHasEntry(); + const bool lookForEntry = _ctx.outputTypeHasEntry(); for (SectionInfo *sect : _sectionInfos) { for (const AtomInfo &info : sect->atomsAndOffsets) { _atomToAddress[info.atom] = sect->address + info.offsetInSection; if (lookForEntry && (info.atom->contentType() == DefinedAtom::typeCode) && (info.atom->size() != 0) && - info.atom->name() == _context.entrySymbolName()) { + info.atom->name() == _ctx.entrySymbolName()) { _entryAtom = info.atom; } DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() @@ -678,7 +670,7 @@ void Util::buildAtomToAddressMap() { } } for (const Atom *atom : _machHeaderAliasAtoms) { - _atomToAddress[atom] = _context.baseAddress(); + _atomToAddress[atom] = _ctx.baseAddress(); DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() << " address=" << llvm::format("0x%016X", _atomToAddress[atom]) @@ -715,30 +707,28 @@ uint16_t Util::descBits(const DefinedAtom* atom) { return desc; } - bool Util::AtomSorter::operator()(const AtomAndIndex &left, const AtomAndIndex &right) { return (left.atom->name().compare(right.atom->name()) < 0); } - std::error_code Util::getSymbolTableRegion(const DefinedAtom* atom, bool &inGlobalsRegion, SymbolScope &scope) { - bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); switch (atom->scope()) { case Atom::scopeTranslationUnit: scope = 0; inGlobalsRegion = false; return std::error_code(); case Atom::scopeLinkageUnit: - if ((_context.exportMode() == MachOLinkingContext::ExportMode::whiteList) - && _context.exportSymbolNamed(atom->name())) { + if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) && + _ctx.exportSymbolNamed(atom->name())) { return make_dynamic_error_code(Twine("cannot export hidden symbol ") + atom->name()); } if (rMode) { - if (_context.keepPrivateExterns()) { + if (_ctx.keepPrivateExterns()) { // -keep_private_externs means keep in globals region as N_PEXT. scope = N_PEXT | N_EXT; inGlobalsRegion = true; @@ -750,8 +740,8 @@ std::error_code Util::getSymbolTableRegion(const DefinedAtom* atom, inGlobalsRegion = false; return std::error_code(); case Atom::scopeGlobal: - if (_context.exportRestrictMode()) { - if (_context.exportSymbolNamed(atom->name())) { + if (_ctx.exportRestrictMode()) { + if (_ctx.exportSymbolNamed(atom->name())) { scope = N_EXT; inGlobalsRegion = true; return std::error_code(); @@ -772,7 +762,7 @@ std::error_code Util::getSymbolTableRegion(const DefinedAtom* atom, std::error_code Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) { - bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT); // Mach-O symbol table has three regions: locals, globals, undefs. // Add all local (non-global) symbols in address order @@ -835,7 +825,6 @@ std::error_code Util::addSymbols(const lld::File &atomFile, file.globalSymbols.push_back(sym); } - // Sort undefined symbol alphabetically, then add to symbol table. std::vector<AtomAndIndex> undefs; undefs.reserve(128); @@ -853,7 +842,9 @@ std::error_code Util::addSymbols(const lld::File &atomFile, Symbol sym; uint16_t desc = 0; if (!rMode) { - uint8_t ordinal = dylibOrdinal(dyn_cast<SharedLibraryAtom>(ai.atom)); + uint8_t ordinal = 0; + if (!_ctx.useFlatNamespace()) + ordinal = dylibOrdinal(dyn_cast<SharedLibraryAtom>(ai.atom)); llvm::MachO::SET_LIBRARY_ORDINAL(desc, ordinal); } sym.name = ai.atom->name(); @@ -891,7 +882,6 @@ const Atom *Util::targetOfStub(const DefinedAtom *stubAtom) { return nullptr; } - void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) { for (SectionInfo *si : _sectionInfos) { Section &normSect = file.sections[si->normalizedSectionIndex]; @@ -940,7 +930,6 @@ void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) { break; } } - } void Util::addDependentDylibs(const lld::File &atomFile,NormalizedFile &nFile) { @@ -951,16 +940,28 @@ void Util::addDependentDylibs(const lld::File &atomFile,NormalizedFile &nFile) { DylibPathToInfo::iterator pos = _dylibInfo.find(loadPath); if (pos == _dylibInfo.end()) { DylibInfo info; - info.ordinal = ordinal++; + bool flatNamespaceAtom = &slAtom->file() == _ctx.flatNamespaceFile(); + + // If we're in -flat_namespace mode (or this atom came from the flat + // namespace file under -undefined dynamic_lookup) then use the flat + // lookup ordinal. + if (flatNamespaceAtom || _ctx.useFlatNamespace()) + info.ordinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + else + info.ordinal = ordinal++; info.hasWeak = slAtom->canBeNullAtRuntime(); info.hasNonWeak = !info.hasWeak; _dylibInfo[loadPath] = info; - DependentDylib depInfo; - depInfo.path = loadPath; - depInfo.kind = llvm::MachO::LC_LOAD_DYLIB; - depInfo.currentVersion = _context.dylibCurrentVersion(loadPath); - depInfo.compatVersion = _context.dylibCompatVersion(loadPath); - nFile.dependentDylibs.push_back(depInfo); + + // Unless this was a flat_namespace atom, record the source dylib. + if (!flatNamespaceAtom) { + DependentDylib depInfo; + depInfo.path = loadPath; + depInfo.kind = llvm::MachO::LC_LOAD_DYLIB; + depInfo.currentVersion = _ctx.dylibCurrentVersion(loadPath); + depInfo.compatVersion = _ctx.dylibCompatVersion(loadPath); + nFile.dependentDylibs.push_back(depInfo); + } } else { if ( slAtom->canBeNullAtRuntime() ) pos->second.hasWeak = true; @@ -973,12 +974,11 @@ void Util::addDependentDylibs(const lld::File &atomFile,NormalizedFile &nFile) { DylibInfo &info = _dylibInfo[dep.path]; if (info.hasWeak && !info.hasNonWeak) dep.kind = llvm::MachO::LC_LOAD_WEAK_DYLIB; - else if (_context.isUpwardDylib(dep.path)) + else if (_ctx.isUpwardDylib(dep.path)) dep.kind = llvm::MachO::LC_LOAD_UPWARD_DYLIB; } } - int Util::dylibOrdinal(const SharedLibraryAtom *sa) { return _dylibInfo[sa->loadName()].ordinal; } @@ -997,7 +997,6 @@ void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex, llvm_unreachable("section not in any segment"); } - uint32_t Util::sectionIndexForAtom(const Atom *atom) { uint64_t address = _atomToAddress[atom]; uint32_t index = 1; @@ -1010,10 +1009,9 @@ uint32_t Util::sectionIndexForAtom(const Atom *atom) { } void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) { - if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) + if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) return; - // Utility function for ArchHandler to find symbol index for an atom. auto symIndexForAtom = [&] (const Atom &atom) -> uint32_t { auto pos = _atomToSymbolIndex.find(&atom); @@ -1090,7 +1088,7 @@ void Util::buildDataInCodeArray(const lld::File &, NormalizedFile &file) { void Util::addRebaseAndBindingInfo(const lld::File &atomFile, NormalizedFile &nFile) { - if (_context.outputMachOType() == llvm::MachO::MH_OBJECT) + if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) return; uint8_t segmentIndex; @@ -1146,7 +1144,7 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, } void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { - if (_context.outputMachOType() == llvm::MachO::MH_OBJECT) + if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT) return; for (SectionInfo *sect : _sectionInfos) { @@ -1154,13 +1152,13 @@ void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { const DefinedAtom *atom = info.atom; if (atom->scope() != Atom::scopeGlobal) continue; - if (_context.exportRestrictMode()) { - if (!_context.exportSymbolNamed(atom->name())) + if (_ctx.exportRestrictMode()) { + if (!_ctx.exportSymbolNamed(atom->name())) continue; } Export exprt; exprt.name = atom->name(); - exprt.offset = _atomToAddress[atom]; // FIXME: subtract base address + exprt.offset = _atomToAddress[atom] - _ctx.baseAddress(); exprt.kind = EXPORT_SYMBOL_FLAGS_KIND_REGULAR; if (atom->merge() == DefinedAtom::mergeAsWeak) exprt.flags = EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; @@ -1175,19 +1173,22 @@ void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { uint32_t Util::fileFlags() { // FIXME: these need to determined at runtime. - if (_context.outputMachOType() == MH_OBJECT) { + if (_ctx.outputMachOType() == MH_OBJECT) { return MH_SUBSECTIONS_VIA_SYMBOLS; } else { - if ((_context.outputMachOType() == MH_EXECUTE) && _context.PIE()) - return MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL | MH_PIE; - else - return MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL; + uint32_t flags = MH_DYLDLINK; + if (!_ctx.useFlatNamespace()) + flags |= MH_TWOLEVEL | MH_NOUNDEFS; + if ((_ctx.outputMachOType() == MH_EXECUTE) && _ctx.PIE()) + flags |= MH_PIE; + if (_hasTLVDescriptors) + flags |= (MH_PIE | MH_HAS_TLV_DESCRIPTORS); + return flags; } } } // end anonymous namespace - namespace lld { namespace mach_o { namespace normalized { @@ -1206,6 +1207,7 @@ normalizedFromAtoms(const lld::File &atomFile, normFile.arch = context.arch(); normFile.fileType = context.outputMachOType(); normFile.flags = util.fileFlags(); + normFile.stackSize = context.stackSize(); normFile.installName = context.installName(); normFile.currentVersion = context.currentVersion(); normFile.compatVersion = context.compatibilityVersion(); @@ -1231,8 +1233,6 @@ normalizedFromAtoms(const lld::File &atomFile, return std::move(f); } - } // namespace normalized } // namespace mach_o } // namespace lld - diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp index 124e0eaffeeb..1c4bb1d4f6a3 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -27,12 +27,17 @@ #include "MachONormalizedFileBinaryUtils.h" #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" #include "llvm/Support/MachO.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; +#define DEBUG_TYPE "normalized-file-to-atoms" + namespace lld { namespace mach_o { @@ -78,6 +83,11 @@ const MachORelocatableSectionToAtomType sectsToAtomType[] = { ENTRY("", "", S_NON_LAZY_SYMBOL_POINTERS, typeGOT), ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), + ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, + typeThunkTLV), + ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData), + ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL, + typeTLVInitialZeroFill), ENTRY("", "", S_INTERPOSING, typeInterposingTuples), ENTRY("__LD", "__compact_unwind", S_REGULAR, typeCompactUnwindInfo), @@ -228,7 +238,7 @@ void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion, uint64_t size = nextSymbolAddr - symbolAddr; uint64_t offset = symbolAddr - section.address; bool noDeadStrip = (symbolDescFlags & N_NO_DEAD_STRIP) || !scatterable; - if (section.type == llvm::MachO::S_ZEROFILL) { + if (isZeroFillSection(section.type)) { file.addZeroFillDefinedAtom(symbolName, symbolScope, offset, size, noDeadStrip, copyRefs, §ion); } else { @@ -558,17 +568,17 @@ std::error_code convertRelocs(const Section §ion, *result = target; return std::error_code(); } - return make_dynamic_error_code(Twine("no atom found for defined symbol")); + return make_dynamic_error_code("no atom found for defined symbol"); } else if ((sym->type & N_TYPE) == N_UNDF) { const lld::Atom *target = file.findUndefAtom(sym->name); if (target) { *result = target; return std::error_code(); } - return make_dynamic_error_code(Twine("no undefined atom found for sym")); + return make_dynamic_error_code("no undefined atom found for sym"); } else { // Search undefs - return make_dynamic_error_code(Twine("no atom found for symbol")); + return make_dynamic_error_code("no atom found for symbol"); } }; @@ -594,10 +604,37 @@ std::error_code convertRelocs(const Section §ion, Reference::KindValue kind; std::error_code relocErr; if (handler.isPairedReloc(reloc)) { - // Handle paired relocations together. + // Handle paired relocations together. + const Relocation &reloc2 = *++it; relocErr = handler.getPairReferenceInfo( - reloc, *++it, inAtom, offsetInAtom, fixupAddress, isBig, scatterable, + reloc, reloc2, inAtom, offsetInAtom, fixupAddress, isBig, scatterable, atomByAddr, atomBySymbol, &kind, &target, &addend); + if (relocErr) { + return make_dynamic_error_code( + Twine("bad relocation (") + relocErr.message() + + ") in section " + + section.segmentName + "/" + section.sectionName + + " (r1_address=" + Twine::utohexstr(reloc.offset) + + ", r1_type=" + Twine(reloc.type) + + ", r1_extern=" + Twine(reloc.isExtern) + + ", r1_length=" + Twine((int)reloc.length) + + ", r1_pcrel=" + Twine(reloc.pcRel) + + (!reloc.scattered ? (Twine(", r1_symbolnum=") + + Twine(reloc.symbol)) + : (Twine(", r1_scattered=1, r1_value=") + + Twine(reloc.value))) + + ")" + + ", (r2_address=" + Twine::utohexstr(reloc2.offset) + + ", r2_type=" + Twine(reloc2.type) + + ", r2_extern=" + Twine(reloc2.isExtern) + + ", r2_length=" + Twine((int)reloc2.length) + + ", r2_pcrel=" + Twine(reloc2.pcRel) + + (!reloc2.scattered ? (Twine(", r2_symbolnum=") + + Twine(reloc2.symbol)) + : (Twine(", r2_scattered=1, r2_value=") + + Twine(reloc2.value))) + + ")" ); + } } else { // Use ArchHandler to convert relocation record into information @@ -605,26 +642,25 @@ std::error_code convertRelocs(const Section §ion, relocErr = handler.getReferenceInfo( reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr, atomBySymbol, &kind, &target, &addend); + if (relocErr) { + return make_dynamic_error_code( + Twine("bad relocation (") + relocErr.message() + + ") in section " + + section.segmentName + "/" + section.sectionName + + " (r_address=" + Twine::utohexstr(reloc.offset) + + ", r_type=" + Twine(reloc.type) + + ", r_extern=" + Twine(reloc.isExtern) + + ", r_length=" + Twine((int)reloc.length) + + ", r_pcrel=" + Twine(reloc.pcRel) + + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol)) + : (Twine(", r_scattered=1, r_value=") + + Twine(reloc.value))) + + ")" ); + } } - if (relocErr) { - return make_dynamic_error_code( - Twine("bad relocation (") + relocErr.message() - + ") in section " - + section.segmentName + "/" + section.sectionName - + " (r_address=" + Twine::utohexstr(reloc.offset) - + ", r_type=" + Twine(reloc.type) - + ", r_extern=" + Twine(reloc.isExtern) - + ", r_length=" + Twine((int)reloc.length) - + ", r_pcrel=" + Twine(reloc.pcRel) - + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol)) - : (Twine(", r_scattered=1, r_value=") - + Twine(reloc.value))) - + ")" ); - } else { - // Instantiate an lld::Reference object and add to its atom. - inAtom->addReference(offsetInAtom, kind, target, addend, - handler.kindArch()); - } + // Instantiate an lld::Reference object and add to its atom. + inAtom->addReference(offsetInAtom, kind, target, addend, + handler.kindArch()); } return std::error_code(); @@ -644,11 +680,158 @@ static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) { return res; } +/// --- Augmentation String Processing --- + +struct CIEInfo { + bool _augmentationDataPresent = false; + bool _mayHaveLSDA = false; +}; + +typedef llvm::DenseMap<const MachODefinedAtom*, CIEInfo> CIEInfoMap; + +static std::error_code processAugmentationString(const uint8_t *augStr, + CIEInfo &cieInfo, + unsigned *len = nullptr) { + + if (augStr[0] == '\0') { + if (len) + *len = 1; + return std::error_code(); + } + + if (augStr[0] != 'z') + return make_dynamic_error_code("expected 'z' at start of augmentation " + "string"); + + cieInfo._augmentationDataPresent = true; + uint64_t idx = 1; + + while (augStr[idx] != '\0') { + if (augStr[idx] == 'L') { + cieInfo._mayHaveLSDA = true; + ++idx; + } else + ++idx; + } + + if (len) + *len = idx + 1; + return std::error_code(); +} + +static std::error_code processCIE(const NormalizedFile &normalizedFile, + MachODefinedAtom *atom, + CIEInfoMap &cieInfos) { + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + const uint8_t *frameData = atom->rawContent().data(); + + CIEInfo cieInfo; + + uint32_t size = read32(frameData, isBig); + uint64_t cieIDField = size == 0xffffffffU + ? sizeof(uint32_t) + sizeof(uint64_t) + : sizeof(uint32_t); + uint64_t versionField = cieIDField + sizeof(uint32_t); + uint64_t augmentationStringField = versionField + sizeof(uint8_t); + + if (auto err = processAugmentationString(frameData + augmentationStringField, + cieInfo)) + return err; + + cieInfos[atom] = std::move(cieInfo); + + return std::error_code(); +} + +static std::error_code processFDE(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler, + const Section *ehFrameSection, + MachODefinedAtom *atom, + uint64_t offset, + const CIEInfoMap &cieInfos) { + + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); + + // Compiler wasn't lazy and actually told us what it meant. + if (atom->begin() != atom->end()) + return std::error_code(); + + const uint8_t *frameData = atom->rawContent().data(); + uint32_t size = read32(frameData, isBig); + uint64_t cieFieldInFDE = size == 0xffffffffU + ? sizeof(uint32_t) + sizeof(uint64_t) + : sizeof(uint32_t); + + // Linker needs to fixup a reference from the FDE to its parent CIE (a + // 32-bit byte offset backwards in the __eh_frame section). + uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig); + uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; + cieAddress -= cieDelta; + + Reference::Addend addend; + const MachODefinedAtom *cie = + findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); + atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, + addend, handler.kindArch()); + + assert(cie && cie->contentType() == DefinedAtom::typeCFI && !addend && + "FDE's CIE field does not point at the start of a CIE."); + + const CIEInfo &cieInfo = cieInfos.find(cie)->second; + + // Linker needs to fixup reference from the FDE to the function it's + // describing. FIXME: there are actually different ways to do this, and the + // particular method used is specified in the CIE's augmentation fields + // (hopefully) + uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t); + + int64_t functionFromFDE = readSPtr(is64, isBig, + frameData + rangeFieldInFDE); + uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; + rangeStart += functionFromFDE; + + const Atom *func = + findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); + atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), + func, addend, handler.kindArch()); + + // Handle the augmentation data if there is any. + if (cieInfo._augmentationDataPresent) { + // First process the augmentation data length field. + uint64_t augmentationDataLengthFieldInFDE = + rangeFieldInFDE + 2 * (is64 ? sizeof(uint64_t) : sizeof(uint32_t)); + unsigned lengthFieldSize = 0; + uint64_t augmentationDataLength = + llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE, + &lengthFieldSize); + + if (cieInfo._mayHaveLSDA && augmentationDataLength > 0) { + + // Look at the augmentation data field. + uint64_t augmentationDataFieldInFDE = + augmentationDataLengthFieldInFDE + lengthFieldSize; + + int64_t lsdaFromFDE = readSPtr(is64, isBig, + frameData + augmentationDataFieldInFDE); + uint64_t lsdaStart = + ehFrameSection->address + offset + augmentationDataFieldInFDE + + lsdaFromFDE; + const Atom *lsda = + findAtomCoveringAddress(normalizedFile, file, lsdaStart, &addend); + atom->addReference(augmentationDataFieldInFDE, + handler.unwindRefToFunctionKind(), + lsda, addend, handler.kindArch()); + } + } + + return std::error_code(); +} + std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, MachOFile &file, mach_o::ArchHandler &handler) { - const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); - const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); const Section *ehFrameSection = nullptr; for (auto §ion : normalizedFile.sections) @@ -662,51 +845,26 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, if (!ehFrameSection) return std::error_code(); + std::error_code ehFrameErr; + CIEInfoMap cieInfos; + file.eachAtomInSection(*ehFrameSection, [&](MachODefinedAtom *atom, uint64_t offset) -> void { assert(atom->contentType() == DefinedAtom::typeCFI); - if (ArchHandler::isDwarfCIE(isBig, atom)) + // Bail out if we've encountered an error. + if (ehFrameErr) return; - // Compiler wasn't lazy and actually told us what it meant. - if (atom->begin() != atom->end()) - return; - - const uint8_t *frameData = atom->rawContent().data(); - uint32_t size = read32(frameData, isBig); - uint64_t cieFieldInFDE = size == 0xffffffffU - ? sizeof(uint32_t) + sizeof(uint64_t) - : sizeof(uint32_t); - - // Linker needs to fixup a reference from the FDE to its parent CIE (a - // 32-bit byte offset backwards in the __eh_frame section). - uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig); - uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; - cieAddress -= cieDelta; - - Reference::Addend addend; - const Atom *cie = - findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); - atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, - addend, handler.kindArch()); - - // Linker needs to fixup reference from the FDE to the function it's - // describing. FIXME: there are actually different ways to do this, and the - // particular method used is specified in the CIE's augmentation fields - // (hopefully) - uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t); - - int64_t functionFromFDE = readSPtr(is64, isBig, frameData + rangeFieldInFDE); - uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; - rangeStart += functionFromFDE; - - const Atom *func = - findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); - atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), func, - addend, handler.kindArch()); + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + if (ArchHandler::isDwarfCIE(isBig, atom)) + ehFrameErr = processCIE(normalizedFile, atom, cieInfos); + else + ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection, + atom, offset, cieInfos); }); - return std::error_code(); + + return ehFrameErr; } @@ -738,10 +896,13 @@ std::error_code normalizedObjectToAtoms(MachOFile *file, const NormalizedFile &normalizedFile, bool copyRefs) { + DEBUG(llvm::dbgs() << "******** Normalizing file to atoms: " + << file->path() << "\n"); bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0); // Create atoms from each section. for (auto § : normalizedFile.sections) { + DEBUG(llvm::dbgs() << "Creating atoms: "; sect.dump()); if (isDebugInfoSection(sect)) continue; bool customSectionName; @@ -759,7 +920,8 @@ normalizedObjectToAtoms(MachOFile *file, file->addUndefinedAtom(sym.name, copyRefs); } else { file->addTentativeDefAtom(sym.name, atomScope(sym.scope), sym.value, - DefinedAtom::Alignment(sym.desc >> 8), copyRefs); + DefinedAtom::Alignment(1 << (sym.desc >> 8)), + copyRefs); } } @@ -906,6 +1068,14 @@ normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, } } +#ifndef NDEBUG +void Section::dump(llvm::raw_ostream &OS) const { + OS << "Section (\"" << segmentName << ", " << sectionName << "\""; + OS << ", addr: " << llvm::format_hex(address, 16, true); + OS << ", size: " << llvm::format_hex(content.size(), 8, true) << ")\n"; +} +#endif + } // namespace normalized } // namespace mach_o } // namespace lld diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp index ae14d755e2b9..0b92a68eeae8 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp @@ -276,9 +276,9 @@ struct MappingTraits<Section> { io.mapRequired("section", sect.sectionName); io.mapRequired("type", sect.type); io.mapOptional("attributes", sect.attributes); - io.mapOptional("alignment", sect.alignment, 0U); + io.mapOptional("alignment", sect.alignment, (uint16_t)1); io.mapRequired("address", sect.address); - if (sect.type == llvm::MachO::S_ZEROFILL) { + if (isZeroFillSection(sect.type)) { // S_ZEROFILL sections use "size:" instead of "content:" uint64_t size = sect.content.size(); io.mapOptional("size", size); @@ -688,6 +688,7 @@ struct MappingTraits<NormalizedFile> { io.mapOptional("has-UUID", file.hasUUID, true); io.mapOptional("rpaths", file.rpaths); io.mapOptional("entry-point", file.entryAddress, Hex64(0)); + io.mapOptional("stack-size", file.stackSize, Hex64(0)); io.mapOptional("source-version", file.sourceVersion, Hex64(0)); io.mapOptional("OS", file.os); io.mapOptional("min-os-version", file.minOSverson, PackedVersion(0)); @@ -799,4 +800,3 @@ std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out) { } // namespace normalized } // namespace mach_o } // namespace lld - diff --git a/lib/ReaderWriter/MachO/MachOPasses.h b/lib/ReaderWriter/MachO/MachOPasses.h index 86f4bc0f5d54..a73785418d5f 100644 --- a/lib/ReaderWriter/MachO/MachOPasses.h +++ b/lib/ReaderWriter/MachO/MachOPasses.h @@ -19,6 +19,7 @@ namespace mach_o { void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx); void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx); void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx); +void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx); void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx); void addShimPass(PassManager &pm, const MachOLinkingContext &ctx); diff --git a/lib/ReaderWriter/MachO/Makefile b/lib/ReaderWriter/MachO/Makefile deleted file mode 100644 index 1acd578ba9d3..000000000000 --- a/lib/ReaderWriter/MachO/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lld/lib/ReaderWriter/MachO/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 := lldMachO -USEDLIBS = lldCore.a - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/MachO/SectCreateFile.h b/lib/ReaderWriter/MachO/SectCreateFile.h new file mode 100644 index 000000000000..2e6e97c5433f --- /dev/null +++ b/lib/ReaderWriter/MachO/SectCreateFile.h @@ -0,0 +1,93 @@ +//===---- lib/ReaderWriter/MachO/SectCreateFile.h ---------------*- c++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H +#define LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H + +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" + +namespace lld { +namespace mach_o { + +// +// A FlateNamespaceFile instance may be added as a resolution source of last +// resort, depending on how -flat_namespace and -undefined are set. +// +class SectCreateFile : public File { +public: + class SectCreateAtom : public SimpleDefinedAtom { + public: + SectCreateAtom(const File &file, StringRef segName, StringRef sectName, + std::unique_ptr<MemoryBuffer> content) + : SimpleDefinedAtom(file), + _combinedName((segName + "/" + sectName).str()), + _content(std::move(content)) {} + + uint64_t size() const override { return _content->getBufferSize(); } + + Scope scope() const override { return scopeGlobal; } + + ContentType contentType() const override { return typeSectCreate; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _combinedName; } + + DeadStripKind deadStrip() const override { return deadStripNever; } + + ArrayRef<uint8_t> rawContent() const override { + const uint8_t *data = + reinterpret_cast<const uint8_t*>(_content->getBufferStart()); + return ArrayRef<uint8_t>(data, _content->getBufferSize()); + } + + StringRef segmentName() const { return _segName; } + StringRef sectionName() const { return _sectName; } + + private: + std::string _combinedName; + StringRef _segName; + StringRef _sectName; + std::unique_ptr<MemoryBuffer> _content; + }; + + SectCreateFile() : File("sectcreate", kindObject) {} + + void addSection(StringRef seg, StringRef sect, + std::unique_ptr<MemoryBuffer> content) { + _definedAtoms.push_back( + new (allocator()) SectCreateAtom(*this, seg, sect, std::move(content))); + } + + const AtomVector<DefinedAtom> &defined() const override { + return _definedAtoms; + } + + const AtomVector<UndefinedAtom> &undefined() const override { + return _noUndefinedAtoms; + } + + const AtomVector<SharedLibraryAtom> &sharedLibrary() const override { + return _noSharedLibraryAtoms; + } + + const AtomVector<AbsoluteAtom> &absolute() const override { + return _noAbsoluteAtoms; + } + +private: + AtomVector<DefinedAtom> _definedAtoms; +}; + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp index a8c69f8ceace..df29e37c183b 100644 --- a/lib/ReaderWriter/MachO/ShimPass.cpp +++ b/lib/ReaderWriter/MachO/ShimPass.cpp @@ -41,16 +41,12 @@ namespace mach_o { class ShimPass : public Pass { public: ShimPass(const MachOLinkingContext &context) - : _context(context) - , _archHandler(_context.archHandler()) - , _stubInfo(_archHandler.stubInfo()) - , _file("<mach-o shim pass>") { - } - + : _ctx(context), _archHandler(_ctx.archHandler()), + _stubInfo(_archHandler.stubInfo()), _file("<mach-o shim pass>") {} - void perform(std::unique_ptr<MutableFile> &mergedFile) override { + std::error_code perform(SimpleFile &mergedFile) override { // Scan all references in all atoms. - for (const DefinedAtom *atom : mergedFile->defined()) { + for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { // Look at non-call branches. if (!_archHandler.isNonCallBranch(*ref)) @@ -67,7 +63,7 @@ public: } // Exit early if no shims needed. if (_targetToShim.empty()) - return; + return std::error_code(); // Sort shim atoms so the layout order is stable. std::vector<const DefinedAtom *> shims; @@ -81,9 +77,10 @@ public: }); // Add all shims to master file. - for (const DefinedAtom *shim : shims) { - mergedFile->addAtom(*shim); - } + for (const DefinedAtom *shim : shims) + mergedFile.addAtom(*shim); + + return std::error_code(); } private: @@ -112,7 +109,7 @@ private: } } - const MachOLinkingContext &_context; + const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; const ArchHandler::StubInfo &_stubInfo; MachOFile _file; diff --git a/lib/ReaderWriter/MachO/StubsPass.cpp b/lib/ReaderWriter/MachO/StubsPass.cpp index bc4d9c2087f3..1f61256a5b79 100644 --- a/lib/ReaderWriter/MachO/StubsPass.cpp +++ b/lib/ReaderWriter/MachO/StubsPass.cpp @@ -1,4 +1,4 @@ -//===- lib/ReaderWriter/MachO/StubsPass.cpp -------------------------------===// +//===- lib/ReaderWriter/MachO/StubsPass.cpp ---------------------*- C++ -*-===// // // The LLVM Linker // @@ -26,11 +26,9 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" - namespace lld { namespace mach_o { - // // Lazy Pointer Atom created by the stubs pass. // @@ -44,7 +42,7 @@ public: } Alignment alignment() const override { - return Alignment(_is64 ? 3 : 2); + return _is64 ? 8 : 4; } uint64_t size() const override { @@ -65,7 +63,6 @@ private: const bool _is64; }; - // // NonLazyPointer (GOT) Atom created by the stubs pass. // @@ -79,7 +76,7 @@ public: } Alignment alignment() const override { - return Alignment(_is64 ? 3 : 2); + return _is64 ? 8 : 4; } uint64_t size() const override { @@ -100,8 +97,6 @@ private: const bool _is64; }; - - // // Stub Atom created by the stubs pass. // @@ -115,7 +110,7 @@ public: } Alignment alignment() const override { - return Alignment(_stubInfo.codeAlignment); + return 1 << _stubInfo.codeAlignment; } uint64_t size() const override { @@ -134,7 +129,6 @@ private: const ArchHandler::StubInfo &_stubInfo; }; - // // Stub Helper Atom created by the stubs pass. // @@ -148,7 +142,7 @@ public: } Alignment alignment() const override { - return Alignment(_stubInfo.codeAlignment); + return 1 << _stubInfo.codeAlignment; } uint64_t size() const override { @@ -168,7 +162,6 @@ private: const ArchHandler::StubInfo &_stubInfo; }; - // // Stub Helper Common Atom created by the stubs pass. // @@ -182,7 +175,7 @@ public: } Alignment alignment() const override { - return Alignment(_stubInfo.codeAlignment); + return 1 << _stubInfo.codeAlignment; } uint64_t size() const override { @@ -202,21 +195,19 @@ private: const ArchHandler::StubInfo &_stubInfo; }; - class StubsPass : public Pass { public: StubsPass(const MachOLinkingContext &context) - : _context(context), _archHandler(_context.archHandler()), - _stubInfo(_archHandler.stubInfo()), _file("<mach-o Stubs pass>") { } + : _ctx(context), _archHandler(_ctx.archHandler()), + _stubInfo(_archHandler.stubInfo()), _file("<mach-o Stubs pass>") {} - - void perform(std::unique_ptr<MutableFile> &mergedFile) override { + std::error_code perform(SimpleFile &mergedFile) override { // Skip this pass if output format uses text relocations instead of stubs. if (!this->noTextRelocs()) - return; + return std::error_code(); // Scan all references in all atoms. - for (const DefinedAtom *atom : mergedFile->defined()) { + for (const DefinedAtom *atom : mergedFile.defined()) { for (const Reference *ref : *atom) { // Look at call-sites. if (!this->isCallSite(*ref)) @@ -240,15 +231,15 @@ public: // Exit early if no stubs needed. if (_targetToUses.empty()) - return; + return std::error_code(); // First add help-common and GOT slots used by lazy binding. SimpleDefinedAtom *helperCommonAtom = new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo); SimpleDefinedAtom *helperCacheNLPAtom = - new (_file.allocator()) NonLazyPointerAtom(_file, _context.is64Bit()); + new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit()); SimpleDefinedAtom *helperBinderNLPAtom = - new (_file.allocator()) NonLazyPointerAtom(_file, _context.is64Bit()); + new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit()); addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, helperCacheNLPAtom); addOptReference( @@ -259,17 +250,18 @@ public: addOptReference( helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, _stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom); - mergedFile->addAtom(*helperCommonAtom); - mergedFile->addAtom(*helperBinderNLPAtom); - mergedFile->addAtom(*helperCacheNLPAtom); + mergedFile.addAtom(*helperCommonAtom); + mergedFile.addAtom(*helperBinderNLPAtom); + mergedFile.addAtom(*helperCacheNLPAtom); // Add reference to dyld_stub_binder in libSystem.dylib auto I = std::find_if( - mergedFile->sharedLibrary().begin(), mergedFile->sharedLibrary().end(), + mergedFile.sharedLibrary().begin(), mergedFile.sharedLibrary().end(), [&](const SharedLibraryAtom *atom) { return atom->name().equals(_stubInfo.binderSymbolName); }); - assert(I != mergedFile->sharedLibrary().end() && "dyld_stub_binder not found"); + assert(I != mergedFile.sharedLibrary().end() && + "dyld_stub_binder not found"); addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I); // Sort targets by name, so stubs and lazy pointers are consistent @@ -284,11 +276,10 @@ public: // Make and append stubs, lazy pointers, and helpers in alphabetical order. unsigned lazyOffset = 0; for (const Atom *target : targetsNeedingStubs) { - StubAtom *stub = new (_file.allocator()) StubAtom(_file, _stubInfo); - LazyPointerAtom *lp = - new (_file.allocator()) LazyPointerAtom(_file, _context.is64Bit()); - StubHelperAtom *helper = - new (_file.allocator()) StubHelperAtom(_file, _stubInfo); + auto *stub = new (_file.allocator()) StubAtom(_file, _stubInfo); + auto *lp = + new (_file.allocator()) LazyPointerAtom(_file, _ctx.is64Bit()); + auto *helper = new (_file.allocator()) StubHelperAtom(_file, _stubInfo); addReference(stub, _stubInfo.stubReferenceToLP, lp); addOptReference(stub, _stubInfo.stubReferenceToLP, @@ -301,9 +292,9 @@ public: addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon, helperCommonAtom); - mergedFile->addAtom(*stub); - mergedFile->addAtom(*lp); - mergedFile->addAtom(*helper); + mergedFile.addAtom(*stub); + mergedFile.addAtom(*lp); + mergedFile.addAtom(*helper); // Update each reference to use stub. for (const Reference *ref : _targetToUses[target]) { @@ -315,10 +306,11 @@ public: // Calculate new offset lazyOffset += target->name().size() + 12; } + + return std::error_code(); } private: - bool noTextRelocs() { return true; } @@ -356,15 +348,13 @@ private: typedef llvm::DenseMap<const Atom*, llvm::SmallVector<const Reference *, 8>> TargetToUses; - const MachOLinkingContext &_context; + const MachOLinkingContext &_ctx; mach_o::ArchHandler &_archHandler; const ArchHandler::StubInfo &_stubInfo; MachOFile _file; TargetToUses _targetToUses; }; - - void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) { pm.add(std::unique_ptr<Pass>(new StubsPass(ctx))); } diff --git a/lib/ReaderWriter/MachO/TLVPass.cpp b/lib/ReaderWriter/MachO/TLVPass.cpp new file mode 100644 index 000000000000..aba222edcd27 --- /dev/null +++ b/lib/ReaderWriter/MachO/TLVPass.cpp @@ -0,0 +1,137 @@ +//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This linker pass transforms all TLV references to real references. +/// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" + +namespace lld { +namespace mach_o { + +// +// TLVP Entry Atom created by the TLV pass. +// +class TLVPEntryAtom : public SimpleDefinedAtom { +public: + TLVPEntryAtom(const File &file, bool is64, StringRef name) + : SimpleDefinedAtom(file), _is64(is64), _name(name) {} + + ContentType contentType() const override { + return DefinedAtom::typeTLVInitializerPtr; + } + + Alignment alignment() const override { + return _is64 ? 8 : 4; + } + + uint64_t size() const override { + return _is64 ? 8 : 4; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permRW_; + } + + ArrayRef<uint8_t> rawContent() const override { + static const uint8_t zeros[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return llvm::makeArrayRef(zeros, size()); + } + + StringRef slotName() const { + return _name; + } + +private: + const bool _is64; + StringRef _name; +}; + +class TLVPass : public Pass { +public: + TLVPass(const MachOLinkingContext &context) + : _ctx(context), _archHandler(_ctx.archHandler()), + _file("<mach-o TLV Pass>") {} + +private: + std::error_code perform(SimpleFile &mergedFile) override { + bool allowTLV = _ctx.minOS("10.7", "1.0"); + + for (const DefinedAtom *atom : mergedFile.defined()) { + for (const Reference *ref : *atom) { + if (!_archHandler.isTLVAccess(*ref)) + continue; + + if (!allowTLV) + return make_dynamic_error_code( + "targeted OS version does not support use of thread local " + "variables in " + atom->name() + " for architecture " + + _ctx.archName()); + + const Atom *target = ref->target(); + assert(target != nullptr); + + const DefinedAtom *tlvpEntry = makeTLVPEntry(target); + const_cast<Reference*>(ref)->setTarget(tlvpEntry); + _archHandler.updateReferenceToTLV(ref); + } + } + + std::vector<const TLVPEntryAtom*> entries; + entries.reserve(_targetToTLVP.size()); + for (auto &it : _targetToTLVP) + entries.push_back(it.second); + std::sort(entries.begin(), entries.end(), + [](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) { + return (lhs->slotName().compare(rhs->slotName()) < 0); + }); + + for (const TLVPEntryAtom *slot : entries) + mergedFile.addAtom(*slot); + + return std::error_code(); + } + + const DefinedAtom *makeTLVPEntry(const Atom *target) { + auto pos = _targetToTLVP.find(target); + + if (pos != _targetToTLVP.end()) + return pos->second; + + auto *tlvpEntry = new (_file.allocator()) + TLVPEntryAtom(_file, _ctx.is64Bit(), target->name()); + _targetToTLVP[target] = tlvpEntry; + const ArchHandler::ReferenceInfo &nlInfo = + _archHandler.stubInfo().nonLazyPointerReferenceToBinder; + tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch, + nlInfo.kind, 0, target, 0); + return tlvpEntry; + } + + const MachOLinkingContext &_ctx; + mach_o::ArchHandler &_archHandler; + MachOFile _file; + llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP; +}; + +void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) { + assert(ctx.needsTLVPass()); + pm.add(llvm::make_unique<TLVPass>(ctx)); +} + +} // end namesapce mach_o +} // end namesapce lld diff --git a/lib/ReaderWriter/MachO/WriterMachO.cpp b/lib/ReaderWriter/MachO/WriterMachO.cpp index de1c0e38063b..cce0a179608c 100644 --- a/lib/ReaderWriter/MachO/WriterMachO.cpp +++ b/lib/ReaderWriter/MachO/WriterMachO.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "ExecutableAtoms.hpp" +#include "ExecutableAtoms.h" #include "MachONormalizedFile.h" #include "lld/Core/File.h" #include "lld/Core/Writer.h" @@ -26,18 +26,18 @@ namespace mach_o { class MachOWriter : public Writer { public: - MachOWriter(const MachOLinkingContext &ctxt) : _context(ctxt) { } + MachOWriter(const MachOLinkingContext &ctxt) : _ctx(ctxt) {} std::error_code writeFile(const lld::File &file, StringRef path) override { // Construct empty normalized file from atoms. ErrorOr<std::unique_ptr<NormalizedFile>> nFile = - normalized::normalizedFromAtoms(file, _context); + normalized::normalizedFromAtoms(file, _ctx); if (std::error_code ec = nFile.getError()) return ec; // For testing, write out yaml form of normalized file. - if (_context.printAtoms()) { - std::unique_ptr<Writer> yamlWriter = createWriterYAML(_context); + if (_ctx.printAtoms()) { + std::unique_ptr<Writer> yamlWriter = createWriterYAML(_ctx); yamlWriter->writeFile(file, "-"); } @@ -45,21 +45,19 @@ public: return writeBinary(*nFile->get(), path); } - bool createImplicitFiles(std::vector<std::unique_ptr<File> > &r) override { + void createImplicitFiles(std::vector<std::unique_ptr<File>> &r) override { // When building main executables, add _main as required entry point. - if (_context.outputTypeHasEntry()) - r.emplace_back(new CEntryFile(_context)); + if (_ctx.outputTypeHasEntry()) + r.emplace_back(new CEntryFile(_ctx)); // If this can link with dylibs, need helper function (dyld_stub_binder). - if (_context.needsStubsPass()) - r.emplace_back(new StubHelperFile(_context)); + if (_ctx.needsStubsPass()) + r.emplace_back(new StubHelperFile(_ctx)); // Final linked images can access a symbol for their mach_header. - if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) - r.emplace_back(new MachHeaderAliasFile(_context)); - - return true; + if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT) + r.emplace_back(new MachHeaderAliasFile(_ctx)); } private: - const MachOLinkingContext &_context; + const MachOLinkingContext &_ctx; }; diff --git a/lib/ReaderWriter/Makefile b/lib/ReaderWriter/Makefile deleted file mode 100644 index 23587440805f..000000000000 --- a/lib/ReaderWriter/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- lld/lib/ReaderWriter/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 := lldReaderWriter - -# these link against this lib -PARALLEL_DIRS := ELF MachO Native PECOFF YAML - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/Native/CMakeLists.txt b/lib/ReaderWriter/Native/CMakeLists.txt deleted file mode 100644 index e15f3d60e89c..000000000000 --- a/lib/ReaderWriter/Native/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_llvm_library(lldNative - ReaderNative.cpp - WriterNative.cpp - LINK_LIBS - lldCore - LLVMSupport - ) diff --git a/lib/ReaderWriter/Native/Makefile b/lib/ReaderWriter/Native/Makefile deleted file mode 100644 index 6aba37868900..000000000000 --- a/lib/ReaderWriter/Native/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lld/lib/ReaderWriter/Native/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 := lldNative -USEDLIBS = lldCore.a - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/Native/NativeFileFormat.h b/lib/ReaderWriter/Native/NativeFileFormat.h deleted file mode 100644 index 535072fe2314..000000000000 --- a/lib/ReaderWriter/Native/NativeFileFormat.h +++ /dev/null @@ -1,258 +0,0 @@ -//===- lib/ReaderWriter/Native/NativeFileFormat.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_NATIVE_NATIVE_FILE_FORMAT_H -#define LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H - -#include "llvm/Support/DataTypes.h" -#include <cstdint> - -namespace lld { - -// -// Overview: -// -// The number one design goal of this file format is enable the linker to -// read object files into in-memory Atom objects extremely quickly. -// The second design goal is to enable future modifications to the -// Atom attribute model. -// -// The llvm native object file format is not like traditional object file -// formats (e.g. ELF, COFF, mach-o). There is no symbol table and no -// sections. Instead the file is essentially an array of archived Atoms. -// It is *not* serialized Atoms which would require deserialization into -// in memory objects. Instead it is an array of read-only info about each -// Atom. The NativeReader bulk creates in-memory Atoms which just have -// an ivar which points to the read-only info for that Atom. No additional -// processing is done to construct the in-memory Atoms. All Atom attribute -// getter methods are virtual calls which dig up the info they need from the -// ivar data. -// -// To support the gradual evolution of Atom attributes, the Atom read-only -// data is versioned. The NativeReader chooses which in-memory Atom class -// to use based on the version. What this means is that if new attributes -// are added (or changed) in the Atom model, a new native atom class and -// read-only atom info struct needs to be defined. Then, all the existing -// native reader atom classes need to be modified to do their best effort -// to map their old style read-only data to the new Atom model. At some point -// some classes to support old versions may be dropped. -// -// -// Details: -// -// The native object file format consists of a header that specifies the -// endianness of the file and the architecture along with a list of "chunks" -// in the file. A Chunk is simply a tagged range of the file. There is -// one chunk for the array of atom infos. There is another chunk for the -// string pool, and another for the content pool. -// -// It turns out there most atoms have very similar sets of attributes, only -// the name and content attribute vary. To exploit this fact to reduce the file -// size, the atom read-only info contains just the name and content info plus -// a reference to which attribute set it uses. The attribute sets are stored -// in another chunk. -// - - -// -// An entry in the NativeFileHeader that describes one chunk of the file. -// -struct NativeChunk { - uint32_t signature; - uint32_t fileOffset; - uint32_t fileSize; - uint32_t elementCount; -}; - - -// -// The header in a native object file -// -struct NativeFileHeader { - uint8_t magic[16]; - uint32_t endian; - uint32_t architecture; - uint32_t fileSize; - uint32_t chunkCount; - // NativeChunk chunks[] -}; - -// -// Possible values for NativeChunk.signature field -// -enum NativeChunkSignatures { - NCS_DefinedAtomsV1 = 1, - NCS_AttributesArrayV1 = 2, - NCS_AbsoluteAttributesV1 = 12, - NCS_UndefinedAtomsV1 = 3, - NCS_SharedLibraryAtomsV1 = 4, - NCS_AbsoluteAtomsV1 = 5, - NCS_Strings = 6, - NCS_ReferencesArrayV1 = 7, - NCS_ReferencesArrayV2 = 8, - NCS_TargetsTable = 9, - NCS_AddendsTable = 10, - NCS_Content = 11, -}; - -// -// The 16-bytes at the start of a native object file -// -#define NATIVE_FILE_HEADER_MAGIC "llvm nat obj v1 " - -// -// Possible values for the NativeFileHeader.endian field -// -enum { - NFH_BigEndian = 0x42696745, - NFH_LittleEndian = 0x4574696c -}; - - -// -// Possible values for the NativeFileHeader.architecture field -// -enum { - NFA_x86 = 1, - NFA_x86_64 = 2, - NFA_armv6 = 3, - NFA_armv7 = 4, -}; - - -// -// The NCS_DefinedAtomsV1 chunk contains an array of these structs -// -struct NativeDefinedAtomIvarsV1 { - uint32_t nameOffset; - uint32_t attributesOffset; - uint32_t referencesStartIndex; - uint32_t referencesCount; - uint32_t contentOffset; - uint32_t contentSize; - uint64_t sectionSize; -}; - - -// -// The NCS_AttributesArrayV1 chunk contains an array of these structs -// -struct NativeAtomAttributesV1 { - uint32_t sectionNameOffset; - uint16_t align2; - uint16_t alignModulus; - uint8_t scope; - uint8_t interposable; - uint8_t merge; - uint8_t contentType; - uint8_t sectionChoice; - uint8_t deadStrip; - uint8_t dynamicExport; - uint8_t permissions; - uint8_t alias; - uint8_t codeModel; -}; - - - -// -// The NCS_UndefinedAtomsV1 chunk contains an array of these structs -// -struct NativeUndefinedAtomIvarsV1 { - uint32_t nameOffset; - uint32_t flags; - uint32_t fallbackNameOffset; -}; - - -// -// The NCS_SharedLibraryAtomsV1 chunk contains an array of these structs -// -struct NativeSharedLibraryAtomIvarsV1 { - uint64_t size; - uint32_t nameOffset; - uint32_t loadNameOffset; - uint32_t type; - uint32_t flags; -}; - - - -// -// The NCS_AbsoluteAtomsV1 chunk contains an array of these structs -// -struct NativeAbsoluteAtomIvarsV1 { - uint32_t nameOffset; - uint32_t attributesOffset; - uint32_t reserved; - uint64_t value; -}; - - - -// -// The NCS_ReferencesArrayV1 chunk contains an array of these structs -// -struct NativeReferenceIvarsV1 { - enum { - noTarget = UINT16_MAX - }; - uint32_t offsetInAtom; - uint16_t kindValue; - uint8_t kindNamespace; - uint8_t kindArch; - uint16_t targetIndex; - uint16_t addendIndex; -}; - - -// -// The NCS_ReferencesArrayV2 chunk contains an array of these structs -// -struct NativeReferenceIvarsV2 { - enum : unsigned { - noTarget = UINT32_MAX - }; - uint64_t offsetInAtom; - int64_t addend; - uint16_t kindValue; - uint8_t kindNamespace; - uint8_t kindArch; - uint32_t targetIndex; - uint32_t tag; -}; - - -// -// The NCS_TargetsTable chunk contains an array of uint32_t entries. -// The C++ class Reference has a target() method that returns a -// pointer to another Atom. We can't have pointers in object files, -// so instead NativeReferenceIvarsV1 contains an index to the target. -// The index is into this NCS_TargetsTable of uint32_t entries. -// The values in this table are the index of the (target) atom in this file. -// For DefinedAtoms the value is from 0 to NCS_DefinedAtomsV1.elementCount. -// For UndefinedAtoms the value is from NCS_DefinedAtomsV1.elementCount to -// NCS_DefinedAtomsV1.elementCount+NCS_UndefinedAtomsV1.elementCount. -// - - -// -// The NCS_AddendsTable chunk contains an array of int64_t entries. -// If we allocated space for addends directly in NativeReferenceIvarsV1 -// it would double the size of that struct. But since addends are rare, -// we instead just keep a pool of addends and have NativeReferenceIvarsV1 -// (if it needs an addend) just store the index (into the pool) of the -// addend it needs. -// - - - -} // namespace lld - -#endif // LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H diff --git a/lib/ReaderWriter/Native/ReaderNative.cpp b/lib/ReaderWriter/Native/ReaderNative.cpp deleted file mode 100644 index 84cdb4b997e8..000000000000 --- a/lib/ReaderWriter/Native/ReaderNative.cpp +++ /dev/null @@ -1,1013 +0,0 @@ -//===- lib/ReaderWriter/Native/ReaderNative.cpp ---------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "NativeFileFormat.h" -#include "lld/Core/Atom.h" -#include "lld/Core/Error.h" -#include "lld/Core/File.h" -#include "lld/Core/Reader.h" -#include "lld/Core/Simple.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include <memory> -#include <vector> - -namespace lld { -namespace native { - -// forward reference -class File; - -// -// An object of this class is instantied for each NativeDefinedAtomIvarsV1 -// struct in the NCS_DefinedAtomsV1 chunk. -// -class NativeDefinedAtomV1 : public DefinedAtom { -public: - NativeDefinedAtomV1(const File& f, - const NativeDefinedAtomIvarsV1* ivarData) - : _file(&f), _ivarData(ivarData) { } - - const lld::File& file() const override; - - uint64_t ordinal() const override; - - StringRef name() const override; - - uint64_t size() const override { return _ivarData->contentSize; } - - uint64_t sectionSize() const override { return _ivarData->sectionSize; } - - DefinedAtom::Scope scope() const override { - return (DefinedAtom::Scope)(attributes().scope); - } - - DefinedAtom::Interposable interposable() const override { - return (DefinedAtom::Interposable)(attributes().interposable); - } - - DefinedAtom::Merge merge() const override { - return (DefinedAtom::Merge)(attributes().merge); - } - - DefinedAtom::ContentType contentType() const override { - const NativeAtomAttributesV1& attr = attributes(); - return (DefinedAtom::ContentType)(attr.contentType); - } - - DefinedAtom::Alignment alignment() const override { - return DefinedAtom::Alignment(attributes().align2, attributes().alignModulus); - } - - DefinedAtom::SectionChoice sectionChoice() const override { - return (DefinedAtom::SectionChoice)(attributes().sectionChoice); - } - - StringRef customSectionName() const override; - - DefinedAtom::DeadStripKind deadStrip() const override { - return (DefinedAtom::DeadStripKind)(attributes().deadStrip); - } - - DynamicExport dynamicExport() const override { - return (DynamicExport)attributes().dynamicExport; - } - - DefinedAtom::CodeModel codeModel() const override { - return DefinedAtom::CodeModel(attributes().codeModel); - } - - DefinedAtom::ContentPermissions permissions() const override { - return (DefinedAtom::ContentPermissions)(attributes().permissions); - } - - ArrayRef<uint8_t> rawContent() const override; - - reference_iterator begin() const override; - - reference_iterator end() const override; - - const Reference* derefIterator(const void*) const override; - - void incrementIterator(const void*& it) const override; - -private: - const NativeAtomAttributesV1& attributes() const; - - const File *_file; - const NativeDefinedAtomIvarsV1 *_ivarData; -}; - - - -// -// An object of this class is instantied for each NativeUndefinedAtomIvarsV1 -// struct in the NCS_UndefinedAtomsV1 chunk. -// -class NativeUndefinedAtomV1 : public UndefinedAtom { -public: - NativeUndefinedAtomV1(const File& f, - const NativeUndefinedAtomIvarsV1* ivarData) - : _file(&f), _ivarData(ivarData) { } - - const lld::File& file() const override; - StringRef name() const override; - - CanBeNull canBeNull() const override { - return (CanBeNull)(_ivarData->flags & 0x3); - } - - const UndefinedAtom *fallback() const override; - -private: - const File *_file; - const NativeUndefinedAtomIvarsV1 *_ivarData; - mutable std::unique_ptr<const SimpleUndefinedAtom> _fallback; -}; - - -// -// An object of this class is instantied for each NativeUndefinedAtomIvarsV1 -// struct in the NCS_SharedLibraryAtomsV1 chunk. -// -class NativeSharedLibraryAtomV1 : public SharedLibraryAtom { -public: - NativeSharedLibraryAtomV1(const File& f, - const NativeSharedLibraryAtomIvarsV1* ivarData) - : _file(&f), _ivarData(ivarData) { } - - const lld::File& file() const override; - StringRef name() const override; - StringRef loadName() const override; - - bool canBeNullAtRuntime() const override { - return (_ivarData->flags & 0x1); - } - - Type type() const override { - return (Type)_ivarData->type; - } - - uint64_t size() const override { - return _ivarData->size; - } - -private: - const File *_file; - const NativeSharedLibraryAtomIvarsV1 *_ivarData; -}; - - -// -// An object of this class is instantied for each NativeAbsoluteAtomIvarsV1 -// struct in the NCS_AbsoluteAtomsV1 chunk. -// -class NativeAbsoluteAtomV1 : public AbsoluteAtom { -public: - NativeAbsoluteAtomV1(const File& f, - const NativeAbsoluteAtomIvarsV1* ivarData) - : _file(&f), _ivarData(ivarData) { } - - const lld::File& file() const override; - StringRef name() const override; - Scope scope() const override { - const NativeAtomAttributesV1& attr = absAttributes(); - return (Scope)(attr.scope); - } - uint64_t value() const override { - return _ivarData->value; - } - -private: - const NativeAtomAttributesV1& absAttributes() const; - const File *_file; - const NativeAbsoluteAtomIvarsV1 *_ivarData; -}; - - -// -// An object of this class is instantied for each NativeReferenceIvarsV1 -// struct in the NCS_ReferencesArrayV1 chunk. -// -class NativeReferenceV1 : public Reference { -public: - NativeReferenceV1(const File &f, const NativeReferenceIvarsV1 *ivarData) - : Reference((KindNamespace)ivarData->kindNamespace, - (KindArch)ivarData->kindArch, ivarData->kindValue), - _file(&f), _ivarData(ivarData) {} - - uint64_t offsetInAtom() const override { - return _ivarData->offsetInAtom; - } - - const Atom* target() const override; - Addend addend() const override; - void setTarget(const Atom* newAtom) override; - void setAddend(Addend a) override; - -private: - const File *_file; - const NativeReferenceIvarsV1 *_ivarData; -}; - - -// -// An object of this class is instantied for each NativeReferenceIvarsV1 -// struct in the NCS_ReferencesArrayV1 chunk. -// -class NativeReferenceV2 : public Reference { -public: - NativeReferenceV2(const File &f, const NativeReferenceIvarsV2 *ivarData) - : Reference((KindNamespace)ivarData->kindNamespace, - (KindArch)ivarData->kindArch, ivarData->kindValue), - _file(&f), _ivarData(ivarData) {} - - uint64_t offsetInAtom() const override { - return _ivarData->offsetInAtom; - } - - const Atom* target() const override; - Addend addend() const override; - void setTarget(const Atom* newAtom) override; - void setAddend(Addend a) override; - uint32_t tag() const override; - -private: - const File *_file; - const NativeReferenceIvarsV2 *_ivarData; -}; - - -// -// lld::File object for native llvm object file -// -class File : public lld::File { -public: - File(std::unique_ptr<MemoryBuffer> mb) - : lld::File(mb->getBufferIdentifier(), kindObject), - _mb(std::move(mb)), // Reader now takes ownership of buffer - _header(nullptr), _targetsTable(nullptr), _targetsTableCount(0), - _strings(nullptr), _stringsMaxOffset(0), _addends(nullptr), - _addendsMaxIndex(0), _contentStart(nullptr), _contentEnd(nullptr) { - _header = - reinterpret_cast<const NativeFileHeader *>(_mb->getBufferStart()); - } - - /// Parses a File object from a native object file. - std::error_code doParse() override { - const uint8_t *const base = - reinterpret_cast<const uint8_t *>(_mb->getBufferStart()); - StringRef path(_mb->getBufferIdentifier()); - const NativeFileHeader *const header = - reinterpret_cast<const NativeFileHeader *>(base); - const NativeChunk *const chunks = - reinterpret_cast<const NativeChunk *>(base + sizeof(NativeFileHeader)); - // make sure magic matches - if (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, - sizeof(header->magic)) != 0) - return make_error_code(NativeReaderError::unknown_file_format); - - // make sure mapped file contains all needed data - const size_t fileSize = _mb->getBufferSize(); - if (header->fileSize > fileSize) - return make_error_code(NativeReaderError::file_too_short); - - DEBUG_WITH_TYPE("ReaderNative", - llvm::dbgs() << " Native File Header:" << " fileSize=" - << header->fileSize << " chunkCount=" - << header->chunkCount << "\n"); - - // process each chunk - for (uint32_t i = 0; i < header->chunkCount; ++i) { - std::error_code ec; - const NativeChunk* chunk = &chunks[i]; - // sanity check chunk is within file - if ( chunk->fileOffset > fileSize ) - return make_error_code(NativeReaderError::file_malformed); - if ( (chunk->fileOffset + chunk->fileSize) > fileSize) - return make_error_code(NativeReaderError::file_malformed); - // process chunk, based on signature - switch ( chunk->signature ) { - case NCS_DefinedAtomsV1: - ec = processDefinedAtomsV1(base, chunk); - break; - case NCS_AttributesArrayV1: - ec = processAttributesV1(base, chunk); - break; - case NCS_UndefinedAtomsV1: - ec = processUndefinedAtomsV1(base, chunk); - break; - case NCS_SharedLibraryAtomsV1: - ec = processSharedLibraryAtomsV1(base, chunk); - break; - case NCS_AbsoluteAtomsV1: - ec = processAbsoluteAtomsV1(base, chunk); - break; - case NCS_AbsoluteAttributesV1: - ec = processAbsoluteAttributesV1(base, chunk); - break; - case NCS_ReferencesArrayV1: - ec = processReferencesV1(base, chunk); - break; - case NCS_ReferencesArrayV2: - ec = processReferencesV2(base, chunk); - break; - case NCS_TargetsTable: - ec = processTargetsTable(base, chunk); - break; - case NCS_AddendsTable: - ec = processAddendsTable(base, chunk); - break; - case NCS_Content: - ec = processContent(base, chunk); - break; - case NCS_Strings: - ec = processStrings(base, chunk); - break; - default: - return make_error_code(NativeReaderError::unknown_chunk_type); - } - if ( ec ) { - return ec; - } - } - // TO DO: validate enough chunks were used - - DEBUG_WITH_TYPE("ReaderNative", { - llvm::dbgs() << " ReaderNative DefinedAtoms:\n"; - for (const DefinedAtom *a : defined()) { - llvm::dbgs() << llvm::format(" 0x%09lX", a) - << ", name=" << a->name() - << ", size=" << a->size() << "\n"; - for (const Reference *r : *a) { - llvm::dbgs() << " offset=" - << llvm::format("0x%03X", r->offsetInAtom()) - << ", kind=" << r->kindValue() - << ", target=" << r->target() << "\n"; - } - } - }); - return make_error_code(NativeReaderError::success); - } - - virtual ~File() { - // _mb is automatically deleted because of std::unique_ptr<> - - // All other ivar pointers are pointers into the MemoryBuffer, except - // the _definedAtoms array which was allocated to contain an array - // of Atom objects. The atoms have empty destructors, so it is ok - // to just delete the memory. - delete _definedAtoms._arrayStart; - delete _undefinedAtoms._arrayStart; - delete _sharedLibraryAtoms._arrayStart; - delete _absoluteAtoms._arrayStart; - delete _referencesV1.arrayStart; - delete _referencesV2.arrayStart; - delete [] _targetsTable; - } - - 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; - } - -private: - friend NativeDefinedAtomV1; - friend NativeUndefinedAtomV1; - friend NativeSharedLibraryAtomV1; - friend NativeAbsoluteAtomV1; - friend NativeReferenceV1; - friend NativeReferenceV2; - - // instantiate array of DefinedAtoms from v1 ivar data in file - std::error_code processDefinedAtomsV1(const uint8_t *base, - const NativeChunk *chunk) { - const size_t atomSize = sizeof(NativeDefinedAtomV1); - size_t atomsArraySize = chunk->elementCount * atomSize; - uint8_t* atomsStart = reinterpret_cast<uint8_t*> - (operator new(atomsArraySize, std::nothrow)); - if (atomsStart == nullptr) - return make_error_code(NativeReaderError::memory_error); - const size_t ivarElementSize = chunk->fileSize - / chunk->elementCount; - if ( ivarElementSize != sizeof(NativeDefinedAtomIvarsV1) ) - return make_error_code(NativeReaderError::file_malformed); - uint8_t* atomsEnd = atomsStart + atomsArraySize; - const NativeDefinedAtomIvarsV1* ivarData = - reinterpret_cast<const NativeDefinedAtomIvarsV1*> - (base + chunk->fileOffset); - for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { - NativeDefinedAtomV1* atomAllocSpace = - reinterpret_cast<NativeDefinedAtomV1*>(s); - new (atomAllocSpace) NativeDefinedAtomV1(*this, ivarData); - ++ivarData; - } - this->_definedAtoms._arrayStart = atomsStart; - this->_definedAtoms._arrayEnd = atomsEnd; - this->_definedAtoms._elementSize = atomSize; - this->_definedAtoms._elementCount = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk DefinedAtomsV1: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - - - // set up pointers to attributes array - std::error_code processAttributesV1(const uint8_t *base, - const NativeChunk *chunk) { - this->_attributes = base + chunk->fileOffset; - this->_attributesMaxOffset = chunk->fileSize; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk AttributesV1: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - // set up pointers to attributes array - std::error_code processAbsoluteAttributesV1(const uint8_t *base, - const NativeChunk *chunk) { - this->_absAttributes = base + chunk->fileOffset; - this->_absAbsoluteMaxOffset = chunk->fileSize; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk AbsoluteAttributesV1: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - // instantiate array of UndefinedAtoms from v1 ivar data in file - std::error_code processUndefinedAtomsV1(const uint8_t *base, - const NativeChunk *chunk) { - const size_t atomSize = sizeof(NativeUndefinedAtomV1); - size_t atomsArraySize = chunk->elementCount * atomSize; - uint8_t* atomsStart = reinterpret_cast<uint8_t*> - (operator new(atomsArraySize, std::nothrow)); - if (atomsStart == nullptr) - return make_error_code(NativeReaderError::memory_error); - const size_t ivarElementSize = chunk->fileSize - / chunk->elementCount; - if ( ivarElementSize != sizeof(NativeUndefinedAtomIvarsV1) ) - return make_error_code(NativeReaderError::file_malformed); - uint8_t* atomsEnd = atomsStart + atomsArraySize; - const NativeUndefinedAtomIvarsV1* ivarData = - reinterpret_cast<const NativeUndefinedAtomIvarsV1*> - (base + chunk->fileOffset); - for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { - NativeUndefinedAtomV1* atomAllocSpace = - reinterpret_cast<NativeUndefinedAtomV1*>(s); - new (atomAllocSpace) NativeUndefinedAtomV1(*this, ivarData); - ++ivarData; - } - this->_undefinedAtoms._arrayStart = atomsStart; - this->_undefinedAtoms._arrayEnd = atomsEnd; - this->_undefinedAtoms._elementSize = atomSize; - this->_undefinedAtoms._elementCount = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk UndefinedAtomsV1:" - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - - // instantiate array of ShareLibraryAtoms from v1 ivar data in file - std::error_code processSharedLibraryAtomsV1(const uint8_t *base, - const NativeChunk *chunk) { - const size_t atomSize = sizeof(NativeSharedLibraryAtomV1); - size_t atomsArraySize = chunk->elementCount * atomSize; - uint8_t* atomsStart = reinterpret_cast<uint8_t*> - (operator new(atomsArraySize, std::nothrow)); - if (atomsStart == nullptr) - return make_error_code(NativeReaderError::memory_error); - const size_t ivarElementSize = chunk->fileSize - / chunk->elementCount; - if ( ivarElementSize != sizeof(NativeSharedLibraryAtomIvarsV1) ) - return make_error_code(NativeReaderError::file_malformed); - uint8_t* atomsEnd = atomsStart + atomsArraySize; - const NativeSharedLibraryAtomIvarsV1* ivarData = - reinterpret_cast<const NativeSharedLibraryAtomIvarsV1*> - (base + chunk->fileOffset); - for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { - NativeSharedLibraryAtomV1* atomAllocSpace = - reinterpret_cast<NativeSharedLibraryAtomV1*>(s); - new (atomAllocSpace) NativeSharedLibraryAtomV1(*this, ivarData); - ++ivarData; - } - this->_sharedLibraryAtoms._arrayStart = atomsStart; - this->_sharedLibraryAtoms._arrayEnd = atomsEnd; - this->_sharedLibraryAtoms._elementSize = atomSize; - this->_sharedLibraryAtoms._elementCount = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk SharedLibraryAtomsV1:" - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - - // instantiate array of AbsoluteAtoms from v1 ivar data in file - std::error_code processAbsoluteAtomsV1(const uint8_t *base, - const NativeChunk *chunk) { - const size_t atomSize = sizeof(NativeAbsoluteAtomV1); - size_t atomsArraySize = chunk->elementCount * atomSize; - uint8_t* atomsStart = reinterpret_cast<uint8_t*> - (operator new(atomsArraySize, std::nothrow)); - if (atomsStart == nullptr) - return make_error_code(NativeReaderError::memory_error); - const size_t ivarElementSize = chunk->fileSize - / chunk->elementCount; - if ( ivarElementSize != sizeof(NativeAbsoluteAtomIvarsV1) ) - return make_error_code(NativeReaderError::file_malformed); - uint8_t* atomsEnd = atomsStart + atomsArraySize; - const NativeAbsoluteAtomIvarsV1* ivarData = - reinterpret_cast<const NativeAbsoluteAtomIvarsV1*> - (base + chunk->fileOffset); - for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { - NativeAbsoluteAtomV1* atomAllocSpace = - reinterpret_cast<NativeAbsoluteAtomV1*>(s); - new (atomAllocSpace) NativeAbsoluteAtomV1(*this, ivarData); - ++ivarData; - } - this->_absoluteAtoms._arrayStart = atomsStart; - this->_absoluteAtoms._arrayEnd = atomsEnd; - this->_absoluteAtoms._elementSize = atomSize; - this->_absoluteAtoms._elementCount = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk AbsoluteAtomsV1: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - template <class T, class U> - std::error_code - processReferences(const uint8_t *base, const NativeChunk *chunk, - uint8_t *&refsStart, uint8_t *&refsEnd) const { - if (chunk->elementCount == 0) - return make_error_code(NativeReaderError::success); - size_t refsArraySize = chunk->elementCount * sizeof(T); - refsStart = reinterpret_cast<uint8_t *>( - operator new(refsArraySize, std::nothrow)); - if (refsStart == nullptr) - return make_error_code(NativeReaderError::memory_error); - const size_t ivarElementSize = chunk->fileSize / chunk->elementCount; - if (ivarElementSize != sizeof(U)) - return make_error_code(NativeReaderError::file_malformed); - refsEnd = refsStart + refsArraySize; - const U* ivarData = reinterpret_cast<const U *>(base + chunk->fileOffset); - for (uint8_t *s = refsStart; s != refsEnd; s += sizeof(T), ++ivarData) { - T *atomAllocSpace = reinterpret_cast<T *>(s); - new (atomAllocSpace) T(*this, ivarData); - } - return make_error_code(NativeReaderError::success); - } - - // instantiate array of References from v1 ivar data in file - std::error_code processReferencesV1(const uint8_t *base, - const NativeChunk *chunk) { - uint8_t *refsStart, *refsEnd; - if (std::error_code ec = - processReferences<NativeReferenceV1, NativeReferenceIvarsV1>( - base, chunk, refsStart, refsEnd)) - return ec; - this->_referencesV1.arrayStart = refsStart; - this->_referencesV1.arrayEnd = refsEnd; - this->_referencesV1.elementSize = sizeof(NativeReferenceV1); - this->_referencesV1.elementCount = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", { - llvm::dbgs() << " chunk ReferencesV1: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize << "\n"; - }); - return make_error_code(NativeReaderError::success); - } - - // instantiate array of References from v2 ivar data in file - std::error_code processReferencesV2(const uint8_t *base, - const NativeChunk *chunk) { - uint8_t *refsStart, *refsEnd; - if (std::error_code ec = - processReferences<NativeReferenceV2, NativeReferenceIvarsV2>( - base, chunk, refsStart, refsEnd)) - return ec; - this->_referencesV2.arrayStart = refsStart; - this->_referencesV2.arrayEnd = refsEnd; - this->_referencesV2.elementSize = sizeof(NativeReferenceV2); - this->_referencesV2.elementCount = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", { - llvm::dbgs() << " chunk ReferencesV2: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize << "\n"; - }); - return make_error_code(NativeReaderError::success); - } - - // set up pointers to target table - std::error_code processTargetsTable(const uint8_t *base, - const NativeChunk *chunk) { - const uint32_t* targetIndexes = reinterpret_cast<const uint32_t*> - (base + chunk->fileOffset); - this->_targetsTableCount = chunk->elementCount; - this->_targetsTable = new const Atom*[chunk->elementCount]; - for (uint32_t i=0; i < chunk->elementCount; ++i) { - const uint32_t index = targetIndexes[i]; - if ( index < _definedAtoms._elementCount ) { - const uint8_t* p = _definedAtoms._arrayStart - + index * _definedAtoms._elementSize; - this->_targetsTable[i] = reinterpret_cast<const DefinedAtom*>(p); - continue; - } - const uint32_t undefIndex = index - _definedAtoms._elementCount; - if ( undefIndex < _undefinedAtoms._elementCount ) { - const uint8_t* p = _undefinedAtoms._arrayStart - + undefIndex * _undefinedAtoms._elementSize; - this->_targetsTable[i] = reinterpret_cast<const UndefinedAtom*>(p); - continue; - } - const uint32_t slIndex = index - _definedAtoms._elementCount - - _undefinedAtoms._elementCount; - if ( slIndex < _sharedLibraryAtoms._elementCount ) { - const uint8_t* p = _sharedLibraryAtoms._arrayStart - + slIndex * _sharedLibraryAtoms._elementSize; - this->_targetsTable[i] = reinterpret_cast<const SharedLibraryAtom*>(p); - continue; - } - const uint32_t abIndex = index - _definedAtoms._elementCount - - _undefinedAtoms._elementCount - - _sharedLibraryAtoms._elementCount; - if ( abIndex < _absoluteAtoms._elementCount ) { - const uint8_t* p = _absoluteAtoms._arrayStart - + abIndex * _absoluteAtoms._elementSize; - this->_targetsTable[i] = reinterpret_cast<const AbsoluteAtom*>(p); - continue; - } - return make_error_code(NativeReaderError::file_malformed); - } - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk Targets Table: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - - // set up pointers to addend pool in file - std::error_code processAddendsTable(const uint8_t *base, - const NativeChunk *chunk) { - this->_addends = reinterpret_cast<const Reference::Addend*> - (base + chunk->fileOffset); - this->_addendsMaxIndex = chunk->elementCount; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk Addends: " - << " count=" << chunk->elementCount - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - // set up pointers to string pool in file - std::error_code processStrings(const uint8_t *base, - const NativeChunk *chunk) { - this->_strings = reinterpret_cast<const char*>(base + chunk->fileOffset); - this->_stringsMaxOffset = chunk->fileSize; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk Strings: " - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - // set up pointers to content area in file - std::error_code processContent(const uint8_t *base, - const NativeChunk *chunk) { - this->_contentStart = base + chunk->fileOffset; - this->_contentEnd = base + chunk->fileOffset + chunk->fileSize; - DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() - << " chunk content: " - << " chunkSize=" << chunk->fileSize - << "\n"); - return make_error_code(NativeReaderError::success); - } - - StringRef string(uint32_t offset) const { - assert(offset < _stringsMaxOffset); - return StringRef(&_strings[offset]); - } - - Reference::Addend addend(uint32_t index) const { - if ( index == 0 ) - return 0; // addend index zero is used to mean "no addend" - assert(index <= _addendsMaxIndex); - return _addends[index-1]; // one-based indexing - } - - const NativeAtomAttributesV1& attribute(uint32_t off) const { - assert(off < _attributesMaxOffset); - return *reinterpret_cast<const NativeAtomAttributesV1*>(_attributes + off); - } - - const NativeAtomAttributesV1& absAttribute(uint32_t off) const { - assert(off < _absAbsoluteMaxOffset); - return *reinterpret_cast<const NativeAtomAttributesV1*>(_absAttributes + off); - } - - const uint8_t* content(uint32_t offset, uint32_t size) const { - const uint8_t* result = _contentStart + offset; - assert((result+size) <= _contentEnd); - return result; - } - - const Reference* referenceByIndex(uintptr_t index) const { - if (index < _referencesV1.elementCount) { - return reinterpret_cast<const NativeReferenceV1*>( - _referencesV1.arrayStart + index * _referencesV1.elementSize); - } - assert(index < _referencesV2.elementCount); - return reinterpret_cast<const NativeReferenceV2*>( - _referencesV2.arrayStart + index * _referencesV2.elementSize); - } - - const Atom* targetV1(uint16_t index) const { - if ( index == NativeReferenceIvarsV1::noTarget ) - return nullptr; - assert(index < _targetsTableCount); - return _targetsTable[index]; - } - - void setTargetV1(uint16_t index, const Atom* newAtom) const { - assert(index != NativeReferenceIvarsV1::noTarget); - assert(index > _targetsTableCount); - _targetsTable[index] = newAtom; - } - - const Atom* targetV2(uint32_t index) const { - if (index == NativeReferenceIvarsV2::noTarget) - return nullptr; - assert(index < _targetsTableCount); - return _targetsTable[index]; - } - - void setTargetV2(uint32_t index, const Atom* newAtom) const { - assert(index != NativeReferenceIvarsV2::noTarget); - assert(index > _targetsTableCount); - _targetsTable[index] = newAtom; - } - - template <typename T> - class AtomArray : public File::atom_collection<T> { - public: - AtomArray() : _arrayStart(nullptr), _arrayEnd(nullptr), - _elementSize(0), _elementCount(0) { } - - virtual atom_iterator<T> begin() const { - return atom_iterator<T>(*this, reinterpret_cast<const void*>(_arrayStart)); - } - virtual atom_iterator<T> end() const{ - return atom_iterator<T>(*this, reinterpret_cast<const void*>(_arrayEnd)); - } - virtual const T* deref(const void* it) const { - return reinterpret_cast<const T*>(it); - } - virtual void next(const void*& it) const { - const uint8_t* p = reinterpret_cast<const uint8_t*>(it); - p += _elementSize; - it = reinterpret_cast<const void*>(p); - } - virtual uint64_t size() const { return _elementCount; } - const uint8_t *_arrayStart; - const uint8_t *_arrayEnd; - uint32_t _elementSize; - uint32_t _elementCount; - }; - - struct IvarArray { - IvarArray() : - arrayStart(nullptr), - arrayEnd(nullptr), - elementSize(0), - elementCount(0) { } - - const uint8_t* arrayStart; - const uint8_t* arrayEnd; - uint32_t elementSize; - uint32_t elementCount; - }; - - std::unique_ptr<MemoryBuffer> _mb; - const NativeFileHeader* _header; - AtomArray<DefinedAtom> _definedAtoms; - AtomArray<UndefinedAtom> _undefinedAtoms; - AtomArray<SharedLibraryAtom> _sharedLibraryAtoms; - AtomArray<AbsoluteAtom> _absoluteAtoms; - const uint8_t* _absAttributes; - uint32_t _absAbsoluteMaxOffset; - const uint8_t* _attributes; - uint32_t _attributesMaxOffset; - IvarArray _referencesV1; - IvarArray _referencesV2; - const Atom** _targetsTable; - uint32_t _targetsTableCount; - const char* _strings; - uint32_t _stringsMaxOffset; - const Reference::Addend* _addends; - uint32_t _addendsMaxIndex; - const uint8_t *_contentStart; - const uint8_t *_contentEnd; -}; - -inline const lld::File &NativeDefinedAtomV1::file() const { - return *_file; -} - -inline uint64_t NativeDefinedAtomV1:: ordinal() const { - const uint8_t* p = reinterpret_cast<const uint8_t*>(_ivarData); - return p - _file->_definedAtoms._arrayStart; -} - -inline StringRef NativeDefinedAtomV1::name() const { - return _file->string(_ivarData->nameOffset); -} - -inline const NativeAtomAttributesV1& NativeDefinedAtomV1::attributes() const { - return _file->attribute(_ivarData->attributesOffset); -} - -inline ArrayRef<uint8_t> NativeDefinedAtomV1::rawContent() const { - if (!occupiesDiskSpace()) - return ArrayRef<uint8_t>(); - const uint8_t* p = _file->content(_ivarData->contentOffset, - _ivarData->contentSize); - return ArrayRef<uint8_t>(p, _ivarData->contentSize); -} - -inline StringRef NativeDefinedAtomV1::customSectionName() const { - uint32_t offset = attributes().sectionNameOffset; - return _file->string(offset); -} - -DefinedAtom::reference_iterator NativeDefinedAtomV1::begin() const { - uintptr_t index = _ivarData->referencesStartIndex; - const void* it = reinterpret_cast<const void*>(index); - return reference_iterator(*this, it); -} - -DefinedAtom::reference_iterator NativeDefinedAtomV1::end() const { - uintptr_t index = _ivarData->referencesStartIndex+_ivarData->referencesCount; - const void* it = reinterpret_cast<const void*>(index); - return reference_iterator(*this, it); -} - -const Reference* NativeDefinedAtomV1::derefIterator(const void* it) const { - uintptr_t index = reinterpret_cast<uintptr_t>(it); - return _file->referenceByIndex(index); -} - -void NativeDefinedAtomV1::incrementIterator(const void*& it) const { - uintptr_t index = reinterpret_cast<uintptr_t>(it); - ++index; - it = reinterpret_cast<const void*>(index); -} - -inline const lld::File& NativeUndefinedAtomV1::file() const { - return *_file; -} - -inline StringRef NativeUndefinedAtomV1::name() const { - return _file->string(_ivarData->nameOffset); -} - -inline const UndefinedAtom *NativeUndefinedAtomV1::fallback() const { - if (!_ivarData->fallbackNameOffset) - return nullptr; - if (!_fallback) - _fallback.reset(new SimpleUndefinedAtom( - *_file, _file->string(_ivarData->fallbackNameOffset))); - return _fallback.get(); -} - -inline const lld::File& NativeSharedLibraryAtomV1::file() const { - return *_file; -} - -inline StringRef NativeSharedLibraryAtomV1::name() const { - return _file->string(_ivarData->nameOffset); -} - -inline StringRef NativeSharedLibraryAtomV1::loadName() const { - return _file->string(_ivarData->loadNameOffset); -} - - - -inline const lld::File& NativeAbsoluteAtomV1::file() const { - return *_file; -} - -inline StringRef NativeAbsoluteAtomV1::name() const { - return _file->string(_ivarData->nameOffset); -} - -inline const NativeAtomAttributesV1& NativeAbsoluteAtomV1::absAttributes() const { - return _file->absAttribute(_ivarData->attributesOffset); -} - -inline const Atom* NativeReferenceV1::target() const { - return _file->targetV1(_ivarData->targetIndex); -} - -inline Reference::Addend NativeReferenceV1::addend() const { - return _file->addend(_ivarData->addendIndex); -} - -inline void NativeReferenceV1::setTarget(const Atom* newAtom) { - return _file->setTargetV1(_ivarData->targetIndex, newAtom); -} - -inline void NativeReferenceV1::setAddend(Addend a) { - // Do nothing if addend value is not being changed. - if (addend() == a) - return; - llvm_unreachable("setAddend() not supported"); -} - -inline const Atom* NativeReferenceV2::target() const { - return _file->targetV2(_ivarData->targetIndex); -} - -inline Reference::Addend NativeReferenceV2::addend() const { - return _ivarData->addend; -} - -inline void NativeReferenceV2::setTarget(const Atom* newAtom) { - return _file->setTargetV2(_ivarData->targetIndex, newAtom); -} - -inline void NativeReferenceV2::setAddend(Addend a) { - // Do nothing if addend value is not being changed. - if (addend() == a) - return; - llvm_unreachable("setAddend() not supported"); -} - -uint32_t NativeReferenceV2::tag() const { return _ivarData->tag; } - -} // end namespace native - -namespace { - -class NativeReader : public Reader { -public: - virtual bool canParse(file_magic magic, StringRef, - const MemoryBuffer &mb) const override { - const NativeFileHeader *const header = - reinterpret_cast<const NativeFileHeader *>(mb.getBufferStart()); - return (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, - sizeof(header->magic)) == 0); - } - - virtual std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, - std::vector<std::unique_ptr<File>> &result) const override { - auto *file = new lld::native::File(std::move(mb)); - result.push_back(std::unique_ptr<File>(file)); - return std::error_code(); - } -}; - -} - -void Registry::addSupportNativeObjects() { - add(std::unique_ptr<Reader>(new NativeReader())); -} - -} // end namespace lld diff --git a/lib/ReaderWriter/Native/WriterNative.cpp b/lib/ReaderWriter/Native/WriterNative.cpp deleted file mode 100644 index 5e01a6ce1c7c..000000000000 --- a/lib/ReaderWriter/Native/WriterNative.cpp +++ /dev/null @@ -1,566 +0,0 @@ -//===- lib/ReaderWriter/Native/WriterNative.cpp ---------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "NativeFileFormat.h" -#include "lld/Core/File.h" -#include "lld/Core/LinkingContext.h" -#include "lld/Core/Writer.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/raw_ostream.h" -#include <cstdint> -#include <set> -#include <system_error> -#include <vector> - -namespace lld { -namespace native { - -/// -/// Class for writing native object files. -/// -class Writer : public lld::Writer { -public: - std::error_code writeFile(const lld::File &file, StringRef outPath) override { - // reserve first byte for unnamed atoms - _stringPool.push_back('\0'); - // visit all atoms - for ( const DefinedAtom *defAtom : file.defined() ) { - this->addIVarsForDefinedAtom(*defAtom); - // We are trying to process all atoms, but the defined() iterator does not - // return group children. So, when a group parent is found, we need to - // handle each child atom. - if (defAtom->isGroupParent()) { - for (const Reference *r : *defAtom) { - if (r->kindNamespace() != lld::Reference::KindNamespace::all) - continue; - if (r->kindValue() == lld::Reference::kindGroupChild) { - const DefinedAtom *target = dyn_cast<DefinedAtom>(r->target()); - assert(target && "Internal Error: kindGroupChild references need " - "to be associated with Defined Atoms only"); - this->addIVarsForDefinedAtom(*target); - } - } - } - } - for ( const UndefinedAtom *undefAtom : file.undefined() ) { - this->addIVarsForUndefinedAtom(*undefAtom); - } - for ( const SharedLibraryAtom *shlibAtom : file.sharedLibrary() ) { - this->addIVarsForSharedLibraryAtom(*shlibAtom); - } - for ( const AbsoluteAtom *absAtom : file.absolute() ) { - this->addIVarsForAbsoluteAtom(*absAtom); - } - - maybeConvertReferencesToV1(); - - // construct file header based on atom information accumulated - this->makeHeader(); - - std::error_code ec; - llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::F_None); - if (ec) - return ec; - - this->write(out); - - return std::error_code(); - } - - virtual ~Writer() { - } - -private: - - // write the lld::File in native format to the specified stream - void write(raw_ostream &out) { - assert(out.tell() == 0); - out.write((char*)_headerBuffer, _headerBufferSize); - - writeChunk(out, _definedAtomIvars, NCS_DefinedAtomsV1); - writeChunk(out, _attributes, NCS_AttributesArrayV1); - writeChunk(out, _undefinedAtomIvars, NCS_UndefinedAtomsV1); - writeChunk(out, _sharedLibraryAtomIvars, NCS_SharedLibraryAtomsV1); - writeChunk(out, _absoluteAtomIvars, NCS_AbsoluteAtomsV1); - writeChunk(out, _absAttributes, NCS_AbsoluteAttributesV1); - writeChunk(out, _stringPool, NCS_Strings); - writeChunk(out, _referencesV1, NCS_ReferencesArrayV1); - writeChunk(out, _referencesV2, NCS_ReferencesArrayV2); - - if (!_targetsTableIndex.empty()) { - assert(out.tell() == findChunk(NCS_TargetsTable).fileOffset); - writeTargetTable(out); - } - - if (!_addendsTableIndex.empty()) { - assert(out.tell() == findChunk(NCS_AddendsTable).fileOffset); - writeAddendTable(out); - } - - writeChunk(out, _contentPool, NCS_Content); - } - - template<class T> - void writeChunk(raw_ostream &out, std::vector<T> &vector, uint32_t signature) { - if (vector.empty()) - return; - assert(out.tell() == findChunk(signature).fileOffset); - out.write((char*)&vector[0], vector.size() * sizeof(T)); - } - - void addIVarsForDefinedAtom(const DefinedAtom& atom) { - _definedAtomIndex[&atom] = _definedAtomIvars.size(); - NativeDefinedAtomIvarsV1 ivar; - unsigned refsCount; - ivar.nameOffset = getNameOffset(atom); - ivar.attributesOffset = getAttributeOffset(atom); - ivar.referencesStartIndex = getReferencesIndex(atom, refsCount); - ivar.referencesCount = refsCount; - ivar.contentOffset = getContentOffset(atom); - ivar.contentSize = atom.size(); - ivar.sectionSize = atom.sectionSize(); - _definedAtomIvars.push_back(ivar); - } - - void addIVarsForUndefinedAtom(const UndefinedAtom& atom) { - _undefinedAtomIndex[&atom] = _undefinedAtomIvars.size(); - NativeUndefinedAtomIvarsV1 ivar; - ivar.nameOffset = getNameOffset(atom); - ivar.flags = (atom.canBeNull() & 0x03); - ivar.fallbackNameOffset = 0; - if (atom.fallback()) - ivar.fallbackNameOffset = getNameOffset(*atom.fallback()); - _undefinedAtomIvars.push_back(ivar); - } - - void addIVarsForSharedLibraryAtom(const SharedLibraryAtom& atom) { - _sharedLibraryAtomIndex[&atom] = _sharedLibraryAtomIvars.size(); - NativeSharedLibraryAtomIvarsV1 ivar; - ivar.size = atom.size(); - ivar.nameOffset = getNameOffset(atom); - ivar.loadNameOffset = getSharedLibraryNameOffset(atom.loadName()); - ivar.type = (uint32_t)atom.type(); - ivar.flags = atom.canBeNullAtRuntime(); - _sharedLibraryAtomIvars.push_back(ivar); - } - - void addIVarsForAbsoluteAtom(const AbsoluteAtom& atom) { - _absoluteAtomIndex[&atom] = _absoluteAtomIvars.size(); - NativeAbsoluteAtomIvarsV1 ivar; - ivar.nameOffset = getNameOffset(atom); - ivar.attributesOffset = getAttributeOffset(atom); - ivar.reserved = 0; - ivar.value = atom.value(); - _absoluteAtomIvars.push_back(ivar); - } - - void convertReferencesToV1() { - for (const NativeReferenceIvarsV2 &v2 : _referencesV2) { - NativeReferenceIvarsV1 v1; - v1.offsetInAtom = v2.offsetInAtom; - v1.kindNamespace = v2.kindNamespace; - v1.kindArch = v2.kindArch; - v1.kindValue = v2.kindValue; - v1.targetIndex = (v2.targetIndex == NativeReferenceIvarsV2::noTarget) ? - (uint16_t)NativeReferenceIvarsV1::noTarget : v2.targetIndex; - v1.addendIndex = this->getAddendIndex(v2.addend); - _referencesV1.push_back(v1); - } - _referencesV2.clear(); - } - - bool canConvertReferenceToV1(const NativeReferenceIvarsV2 &ref) { - bool validOffset = (ref.offsetInAtom == NativeReferenceIvarsV2::noTarget) || - ref.offsetInAtom < NativeReferenceIvarsV1::noTarget; - return validOffset && ref.targetIndex < UINT16_MAX; - } - - // Convert vector of NativeReferenceIvarsV2 to NativeReferenceIvarsV1 if - // possible. - void maybeConvertReferencesToV1() { - std::set<int64_t> addends; - for (const NativeReferenceIvarsV2 &ref : _referencesV2) { - if (!canConvertReferenceToV1(ref)) - return; - addends.insert(ref.addend); - if (addends.size() >= UINT16_MAX) - return; - } - convertReferencesToV1(); - } - - // fill out native file header and chunk directory - void makeHeader() { - const bool hasDefines = !_definedAtomIvars.empty(); - const bool hasUndefines = !_undefinedAtomIvars.empty(); - const bool hasSharedLibraries = !_sharedLibraryAtomIvars.empty(); - const bool hasAbsolutes = !_absoluteAtomIvars.empty(); - const bool hasReferencesV1 = !_referencesV1.empty(); - const bool hasReferencesV2 = !_referencesV2.empty(); - const bool hasTargetsTable = !_targetsTableIndex.empty(); - const bool hasAddendTable = !_addendsTableIndex.empty(); - const bool hasContent = !_contentPool.empty(); - - int chunkCount = 1; // always have string pool chunk - if ( hasDefines ) chunkCount += 2; - if ( hasUndefines ) ++chunkCount; - if ( hasSharedLibraries ) ++chunkCount; - if ( hasAbsolutes ) chunkCount += 2; - if ( hasReferencesV1 ) ++chunkCount; - if ( hasReferencesV2 ) ++chunkCount; - if ( hasTargetsTable ) ++chunkCount; - if ( hasAddendTable ) ++chunkCount; - if ( hasContent ) ++chunkCount; - - _headerBufferSize = sizeof(NativeFileHeader) - + chunkCount*sizeof(NativeChunk); - _headerBuffer = reinterpret_cast<NativeFileHeader*> - (operator new(_headerBufferSize, std::nothrow)); - NativeChunk *chunks = - reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer) - + sizeof(NativeFileHeader)); - memcpy(_headerBuffer->magic, NATIVE_FILE_HEADER_MAGIC, - sizeof(_headerBuffer->magic)); - _headerBuffer->endian = NFH_LittleEndian; - _headerBuffer->architecture = 0; - _headerBuffer->fileSize = 0; - _headerBuffer->chunkCount = chunkCount; - - // create chunk for defined atom ivar array - int nextIndex = 0; - uint32_t nextFileOffset = _headerBufferSize; - if (hasDefines) { - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _definedAtomIvars, - NCS_DefinedAtomsV1); - - // create chunk for attributes - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _attributes, - NCS_AttributesArrayV1); - } - - // create chunk for undefined atom array - if (hasUndefines) - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _undefinedAtomIvars, - NCS_UndefinedAtomsV1); - - // create chunk for shared library atom array - if (hasSharedLibraries) - fillChunkHeader(chunks[nextIndex++], nextFileOffset, - _sharedLibraryAtomIvars, NCS_SharedLibraryAtomsV1); - - // create chunk for shared library atom array - if (hasAbsolutes) { - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _absoluteAtomIvars, - NCS_AbsoluteAtomsV1); - - // create chunk for attributes - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _absAttributes, - NCS_AbsoluteAttributesV1); - } - - // create chunk for symbol strings - // pad end of string pool to 4-bytes - while ((_stringPool.size() % 4) != 0) - _stringPool.push_back('\0'); - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _stringPool, - NCS_Strings); - - // create chunk for referencesV2 - if (hasReferencesV1) - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV1, - NCS_ReferencesArrayV1); - - // create chunk for referencesV2 - if (hasReferencesV2) - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV2, - NCS_ReferencesArrayV2); - - // create chunk for target table - if (hasTargetsTable) { - NativeChunk& cht = chunks[nextIndex++]; - cht.signature = NCS_TargetsTable; - cht.fileOffset = nextFileOffset; - cht.fileSize = _targetsTableIndex.size() * sizeof(uint32_t); - cht.elementCount = _targetsTableIndex.size(); - nextFileOffset = cht.fileOffset + cht.fileSize; - } - - // create chunk for addend table - if (hasAddendTable) { - NativeChunk& chad = chunks[nextIndex++]; - chad.signature = NCS_AddendsTable; - chad.fileOffset = nextFileOffset; - chad.fileSize = _addendsTableIndex.size() * sizeof(Reference::Addend); - chad.elementCount = _addendsTableIndex.size(); - nextFileOffset = chad.fileOffset + chad.fileSize; - } - - // create chunk for content - if (hasContent) - fillChunkHeader(chunks[nextIndex++], nextFileOffset, _contentPool, - NCS_Content); - - _headerBuffer->fileSize = nextFileOffset; - } - - template<class T> - void fillChunkHeader(NativeChunk &chunk, uint32_t &nextFileOffset, - const std::vector<T> &data, uint32_t signature) { - chunk.signature = signature; - chunk.fileOffset = nextFileOffset; - chunk.fileSize = data.size() * sizeof(T); - chunk.elementCount = data.size(); - nextFileOffset = chunk.fileOffset + chunk.fileSize; - } - - // scan header to find particular chunk - NativeChunk& findChunk(uint32_t signature) { - const uint32_t chunkCount = _headerBuffer->chunkCount; - NativeChunk* chunks = - reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer) - + sizeof(NativeFileHeader)); - for (uint32_t i=0; i < chunkCount; ++i) { - if ( chunks[i].signature == signature ) - return chunks[i]; - } - llvm_unreachable("findChunk() signature not found"); - } - - // append atom name to string pool and return offset - uint32_t getNameOffset(const Atom& atom) { - return this->getNameOffset(atom.name()); - } - - // check if name is already in pool or append and return offset - uint32_t getSharedLibraryNameOffset(StringRef name) { - assert(!name.empty()); - // look to see if this library name was used by another atom - for (auto &it : _sharedLibraryNames) - if (name.equals(it.first)) - return it.second; - // first use of this library name - uint32_t result = this->getNameOffset(name); - _sharedLibraryNames.push_back(std::make_pair(name, result)); - return result; - } - - // append atom name to string pool and return offset - uint32_t getNameOffset(StringRef name) { - if ( name.empty() ) - return 0; - uint32_t result = _stringPool.size(); - _stringPool.insert(_stringPool.end(), name.begin(), name.end()); - _stringPool.push_back(0); - return result; - } - - // append atom cotent to content pool and return offset - uint32_t getContentOffset(const DefinedAtom& atom) { - if (!atom.occupiesDiskSpace()) - return 0; - uint32_t result = _contentPool.size(); - ArrayRef<uint8_t> cont = atom.rawContent(); - _contentPool.insert(_contentPool.end(), cont.begin(), cont.end()); - return result; - } - - // reuse existing attributes entry or create a new one and return offet - uint32_t getAttributeOffset(const DefinedAtom& atom) { - NativeAtomAttributesV1 attrs = computeAttributesV1(atom); - return getOrPushAttribute(_attributes, attrs); - } - - uint32_t getAttributeOffset(const AbsoluteAtom& atom) { - NativeAtomAttributesV1 attrs = computeAbsoluteAttributes(atom); - return getOrPushAttribute(_absAttributes, attrs); - } - - uint32_t getOrPushAttribute(std::vector<NativeAtomAttributesV1> &dest, - const NativeAtomAttributesV1 &attrs) { - for (size_t i = 0, e = dest.size(); i < e; ++i) { - if (!memcmp(&dest[i], &attrs, sizeof(attrs))) { - // found that this set of attributes already used, so re-use - return i * sizeof(attrs); - } - } - // append new attribute set to end - uint32_t result = dest.size() * sizeof(attrs); - dest.push_back(attrs); - return result; - } - - uint32_t sectionNameOffset(const DefinedAtom& atom) { - // if section based on content, then no custom section name available - if (atom.sectionChoice() == DefinedAtom::sectionBasedOnContent) - return 0; - StringRef name = atom.customSectionName(); - assert(!name.empty()); - // look to see if this section name was used by another atom - for (auto &it : _sectionNames) - if (name.equals(it.first)) - return it.second; - // first use of this section name - uint32_t result = this->getNameOffset(name); - _sectionNames.push_back(std::make_pair(name, result)); - return result; - } - - NativeAtomAttributesV1 computeAttributesV1(const DefinedAtom& atom) { - NativeAtomAttributesV1 attrs; - attrs.sectionNameOffset = sectionNameOffset(atom); - attrs.align2 = atom.alignment().powerOf2; - attrs.alignModulus = atom.alignment().modulus; - attrs.scope = atom.scope(); - attrs.interposable = atom.interposable(); - attrs.merge = atom.merge(); - attrs.contentType = atom.contentType(); - attrs.sectionChoice = atom.sectionChoice(); - attrs.deadStrip = atom.deadStrip(); - attrs.dynamicExport = atom.dynamicExport(); - attrs.codeModel = atom.codeModel(); - attrs.permissions = atom.permissions(); - return attrs; - } - - NativeAtomAttributesV1 computeAbsoluteAttributes(const AbsoluteAtom& atom) { - NativeAtomAttributesV1 attrs; - attrs.scope = atom.scope(); - return attrs; - } - - // add references for this atom in a contiguous block in NCS_ReferencesArrayV2 - uint32_t getReferencesIndex(const DefinedAtom& atom, unsigned& refsCount) { - size_t startRefSize = _referencesV2.size(); - uint32_t result = startRefSize; - for (const Reference *ref : atom) { - NativeReferenceIvarsV2 nref; - nref.offsetInAtom = ref->offsetInAtom(); - nref.kindNamespace = (uint8_t)ref->kindNamespace(); - nref.kindArch = (uint8_t)ref->kindArch(); - nref.kindValue = ref->kindValue(); - nref.targetIndex = this->getTargetIndex(ref->target()); - nref.addend = ref->addend(); - nref.tag = ref->tag(); - _referencesV2.push_back(nref); - } - refsCount = _referencesV2.size() - startRefSize; - return (refsCount == 0) ? 0 : result; - } - - uint32_t getTargetIndex(const Atom* target) { - if ( target == nullptr ) - return NativeReferenceIvarsV2::noTarget; - TargetToIndex::const_iterator pos = _targetsTableIndex.find(target); - if ( pos != _targetsTableIndex.end() ) { - return pos->second; - } - uint32_t result = _targetsTableIndex.size(); - _targetsTableIndex[target] = result; - return result; - } - - void writeTargetTable(raw_ostream &out) { - // Build table of target indexes - uint32_t maxTargetIndex = _targetsTableIndex.size(); - assert(maxTargetIndex > 0); - std::vector<uint32_t> targetIndexes(maxTargetIndex); - for (auto &it : _targetsTableIndex) { - const Atom* atom = it.first; - uint32_t targetIndex = it.second; - assert(targetIndex < maxTargetIndex); - - TargetToIndex::iterator pos = _definedAtomIndex.find(atom); - if (pos != _definedAtomIndex.end()) { - targetIndexes[targetIndex] = pos->second; - continue; - } - uint32_t base = _definedAtomIvars.size(); - - pos = _undefinedAtomIndex.find(atom); - if (pos != _undefinedAtomIndex.end()) { - targetIndexes[targetIndex] = pos->second + base; - continue; - } - base += _undefinedAtomIndex.size(); - - pos = _sharedLibraryAtomIndex.find(atom); - if (pos != _sharedLibraryAtomIndex.end()) { - targetIndexes[targetIndex] = pos->second + base; - continue; - } - base += _sharedLibraryAtomIndex.size(); - - pos = _absoluteAtomIndex.find(atom); - assert(pos != _absoluteAtomIndex.end()); - targetIndexes[targetIndex] = pos->second + base; - } - // write table - out.write((char*)&targetIndexes[0], maxTargetIndex * sizeof(uint32_t)); - } - - uint32_t getAddendIndex(Reference::Addend addend) { - if ( addend == 0 ) - return 0; // addend index zero is used to mean "no addend" - AddendToIndex::const_iterator pos = _addendsTableIndex.find(addend); - if ( pos != _addendsTableIndex.end() ) { - return pos->second; - } - uint32_t result = _addendsTableIndex.size() + 1; // one-based index - _addendsTableIndex[addend] = result; - return result; - } - - void writeAddendTable(raw_ostream &out) { - // Build table of addends - uint32_t maxAddendIndex = _addendsTableIndex.size(); - std::vector<Reference::Addend> addends(maxAddendIndex); - for (auto &it : _addendsTableIndex) { - Reference::Addend addend = it.first; - uint32_t index = it.second; - assert(index <= maxAddendIndex); - addends[index-1] = addend; - } - // write table - out.write((char*)&addends[0], maxAddendIndex*sizeof(Reference::Addend)); - } - - typedef std::vector<std::pair<StringRef, uint32_t>> NameToOffsetVector; - - typedef llvm::DenseMap<const Atom*, uint32_t> TargetToIndex; - typedef llvm::DenseMap<Reference::Addend, uint32_t> AddendToIndex; - - NativeFileHeader* _headerBuffer; - size_t _headerBufferSize; - std::vector<char> _stringPool; - std::vector<uint8_t> _contentPool; - std::vector<NativeDefinedAtomIvarsV1> _definedAtomIvars; - std::vector<NativeAtomAttributesV1> _attributes; - std::vector<NativeAtomAttributesV1> _absAttributes; - std::vector<NativeUndefinedAtomIvarsV1> _undefinedAtomIvars; - std::vector<NativeSharedLibraryAtomIvarsV1> _sharedLibraryAtomIvars; - std::vector<NativeAbsoluteAtomIvarsV1> _absoluteAtomIvars; - std::vector<NativeReferenceIvarsV1> _referencesV1; - std::vector<NativeReferenceIvarsV2> _referencesV2; - TargetToIndex _targetsTableIndex; - TargetToIndex _definedAtomIndex; - TargetToIndex _undefinedAtomIndex; - TargetToIndex _sharedLibraryAtomIndex; - TargetToIndex _absoluteAtomIndex; - AddendToIndex _addendsTableIndex; - NameToOffsetVector _sectionNames; - NameToOffsetVector _sharedLibraryNames; -}; -} // end namespace native - -std::unique_ptr<Writer> createWriterNative() { - return std::unique_ptr<Writer>(new native::Writer()); -} -} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/Atoms.h b/lib/ReaderWriter/PECOFF/Atoms.h deleted file mode 100644 index 257edc17884b..000000000000 --- a/lib/ReaderWriter/PECOFF/Atoms.h +++ /dev/null @@ -1,312 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/Atoms.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_PE_COFF_ATOMS_H -#define LLD_READER_WRITER_PE_COFF_ATOMS_H - -#include "lld/Core/File.h" -#include "lld/Core/Simple.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Object/COFF.h" -#include <vector> - -namespace lld { -namespace pecoff { -class COFFDefinedAtom; - -class COFFUndefinedAtom : public UndefinedAtom { -public: - COFFUndefinedAtom(const File &file, StringRef name, - const UndefinedAtom *fallback = nullptr) - : _owningFile(file), _name(name), _fallback(fallback) {} - - const File &file() const override { return _owningFile; } - StringRef name() const override { return _name; } - CanBeNull canBeNull() const override { return CanBeNull::canBeNullNever; } - const UndefinedAtom *fallback() const override { return _fallback; } - -private: - const File &_owningFile; - StringRef _name; - const UndefinedAtom *_fallback; -}; - -/// The base class of all COFF defined atoms. A derived class of -/// COFFBaseDefinedAtom may represent atoms read from a file or atoms created -/// by the linker. An example of the latter case is the jump table for symbols -/// in a DLL. -class COFFBaseDefinedAtom : public DefinedAtom { -public: - enum class Kind { - File, - Internal - }; - - const File &file() const override { return _file; } - StringRef name() const override { return _name; } - Interposable interposable() const override { return interposeNo; } - Merge merge() const override { return mergeNo; } - Alignment alignment() const override { return Alignment(0); } - StringRef customSectionName() const override { return ""; } - DeadStripKind deadStrip() const override { return deadStripNormal; } - - Kind getKind() const { return _kind; } - - void addReference(std::unique_ptr<SimpleReference> reference) { - _references.push_back(std::move(reference)); - } - - reference_iterator begin() const override { - return reference_iterator(*this, reinterpret_cast<const void *>(0)); - } - - reference_iterator end() const override { - return reference_iterator( - *this, reinterpret_cast<const void *>(_references.size())); - } - -protected: - COFFBaseDefinedAtom(const File &file, StringRef name, Kind kind) - : _file(file), _name(name), _kind(kind) {} - -private: - const Reference *derefIterator(const void *iter) const override { - size_t index = reinterpret_cast<size_t>(iter); - return _references[index].get(); - } - - void incrementIterator(const void *&iter) const override { - size_t index = reinterpret_cast<size_t>(iter); - iter = reinterpret_cast<const void *>(index + 1); - } - - const File &_file; - StringRef _name; - Kind _kind; - std::vector<std::unique_ptr<SimpleReference>> _references; -}; - -/// This is the root class of the atom read from a file. This class have two -/// subclasses; one for the regular atom and another for the BSS atom. -class COFFDefinedFileAtom : public COFFBaseDefinedAtom { -public: - COFFDefinedFileAtom(const File &file, StringRef name, StringRef sectionName, - uint64_t sectionSize, Scope scope, - ContentType contentType, ContentPermissions perms, - uint64_t ordinal) - : COFFBaseDefinedAtom(file, name, Kind::File), _sectionName(sectionName), - _sectionSize(sectionSize), _scope(scope), _contentType(contentType), - _permissions(perms), _ordinal(ordinal), _alignment(0) {} - - static bool classof(const COFFBaseDefinedAtom *atom) { - return atom->getKind() == Kind::File; - } - - void setAlignment(Alignment val) { _alignment = val; } - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return _sectionName; } - uint64_t sectionSize() const override { return _sectionSize; } - Scope scope() const override { return _scope; } - ContentType contentType() const override { return _contentType; } - ContentPermissions permissions() const override { return _permissions; } - uint64_t ordinal() const override { return _ordinal; } - Alignment alignment() const override { return _alignment; } - - void addAssociate(const DefinedAtom *other) { - auto *ref = new SimpleReference(Reference::KindNamespace::all, - Reference::KindArch::all, - lld::Reference::kindAssociate, 0, other, 0); - addReference(std::unique_ptr<SimpleReference>(ref)); - } - -private: - StringRef _sectionName; - uint64_t _sectionSize; - Scope _scope; - ContentType _contentType; - ContentPermissions _permissions; - uint64_t _ordinal; - Alignment _alignment; - std::vector<std::unique_ptr<SimpleReference>> _references; -}; - -// A COFFDefinedAtom represents an atom read from a file and has contents. -class COFFDefinedAtom : public COFFDefinedFileAtom { -public: - COFFDefinedAtom(const File &file, StringRef name, StringRef sectionName, - uint64_t sectionSize, Scope scope, ContentType type, - bool isComdat, ContentPermissions perms, Merge merge, - ArrayRef<uint8_t> data, uint64_t ordinal) - : COFFDefinedFileAtom(file, name, sectionName, sectionSize, - scope, type, perms, ordinal), - _isComdat(isComdat), _merge(merge), _dataref(data) {} - - Merge merge() const override { return _merge; } - uint64_t size() const override { return _dataref.size(); } - ArrayRef<uint8_t> rawContent() const override { return _dataref; } - - DeadStripKind deadStrip() const override { - // Only COMDAT symbols would be dead-stripped. - return _isComdat ? deadStripNormal : deadStripNever; - } - -private: - bool _isComdat; - Merge _merge; - ArrayRef<uint8_t> _dataref; -}; - -// A COFFDefinedAtom represents an atom for BSS section. -class COFFBSSAtom : public COFFDefinedFileAtom { -public: - COFFBSSAtom(const File &file, StringRef name, Scope scope, - ContentPermissions perms, Merge merge, uint32_t size, - uint64_t ordinal) - : COFFDefinedFileAtom(file, name, ".bss", 0, scope, typeZeroFill, - perms, ordinal), - _merge(merge), _size(size) {} - - Merge merge() const override { return _merge; } - uint64_t size() const override { return _size; } - ArrayRef<uint8_t> rawContent() const override { return _contents; } - -private: - Merge _merge; - uint32_t _size; - std::vector<uint8_t> _contents; -}; - -/// A COFFLinkerInternalAtom represents a defined atom created by the linker, -/// not read from file. -class COFFLinkerInternalAtom : public COFFBaseDefinedAtom { -public: - SectionChoice sectionChoice() const override { return sectionBasedOnContent; } - uint64_t ordinal() const override { return _ordinal; } - Scope scope() const override { return scopeGlobal; } - Alignment alignment() const override { return Alignment(0); } - uint64_t size() const override { return _data.size(); } - ArrayRef<uint8_t> rawContent() const override { return _data; } - -protected: - COFFLinkerInternalAtom(const File &file, uint64_t ordinal, - std::vector<uint8_t> data, StringRef symbolName = "") - : COFFBaseDefinedAtom(file, symbolName, Kind::Internal), - _ordinal(ordinal), _data(std::move(data)) {} - -private: - uint64_t _ordinal; - std::vector<uint8_t> _data; -}; - -class COFFStringAtom : public COFFLinkerInternalAtom { -public: - COFFStringAtom(const File &file, uint64_t ordinal, StringRef sectionName, - StringRef contents) - : COFFLinkerInternalAtom(file, ordinal, stringRefToVector(contents)), - _sectionName(sectionName) {} - - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return _sectionName; } - ContentType contentType() const override { return typeData; } - ContentPermissions permissions() const override { return permR__; } - -private: - StringRef _sectionName; - - std::vector<uint8_t> stringRefToVector(StringRef name) const { - std::vector<uint8_t> ret(name.size() + 1); - memcpy(&ret[0], name.data(), name.size()); - ret[name.size()] = 0; - return ret; - } -}; - -// A COFFSharedLibraryAtom represents a symbol for data in an import library. A -// reference to a COFFSharedLibraryAtom will be transformed to a real reference -// to an import address table entry in Idata pass. -class COFFSharedLibraryAtom : public SharedLibraryAtom { -public: - COFFSharedLibraryAtom(const File &file, uint16_t hint, StringRef symbolName, - StringRef importName, StringRef dllName) - : _file(file), _hint(hint), _mangledName(addImpPrefix(symbolName)), - _importName(importName), _dllName(dllName), _importTableEntry(nullptr) { - } - - const File &file() const override { return _file; } - uint16_t hint() const { return _hint; } - - /// Returns the symbol name to be used by the core linker. - StringRef name() const override { return _mangledName; } - - /// Returns the symbol name to be used in the import description table in the - /// COFF header. - virtual StringRef importName() const { return _importName; } - - StringRef loadName() const override { return _dllName; } - bool canBeNullAtRuntime() const override { return false; } - Type type() const override { return Type::Unknown; } - uint64_t size() const override { return 0; } - - void setImportTableEntry(const DefinedAtom *atom) { - _importTableEntry = atom; - } - - const DefinedAtom *getImportTableEntry() const { return _importTableEntry; } - -private: - /// Mangle the symbol name by adding "__imp_" prefix. See the file comment of - /// ReaderImportHeader.cpp for details about the prefix. - std::string addImpPrefix(StringRef symbolName) { - std::string ret("__imp_"); - ret.append(symbolName); - return ret; - } - - const File &_file; - uint16_t _hint; - std::string _mangledName; - std::string _importName; - StringRef _dllName; - const DefinedAtom *_importTableEntry; -}; - -// An instance of this class represents "input file" for atoms created in a -// pass. Atoms need to be associated to an input file even if it's not read from -// a file, so we use this class for that. -class VirtualFile : public SimpleFile { -public: - VirtualFile(const LinkingContext &ctx) - : SimpleFile("<virtual-file>"), _nextOrdinal(0) { - setOrdinal(ctx.getNextOrdinalAndIncrement()); - } - - uint64_t getNextOrdinal() { return _nextOrdinal++; } - -private: - uint64_t _nextOrdinal; -}; - -//===----------------------------------------------------------------------===// -// -// Utility functions to handle layout edges. -// -//===----------------------------------------------------------------------===// - -template <typename T, typename U> -void addLayoutEdge(T *a, U *b, uint32_t which) { - auto ref = new SimpleReference(Reference::KindNamespace::all, - Reference::KindArch::all, - which, 0, b, 0); - a->addReference(std::unique_ptr<SimpleReference>(ref)); -} - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/CMakeLists.txt b/lib/ReaderWriter/PECOFF/CMakeLists.txt deleted file mode 100644 index 86b49b79f194..000000000000 --- a/lib/ReaderWriter/PECOFF/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_llvm_library(lldPECOFF - EdataPass.cpp - IdataPass.cpp - LinkerGeneratedSymbolFile.cpp - LoadConfigPass.cpp - PECOFFLinkingContext.cpp - Pass.cpp - ReaderCOFF.cpp - ReaderImportHeader.cpp - WriterImportLibrary.cpp - WriterPECOFF.cpp - LINK_LIBS - lldCore - LLVMObject - LLVMSupport - ) diff --git a/lib/ReaderWriter/PECOFF/EdataPass.cpp b/lib/ReaderWriter/PECOFF/EdataPass.cpp deleted file mode 100644 index ad79f171f3c9..000000000000 --- a/lib/ReaderWriter/PECOFF/EdataPass.cpp +++ /dev/null @@ -1,227 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/EdataPass.cpp ------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "Pass.h" -#include "EdataPass.h" -#include "lld/Core/File.h" -#include "lld/Core/Pass.h" -#include "lld/Core/Simple.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Path.h" -#include <climits> -#include <ctime> -#include <utility> - -using lld::pecoff::edata::EdataAtom; -using lld::pecoff::edata::TableEntry; -using llvm::object::export_address_table_entry; -using llvm::object::export_directory_table_entry; - -namespace lld { -namespace pecoff { - -typedef PECOFFLinkingContext::ExportDesc ExportDesc; - -// dedupExports removes duplicate export entries. If two exports are -// referring the same symbol, they are considered duplicates. -// This could happen if the same symbol name is specified as an argument -// to /export more than once, or an unmangled and mangled name of the -// same symbol are given to /export. In the latter case, we choose -// unmangled (shorter) name. -static void dedupExports(PECOFFLinkingContext &ctx) { - std::vector<ExportDesc> &exports = ctx.getDllExports(); - // Pass 1: find duplicate entries - std::set<const ExportDesc *> dup; - std::map<StringRef, ExportDesc *> map; - for (ExportDesc &exp : exports) { - if (!exp.externalName.empty()) - continue; - StringRef symbol = exp.getRealName(); - auto it = map.find(symbol); - if (it == map.end()) { - map[symbol] = &exp; - } else if (symbol.size() < it->second->getRealName().size()) { - map[symbol] = &exp; - dup.insert(it->second); - } else { - dup.insert(&exp); - } - } - // Pass 2: remove duplicate entries - auto pred = [&](const ExportDesc &exp) { - return dup.count(&exp) == 1; - }; - exports.erase(std::remove_if(exports.begin(), exports.end(), pred), - exports.end()); -} - -static void assignOrdinals(PECOFFLinkingContext &ctx) { - std::vector<ExportDesc> &exports = ctx.getDllExports(); - int maxOrdinal = -1; - for (ExportDesc &desc : exports) - maxOrdinal = std::max(maxOrdinal, desc.ordinal); - - std::sort(exports.begin(), exports.end(), - [](const ExportDesc &a, const ExportDesc &b) { - return a.getExternalName().compare(b.getExternalName()) < 0; - }); - - int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1); - for (ExportDesc &desc : exports) - if (desc.ordinal == -1) - desc.ordinal = nextOrdinal++; -} - -static bool getExportedAtoms(PECOFFLinkingContext &ctx, MutableFile *file, - std::vector<TableEntry> &ret) { - std::map<StringRef, const DefinedAtom *> definedAtoms; - for (const DefinedAtom *atom : file->defined()) - definedAtoms[atom->name()] = atom; - - for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) { - auto it = definedAtoms.find(desc.getRealName()); - if (it == definedAtoms.end()) { - llvm::errs() << "Symbol <" << desc.name - << "> is exported but not defined.\n"; - return false; - } - const DefinedAtom *atom = it->second; - - // One can export a symbol with a different name than the symbol - // name used in DLL. If such name is specified, use it in the - // .edata section. - ret.push_back(TableEntry(ctx.undecorateSymbol(desc.getExternalName()), - desc.ordinal, atom, desc.noname)); - } - std::sort(ret.begin(), ret.end(), - [](const TableEntry &a, const TableEntry &b) { - return a.exportName.compare(b.exportName) < 0; - }); - - return true; -} - -static std::pair<int, int> getOrdinalBase(std::vector<TableEntry> &entries) { - int ordinalBase = INT_MAX; - int maxOrdinal = -1; - for (TableEntry &e : entries) { - ordinalBase = std::min(ordinalBase, e.ordinal); - maxOrdinal = std::max(maxOrdinal, e.ordinal); - } - return std::pair<int, int>(ordinalBase, maxOrdinal); -} - -edata::EdataAtom * -EdataPass::createAddressTable(const std::vector<TableEntry> &entries, - int ordinalBase, int maxOrdinal) { - EdataAtom *addressTable = - new (_alloc) EdataAtom(_file, sizeof(export_address_table_entry) * - (maxOrdinal - ordinalBase + 1)); - - for (const TableEntry &e : entries) { - int index = e.ordinal - ordinalBase; - size_t offset = index * sizeof(export_address_table_entry); - addDir32NBReloc(addressTable, e.atom, _ctx.getMachineType(), offset); - } - return addressTable; -} - -edata::EdataAtom * -EdataPass::createNamePointerTable(const PECOFFLinkingContext &ctx, - const std::vector<TableEntry> &entries, - MutableFile *file) { - EdataAtom *table = - new (_alloc) EdataAtom(_file, sizeof(uint32_t) * entries.size()); - - size_t offset = 0; - for (const TableEntry &e : entries) { - auto *stringAtom = new (_alloc) COFFStringAtom( - _file, _stringOrdinal++, ".edata", e.exportName); - file->addAtom(*stringAtom); - addDir32NBReloc(table, stringAtom, _ctx.getMachineType(), offset); - offset += sizeof(uint32_t); - } - return table; -} - -edata::EdataAtom *EdataPass::createExportDirectoryTable( - const std::vector<edata::TableEntry> &namedEntries, int ordinalBase, - int maxOrdinal) { - EdataAtom *ret = - new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry)); - auto *data = ret->getContents<export_directory_table_entry>(); - data->TimeDateStamp = time(nullptr); - data->OrdinalBase = ordinalBase; - data->AddressTableEntries = maxOrdinal - ordinalBase + 1; - data->NumberOfNamePointers = namedEntries.size(); - return ret; -} - -edata::EdataAtom * -EdataPass::createOrdinalTable(const std::vector<TableEntry> &entries, - int ordinalBase) { - EdataAtom *ret = - new (_alloc) EdataAtom(_file, sizeof(uint16_t) * entries.size()); - uint16_t *data = ret->getContents<uint16_t>(); - int i = 0; - for (const TableEntry &e : entries) - data[i++] = e.ordinal - ordinalBase; - return ret; -} - -void EdataPass::perform(std::unique_ptr<MutableFile> &file) { - dedupExports(_ctx); - assignOrdinals(_ctx); - - std::vector<TableEntry> entries; - if (!getExportedAtoms(_ctx, file.get(), entries)) - return; - if (entries.empty()) - return; - - int ordinalBase, maxOrdinal; - std::tie(ordinalBase, maxOrdinal) = getOrdinalBase(entries); - - std::vector<TableEntry> namedEntries; - for (TableEntry &e : entries) - if (!e.noname) - namedEntries.push_back(e); - - EdataAtom *table = - createExportDirectoryTable(namedEntries, ordinalBase, maxOrdinal); - file->addAtom(*table); - - COFFStringAtom *dllName = - new (_alloc) COFFStringAtom(_file, _stringOrdinal++, ".edata", - llvm::sys::path::filename(_ctx.outputPath())); - file->addAtom(*dllName); - addDir32NBReloc(table, dllName, _ctx.getMachineType(), - offsetof(export_directory_table_entry, NameRVA)); - - EdataAtom *addressTable = - createAddressTable(entries, ordinalBase, maxOrdinal); - file->addAtom(*addressTable); - addDir32NBReloc( - table, addressTable, _ctx.getMachineType(), - offsetof(export_directory_table_entry, ExportAddressTableRVA)); - - EdataAtom *namePointerTable = - createNamePointerTable(_ctx, namedEntries, file.get()); - file->addAtom(*namePointerTable); - addDir32NBReloc(table, namePointerTable, _ctx.getMachineType(), - offsetof(export_directory_table_entry, NamePointerRVA)); - - EdataAtom *ordinalTable = createOrdinalTable(namedEntries, ordinalBase); - file->addAtom(*ordinalTable); - addDir32NBReloc(table, ordinalTable, _ctx.getMachineType(), - offsetof(export_directory_table_entry, OrdinalTableRVA)); -} - -} // namespace pecoff -} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/EdataPass.h b/lib/ReaderWriter/PECOFF/EdataPass.h deleted file mode 100644 index 442be3ca24aa..000000000000 --- a/lib/ReaderWriter/PECOFF/EdataPass.h +++ /dev/null @@ -1,99 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/EdataPass.h --------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file \brief This linker pass creates atoms for the DLL export -/// information. The defined atoms constructed in this pass will go into .edata -/// section. -/// -/// For the details of the .edata section format, see Microsoft PE/COFF -/// Specification section 5.3, The .edata Section. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_PE_COFF_EDATA_PASS_H -#define LLD_READER_WRITER_PE_COFF_EDATA_PASS_H - -#include "Atoms.h" -#include "lld/Core/File.h" -#include "lld/Core/Pass.h" -#include "lld/Core/Simple.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/Support/COFF.h" -#include <map> - -using llvm::COFF::ImportDirectoryTableEntry; - -namespace lld { -namespace pecoff { -namespace edata { - -struct TableEntry { - TableEntry(StringRef exp, int ord, const DefinedAtom *a, bool n) - : exportName(exp), ordinal(ord), atom(a), noname(n) {} - std::string exportName; - int ordinal; - const DefinedAtom *atom; - bool noname; -}; - -/// The root class of all edata atoms. -class EdataAtom : public COFFLinkerInternalAtom { -public: - EdataAtom(VirtualFile &file, size_t size) - : COFFLinkerInternalAtom(file, file.getNextOrdinal(), - std::vector<uint8_t>(size)) {} - - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return ".edata"; } - ContentType contentType() const override { return typeData; } - ContentPermissions permissions() const override { return permR__; } - - template <typename T> T *getContents() const { - return (T *)const_cast<uint8_t *>(rawContent().data()); - } -}; - -} // namespace edata - -class EdataPass : public lld::Pass { -public: - EdataPass(PECOFFLinkingContext &ctx) - : _ctx(ctx), _file(ctx), _is64(ctx.is64Bit()), _stringOrdinal(1024) {} - - void perform(std::unique_ptr<MutableFile> &file) override; - -private: - edata::EdataAtom * - createExportDirectoryTable(const std::vector<edata::TableEntry> &namedEntries, - int ordinalBase, int maxOrdinal); - - edata::EdataAtom * - createAddressTable(const std::vector<edata::TableEntry> &entries, - int ordinalBase, int maxOrdinal); - - edata::EdataAtom * - createNamePointerTable(const PECOFFLinkingContext &ctx, - const std::vector<edata::TableEntry> &entries, - MutableFile *file); - - edata::EdataAtom * - createOrdinalTable(const std::vector<edata::TableEntry> &entries, - int ordinalBase); - - PECOFFLinkingContext &_ctx; - VirtualFile _file; - bool _is64; - int _stringOrdinal; - mutable llvm::BumpPtrAllocator _alloc; -}; - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/IdataPass.cpp b/lib/ReaderWriter/PECOFF/IdataPass.cpp deleted file mode 100644 index d41ef581f7fa..000000000000 --- a/lib/ReaderWriter/PECOFF/IdataPass.cpp +++ /dev/null @@ -1,345 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/IdataPass.cpp ------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IdataPass.h" -#include "Pass.h" -#include "lld/Core/File.h" -#include "lld/Core/Pass.h" -#include "lld/Core/Simple.h" -#include "llvm/Support/COFF.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Endian.h" -#include <algorithm> -#include <cstddef> -#include <cstring> -#include <map> -#include <vector> - -using namespace llvm::support::endian; -using llvm::object::delay_import_directory_table_entry; - -namespace lld { -namespace pecoff { -namespace idata { - -IdataAtom::IdataAtom(IdataContext &context, std::vector<uint8_t> data) - : COFFLinkerInternalAtom(context.dummyFile, - context.dummyFile.getNextOrdinal(), data) { - context.file.addAtom(*this); -} - -HintNameAtom::HintNameAtom(IdataContext &context, uint16_t hint, - StringRef importName) - : IdataAtom(context, assembleRawContent(hint, importName)), - _importName(importName) {} - -std::vector<uint8_t> HintNameAtom::assembleRawContent(uint16_t hint, - StringRef importName) { - size_t size = - llvm::RoundUpToAlignment(sizeof(hint) + importName.size() + 1, 2); - std::vector<uint8_t> ret(size); - ret[importName.size()] = 0; - ret[importName.size() - 1] = 0; - write16le(&ret[0], hint); - std::memcpy(&ret[2], importName.data(), importName.size()); - return ret; -} - -std::vector<uint8_t> -ImportTableEntryAtom::assembleRawContent(uint64_t rva, bool is64) { - // The element size of the import table is 32 bit in PE and 64 bit - // in PE+. In PE+, bits 62-31 are filled with zero. - if (is64) { - std::vector<uint8_t> ret(8); - write64le(&ret[0], rva); - return ret; - } - std::vector<uint8_t> ret(4); - write32le(&ret[0], rva); - return ret; -} - -static std::vector<ImportTableEntryAtom *> -createImportTableAtoms(IdataContext &context, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms, - bool shouldAddReference, StringRef sectionName, - llvm::BumpPtrAllocator &alloc) { - std::vector<ImportTableEntryAtom *> ret; - for (COFFSharedLibraryAtom *atom : sharedAtoms) { - ImportTableEntryAtom *entry = nullptr; - if (atom->importName().empty()) { - // Import by ordinal - uint64_t hint = atom->hint(); - hint |= context.ctx.is64Bit() ? (uint64_t(1) << 63) : (uint64_t(1) << 31); - entry = new (alloc) ImportTableEntryAtom(context, hint, sectionName); - } else { - // Import by name - entry = new (alloc) ImportTableEntryAtom(context, 0, sectionName); - HintNameAtom *hintName = - new (alloc) HintNameAtom(context, atom->hint(), atom->importName()); - addDir32NBReloc(entry, hintName, context.ctx.getMachineType(), 0); - } - ret.push_back(entry); - if (shouldAddReference) - atom->setImportTableEntry(entry); - } - // Add the NULL entry. - ret.push_back(new (alloc) ImportTableEntryAtom(context, 0, sectionName)); - return ret; -} - -// Creates atoms for an import lookup table. The import lookup table is an -// array of pointers to hint/name atoms. The array needs to be terminated with -// the NULL entry. -void ImportDirectoryAtom::addRelocations( - IdataContext &context, StringRef loadName, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) { - // Create parallel arrays. The contents of the two are initially the - // same. The PE/COFF loader overwrites the import address tables with the - // pointers to the referenced items after loading the executable into - // memory. - std::vector<ImportTableEntryAtom *> importLookupTables = - createImportTableAtoms(context, sharedAtoms, false, ".idata.t", _alloc); - std::vector<ImportTableEntryAtom *> importAddressTables = - createImportTableAtoms(context, sharedAtoms, true, ".idata.a", _alloc); - - addDir32NBReloc(this, importLookupTables[0], context.ctx.getMachineType(), - offsetof(ImportDirectoryTableEntry, ImportLookupTableRVA)); - addDir32NBReloc(this, importAddressTables[0], context.ctx.getMachineType(), - offsetof(ImportDirectoryTableEntry, ImportAddressTableRVA)); - auto *atom = new (_alloc) - COFFStringAtom(context.dummyFile, context.dummyFile.getNextOrdinal(), - ".idata", loadName); - context.file.addAtom(*atom); - addDir32NBReloc(this, atom, context.ctx.getMachineType(), - offsetof(ImportDirectoryTableEntry, NameRVA)); -} - -// Create the contents for the delay-import table. -std::vector<uint8_t> DelayImportDirectoryAtom::createContent() { - std::vector<uint8_t> r(sizeof(delay_import_directory_table_entry), 0); - auto entry = reinterpret_cast<delay_import_directory_table_entry *>(&r[0]); - // link.exe seems to set 1 to Attributes field, so do we. - entry->Attributes = 1; - return r; -} - -// Find "___delayLoadHelper2@8" (or "__delayLoadHelper2" on x64). -// This is not efficient but should be OK for now. -static const Atom * -findDelayLoadHelper(MutableFile &file, const PECOFFLinkingContext &ctx) { - StringRef sym = ctx.getDelayLoadHelperName(); - for (const DefinedAtom *atom : file.defined()) - if (atom->name() == sym) - return atom; - std::string msg = (sym + " was not found").str(); - llvm_unreachable(msg.c_str()); -} - -// Create the data referred by the delay-import table. -void DelayImportDirectoryAtom::addRelocations( - IdataContext &context, StringRef loadName, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) { - // "ModuleHandle" field. This points to an array of pointer-size data - // in ".data" section. Initially the array is initialized with zero. - // The delay-load import helper will set DLL base address at runtime. - auto *hmodule = new (_alloc) DelayImportAddressAtom(context); - addDir32NBReloc(this, hmodule, context.ctx.getMachineType(), - offsetof(delay_import_directory_table_entry, ModuleHandle)); - - // "NameTable" field. The data structure of this field is the same - // as (non-delay) import table's Import Lookup Table. Contains - // imported function names. This is a parallel array of AddressTable - // field. - std::vector<ImportTableEntryAtom *> nameTable = - createImportTableAtoms(context, sharedAtoms, false, ".didat", _alloc); - addDir32NBReloc( - this, nameTable[0], context.ctx.getMachineType(), - offsetof(delay_import_directory_table_entry, DelayImportNameTable)); - - // "Name" field. This points to the NUL-terminated DLL name string. - auto *name = new (_alloc) - COFFStringAtom(context.dummyFile, context.dummyFile.getNextOrdinal(), - ".didat", loadName); - context.file.addAtom(*name); - addDir32NBReloc(this, name, context.ctx.getMachineType(), - offsetof(delay_import_directory_table_entry, Name)); - - // "AddressTable" field. This points to an array of pointers, which - // in turn pointing to delay-load functions. - std::vector<DelayImportAddressAtom *> addrTable; - for (int i = 0, e = sharedAtoms.size() + 1; i < e; ++i) - addrTable.push_back(new (_alloc) DelayImportAddressAtom(context)); - for (int i = 0, e = sharedAtoms.size(); i < e; ++i) - sharedAtoms[i]->setImportTableEntry(addrTable[i]); - addDir32NBReloc( - this, addrTable[0], context.ctx.getMachineType(), - offsetof(delay_import_directory_table_entry, DelayImportAddressTable)); - - const Atom *delayLoadHelper = findDelayLoadHelper(context.file, context.ctx); - for (int i = 0, e = sharedAtoms.size(); i < e; ++i) { - const DefinedAtom *loader = new (_alloc) DelayLoaderAtom( - context, addrTable[i], this, delayLoadHelper); - if (context.ctx.is64Bit()) - addDir64Reloc(addrTable[i], loader, context.ctx.getMachineType(), 0); - else - addDir32Reloc(addrTable[i], loader, context.ctx.getMachineType(), 0); - } -} - -DelayLoaderAtom::DelayLoaderAtom(IdataContext &context, const Atom *impAtom, - const Atom *descAtom, const Atom *delayLoadHelperAtom) - : IdataAtom(context, createContent(context.ctx.getMachineType())) { - MachineTypes machine = context.ctx.getMachineType(); - switch (machine) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - addDir32Reloc(this, impAtom, machine, 3); - addDir32Reloc(this, descAtom, machine, 8); - addRel32Reloc(this, delayLoadHelperAtom, machine, 13); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - addRel32Reloc(this, impAtom, machine, 36); - addRel32Reloc(this, descAtom, machine, 43); - addRel32Reloc(this, delayLoadHelperAtom, machine, 48); - break; - default: - llvm::report_fatal_error("unsupported machine type"); - } -} - -// DelayLoaderAtom contains a wrapper function for __delayLoadHelper2. -// -// __delayLoadHelper2 takes two pointers: a pointer to the delay-load -// table descripter and a pointer to _imp_ symbol for the function -// to be resolved. -// -// __delayLoadHelper2 looks at the table descriptor to know the DLL -// name, calls dlopen()-like function to load it, resolves all -// imported symbols, and then writes the resolved addresses to the -// import address table. It returns a pointer to the resolved -// function. -// -// __delayLoadHelper2 is defined in delayimp.lib. -std::vector<uint8_t> -DelayLoaderAtom::createContent(MachineTypes machine) const { - static const uint8_t x86[] = { - 0x51, // push ecx - 0x52, // push edx - 0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME> - 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll - 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 - 0x5A, // pop edx - 0x59, // pop ecx - 0xFF, 0xE0, // jmp eax - }; - static const uint8_t x64[] = { - 0x51, // push rcx - 0x52, // push rdx - 0x41, 0x50, // push r8 - 0x41, 0x51, // push r9 - 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h - 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 - 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 - 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 - 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 - 0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_<FUNCNAME>] - 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] - 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 - 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] - 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] - 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] - 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] - 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h - 0x41, 0x59, // pop r9 - 0x41, 0x58, // pop r8 - 0x5A, // pop rdx - 0x59, // pop rcx - 0xFF, 0xE0, // jmp rax - }; - switch (machine) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - return std::vector<uint8_t>(x86, x86 + sizeof(x86)); - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - return std::vector<uint8_t>(x64, x64 + sizeof(x64)); - default: - llvm::report_fatal_error("unsupported machine type"); - } -} - -} // namespace idata - -void IdataPass::perform(std::unique_ptr<MutableFile> &file) { - if (file->sharedLibrary().empty()) - return; - - idata::IdataContext context(*file, _dummyFile, _ctx); - std::map<StringRef, std::vector<COFFSharedLibraryAtom *>> sharedAtoms = - groupByLoadName(*file); - bool hasImports = false; - bool hasDelayImports = false; - - // Create the import table and terminate it with the null entry. - for (auto i : sharedAtoms) { - StringRef loadName = i.first; - if (_ctx.isDelayLoadDLL(loadName)) - continue; - hasImports = true; - std::vector<COFFSharedLibraryAtom *> &atoms = i.second; - new (_alloc) idata::ImportDirectoryAtom(context, loadName, atoms); - } - if (hasImports) - new (_alloc) idata::NullImportDirectoryAtom(context); - - // Create the delay import table and terminate it with the null entry. - for (auto i : sharedAtoms) { - StringRef loadName = i.first; - if (!_ctx.isDelayLoadDLL(loadName)) - continue; - hasDelayImports = true; - std::vector<COFFSharedLibraryAtom *> &atoms = i.second; - new (_alloc) idata::DelayImportDirectoryAtom(context, loadName, atoms); - } - if (hasDelayImports) - new (_alloc) idata::DelayNullImportDirectoryAtom(context); - - replaceSharedLibraryAtoms(*file); -} - -std::map<StringRef, std::vector<COFFSharedLibraryAtom *> > -IdataPass::groupByLoadName(MutableFile &file) { - std::map<StringRef, COFFSharedLibraryAtom *> uniqueAtoms; - for (const SharedLibraryAtom *atom : file.sharedLibrary()) - uniqueAtoms[atom->name()] = - (COFFSharedLibraryAtom *)const_cast<SharedLibraryAtom *>(atom); - - std::map<StringRef, std::vector<COFFSharedLibraryAtom *> > ret; - for (auto i : uniqueAtoms) { - COFFSharedLibraryAtom *atom = i.second; - ret[atom->loadName()].push_back(atom); - } - return ret; -} - -/// Transforms a reference to a COFFSharedLibraryAtom to a real reference. -void IdataPass::replaceSharedLibraryAtoms(MutableFile &file) { - for (const DefinedAtom *atom : file.defined()) { - for (const Reference *ref : *atom) { - const Atom *target = ref->target(); - auto *sharedAtom = dyn_cast<SharedLibraryAtom>(target); - if (!sharedAtom) - continue; - const auto *coffSharedAtom = (const COFFSharedLibraryAtom *)sharedAtom; - const DefinedAtom *entry = coffSharedAtom->getImportTableEntry(); - const_cast<Reference *>(ref)->setTarget(entry); - } - } -} - -} // namespace pecoff -} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/IdataPass.h b/lib/ReaderWriter/PECOFF/IdataPass.h deleted file mode 100644 index 9db82160339a..000000000000 --- a/lib/ReaderWriter/PECOFF/IdataPass.h +++ /dev/null @@ -1,218 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/IdataPass.h---------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file \brief This linker pass creates atoms for the DLL import -/// information. The defined atoms constructed in this pass will go into .idata -/// section, unless .idata section is merged with other section such as .data. -/// -/// For the details of the .idata section format, see Microsoft PE/COFF -/// Specification section 5.4, The .idata Section. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_PE_COFF_IDATA_PASS_H -#define LLD_READER_WRITER_PE_COFF_IDATA_PASS_H - -#include "Atoms.h" -#include "lld/Core/File.h" -#include "lld/Core/Pass.h" -#include "lld/Core/Simple.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/Support/COFF.h" -#include <algorithm> -#include <map> - -using llvm::COFF::ImportDirectoryTableEntry; - -namespace lld { -namespace pecoff { -namespace idata { - -class DLLNameAtom; -class HintNameAtom; -class ImportTableEntryAtom; - -// A state object of this pass. -struct IdataContext { - IdataContext(MutableFile &f, VirtualFile &g, const PECOFFLinkingContext &c) - : file(f), dummyFile(g), ctx(c) {} - MutableFile &file; - VirtualFile &dummyFile; - const PECOFFLinkingContext &ctx; -}; - -/// The root class of all idata atoms. -class IdataAtom : public COFFLinkerInternalAtom { -public: - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return ".idata"; } - ContentType contentType() const override { return typeData; } - ContentPermissions permissions() const override { return permR__; } - -protected: - IdataAtom(IdataContext &context, std::vector<uint8_t> data); -}; - -/// A HintNameAtom represents a symbol that will be imported from a DLL at -/// runtime. It consists with an optional hint, which is a small integer, and a -/// symbol name. -/// -/// A hint is an index of the export pointer table in a DLL. If the import -/// library and DLL is in sync (i.e., ".lib" and ".dll" is for the same version -/// or the symbol ordinal is maintained by hand with ".exp" file), the PE/COFF -/// loader can find the symbol quickly. -class HintNameAtom : public IdataAtom { -public: - HintNameAtom(IdataContext &context, uint16_t hint, StringRef importName); - - StringRef getContentString() { return _importName; } - -private: - std::vector<uint8_t> assembleRawContent(uint16_t hint, StringRef importName); - StringRef _importName; -}; - -class ImportTableEntryAtom : public IdataAtom { -public: - ImportTableEntryAtom(IdataContext &ctx, uint64_t contents, - StringRef sectionName) - : IdataAtom(ctx, assembleRawContent(contents, ctx.ctx.is64Bit())), - _sectionName(sectionName) {} - - StringRef customSectionName() const override { - return _sectionName; - }; - -private: - std::vector<uint8_t> assembleRawContent(uint64_t contents, bool is64); - StringRef _sectionName; -}; - -/// An ImportDirectoryAtom includes information to load a DLL, including a DLL -/// name, symbols that will be resolved from the DLL, and the import address -/// table that are overwritten by the loader with the pointers to the referenced -/// items. The executable has one ImportDirectoryAtom per one imported DLL. -class ImportDirectoryAtom : public IdataAtom { -public: - ImportDirectoryAtom(IdataContext &context, StringRef loadName, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) - : IdataAtom(context, std::vector<uint8_t>(20, 0)) { - addRelocations(context, loadName, sharedAtoms); - } - - StringRef customSectionName() const override { return ".idata.d"; } - -private: - void addRelocations(IdataContext &context, StringRef loadName, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms); - - mutable llvm::BumpPtrAllocator _alloc; -}; - -/// The last NULL entry in the import directory. -class NullImportDirectoryAtom : public IdataAtom { -public: - explicit NullImportDirectoryAtom(IdataContext &context) - : IdataAtom(context, std::vector<uint8_t>(20, 0)) {} - - StringRef customSectionName() const override { return ".idata.d"; } -}; - -/// The class for the the delay-load import table. -class DelayImportDirectoryAtom : public IdataAtom { -public: - DelayImportDirectoryAtom( - IdataContext &context, StringRef loadName, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) - : IdataAtom(context, createContent()) { - addRelocations(context, loadName, sharedAtoms); - } - - StringRef customSectionName() const override { return ".didat.d"; } - -private: - std::vector<uint8_t> createContent(); - void addRelocations(IdataContext &context, StringRef loadName, - const std::vector<COFFSharedLibraryAtom *> &sharedAtoms); - - mutable llvm::BumpPtrAllocator _alloc; -}; - -/// Terminator of the delay-load import table. The content of this atom is all -/// zero. -class DelayNullImportDirectoryAtom : public IdataAtom { -public: - explicit DelayNullImportDirectoryAtom(IdataContext &context) - : IdataAtom(context, createContent()) {} - StringRef customSectionName() const override { return ".didat.d"; } - -private: - std::vector<uint8_t> createContent() const { - return std::vector<uint8_t>( - sizeof(llvm::object::delay_import_directory_table_entry), 0); - } -}; - -class DelayImportAddressAtom : public IdataAtom { -public: - explicit DelayImportAddressAtom(IdataContext &context) - : IdataAtom(context, createContent(context.ctx)), - _align(Alignment(context.ctx.is64Bit() ? 3 : 2)) {} - StringRef customSectionName() const override { return ".data"; } - ContentPermissions permissions() const override { return permRW_; } - Alignment alignment() const override { return _align; } - -private: - std::vector<uint8_t> createContent(const PECOFFLinkingContext &ctx) const { - return std::vector<uint8_t>(ctx.is64Bit() ? 8 : 4, 0); - } - - Alignment _align; -}; - -// DelayLoaderAtom contains a wrapper function for __delayLoadHelper2. -class DelayLoaderAtom : public IdataAtom { -public: - DelayLoaderAtom(IdataContext &context, const Atom *impAtom, - const Atom *descAtom, const Atom *delayLoadHelperAtom); - StringRef customSectionName() const override { return ".text"; } - ContentPermissions permissions() const override { return permR_X; } - Alignment alignment() const override { return Alignment(0); } - -private: - std::vector<uint8_t> createContent(MachineTypes machine) const; -}; - -} // namespace idata - -class IdataPass : public lld::Pass { -public: - IdataPass(const PECOFFLinkingContext &ctx) : _dummyFile(ctx), _ctx(ctx) {} - - void perform(std::unique_ptr<MutableFile> &file) override; - -private: - std::map<StringRef, std::vector<COFFSharedLibraryAtom *>> - groupByLoadName(MutableFile &file); - - void replaceSharedLibraryAtoms(MutableFile &file); - - // A dummy file with which all the atoms created in the pass will be - // associated. Atoms need to be associated to an input file even if it's not - // read from a file, so we use this object. - VirtualFile _dummyFile; - - const PECOFFLinkingContext &_ctx; - llvm::BumpPtrAllocator _alloc; -}; - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/InferSubsystemPass.h b/lib/ReaderWriter/PECOFF/InferSubsystemPass.h deleted file mode 100644 index cbf863ee4784..000000000000 --- a/lib/ReaderWriter/PECOFF/InferSubsystemPass.h +++ /dev/null @@ -1,66 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/InferSubsystemPass.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_PE_COFF_INFER_SUBSYSTEM_PASS_H -#define LLD_READER_WRITER_PE_COFF_INFER_SUBSYSTEM_PASS_H - -#include "Atoms.h" -#include "lld/Core/Pass.h" -#include <vector> - -namespace lld { -namespace pecoff { - -// Infers subsystem from entry point function name. -class InferSubsystemPass : public lld::Pass { -public: - InferSubsystemPass(PECOFFLinkingContext &ctx) : _ctx(ctx) {} - - void perform(std::unique_ptr<MutableFile> &file) override { - if (_ctx.getSubsystem() != WindowsSubsystem::IMAGE_SUBSYSTEM_UNKNOWN) - return; - - if (_ctx.isDll()) { - _ctx.setSubsystem(WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI); - return; - } - - // Scan the resolved symbols to infer the subsystem. - const std::string wWinMain = _ctx.decorateSymbol("wWinMainCRTStartup"); - const std::string wWinMainAt = _ctx.decorateSymbol("wWinMainCRTStartup@"); - const std::string winMain = _ctx.decorateSymbol("WinMainCRTStartup"); - const std::string winMainAt = _ctx.decorateSymbol("WinMainCRTStartup@"); - const std::string wmain = _ctx.decorateSymbol("wmainCRTStartup"); - const std::string wmainAt = _ctx.decorateSymbol("wmainCRTStartup@"); - const std::string main = _ctx.decorateSymbol("mainCRTStartup"); - const std::string mainAt = _ctx.decorateSymbol("mainCRTStartup@"); - - for (const DefinedAtom *atom : file->definedAtoms()) { - if (atom->name() == wWinMain || atom->name().startswith(wWinMainAt) || - atom->name() == winMain || atom->name().startswith(winMainAt)) { - _ctx.setSubsystem(WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI); - return; - } - if (atom->name() == wmain || atom->name().startswith(wmainAt) || - atom->name() == main || atom->name().startswith(mainAt)) { - _ctx.setSubsystem(WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_CUI); - return; - } - } - llvm::report_fatal_error("Failed to infer subsystem"); - } - -private: - PECOFFLinkingContext &_ctx; -}; - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp deleted file mode 100644 index a11410784b8c..000000000000 --- a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp --------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "LinkerGeneratedSymbolFile.h" - -namespace lld { -namespace pecoff { - -// Find decorated symbol, namely /sym@[0-9]+/ or /\?sym@@.+/. -bool findDecoratedSymbol(PECOFFLinkingContext *ctx, - std::string sym, std::string &res) { - const std::set<std::string> &defined = ctx->definedSymbols(); - // Search for /sym@[0-9]+/ - { - std::string s = sym + '@'; - auto it = defined.lower_bound(s); - for (auto e = defined.end(); it != e; ++it) { - if (!StringRef(*it).startswith(s)) - break; - if (it->size() == s.size()) - continue; - StringRef suffix = StringRef(*it).substr(s.size()); - if (suffix.find_first_not_of("0123456789") != StringRef::npos) - continue; - res = *it; - return true; - } - } - // Search for /\?sym@@.+/ - { - std::string s = "?" + ctx->undecorateSymbol(sym).str() + "@@"; - auto it = defined.lower_bound(s); - if (it != defined.end() && StringRef(*it).startswith(s)) { - res = *it; - return true; - } - } - return false; -} - -} // namespace pecoff -} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h deleted file mode 100644 index b9764d70bb3b..000000000000 --- a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h +++ /dev/null @@ -1,309 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h ----------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "Atoms.h" -#include "lld/Core/Simple.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/Support/Allocator.h" -#include <algorithm> -#include <mutex> - -using llvm::COFF::WindowsSubsystem; - -namespace lld { -namespace pecoff { - -bool findDecoratedSymbol(PECOFFLinkingContext *ctx, - std::string sym, std::string &res); - -namespace impl { - -/// The defined atom for dllexported symbols with __imp_ prefix. -class ImpPointerAtom : public COFFLinkerInternalAtom { -public: - ImpPointerAtom(const File &file, StringRef symbolName, uint64_t ordinal) - : COFFLinkerInternalAtom(file, /*oridnal*/ 0, std::vector<uint8_t>(4), - symbolName), - _ordinal(ordinal) {} - - uint64_t ordinal() const override { return _ordinal; } - Scope scope() const override { return scopeGlobal; } - ContentType contentType() const override { return typeData; } - Alignment alignment() const override { return Alignment(4); } - ContentPermissions permissions() const override { return permR__; } - -private: - uint64_t _ordinal; -}; - -class ImpSymbolFile : public SimpleFile { -public: - ImpSymbolFile(StringRef defsym, StringRef undefsym, uint64_t ordinal, - bool is64) - : SimpleFile(defsym), _undefined(*this, undefsym), - _defined(*this, defsym, ordinal) { - SimpleReference *ref; - if (is64) { - ref = new SimpleReference(Reference::KindNamespace::COFF, - Reference::KindArch::x86_64, - llvm::COFF::IMAGE_REL_AMD64_ADDR32, - 0, &_undefined, 0); - } else { - ref = new SimpleReference(Reference::KindNamespace::COFF, - Reference::KindArch::x86, - llvm::COFF::IMAGE_REL_I386_DIR32, - 0, &_undefined, 0); - } - _defined.addReference(std::unique_ptr<SimpleReference>(ref)); - addAtom(_defined); - addAtom(_undefined); - }; - -private: - SimpleUndefinedAtom _undefined; - ImpPointerAtom _defined; -}; - -// A file to make Resolver to resolve a symbol TO instead of a symbol FROM, -// using fallback mechanism for an undefined symbol. One can virtually rename an -// undefined symbol using this file. -class SymbolRenameFile : public SimpleFile { -public: - SymbolRenameFile(StringRef from, StringRef to) - : SimpleFile("<symbol-rename>"), _fromSym(from), _toSym(to), - _from(*this, _fromSym, &_to), _to(*this, _toSym) { - addAtom(_from); - }; - -private: - std::string _fromSym; - std::string _toSym; - COFFUndefinedAtom _from; - COFFUndefinedAtom _to; -}; - -} // namespace impl - -// A virtual file containing absolute symbol __ImageBase. __ImageBase (or -// ___ImageBase on x86) is a linker-generated symbol whose address is the same -// as the image base address. -class LinkerGeneratedSymbolFile : public SimpleFile { -public: - LinkerGeneratedSymbolFile(const PECOFFLinkingContext &ctx) - : SimpleFile("<linker-internal-file>"), - _imageBaseAtom(*this, ctx.decorateSymbol("__ImageBase"), - Atom::scopeGlobal, ctx.getBaseAddress()) { - addAtom(_imageBaseAtom); - }; - -private: - SimpleAbsoluteAtom _imageBaseAtom; -}; - -// A LocallyImporteSymbolFile is an archive file containing __imp_ -// symbols for local use. -// -// For each defined symbol, linker creates an implicit defined symbol -// by appending "__imp_" prefix to the original name. The content of -// the implicit symbol is a pointer to the original symbol -// content. This feature allows one to compile and link the following -// code without error, although _imp__hello is not defined in the -// code. (the leading "_" in this example is automatically appended, -// assuming it's x86.) -// -// void hello() { printf("Hello\n"); } -// extern void (*_imp__hello)(); -// int main() { -// _imp__hello(); -// return 0; -// } -// -// This odd feature is for the compatibility with MSVC link.exe. -class LocallyImportedSymbolFile : public SimpleArchiveLibraryFile { -public: - LocallyImportedSymbolFile(const PECOFFLinkingContext &ctx) - : SimpleArchiveLibraryFile("__imp_"), _is64(ctx.is64Bit()), - _ordinal(0) {} - - File *find(StringRef sym, bool dataSymbolOnly) override { - std::string prefix = "__imp_"; - if (!sym.startswith(prefix)) - return nullptr; - StringRef undef = sym.substr(prefix.size()); - return new (_alloc) impl::ImpSymbolFile(sym, undef, _ordinal++, _is64); - } - -private: - bool _is64; - uint64_t _ordinal; - llvm::BumpPtrAllocator _alloc; -}; - -// A ExportedSymbolRenameFile is a virtual archive file for dllexported symbols. -// -// One usually has to specify the exact symbol name to resolve it. That's true -// in most cases for PE/COFF, except the one described below. -// -// DLLExported symbols can be specified using a module definition file. In a -// file, one can write an EXPORT directive followed by symbol names. Such -// symbols may not be fully decorated. -// -// If a symbol FOO is specified to be dllexported by a module definition file, -// linker has to search not only for /FOO/ but also for /FOO@[0-9]+/ for stdcall -// and for /\?FOO@@.+/ for C++. This ambiguous matching semantics does not fit -// well with Resolver. -// -// We could probably modify Resolver to resolve ambiguous symbols, but I think -// we don't want to do that because it'd be rarely used, and only this Windows -// specific feature would use it. It's probably not a good idea to make the core -// linker to be able to deal with it. -// -// So, instead of tweaking Resolver, we chose to do some hack here. An -// ExportedSymbolRenameFile maintains a set containing all possibly defined -// symbol names. That set would be a union of (1) all the defined symbols that -// are already parsed and read and (2) all the defined symbols in archive files -// that are not yet be parsed. -// -// If Resolver asks this file to return an atom for a dllexported symbol, find() -// looks up the set, doing ambiguous matching. If there's a symbol with @ -// prefix, it returns an atom to rename the dllexported symbol, hoping that -// Resolver will find the new symbol with atsign from an archive file at the -// next visit. -class ExportedSymbolRenameFile : public SimpleArchiveLibraryFile { -public: - ExportedSymbolRenameFile(const PECOFFLinkingContext &ctx) - : SimpleArchiveLibraryFile("<export>"), - _ctx(const_cast<PECOFFLinkingContext *>(&ctx)) { - for (PECOFFLinkingContext::ExportDesc &desc : _ctx->getDllExports()) - _exportedSyms.insert(desc.name); - } - - File *find(StringRef sym, bool dataSymbolOnly) override { - typedef PECOFFLinkingContext::ExportDesc ExportDesc; - if (_exportedSyms.count(sym) == 0) - return nullptr; - std::string replace; - if (!findDecoratedSymbol(_ctx, sym.str(), replace)) - return nullptr; - - for (ExportDesc &exp : _ctx->getDllExports()) - if (exp.name == sym) - exp.mangledName = replace; - if (_ctx->deadStrip()) - _ctx->addDeadStripRoot(_ctx->allocate(replace)); - return new (_alloc) impl::SymbolRenameFile(sym, replace); - } - -private: - std::set<std::string> _exportedSyms; - llvm::BumpPtrAllocator _alloc; - PECOFFLinkingContext *_ctx; -}; - -// Windows has not only one but many entry point functions. The -// appropriate one is automatically selected based on the subsystem -// setting and the user-supplied entry point function. -// -// http://msdn.microsoft.com/en-us/library/f9t8842e.aspx -class EntryPointFile : public SimpleFile { -public: - EntryPointFile(const PECOFFLinkingContext &ctx) - : SimpleFile("<entry>"), _ctx(const_cast<PECOFFLinkingContext *>(&ctx)), - _firstTime(true) {} - - const atom_collection<UndefinedAtom> &undefined() const override { - return const_cast<EntryPointFile *>(this)->getUndefinedAtoms(); - } - -private: - const atom_collection<UndefinedAtom> &getUndefinedAtoms() { - std::lock_guard<std::mutex> lock(_mutex); - if (!_firstTime) - return _undefinedAtoms; - _firstTime = false; - - if (_ctx->hasEntry()) { - StringRef entrySym = _ctx->allocate(getEntry()); - _undefinedAtoms._atoms.push_back( - new (_alloc) SimpleUndefinedAtom(*this, entrySym)); - _ctx->setHasEntry(true); - _ctx->setEntrySymbolName(entrySym); - if (_ctx->deadStrip()) - _ctx->addDeadStripRoot(entrySym); - } - return _undefinedAtoms; - } - - // Returns the entry point function name. - std::string getEntry() const { - StringRef opt = _ctx->getEntrySymbolName(); - if (!opt.empty()) { - std::string mangled; - if (findDecoratedSymbol(_ctx, opt, mangled)) - return mangled; - return _ctx->decorateSymbol(opt); - } - return _ctx->decorateSymbol(getDefaultEntry()); - } - - std::string getDefaultEntry() const { - const std::string wWinMainCRTStartup = "wWinMainCRTStartup"; - const std::string WinMainCRTStartup = "WinMainCRTStartup"; - const std::string wmainCRTStartup = "wmainCRTStartup"; - const std::string mainCRTStartup = "mainCRTStartup"; - - if (_ctx->isDll()) { - if (_ctx->getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_I386) - return "_DllMainCRTStartup@12"; - return "_DllMainCRTStartup"; - } - - // Returns true if a given name exists in an input object file. - auto defined = [&](StringRef name) -> bool { - StringRef sym = _ctx->decorateSymbol(name); - if (_ctx->definedSymbols().count(sym)) - return true; - std::string ignore; - return findDecoratedSymbol(_ctx, sym, ignore); - }; - - switch (_ctx->getSubsystem()) { - case WindowsSubsystem::IMAGE_SUBSYSTEM_UNKNOWN: { - if (defined("wWinMain")) - return wWinMainCRTStartup; - if (defined("WinMain")) - return WinMainCRTStartup; - if (defined("wmain")) - return wmainCRTStartup; - if (!defined("main")) - llvm::errs() << "Cannot infer subsystem; assuming /subsystem:console\n"; - return mainCRTStartup; - } - case WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI: - if (defined("WinMain")) - return WinMainCRTStartup; - return wWinMainCRTStartup; - case WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_CUI: - if (defined("wmain")) - return wmainCRTStartup; - return mainCRTStartup; - default: - return mainCRTStartup; - } - } - - PECOFFLinkingContext *_ctx; - atom_collection_vector<UndefinedAtom> _undefinedAtoms; - std::mutex _mutex; - llvm::BumpPtrAllocator _alloc; - bool _firstTime; -}; - -} // end namespace pecoff -} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/LoadConfigPass.cpp b/lib/ReaderWriter/PECOFF/LoadConfigPass.cpp deleted file mode 100644 index be2f5627f4ea..000000000000 --- a/lib/ReaderWriter/PECOFF/LoadConfigPass.cpp +++ /dev/null @@ -1,75 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/LoadConfigPass.cpp -------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// A Load Configuration is a data structure for x86 containing an address of the -// SEH handler table. The Data Directory in the file header points to a load -// configuration. Technically that indirection is not needed but exists for -// historical reasons. -// -// If the file being handled has .sxdata section containing SEH handler table, -// this pass will create a Load Configuration atom. -// -//===----------------------------------------------------------------------===// - -#include "Pass.h" -#include "LoadConfigPass.h" -#include "lld/Core/File.h" -#include "lld/Core/Pass.h" -#include "lld/Core/Simple.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Path.h" -#include <climits> -#include <ctime> -#include <utility> - -using llvm::object::coff_load_configuration32; - -namespace lld { -namespace pecoff { -namespace loadcfg { - -LoadConfigAtom::LoadConfigAtom(VirtualFile &file, const DefinedAtom *sxdata, - int count) - : COFFLinkerInternalAtom( - file, file.getNextOrdinal(), - std::vector<uint8_t>(sizeof(coff_load_configuration32))) { - addDir32Reloc( - this, sxdata, llvm::COFF::IMAGE_FILE_MACHINE_I386, - offsetof(llvm::object::coff_load_configuration32, SEHandlerTable)); - auto *data = getContents<llvm::object::coff_load_configuration32>(); - data->SEHandlerCount = count; -} - -} // namespace loadcfg - -void LoadConfigPass::perform(std::unique_ptr<MutableFile> &file) { - if (_ctx.noSEH()) - return; - - // Find the first atom in .sxdata section. - const DefinedAtom *sxdata = nullptr; - int sectionSize = 0; - for (const DefinedAtom *atom : file->defined()) { - if (atom->customSectionName() == ".sxdata") { - if (!sxdata) - sxdata = atom; - sectionSize += sxdata->size(); - } - } - if (!sxdata) - return; - - auto *loadcfg = new (_alloc) - loadcfg::LoadConfigAtom(_file, sxdata, sectionSize / sizeof(uint32_t)); - file->addAtom(*loadcfg); -} - -} // namespace pecoff -} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/LoadConfigPass.h b/lib/ReaderWriter/PECOFF/LoadConfigPass.h deleted file mode 100644 index 9f4a25f2b10e..000000000000 --- a/lib/ReaderWriter/PECOFF/LoadConfigPass.h +++ /dev/null @@ -1,63 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/LoadConfigPass.h ---------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file \brief This linker pass creates an atom for Load Configuration -/// structure. -/// -/// For the details of the Load Configuration structure, see Microsoft PE/COFF -/// Specification section 5.8. The Load Configuration Structure (Image Only). -/// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_PE_COFF_LOAD_CONFIG_PASS_H -#define LLD_READER_WRITER_PE_COFF_LOAD_CONFIG_PASS_H - -#include "Atoms.h" -#include "lld/Core/File.h" -#include "lld/Core/Pass.h" -#include "lld/Core/Simple.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include <map> - -namespace lld { -namespace pecoff { -namespace loadcfg { - -class LoadConfigAtom : public COFFLinkerInternalAtom { -public: - LoadConfigAtom(VirtualFile &file, const DefinedAtom *sxdata, int count); - - SectionChoice sectionChoice() const override { return sectionCustomRequired; } - StringRef customSectionName() const override { return ".loadcfg"; } - ContentType contentType() const override { return typeData; } - ContentPermissions permissions() const override { return permR__; } - - template <typename T> T *getContents() const { - return (T *)const_cast<uint8_t *>(rawContent().data()); - } -}; - -} // namespace loadcfg - -class LoadConfigPass : public lld::Pass { -public: - LoadConfigPass(PECOFFLinkingContext &ctx) : _ctx(ctx), _file(ctx) {} - - void perform(std::unique_ptr<MutableFile> &file) override; - -private: - PECOFFLinkingContext &_ctx; - VirtualFile _file; - mutable llvm::BumpPtrAllocator _alloc; -}; - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/Makefile b/lib/ReaderWriter/PECOFF/Makefile deleted file mode 100644 index 3ad16969bba7..000000000000 --- a/lib/ReaderWriter/PECOFF/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lld/lib/ReaderWriter/PECOFF/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 := lldPECOFF -USEDLIBS = lldCore.a - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/PECOFF/OrderPass.h b/lib/ReaderWriter/PECOFF/OrderPass.h deleted file mode 100644 index 73133ff73638..000000000000 --- a/lib/ReaderWriter/PECOFF/OrderPass.h +++ /dev/null @@ -1,67 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/OrderPass.h -------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file \brief This pass sorts atoms by section name, so that they will appear -/// in the correct order in the output. -/// -/// In COFF, sections will be merged into one section by the linker if their -/// names are the same after discarding the "$" character and all characters -/// follow it from their names. The characters following the "$" character -/// determines the merge order. Assume there's an object file containing four -/// data sections in the following order. -/// -/// - .data$2 -/// - .data$3 -/// - .data$1 -/// - .data -/// -/// In this case, the resulting binary should have ".data" section with the -/// contents of ".data", ".data$1", ".data$2" and ".data$3" in that order. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLD_READER_WRITER_PE_COFF_ORDER_PASS_H -#define LLD_READER_WRITER_PE_COFF_ORDER_PASS_H - -#include "Atoms.h" -#include "lld/Core/Parallel.h" -#include "lld/Core/Pass.h" -#include <algorithm> - -namespace lld { -namespace pecoff { - -static bool compare(const DefinedAtom *lhs, const DefinedAtom *rhs) { - bool lhsCustom = (lhs->sectionChoice() == DefinedAtom::sectionCustomRequired); - bool rhsCustom = (rhs->sectionChoice() == DefinedAtom::sectionCustomRequired); - if (lhsCustom && rhsCustom) { - int cmp = lhs->customSectionName().compare(rhs->customSectionName()); - if (cmp != 0) - return cmp < 0; - return DefinedAtom::compareByPosition(lhs, rhs); - } - if (lhsCustom && !rhsCustom) - return true; - if (!lhsCustom && rhsCustom) - return false; - return DefinedAtom::compareByPosition(lhs, rhs); -} - -class OrderPass : public lld::Pass { -public: - void perform(std::unique_ptr<MutableFile> &file) override { - MutableFile::DefinedAtomRange defined = file->definedAtoms(); - parallel_sort(defined.begin(), defined.end(), compare); - } -}; - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/PDBPass.h b/lib/ReaderWriter/PECOFF/PDBPass.h deleted file mode 100644 index 0efa054db823..000000000000 --- a/lib/ReaderWriter/PECOFF/PDBPass.h +++ /dev/null @@ -1,43 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/PDBPass.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_PE_COFF_PDB_PASS_H -#define LLD_READER_WRITER_PE_COFF_PDB_PASS_H - -#include "lld/Core/Pass.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Process.h" - -namespace lld { -namespace pecoff { - -class PDBPass : public lld::Pass { -public: - PDBPass(PECOFFLinkingContext &ctx) : _ctx(ctx) {} - - void perform(std::unique_ptr<MutableFile> &file) override { - if (_ctx.getDebug()) - touch(_ctx.getPDBFilePath()); - } - -private: - void touch(StringRef path) { - int fd; - if (llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Append)) - llvm::report_fatal_error("failed to create a PDB file"); - llvm::sys::Process::SafelyCloseFileDescriptor(fd); - } - - PECOFFLinkingContext &_ctx; -}; - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp b/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp deleted file mode 100644 index 6a657e33541d..000000000000 --- a/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp +++ /dev/null @@ -1,352 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/PECOFFLinkingContext.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 "EdataPass.h" -#include "IdataPass.h" -#include "InferSubsystemPass.h" -#include "LinkerGeneratedSymbolFile.h" -#include "LoadConfigPass.h" -#include "OrderPass.h" -#include "PDBPass.h" -#include "lld/Core/PassManager.h" -#include "lld/Core/Reader.h" -#include "lld/Core/Simple.h" -#include "lld/Core/Writer.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Path.h" -#include <bitset> -#include <climits> -#include <set> - -namespace lld { - -bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) { - if (_stackReserve < _stackCommit) { - diagnostics << "Invalid stack size: reserve size must be equal to or " - << "greater than commit size, but got " << _stackCommit - << " and " << _stackReserve << ".\n"; - return false; - } - - if (_heapReserve < _heapCommit) { - diagnostics << "Invalid heap size: reserve size must be equal to or " - << "greater than commit size, but got " << _heapCommit - << " and " << _heapReserve << ".\n"; - return false; - } - - // It's an error if the base address is not multiple of 64K. - if (getBaseAddress() & 0xffff) { - diagnostics << "Base address have to be multiple of 64K, but got " - << getBaseAddress() << "\n"; - return false; - } - - // Specifing /noentry without /dll is an error. - if (!hasEntry() && !isDll()) { - diagnostics << "/noentry must be specified with /dll\n"; - return false; - } - - // Check for duplicate export ordinals. - std::set<int> exports; - for (const PECOFFLinkingContext::ExportDesc &desc : getDllExports()) { - if (desc.ordinal == -1) - continue; - if (exports.count(desc.ordinal) == 1) { - diagnostics << "Duplicate export ordinals: " << desc.ordinal << "\n"; - return false; - } - exports.insert(desc.ordinal); - } - - // Check for /align. - std::bitset<64> alignment(_sectionDefaultAlignment); - if (alignment.count() != 1) { - diagnostics << "Section alignment must be a power of 2, but got " - << _sectionDefaultAlignment << "\n"; - return false; - } - - _writer = createWriterPECOFF(*this); - return true; -} - -const std::set<std::string> &PECOFFLinkingContext::definedSymbols() { - std::lock_guard<std::recursive_mutex> lock(_mutex); - for (std::unique_ptr<Node> &node : getNodes()) { - if (_seen.count(node.get()) > 0) - continue; - FileNode *fnode = dyn_cast<FileNode>(node.get()); - if (!fnode) - continue; - File *file = fnode->getFile(); - if (file->parse()) - continue; - if (auto *archive = dyn_cast<ArchiveLibraryFile>(file)) { - for (const std::string &sym : archive->getDefinedSymbols()) - _definedSyms.insert(sym); - continue; - } - for (const DefinedAtom *atom : file->defined()) - if (!atom->name().empty()) - _definedSyms.insert(atom->name()); - } - return _definedSyms; -} - -std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const { - return LinkingContext::createEntrySymbolFile("<command line option /entry>"); -} - -std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const { - return LinkingContext::createUndefinedSymbolFile( - "<command line option /include>"); -} - -static int getGroupStartPos(std::vector<std::unique_ptr<Node>> &nodes) { - for (int i = 0, e = nodes.size(); i < e; ++i) - if (GroupEnd *group = dyn_cast<GroupEnd>(nodes[i].get())) - return i - group->getSize(); - llvm::report_fatal_error("internal error"); -} - -void PECOFFLinkingContext::addLibraryFile(std::unique_ptr<FileNode> file) { - GroupEnd *currentGroupEnd; - int pos = -1; - std::vector<std::unique_ptr<Node>> &elements = getNodes(); - for (int i = 0, e = elements.size(); i < e; ++i) { - if ((currentGroupEnd = dyn_cast<GroupEnd>(elements[i].get()))) { - pos = i; - break; - } - } - assert(pos >= 0); - elements.insert(elements.begin() + pos, std::move(file)); - elements[pos + 1] = llvm::make_unique<GroupEnd>( - currentGroupEnd->getSize() + 1); -} - -bool PECOFFLinkingContext::createImplicitFiles( - std::vector<std::unique_ptr<File>> &) { - std::vector<std::unique_ptr<Node>> &members = getNodes(); - - // Create a file for the entry point function. - std::unique_ptr<FileNode> entry(new FileNode( - llvm::make_unique<pecoff::EntryPointFile>(*this))); - members.insert(members.begin() + getGroupStartPos(members), std::move(entry)); - - // Create a file for __ImageBase. - std::unique_ptr<FileNode> fileNode(new FileNode( - llvm::make_unique<pecoff::LinkerGeneratedSymbolFile>(*this))); - members.push_back(std::move(fileNode)); - - // Create a file for _imp_ symbols. - std::unique_ptr<FileNode> impFileNode(new FileNode( - llvm::make_unique<pecoff::LocallyImportedSymbolFile>(*this))); - members.push_back(std::move(impFileNode)); - - // Create a file for dllexported symbols. - std::unique_ptr<FileNode> exportNode(new FileNode( - llvm::make_unique<pecoff::ExportedSymbolRenameFile>(*this))); - addLibraryFile(std::move(exportNode)); - - return true; -} - -/// Returns the section name in the resulting executable. -/// -/// Sections in object files are usually output to the executable with the same -/// name, but you can rename by command line option. /merge:from=to makes the -/// linker to combine "from" section contents to "to" section in the -/// executable. We have a mapping for the renaming. This method looks up the -/// table and returns a new section name if renamed. -StringRef -PECOFFLinkingContext::getOutputSectionName(StringRef sectionName) const { - auto it = _renamedSections.find(sectionName); - if (it == _renamedSections.end()) - return sectionName; - return getOutputSectionName(it->second); -} - -/// Adds a mapping to the section renaming table. This method will be used for -/// /merge command line option. -bool PECOFFLinkingContext::addSectionRenaming(raw_ostream &diagnostics, - StringRef from, StringRef to) { - auto it = _renamedSections.find(from); - if (it != _renamedSections.end()) { - if (it->second == to) - // There's already the same mapping. - return true; - diagnostics << "Section \"" << from << "\" is already mapped to \"" - << it->second << ", so it cannot be mapped to \"" << to << "\"."; - return true; - } - - // Add a mapping, and check if there's no cycle in the renaming mapping. The - // cycle detection algorithm we use here is naive, but that's OK because the - // number of mapping is usually less than 10. - _renamedSections[from] = to; - for (auto elem : _renamedSections) { - StringRef sectionName = elem.first; - std::set<StringRef> visited; - visited.insert(sectionName); - for (;;) { - auto pos = _renamedSections.find(sectionName); - if (pos == _renamedSections.end()) - break; - if (visited.count(pos->second)) { - diagnostics << "/merge:" << from << "=" << to << " makes a cycle"; - return false; - } - sectionName = pos->second; - visited.insert(sectionName); - } - } - return true; -} - -/// Try to find the input library file from the search paths and append it to -/// the input file list. Returns true if the library file is found. -StringRef PECOFFLinkingContext::searchLibraryFile(StringRef filename) const { - // Current directory always takes precedence over the search paths. - if (llvm::sys::path::is_absolute(filename) || llvm::sys::fs::exists(filename)) - return filename; - // Iterate over the search paths. - for (StringRef dir : _inputSearchPaths) { - SmallString<128> path = dir; - llvm::sys::path::append(path, filename); - if (llvm::sys::fs::exists(path.str())) - return allocate(path.str()); - } - return filename; -} - -/// Returns the decorated name of the given symbol name. On 32-bit x86, it -/// adds "_" at the beginning of the string. On other architectures, the -/// return value is the same as the argument. -StringRef PECOFFLinkingContext::decorateSymbol(StringRef name) const { - if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) - return name; - std::string str = "_"; - str.append(name); - return allocate(str); -} - -StringRef PECOFFLinkingContext::undecorateSymbol(StringRef name) const { - if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) - return name; - if (!name.startswith("_")) - return name; - return name.substr(1); -} - -uint64_t PECOFFLinkingContext::getBaseAddress() const { - if (_baseAddress == invalidBaseAddress) - return is64Bit() ? pe32PlusDefaultBaseAddress : pe32DefaultBaseAddress; - return _baseAddress; -} - -Writer &PECOFFLinkingContext::writer() const { return *_writer; } - -void PECOFFLinkingContext::setSectionSetMask(StringRef sectionName, - uint32_t newFlags) { - _sectionSetMask[sectionName] |= newFlags; - _sectionClearMask[sectionName] &= ~newFlags; - const uint32_t rwx = (llvm::COFF::IMAGE_SCN_MEM_READ | - llvm::COFF::IMAGE_SCN_MEM_WRITE | - llvm::COFF::IMAGE_SCN_MEM_EXECUTE); - if (newFlags & rwx) - _sectionClearMask[sectionName] |= ~_sectionSetMask[sectionName] & rwx; - assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0); -} - -void PECOFFLinkingContext::setSectionClearMask(StringRef sectionName, - uint32_t newFlags) { - _sectionClearMask[sectionName] |= newFlags; - _sectionSetMask[sectionName] &= ~newFlags; - assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0); -} - -uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName, - uint32_t flags) const { - auto si = _sectionSetMask.find(sectionName); - uint32_t setMask = (si == _sectionSetMask.end()) ? 0 : si->second; - auto ci = _sectionClearMask.find(sectionName); - uint32_t clearMask = (ci == _sectionClearMask.end()) ? 0 : ci->second; - return (flags | setMask) & ~clearMask; -} - -// Returns true if two export descriptors are the same. -static bool sameExportDesc(const PECOFFLinkingContext::ExportDesc &a, - const PECOFFLinkingContext::ExportDesc &b) { - return a.ordinal == b.ordinal && a.ordinal == b.ordinal && - a.noname == b.noname && a.isData == b.isData; -} - -void PECOFFLinkingContext::addDllExport(ExportDesc &desc) { - addInitialUndefinedSymbol(allocate(desc.name)); - - // MSVC link.exe silently drops characters after the first atsign. - // For example, /export:foo@4=bar is equivalent to /export:foo=bar. - // We do the same thing for compatibility. - if (!desc.externalName.empty()) { - StringRef s(desc.externalName); - size_t pos = s.find('@'); - if (pos != s.npos) - desc.externalName = s.substr(0, pos); - } - - // Scan the vector to look for existing entry. It's not very fast, - // but because the number of exported symbol is usually not that - // much, it should be okay. - for (ExportDesc &e : _dllExports) { - if (e.name != desc.name) - continue; - if (!sameExportDesc(e, desc)) - llvm::errs() << "Export symbol '" << desc.name - << "' specified more than once.\n"; - return; - } - _dllExports.push_back(desc); -} - -static std::string replaceExtension(StringRef path, StringRef ext) { - SmallString<128> ss = path; - llvm::sys::path::replace_extension(ss, ext); - return ss.str(); -} - -std::string PECOFFLinkingContext::getOutputImportLibraryPath() const { - if (!_implib.empty()) - return _implib; - return replaceExtension(outputPath(), ".lib"); -} - -std::string PECOFFLinkingContext::getPDBFilePath() const { - assert(_debug); - if (!_pdbFilePath.empty()) - return _pdbFilePath; - return replaceExtension(outputPath(), ".pdb"); -} - -void PECOFFLinkingContext::addPasses(PassManager &pm) { - pm.add(llvm::make_unique<pecoff::PDBPass>(*this)); - pm.add(llvm::make_unique<pecoff::EdataPass>(*this)); - pm.add(llvm::make_unique<pecoff::IdataPass>(*this)); - pm.add(llvm::make_unique<pecoff::OrderPass>()); - pm.add(llvm::make_unique<pecoff::LoadConfigPass>(*this)); - pm.add(llvm::make_unique<pecoff::InferSubsystemPass>(*this)); -} - -} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/Pass.cpp b/lib/ReaderWriter/PECOFF/Pass.cpp deleted file mode 100644 index ed731984e378..000000000000 --- a/lib/ReaderWriter/PECOFF/Pass.cpp +++ /dev/null @@ -1,95 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/Pass.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 "Pass.h" -#include "lld/Core/File.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Support/COFF.h" - -namespace lld { -namespace pecoff { - -static void addReloc(COFFBaseDefinedAtom *atom, const Atom *target, - size_t offsetInAtom, Reference::KindArch arch, - Reference::KindValue relType) { - atom->addReference(llvm::make_unique<SimpleReference>( - Reference::KindNamespace::COFF, arch, relType, offsetInAtom, target, 0)); -} - -void addDir64Reloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom) { - switch (machine) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, - llvm::COFF::IMAGE_REL_I386_DIR32); - return; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, - llvm::COFF::IMAGE_REL_AMD64_ADDR64); - return; - default: - llvm_unreachable("unsupported machine type"); - } -} - -void addDir32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom) { - switch (machine) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, - llvm::COFF::IMAGE_REL_I386_DIR32); - return; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, - llvm::COFF::IMAGE_REL_AMD64_ADDR32); - return; - default: - llvm_unreachable("unsupported machine type"); - } -} - -void addDir32NBReloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom) { - switch (machine) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, - llvm::COFF::IMAGE_REL_I386_DIR32NB); - return; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, - llvm::COFF::IMAGE_REL_AMD64_ADDR32NB); - return; - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - addReloc(atom, target, offsetInAtom, Reference::KindArch::ARM, - llvm::COFF::IMAGE_REL_ARM_ADDR32NB); - return; - default: - llvm_unreachable("unsupported machine type"); - } -} - -void addRel32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom) { - switch (machine) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, - llvm::COFF::IMAGE_REL_I386_REL32); - return; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, - llvm::COFF::IMAGE_REL_AMD64_REL32); - return; - default: - llvm_unreachable("unsupported machine type"); - } -} - -} // end namespace pecoff -} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/Pass.h b/lib/ReaderWriter/PECOFF/Pass.h deleted file mode 100644 index 22466f77859e..000000000000 --- a/lib/ReaderWriter/PECOFF/Pass.h +++ /dev/null @@ -1,34 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/Pass.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_PE_COFF_PASS_H -#define LLD_READER_WRITER_PE_COFF_PASS_H - -#include "Atoms.h" -#include "llvm/Support/COFF.h" - -namespace lld { -namespace pecoff { - -void addDir64Reloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom); - -void addDir32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom); - -void addDir32NBReloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom); - -void addRel32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, - llvm::COFF::MachineTypes machine, size_t offsetInAtom); - -} // namespace pecoff -} // namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp b/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp deleted file mode 100644 index f060bd8dc0bc..000000000000 --- a/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp +++ /dev/null @@ -1,1140 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/ReaderCOFF.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 "lld/Core/Alias.h" -#include "lld/Core/File.h" -#include "lld/Core/Reader.h" -#include "lld/Driver/Driver.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/FileUtilities.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <map> -#include <mutex> -#include <set> -#include <system_error> -#include <vector> - -#define DEBUG_TYPE "ReaderCOFF" - -using lld::pecoff::COFFBSSAtom; -using lld::pecoff::COFFDefinedAtom; -using lld::pecoff::COFFDefinedFileAtom; -using lld::pecoff::COFFUndefinedAtom; -using llvm::object::coff_aux_section_definition; -using llvm::object::coff_aux_weak_external; -using llvm::object::coff_relocation; -using llvm::object::coff_section; -using llvm::object::coff_symbol; -using llvm::support::ulittle32_t; - -using namespace lld; - -namespace { - -class BumpPtrStringSaver : public llvm::cl::StringSaver { -public: - const char *SaveString(const char *str) override { - size_t len = strlen(str); - std::lock_guard<std::mutex> lock(_allocMutex); - char *copy = _alloc.Allocate<char>(len + 1); - memcpy(copy, str, len + 1); - return copy; - } - -private: - llvm::BumpPtrAllocator _alloc; - std::mutex _allocMutex; -}; - -class FileCOFF : public File { -private: - typedef std::vector<llvm::object::COFFSymbolRef> SymbolVectorT; - typedef std::map<const coff_section *, SymbolVectorT> SectionToSymbolsT; - -public: - FileCOFF(std::unique_ptr<MemoryBuffer> mb, PECOFFLinkingContext &ctx) - : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), - _compatibleWithSEH(false), _ordinal(1), - _machineType(llvm::COFF::MT_Invalid), _ctx(ctx) {} - - std::error_code doParse() override; - bool isCompatibleWithSEH() const { return _compatibleWithSEH; } - llvm::COFF::MachineTypes getMachineType() { return _machineType; } - - 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; - } - - void beforeLink() override; - - void addUndefinedSymbol(StringRef sym) { - _undefinedAtoms._atoms.push_back(new (_alloc) COFFUndefinedAtom(*this, sym)); - } - - AliasAtom *createAlias(StringRef name, const DefinedAtom *target, int cnt); - void createAlternateNameAtoms(); - std::error_code parseDirectiveSection(StringRef directives); - - mutable llvm::BumpPtrAllocator _alloc; - -private: - std::error_code readSymbolTable(SymbolVectorT &result); - - void createAbsoluteAtoms(const SymbolVectorT &symbols, - std::vector<const AbsoluteAtom *> &result); - - std::error_code - createUndefinedAtoms(const SymbolVectorT &symbols, - std::vector<const UndefinedAtom *> &result); - - std::error_code - createDefinedSymbols(const SymbolVectorT &symbols, - std::vector<const DefinedAtom *> &result); - - std::error_code cacheSectionAttributes(); - std::error_code maybeCreateSXDataAtoms(); - - std::error_code - AtomizeDefinedSymbolsInSection(const coff_section *section, - SymbolVectorT &symbols, - std::vector<COFFDefinedFileAtom *> &atoms); - - std::error_code - AtomizeDefinedSymbols(SectionToSymbolsT &definedSymbols, - std::vector<const DefinedAtom *> &definedAtoms); - - std::error_code findAtomAt(const coff_section *section, - uint32_t targetAddress, - COFFDefinedFileAtom *&result, - uint32_t &offsetInAtom); - - std::error_code getAtomBySymbolIndex(uint32_t index, Atom *&ret); - - std::error_code - addRelocationReference(const coff_relocation *rel, - const coff_section *section); - - std::error_code getSectionContents(StringRef sectionName, - ArrayRef<uint8_t> &result); - std::error_code getReferenceArch(Reference::KindArch &result); - std::error_code addRelocationReferenceToAtoms(); - std::error_code findSection(StringRef name, const coff_section *&result); - StringRef ArrayRefToString(ArrayRef<uint8_t> array); - uint64_t getNextOrdinal(); - - std::unique_ptr<const llvm::object::COFFObjectFile> _obj; - std::unique_ptr<MemoryBuffer> _mb; - atom_collection_vector<DefinedAtom> _definedAtoms; - atom_collection_vector<UndefinedAtom> _undefinedAtoms; - atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; - atom_collection_vector<AbsoluteAtom> _absoluteAtoms; - - // The target type of the object. - Reference::KindArch _referenceArch; - - // True if the object has "@feat.00" symbol. - bool _compatibleWithSEH; - - // A map from symbol to its name. All symbols should be in this map except - // unnamed ones. - std::map<llvm::object::COFFSymbolRef, StringRef> _symbolName; - - // A map from symbol to its resultant atom. - std::map<llvm::object::COFFSymbolRef, Atom *> _symbolAtom; - - // A map from symbol to its aux symbol. - std::map<llvm::object::COFFSymbolRef, llvm::object::COFFSymbolRef> _auxSymbol; - - // A map from section to its atoms. - std::map<const coff_section *, std::vector<COFFDefinedFileAtom *>> - _sectionAtoms; - - // A set of COMDAT sections. - std::set<const coff_section *> _comdatSections; - - // A map to get whether the section allows its contents to be merged or not. - std::map<const coff_section *, DefinedAtom::Merge> _merge; - - // COMDAT associative sections - std::multimap<const coff_section *, const coff_section *> _association; - - // A sorted map to find an atom from a section and an offset within - // the section. - std::map<const coff_section *, std::multimap<uint32_t, COFFDefinedAtom *>> - _definedAtomLocations; - - uint64_t _ordinal; - llvm::COFF::MachineTypes _machineType; - PECOFFLinkingContext &_ctx; - mutable BumpPtrStringSaver _stringSaver; -}; - -// Converts the COFF symbol attribute to the LLD's atom attribute. -Atom::Scope getScope(llvm::object::COFFSymbolRef symbol) { - switch (symbol.getStorageClass()) { - case llvm::COFF::IMAGE_SYM_CLASS_EXTERNAL: - return Atom::scopeGlobal; - case llvm::COFF::IMAGE_SYM_CLASS_STATIC: - case llvm::COFF::IMAGE_SYM_CLASS_LABEL: - return Atom::scopeTranslationUnit; - } - llvm_unreachable("Unknown scope"); -} - -DefinedAtom::ContentType getContentType(const coff_section *section) { - if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_CODE) - return DefinedAtom::typeCode; - if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) - return DefinedAtom::typeData; - if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) - return DefinedAtom::typeZeroFill; - return DefinedAtom::typeUnknown; -} - -DefinedAtom::ContentPermissions getPermissions(const coff_section *section) { - if (section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_READ && - section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_WRITE) - return DefinedAtom::permRW_; - if (section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_READ && - section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) - return DefinedAtom::permR_X; - if (section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_READ) - return DefinedAtom::permR__; - return DefinedAtom::perm___; -} - -/// Returns the alignment of the section. The contents of the section must be -/// aligned by this value in the resulting executable/DLL. -DefinedAtom::Alignment getAlignment(const coff_section *section) { - if (section->Characteristics & llvm::COFF::IMAGE_SCN_TYPE_NO_PAD) - return DefinedAtom::Alignment(0); - - // Bit [20:24] contains section alignment information. We need to decrease - // the value stored by 1 in order to get the real exponent (e.g, ALIGN_1BYTE - // is 0x00100000, but the exponent should be 0) - uint32_t characteristics = (section->Characteristics >> 20) & 0xf; - - // If all bits are off, we treat it as if ALIGN_1BYTE was on. The PE/COFF spec - // does not say anything about this case, but CVTRES.EXE does not set any bit - // in characteristics[20:24], and its output is intended to be copied to .rsrc - // section with no padding, so I think doing this is the right thing. - if (characteristics == 0) - return DefinedAtom::Alignment(0); - - uint32_t powerOf2 = characteristics - 1; - return DefinedAtom::Alignment(powerOf2); -} - -DefinedAtom::Merge getMerge(const coff_aux_section_definition *auxsym) { - switch (auxsym->Selection) { - case llvm::COFF::IMAGE_COMDAT_SELECT_NODUPLICATES: - return DefinedAtom::mergeNo; - case llvm::COFF::IMAGE_COMDAT_SELECT_ANY: - return DefinedAtom::mergeAsWeakAndAddressUsed; - case llvm::COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH: - // TODO: This mapping is wrong. Fix it. - return DefinedAtom::mergeByContent; - case llvm::COFF::IMAGE_COMDAT_SELECT_SAME_SIZE: - return DefinedAtom::mergeSameNameAndSize; - case llvm::COFF::IMAGE_COMDAT_SELECT_LARGEST: - return DefinedAtom::mergeByLargestSection; - case llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE: - case llvm::COFF::IMAGE_COMDAT_SELECT_NEWEST: - // FIXME: These attributes has more complicated semantics than the regular - // weak symbol. These are mapped to mergeAsWeakAndAddressUsed for now - // because the core linker does not support them yet. We eventually have - // to implement them for full COFF support. - return DefinedAtom::mergeAsWeakAndAddressUsed; - default: - llvm_unreachable("Unknown merge type"); - } -} - -StringRef getMachineName(llvm::COFF::MachineTypes Type) { - switch (Type) { - default: llvm_unreachable("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - return "ARM"; - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - return "X86"; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - return "X64"; - } -} - -std::error_code FileCOFF::doParse() { - auto binaryOrErr = llvm::object::createBinary(_mb->getMemBufferRef()); - if (std::error_code ec = binaryOrErr.getError()) - return ec; - std::unique_ptr<llvm::object::Binary> bin = std::move(binaryOrErr.get()); - - _obj.reset(dyn_cast<const llvm::object::COFFObjectFile>(bin.get())); - if (!_obj) - return make_error_code(llvm::object::object_error::invalid_file_type); - bin.release(); - - _machineType = static_cast<llvm::COFF::MachineTypes>(_obj->getMachine()); - - if (getMachineType() != llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN && - getMachineType() != _ctx.getMachineType()) { - llvm::errs() << "module machine type '" - << getMachineName(getMachineType()) - << "' conflicts with target machine type '" - << getMachineName(_ctx.getMachineType()) << "'\n"; - return NativeReaderError::conflicting_target_machine; - } - - if (std::error_code ec = getReferenceArch(_referenceArch)) - return ec; - - // Read the symbol table and atomize them if possible. Defined atoms - // cannot be atomized in one pass, so they will be not be atomized but - // added to symbolToAtom. - SymbolVectorT symbols; - if (std::error_code ec = readSymbolTable(symbols)) - return ec; - - createAbsoluteAtoms(symbols, _absoluteAtoms._atoms); - if (std::error_code ec = - createUndefinedAtoms(symbols, _undefinedAtoms._atoms)) - return ec; - if (std::error_code ec = createDefinedSymbols(symbols, _definedAtoms._atoms)) - return ec; - if (std::error_code ec = addRelocationReferenceToAtoms()) - return ec; - if (std::error_code ec = maybeCreateSXDataAtoms()) - return ec; - - // Check for /SAFESEH. - if (_ctx.requireSEH() && !isCompatibleWithSEH()) { - llvm::errs() << "/SAFESEH is specified, but " - << _mb->getBufferIdentifier() - << " is not compatible with SEH.\n"; - return llvm::object::object_error::parse_failed; - } - return std::error_code(); -} - -void FileCOFF::beforeLink() { - // Acquire the mutex to mutate _ctx. - std::lock_guard<std::recursive_mutex> lock(_ctx.getMutex()); - std::set<StringRef> undefSyms; - - // Interpret .drectve section if the section has contents. - ArrayRef<uint8_t> directives; - if (getSectionContents(".drectve", directives)) - return; - if (!directives.empty()) { - std::set<StringRef> orig; - for (StringRef sym : _ctx.initialUndefinedSymbols()) - orig.insert(sym); - if (parseDirectiveSection(ArrayRefToString(directives))) - return; - for (StringRef sym : _ctx.initialUndefinedSymbols()) - if (orig.count(sym) == 0) - undefSyms.insert(sym); - } - - // Add /INCLUDE'ed symbols to the file as if they existed in the - // file as undefined symbols. - for (StringRef sym : undefSyms) { - addUndefinedSymbol(sym); - _ctx.addDeadStripRoot(sym); - } - - // One can define alias symbols using /alternatename:<sym>=<sym> option. - // The mapping for /alternatename is in the context object. This helper - // function iterate over defined atoms and create alias atoms if needed. - createAlternateNameAtoms(); - - // In order to emit SEH table, all input files need to be compatible with - // SEH. Disable SEH if the file being read is not compatible. - if (!isCompatibleWithSEH()) - _ctx.setSafeSEH(false); -} - -/// Iterate over the symbol table to retrieve all symbols. -std::error_code -FileCOFF::readSymbolTable(SymbolVectorT &result) { - for (uint32_t i = 0, e = _obj->getNumberOfSymbols(); i != e; ++i) { - // Retrieve the symbol. - ErrorOr<llvm::object::COFFSymbolRef> sym = _obj->getSymbol(i); - StringRef name; - if (std::error_code ec = sym.getError()) - return ec; - if (sym->getSectionNumber() == llvm::COFF::IMAGE_SYM_DEBUG) - goto next; - result.push_back(*sym); - - if (std::error_code ec = _obj->getSymbolName(*sym, name)) - return ec; - - // Existence of the symbol @feat.00 indicates that object file is compatible - // with Safe Exception Handling. - if (name == "@feat.00") { - _compatibleWithSEH = true; - goto next; - } - - // Cache the name. - _symbolName[*sym] = name; - - // Symbol may be followed by auxiliary symbol table records. The aux - // record can be in any format, but the size is always the same as the - // regular symbol. The aux record supplies additional information for the - // standard symbol. We do not interpret the aux record here, but just - // store it to _auxSymbol. - if (sym->getNumberOfAuxSymbols() > 0) { - ErrorOr<llvm::object::COFFSymbolRef> aux = _obj->getSymbol(i + 1); - if (std::error_code ec = aux.getError()) - return ec; - _auxSymbol[*sym] = *aux; - } - next: - i += sym->getNumberOfAuxSymbols(); - } - return std::error_code(); -} - -/// Create atoms for the absolute symbols. -void FileCOFF::createAbsoluteAtoms(const SymbolVectorT &symbols, - std::vector<const AbsoluteAtom *> &result) { - for (llvm::object::COFFSymbolRef sym : symbols) { - if (sym.getSectionNumber() != llvm::COFF::IMAGE_SYM_ABSOLUTE) - continue; - auto *atom = new (_alloc) SimpleAbsoluteAtom(*this, _symbolName[sym], - getScope(sym), sym.getValue()); - result.push_back(atom); - _symbolAtom[sym] = atom; - } -} - -/// Create atoms for the undefined symbols. This code is bit complicated -/// because it supports "weak externals" mechanism of COFF. If an undefined -/// symbol (sym1) has auxiliary data, the data contains a symbol table index -/// at which the "second symbol" (sym2) for sym1 exists. If sym1 is resolved, -/// it's linked normally. If not, sym1 is resolved as if it has sym2's -/// name. This relationship between sym1 and sym2 is represented using -/// fallback mechanism of undefined symbol. -std::error_code -FileCOFF::createUndefinedAtoms(const SymbolVectorT &symbols, - std::vector<const UndefinedAtom *> &result) { - std::map<llvm::object::COFFSymbolRef, llvm::object::COFFSymbolRef> - weakExternal; - std::set<llvm::object::COFFSymbolRef> fallback; - for (llvm::object::COFFSymbolRef sym : symbols) { - if (sym.getSectionNumber() != llvm::COFF::IMAGE_SYM_UNDEFINED) - continue; - // Create a mapping from sym1 to sym2, if the undefined symbol has - // auxiliary data. - auto iter = _auxSymbol.find(sym); - if (iter == _auxSymbol.end()) - continue; - const coff_aux_weak_external *aux = - reinterpret_cast<const coff_aux_weak_external *>( - iter->second.getRawPtr()); - ErrorOr<llvm::object::COFFSymbolRef> sym2 = _obj->getSymbol(aux->TagIndex); - if (std::error_code ec = sym2.getError()) - return ec; - weakExternal[sym] = *sym2; - fallback.insert(*sym2); - } - - // Create atoms for the undefined symbols. - for (llvm::object::COFFSymbolRef sym : symbols) { - if (sym.getSectionNumber() != llvm::COFF::IMAGE_SYM_UNDEFINED) - continue; - if (fallback.count(sym) > 0) - continue; - - // If the symbol has sym2, create an undefiend atom for sym2, so that we - // can pass it as a fallback atom. - UndefinedAtom *fallback = nullptr; - auto iter = weakExternal.find(sym); - if (iter != weakExternal.end()) { - llvm::object::COFFSymbolRef sym2 = iter->second; - fallback = new (_alloc) COFFUndefinedAtom(*this, _symbolName[sym2]); - _symbolAtom[sym2] = fallback; - } - - // Create an atom for the symbol. - auto *atom = - new (_alloc) COFFUndefinedAtom(*this, _symbolName[sym], fallback); - result.push_back(atom); - _symbolAtom[sym] = atom; - } - return std::error_code(); -} - -/// Create atoms for the defined symbols. This pass is a bit complicated than -/// the other two, because in order to create the atom for the defined symbol -/// we need to know the adjacent symbols. -std::error_code -FileCOFF::createDefinedSymbols(const SymbolVectorT &symbols, - std::vector<const DefinedAtom *> &result) { - // A defined atom can be merged if its section attribute allows its contents - // to be merged. In COFF, it's not very easy to get the section attribute - // for the symbol, so scan all sections in advance and cache the attributes - // for later use. - if (std::error_code ec = cacheSectionAttributes()) - return ec; - - // Filter non-defined atoms, and group defined atoms by its section. - SectionToSymbolsT definedSymbols; - for (llvm::object::COFFSymbolRef sym : symbols) { - // A symbol with section number 0 and non-zero value represents a common - // symbol. The MS COFF spec did not give a definition of what the common - // symbol is. We should probably follow ELF's definition shown below. - // - // - If one object file has a common symbol and another has a definition, - // the common symbol is treated as an undefined reference. - // - If there is no definition for a common symbol, the program linker - // acts as though it saw a definition initialized to zero of the - // appropriate size. - // - Two object files may have common symbols of - // different sizes, in which case the program linker will use the - // largest size. - // - // FIXME: We are currently treating the common symbol as a normal - // mergeable atom. Implement the above semantcis. - if (sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_UNDEFINED && - sym.getValue() > 0) { - StringRef name = _symbolName[sym]; - uint32_t size = sym.getValue(); - auto *atom = new (_alloc) - COFFBSSAtom(*this, name, getScope(sym), DefinedAtom::permRW_, - DefinedAtom::mergeAsWeakAndAddressUsed, size, getNextOrdinal()); - - // Common symbols should be aligned on natural boundaries with the maximum - // of 32 byte. It's not documented anywhere, but it's what MSVC link.exe - // seems to be doing. - uint64_t alignment = std::min((uint64_t)32, llvm::NextPowerOf2(size)); - atom->setAlignment( - DefinedAtom::Alignment(llvm::countTrailingZeros(alignment))); - result.push_back(atom); - continue; - } - - // Skip if it's not for defined atom. - if (sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_DEBUG || - sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_ABSOLUTE || - sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_UNDEFINED) - continue; - - const coff_section *sec; - if (std::error_code ec = _obj->getSection(sym.getSectionNumber(), sec)) - return ec; - assert(sec && "SectionIndex > 0, Sec must be non-null!"); - - uint8_t sc = sym.getStorageClass(); - if (sc != llvm::COFF::IMAGE_SYM_CLASS_EXTERNAL && - sc != llvm::COFF::IMAGE_SYM_CLASS_STATIC && - sc != llvm::COFF::IMAGE_SYM_CLASS_FUNCTION && - sc != llvm::COFF::IMAGE_SYM_CLASS_LABEL) { - llvm::errs() << "Unable to create atom for: " << _symbolName[sym] << " (" - << static_cast<int>(sc) << ")\n"; - return llvm::object::object_error::parse_failed; - } - - definedSymbols[sec].push_back(sym); - } - - // Atomize the defined symbols. - if (std::error_code ec = AtomizeDefinedSymbols(definedSymbols, result)) - return ec; - - return std::error_code(); -} - -// Cache the COMDAT attributes, which indicate whether the symbols in the -// section can be merged or not. -std::error_code FileCOFF::cacheSectionAttributes() { - // The COMDAT section attribute is not an attribute of coff_section, but is - // stored in the auxiliary symbol for the first symbol referring a COMDAT - // section. It feels to me that it's unnecessarily complicated, but this is - // how COFF works. - for (auto i : _auxSymbol) { - // Read a section from the file - llvm::object::COFFSymbolRef sym = i.first; - if (sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_ABSOLUTE || - sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_UNDEFINED) - continue; - - const coff_section *sec; - if (std::error_code ec = _obj->getSection(sym.getSectionNumber(), sec)) - return ec; - const coff_aux_section_definition *aux = - reinterpret_cast<const coff_aux_section_definition *>( - i.second.getRawPtr()); - - if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_COMDAT) { - // Read aux symbol data. - _comdatSections.insert(sec); - _merge[sec] = getMerge(aux); - } - - // Handle associative sections. - if (aux->Selection == llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { - const coff_section *parent; - if (std::error_code ec = - _obj->getSection(aux->getNumber(sym.isBigObj()), parent)) - return ec; - _association.insert(std::make_pair(parent, sec)); - } - } - - // The sections that does not have auxiliary symbol are regular sections, in - // which symbols are not allowed to be merged. - for (const auto §ion : _obj->sections()) { - const coff_section *sec = _obj->getCOFFSection(section); - if (!_merge.count(sec)) - _merge[sec] = DefinedAtom::mergeNo; - } - return std::error_code(); -} - -/// Atomize \p symbols and append the results to \p atoms. The symbols are -/// assumed to have been defined in the \p section. -std::error_code FileCOFF::AtomizeDefinedSymbolsInSection( - const coff_section *section, SymbolVectorT &symbols, - std::vector<COFFDefinedFileAtom *> &atoms) { - // Sort symbols by position. - std::stable_sort( - symbols.begin(), symbols.end(), - [](llvm::object::COFFSymbolRef a, llvm::object::COFFSymbolRef b) - -> bool { return a.getValue() < b.getValue(); }); - - StringRef sectionName; - if (std::error_code ec = _obj->getSectionName(section, sectionName)) - return ec; - - // BSS section does not have contents. If this is the BSS section, create - // COFFBSSAtom instead of COFFDefinedAtom. - if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) { - for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { - llvm::object::COFFSymbolRef sym = *si; - uint32_t size = (si + 1 == se) ? section->SizeOfRawData - sym.getValue() - : si[1].getValue() - sym.getValue(); - auto *atom = new (_alloc) COFFBSSAtom( - *this, _symbolName[sym], getScope(sym), getPermissions(section), - DefinedAtom::mergeAsWeakAndAddressUsed, size, getNextOrdinal()); - atoms.push_back(atom); - _symbolAtom[sym] = atom; - } - return std::error_code(); - } - - ArrayRef<uint8_t> secData; - if (std::error_code ec = _obj->getSectionContents(section, secData)) - return ec; - - // A section with IMAGE_SCN_LNK_{INFO,REMOVE} attribute will never become - // a part of the output image. That's what the COFF spec says. - if (section->Characteristics & llvm::COFF::IMAGE_SCN_LNK_INFO || - section->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) - return std::error_code(); - - // Supporting debug info needs more work than just linking and combining - // .debug sections. We don't support it yet. Let's discard .debug sections at - // the very beginning of the process so that we don't spend time on linking - // blobs that nobody would understand. - if ((section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE) && - (sectionName == ".debug" || sectionName.startswith(".debug$"))) { - return std::error_code(); - } - - DefinedAtom::ContentType type = getContentType(section); - DefinedAtom::ContentPermissions perms = getPermissions(section); - uint64_t sectionSize = section->SizeOfRawData; - bool isComdat = (_comdatSections.count(section) == 1); - - // Create an atom for the entire section. - if (symbols.empty()) { - ArrayRef<uint8_t> data(secData.data(), secData.size()); - auto *atom = new (_alloc) COFFDefinedAtom( - *this, "", sectionName, sectionSize, Atom::scopeTranslationUnit, - type, isComdat, perms, _merge[section], data, getNextOrdinal()); - atoms.push_back(atom); - _definedAtomLocations[section].insert(std::make_pair(0, atom)); - return std::error_code(); - } - - // Create an unnamed atom if the first atom isn't at the start of the - // section. - if (symbols[0].getValue() != 0) { - uint64_t size = symbols[0].getValue(); - ArrayRef<uint8_t> data(secData.data(), size); - auto *atom = new (_alloc) COFFDefinedAtom( - *this, "", sectionName, sectionSize, Atom::scopeTranslationUnit, - type, isComdat, perms, _merge[section], data, getNextOrdinal()); - atoms.push_back(atom); - _definedAtomLocations[section].insert(std::make_pair(0, atom)); - } - - for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { - const uint8_t *start = secData.data() + si->getValue(); - // if this is the last symbol, take up the remaining data. - const uint8_t *end = (si + 1 == se) ? secData.data() + secData.size() - : secData.data() + (si + 1)->getValue(); - ArrayRef<uint8_t> data(start, end); - auto *atom = new (_alloc) COFFDefinedAtom( - *this, _symbolName[*si], sectionName, sectionSize, getScope(*si), - type, isComdat, perms, _merge[section], data, getNextOrdinal()); - atoms.push_back(atom); - _symbolAtom[*si] = atom; - _definedAtomLocations[section].insert(std::make_pair(si->getValue(), atom)); - } - return std::error_code(); -} - -std::error_code FileCOFF::AtomizeDefinedSymbols( - SectionToSymbolsT &definedSymbols, - std::vector<const DefinedAtom *> &definedAtoms) { - // For each section, make atoms for all the symbols defined in the - // section, and append the atoms to the result objects. - for (auto &i : definedSymbols) { - const coff_section *section = i.first; - SymbolVectorT &symbols = i.second; - std::vector<COFFDefinedFileAtom *> atoms; - if (std::error_code ec = - AtomizeDefinedSymbolsInSection(section, symbols, atoms)) - return ec; - - // Set alignment to the first atom so that the section contents - // will be aligned as specified by the object section header. - if (atoms.size() > 0) - atoms[0]->setAlignment(getAlignment(section)); - - // Connect atoms with layout-after edges. It prevents atoms - // from being GC'ed if there is a reference to one of the atoms - // in the same layout-after chain. In such case we want to emit - // all the atoms appeared in the same chain, because the "live" - // atom may reference other atoms in the same chain. - if (atoms.size() >= 2) - for (auto it = atoms.begin(), e = atoms.end(); it + 1 != e; ++it) - addLayoutEdge(*it, *(it + 1), lld::Reference::kindLayoutAfter); - - for (COFFDefinedFileAtom *atom : atoms) { - _sectionAtoms[section].push_back(atom); - definedAtoms.push_back(atom); - } - } - - // A COMDAT section with SELECT_ASSOCIATIVE attribute refer to other - // section. If the referred section is linked to a binary, the - // referring section needs to be linked too. A typical use case of - // this attribute is a static initializer; a parent is a comdat BSS - // section, and a child is a static initializer code for the data. - // - // We add referring section contents to the referred section's - // associate list, so that Resolver takes care of them. - for (auto i : _association) { - const coff_section *parent = i.first; - const coff_section *child = i.second; - if (_sectionAtoms.count(child)) { - COFFDefinedFileAtom *p = _sectionAtoms[parent][0]; - p->addAssociate(_sectionAtoms[child][0]); - } - } - - return std::error_code(); -} - -/// Find the atom that is at \p targetAddress in \p section. -std::error_code FileCOFF::findAtomAt(const coff_section *section, - uint32_t targetAddress, - COFFDefinedFileAtom *&result, - uint32_t &offsetInAtom) { - auto loc = _definedAtomLocations.find(section); - if (loc == _definedAtomLocations.end()) - return llvm::object::object_error::parse_failed; - std::multimap<uint32_t, COFFDefinedAtom *> &map = loc->second; - - auto it = map.upper_bound(targetAddress); - if (it == map.begin()) - return llvm::object::object_error::parse_failed; - --it; - uint32_t atomAddress = it->first; - result = it->second; - offsetInAtom = targetAddress - atomAddress; - return std::error_code(); -} - -/// Find the atom for the symbol that was at the \p index in the symbol -/// table. -std::error_code FileCOFF::getAtomBySymbolIndex(uint32_t index, Atom *&ret) { - ErrorOr<llvm::object::COFFSymbolRef> symbol = _obj->getSymbol(index); - if (std::error_code ec = symbol.getError()) - return ec; - ret = _symbolAtom[*symbol]; - assert(ret); - return std::error_code(); -} - -/// Add relocation information to an atom based on \p rel. \p rel is an -/// relocation entry for the \p section, and \p atoms are all the atoms -/// defined in the \p section. -std::error_code FileCOFF::addRelocationReference( - const coff_relocation *rel, const coff_section *section) { - // The address of the item which relocation is applied. Section's - // VirtualAddress needs to be added for historical reasons, but the value - // is usually just zero, so adding it is usually no-op. - uint32_t itemAddress = rel->VirtualAddress + section->VirtualAddress; - - Atom *targetAtom = nullptr; - if (std::error_code ec = - getAtomBySymbolIndex(rel->SymbolTableIndex, targetAtom)) - return ec; - - COFFDefinedFileAtom *atom; - uint32_t offsetInAtom; - if (std::error_code ec = findAtomAt(section, itemAddress, atom, offsetInAtom)) - return ec; - atom->addReference(llvm::make_unique<SimpleReference>( - Reference::KindNamespace::COFF, _referenceArch, rel->Type, offsetInAtom, - targetAtom, 0)); - return std::error_code(); -} - -// Read section contents. -std::error_code FileCOFF::getSectionContents(StringRef sectionName, - ArrayRef<uint8_t> &result) { - const coff_section *section = nullptr; - if (std::error_code ec = findSection(sectionName, section)) - return ec; - if (!section) - return std::error_code(); - if (std::error_code ec = _obj->getSectionContents(section, result)) - return ec; - return std::error_code(); -} - -AliasAtom * -FileCOFF::createAlias(StringRef name, const DefinedAtom *target, int cnt) { - AliasAtom *alias = new (_alloc) AliasAtom(*this, name); - alias->addReference(Reference::KindNamespace::all, Reference::KindArch::all, - Reference::kindLayoutAfter, 0, target, 0); - alias->setMerge(DefinedAtom::mergeAsWeak); - if (target->contentType() == DefinedAtom::typeCode) - alias->setDeadStrip(DefinedAtom::deadStripNever); - alias->setOrdinal(target->ordinal() - cnt); - return alias; -} - -void FileCOFF::createAlternateNameAtoms() { - std::vector<AliasAtom *> aliases; - for (const DefinedAtom *atom : defined()) { - int cnt = 1; - for (StringRef alias : _ctx.getAlternateNames(atom->name())) - aliases.push_back(createAlias(alias, atom, cnt++)); - } - for (AliasAtom *alias : aliases) - _definedAtoms._atoms.push_back(alias); -} - -// Interpret the contents of .drectve section. If exists, the section contains -// a string containing command line options. The linker is expected to -// interpret the options as if they were given via the command line. -// -// The section mainly contains /defaultlib (-l in Unix), but can contain any -// options as long as they are valid. -std::error_code -FileCOFF::parseDirectiveSection(StringRef directives) { - DEBUG(llvm::dbgs() << ".drectve: " << directives << "\n"); - - // Split the string into tokens, as the shell would do for argv. - SmallVector<const char *, 16> tokens; - tokens.push_back("link"); // argv[0] is the command name. Will be ignored. - llvm::cl::TokenizeWindowsCommandLine(directives, _stringSaver, tokens); - tokens.push_back(nullptr); - - // Calls the command line parser to interpret the token string as if they - // were given via the command line. - int argc = tokens.size() - 1; - const char **argv = &tokens[0]; - std::string errorMessage; - llvm::raw_string_ostream stream(errorMessage); - PECOFFLinkingContext::ParseDirectives parseDirectives = - _ctx.getParseDirectives(); - bool parseFailed = !parseDirectives(argc, argv, _ctx, stream); - stream.flush(); - // Print error message if error. - if (parseFailed) { - return make_dynamic_error_code( - Twine("Failed to parse '") + directives + "'\n" - + "Reason: " + errorMessage); - } - if (!errorMessage.empty()) { - llvm::errs() << "lld warning: " << errorMessage << "\n"; - } - return std::error_code(); -} - -/// Returns the target machine type of the current object file. -std::error_code FileCOFF::getReferenceArch(Reference::KindArch &result) { - switch (_obj->getMachine()) { - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - result = Reference::KindArch::x86; - return std::error_code(); - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - result = Reference::KindArch::x86_64; - return std::error_code(); - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - result = Reference::KindArch::ARM; - return std::error_code(); - case llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN: - result = Reference::KindArch::all; - return std::error_code(); - } - llvm::errs() << "Unsupported machine type: 0x" - << llvm::utohexstr(_obj->getMachine()) << '\n'; - return llvm::object::object_error::parse_failed; -} - -/// Add relocation information to atoms. -std::error_code FileCOFF::addRelocationReferenceToAtoms() { - // Relocation entries are defined for each section. - for (const auto &sec : _obj->sections()) { - const coff_section *section = _obj->getCOFFSection(sec); - - // Skip if there's no atom for the section. Currently we do not create any - // atoms for some sections, such as "debug$S", and such sections need to - // be skipped here too. - if (_sectionAtoms.find(section) == _sectionAtoms.end()) - continue; - - for (const auto &reloc : sec.relocations()) { - const coff_relocation *rel = _obj->getCOFFRelocation(reloc); - if (auto ec = addRelocationReference(rel, section)) - return ec; - } - } - return std::error_code(); -} - -// Read .sxdata section if exists. .sxdata is a x86-only section that contains a -// vector of symbol offsets. The symbols pointed by this section are SEH handler -// functions contained in the same object file. The linker needs to construct a -// SEH table and emit it to executable. -// -// On x86, exception handler addresses are in stack, so they are vulnerable to -// stack overflow attack. In order to protect against it, Windows runtime uses -// the SEH table to check if a SEH handler address in stack is a real address of -// a handler created by compiler. -// -// What we want to emit from the linker is a vector of SEH handler VAs, but here -// we have a vector of offsets to the symbol table. So we convert the latter to -// the former. -std::error_code FileCOFF::maybeCreateSXDataAtoms() { - ArrayRef<uint8_t> sxdata; - if (std::error_code ec = getSectionContents(".sxdata", sxdata)) - return ec; - if (sxdata.empty()) - return std::error_code(); - - auto *atom = new (_alloc) COFFDefinedAtom( - *this, "", ".sxdata", 0, Atom::scopeTranslationUnit, - DefinedAtom::typeData, false /*isComdat*/, DefinedAtom::permR__, - DefinedAtom::mergeNo, sxdata, getNextOrdinal()); - - const ulittle32_t *symbolIndex = - reinterpret_cast<const ulittle32_t *>(sxdata.data()); - int numSymbols = sxdata.size() / sizeof(uint32_t); - - for (int i = 0; i < numSymbols; ++i) { - Atom *handlerFunc; - if (std::error_code ec = getAtomBySymbolIndex(symbolIndex[i], handlerFunc)) - return ec; - int offsetInAtom = i * sizeof(uint32_t); - - uint16_t rtype; - switch (_obj->getMachine()) { - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - rtype = llvm::COFF::IMAGE_REL_AMD64_ADDR32; - break; - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - rtype = llvm::COFF::IMAGE_REL_I386_DIR32; - break; - default: - llvm_unreachable("unsupported machine type"); - } - - atom->addReference(llvm::make_unique<SimpleReference>( - Reference::KindNamespace::COFF, _referenceArch, rtype, offsetInAtom, - handlerFunc, 0)); - } - - _definedAtoms._atoms.push_back(atom); - return std::error_code(); -} - -/// Find a section by name. -std::error_code FileCOFF::findSection(StringRef name, - const coff_section *&result) { - for (const auto &sec : _obj->sections()) { - const coff_section *section = _obj->getCOFFSection(sec); - StringRef sectionName; - if (auto ec = _obj->getSectionName(section, sectionName)) - return ec; - if (sectionName == name) { - result = section; - return std::error_code(); - } - } - // Section was not found, but it's not an error. This method returns - // an error only when there's a read error. - return std::error_code(); -} - -// Convert ArrayRef<uint8_t> to std::string. The array contains a string which -// may not be terminated by NUL. -StringRef FileCOFF::ArrayRefToString(ArrayRef<uint8_t> array) { - // .drectve sections are encoded in either ASCII or UTF-8 with BOM. - // The PE/COFF spec allows ANSI (Windows-1252 encoding), but seems - // it's no longer in use. - // Skip a UTF-8 byte marker if exists. - if (array.size() >= 3 && array[0] == 0xEF && array[1] == 0xBB && - array[2] == 0xBF) { - array = array.slice(3); - } - if (array.empty()) - return ""; - StringRef s(reinterpret_cast<const char *>(array.data()), array.size()); - s = s.substr(0, s.find_first_of('\0')); - std::string *contents = new (_alloc) std::string(s.data(), s.size()); - return StringRef(*contents).trim(); -} - -// getNextOrdinal returns a monotonically increasaing uint64_t number -// starting from 1. There's a large gap between two numbers returned -// from this function, so that you can put other atoms between them. -uint64_t FileCOFF::getNextOrdinal() { - return _ordinal++ << 32; -} - -class COFFObjectReader : public Reader { -public: - COFFObjectReader(PECOFFLinkingContext &ctx) : _ctx(ctx) {} - - bool canParse(file_magic magic, StringRef ext, - const MemoryBuffer &) const override { - return magic == llvm::sys::fs::file_magic::coff_object; - } - - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry &, - std::vector<std::unique_ptr<File>> &result) const override { - // Parse the memory buffer as PECOFF file. - auto *file = new FileCOFF(std::move(mb), _ctx); - result.push_back(std::unique_ptr<File>(file)); - return std::error_code(); - } - -private: - PECOFFLinkingContext &_ctx; -}; - -using namespace llvm::COFF; - -const Registry::KindStrings kindStringsI386[] = { - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_ABSOLUTE), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_DIR16), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_REL16), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_DIR32), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_DIR32NB), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SEG12), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SECTION), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SECREL), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_TOKEN), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SECREL7), - LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_REL32), - LLD_KIND_STRING_END}; - -const Registry::KindStrings kindStringsAMD64[] = { - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ABSOLUTE), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ADDR64), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ADDR32), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ADDR32NB), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_1), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_2), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_3), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_4), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_5), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SECTION), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SECREL), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SECREL7), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_TOKEN), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SREL32), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_PAIR), - LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SSPAN32), - LLD_KIND_STRING_END}; - -const Registry::KindStrings kindStringsARMNT[] = { - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_ABSOLUTE), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_ADDR32), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_ADDR32NB), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH24), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH11), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_TOKEN), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BLX24), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BLX11), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_SECTION), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_SECREL), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_MOV32A), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_MOV32T), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH20T), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH24T), - LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BLX23T), -}; - -} // end namespace anonymous - -namespace lld { - -void Registry::addSupportCOFFObjects(PECOFFLinkingContext &ctx) { - add(std::unique_ptr<Reader>(new COFFObjectReader(ctx))); - addKindTable(Reference::KindNamespace::COFF, Reference::KindArch::x86, - kindStringsI386); - addKindTable(Reference::KindNamespace::COFF, Reference::KindArch::x86_64, - kindStringsAMD64); - addKindTable(Reference::KindNamespace::COFF, Reference::KindArch::ARM, - kindStringsARMNT); -} - -} diff --git a/lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp b/lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp deleted file mode 100644 index 8c9641376a0d..000000000000 --- a/lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp +++ /dev/null @@ -1,389 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp ---------------------===// -// -// 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 way to read an import library member in a -/// .lib file. -/// -/// Archive Files in Windows -/// ======================== -/// -/// In Windows, archive files with .lib file extension serve two different -/// purposes. -/// -/// - For static linking: An archive file in this use case contains multiple -/// regular .obj files and is used for static linking. This is the same -/// usage as .a file in Unix. -/// -/// - For dynamic linking: An archive file in this use case contains pseudo -/// .obj files to describe exported symbols of a DLL. Each pseudo .obj file -/// in an archive has a name of an exported symbol and a DLL filename from -/// which the symbol can be imported. When you link a DLL on Windows, you -/// pass the name of the .lib file for the DLL instead of the DLL filename -/// itself. That is the Windows way of linking against a shared library. -/// -/// This file contains a function to handle the pseudo object file. -/// -/// Windows Loader and Import Address Table -/// ======================================= -/// -/// Windows supports a GOT-like mechanism for DLLs. The executable using DLLs -/// contains a list of DLL names and list of symbols that need to be resolved by -/// the loader. Windows loader maps the executable and all the DLLs to memory, -/// resolves the symbols referencing items in DLLs, and updates the import -/// address table (IAT) in memory. The IAT is an array of pointers to all of the -/// data or functions in DLL referenced by the executable. You cannot access -/// items in DLLs directly. They have to be accessed through an extra level of -/// indirection. -/// -/// So, if you want to access an item in DLL, you have to go through a -/// pointer. How do you actually do that? You need a symbol for a pointer in the -/// IAT. For each symbol defined in a DLL, a symbol with "__imp_" prefix is -/// exported from the DLL for an IAT entry. For example, if you have a global -/// variable "foo" in a DLL, a pointer to the variable is available as -/// "_imp__foo". The IAT is an array of _imp__ symbols. -/// -/// Is this OK? That's not that complicated. Because items in a DLL are not -/// directly accessible, you need to access through a pointer, and the pointer -/// is available as a symbol with _imp__ prefix. -/// -/// Note 1: Although you can write code with _imp__ prefix, today's compiler and -/// linker let you write code as if there's no extra level of indirection. -/// That's why you haven't seen lots of _imp__ in your code. A variable or a -/// function declared with "dllimport" attribute is treated as an item in a DLL, -/// and the compiler automatically mangles its name and inserts the extra level -/// of indirection when accessing the item. Here are some examples: -/// -/// __declspec(dllimport) int var_in_dll; -/// var_in_dll = 3; // is equivalent to *_imp__var_in_dll = 3; -/// -/// __declspec(dllimport) int fn_in_dll(void); -/// fn_in_dll(); // is equivalent to (*_imp__fn_in_dll)(); -/// -/// It's just the compiler rewrites code for you so that you don't need to -/// handle the indirection yourself. -/// -/// Note 2: __declspec(dllimport) is mandatory for data but optional for -/// function. For a function, the linker creates a jump table with the original -/// symbol name, so that the function is accessible without _imp__ prefix. The -/// same function in a DLL can be called through two different symbols if it's -/// not dllimport'ed. -/// -/// (*_imp__fn)() -/// fn() -/// -/// The above functions do the same thing. fn's content is a JMP instruction to -/// branch to the address pointed by _imp__fn. The latter may be a little bit -/// slower than the former because it will execute the extra JMP instruction, -/// but that's usually negligible. -/// -/// If a function is dllimport'ed, which is usually done in a header file, -/// mangled name will be used at compile time so the jump table will not be -/// used. -/// -/// Because there's no way to hide the indirection for data access at link time, -/// data has to be accessed through dllimport'ed symbols or explicit _imp__ -/// prefix. -/// -/// Idata Sections in the Pseudo Object File -/// ======================================== -/// -/// The object file created by cl.exe has several sections whose name starts -/// with ".idata$" followed by a number. The contents of the sections seem the -/// fragments of a complete ".idata" section. These sections has relocations for -/// the data referenced from the idata secton. Generally, the linker discards -/// "$" and all characters that follow from the section name and merges their -/// contents to one section. So, it looks like if everything would work fine, -/// the idata section would naturally be constructed without having any special -/// code for doing that. -/// -/// However, the LLD linker cannot do that. An idata section constructed in that -/// way was never be in valid format. We don't know the reason yet. Our -/// assumption on the idata fragment could simply be wrong, or the LLD linker is -/// not powerful enough to do the job. Meanwhile, we construct the idata section -/// ourselves. All the "idata$" sections in the pseudo object file are currently -/// ignored. -/// -/// Creating Atoms for the Import Address Table -/// =========================================== -/// -/// The function in this file reads a pseudo object file and creates at most two -/// atoms. One is a shared library atom for _imp__ symbol. The another is a -/// defined atom for the JMP instruction if the symbol is for a function. -/// -//===----------------------------------------------------------------------===// - -#include "Atoms.h" -#include "lld/Core/Error.h" -#include "lld/Core/File.h" -#include "lld/Core/SharedLibraryAtom.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/COFF.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include <cstring> -#include <map> -#include <system_error> -#include <vector> - -using namespace lld; -using namespace lld::pecoff; -using namespace llvm; -using namespace llvm::support::endian; - -#define DEBUG_TYPE "ReaderImportHeader" - -namespace lld { - -namespace { - -// This code is valid both in x86 and x64. -const uint8_t FuncAtomContentX86[] = { - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0 - 0xcc, 0xcc // INT 3; INT 3 -}; - -const uint8_t FuncAtomContentARMNT[] = { - 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 - 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 - 0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip] -}; - -static void setJumpInstTarget(COFFLinkerInternalAtom *src, const Atom *dst, - int off, MachineTypes machine) { - SimpleReference *ref; - - switch (machine) { - default: llvm::report_fatal_error("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - ref = new SimpleReference(Reference::KindNamespace::COFF, - Reference::KindArch::x86, - llvm::COFF::IMAGE_REL_I386_DIR32, - off, dst, 0); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - ref = new SimpleReference(Reference::KindNamespace::COFF, - Reference::KindArch::x86_64, - llvm::COFF::IMAGE_REL_AMD64_REL32, - off, dst, 0); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - ref = new SimpleReference(Reference::KindNamespace::COFF, - Reference::KindArch::ARM, - llvm::COFF::IMAGE_REL_ARM_MOV32T, - off, dst, 0); - break; - } - src->addReference(std::unique_ptr<SimpleReference>(ref)); -} - -/// The defined atom for jump table. -class FuncAtom : public COFFLinkerInternalAtom { -public: - FuncAtom(const File &file, StringRef symbolName, - const COFFSharedLibraryAtom *impAtom, MachineTypes machine) - : COFFLinkerInternalAtom(file, /*oridnal*/ 0, createContent(machine), - symbolName) { - size_t Offset; - - switch (machine) { - default: llvm::report_fatal_error("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - Offset = 2; - break; - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - Offset = 0; - break; - } - - setJumpInstTarget(this, impAtom, Offset, machine); - } - - uint64_t ordinal() const override { return 0; } - Scope scope() const override { return scopeGlobal; } - ContentType contentType() const override { return typeCode; } - Alignment alignment() const override { return Alignment(1); } - ContentPermissions permissions() const override { return permR_X; } - -private: - std::vector<uint8_t> createContent(MachineTypes machine) const { - const uint8_t *Data; - size_t Size; - - switch (machine) { - default: llvm::report_fatal_error("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - Data = FuncAtomContentX86; - Size = sizeof(FuncAtomContentX86); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - Data = FuncAtomContentARMNT; - Size = sizeof(FuncAtomContentARMNT); - break; - } - - return std::vector<uint8_t>(Data, Data + Size); - } -}; - -class FileImportLibrary : public File { -public: - FileImportLibrary(std::unique_ptr<MemoryBuffer> mb, MachineTypes machine) - : File(mb->getBufferIdentifier(), kindSharedLibrary), - _mb(std::move(mb)), _machine(machine) {} - - std::error_code doParse() override { - const char *buf = _mb->getBufferStart(); - const char *end = _mb->getBufferEnd(); - - // The size of the string that follows the header. - uint32_t dataSize - = read32le(buf + offsetof(COFF::ImportHeader, SizeOfData)); - - // Check if the total size is valid. - if (std::size_t(end - buf) != sizeof(COFF::ImportHeader) + dataSize) - return make_error_code(NativeReaderError::unknown_file_format); - - uint16_t hint = read16le(buf + offsetof(COFF::ImportHeader, OrdinalHint)); - StringRef symbolName(buf + sizeof(COFF::ImportHeader)); - StringRef dllName(buf + sizeof(COFF::ImportHeader) + symbolName.size() + 1); - - // TypeInfo is a bitfield. The least significant 2 bits are import - // type, followed by 3 bit import name type. - uint16_t typeInfo = read16le(buf + offsetof(COFF::ImportHeader, TypeInfo)); - int type = typeInfo & 0x3; - int nameType = (typeInfo >> 2) & 0x7; - - // Symbol name used by the linker may be different from the symbol name used - // by the loader. The latter may lack symbol decorations, or may not even - // have name if it's imported by ordinal. - StringRef importName = symbolNameToImportName(symbolName, nameType); - - const COFFSharedLibraryAtom *dataAtom = - addSharedLibraryAtom(hint, symbolName, importName, dllName); - if (type == llvm::COFF::IMPORT_CODE) - addFuncAtom(symbolName, dllName, dataAtom); - - return std::error_code(); - } - - const atom_collection<DefinedAtom> &defined() const override { - return _definedAtoms; - } - - const atom_collection<UndefinedAtom> &undefined() const override { - return _noUndefinedAtoms; - } - - const atom_collection<SharedLibraryAtom> &sharedLibrary() const override { - return _sharedLibraryAtoms; - } - - const atom_collection<AbsoluteAtom> &absolute() const override { - return _noAbsoluteAtoms; - } - -private: - const COFFSharedLibraryAtom *addSharedLibraryAtom(uint16_t hint, - StringRef symbolName, - StringRef importName, - StringRef dllName) { - auto *atom = new (_alloc) - COFFSharedLibraryAtom(*this, hint, symbolName, importName, dllName); - _sharedLibraryAtoms._atoms.push_back(atom); - return atom; - } - - void addFuncAtom(StringRef symbolName, StringRef dllName, - const COFFSharedLibraryAtom *impAtom) { - auto *atom = new (_alloc) FuncAtom(*this, symbolName, impAtom, _machine); - _definedAtoms._atoms.push_back(atom); - } - - atom_collection_vector<DefinedAtom> _definedAtoms; - atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; - mutable llvm::BumpPtrAllocator _alloc; - - // Does the same thing as StringRef::ltrim() but removes at most one - // character. - StringRef ltrim1(StringRef str, const char *chars) const { - if (!str.empty() && strchr(chars, str[0])) - return str.substr(1); - return str; - } - - // Convert the given symbol name to the import symbol name exported by the - // DLL. - StringRef symbolNameToImportName(StringRef symbolName, int nameType) const { - StringRef ret; - switch (nameType) { - case llvm::COFF::IMPORT_ORDINAL: - // The import is by ordinal. No symbol name will be used to identify the - // item in the DLL. Only its ordinal will be used. - return ""; - case llvm::COFF::IMPORT_NAME: - // The import name in this case is identical to the symbol name. - return symbolName; - case llvm::COFF::IMPORT_NAME_NOPREFIX: - // The import name is the symbol name without leading ?, @ or _. - ret = ltrim1(symbolName, "?@_"); - break; - case llvm::COFF::IMPORT_NAME_UNDECORATE: - // Similar to NOPREFIX, but we also need to truncate at the first @. - ret = ltrim1(symbolName, "?@_"); - ret = ret.substr(0, ret.find('@')); - break; - } - std::string *str = new (_alloc) std::string(ret); - return *str; - } - - std::unique_ptr<MemoryBuffer> _mb; - MachineTypes _machine; -}; - -class COFFImportLibraryReader : public Reader { -public: - COFFImportLibraryReader(PECOFFLinkingContext &ctx) : _ctx(ctx) {} - - bool canParse(file_magic magic, StringRef, - const MemoryBuffer &mb) const override { - if (mb.getBufferSize() < sizeof(COFF::ImportHeader)) - return false; - return (magic == llvm::sys::fs::file_magic::coff_import_library); - } - - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, - std::vector<std::unique_ptr<File> > &result) const override { - auto *file = new FileImportLibrary(std::move(mb), _ctx.getMachineType()); - result.push_back(std::unique_ptr<File>(file)); - return std::error_code(); - } - -private: - PECOFFLinkingContext &_ctx; -}; - -} // end anonymous namespace - -void Registry::addSupportCOFFImportLibraries(PECOFFLinkingContext &ctx) { - add(llvm::make_unique<COFFImportLibraryReader>(ctx)); -} - -} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp b/lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp deleted file mode 100644 index fd3360f018b6..000000000000 --- a/lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp +++ /dev/null @@ -1,118 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp --------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// This file is responsible for creating the Import Library file. -/// -//===----------------------------------------------------------------------===// - -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/FileUtilities.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/raw_ostream.h" - -namespace lld { -namespace pecoff { - -/// Creates a .def file containing the list of exported symbols. -static std::string -createModuleDefinitionFile(const PECOFFLinkingContext &ctx) { - std::string ret; - llvm::raw_string_ostream os(ret); - os << "LIBRARY \"" << llvm::sys::path::filename(ctx.outputPath()) << "\"\n" - << "EXPORTS\n"; - - for (const PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) { - // Symbol names in a module-definition file will be mangled by lib.exe, - // so we need to demangle them before writing to a .def file. - os << " "; - if (!desc.externalName.empty()) { - os << desc.externalName; - } else if (!desc.mangledName.empty()) { - os << ctx.undecorateSymbol(desc.mangledName); - } else { - os << ctx.undecorateSymbol(desc.name); - } - - if (!desc.isPrivate) - os << " @" << desc.ordinal; - if (desc.noname) - os << " NONAME"; - if (desc.isData) - os << " DATA"; - if (desc.isPrivate) - os << " PRIVATE"; - os << "\n"; - } - os.flush(); - return ret; -} - -static std::string writeToTempFile(StringRef contents) { - SmallString<128> path; - int fd; - if (llvm::sys::fs::createTemporaryFile("tmp", "def", fd, path)) { - llvm::errs() << "Failed to create temporary file\n"; - return ""; - } - llvm::raw_fd_ostream os(fd, /*shouldClose*/ true); - os << contents; - return path.str(); -} - -static void writeTo(StringRef path, StringRef contents) { - int fd; - if (llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text)) { - llvm::errs() << "Failed to open " << path << "\n"; - return; - } - llvm::raw_fd_ostream os(fd, /*shouldClose*/ true); - os << contents; -} - -/// Creates a .def file and runs lib.exe on it to create an import library. -void writeImportLibrary(const PECOFFLinkingContext &ctx) { - std::string fileContents = createModuleDefinitionFile(ctx); - - std::string program = "lib.exe"; - ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); - if (!programPathOrErr) { - llvm::errs() << "Unable to find " << program << " in PATH\n"; - } else { - const std::string &programPath = *programPathOrErr; - - std::string defPath = writeToTempFile(fileContents); - llvm::FileRemover tmpFile(defPath); - - std::string defArg = "/def:"; - defArg.append(defPath); - std::string outputArg = "/out:"; - outputArg.append(ctx.getOutputImportLibraryPath()); - - std::vector<const char *> args; - args.push_back(programPath.c_str()); - args.push_back("/nologo"); - args.push_back(ctx.is64Bit() ? "/machine:x64" : "/machine:x86"); - args.push_back(defArg.c_str()); - args.push_back(outputArg.c_str()); - args.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) - llvm::errs() << program << " failed\n"; - } - - // If /lldmoduledeffile:<filename> is given, make a copy of the - // temporary module definition file. This feature is for unit tests. - if (!ctx.getModuleDefinitionFile().empty()) - writeTo(ctx.getModuleDefinitionFile(), fileContents); -} - -} // end namespace pecoff -} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/WriterImportLibrary.h b/lib/ReaderWriter/PECOFF/WriterImportLibrary.h deleted file mode 100644 index a51b9a3648c5..000000000000 --- a/lib/ReaderWriter/PECOFF/WriterImportLibrary.h +++ /dev/null @@ -1,23 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/WriterImportLibrary.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_PE_COFF_WRITER_IMPORT_LIBRARY_H -#define LLD_READER_WRITER_PE_COFF_WRITER_IMPORT_LIBRARY_H - -namespace lld { -class PECOFFLinkingContext; - -namespace pecoff { - -void writeImportLibrary(const PECOFFLinkingContext &ctx); - -} // end namespace pecoff -} // end namespace lld - -#endif diff --git a/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp b/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp deleted file mode 100644 index d34e2d3d63fd..000000000000 --- a/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp +++ /dev/null @@ -1,1417 +0,0 @@ -//===- lib/ReaderWriter/PECOFF/WriterPECOFF.cpp ---------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// PE/COFF file consists of DOS Header, PE Header, COFF Header and Section -/// Tables followed by raw section data. -/// -/// This writer is responsible for writing Core Linker results to an Windows -/// executable file. -/// -/// This writer currently supports 32 bit PE/COFF for x86 processor only. -/// -//===----------------------------------------------------------------------===// - -#include "Atoms.h" -#include "WriterImportLibrary.h" -#include "lld/Core/DefinedAtom.h" -#include "lld/Core/File.h" -#include "lld/Core/Writer.h" -#include "lld/ReaderWriter/AtomLayout.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/COFF.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/Format.h" -#include <algorithm> -#include <cstdlib> -#include <map> -#include <time.h> -#include <vector> - -#define DEBUG_TYPE "WriterPECOFF" - -using namespace llvm::support::endian; - -using llvm::COFF::DataDirectoryIndex; -using llvm::object::coff_runtime_function_x64; -using llvm::support::ulittle16_t; -using llvm::support::ulittle32_t; -using llvm::support::ulittle64_t; - -namespace lld { -namespace pecoff { - -// Disk sector size. Some data needs to be aligned at disk sector boundary in -// file. -static const int SECTOR_SIZE = 512; - -namespace { -class SectionChunk; - -/// A Chunk is an abstract contiguous range in an output file. -class Chunk { -public: - enum Kind { - kindHeader, - kindSection, - kindStringTable, - kindAtomChunk - }; - - explicit Chunk(Kind kind) : _kind(kind), _size(0) {} - virtual ~Chunk() {} - virtual void write(uint8_t *buffer) = 0; - virtual uint64_t size() const { return _size; } - virtual uint64_t onDiskSize() const { return size(); } - virtual uint64_t align() const { return 1; } - - uint64_t fileOffset() const { return _fileOffset; } - void setFileOffset(uint64_t fileOffset) { _fileOffset = fileOffset; } - Kind getKind() const { return _kind; } - -protected: - Kind _kind; - uint64_t _size; - uint64_t _fileOffset; -}; - -/// A HeaderChunk is an abstract class to represent a file header for -/// PE/COFF. The data in the header chunk is metadata about program and will -/// be consumed by the windows loader. HeaderChunks are not mapped to memory -/// when executed. -class HeaderChunk : public Chunk { -public: - HeaderChunk() : Chunk(kindHeader) {} - - static bool classof(const Chunk *c) { return c->getKind() == kindHeader; } -}; - -/// A DOSStubChunk represents the DOS compatible header at the beginning -/// of PE/COFF files. -class DOSStubChunk : public HeaderChunk { -public: - explicit DOSStubChunk(const PECOFFLinkingContext &ctx) - : HeaderChunk(), _context(ctx) { - // Minimum size of DOS stub is 64 bytes. The next block (PE header) needs to - // be aligned on 8 byte boundary. - size_t size = std::max(_context.getDosStub().size(), (size_t)64); - _size = llvm::RoundUpToAlignment(size, 8); - } - - void write(uint8_t *buffer) override { - ArrayRef<uint8_t> array = _context.getDosStub(); - std::memcpy(buffer, array.data(), array.size()); - auto *header = reinterpret_cast<llvm::object::dos_header *>(buffer); - header->AddressOfRelocationTable = sizeof(llvm::object::dos_header); - header->AddressOfNewExeHeader = _size; - } - -private: - const PECOFFLinkingContext &_context; -}; - -/// A PEHeaderChunk represents PE header including COFF header. -template <class PEHeader> -class PEHeaderChunk : public HeaderChunk { -public: - explicit PEHeaderChunk(const PECOFFLinkingContext &ctx); - - void write(uint8_t *buffer) override; - - void setSizeOfHeaders(uint64_t size) { - // Must be multiple of FileAlignment. - _peHeader.SizeOfHeaders = llvm::RoundUpToAlignment(size, SECTOR_SIZE); - } - - void setSizeOfCode(uint64_t size) { _peHeader.SizeOfCode = size; } - void setBaseOfCode(uint32_t rva) { _peHeader.BaseOfCode = rva; } - void setBaseOfData(uint32_t rva); - void setSizeOfImage(uint32_t size) { _peHeader.SizeOfImage = size; } - - void setSizeOfInitializedData(uint64_t size) { - _peHeader.SizeOfInitializedData = size; - } - - void setSizeOfUninitializedData(uint64_t size) { - _peHeader.SizeOfUninitializedData = size; - } - - void setNumberOfSections(uint32_t num) { _coffHeader.NumberOfSections = num; } - void setNumberOfSymbols(uint32_t num) { _coffHeader.NumberOfSymbols = num; } - - void setAddressOfEntryPoint(uint32_t address) { - _peHeader.AddressOfEntryPoint = address; - } - - void setPointerToSymbolTable(uint32_t rva) { - _coffHeader.PointerToSymbolTable = rva; - } - -private: - llvm::object::coff_file_header _coffHeader; - PEHeader _peHeader; -}; - -/// A SectionHeaderTableChunk represents Section Table Header of PE/COFF -/// format, which is a list of section headers. -class SectionHeaderTableChunk : public HeaderChunk { -public: - SectionHeaderTableChunk() : HeaderChunk() {} - void addSection(SectionChunk *chunk); - uint64_t size() const override; - void write(uint8_t *buffer) override; - -private: - static llvm::object::coff_section createSectionHeader(SectionChunk *chunk); - - std::vector<SectionChunk *> _sections; -}; - -class StringTableChunk : public Chunk { -public: - StringTableChunk() : Chunk(kindStringTable) {} - - static bool classof(const Chunk *c) { - return c->getKind() == kindStringTable; - } - - uint32_t addSectionName(StringRef sectionName) { - if (_stringTable.empty()) { - // The string table immediately follows the symbol table. - // We don't really need a symbol table, but some tools (e.g. dumpbin) - // don't like zero-length symbol table. - // Make room for the empty symbol slot, which occupies 18 byte. - // We also need to reserve 4 bytes for the string table header. - int size = sizeof(llvm::object::coff_symbol16) + 4; - _stringTable.insert(_stringTable.begin(), size, 0); - // Set the name of the dummy symbol to the first string table entry. - // It's better than letting dumpbin print out a garabage as a symbol name. - char *off = _stringTable.data() + 4; - write32le(off, 4); - } - uint32_t offset = _stringTable.size(); - _stringTable.insert(_stringTable.end(), sectionName.begin(), - sectionName.end()); - _stringTable.push_back('\0'); - return offset - sizeof(llvm::object::coff_symbol16); - } - - uint64_t size() const override { return _stringTable.size(); } - - void write(uint8_t *buffer) override { - if (_stringTable.empty()) - return; - char *off = _stringTable.data() + sizeof(llvm::object::coff_symbol16); - write32le(off, _stringTable.size()); - std::memcpy(buffer, _stringTable.data(), _stringTable.size()); - } - -private: - std::vector<char> _stringTable; -}; - -class SectionChunk : public Chunk { -public: - uint64_t onDiskSize() const override { - if (_characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) - return 0; - return llvm::RoundUpToAlignment(size(), SECTOR_SIZE); - } - - uint64_t align() const override { return SECTOR_SIZE; } - uint32_t getCharacteristics() const { return _characteristics; } - StringRef getSectionName() const { return _sectionName; } - virtual uint64_t memAlign() const { return _memAlign; } - - static bool classof(const Chunk *c) { - Kind kind = c->getKind(); - return kind == kindSection || kind == kindAtomChunk; - } - - uint64_t getVirtualAddress() { return _virtualAddress; } - virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; } - - uint32_t getStringTableOffset() const { return _stringTableOffset; } - void setStringTableOffset(uint32_t offset) { _stringTableOffset = offset; } - -protected: - SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics, - const PECOFFLinkingContext &ctx) - : Chunk(kind), _sectionName(sectionName), - _characteristics(characteristics), _virtualAddress(0), - _stringTableOffset(0), _memAlign(ctx.getPageSize()) {} - -private: - StringRef _sectionName; - const uint32_t _characteristics; - uint64_t _virtualAddress; - uint32_t _stringTableOffset; - uint64_t _memAlign; -}; - -struct BaseReloc { - BaseReloc(uint64_t a, llvm::COFF::BaseRelocationType t) : addr(a), type(t) {} - uint64_t addr; - llvm::COFF::BaseRelocationType type; -}; - -/// An AtomChunk represents a section containing atoms. -class AtomChunk : public SectionChunk { -public: - AtomChunk(const PECOFFLinkingContext &ctx, StringRef name, - const std::vector<const DefinedAtom *> &atoms); - - void write(uint8_t *buffer) override; - - uint64_t memAlign() const override; - void appendAtom(const DefinedAtom *atom); - void buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const; - - void applyRelocationsARM(uint8_t *buffer, - std::map<const Atom *, uint64_t> &atomRva, - std::vector<uint64_t> §ionRva, - uint64_t imageBaseAddress); - void applyRelocationsX86(uint8_t *buffer, - std::map<const Atom *, uint64_t> &atomRva, - std::vector<uint64_t> §ionRva, - uint64_t imageBaseAddress); - void applyRelocationsX64(uint8_t *buffer, - std::map<const Atom *, uint64_t> &atomRva, - std::vector<uint64_t> §ionRva, - uint64_t imageBaseAddress); - - void printAtomAddresses(uint64_t baseAddr) const; - void addBaseRelocations(std::vector<BaseReloc> &relocSites) const; - - void setVirtualAddress(uint32_t rva) override; - uint64_t getAtomVirtualAddress(StringRef name) const; - - static bool classof(const Chunk *c) { return c->getKind() == kindAtomChunk; } - -protected: - std::vector<AtomLayout *> _atomLayouts; - uint64_t _virtualAddress; - -private: - uint32_t - computeCharacteristics(const PECOFFLinkingContext &ctx, StringRef name, - const std::vector<const DefinedAtom *> &atoms) const { - return ctx.getSectionAttributes(name, - getDefaultCharacteristics(name, atoms)); - } - - uint32_t getDefaultCharacteristics( - StringRef name, const std::vector<const DefinedAtom *> &atoms) const; - - mutable llvm::BumpPtrAllocator _alloc; - llvm::COFF::MachineTypes _machineType; - const PECOFFLinkingContext &_ctx; -}; - -/// A DataDirectoryChunk represents data directory entries that follows the PE -/// header in the output file. An entry consists of an 8 byte field that -/// indicates a relative virtual address (the starting address of the entry data -/// in memory) and 8 byte entry data size. -class DataDirectoryChunk : public HeaderChunk { -public: - DataDirectoryChunk() - : HeaderChunk(), _data(std::vector<llvm::object::data_directory>(16)) {} - - uint64_t size() const override { - return sizeof(llvm::object::data_directory) * _data.size(); - } - - void setField(DataDirectoryIndex index, uint32_t addr, uint32_t size); - void write(uint8_t *buffer) override; - -private: - std::vector<llvm::object::data_directory> _data; -}; - -/// A BaseRelocChunk represents ".reloc" section. -/// -/// .reloc section contains a list of addresses. If the PE/COFF loader decides -/// to load the binary at a memory address different from its preferred base -/// address, which is specified by ImageBase field in the COFF header, the -/// loader needs to relocate the binary, so that all the addresses in the binary -/// point to new locations. The loader will do that by fixing up the addresses -/// specified by .reloc section. -/// -/// The executable is almost always loaded at the preferred base address because -/// it's loaded into an empty address space. The DLL is however an subject of -/// load-time relocation because it may conflict with other DLLs or the -/// executable. -class BaseRelocChunk : public SectionChunk { - typedef std::vector<std::unique_ptr<Chunk> > ChunkVectorT; - -public: - BaseRelocChunk(ChunkVectorT &chunks, const PECOFFLinkingContext &ctx) - : SectionChunk(kindSection, ".reloc", characteristics, ctx), - _ctx(ctx), _contents(createContents(chunks)) {} - - void write(uint8_t *buffer) override { - std::memcpy(buffer, &_contents[0], _contents.size()); - } - - uint64_t size() const override { return _contents.size(); } - -private: - // When loaded into memory, reloc section should be readable and writable. - static const uint32_t characteristics = - llvm::COFF::IMAGE_SCN_MEM_READ | - llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | - llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE; - - std::vector<uint8_t> createContents(ChunkVectorT &chunks) const; - - // Returns a list of RVAs that needs to be relocated if the binary is loaded - // at an address different from its preferred one. - std::vector<BaseReloc> listRelocSites(ChunkVectorT &chunks) const; - - // Create the content of a relocation block. - std::vector<uint8_t> - createBaseRelocBlock(uint64_t pageAddr, const BaseReloc *begin, - const BaseReloc *end) const; - - const PECOFFLinkingContext &_ctx; - std::vector<uint8_t> _contents; -}; - -template <class PEHeader> -PEHeaderChunk<PEHeader>::PEHeaderChunk(const PECOFFLinkingContext &ctx) - : HeaderChunk() { - // Set the size of the chunk and initialize the header with null bytes. - _size = sizeof(llvm::COFF::PEMagic) + sizeof(_coffHeader) + sizeof(_peHeader); - std::memset(&_coffHeader, 0, sizeof(_coffHeader)); - std::memset(&_peHeader, 0, sizeof(_peHeader)); - - _coffHeader.Machine = ctx.getMachineType(); - _coffHeader.TimeDateStamp = time(nullptr); - - // Attributes of the executable. - uint16_t characteristics = llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE; - if (!ctx.is64Bit()) - characteristics |= llvm::COFF::IMAGE_FILE_32BIT_MACHINE; - if (ctx.isDll()) - characteristics |= llvm::COFF::IMAGE_FILE_DLL; - if (ctx.getLargeAddressAware() || ctx.is64Bit()) - characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE; - if (ctx.getSwapRunFromCD()) - characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; - if (ctx.getSwapRunFromNet()) - characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP; - if (!ctx.getBaseRelocationEnabled()) - characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED; - - _coffHeader.Characteristics = characteristics; - - _peHeader.Magic = ctx.is64Bit() ? llvm::COFF::PE32Header::PE32_PLUS - : llvm::COFF::PE32Header::PE32; - - // The address of the executable when loaded into memory. The default for - // DLLs is 0x10000000. The default for executables is 0x400000. - _peHeader.ImageBase = ctx.getBaseAddress(); - - // Sections should be page-aligned when loaded into memory, which is 4KB on - // x86. - _peHeader.SectionAlignment = ctx.getSectionDefaultAlignment(); - - // Sections in an executable file on disk should be sector-aligned (512 byte). - _peHeader.FileAlignment = SECTOR_SIZE; - - // The version number of the resultant executable/DLL. The number is purely - // informative, and neither the linker nor the loader won't use it. User can - // set the value using /version command line option. Default is 0.0. - PECOFFLinkingContext::Version imageVersion = ctx.getImageVersion(); - _peHeader.MajorImageVersion = imageVersion.majorVersion; - _peHeader.MinorImageVersion = imageVersion.minorVersion; - - // The required Windows version number. This is the internal version and - // shouldn't be confused with product name. Windows 7 is version 6.1 and - // Windows 8 is 6.2, for example. - PECOFFLinkingContext::Version minOSVersion = ctx.getMinOSVersion(); - _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion; - _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion; - _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion; - _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion; - - _peHeader.Subsystem = ctx.getSubsystem(); - - // Despite its name, DLL characteristics field has meaning both for - // executables and DLLs. We are not very sure if the following bits must - // be set, but regular binaries seem to have these bits, so we follow - // them. - uint16_t dllCharacteristics = 0; - if (ctx.noSEH()) - dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH; - if (ctx.isTerminalServerAware()) - dllCharacteristics |= - llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; - if (ctx.isNxCompat()) - dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; - if (ctx.getDynamicBaseEnabled()) - dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; - if (!ctx.getAllowBind()) - dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND; - if (!ctx.getAllowIsolation()) - dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; - if (ctx.getHighEntropyVA() && ctx.is64Bit()) - dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; - _peHeader.DLLCharacteristics = dllCharacteristics; - - _peHeader.SizeOfStackReserve = ctx.getStackReserve(); - _peHeader.SizeOfStackCommit = ctx.getStackCommit(); - _peHeader.SizeOfHeapReserve = ctx.getHeapReserve(); - _peHeader.SizeOfHeapCommit = ctx.getHeapCommit(); - - // The number of data directory entries. We always have 16 entries. - _peHeader.NumberOfRvaAndSize = 16; - - // The size of PE header including optional data directory. - _coffHeader.SizeOfOptionalHeader = sizeof(PEHeader) + - _peHeader.NumberOfRvaAndSize * sizeof(llvm::object::data_directory); -} - -template <> -void PEHeaderChunk<llvm::object::pe32_header>::setBaseOfData(uint32_t rva) { - _peHeader.BaseOfData = rva; -} - -template <> -void PEHeaderChunk<llvm::object::pe32plus_header>::setBaseOfData(uint32_t rva) { - // BaseOfData field does not exist in PE32+ header. -} - -template <class PEHeader> -void PEHeaderChunk<PEHeader>::write(uint8_t *buffer) { - std::memcpy(buffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic)); - buffer += sizeof(llvm::COFF::PEMagic); - std::memcpy(buffer, &_coffHeader, sizeof(_coffHeader)); - buffer += sizeof(_coffHeader); - std::memcpy(buffer, &_peHeader, sizeof(_peHeader)); -} - -AtomChunk::AtomChunk(const PECOFFLinkingContext &ctx, StringRef sectionName, - const std::vector<const DefinedAtom *> &atoms) - : SectionChunk(kindAtomChunk, sectionName, - computeCharacteristics(ctx, sectionName, atoms), ctx), - _virtualAddress(0), _machineType(ctx.getMachineType()), _ctx(ctx) { - for (auto *a : atoms) - appendAtom(a); -} - -void AtomChunk::write(uint8_t *buffer) { - if (_atomLayouts.empty()) - return; - if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) - return; - if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_CODE) { - // Fill the section with INT 3 (0xCC) rather than NUL, so that the - // disassembler will not interpret a garbage between atoms as the beginning - // of multi-byte machine code. This does not change the behavior of - // resulting binary but help debugging. - uint8_t *start = buffer + _atomLayouts.front()->_fileOffset; - uint8_t *end = buffer + _atomLayouts.back()->_fileOffset; - memset(start, 0xCC, end - start); - } - - for (const auto *layout : _atomLayouts) { - const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); - ArrayRef<uint8_t> rawContent = atom->rawContent(); - std::memcpy(buffer + layout->_fileOffset, rawContent.data(), - rawContent.size()); - } -} - -// Add all atoms to the given map. This data will be used to do relocation. -void -AtomChunk::buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const { - for (const auto *layout : _atomLayouts) - atomRva[layout->_atom] = layout->_virtualAddr; -} - -static int getSectionIndex(uint64_t targetAddr, - const std::vector<uint64_t> §ionRva) { - int i = 1; - for (uint64_t rva : sectionRva) { - if (targetAddr < rva) - return i; - ++i; - } - return i; -} - -static uint32_t getSectionStartAddr(uint64_t targetAddr, - const std::vector<uint64_t> §ionRva) { - // Scan the list of section start addresses to find the section start address - // for the given RVA. - for (int i = 0, e = sectionRva.size(); i < e; ++i) - if (i == e - 1 || (sectionRva[i] <= targetAddr && targetAddr < sectionRva[i + 1])) - return sectionRva[i]; - llvm_unreachable("Section missing"); -} - -static void applyThumbMoveImmediate(ulittle16_t *mov, uint16_t imm) { - // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8| - // imm32 = zext imm4:i:imm3:imm8 - // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8| - // imm16 = imm4:i:imm3:imm8 - mov[0] = - mov[0] | (((imm & 0x0800) >> 11) << 10) | (((imm & 0xf000) >> 12) << 0); - mov[1] = - mov[1] | (((imm & 0x0700) >> 8) << 12) | (((imm & 0x00ff) >> 0) << 0); -} - -static void applyThumbBranchImmediate(ulittle16_t *bl, int32_t imm) { - // BL(T1): |11110|S|imm10|11|J1|1|J2|imm11| - // imm32 = sext S:I1:I2:imm10:imm11:'0' - // B.W(T4): |11110|S|imm10|10|J1|1|J2|imm11| - // imm32 = sext S:I1:I2:imm10:imm11:'0' - // - // I1 = ~(J1 ^ S), I2 = ~(J2 ^ S) - - assert((~abs(imm) & (-1 << 24)) && "bl/b.w out of range"); - - uint32_t S = (imm < 0 ? 1 : 0); - uint32_t J1 = ((~imm & 0x00800000) >> 23) ^ S; - uint32_t J2 = ((~imm & 0x00400000) >> 22) ^ S; - - bl[0] = bl[0] | (((imm & 0x003ff000) >> 12) << 0) | (S << 10); - bl[1] = bl[1] | (((imm & 0x00000ffe) >> 1) << 0) | (J2 << 11) | (J1 << 13); -} - -void AtomChunk::applyRelocationsARM(uint8_t *Buffer, - std::map<const Atom *, uint64_t> &AtomRVA, - std::vector<uint64_t> &SectionRVA, - uint64_t ImageBase) { - Buffer = Buffer + _fileOffset; - parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(), - [&](const AtomLayout *layout) { - const DefinedAtom *Atom = cast<DefinedAtom>(layout->_atom); - for (const Reference *R : *Atom) { - if (R->kindNamespace() != Reference::KindNamespace::COFF) - continue; - - bool AssumeTHUMBCode = false; - if (auto Target = dyn_cast<DefinedAtom>(R->target())) - AssumeTHUMBCode = Target->permissions() == DefinedAtom::permR_X || - Target->permissions() == DefinedAtom::permRWX; - - const auto AtomOffset = R->offsetInAtom(); - const auto FileOffset = layout->_fileOffset; - const auto TargetAddr = AtomRVA[R->target()] | (AssumeTHUMBCode ? 1 : 0); - auto RelocSite16 = - reinterpret_cast<ulittle16_t *>(Buffer + FileOffset + AtomOffset); - auto RelocSite32 = - reinterpret_cast<ulittle32_t *>(Buffer + FileOffset + AtomOffset); - - switch (R->kindValue()) { - default: llvm_unreachable("unsupported relocation type"); - case llvm::COFF::IMAGE_REL_ARM_ADDR32: - *RelocSite32 = *RelocSite32 + TargetAddr + ImageBase; - break; - case llvm::COFF::IMAGE_REL_ARM_ADDR32NB: - *RelocSite32 = *RelocSite32 + TargetAddr; - break; - case llvm::COFF::IMAGE_REL_ARM_MOV32T: - applyThumbMoveImmediate(&RelocSite16[0], (TargetAddr + ImageBase) >> 0); - applyThumbMoveImmediate(&RelocSite16[2], (TargetAddr + ImageBase) >> 16); - break; - case llvm::COFF::IMAGE_REL_ARM_BRANCH24T: - // NOTE: the thumb bit will implicitly be truncated properly - applyThumbBranchImmediate(RelocSite16, - TargetAddr - AtomRVA[Atom] - AtomOffset - 4); - break; - case llvm::COFF::IMAGE_REL_ARM_BLX23T: - // NOTE: the thumb bit will implicitly be truncated properly - applyThumbBranchImmediate(RelocSite16, - TargetAddr - AtomRVA[Atom] - AtomOffset - 4); - break; - } - } - }); -} - -void AtomChunk::applyRelocationsX86(uint8_t *buffer, - std::map<const Atom *, uint64_t> &atomRva, - std::vector<uint64_t> §ionRva, - uint64_t imageBaseAddress) { - buffer += _fileOffset; - parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(), - [&](const AtomLayout *layout) { - const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); - for (const Reference *ref : *atom) { - // Skip if this reference is not for COFF relocation. - if (ref->kindNamespace() != Reference::KindNamespace::COFF) - continue; - auto relocSite32 = reinterpret_cast<ulittle32_t *>( - buffer + layout->_fileOffset + ref->offsetInAtom()); - auto relocSite16 = reinterpret_cast<ulittle16_t *>(relocSite32); - const Atom *target = ref->target(); - uint64_t targetAddr = atomRva[target]; - // Also account for whatever offset is already stored at the relocation - // site. - switch (ref->kindValue()) { - case llvm::COFF::IMAGE_REL_I386_ABSOLUTE: - // This relocation is no-op. - break; - case llvm::COFF::IMAGE_REL_I386_DIR32: - // Set target's 32-bit VA. - if (auto *abs = dyn_cast<AbsoluteAtom>(target)) - *relocSite32 += abs->value(); - else - *relocSite32 += targetAddr + imageBaseAddress; - break; - case llvm::COFF::IMAGE_REL_I386_DIR32NB: - // Set target's 32-bit RVA. - *relocSite32 += targetAddr; - break; - case llvm::COFF::IMAGE_REL_I386_REL32: { - // Set 32-bit relative address of the target. This relocation is - // usually used for relative branch or call instruction. - uint32_t disp = atomRva[atom] + ref->offsetInAtom() + 4; - *relocSite32 += targetAddr - disp; - break; - } - case llvm::COFF::IMAGE_REL_I386_SECTION: - // The 16-bit section index that contains the target symbol. - *relocSite16 += getSectionIndex(targetAddr, sectionRva); - break; - case llvm::COFF::IMAGE_REL_I386_SECREL: - // The 32-bit relative address from the beginning of the section that - // contains the target symbol. - *relocSite32 += - targetAddr - getSectionStartAddr(targetAddr, sectionRva); - break; - default: - llvm::report_fatal_error("Unsupported relocation kind"); - } - } - }); -} - -void AtomChunk::applyRelocationsX64(uint8_t *buffer, - std::map<const Atom *, uint64_t> &atomRva, - std::vector<uint64_t> §ionRva, - uint64_t imageBase) { - buffer += _fileOffset; - parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(), - [&](const AtomLayout *layout) { - const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); - for (const Reference *ref : *atom) { - if (ref->kindNamespace() != Reference::KindNamespace::COFF) - continue; - - uint8_t *loc = buffer + layout->_fileOffset + ref->offsetInAtom(); - auto relocSite16 = reinterpret_cast<ulittle16_t *>(loc); - auto relocSite32 = reinterpret_cast<ulittle32_t *>(loc); - auto relocSite64 = reinterpret_cast<ulittle64_t *>(loc); - uint64_t targetAddr = atomRva[ref->target()]; - - switch (ref->kindValue()) { - case llvm::COFF::IMAGE_REL_AMD64_ADDR64: - *relocSite64 += targetAddr + imageBase; - break; - case llvm::COFF::IMAGE_REL_AMD64_ADDR32: - *relocSite32 += targetAddr + imageBase; - break; - case llvm::COFF::IMAGE_REL_AMD64_ADDR32NB: - *relocSite32 += targetAddr; - break; - case llvm::COFF::IMAGE_REL_AMD64_REL32: - *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 4; - break; - case llvm::COFF::IMAGE_REL_AMD64_REL32_1: - *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 5; - break; - case llvm::COFF::IMAGE_REL_AMD64_REL32_2: - *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 6; - break; - case llvm::COFF::IMAGE_REL_AMD64_REL32_3: - *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 7; - break; - case llvm::COFF::IMAGE_REL_AMD64_REL32_4: - *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 8; - break; - case llvm::COFF::IMAGE_REL_AMD64_REL32_5: - *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 9; - break; - case llvm::COFF::IMAGE_REL_AMD64_SECTION: - *relocSite16 += getSectionIndex(targetAddr, sectionRva) - 1; - break; - case llvm::COFF::IMAGE_REL_AMD64_SECREL: - *relocSite32 += - targetAddr - getSectionStartAddr(targetAddr, sectionRva); - break; - default: - llvm::errs() << "Kind: " << (int)ref->kindValue() << "\n"; - llvm::report_fatal_error("Unsupported relocation kind"); - } - } - }); -} - -/// Print atom VAs. Used only for debugging. -void AtomChunk::printAtomAddresses(uint64_t baseAddr) const { - for (const auto *layout : _atomLayouts) { - const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); - uint64_t addr = layout->_virtualAddr; - llvm::dbgs() << llvm::format("0x%08llx: ", addr + baseAddr) - << (atom->name().empty() ? "(anonymous)" : atom->name()) - << "\n"; - } -} - -/// List all virtual addresses (and not relative virtual addresses) that need -/// to be fixed up if image base is relocated. The only relocation type that -/// needs to be fixed is DIR32 on i386. REL32 is not (and should not be) -/// fixed up because it's PC-relative. -void AtomChunk::addBaseRelocations(std::vector<BaseReloc> &relocSites) const { - for (const auto *layout : _atomLayouts) { - const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); - for (const Reference *ref : *atom) { - if (ref->kindNamespace() != Reference::KindNamespace::COFF) - continue; - - // An absolute symbol points to a fixed location in memory. Their - // address should not be fixed at load time. One exception is ImageBase - // because that's relative to run-time image base address. - if (auto *abs = dyn_cast<AbsoluteAtom>(ref->target())) - if (!abs->name().equals("__ImageBase") && - !abs->name().equals("___ImageBase")) - continue; - - uint64_t address = layout->_virtualAddr + ref->offsetInAtom(); - switch (_machineType) { - default: llvm_unreachable("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - if (ref->kindValue() == llvm::COFF::IMAGE_REL_I386_DIR32) - relocSites.push_back( - BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW)); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - if (ref->kindValue() == llvm::COFF::IMAGE_REL_AMD64_ADDR64) - relocSites.push_back( - BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_DIR64)); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_ADDR32) - relocSites.push_back( - BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW)); - else if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_MOV32T) - relocSites.push_back( - BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_ARM_MOV32T)); - break; - } - } - } -} - -void AtomChunk::setVirtualAddress(uint32_t rva) { - SectionChunk::setVirtualAddress(rva); - for (AtomLayout *layout : _atomLayouts) - layout->_virtualAddr += rva; -} - -uint64_t AtomChunk::getAtomVirtualAddress(StringRef name) const { - for (auto atomLayout : _atomLayouts) - if (atomLayout->_atom->name() == name) - return atomLayout->_virtualAddr; - return 0; -} - -void DataDirectoryChunk::setField(DataDirectoryIndex index, uint32_t addr, - uint32_t size) { - llvm::object::data_directory &dir = _data[index]; - dir.RelativeVirtualAddress = addr; - dir.Size = size; -} - -void DataDirectoryChunk::write(uint8_t *buffer) { - std::memcpy(buffer, &_data[0], size()); -} - -uint64_t AtomChunk::memAlign() const { - // ReaderCOFF propagated the section alignment to the first atom in - // the section. We restore that here. - if (_atomLayouts.empty()) - return _ctx.getPageSize(); - int align = _ctx.getPageSize(); - for (auto atomLayout : _atomLayouts) { - auto *atom = cast<const DefinedAtom>(atomLayout->_atom); - align = std::max(align, 1 << atom->alignment().powerOf2); - } - return align; -} - -void AtomChunk::appendAtom(const DefinedAtom *atom) { - // Atom may have to be at a proper alignment boundary. If so, move the - // pointer to make a room after the last atom before adding new one. - _size = llvm::RoundUpToAlignment(_size, 1 << atom->alignment().powerOf2); - - // Create an AtomLayout and move the current pointer. - auto *layout = new (_alloc) AtomLayout(atom, _size, _size); - _atomLayouts.push_back(layout); - _size += atom->size(); -} - -uint32_t AtomChunk::getDefaultCharacteristics( - StringRef name, const std::vector<const DefinedAtom *> &atoms) const { - const uint32_t code = llvm::COFF::IMAGE_SCN_CNT_CODE; - const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE; - const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ; - const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE; - const uint32_t data = llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; - const uint32_t bss = llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA; - if (name == ".text") - return code | execute | read; - if (name == ".data") - return data | read | write; - if (name == ".rdata") - return data | read; - if (name == ".bss") - return bss | read | write; - assert(atoms.size() > 0); - switch (atoms[0]->permissions()) { - case DefinedAtom::permR__: - return data | read; - case DefinedAtom::permRW_: - return data | read | write; - case DefinedAtom::permR_X: - return code | execute | read; - case DefinedAtom::permRWX: - return code | execute | read | write; - default: - llvm_unreachable("Unsupported permission"); - } -} - -void SectionHeaderTableChunk::addSection(SectionChunk *chunk) { - _sections.push_back(chunk); -} - -uint64_t SectionHeaderTableChunk::size() const { - return _sections.size() * sizeof(llvm::object::coff_section); -} - -void SectionHeaderTableChunk::write(uint8_t *buffer) { - uint64_t offset = 0; - for (SectionChunk *chunk : _sections) { - llvm::object::coff_section header = createSectionHeader(chunk); - std::memcpy(buffer + offset, &header, sizeof(header)); - offset += sizeof(header); - } -} - -llvm::object::coff_section -SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) { - llvm::object::coff_section header; - - // We have extended the COFF specification by allowing section names to be - // greater than eight characters. We achieve this by adding the section names - // to the string table. Binutils' linker, ld, performs the same trick. - StringRef sectionName = chunk->getSectionName(); - std::memset(header.Name, 0, llvm::COFF::NameSize); - if (uint32_t stringTableOffset = chunk->getStringTableOffset()) - sprintf(header.Name, "/%u", stringTableOffset); - else - std::strncpy(header.Name, sectionName.data(), sectionName.size()); - - uint32_t characteristics = chunk->getCharacteristics(); - header.VirtualSize = chunk->size(); - header.VirtualAddress = chunk->getVirtualAddress(); - header.SizeOfRawData = chunk->onDiskSize(); - header.PointerToRelocations = 0; - header.PointerToLinenumbers = 0; - header.NumberOfRelocations = 0; - header.NumberOfLinenumbers = 0; - header.Characteristics = characteristics; - - if (characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) { - header.PointerToRawData = 0; - } else { - header.PointerToRawData = chunk->fileOffset(); - } - return header; -} - -/// Creates .reloc section content from the other sections. The content of -/// .reloc is basically a list of relocation sites. The relocation sites are -/// divided into blocks. Each block represents the base relocation for a 4K -/// page. -/// -/// By dividing 32 bit RVAs into blocks, COFF saves disk and memory space for -/// the base relocation. A block consists of a 32 bit page RVA and 16 bit -/// relocation entries which represent offsets in the page. That is a more -/// compact representation than a simple vector of 32 bit RVAs. -std::vector<uint8_t> -BaseRelocChunk::createContents(ChunkVectorT &chunks) const { - std::vector<uint8_t> contents; - std::vector<BaseReloc> relocSites = listRelocSites(chunks); - - uint64_t mask = _ctx.getPageSize() - 1; - parallel_sort(relocSites.begin(), relocSites.end(), - [=](const BaseReloc &a, const BaseReloc &b) { - return (a.addr & ~mask) < (b.addr & ~mask); - }); - - // Base relocations for the same memory page are grouped together - // and passed to createBaseRelocBlock. - for (auto it = relocSites.begin(), e = relocSites.end(); it != e;) { - auto beginIt = it; - uint64_t pageAddr = (beginIt->addr & ~mask); - for (++it; it != e; ++it) - if ((it->addr & ~mask) != pageAddr) - break; - const BaseReloc *begin = &*beginIt; - const BaseReloc *end = begin + (it - beginIt); - std::vector<uint8_t> block = createBaseRelocBlock(pageAddr, begin, end); - contents.insert(contents.end(), block.begin(), block.end()); - } - return contents; -} - -// Returns a list of RVAs that needs to be relocated if the binary is loaded -// at an address different from its preferred one. -std::vector<BaseReloc> -BaseRelocChunk::listRelocSites(ChunkVectorT &chunks) const { - std::vector<BaseReloc> ret; - for (auto &cp : chunks) - if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) - chunk->addBaseRelocations(ret); - return ret; -} - -// Create the content of a relocation block. -std::vector<uint8_t> -BaseRelocChunk::createBaseRelocBlock(uint64_t pageAddr, - const BaseReloc *begin, - const BaseReloc *end) const { - // Relocation blocks should be padded with IMAGE_REL_I386_ABSOLUTE to be - // aligned to a DWORD size boundary. - uint32_t size = llvm::RoundUpToAlignment( - sizeof(ulittle32_t) * 2 + sizeof(ulittle16_t) * (end - begin), - sizeof(ulittle32_t)); - std::vector<uint8_t> contents(size); - uint8_t *ptr = &contents[0]; - - // The first four bytes is the page RVA. - write32le(ptr, pageAddr); - ptr += sizeof(ulittle32_t); - - // The second four bytes is the size of the block, including the the page - // RVA and this size field. - write32le(ptr, size); - ptr += sizeof(ulittle32_t); - - uint64_t mask = _ctx.getPageSize() - 1; - for (const BaseReloc *i = begin; i < end; ++i) { - write16le(ptr, (i->type << 12) | (i->addr & mask)); - ptr += sizeof(ulittle16_t); - } - return contents; -} - -} // end anonymous namespace - -class PECOFFWriter : public Writer { -public: - explicit PECOFFWriter(const PECOFFLinkingContext &context) - : _ctx(context), _numSections(0), _imageSizeInMemory(_ctx.getPageSize()), - _imageSizeOnDisk(0) {} - - template <class PEHeader> void build(const File &linkedFile); - std::error_code writeFile(const File &linkedFile, StringRef path) override; - -private: - void applyAllRelocations(uint8_t *bufferStart); - void printAllAtomAddresses() const; - void reorderSEHTableEntries(uint8_t *bufferStart); - void reorderSEHTableEntriesX86(uint8_t *bufferStart); - void reorderSEHTableEntriesX64(uint8_t *bufferStart); - - void addChunk(Chunk *chunk); - void addSectionChunk(std::unique_ptr<SectionChunk> chunk, - SectionHeaderTableChunk *table, - StringTableChunk *stringTable); - void setImageSizeOnDisk(); - uint64_t - calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const; - - uint64_t calcSizeOfInitializedData() const { - return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA); - } - - uint64_t calcSizeOfUninitializedData() const { - return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA); - } - - uint64_t calcSizeOfCode() const { - return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_CODE); - } - - std::vector<std::unique_ptr<Chunk> > _chunks; - const PECOFFLinkingContext &_ctx; - uint32_t _numSections; - - // The size of the image in memory. This is initialized with - // _ctx.getPageSize(), as the first page starting at ImageBase is usually left - // unmapped. IIUC there's no technical reason to do so, but we'll follow that - // convention so that we don't produce odd-looking binary. - uint32_t _imageSizeInMemory; - - // The size of the image on disk. This is basically the sum of all chunks in - // the output file with paddings between them. - uint32_t _imageSizeOnDisk; - - // The map from atom to its relative virtual address. - std::map<const Atom *, uint64_t> _atomRva; -}; - -StringRef customSectionName(const DefinedAtom *atom) { - assert(atom->sectionChoice() == DefinedAtom::sectionCustomRequired); - StringRef s = atom->customSectionName(); - size_t pos = s.find('$'); - return (pos == StringRef::npos) ? s : s.substr(0, pos); -} - -StringRef chooseSectionByContent(const DefinedAtom *atom) { - switch (atom->contentType()) { - case DefinedAtom::typeCode: - return ".text"; - case DefinedAtom::typeZeroFill: - return ".bss"; - case DefinedAtom::typeData: - if (atom->permissions() == DefinedAtom::permR__) - return ".rdata"; - if (atom->permissions() == DefinedAtom::permRW_) - return ".data"; - break; - default: - break; - } - llvm::errs() << "Atom: contentType=" << atom->contentType() - << " permission=" << atom->permissions() << "\n"; - llvm::report_fatal_error("Failed to choose section based on content"); -} - -typedef std::map<StringRef, std::vector<const DefinedAtom *> > AtomVectorMap; - -void groupAtoms(const PECOFFLinkingContext &ctx, const File &file, - AtomVectorMap &result) { - for (const DefinedAtom *atom : file.defined()) { - if (atom->sectionChoice() == DefinedAtom::sectionCustomRequired) { - StringRef section = customSectionName(atom); - result[ctx.getOutputSectionName(section)].push_back(atom); - continue; - } - if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) { - StringRef section = chooseSectionByContent(atom); - result[ctx.getOutputSectionName(section)].push_back(atom); - continue; - } - llvm_unreachable("Unknown section choice"); - } -} - -static const DefinedAtom *findTLSUsedSymbol(const PECOFFLinkingContext &ctx, - const File &file) { - StringRef sym = ctx.decorateSymbol("_tls_used"); - for (const DefinedAtom *atom : file.defined()) - if (atom->name() == sym) - return atom; - return nullptr; -} - -// Create all chunks that consist of the output file. -template <class PEHeader> -void PECOFFWriter::build(const File &linkedFile) { - AtomVectorMap atoms; - groupAtoms(_ctx, linkedFile, atoms); - - // Create file chunks and add them to the list. - auto *dosStub = new DOSStubChunk(_ctx); - auto *peHeader = new PEHeaderChunk<PEHeader>(_ctx); - auto *dataDirectory = new DataDirectoryChunk(); - auto *sectionTable = new SectionHeaderTableChunk(); - auto *stringTable = new StringTableChunk(); - addChunk(dosStub); - addChunk(peHeader); - addChunk(dataDirectory); - addChunk(sectionTable); - addChunk(stringTable); - - // Create sections and add the atoms to them. - for (auto i : atoms) { - StringRef sectionName = i.first; - std::vector<const DefinedAtom *> &contents = i.second; - std::unique_ptr<SectionChunk> section( - new AtomChunk(_ctx, sectionName, contents)); - if (section->size() > 0) - addSectionChunk(std::move(section), sectionTable, stringTable); - } - - // Build atom to its RVA map. - for (std::unique_ptr<Chunk> &cp : _chunks) - if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) - chunk->buildAtomRvaMap(_atomRva); - - // We know the addresses of all defined atoms that needs to be - // relocated. So we can create the ".reloc" section which contains - // all the relocation sites. - if (_ctx.getBaseRelocationEnabled()) { - std::unique_ptr<SectionChunk> baseReloc(new BaseRelocChunk(_chunks, _ctx)); - if (baseReloc->size()) { - SectionChunk &ref = *baseReloc; - addSectionChunk(std::move(baseReloc), sectionTable, stringTable); - dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE, - ref.getVirtualAddress(), ref.size()); - } - } - - setImageSizeOnDisk(); - - if (stringTable->size()) { - peHeader->setPointerToSymbolTable(stringTable->fileOffset()); - peHeader->setNumberOfSymbols(1); - } - - for (std::unique_ptr<Chunk> &chunk : _chunks) { - SectionChunk *section = dyn_cast<SectionChunk>(chunk.get()); - if (!section) - continue; - if (section->getSectionName() == ".text") { - peHeader->setBaseOfCode(section->getVirtualAddress()); - - // Find the virtual address of the entry point symbol if any. PECOFF spec - // says that entry point for dll images is optional, in which case it must - // be set to 0. - if (_ctx.hasEntry()) { - AtomChunk *atom = cast<AtomChunk>(section); - uint64_t entryPointAddress = - atom->getAtomVirtualAddress(_ctx.getEntrySymbolName()); - - if (entryPointAddress) { - // NOTE: ARM NT assumes a pure THUMB execution, so adjust the entry - // point accordingly - if (_ctx.getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_ARMNT) - entryPointAddress |= 1; - peHeader->setAddressOfEntryPoint(entryPointAddress); - } - } else { - peHeader->setAddressOfEntryPoint(0); - } - } - StringRef name = section->getSectionName(); - if (name == ".data") { - peHeader->setBaseOfData(section->getVirtualAddress()); - continue; - } - DataDirectoryIndex ignore = DataDirectoryIndex(-1); - DataDirectoryIndex idx = llvm::StringSwitch<DataDirectoryIndex>(name) - .Case(".pdata", DataDirectoryIndex::EXCEPTION_TABLE) - .Case(".rsrc", DataDirectoryIndex::RESOURCE_TABLE) - .Case(".idata.a", DataDirectoryIndex::IAT) - .Case(".idata.d", DataDirectoryIndex::IMPORT_TABLE) - .Case(".edata", DataDirectoryIndex::EXPORT_TABLE) - .Case(".loadcfg", DataDirectoryIndex::LOAD_CONFIG_TABLE) - .Case(".didat.d", DataDirectoryIndex::DELAY_IMPORT_DESCRIPTOR) - .Default(ignore); - if (idx == ignore) - continue; - dataDirectory->setField(idx, section->getVirtualAddress(), section->size()); - } - - if (const DefinedAtom *atom = findTLSUsedSymbol(_ctx, linkedFile)) { - dataDirectory->setField(DataDirectoryIndex::TLS_TABLE, _atomRva[atom], - 0x18); - } - - // Now that we know the size and file offset of sections. Set the file - // header accordingly. - peHeader->setSizeOfCode(calcSizeOfCode()); - peHeader->setSizeOfInitializedData(calcSizeOfInitializedData()); - peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData()); - peHeader->setNumberOfSections(_numSections); - peHeader->setSizeOfImage(_imageSizeInMemory); - peHeader->setSizeOfHeaders(sectionTable->fileOffset() + sectionTable->size()); -} - -std::error_code PECOFFWriter::writeFile(const File &linkedFile, - StringRef path) { - if (_ctx.is64Bit()) { - this->build<llvm::object::pe32plus_header>(linkedFile); - } else { - this->build<llvm::object::pe32_header>(linkedFile); - } - - uint64_t totalSize = - _chunks.back()->fileOffset() + _chunks.back()->onDiskSize(); - std::unique_ptr<llvm::FileOutputBuffer> buffer; - std::error_code ec = llvm::FileOutputBuffer::create( - path, totalSize, buffer, llvm::FileOutputBuffer::F_executable); - if (ec) - return ec; - - for (std::unique_ptr<Chunk> &chunk : _chunks) - chunk->write(buffer->getBufferStart() + chunk->fileOffset()); - applyAllRelocations(buffer->getBufferStart()); - reorderSEHTableEntries(buffer->getBufferStart()); - DEBUG(printAllAtomAddresses()); - - if (_ctx.isDll()) - writeImportLibrary(_ctx); - - return buffer->commit(); -} - -/// Apply relocations to the output file buffer. This two pass. In the first -/// pass, we visit all atoms to create a map from atom to its virtual -/// address. In the second pass, we visit all relocation references to fix -/// up addresses in the buffer. -void PECOFFWriter::applyAllRelocations(uint8_t *bufferStart) { - // Create the list of section start addresses. It's needed for - // relocations of SECREL type. - std::vector<uint64_t> sectionRva; - for (auto &cp : _chunks) - if (SectionChunk *section = dyn_cast<SectionChunk>(&*cp)) - sectionRva.push_back(section->getVirtualAddress()); - - uint64_t base = _ctx.getBaseAddress(); - for (auto &cp : _chunks) { - if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) { - switch (_ctx.getMachineType()) { - default: llvm_unreachable("unsupported machine type"); - case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: - chunk->applyRelocationsARM(bufferStart, _atomRva, sectionRva, base); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_I386: - chunk->applyRelocationsX86(bufferStart, _atomRva, sectionRva, base); - break; - case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: - chunk->applyRelocationsX64(bufferStart, _atomRva, sectionRva, base); - break; - } - } - } -} - -/// Print atom VAs. Used only for debugging. -void PECOFFWriter::printAllAtomAddresses() const { - for (auto &cp : _chunks) - if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) - chunk->printAtomAddresses(_ctx.getBaseAddress()); -} - -void PECOFFWriter::reorderSEHTableEntries(uint8_t *bufferStart) { - auto machineType = _ctx.getMachineType(); - if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_I386) - reorderSEHTableEntriesX86(bufferStart); - if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_AMD64) - reorderSEHTableEntriesX64(bufferStart); -} - -/// It seems that the entries in .sxdata must be sorted. This function is called -/// after a COFF file image is created in memory and before it is written to -/// disk. It is safe to reorder entries at this stage because the contents of -/// the entries are RVAs and there's no reference to a .sxdata entry other than -/// to the beginning of the section. -void PECOFFWriter::reorderSEHTableEntriesX86(uint8_t *bufferStart) { - for (std::unique_ptr<Chunk> &chunk : _chunks) { - if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) { - if (section->getSectionName() == ".sxdata") { - int numEntries = section->size() / sizeof(ulittle32_t); - ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(bufferStart + section->fileOffset()); - ulittle32_t *end = begin + numEntries; - std::sort(begin, end); - } - } - } -} - -/// The entries in .pdata must be sorted according to its BeginAddress field -/// value. It's safe to do it because of the same reason as .sxdata. -void PECOFFWriter::reorderSEHTableEntriesX64(uint8_t *bufferStart) { - for (std::unique_ptr<Chunk> &chunk : _chunks) { - if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) { - if (section->getSectionName() != ".pdata") - continue; - int numEntries = section->size() / sizeof(coff_runtime_function_x64); - coff_runtime_function_x64 *begin = - (coff_runtime_function_x64 *)(bufferStart + section->fileOffset()); - coff_runtime_function_x64 *end = begin + numEntries; - std::sort(begin, end, [](const coff_runtime_function_x64 &lhs, - const coff_runtime_function_x64 &rhs) { - return lhs.BeginAddress < rhs.BeginAddress; - }); - } - } -} - -void PECOFFWriter::addChunk(Chunk *chunk) { - _chunks.push_back(std::unique_ptr<Chunk>(chunk)); -} - -void PECOFFWriter::addSectionChunk(std::unique_ptr<SectionChunk> chunk, - SectionHeaderTableChunk *table, - StringTableChunk *stringTable) { - table->addSection(chunk.get()); - _numSections++; - - StringRef sectionName = chunk->getSectionName(); - if (sectionName.size() > llvm::COFF::NameSize) { - uint32_t stringTableOffset = stringTable->addSectionName(sectionName); - chunk->setStringTableOffset(stringTableOffset); - } - - // Compute and set the starting address of sections when loaded in - // memory. They are different from positions on disk because sections need - // to be sector-aligned on disk but page-aligned in memory. - _imageSizeInMemory = llvm::RoundUpToAlignment( - _imageSizeInMemory, chunk->memAlign()); - chunk->setVirtualAddress(_imageSizeInMemory); - _imageSizeInMemory = llvm::RoundUpToAlignment( - _imageSizeInMemory + chunk->size(), _ctx.getPageSize()); - _chunks.push_back(std::move(chunk)); -} - -void PECOFFWriter::setImageSizeOnDisk() { - for (auto &chunk : _chunks) { - // Compute and set the offset of the chunk in the output file. - _imageSizeOnDisk = - llvm::RoundUpToAlignment(_imageSizeOnDisk, chunk->align()); - chunk->setFileOffset(_imageSizeOnDisk); - _imageSizeOnDisk += chunk->onDiskSize(); - } -} - -uint64_t PECOFFWriter::calcSectionSize( - llvm::COFF::SectionCharacteristics sectionType) const { - uint64_t ret = 0; - for (auto &cp : _chunks) - if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp)) - if (chunk->getCharacteristics() & sectionType) - ret += chunk->onDiskSize(); - return ret; -} - -} // end namespace pecoff - -std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &info) { - return std::unique_ptr<Writer>(new pecoff::PECOFFWriter(info)); -} - -} // end namespace lld diff --git a/lib/ReaderWriter/YAML/Makefile b/lib/ReaderWriter/YAML/Makefile deleted file mode 100644 index 739b6eae747a..000000000000 --- a/lib/ReaderWriter/YAML/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lld/lib/ReaderWriter/YAML/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 := lldYAML -USEDLIBS = lldCore.a - -include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp index 868b9497c4cc..78c6797b713f 100644 --- a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include <memory> @@ -214,30 +215,14 @@ private: NameToAtom _groupMap; }; -// Used in NormalizedFile to hold the atoms lists. -template <typename T> class AtomList : public lld::File::atom_collection<T> { +/// Mapping of Atoms. +template <typename T> class AtomList { + typedef lld::File::AtomVector<T> Ty; + public: - virtual lld::File::atom_iterator<T> begin() const { - return lld::File::atom_iterator<T>( - *this, - _atoms.empty() ? 0 : reinterpret_cast<const void *>(_atoms.data())); - } - virtual lld::File::atom_iterator<T> end() const { - return lld::File::atom_iterator<T>( - *this, _atoms.empty() ? 0 : reinterpret_cast<const void *>( - _atoms.data() + _atoms.size())); - } - virtual const T *deref(const void *it) const { - return *reinterpret_cast<const T *const *>(it); - } - virtual void next(const void *&it) const { - const T *const *p = reinterpret_cast<const T *const *>(it); - ++p; - it = reinterpret_cast<const void *>(p); - } - virtual void push_back(const T *element) { _atoms.push_back(element); } - virtual uint64_t size() const { return _atoms.size(); } - std::vector<const T *> _atoms; + typename Ty::iterator begin() { return _atoms.begin(); } + typename Ty::iterator end() { return _atoms.end(); } + Ty _atoms; }; /// Mapping of kind: field in yaml files. @@ -270,7 +255,7 @@ struct RefKind { Reference::KindValue value; }; -} // namespace anon +} // anonymous namespace LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember) LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *) @@ -377,6 +362,9 @@ template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> { io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC); io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16); io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb); + io.enumCase(value, "arm-a", lld::DefinedAtom::codeARM_a); + io.enumCase(value, "arm-d", lld::DefinedAtom::codeARM_d); + io.enumCase(value, "arm-t", lld::DefinedAtom::codeARM_t); } }; @@ -449,6 +437,7 @@ template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> { io.enumCase(value, "no-alloc", DefinedAtom::typeNoAlloc); io.enumCase(value, "group-comdat", DefinedAtom::typeGroupComdat); io.enumCase(value, "gnu-linkonce", DefinedAtom::typeGnuLinkOnce); + io.enumCase(value, "sectcreate", DefinedAtom::typeSectCreate); } }; @@ -478,15 +467,15 @@ struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> { /// This is a custom formatter for lld::DefinedAtom::Alignment. Values look /// like: -/// 2^3 # 8-byte aligned -/// 7 mod 2^4 # 16-byte aligned plus 7 bytes +/// 8 # 8-byte aligned +/// 7 mod 16 # 16-byte aligned plus 7 bytes template <> struct ScalarTraits<lld::DefinedAtom::Alignment> { static void output(const lld::DefinedAtom::Alignment &value, void *ctxt, raw_ostream &out) { if (value.modulus == 0) { - out << llvm::format("2^%d", value.powerOf2); + out << llvm::format("%d", value.value); } else { - out << llvm::format("%d mod 2^%d", value.modulus, value.powerOf2); + out << llvm::format("%d mod %d", value.modulus, value.value); } } @@ -505,16 +494,12 @@ template <> struct ScalarTraits<lld::DefinedAtom::Alignment> { scalar = scalar.drop_front(modStart + 3); scalar = scalar.ltrim(); } - if (!scalar.startswith("2^")) { - return "malformed alignment"; - } - StringRef powerStr = scalar.drop_front(2); unsigned int power; - if (powerStr.getAsInteger(0, power)) { + if (scalar.getAsInteger(0, power)) { return "malformed alignment power"; } - value.powerOf2 = power; - if (value.modulus > (1 << value.powerOf2)) { + value.value = power; + if (value.modulus >= power) { return "malformed alignment, modulus too large for power"; } return StringRef(); // returning empty string means success @@ -598,19 +583,23 @@ template <> struct MappingTraits<const lld::File *> { const lld::File *denormalize(IO &io) { return this; } - const atom_collection<lld::DefinedAtom> &defined() const override { + const AtomVector<lld::DefinedAtom> &defined() const override { return _noDefinedAtoms; } - const atom_collection<lld::UndefinedAtom> &undefined() const override { + + const AtomVector<lld::UndefinedAtom> &undefined() const override { return _noUndefinedAtoms; } - virtual const atom_collection<lld::SharedLibraryAtom> & + + const AtomVector<lld::SharedLibraryAtom> & sharedLibrary() const override { return _noSharedLibraryAtoms; } - const atom_collection<lld::AbsoluteAtom> &absolute() const override { + + const AtomVector<lld::AbsoluteAtom> &absolute() const override { return _noAbsoluteAtoms; } + File *find(StringRef name, bool dataSymbolOnly) override { for (const ArchMember &member : _members) { for (const lld::DefinedAtom *atom : member._content->defined()) { @@ -630,7 +619,7 @@ template <> struct MappingTraits<const lld::File *> { return nullptr; } - virtual std::error_code + std::error_code parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { return std::error_code(); } @@ -646,28 +635,31 @@ template <> struct MappingTraits<const lld::File *> { : File(file->path(), kindObject), _io(io), _rnb(new RefNameBuilder(*file)), _path(file->path()) { for (const lld::DefinedAtom *a : file->defined()) - _definedAtoms.push_back(a); + _definedAtoms._atoms.push_back(a); for (const lld::UndefinedAtom *a : file->undefined()) - _undefinedAtoms.push_back(a); + _undefinedAtoms._atoms.push_back(a); for (const lld::SharedLibraryAtom *a : file->sharedLibrary()) - _sharedLibraryAtoms.push_back(a); + _sharedLibraryAtoms._atoms.push_back(a); for (const lld::AbsoluteAtom *a : file->absolute()) - _absoluteAtoms.push_back(a); + _absoluteAtoms._atoms.push_back(a); } const lld::File *denormalize(IO &io); - const atom_collection<lld::DefinedAtom> &defined() const override { - return _definedAtoms; + const AtomVector<lld::DefinedAtom> &defined() const override { + return _definedAtoms._atoms; } - const atom_collection<lld::UndefinedAtom> &undefined() const override { - return _undefinedAtoms; + + const AtomVector<lld::UndefinedAtom> &undefined() const override { + return _undefinedAtoms._atoms; } - virtual const atom_collection<lld::SharedLibraryAtom> & + + const AtomVector<lld::SharedLibraryAtom> & sharedLibrary() const override { - return _sharedLibraryAtoms; + return _sharedLibraryAtoms._atoms; } - const atom_collection<lld::AbsoluteAtom> &absolute() const override { - return _absoluteAtoms; + + const AtomVector<lld::AbsoluteAtom> &absolute() const override { + return _absoluteAtoms._atoms; } // Allocate a new copy of this string in _storage, so the strings @@ -795,7 +787,7 @@ template <> struct MappingTraits<const lld::DefinedAtom *> { public: NormalizedAtom(IO &io) : _file(fileFromContext(io)), _name(), _refName(), _contentType(), - _alignment(0), _content(), _references(), _isGroupChild(false) { + _alignment(1), _content(), _references(), _isGroupChild(false) { static uint32_t ordinalCounter = 1; _ordinal = ordinalCounter++; } @@ -941,7 +933,7 @@ template <> struct MappingTraits<const lld::DefinedAtom *> { DefinedAtom::interposeNo); io.mapOptional("merge", keys->_merge, DefinedAtom::mergeNo); io.mapOptional("alignment", keys->_alignment, - DefinedAtom::Alignment(0)); + DefinedAtom::Alignment(1)); io.mapOptional("section-choice", keys->_sectionChoice, DefinedAtom::sectionBasedOnContent); io.mapOptional("section-name", keys->_sectionName, StringRef()); @@ -1246,7 +1238,7 @@ namespace yaml { class Writer : public lld::Writer { public: - Writer(const LinkingContext &context) : _context(context) {} + Writer(const LinkingContext &context) : _ctx(context) {} std::error_code writeFile(const lld::File &file, StringRef outPath) override { // Create stream to path. @@ -1257,8 +1249,8 @@ public: // Create yaml Output writer, using yaml options for context. YamlContext yamlContext; - yamlContext._linkingContext = &_context; - yamlContext._registry = &_context.registry(); + yamlContext._ctx = &_ctx; + yamlContext._registry = &_ctx.registry(); llvm::yaml::Output yout(out, &yamlContext); // Write yaml output. @@ -1269,7 +1261,7 @@ public: } private: - const LinkingContext &_context; + const LinkingContext &_ctx; }; } // end namespace yaml @@ -1287,7 +1279,6 @@ class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { } }; - /// Handles !archive tagged yaml documents. class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override { @@ -1299,19 +1290,18 @@ class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { } }; - - class YAMLReader : public Reader { public: YAMLReader(const Registry ®istry) : _registry(registry) {} - bool canParse(file_magic, StringRef ext, const MemoryBuffer &) const override { - return (ext.equals(".objtxt") || ext.equals(".yaml")); + bool canParse(file_magic magic, MemoryBufferRef mb) const override { + StringRef name = mb.getBufferIdentifier(); + return name.endswith(".objtxt") || name.endswith(".yaml"); } - std::error_code - loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, - std::vector<std::unique_ptr<File>> &result) const override { + ErrorOr<std::unique_ptr<File>> + loadFile(std::unique_ptr<MemoryBuffer> mb, + const class Registry &) const override { // Create YAML Input Reader. YamlContext yamlContext; yamlContext._registry = &_registry; @@ -1321,20 +1311,19 @@ public: // Fill vector with File objects created by parsing yaml. std::vector<const lld::File *> createdFiles; yin >> createdFiles; + assert(createdFiles.size() == 1); // Error out now if there were parsing errors. if (yin.error()) return make_error_code(lld::YamlReaderError::illegal_value); std::shared_ptr<MemoryBuffer> smb(mb.release()); - for (const File *file : createdFiles) { - // Note: loadFile() should return vector of *const* File - File *f = const_cast<File *>(file); - f->setLastError(std::error_code()); - f->setSharedMemoryBuffer(smb); - result.emplace_back(f); - } - return make_error_code(lld::YamlReaderError::success); + const File *file = createdFiles[0]; + // Note: loadFile() should return vector of *const* File + File *f = const_cast<File *>(file); + f->setLastError(std::error_code()); + f->setSharedMemoryBuffer(smb); + return std::unique_ptr<File>(f); } private: |