Index: include/lld/Core/DefinedAtom.h =================================================================== --- include/lld/Core/DefinedAtom.h +++ include/lld/Core/DefinedAtom.h @@ -228,6 +228,13 @@ /// refer items in a DLL. virtual uint64_t ordinal() const = 0; + /// \brief returns the linker script rule id that is associeted with this atom + /// + /// This is used to enforce atom order as imposed by a linker script. Each + /// linker script mapping rule gets assigned a rule id. A lower id means + /// higher precedence when sorting atoms. + virtual uint32_t ruleId() const { return 0; } + /// \brief the number of bytes of space this atom's content will occupy in the /// final linked image. /// Index: include/lld/Core/Error.h =================================================================== --- include/lld/Core/Error.h +++ include/lld/Core/Error.h @@ -51,7 +51,9 @@ enum class LinkerScriptReaderError { success = 0, - parse_error + parse_error, + unknown_symbol_in_expr, + unrecognized_function_in_expr }; inline std::error_code make_error_code(LinkerScriptReaderError e) { Index: include/lld/ReaderWriter/ELFLinkingContext.h =================================================================== --- include/lld/ReaderWriter/ELFLinkingContext.h +++ include/lld/ReaderWriter/ELFLinkingContext.h @@ -295,17 +295,13 @@ bool collectStats() const { return _collectStats; } void setCollectStats(bool s) { _collectStats = s; } - // We can parse several linker scripts via command line whose ASTs are stored - // in the current linking context via addLinkerScript(). - void addLinkerScript(std::unique_ptr script) { - _scripts.push_back(std::move(script)); - } - // --wrap option. void addWrapForSymbol(StringRef sym) { _wrapCalls.insert(sym); } const llvm::StringSet<> &wrapCalls() const { return _wrapCalls; } + script::Sema &linkerScriptSema() { return _linkerScriptSema; } + private: ELFLinkingContext() = delete; @@ -349,7 +345,10 @@ llvm::StringSet<> _wrapCalls; std::map _absoluteSymbols; llvm::StringSet<> _dynamicallyExportedSymbols; - std::vector> _scripts; + + // The linker script semantic object, which owns all script ASTs, is stored + // in the current linking context via _linkerScriptSema. + script::Sema _linkerScriptSema; }; } // end namespace lld Index: include/lld/ReaderWriter/LinkerScript.h =================================================================== --- include/lld/ReaderWriter/LinkerScript.h +++ include/lld/ReaderWriter/LinkerScript.h @@ -18,6 +18,8 @@ #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" #include "lld/Core/range.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/MemoryBuffer.h" @@ -25,6 +27,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include #include namespace lld { @@ -153,6 +156,7 @@ Group, Input, InputSectionsCmd, + InputSectionName, Output, OutputArch, OutputFormat, @@ -160,6 +164,7 @@ Overlay, SearchDir, Sections, + SortedGroup, SymbolAssignment, }; @@ -360,11 +365,16 @@ /// class Expression { public: + // The symbol table does not need to own its string keys and the use of StringMap + // here is an overkill. + typedef llvm::StringMap SymbolTableTy; + enum class Kind { Constant, Symbol, FunctionCall, Unary, BinOp, TernaryConditional }; Kind getKind() const { return _kind; } inline llvm::BumpPtrAllocator &getAllocator() const; virtual void dump(raw_ostream &os) const = 0; + virtual ErrorOr evalExpr(SymbolTableTy &symbolTable) const = 0; virtual ~Expression() {} protected: @@ -388,6 +398,10 @@ return c->getKind() == Kind::Constant; } + ErrorOr evalExpr(SymbolTableTy &symbolTable) const override { + return _num; + } + private: uint64_t _num; }; @@ -402,6 +416,12 @@ return c->getKind() == Kind::Symbol; } + ErrorOr evalExpr(SymbolTableTy &symbolTable) const override { + if (symbolTable.count(_name) == 0) + return LinkerScriptReaderError::unknown_symbol_in_expr; + return symbolTable[_name]; + } + private: StringRef _name; }; @@ -424,6 +444,10 @@ return c->getKind() == Kind::FunctionCall; } + ErrorOr evalExpr(SymbolTableTy &symbolTable) const override { + return LinkerScriptReaderError::unrecognized_function_in_expr; + } + private: StringRef _name; llvm::ArrayRef _args; @@ -444,6 +468,20 @@ return c->getKind() == Kind::Unary; } + ErrorOr evalExpr(SymbolTableTy &symbolTable) const override { + auto child = _child->evalExpr(symbolTable); + if (child.getError()) + return child.getError(); + + int64_t childRes = *child; + switch(_op) { + case Unary::Minus: + return 0 - childRes; + case Unary::Not: + return ~childRes; + } + } + private: Operation _op; const Expression *_child; @@ -477,6 +515,35 @@ return c->getKind() == Kind::BinOp; } + ErrorOr evalExpr(SymbolTableTy &symbolTable) const override { + auto lhs = _lhs->evalExpr(symbolTable); + if (lhs.getError()) + return lhs.getError(); + auto rhs = _rhs->evalExpr(symbolTable); + if (rhs.getError()) + return rhs.getError(); + + int64_t lhsRes = *lhs; + int64_t rhsRes = *rhs; + + switch(_op) { + case And: return lhsRes & rhsRes; + case CompareDifferent: return lhsRes != rhsRes; + case CompareEqual: return lhsRes == rhsRes; + case CompareGreater: return lhsRes > rhsRes; + case CompareGreaterEqual: return lhsRes >= rhsRes; + case CompareLess: return lhsRes < rhsRes; + case CompareLessEqual: return lhsRes <= rhsRes; + case Div: return lhsRes / rhsRes; + case Mul: return lhsRes * rhsRes; + case Or: return lhsRes | rhsRes; + case Shl: return lhsRes << rhsRes; + case Shr: return lhsRes >> rhsRes; + case Sub: return lhsRes - rhsRes; + case Sum: return lhsRes + rhsRes; + } + } + private: Operation _op; const Expression *_lhs; @@ -510,6 +577,26 @@ return c->getKind() == Kind::TernaryConditional; } + ErrorOr evalExpr(SymbolTableTy &symbolTable) const override { + auto conditional = _conditional->evalExpr(symbolTable); + if (conditional.getError()) + return conditional.getError(); + auto trueExpr = _trueExpr->evalExpr(symbolTable); + if (trueExpr.getError()) + return trueExpr.getError(); + auto falseExpr = _falseExpr->evalExpr(symbolTable); + if (falseExpr.getError()) + return falseExpr.getError(); + + int64_t conditionalRes = *conditional; + int64_t trueExprRes = *trueExpr; + int64_t falseExprRes = *falseExpr; + if (conditionalRes) + return trueExprRes; + else + return falseExprRes; + } + private: const Expression *_conditional; const Expression *_trueExpr; @@ -542,6 +629,8 @@ } void dump(raw_ostream &os) const override; + const Expression *expr() const { return _expression; } + StringRef symbol() const { return _symbol; } private: const Expression *_expression; @@ -574,23 +663,15 @@ /// .y: { *(SORT(.text*)) } /// /* ^~~~~~~~~~~^ InputSectionSortedGroup : InputSection */ /// } -class InputSection { +class InputSection : public Command { public: - enum class Kind { InputSectionName, SortedGroup }; - - Kind getKind() const { return _kind; } - inline llvm::BumpPtrAllocator &getAllocator() const; - - virtual void dump(raw_ostream &os) const = 0; - - virtual ~InputSection() {} + static bool classof(const Command *c) { + return c->getKind() == Kind::InputSectionName + || c->getKind() == Kind::SortedGroup; + } protected: - InputSection(Parser &ctx, Kind k) : _ctx(ctx), _kind(k) {} - -private: - Parser &_ctx; - Kind _kind; + InputSection(Parser &ctx, Kind k) : Command(ctx, k) {} }; class InputSectionName : public InputSection { @@ -601,10 +682,11 @@ void dump(raw_ostream &os) const override; - static bool classof(const InputSection *c) { + static bool classof(const Command *c) { return c->getKind() == Kind::InputSectionName; } bool hasExcludeFile() const { return _excludeFile; } + StringRef name() const { return _name; } private: StringRef _name; @@ -613,6 +695,8 @@ class InputSectionSortedGroup : public InputSection { public: + typedef llvm::ArrayRef::const_iterator const_iterator; + InputSectionSortedGroup(Parser &ctx, WildcardSortMode sort, const SmallVectorImpl §ions) : InputSection(ctx, Kind::SortedGroup), _sortMode(sort) { @@ -626,10 +710,13 @@ void dump(raw_ostream &os) const override; WildcardSortMode getSortMode() const { return _sortMode; } - static bool classof(const InputSection *c) { + static bool classof(const Command *c) { return c->getKind() == Kind::SortedGroup; } + const_iterator begin() const { return _sections.begin(); } + const_iterator end() const { return _sections.end(); } + private: WildcardSortMode _sortMode; llvm::ArrayRef _sections; @@ -648,6 +735,7 @@ class InputSectionsCmd : public Command { public: typedef std::vector VectorTy; + typedef llvm::ArrayRef::const_iterator const_iterator; InputSectionsCmd(Parser &ctx, StringRef fileName, StringRef archiveName, bool keep, WildcardSortMode fileSortMode, @@ -669,6 +757,11 @@ return c->getKind() == Kind::InputSectionsCmd; } + StringRef fileName() const { return _fileName; } + StringRef archiveName() const { return _archiveName; } + const_iterator begin() const { return _sections.begin(); } + const_iterator end() const { return _sections.end(); } + private: StringRef _fileName; StringRef _archiveName; @@ -694,6 +787,8 @@ public: enum Constraint { C_None, C_OnlyIfRO, C_OnlyIfRW }; + typedef llvm::ArrayRef::const_iterator const_iterator; + OutputSectionDescription( Parser &ctx, StringRef sectionName, const Expression *address, const Expression *align, const Expression *subAlign, const Expression *at, @@ -719,6 +814,10 @@ void dump(raw_ostream &os) const override; + const_iterator begin() const { return _outputSectionCommands.begin(); } + const_iterator end() const { return _outputSectionCommands.end(); } + StringRef name() const { return _sectionName; } + private: StringRef _sectionName; const Expression *_address; @@ -749,6 +848,8 @@ /// Represents all the contents of the SECTIONS {} construct. class Sections : public Command { public: + typedef llvm::ArrayRef::const_iterator const_iterator; + Sections(Parser &ctx, const SmallVectorImpl §ionsCommands) : Command(ctx, Kind::Sections) { @@ -766,6 +867,8 @@ void dump(raw_ostream &os) const override; + const_iterator begin() const { return _sectionsCommands.begin(); } + const_iterator end() const { return _sectionsCommands.end(); } private: llvm::ArrayRef _sectionsCommands; }; @@ -1055,15 +1158,257 @@ Token _bufferedToken; }; +/// script::Sema traverses all parsed linker script structures and assigns an +/// increasing "rule id" to each input-to-output mapping construct. +/// This includes: +/// +/// * OutputSectionDescription, containing an output section name +/// * InputSectionsCmd, containing an input file name +/// * InputSectionName, containing a single input section name +/// * InputSectionSortedName, a group of input section names +/// * AssignmentExpression, containing an expression that may +/// change the address where the linker is outputting data +/// +/// In this way, the sections described first in the linker script will have +/// lower (higher precedence) ids. Input sections that appear first also have +/// higher precedence. This allows an order pass to later assign to each atom +/// a corresponding rule id according to the input file and section where it +/// came from and then stable-sort all atoms according to their ids. The result +/// is an output file layout that follows what is described in the linker +/// script. +/// +/// In order to easily match atoms with rules ids, we also maintain two string +/// maps. One matches archive file names to rule ids and the other, input object +/// file names to rule ids. In this way, we can easily locate the group of input +/// section names and their respective ids. +/// +/// Example: +/// +/// .data : { input.o(.data .bss) } +/// +/// The above would be organized in the _rules vector, indexed by rule ids, as +/// follows: +/// +/// _rules vector: id 0: (_sectionName = ".data") +/// id 1: (_fileName = "input.o") +/// id 2: (_name = ".data") +/// id 3: (_name = ".bss") +/// +/// In this scenario, all atoms from input.o:.data gets assigned rule id 1. +class Sema { +public: + Sema(); + + // We can parse several linker scripts via command line whose ASTs are stored + // here via addLinkerScript(). + void addLinkerScript(std::unique_ptr script) { + _scripts.push_back(std::move(script)); + } + + std::vector> &getLinkerScripts() { return _scripts; } + + void perform() { + for (auto &parser : _scripts) + assignRuleIds(parser->get()); + } + + bool hasRules() const { return _rules.size() > 0; } + uint32_t lastRuleId() const { return _rules.size(); } + bool isLegalRuleId(uint32_t ruleId) const { return ruleId < _rules.size(); } + + uint32_t getRuleId(StringRef archiveName, StringRef fileName, + StringRef sectionName) const { + if (archiveName.empty()) + return getRuleId(fileName, sectionName); + + auto range = _fileToRuleId.equal_range(fileName); + for (auto I = range.first, E = range.second; I != E; ++I) { + uint32_t id = I->second; + const InputSectionsCmd *cmd = dyn_cast(_rules[id]); + if (!cmd || cmd->archiveName() != archiveName) + continue; + + while (++id < _rules.size() && (isa(_rules[id]))) { + if (isa(_rules[id])) + continue; + + const InputSectionName *in = dyn_cast(_rules[id]); + if (in->name() == sectionName) + return id; + } + } + return _rules.size(); + } + + uint32_t getRuleId(StringRef fileName, StringRef sectionName) const { + auto range = _fileToRuleId.equal_range(fileName); + for (auto I = range.first, E = range.second; I != E; ++I) { + uint32_t id = I->second; + while (++id < _rules.size() && (isa(_rules[id]))) { + if (isa(_rules[id])) + continue; + + const InputSectionName *in = dyn_cast(_rules[id]); + if (in->name() == sectionName) + return id; + } + } + return _rules.size(); + } + + StringRef getOutputSection(uint32_t ruleId) const { + if (ruleId == _rules.size() || ruleId == 0) + return StringRef(); + + for (int i = ruleId - 1; i >= 0; --i) { + if (!isa(_rules[i])) + continue; + + const OutputSectionDescription *out = + dyn_cast(_rules[i]); + return out->name(); + } + + return StringRef(); + } + + std::vector getExprs(uint32_t ruleId) const { + auto ans = std::vector(); + + if (!isLegalRuleId(ruleId) || ruleId == 0) + return ans; + + for (int i = ruleId - 1; i >= 0; --i) { + if (isa(_rules[i])) + break; + if (auto assgn = dyn_cast(_rules[i])) + ans.push_back(assgn); + } + + return ans; + } + + std::error_code evalExpr(const SymbolAssignment *assgn, uint64_t &curPos) { + _symbolTable[StringRef(".")] = curPos; + + auto ans = assgn->expr()->evalExpr(_symbolTable); + if (ans.getError()) + return ans.getError(); + uint64_t result = *ans; + + if (assgn->symbol() == ".") { + curPos = result; + return std::error_code(); + } + + _symbolTable[assgn->symbol()] = result; + return std::error_code(); + } + + void dump() const { + raw_ostream &os = llvm::outs(); + os << "Linker script semantics dump\n"; + int num = 0; + for (auto &parser : _scripts) { + os << "Dumping script #" << ++num << ":\n"; + parser->get()->dump(os); + os << "\n"; + } + os << "Dumping rule ids:\n"; + for (unsigned i = 0; i < _rules.size(); ++i) { + os << "RuleId " << i << ":\n"; + _rules[i]->dump(os); + os << "\n\n"; + } + } + +private: + void assignRuleIds(const InputSection *inputSection) { + if (isa(inputSection)) { + _idMap[inputSection] = _rules.size(); + _rules.push_back(inputSection); + return; + } + + auto *sortedGroup = dyn_cast(inputSection); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + for (const InputSection *child : *sortedGroup) { + // FIXME: Implement all SORT* variations + assignRuleIds(child); + } + } + + void assignRuleIds(const InputSectionsCmd *inputSections) { + StringRef fileName = inputSections->fileName(); + if (!fileName.empty()) + _fileToRuleId.insert( + std::make_pair(fileName.str(), _rules.size())); + + StringRef archiveName = inputSections->archiveName(); + if (!archiveName.empty()) + _archiveToRuleId.insert(std::make_pair( + archiveName.str(), _rules.size())); + + _idMap[inputSections] = _rules.size(); + _rules.push_back(inputSections); + for (const InputSection *inputSection : *inputSections) + assignRuleIds(inputSection); + } + + void assignRuleIds(const Sections *sections) { + for (const Command *sectionCommand : *sections) { + if (isa(sectionCommand)) { + _idMap[sectionCommand] = _rules.size(); + _rules.push_back(sectionCommand); + continue; + } + + if (!isa(sectionCommand)) + continue; + + _idMap[sectionCommand] = _rules.size(); + _rules.push_back(sectionCommand); + auto *outSection = dyn_cast(sectionCommand); + + for (const Command *outSecCommand : *outSection) { + if (isa(outSecCommand)) { + _idMap[outSecCommand] = _rules.size(); + _rules.push_back(outSecCommand); + continue; + } + + if (!isa(outSecCommand)) + continue; + + assignRuleIds(dyn_cast(outSecCommand)); + } + } + } + + void assignRuleIds(const LinkerScript *ls) { + for (const Command *c : ls->_commands) { + if (!isa(c)) + continue; + + assignRuleIds(dyn_cast(c)); + } + } + + std::vector> _scripts; + std::vector _rules; + llvm::DenseMap _idMap; + std::unordered_multimap _fileToRuleId; + std::unordered_multimap _archiveToRuleId; + Expression::SymbolTableTy _symbolTable; +}; + llvm::BumpPtrAllocator &Command::getAllocator() const { return _ctx.getAllocator(); } llvm::BumpPtrAllocator &Expression::getAllocator() const { return _ctx.getAllocator(); } -llvm::BumpPtrAllocator &InputSection::getAllocator() const { - return _ctx.getAllocator(); -} } // end namespace script } // end namespace lld Index: lib/Core/Error.cpp =================================================================== --- lib/Core/Error.cpp +++ lib/Core/Error.cpp @@ -86,6 +86,11 @@ return "Success"; case LinkerScriptReaderError::parse_error: return "Error parsing linker script"; + case LinkerScriptReaderError::unknown_symbol_in_expr: + return "Unknown symbol found when evaluating linker script expression"; + case LinkerScriptReaderError::unrecognized_function_in_expr: + return "Unrecognized function call when evaluating linker script " + "expression"; } llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a " "message defined."); Index: lib/Driver/GnuLdDriver.cpp =================================================================== --- lib/Driver/GnuLdDriver.cpp +++ lib/Driver/GnuLdDriver.cpp @@ -310,7 +310,7 @@ ctx.setOutputPath(output->getOutputFileName()); } // Transfer ownership of the script to the linking context - ctx.addLinkerScript(std::move(parser)); + ctx.linkerScriptSema().addLinkerScript(std::move(parser)); return std::error_code(); } @@ -727,6 +727,9 @@ if (!ctx->validate(diag)) return false; + // Perform linker script semantic actions + ctx->linkerScriptSema().perform(); + context.swap(ctx); return true; } Index: lib/ReaderWriter/ELF/Atoms.h =================================================================== --- lib/ReaderWriter/ELF/Atoms.h +++ lib/ReaderWriter/ELF/Atoms.h @@ -155,9 +155,9 @@ std::vector *> &referenceList) : _owningFile(file), _symbolName(symbolName), _sectionName(sectionName), _symbol(symbol), _section(section), _contentData(contentData), - _referenceStartIndex(referenceStart), _referenceEndIndex(referenceEnd), - _referenceList(referenceList), _contentType(typeUnknown), - _permissions(permUnknown) {} + _ruleId(0), _referenceStartIndex(referenceStart), + _referenceEndIndex(referenceEnd), _referenceList(referenceList), + _contentType(typeUnknown), _permissions(permUnknown) {} ~ELFDefinedAtom() {} @@ -167,6 +167,8 @@ uint64_t ordinal() const override { return _ordinal; } + uint32_t ruleId() const override { return _ruleId; } + const Elf_Sym *symbol() const { return _symbol; } const Elf_Shdr *section() const { return _section; } @@ -432,6 +434,8 @@ virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + virtual void setRuleId(uint32_t id) { _ruleId = id; } + 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. @@ -449,6 +453,7 @@ ArrayRef _contentData; uint64_t _ordinal; + uint32_t _ruleId; unsigned int _referenceStartIndex; unsigned int _referenceEndIndex; std::vector *> &_referenceList; @@ -465,7 +470,7 @@ const Elf_Shdr *section, ArrayRef contentData, uint64_t offset) : _owningFile(file), _sectionName(sectionName), _section(section), - _contentData(contentData), _offset(offset) { + _contentData(contentData), _ruleId(0), _offset(offset) { } const ELFFile &file() const override { return _owningFile; } @@ -478,6 +483,10 @@ virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + virtual void setRuleId(uint32_t id) { _ruleId = id; } + + uint32_t ruleId() const override { return _ruleId; } + uint64_t ordinal() const override { return _ordinal; } uint64_t size() const override { return _contentData.size(); } @@ -534,18 +543,17 @@ /// \brief Holds the bits that make up the atom. ArrayRef _contentData; uint64_t _ordinal; + uint32_t _ruleId; uint64_t _offset; }; template class ELFCommonAtom : public DefinedAtom { typedef llvm::object::Elf_Sym_Impl Elf_Sym; public: - ELFCommonAtom(const ELFFile &file, - StringRef symbolName, + ELFCommonAtom(const ELFFile &file, StringRef symbolName, const Elf_Sym *symbol) - : _owningFile(file), - _symbolName(symbolName), - _symbol(symbol) {} + : _owningFile(file), _symbolName(symbolName), _symbol(symbol), + _ruleId(0) {} const ELFFile &file() const override { return _owningFile; } @@ -553,6 +561,10 @@ uint64_t ordinal() const override { return _ordinal; } + virtual void setRuleId(uint32_t ruleId) { _ruleId = ruleId; } + + uint32_t ruleId() const override { return _ruleId; } + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } uint64_t size() const override { return _symbol->st_size; } @@ -615,6 +627,7 @@ StringRef _symbolName; const Elf_Sym *_symbol; uint64_t _ordinal; + uint32_t _ruleId; }; /// \brief An atom from a shared library. Index: lib/ReaderWriter/ELF/Chunk.h =================================================================== --- lib/ReaderWriter/ELF/Chunk.h +++ lib/ReaderWriter/ELF/Chunk.h @@ -39,14 +39,15 @@ SectionHeader, ///< Section header ELFSegment, ///< Segment ELFSection, ///< Section - AtomSection ///< A section containing atoms. + 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 }; 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) {} + _ruleId(0), _ordinal(1), _start(0), _fileoffset(0), _context(context) {} virtual ~Chunk() {} // The name of the chunk StringRef name() const { return _name; } @@ -63,6 +64,8 @@ // 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; } + uint32_t ruleId() const { return _ruleId; } + void setRuleId(uint32_t ruleId) { _ruleId = ruleId; } // Output file offset of the chunk uint64_t fileOffset() const { return _fileoffset; } void setFileOffset(uint64_t offset) { _fileoffset = offset; } @@ -89,6 +92,7 @@ uint64_t _msize; uint64_t _alignment; uint32_t _order; + uint32_t _ruleId; uint64_t _ordinal; uint64_t _start; uint64_t _fileoffset; Index: lib/ReaderWriter/ELF/DefaultLayout.h =================================================================== --- lib/ReaderWriter/ELF/DefaultLayout.h +++ lib/ReaderWriter/ELF/DefaultLayout.h @@ -169,7 +169,8 @@ typedef llvm::DenseSet AtomSetT; - DefaultLayout(const ELFLinkingContext &context) : _context(context) {} + DefaultLayout(ELFLinkingContext &context) : _context(context), + _linkerScriptSema(context.linkerScriptSema()) {} /// \brief Return the section order for a input section SectionOrder getSectionOrder(StringRef name, int32_t contentType, @@ -186,7 +187,7 @@ AtomSection * getSection(StringRef name, int32_t contentType, DefinedAtom::ContentPermissions contentPermissions, - StringRef path); + const DefinedAtom *da); /// \brief Gets the segment for a output section virtual Layout::SegmentType getSegmentType(Section *section) const; @@ -224,7 +225,14 @@ /// Inline functions inline range absoluteAtoms() { return _absoluteAtoms; } - inline void addSection(Chunk *c) { + void addSection(Chunk *c) { + if (_linkerScriptSema.hasRules()) { + if (auto sec = dyn_cast>(c)) + c->setRuleId(_linkerScriptSema.getRuleId(StringRef("*"), + sec->inputSectionName())); + else + c->setRuleId(_linkerScriptSema.lastRuleId()); + } _sections.push_back(c); } @@ -327,6 +335,7 @@ AtomSetT _referencedDynAtoms; llvm::StringSet<> _copiedDynSymNames; const ELFLinkingContext &_context; + script::Sema &_linkerScriptSema; }; template @@ -536,17 +545,29 @@ AtomSection * DefaultLayout::getSection(StringRef sectionName, int32_t contentType, DefinedAtom::ContentPermissions permissions, - StringRef path) { - const SectionKey sectionKey(sectionName, permissions, path); - SectionOrder sectionOrder = - getSectionOrder(sectionName, contentType, 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 *newSec = createSection(sectionName, contentType, permissions, sectionOrder); - newSec->setOutputSectionName(getOutputSectionName(sectionName)); + + StringRef outputSectionName; + if (!_linkerScriptSema.hasRules() || + (outputSectionName = _linkerScriptSema.getOutputSection(da->ruleId())).empty()) + outputSectionName = getOutputSectionName(sectionName); + + newSec->setOutputSectionName(outputSectionName); newSec->setOrder(sectionOrder); + if (_linkerScriptSema.hasRules()) { + // In atoms, 0 is an invalid rule id (uninitialized) + if (da->ruleId() != 0) + newSec->setRuleId(da->ruleId()); + else + newSec->setRuleId(_linkerScriptSema.lastRuleId()); + } _sections.push_back(newSec); _sectionMap.insert(std::make_pair(sectionKey, newSec)); return newSec; @@ -566,8 +587,8 @@ const DefinedAtom::ContentType contentType = definedAtom->contentType(); StringRef sectionName = getInputSectionName(definedAtom); - AtomSection *section = getSection( - sectionName, contentType, permissions, definedAtom->file().path()); + AtomSection *section = + getSection(sectionName, contentType, permissions, definedAtom); // Add runtime relocations to the .rela section. for (const auto &reloc : *definedAtom) { @@ -639,6 +660,38 @@ [](Chunk *A, Chunk *B) { return A->order() < B->order(); }); + if (_linkerScriptSema.hasRules()) { + // sort the sections by their order as defined by the linker script + std::stable_sort(_sections.begin(), _sections.end(), + [](Chunk *A, Chunk *B) { + return A->ruleId() < B->ruleId(); + }); + // now try to arrange sections with no mapping rules to sections with + // similar content + auto p = _sections.begin(); + const uint32_t lastRuleId = _linkerScriptSema.lastRuleId(); + // Find first section that has no assigned rule id + while (p != _sections.end() && (*p)->ruleId() != lastRuleId) + ++p; + // For all sections that have no assigned rule id, try to move them near a + // section with similar contents + if (p != _sections.begin()) { + for (; p != _sections.end(); ++p) { + auto q = p; + --q; + while (q != _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); + } + } + } + } // Create output sections. createOutputSections(); // Set the ordinal after sorting the sections @@ -689,8 +742,8 @@ if (!additionalSegmentInsert.second) { segment = additionalSegmentInsert.first->second; } else { - segment = new (_allocator) Segment(_context, segmentName, - segmentType); + segment = new (_allocator) Segment( + _context, segmentName, segmentType, _linkerScriptSema); additionalSegmentInsert.first->second = segment; _segments.push_back(segment); } @@ -716,18 +769,29 @@ if (!segmentInsert.second) { segment = segmentInsert.first->second; } else { - segment = new (_allocator) Segment(_context, "PT_LOAD", - llvm::ELF::PT_LOAD); + segment = new (_allocator) Segment( + _context, "PT_LOAD", llvm::ELF::PT_LOAD, _linkerScriptSema); segmentInsert.first->second = segment; _segments.push_back(segment); } + // Insert chunks with linker script expressions that occur at this + // point + if (_linkerScriptSema.hasRules()) { + std::vector exprs = + _linkerScriptSema.getExprs(section->ruleId()); + for (auto expr : exprs) { + auto expChunk = + new (_allocator) ExpressionChunk(_context, expr); + segment->append(expChunk); + } + } segment->append(section); } } } if (_context.isDynamic() && !_context.isDynamicLibrary()) { - Segment *segment = - new (_allocator) ProgramHeaderSegment(_context); + Segment *segment = new (_allocator) + ProgramHeaderSegment(_context, _linkerScriptSema); _segments.push_back(segment); segment->append(_elfHeader); segment->append(_programHeader); @@ -757,6 +821,7 @@ break; } } + assert(firstLoadSegment != nullptr && "No loadable segment!"); firstLoadSegment->prepend(_programHeader); firstLoadSegment->prepend(_elfHeader); bool newSegmentHeaderAdded = true; Index: lib/ReaderWriter/ELF/ELFFile.h =================================================================== --- lib/ReaderWriter/ELF/ELFFile.h +++ lib/ReaderWriter/ELF/ELFFile.h @@ -638,6 +638,12 @@ ErrorOr *> mergeAtom = handleMergeString(tai->_sectionName, tai->_shdr, content, tai->_offset); (*mergeAtom)->setOrdinal(++_ordinal); + if (!_ctx.linkerScriptSema().hasRules()) + (*mergeAtom)->setRuleId(0); + else + (*mergeAtom) + ->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), tai->_sectionName)); _definedAtoms._atoms.push_back(*mergeAtom); _mergeAtoms.push_back(*mergeAtom); } @@ -682,6 +688,17 @@ ErrorOr *> commonAtom = handleCommonSymbol(*symbolName, &*SymI); (*commonAtom)->setOrdinal(++_ordinal); + if (!_ctx.linkerScriptSema().hasRules()) + (*commonAtom)->setRuleId(0); + else { + ErrorOr sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + (*commonAtom)->setRuleId(0); + else + (*commonAtom) + ->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), *sectionName)); + } _definedAtoms._atoms.push_back(*commonAtom); _symbolToAtomMapping.insert(std::make_pair(&*SymI, *commonAtom)); } else if (isDefinedSymbol(&*SymI)) { @@ -772,6 +789,11 @@ ELFDefinedAtom *newAtom = createSectionAtom(section, *sectionName, *sectionContents); newAtom->setOrdinal(++_ordinal); + if (!_ctx.linkerScriptSema().hasRules()) + newAtom->setRuleId(0); + else + newAtom->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), *sectionName)); if (addAtoms) _definedAtoms._atoms.push_back(newAtom); else @@ -824,6 +846,12 @@ symbolName, *sectionName, &**si, section, symbolData, _references.size(), _references.size(), _references); (*definedMergeAtom)->setOrdinal(++_ordinal); + if (!_ctx.linkerScriptSema().hasRules()) + (*definedMergeAtom)->setRuleId(0); + else + (*definedMergeAtom) + ->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), *sectionName)); if (addAtoms) _definedAtoms._atoms.push_back(*definedMergeAtom); else @@ -856,6 +884,11 @@ ELFDefinedAtom *newAtom = createDefinedAtomAndAssignRelocations( symbolName, *sectionName, &*symbol, section, symbolData, *sectionContents); + if (!_ctx.linkerScriptSema().hasRules()) + newAtom->setRuleId(0); + else + newAtom->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), *sectionName)); newAtom->setOrdinal(++_ordinal); // If the atom was a weak symbol, let's create a followon reference to @@ -881,6 +914,11 @@ _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom)); if (anonAtom) { anonAtom->setOrdinal(++_ordinal); + if (!_ctx.linkerScriptSema().hasRules()) + anonAtom->setRuleId(0); + else + anonAtom->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), *sectionName)); if (addAtoms) _definedAtoms._atoms.push_back(anonAtom); else @@ -1147,6 +1185,11 @@ auto *newAtom = createDefinedAtomAndAssignRelocations( "", sectionName, sym, section, content, content); newAtom->setOrdinal(++_ordinal); + if (!_ctx.linkerScriptSema().hasRules()) + newAtom->setRuleId(0); + else + newAtom->setRuleId(_ctx.linkerScriptSema().getRuleId( + this->archivePath(), this->memberPath(), sectionName)); return newAtom; } Index: lib/ReaderWriter/ELF/ELFLinkingContext.cpp =================================================================== --- lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -62,7 +62,7 @@ _mergeRODataToTextSegment(true), _demangle(true), _stripSymbols(false), _alignSegments(true), _collectStats(false), _outputMagic(OutputMagic::DEFAULT), _initFunction("_init"), - _finiFunction("_fini"), _sysrootPath("") {} + _finiFunction("_fini"), _sysrootPath(""), _linkerScriptSema() {} void ELFLinkingContext::addPasses(PassManager &pm) { pm.add(llvm::make_unique()); Index: lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h =================================================================== --- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h +++ lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h @@ -29,7 +29,7 @@ ORDER_SDATA = 205 }; - HexagonTargetLayout(const HexagonLinkingContext &hti) + HexagonTargetLayout(HexagonLinkingContext &hti) : TargetLayout(hti), _sdataSection(nullptr), _gotSymAtom(nullptr), _cachedGotSymAtom(false) { _sdataSection = new (_alloc) SDataSection(hti); Index: lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h =================================================================== --- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h +++ lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h @@ -26,7 +26,7 @@ template class MipsTargetLayout final : public TargetLayout { public: - MipsTargetLayout(const MipsLinkingContext &ctx) + MipsTargetLayout(MipsLinkingContext &ctx) : TargetLayout(ctx), _gotSection(new (_alloc) MipsGOTSection(ctx)), _pltSection(new (_alloc) MipsPLTSection(ctx)) {} Index: lib/ReaderWriter/ELF/OrderPass.h =================================================================== --- lib/ReaderWriter/ELF/OrderPass.h +++ lib/ReaderWriter/ELF/OrderPass.h @@ -11,6 +11,7 @@ #define LLD_READER_WRITER_ELF_ORDER_PASS_H #include "lld/Core/Parallel.h" +#include "lld/ReaderWriter/LinkerScript.h" #include namespace lld { Index: lib/ReaderWriter/ELF/SegmentChunks.h =================================================================== --- lib/ReaderWriter/ELF/SegmentChunks.h +++ lib/ReaderWriter/ELF/SegmentChunks.h @@ -105,7 +105,7 @@ typedef typename std::vector *>::iterator SectionIter; Segment(const ELFLinkingContext &context, StringRef name, - const Layout::SegmentType type); + const Layout::SegmentType type, script::Sema &linkerScriptSema); /// \brief the Order of segments that appear in the output file enum SegmentOrder { @@ -123,7 +123,7 @@ /// append a chunk to a segment, this function /// is used by the ProgramHeader segment - virtual void append(Chunk *chunk) {} + virtual void append(Chunk *chunk) { _sections.push_back(chunk); } /// Sort segments depending on the property /// If we have a Program Header segment, it should appear first @@ -277,14 +277,43 @@ uint64_t _flags; int64_t _atomflags; llvm::BumpPtrAllocator _segmentAllocate; + script::Sema &_linkerScriptSema; +}; + +template class ExpressionChunk : public Chunk { +public: + ExpressionChunk(const ELFLinkingContext &ctx, + const script::SymbolAssignment *expr) + : Chunk(StringRef(), Chunk::Kind::Expression, ctx), + _expr(expr) { + this->_alignment = 1; + } + + static bool classof(const Chunk *c) { + return c->kind() == Chunk::Kind::Expression; + } + + const script::SymbolAssignment *get() const { return _expr; } + + int getContentType() const override { + return Chunk::ContentType::Unknown; + } + void write(ELFWriter *, TargetLayout &, + llvm::FileOutputBuffer &) override {} + void doPreFlight() override {} + void finalize() override {} + +private: + const script::SymbolAssignment *_expr; }; /// \brief A Program Header segment contains a set of chunks instead of sections /// The segment doesn't contain any slice template class ProgramHeaderSegment : public Segment { public: - ProgramHeaderSegment(const ELFLinkingContext &context) - : Segment(context, "PHDR", llvm::ELF::PT_PHDR) { + ProgramHeaderSegment(const ELFLinkingContext &context, + script::Sema &linkerScriptSema) + : Segment(context, "PHDR", llvm::ELF::PT_PHDR, linkerScriptSema) { this->_alignment = 8; this->_flags = (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR); } @@ -312,9 +341,9 @@ template Segment::Segment(const ELFLinkingContext &context, StringRef name, - const Layout::SegmentType type) + const Layout::SegmentType type, script::Sema &s) : Chunk(name, Chunk::Kind::ELFSegment, context), - _segmentType(type), _flags(0), _atomflags(0) { + _segmentType(type), _flags(0), _atomflags(0), _linkerScriptSema(s) { this->_alignment = 0; this->_fsize = 0; _outputMagic = context.getOutputMagic(); @@ -400,11 +429,16 @@ 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>(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 @@ -439,6 +473,7 @@ } section->setFileOffset(fileOffset); fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); } slice->setFileSize(fileOffset - curSliceFileOffset); } @@ -467,7 +502,7 @@ SegmentSlice *slice = nullptr; uint64_t tlsStartAddr = 0; bool alignSegments = this->_context.alignSegments(); - StringRef prevOutputSectionName; + 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 @@ -491,6 +526,10 @@ } // 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>(*si)) + _linkerScriptSema.evalExpr(expr->get(), newAddr); curSliceAddress = newAddr; sliceAlign = (*si)->alignment(); (*si)->setVirtualAddr(curSliceAddress); @@ -523,9 +562,22 @@ 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>(*si)) + _linkerScriptSema.evalExpr(expr->get(), newAddr); Section *sec = dyn_cast>(*si); - StringRef curOutputSectionName = - sec ? sec->outputSectionName() : (*si)->name(); + 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>(*si)) + curOutputSectionName = prevOutputSectionName; + else + curOutputSectionName = (*si)->name(); + } bool autoCreateSlice = true; if (curOutputSectionName == prevOutputSectionName) autoCreateSlice = false; Index: lib/ReaderWriter/ELF/TargetLayout.h =================================================================== --- lib/ReaderWriter/ELF/TargetLayout.h +++ lib/ReaderWriter/ELF/TargetLayout.h @@ -20,7 +20,7 @@ /// be changed in the final layout template class TargetLayout : public DefaultLayout { public: - TargetLayout(const ELFLinkingContext &context) + TargetLayout(ELFLinkingContext &context) : DefaultLayout(context) {} }; } // end namespace elf Index: lib/ReaderWriter/LinkerScript.cpp =================================================================== --- lib/ReaderWriter/LinkerScript.cpp +++ lib/ReaderWriter/LinkerScript.cpp @@ -1903,5 +1903,9 @@ return new (_alloc) Sections(*this, sectionsCommands); } +Sema::Sema() + : _scripts(), _rules(), _idMap(), _fileToRuleId(), _archiveToRuleId(), + _symbolTable() {} + } // end namespace script } // end namespace lld Index: test/elf/Inputs/prog1.o.yaml =================================================================== --- /dev/null +++ test/elf/Inputs/prog1.o.yaml @@ -0,0 +1,88 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B000E800000000BF01000000BA0E0000004889C6E80000000031C05DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000007 + Symbol: prog2 + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x0000000000000019 + Symbol: write + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E302028687474703A2F2F6C6C766D2E6F72672F6769742F636C616E672E6769742036336134646334616430343938646139623934386330383263623735336430353735323938346638292028687474703A2F2F6C6C766D2E6F72672F6769742F6C6C766D2E67697420623838363135326664656538376564653738613565643965616638663664313839343033616266312900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000002100000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000021 + - Name: prog2 + - Name: write +... Index: test/elf/Inputs/prog2.o.yaml =================================================================== --- /dev/null +++ test/elf/Inputs/prog2.o.yaml @@ -0,0 +1,89 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E548B800000000000000005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000006 + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .rodata.str1.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 48656C6C6F2C20776F726C64210A00 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E302028687474703A2F2F6C6C766D2E6F72672F6769742F636C616E672E6769742036336134646334616430343938646139623934386330383263623735336430353735323938346638292028687474703A2F2F6C6C766D2E6F72672F6769742F6C6C766D2E67697420623838363135326664656538376564653738613565643965616638663664313839343033616266312900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000001000000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .rodata.str1.1 + Type: STT_SECTION + Section: .rodata.str1.1 + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: prog2 + Type: STT_FUNC + Section: .text + Size: 0x0000000000000010 +... Index: test/elf/Inputs/prog3.o.yaml =================================================================== --- /dev/null +++ test/elf/Inputs/prog3.o.yaml @@ -0,0 +1,52 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: B8010000000F05C3E800000000B83C0000000F05C3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000009 + Symbol: main + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: _start + Section: .text + Value: 0x0000000000000008 + - Name: write + Section: .text + - Name: main +... Index: test/elf/script-section-order.test =================================================================== --- /dev/null +++ test/elf/script-section-order.test @@ -0,0 +1,96 @@ +/* +Tests a simple linker script that changes the order of output sections and +also changes the address of output sections by using simple expressions. + +This test uses three X86-64 input objects, prog1.o, prog2.o and prog3.o, +which were created with the following C or assembly code: + +*** prog1.o: + +(command line clang -c prog1.c -o prog1.o) + +const char *prog2(); +void write(int, const char *, int); + +int main() { + write(1, prog2(), 14); +} + +*** prog2.o: + +(command line clang -c prog2.c -o prog2.o) + +const char *prog2() { + return "Hello, world!\n"; +} + +*** prog3.o: + +(command line clang -c prog3.S -o prog3.o) + + .globl write +write: + mov $1, %eax + syscall + ret + + .globl _start +_start: + call main + mov $60, %eax + syscall + ret + +We use the following linker script for this test: +*/ + +ENTRY(_start) + +SECTIONS +{ + . = 0x500000; + .text : { prog1.o(.text) } + .mystring : { prog2.o(.rodata.str1.1) } + . = . + 0x6000; + .text.2 : {prog3.o(.text) prog2.o(.text) } +} + +/* +RUN: yaml2obj -format=elf %p/Inputs/prog1.o.yaml -o=%T/prog1.o +RUN: yaml2obj -format=elf %p/Inputs/prog2.o.yaml -o=%T/prog2.o +RUN: yaml2obj -format=elf %p/Inputs/prog3.o.yaml -o=%T/prog3.o + +RUN: cd %T +RUN: lld -flavor gnu -target x86_64 -T %s prog1.o prog2.o prog3.o \ +RUN: -static -o %t1 +RUN: llvm-readobj -s %t1 | FileCheck -check-prefix CHECKSECTIONS %s + +CHECKSECTIONS: Index: 1 +CHECKSECTIONS: Name: .text +CHECKSECTIONS: Address: 0x500000 +CHECKSECTIONS: Size: 33 + +CHECKSECTIONS: Index: 2 +CHECKSECTIONS: Name: .mystring +CHECKSECTIONS: Address: 0x500021 +CHECKSECTIONS: Size: 15 + +CHECKSECTIONS: Index: 3 +CHECKSECTIONS: Name: .text.2 +CHECKSECTIONS: Address: 0x506030 +CHECKSECTIONS: Size: 48 + +RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: Name: main +CHECKSYMS-NEXT: Value: 0x500000 + +CHECKSYMS: Name: write +CHECKSYMS-NEXT: Value: 0x506030 + +CHECKSYMS: Name: _start +CHECKSYMS-NEXT: Value: 0x506038 + +CHECKSYMS: Name: prog2 +CHECKSYMS-NEXT: Value: 0x506050 +*/