//===- 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 SymbolFile : public RuntimeFile { public: SymbolFile(ELFLinkingContext &ctx) : RuntimeFile(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 DynamicSymbolFile : public SimpleArchiveLibraryFile { typedef std::function &)> 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(_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> _file; std::vector>> _returnedFiles; }; } // end anon namespace template OutputELFWriter::OutputELFWriter(ELFLinkingContext &ctx, TargetLayout &layout) : _ctx(ctx), _targetHandler(ctx.getTargetHandler()), _layout(layout) {} template void OutputELFWriter::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 void OutputELFWriter::buildStaticSymbolTable(const File &file) { ScopedTask task(getDefaultDomain(), "buildStaticSymbolTable"); for (auto sec : _layout.sections()) if (auto section = dyn_cast>(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 StringRef OutputELFWriter::maybeGetSOName(Node *node) { if (auto *fnode = dyn_cast(node)) if (!fnode->asNeeded()) if (auto *file = dyn_cast(fnode->getFile())) return file->getDSOName(); return ""; } template void OutputELFWriter::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 : _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 void OutputELFWriter::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>(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 void OutputELFWriter::buildSectionHeaderTable() { ScopedTask task(getDefaultDomain(), "buildSectionHeaderTable"); for (auto outputSection : _layout.outputSections()) { if (outputSection->kind() != Chunk::Kind::ELFSection && outputSection->kind() != Chunk::Kind::AtomSection) continue; if (outputSection->hasSegment()) _shdrtab->appendSection(outputSection); } } template void OutputELFWriter::assignSectionsWithNoSegments() { ScopedTask task(getDefaultDomain(), "assignSectionsWithNoSegments"); for (auto outputSection : _layout.outputSections()) { if (outputSection->kind() != Chunk::Kind::ELFSection && outputSection->kind() != Chunk::Kind::AtomSection) continue; if (!outputSection->hasSegment()) _shdrtab->appendSection(outputSection); } _layout.assignFileOffsetsForMiscSections(); for (auto sec : _layout.sections()) if (auto section = dyn_cast>(sec)) if (!TargetLayout::hasOutputSegment(section)) _shdrtab->updateSection(section); } template void OutputELFWriter::createImplicitFiles( std::vector> &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 &file) { processUndefinedSymbol(sym, file); }; _ctx.setUndefinesResolver( llvm::make_unique>(_ctx, std::move(callback))); // Add script defined symbols auto file = llvm::make_unique>(_ctx, "Linker script runtime"); for (auto &sym : this->_ctx.linkerScriptSema().getScriptDefinedSymbols()) file->addAbsoluteAtom(sym.getKey()); result.push_back(std::move(file)); } template void OutputELFWriter::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 void OutputELFWriter::createDefaultSections() { _elfHeader.reset(new (_alloc) ELFHeader(_ctx)); _programHeader.reset(new (_alloc) ProgramHeader(_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( _ctx, ".strtab", TargetLayout::ORDER_STRING_TABLE)); _layout.addSection(_symtab.get()); _layout.addSection(_strtab.get()); _symtab->setStringSection(_strtab.get()); } _shstrtab.reset(new (_alloc) StringTable( _ctx, ".shstrtab", TargetLayout::ORDER_SECTION_STRINGS)); _shdrtab.reset(new (_alloc) SectionHeader( _ctx, TargetLayout::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>(sec); if (!section || section->outputSectionName() != ".eh_frame") continue; _ehFrameHeader.reset(new (_alloc) EHFrameHeader( _ctx, ".eh_frame_hdr", _layout, TargetLayout::ORDER_EH_FRAMEHDR)); _layout.addSection(_ehFrameHeader.get()); break; } if (_ctx.isDynamic()) { _dynamicTable = createDynamicTable(); _dynamicStringTable.reset(new (_alloc) StringTable( _ctx, ".dynstr", TargetLayout::ORDER_DYNAMIC_STRINGS, true)); _dynamicSymbolTable = createDynamicSymbolTable(); _hashTable.reset(new (_alloc) HashSection( _ctx, ".hash", TargetLayout::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 unique_bump_ptr> OutputELFWriter::createSymbolTable() { return unique_bump_ptr>(new (_alloc) SymbolTable( this->_ctx, ".symtab", TargetLayout::ORDER_SYMBOL_TABLE)); } /// \brief create dynamic table template unique_bump_ptr> OutputELFWriter::createDynamicTable() { return unique_bump_ptr>(new (_alloc) DynamicTable( this->_ctx, _layout, ".dynamic", TargetLayout::ORDER_DYNAMIC)); } /// \brief create dynamic symbol table template unique_bump_ptr> OutputELFWriter::createDynamicSymbolTable() { return unique_bump_ptr>( new (_alloc) DynamicSymbolTable(this->_ctx, _layout, ".dynsym", TargetLayout::ORDER_DYNAMIC_SYMBOLS)); } template std::error_code OutputELFWriter::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 std::error_code OutputELFWriter::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 uint64_t OutputELFWriter::outputFileSize() const { return _shdrtab->fileOffset() + _shdrtab->fileSize(); } template std::error_code OutputELFWriter::writeOutput(const File &file, StringRef path) { ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); ErrorOr> bufferOrErr = FileOutputBuffer::create(path, outputFileSize(), FileOutputBuffer::F_executable); if (std::error_code ec = bufferOrErr.getError()) return ec; std::unique_ptr &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 *section) { section->write(this, _layout, *buffer); }); writeTask.end(); ScopedTask commitTask(getDefaultDomain(), "ELF Writer commit to disk"); return buffer->commit(); } template std::error_code OutputELFWriter::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 void OutputELFWriter::processUndefinedSymbol( StringRef symName, RuntimeFile &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 void OutputELFWriter::updateScopeAtomValues(StringRef sym, StringRef sec) { updateScopeAtomValues(("__" + sym + "_start").str().c_str(), ("__" + sym + "_end").str().c_str(), sec); } template void OutputELFWriter::updateScopeAtomValues(StringRef start, StringRef end, StringRef sec) { AtomLayout *s = _layout.findAbsoluteAtom(start); AtomLayout *e = _layout.findAbsoluteAtom(end); const OutputSection *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; template class OutputELFWriter; template class OutputELFWriter; template class OutputELFWriter; } // namespace elf } // namespace lld