//===- 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 Section::Section(const ELFLinkingContext &ctx, StringRef sectionName, StringRef chunkName, typename Chunk::Kind k) : Chunk(chunkName, k, ctx), _inputSectionName(sectionName), _outputSectionName(sectionName) {} template int Section::getContentType() const { if (_flags & llvm::ELF::SHF_EXECINSTR) return Chunk::ContentType::Code; else if (_flags & llvm::ELF::SHF_WRITE) return Chunk::ContentType::Data; else if (_flags & llvm::ELF::SHF_ALLOC) return Chunk::ContentType::Code; else return Chunk::ContentType::Unknown; } template AtomSection::AtomSection(const ELFLinkingContext &ctx, StringRef sectionName, int32_t contentType, int32_t permissions, int32_t order) : Section(ctx, sectionName, "AtomSection", Chunk::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 void AtomSection::assignVirtualAddress(uint64_t addr) { parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { ai->_virtualAddr = addr + ai->_fileOffset; }); } template void AtomSection::assignFileOffsets(uint64_t offset) { parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { ai->_fileOffset = offset + ai->_fileOffset; }); } template const AtomLayout * AtomSection::findAtomLayoutByName(StringRef name) const { for (auto ai : _atoms) if (ai->_atom->name() == name) return ai; return nullptr; } template std::string AtomSection::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 uint64_t AtomSection::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 const AtomLayout *AtomSection::appendAtom(const Atom *atom) { const DefinedAtom *definedAtom = cast(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 StringRef Section::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 void AtomSection::write(ELFWriter *writer, TargetLayout &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> 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(ai->_atom); if (!definedAtom->occupiesDiskSpace()) return; // Copy raw content of atom to file buffer. ArrayRef 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 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 void OutputSection::appendSection(Section *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 StringTable::StringTable(const ELFLinkingContext &ctx, const char *str, int32_t order, bool dynamic) : Section(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 uint64_t StringTable::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 void StringTable::write(ELFWriter *writer, TargetLayout &, 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 SymbolTable::SymbolTable(const ELFLinkingContext &ctx, const char *str, int32_t order) : Section(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 void SymbolTable::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 void SymbolTable::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 void SymbolTable::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 void SymbolTable::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 void SymbolTable::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(atom)) addDefinedAtom(symbol, da, addr); else if (const AbsoluteAtom *aa = dyn_cast(atom)) addAbsoluteAtom(symbol, aa, addr); else if (isa(atom)) addSharedLibAtom(symbol, dyn_cast(atom)); else addUndefinedAtom(symbol, dyn_cast(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 void SymbolTable::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 void SymbolTable::write(ELFWriter *writer, TargetLayout &, 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 DynamicSymbolTable::DynamicSymbolTable(const ELFLinkingContext &ctx, TargetLayout &layout, const char *str, int32_t order) : SymbolTable(ctx, str, order), _layout(layout) { this->_type = SHT_DYNSYM; this->_flags = SHF_ALLOC; this->_msize = this->_fsize; } template void DynamicSymbolTable::addSymbolsToHashTable() { int index = 0; for (auto &ste : this->_symbolTable) { if (!ste._atom) _hashTable->addSymbol("", index); else _hashTable->addSymbol(ste._atom->name(), index); ++index; } } template void DynamicSymbolTable::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::finalize(false); } template RelocationTable::RelocationTable(const ELFLinkingContext &ctx, StringRef str, int32_t order) : Section(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 uint32_t RelocationTable::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 bool RelocationTable::getRelocationIndex(const Reference &r, uint32_t &res) { auto rel = std::find_if( _relocs.begin(), _relocs.end(), [&](const std::pair &p) { if (p.second == &r) return true; return false; }); if (rel == _relocs.end()) return false; res = std::distance(_relocs.begin(), rel); return true; } template bool RelocationTable::canModifyReadonlySection() const { for (const auto &rel : _relocs) { const DefinedAtom *atom = rel.first; if ((atom->permissions() & DefinedAtom::permRW_) != DefinedAtom::permRW_) return true; } return false; } template void RelocationTable::finalize() { this->_link = _symbolTable ? _symbolTable->ordinal() : 0; if (this->_outputSection) this->_outputSection->setLink(this->_link); } template void RelocationTable::write(ELFWriter *writer, TargetLayout &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(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(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 void RelocationTable::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 void RelocationTable::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 uint32_t RelocationTable::getSymbolIndex(const Atom *a) { return _symbolTable ? _symbolTable->getSymbolTableIndex(a) : (uint32_t)STN_UNDEF; } template DynamicTable::DynamicTable(const ELFLinkingContext &ctx, TargetLayout &layout, StringRef str, int32_t order) : Section(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 std::size_t DynamicTable::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 void DynamicTable::write(ELFWriter *writer, TargetLayout &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 void DynamicTable::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 void DynamicTable::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 void DynamicTable::finalize() { StringTable *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 void DynamicTable::updateDynamicTable() { StringTable *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 const AtomLayout *DynamicTable::getInitAtomLayout() { auto al = _layout.findAtomLayoutByName(this->_ctx.initFunction()); if (al && isa(al->_atom)) return al; return nullptr; } template const AtomLayout *DynamicTable::getFiniAtomLayout() { auto al = _layout.findAtomLayoutByName(this->_ctx.finiFunction()); if (al && isa(al->_atom)) return al; return nullptr; } template InterpSection::InterpSection(const ELFLinkingContext &ctx, StringRef str, int32_t order, StringRef interp) : Section(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 void InterpSection::write(ELFWriter *writer, TargetLayout &layout, llvm::FileOutputBuffer &buffer) { uint8_t *chunkBuffer = buffer.getBufferStart(); uint8_t *dest = chunkBuffer + this->fileOffset(); std::memcpy(dest, _interp.data(), _interp.size()); } template HashSection::HashSection(const ELFLinkingContext &ctx, StringRef name, int32_t order) : Section(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 void HashSection::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 void HashSection::setSymbolTable( const DynamicSymbolTable *symbolTable) { _symbolTable = symbolTable; } template void HashSection::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 void HashSection::finalize() { this->_link = _symbolTable ? _symbolTable->ordinal() : 0; if (this->_outputSection) this->_outputSection->setLink(this->_link); } template void HashSection::write(ELFWriter *writer, TargetLayout &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 EHFrameHeader::EHFrameHeader(const ELFLinkingContext &ctx, StringRef name, TargetLayout &layout, int32_t order) : Section(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 void EHFrameHeader::doPreFlight() { // TODO: Generate a proper binary search table. } template void EHFrameHeader::finalize() { OutputSection *s = _layout.findOutputSection(".eh_frame"); OutputSection *h = _layout.findOutputSection(".eh_frame_hdr"); if (s && h) _ehFrameOffset = s->virtualAddr() - (h->virtualAddr() + 4); } template void EHFrameHeader::write(ELFWriter *writer, TargetLayout &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::Elf_Sword *>( dest + pos) = _ehFrameOffset; } #define INSTANTIATE(klass) \ template class klass; \ template class klass; \ template class klass; \ template class klass 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