| //===- 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 atoms were added - release the file to the caller. |
| return _file->hasAtoms() ? _file.release() : nullptr; |
| } |
| |
| 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; |
| }; |
| |
| } // 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) { |
| std::unique_ptr<FileOutputBuffer> buffer; |
| ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); |
| if (std::error_code ec = FileOutputBuffer::create( |
| path, outputFileSize(), buffer, FileOutputBuffer::F_executable)) |
| return ec; |
| 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 |