diff options
-rw-r--r-- | lld/ELF/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lld/ELF/OutputSections.cpp | 586 | ||||
-rw-r--r-- | lld/ELF/OutputSections.h | 396 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 829 |
4 files changed, 990 insertions, 822 deletions
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 9e411c9e2dc..49948e350cf 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -8,6 +8,7 @@ add_llvm_library(lldELF2 DriverUtils.cpp Error.cpp InputFiles.cpp + OutputSections.cpp SymbolTable.cpp Symbols.cpp Writer.cpp diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp new file mode 100644 index 00000000000..a9a1ca2073b --- /dev/null +++ b/lld/ELF/OutputSections.cpp @@ -0,0 +1,586 @@ +//===- OutputSections.cpp -------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "OutputSections.h" +#include "Config.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "SymbolTable.h" + +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf2; + +template <bool Is64Bits> +OutputSectionBase<Is64Bits>::OutputSectionBase(StringRef Name, uint32_t sh_type, + uintX_t sh_flags) + : Name(Name) { + memset(&Header, 0, sizeof(HeaderT)); + Header.sh_type = sh_type; + Header.sh_flags = sh_flags; +} + +template <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody *Sym) { + Sym->setGotIndex(Entries.size()); + Entries.push_back(Sym); +} + +template <class ELFT> +typename GotSection<ELFT>::uintX_t +GotSection<ELFT>::getEntryAddr(const SymbolBody &B) const { + return this->getVA() + B.getGotIndex() * this->getAddrSize(); +} + +template <class ELFT> void PltSection<ELFT>::writeTo(uint8_t *Buf) { + uintptr_t Start = reinterpret_cast<uintptr_t>(Buf); + ArrayRef<uint8_t> Jmp = {0xff, 0x25}; // jmpq *val(%rip) + for (const SymbolBody *E : Entries) { + uintptr_t InstPos = reinterpret_cast<uintptr_t>(Buf); + + memcpy(Buf, Jmp.data(), Jmp.size()); + Buf += Jmp.size(); + + uintptr_t OffsetInPLT = (InstPos + 6) - Start; + uintptr_t Delta = GotSec.getEntryAddr(*E) - (this->getVA() + OffsetInPLT); + assert(isInt<32>(Delta)); + support::endian::write32le(Buf, Delta); + Buf += 4; + + *Buf = 0x90; // nop + ++Buf; + *Buf = 0x90; // nop + ++Buf; + } +} + +template <class ELFT> void PltSection<ELFT>::addEntry(SymbolBody *Sym) { + Sym->setPltIndex(Entries.size()); + Entries.push_back(Sym); +} + +template <class ELFT> +typename PltSection<ELFT>::uintX_t +PltSection<ELFT>::getEntryAddr(const SymbolBody &B) const { + return this->getVA() + B.getPltIndex() * EntrySize; +} + +bool lld::elf2::relocNeedsPLT(uint32_t Type) { + switch (Type) { + default: + return false; + case R_X86_64_PLT32: + return true; + } +} + +bool lld::elf2::relocNeedsGOT(uint32_t Type) { + if (relocNeedsPLT(Type)) + return true; + switch (Type) { + default: + return false; + case R_X86_64_GOTPCREL: + return true; + } +} + +template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) { + auto *P = reinterpret_cast<Elf_Rela *>(Buf); + bool IsMips64EL = Relocs[0].C.getFile()->getObj()->isMips64EL(); + for (const DynamicReloc<ELFT> &Rel : Relocs) { + const InputSection<ELFT> &C = Rel.C; + const Elf_Rel &RI = Rel.RI; + OutputSection<ELFT> *Out = C.getOutputSection(); + uint32_t SymIndex = RI.getSymbol(IsMips64EL); + const SymbolBody *Body = C.getFile()->getSymbolBody(SymIndex); + uint32_t Type = RI.getType(IsMips64EL); + if (relocNeedsGOT(Type)) { + P->r_offset = GotSec.getEntryAddr(*Body); + P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), R_X86_64_GLOB_DAT, + IsMips64EL); + } else { + P->r_offset = RI.r_offset + C.getOutputSectionOff() + Out->getVA(); + P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), Type, IsMips64EL); + if (IsRela) + P->r_addend = static_cast<const Elf_Rela &>(RI).r_addend; + } + + ++P; + } +} + +template <class ELFT> void RelocationSection<ELFT>::finalize() { + this->Header.sh_link = DynSymSec.getSectionIndex(); + this->Header.sh_size = Relocs.size() * this->Header.sh_entsize; +} + +template <bool Is64Bits> +InterpSection<Is64Bits>::InterpSection() + : OutputSectionBase<Is64Bits>(".interp", llvm::ELF::SHT_PROGBITS, + llvm::ELF::SHF_ALLOC) { + this->Header.sh_size = Config->DynamicLinker.size() + 1; + this->Header.sh_addralign = 1; +} + +template <bool Is64Bits> +template <endianness E> +void OutputSectionBase<Is64Bits>::writeHeaderTo( + typename ELFFile<ELFType<E, Is64Bits>>::Elf_Shdr *SHdr) { + SHdr->sh_name = Header.sh_name; + SHdr->sh_type = Header.sh_type; + SHdr->sh_flags = Header.sh_flags; + SHdr->sh_addr = Header.sh_addr; + SHdr->sh_offset = Header.sh_offset; + SHdr->sh_size = Header.sh_size; + SHdr->sh_link = Header.sh_link; + SHdr->sh_info = Header.sh_info; + SHdr->sh_addralign = Header.sh_addralign; + SHdr->sh_entsize = Header.sh_entsize; +} + +template <bool Is64Bits> void InterpSection<Is64Bits>::writeTo(uint8_t *Buf) { + memcpy(Buf, Config->DynamicLinker.data(), Config->DynamicLinker.size()); +} + +template <class ELFT> void HashTableSection<ELFT>::addSymbol(SymbolBody *S) { + StringRef Name = S->getName(); + DynSymSec.addSymbol(Name); + Hashes.push_back(hash(Name)); + S->setDynamicSymbolTableIndex(Hashes.size()); +} + +template <class ELFT> void DynamicSection<ELFT>::finalize() { + typename Base::HeaderT &Header = this->Header; + Header.sh_link = DynStrSec.getSectionIndex(); + + unsigned NumEntries = 0; + if (RelaDynSec.hasRelocs()) { + ++NumEntries; // DT_RELA / DT_REL + ++NumEntries; // DT_RELASZ / DTRELSZ + } + ++NumEntries; // DT_SYMTAB + ++NumEntries; // DT_STRTAB + ++NumEntries; // DT_STRSZ + ++NumEntries; // DT_HASH + + StringRef RPath = Config->RPath; + if (!RPath.empty()) { + ++NumEntries; // DT_RUNPATH + DynStrSec.add(RPath); + } + + const std::vector<std::unique_ptr<SharedFileBase>> &SharedFiles = + SymTab.getSharedFiles(); + for (const std::unique_ptr<SharedFileBase> &File : SharedFiles) + DynStrSec.add(File->getName()); + NumEntries += SharedFiles.size(); + + ++NumEntries; // DT_NULL + + Header.sh_size = NumEntries * Header.sh_entsize; +} + +template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) { + typedef typename std::conditional<ELFT::Is64Bits, Elf64_Dyn, Elf32_Dyn>::type + Elf_Dyn; + auto *P = reinterpret_cast<Elf_Dyn *>(Buf); + + if (RelaDynSec.hasRelocs()) { + bool IsRela = RelaDynSec.isRela(); + P->d_tag = IsRela ? DT_RELA : DT_REL; + P->d_un.d_ptr = RelaDynSec.getVA(); + ++P; + + P->d_tag = IsRela ? DT_RELASZ : DT_RELSZ; + P->d_un.d_val = RelaDynSec.getSize(); + ++P; + } + + P->d_tag = DT_SYMTAB; + P->d_un.d_ptr = DynSymSec.getVA(); + ++P; + + P->d_tag = DT_STRTAB; + P->d_un.d_ptr = DynStrSec.getVA(); + ++P; + + P->d_tag = DT_STRSZ; + P->d_un.d_val = DynStrSec.data().size(); + ++P; + + P->d_tag = DT_HASH; + P->d_un.d_ptr = HashSec.getVA(); + ++P; + + StringRef RPath = Config->RPath; + if (!RPath.empty()) { + P->d_tag = DT_RUNPATH; + P->d_un.d_val = DynStrSec.getFileOff(RPath); + ++P; + } + + const std::vector<std::unique_ptr<SharedFileBase>> &SharedFiles = + SymTab.getSharedFiles(); + for (const std::unique_ptr<SharedFileBase> &File : SharedFiles) { + P->d_tag = DT_NEEDED; + P->d_un.d_val = DynStrSec.getFileOff(File->getName()); + ++P; + } + + P->d_tag = DT_NULL; + P->d_un.d_val = 0; + ++P; +} + +template <class ELFT> +void OutputSection<ELFT>::addChunk(InputSection<ELFT> *C) { + Chunks.push_back(C); + C->setOutputSection(this); + uint32_t Align = C->getAlign(); + if (Align > this->Header.sh_addralign) + this->Header.sh_addralign = Align; + + uintX_t Off = this->Header.sh_size; + Off = RoundUpToAlignment(Off, Align); + C->setOutputSectionOff(Off); + Off += C->getSize(); + this->Header.sh_size = Off; +} + +template <class ELFT> +void OutputSection<ELFT>::relocateOne(uint8_t *Buf, const Elf_Rel &Rel, + uint32_t Type, uintX_t BaseAddr, + uintX_t SymVA) { + uintX_t Offset = Rel.r_offset; + uint8_t *Location = Buf + Offset; + switch (Type) { + case R_386_32: + support::endian::write32le(Location, SymVA); + break; + default: + llvm::errs() << Twine("unrecognized reloc ") + Twine(Type) << '\n'; + break; + } +} + +template <class ELFT> +void OutputSection<ELFT>::relocateOne(uint8_t *Buf, const Elf_Rela &Rel, + uint32_t Type, uintX_t BaseAddr, + uintX_t SymVA) { + uintX_t Offset = Rel.r_offset; + uint8_t *Location = Buf + Offset; + switch (Type) { + case R_X86_64_PC32: + support::endian::write32le(Location, + SymVA + (Rel.r_addend - (BaseAddr + Offset))); + break; + case R_X86_64_64: + support::endian::write64le(Location, SymVA + Rel.r_addend); + break; + case R_X86_64_32: { + case R_X86_64_32S: + uint64_t VA = SymVA + Rel.r_addend; + if (Type == R_X86_64_32 && !isUInt<32>(VA)) + error("R_X86_64_32 out of range"); + else if (!isInt<32>(VA)) + error("R_X86_64_32S out of range"); + + support::endian::write32le(Location, VA); + break; + } + default: + llvm::errs() << Twine("unrecognized reloc ") + Twine(Type) << '\n'; + break; + } +} + +template <class ELFT> +typename ELFFile<ELFT>::uintX_t +lld::elf2::getSymVA(const DefinedRegular<ELFT> *DR) { + const InputSection<ELFT> *SC = &DR->Section; + OutputSection<ELFT> *OS = SC->getOutputSection(); + return OS->getVA() + SC->getOutputSectionOff() + DR->Sym.st_value; +} + +template <class ELFT> +typename ELFFile<ELFT>::uintX_t +lld::elf2::getLocalSymVA(const typename ELFFile<ELFT>::Elf_Sym *Sym, + const ObjectFile<ELFT> &File) { + uint32_t SecIndex = Sym->st_shndx; + + if (SecIndex == SHN_XINDEX) + SecIndex = File.getObj()->getExtendedSymbolTableIndex( + Sym, File.getSymbolTable(), File.getSymbolTableShndx()); + ArrayRef<InputSection<ELFT> *> Chunks = File.getChunks(); + InputSection<ELFT> *Section = Chunks[SecIndex]; + OutputSection<ELFT> *Out = Section->getOutputSection(); + return Out->getVA() + Section->getOutputSectionOff() + Sym->st_value; +} + +template <class ELFT> +template <bool isRela> +void OutputSection<ELFT>::relocate( + uint8_t *Buf, iterator_range<const Elf_Rel_Impl<ELFT, isRela> *> Rels, + const ObjectFile<ELFT> &File, uintX_t BaseAddr) { + typedef Elf_Rel_Impl<ELFT, isRela> RelType; + bool IsMips64EL = File.getObj()->isMips64EL(); + for (const RelType &RI : Rels) { + uint32_t SymIndex = RI.getSymbol(IsMips64EL); + uint32_t Type = RI.getType(IsMips64EL); + uintX_t SymVA; + + // Handle relocations for local symbols -- they never get + // resolved so we don't allocate a SymbolBody. + const Elf_Shdr *SymTab = File.getSymbolTable(); + if (SymIndex < SymTab->sh_info) { + const Elf_Sym *Sym = File.getObj()->getRelocationSymbol(&RI, SymTab); + if (!Sym) + continue; + SymVA = getLocalSymVA(Sym, File); + } else { + const SymbolBody *Body = File.getSymbolBody(SymIndex); + if (!Body) + continue; + switch (Body->kind()) { + case SymbolBody::DefinedRegularKind: + SymVA = getSymVA<ELFT>(cast<DefinedRegular<ELFT>>(Body)); + break; + case SymbolBody::DefinedAbsoluteKind: + SymVA = cast<DefinedAbsolute<ELFT>>(Body)->Sym.st_value; + break; + case SymbolBody::DefinedCommonKind: { + auto *DC = cast<DefinedCommon<ELFT>>(Body); + SymVA = DC->OutputSec->getVA() + DC->OffsetInBSS; + break; + } + case SymbolBody::SharedKind: + if (relocNeedsPLT(Type)) { + SymVA = PltSec.getEntryAddr(*Body); + Type = R_X86_64_PC32; + } else if (relocNeedsGOT(Type)) { + SymVA = GotSec.getEntryAddr(*Body); + Type = R_X86_64_PC32; + } else { + continue; + } + break; + case SymbolBody::UndefinedKind: + assert(Body->isWeak() && "Undefined symbol reached writer"); + SymVA = 0; + break; + case SymbolBody::LazyKind: + llvm_unreachable("Lazy symbol reached writer"); + } + } + + relocateOne(Buf, RI, Type, BaseAddr, SymVA); + } +} + +template <class ELFT> void OutputSection<ELFT>::writeTo(uint8_t *Buf) { + for (InputSection<ELFT> *C : Chunks) { + C->writeTo(Buf); + const ObjectFile<ELFT> *File = C->getFile(); + ELFFile<ELFT> *EObj = File->getObj(); + uint8_t *Base = Buf + C->getOutputSectionOff(); + uintX_t BaseAddr = this->getVA() + C->getOutputSectionOff(); + // Iterate over all relocation sections that apply to this section. + for (const Elf_Shdr *RelSec : C->RelocSections) { + if (RelSec->sh_type == SHT_RELA) + relocate(Base, EObj->relas(RelSec), *File, BaseAddr); + else + relocate(Base, EObj->rels(RelSec), *File, BaseAddr); + } + } +} + +template <bool Is64Bits> +void StringTableSection<Is64Bits>::writeTo(uint8_t *Buf) { + StringRef Data = StrTabBuilder.data(); + memcpy(Buf, Data.data(), Data.size()); +} + +bool lld::elf2::includeInSymtab(const SymbolBody &B) { + if (B.isLazy()) + return false; + if (!B.isUsedInRegularObj()) + return false; + uint8_t V = B.getMostConstrainingVisibility(); + if (V != STV_DEFAULT && V != STV_PROTECTED) + return false; + return true; +} + +template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { + const OutputSection<ELFT> *Out = nullptr; + const InputSection<ELFT> *Section = nullptr; + Buf += sizeof(Elf_Sym); + + // All symbols with STB_LOCAL binding precede the weak and global symbols. + // .dynsym only contains global symbols. + if (!Config->DiscardAll && !StrTabSec.isDynamic()) { + for (const std::unique_ptr<ObjectFileBase> &FileB : + Table.getObjectFiles()) { + auto &File = cast<ObjectFile<ELFT>>(*FileB); + Elf_Sym_Range Syms = File.getLocalSymbols(); + for (const Elf_Sym &Sym : Syms) { + auto *ESym = reinterpret_cast<Elf_Sym *>(Buf); + uint32_t SecIndex = Sym.st_shndx; + ErrorOr<StringRef> SymName = Sym.getName(File.getStringTable()); + if (Config->DiscardLocals && SymName->startswith(".L")) + continue; + ESym->st_name = (SymName) ? StrTabSec.getFileOff(*SymName) : 0; + ESym->st_size = Sym.st_size; + ESym->setBindingAndType(Sym.getBinding(), Sym.getType()); + if (SecIndex == SHN_XINDEX) + SecIndex = File.getObj()->getExtendedSymbolTableIndex( + &Sym, File.getSymbolTable(), File.getSymbolTableShndx()); + ArrayRef<InputSection<ELFT> *> Chunks = File.getChunks(); + Section = Chunks[SecIndex]; + assert(Section != nullptr); + Out = Section->getOutputSection(); + assert(Out != nullptr); + ESym->st_shndx = Out->getSectionIndex(); + ESym->st_value = + Out->getVA() + Section->getOutputSectionOff() + Sym.st_value; + Buf += sizeof(Elf_Sym); + } + } + } + + for (auto &P : Table.getSymbols()) { + StringRef Name = P.first; + Symbol *Sym = P.second; + SymbolBody *Body = Sym->Body; + if (!includeInSymtab(*Body)) + continue; + const Elf_Sym &InputSym = cast<ELFSymbolBody<ELFT>>(Body)->Sym; + + auto *ESym = reinterpret_cast<Elf_Sym *>(Buf); + ESym->st_name = StrTabSec.getFileOff(Name); + + Out = nullptr; + Section = nullptr; + + switch (Body->kind()) { + case SymbolBody::DefinedRegularKind: + Section = &cast<DefinedRegular<ELFT>>(Body)->Section; + break; + case SymbolBody::DefinedCommonKind: + Out = BssSec; + break; + case SymbolBody::UndefinedKind: + case SymbolBody::DefinedAbsoluteKind: + case SymbolBody::SharedKind: + break; + case SymbolBody::LazyKind: + llvm_unreachable("Lazy symbol got to output symbol table!"); + } + + ESym->setBindingAndType(InputSym.getBinding(), InputSym.getType()); + ESym->st_size = InputSym.st_size; + ESym->setVisibility(Body->getMostConstrainingVisibility()); + if (InputSym.isAbsolute()) { + ESym->st_shndx = SHN_ABS; + ESym->st_value = InputSym.st_value; + } + + if (Section) + Out = Section->getOutputSection(); + + if (Out) { + ESym->st_shndx = Out->getSectionIndex(); + uintX_t VA = Out->getVA(); + if (Section) + VA += Section->getOutputSectionOff(); + if (auto *C = dyn_cast<DefinedCommon<ELFT>>(Body)) + VA += C->OffsetInBSS; + else + VA += InputSym.st_value; + ESym->st_value = VA; + } + + Buf += sizeof(Elf_Sym); + } +} + +namespace lld { +namespace elf2 { +template class OutputSectionBase<false>; +template class OutputSectionBase<true>; + +template void OutputSectionBase<false>::writeHeaderTo<support::little>( + typename ELFFile<ELFType<support::little, false>>::Elf_Shdr *SHdr); +template void OutputSectionBase<true>::writeHeaderTo<support::little>( + typename ELFFile<ELFType<support::little, true>>::Elf_Shdr *SHdr); +template void OutputSectionBase<false>::writeHeaderTo<support::big>( + typename ELFFile<ELFType<support::big, false>>::Elf_Shdr *SHdr); +template void OutputSectionBase<true>::writeHeaderTo<support::big>( + typename ELFFile<ELFType<support::big, true>>::Elf_Shdr *SHdr); + +template class GotSection<ELF32LE>; +template class GotSection<ELF32BE>; +template class GotSection<ELF64LE>; +template class GotSection<ELF64BE>; + +template class PltSection<ELF32LE>; +template class PltSection<ELF32BE>; +template class PltSection<ELF64LE>; +template class PltSection<ELF64BE>; + +template class RelocationSection<ELF32LE>; +template class RelocationSection<ELF32BE>; +template class RelocationSection<ELF64LE>; +template class RelocationSection<ELF64BE>; + +template class InterpSection<false>; +template class InterpSection<true>; + +template class HashTableSection<ELF32LE>; +template class HashTableSection<ELF32BE>; +template class HashTableSection<ELF64LE>; +template class HashTableSection<ELF64BE>; + +template class DynamicSection<ELF32LE>; +template class DynamicSection<ELF32BE>; +template class DynamicSection<ELF64LE>; +template class DynamicSection<ELF64BE>; + +template class OutputSection<ELF32LE>; +template class OutputSection<ELF32BE>; +template class OutputSection<ELF64LE>; +template class OutputSection<ELF64BE>; + +template class StringTableSection<false>; +template class StringTableSection<true>; + +template class SymbolTableSection<ELF32LE>; +template class SymbolTableSection<ELF32BE>; +template class SymbolTableSection<ELF64LE>; +template class SymbolTableSection<ELF64BE>; + +template typename ELFFile<ELF32LE>::uintX_t +getSymVA(const DefinedRegular<ELF32LE> *DR); + +template typename ELFFile<ELF32BE>::uintX_t +getSymVA(const DefinedRegular<ELF32BE> *DR); + +template typename ELFFile<ELF64LE>::uintX_t +getSymVA(const DefinedRegular<ELF64LE> *DR); + +template typename ELFFile<ELF64BE>::uintX_t +getSymVA(const DefinedRegular<ELF64BE> *DR); +} +} diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h new file mode 100644 index 00000000000..2e100869576 --- /dev/null +++ b/lld/ELF/OutputSections.h @@ -0,0 +1,396 @@ +//===- OutputSections.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_OUTPUT_SECTIONS_H +#define LLD_ELF_OUTPUT_SECTIONS_H + +#include "lld/Core/LLVM.h" + +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/ELF.h" + +#include <type_traits> + +namespace lld { +namespace elf2 { + +class SymbolBody; +class SymbolTable; +template <class ELFT> class SymbolTableSection; +template <bool Is64Bits> class StringTableSection; +template <class ELFT> class InputSection; +template <class ELFT> class OutputSection; +template <class ELFT> class ObjectFile; +template <class ELFT> class DefinedRegular; + +bool relocNeedsPLT(uint32_t Type); +bool relocNeedsGOT(uint32_t Type); + +template <class ELFT> +typename llvm::object::ELFFile<ELFT>::uintX_t +getSymVA(const DefinedRegular<ELFT> *DR); + +template <class ELFT> +typename llvm::object::ELFFile<ELFT>::uintX_t +getLocalSymVA(const typename llvm::object::ELFFile<ELFT>::Elf_Sym *Sym, + const ObjectFile<ELFT> &File); + +bool includeInSymtab(const SymbolBody &B); + +// OutputSection represents a section in an output file. It's a +// container of chunks. OutputSection and Chunk are 1:N relationship. +// Chunks cannot belong to more than one OutputSections. The writer +// creates multiple OutputSections and assign them unique, +// non-overlapping file offsets and VAs. +template <bool Is64Bits> class OutputSectionBase { +public: + typedef typename std::conditional<Is64Bits, uint64_t, uint32_t>::type uintX_t; + typedef typename std::conditional<Is64Bits, llvm::ELF::Elf64_Shdr, + llvm::ELF::Elf32_Shdr>::type HeaderT; + + OutputSectionBase(StringRef Name, uint32_t sh_type, uintX_t sh_flags); + void setVA(uintX_t VA) { Header.sh_addr = VA; } + uintX_t getVA() const { return Header.sh_addr; } + void setFileOffset(uintX_t Off) { Header.sh_offset = Off; } + template <llvm::object::endianness E> + void writeHeaderTo(typename llvm::object::ELFFile< + llvm::object::ELFType<E, Is64Bits>>::Elf_Shdr *SHdr); + StringRef getName() { return Name; } + void setNameOffset(uintX_t Offset) { Header.sh_name = Offset; } + + unsigned getSectionIndex() const { return SectionIndex; } + void setSectionIndex(unsigned I) { SectionIndex = I; } + + // Returns the size of the section in the output file. + uintX_t getSize() { return Header.sh_size; } + void setSize(uintX_t Val) { Header.sh_size = Val; } + uintX_t getFlags() { return Header.sh_flags; } + uintX_t getFileOff() { return Header.sh_offset; } + uintX_t getAlign() { + // The ELF spec states that a value of 0 means the section has no alignment + // constraits. + return std::max<uintX_t>(Header.sh_addralign, 1); + } + uint32_t getType() { return Header.sh_type; } + + static unsigned getAddrSize() { return Is64Bits ? 8 : 4; } + + virtual void finalize() {} + virtual void writeTo(uint8_t *Buf) = 0; + +protected: + StringRef Name; + HeaderT Header; + unsigned SectionIndex; + ~OutputSectionBase() = default; +}; + +template <class ELFT> +class GotSection final : public OutputSectionBase<ELFT::Is64Bits> { + typedef OutputSectionBase<ELFT::Is64Bits> Base; + typedef typename Base::uintX_t uintX_t; + +public: + GotSection() + : OutputSectionBase<ELFT::Is64Bits>(".got", llvm::ELF::SHT_PROGBITS, + llvm::ELF::SHF_ALLOC | + llvm::ELF::SHF_WRITE) { + this->Header.sh_addralign = this->getAddrSize(); + } + void finalize() override { + this->Header.sh_size = Entries.size() * this->getAddrSize(); + } + void writeTo(uint8_t *Buf) override {} + void addEntry(SymbolBody *Sym); + bool empty() const { return Entries.empty(); } + uintX_t getEntryAddr(const SymbolBody &B) const; + +private: + std::vector<const SymbolBody *> Entries; +}; + +template <class ELFT> +class PltSection final : public OutputSectionBase<ELFT::Is64Bits> { + typedef OutputSectionBase<ELFT::Is64Bits> Base; + typedef typename Base::uintX_t uintX_t; + +public: + PltSection(const GotSection<ELFT> &GotSec) + : OutputSectionBase<ELFT::Is64Bits>(".plt", llvm::ELF::SHT_PROGBITS, + llvm::ELF::SHF_ALLOC | + llvm::ELF::SHF_EXECINSTR), + GotSec(GotSec) { + this->Header.sh_addralign = 16; + } + void finalize() override { + this->Header.sh_size = Entries.size() * EntrySize; + } + void writeTo(uint8_t *Buf) override; + void addEntry(SymbolBody *Sym); + bool empty() const { return Entries.empty(); } + uintX_t getEntryAddr(const SymbolBody &B) const; + static const unsigned EntrySize = 8; + +private: + std::vector<const SymbolBody *> Entries; + const GotSection<ELFT> &GotSec; +}; + +template <class ELFT> struct DynamicReloc { + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel; + const InputSection<ELFT> &C; + const Elf_Rel &RI; +}; + +template <class ELFT> +class SymbolTableSection final : public OutputSectionBase<ELFT::Is64Bits> { +public: + typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range; + typedef typename OutputSectionBase<ELFT::Is64Bits>::uintX_t uintX_t; + SymbolTableSection(SymbolTable &Table, + StringTableSection<ELFT::Is64Bits> &StrTabSec) + : OutputSectionBase<ELFT::Is64Bits>( + StrTabSec.isDynamic() ? ".dynsym" : ".symtab", + StrTabSec.isDynamic() ? llvm::ELF::SHT_DYNSYM + : llvm::ELF::SHT_SYMTAB, + StrTabSec.isDynamic() ? (uintX_t)llvm::ELF::SHF_ALLOC : 0), + Table(Table), StrTabSec(StrTabSec) { + typedef OutputSectionBase<ELFT::Is64Bits> Base; + typename Base::HeaderT &Header = this->Header; + + Header.sh_entsize = sizeof(Elf_Sym); + Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; + } + + void finalize() override { + this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym); + this->Header.sh_link = StrTabSec.getSectionIndex(); + this->Header.sh_info = NumLocals + 1; + } + + void writeTo(uint8_t *Buf) override; + + const SymbolTable &getSymTable() const { return Table; } + + void addSymbol(StringRef Name, bool isLocal = false) { + StrTabSec.add(Name); + ++NumVisible; + if (isLocal) + ++NumLocals; + } + + StringTableSection<ELFT::Is64Bits> &getStrTabSec() { return StrTabSec; } + unsigned getNumSymbols() const { return NumVisible + 1; } + void setBssSec(const OutputSection<ELFT> *V) { BssSec = V; } + +private: + SymbolTable &Table; + StringTableSection<ELFT::Is64Bits> &StrTabSec; + unsigned NumVisible = 0; + unsigned NumLocals = 0; + const OutputSection<ELFT> *BssSec = nullptr; +}; + +template <class ELFT> +class RelocationSection final : public OutputSectionBase<ELFT::Is64Bits> { + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela; + +public: + RelocationSection(SymbolTableSection<ELFT> &DynSymSec, + const GotSection<ELFT> &GotSec, bool IsRela) + : OutputSectionBase<ELFT::Is64Bits>(IsRela ? ".rela.dyn" : ".rel.dyn", + IsRela ? llvm::ELF::SHT_RELA + : llvm::ELF::SHT_REL, + llvm::ELF::SHF_ALLOC), + DynSymSec(DynSymSec), GotSec(GotSec), IsRela(IsRela) { + this->Header.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); + this->Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; + } + + void addReloc(const DynamicReloc<ELFT> &Reloc) { Relocs.push_back(Reloc); } + void finalize() override; + void writeTo(uint8_t *Buf) override; + bool hasRelocs() const { return !Relocs.empty(); } + bool isRela() const { return IsRela; } + +private: + std::vector<DynamicReloc<ELFT>> Relocs; + SymbolTableSection<ELFT> &DynSymSec; + const GotSection<ELFT> &GotSec; + const bool IsRela; +}; + +template <class ELFT> +class OutputSection final : public OutputSectionBase<ELFT::Is64Bits> { +public: + typedef typename OutputSectionBase<ELFT::Is64Bits>::uintX_t uintX_t; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela; + OutputSection(const PltSection<ELFT> &PltSec, const GotSection<ELFT> &GotSec, + StringRef Name, uint32_t sh_type, uintX_t sh_flags) + : OutputSectionBase<ELFT::Is64Bits>(Name, sh_type, sh_flags), + PltSec(PltSec), GotSec(GotSec) {} + + void addChunk(InputSection<ELFT> *C); + void writeTo(uint8_t *Buf) override; + + template <bool isRela> + void relocate(uint8_t *Buf, + llvm::iterator_range< + const llvm::object::Elf_Rel_Impl<ELFT, isRela> *> Rels, + const ObjectFile<ELFT> &File, uintX_t BaseAddr); + + void relocateOne(uint8_t *Buf, const Elf_Rela &Rel, uint32_t Type, + uintX_t BaseAddr, uintX_t SymVA); + void relocateOne(uint8_t *Buf, const Elf_Rel &Rel, uint32_t Type, + uintX_t BaseAddr, uintX_t SymVA); + +private: + std::vector<InputSection<ELFT> *> Chunks; + const PltSection<ELFT> &PltSec; + const GotSection<ELFT> &GotSec; +}; + +template <bool Is64Bits> +class InterpSection final : public OutputSectionBase<Is64Bits> { +public: + InterpSection(); + + void writeTo(uint8_t *Buf); +}; + +template <bool Is64Bits> +class StringTableSection final : public OutputSectionBase<Is64Bits> { +public: + typedef typename OutputSectionBase<Is64Bits>::uintX_t uintX_t; + StringTableSection(bool Dynamic) + : OutputSectionBase<Is64Bits>( + Dynamic ? ".dynstr" : ".strtab", llvm::ELF::SHT_STRTAB, + Dynamic ? (uintX_t)llvm::ELF::SHF_ALLOC : 0), + Dynamic(Dynamic) { + this->Header.sh_addralign = 1; + } + + void add(StringRef S) { StrTabBuilder.add(S); } + size_t getFileOff(StringRef S) const { return StrTabBuilder.getOffset(S); } + StringRef data() const { return StrTabBuilder.data(); } + void writeTo(uint8_t *Buf) override; + + void finalize() override { + StrTabBuilder.finalize(llvm::StringTableBuilder::ELF); + this->Header.sh_size = StrTabBuilder.data().size(); + } + + bool isDynamic() const { return Dynamic; } + +private: + const bool Dynamic; + llvm::StringTableBuilder StrTabBuilder; +}; + +template <class ELFT> +class HashTableSection final : public OutputSectionBase<ELFT::Is64Bits> { + typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word; + +public: + HashTableSection(SymbolTableSection<ELFT> &DynSymSec) + : OutputSectionBase<ELFT::Is64Bits>(".hash", llvm::ELF::SHT_HASH, + llvm::ELF::SHF_ALLOC), + DynSymSec(DynSymSec) { + this->Header.sh_entsize = sizeof(Elf_Word); + this->Header.sh_addralign = sizeof(Elf_Word); + } + + void addSymbol(SymbolBody *S); + + void finalize() override { + this->Header.sh_link = DynSymSec.getSectionIndex(); + + assert(DynSymSec.getNumSymbols() == Hashes.size() + 1); + unsigned NumEntries = 2; // nbucket and nchain. + NumEntries += DynSymSec.getNumSymbols(); // The chain entries. + + // Create as many buckets as there are symbols. + // FIXME: This is simplistic. We can try to optimize it, but implementing + // support for SHT_GNU_HASH is probably even more profitable. + NumEntries += DynSymSec.getNumSymbols(); + this->Header.sh_size = NumEntries * sizeof(Elf_Word); + } + + void writeTo(uint8_t *Buf) override { + unsigned NumSymbols = DynSymSec.getNumSymbols(); + auto *P = reinterpret_cast<Elf_Word *>(Buf); + *P++ = NumSymbols; // nbucket + *P++ = NumSymbols; // nchain + + Elf_Word *Buckets = P; + Elf_Word *Chains = P + NumSymbols; + + for (unsigned I = 1; I < NumSymbols; ++I) { + uint32_t Hash = Hashes[I - 1] % NumSymbols; + Chains[I] = Buckets[Hash]; + Buckets[Hash] = I; + } + } + + SymbolTableSection<ELFT> &getDynSymSec() { return DynSymSec; } + +private: + uint32_t hash(StringRef Name) { + uint32_t H = 0; + for (char C : Name) { + H = (H << 4) + C; + uint32_t G = H & 0xf0000000; + if (G) + H ^= G >> 24; + H &= ~G; + } + return H; + } + SymbolTableSection<ELFT> &DynSymSec; + std::vector<uint32_t> Hashes; +}; + +template <class ELFT> +class DynamicSection final : public OutputSectionBase<ELFT::Is64Bits> { + typedef OutputSectionBase<ELFT::Is64Bits> Base; + typedef typename Base::HeaderT HeaderT; + +public: + DynamicSection(SymbolTable &SymTab, HashTableSection<ELFT> &HashSec, + RelocationSection<ELFT> &RelaDynSec) + : OutputSectionBase<ELFT::Is64Bits>(".dynamic", llvm::ELF::SHT_DYNAMIC, + llvm::ELF::SHF_ALLOC | + llvm::ELF::SHF_WRITE), + HashSec(HashSec), DynSymSec(HashSec.getDynSymSec()), + DynStrSec(DynSymSec.getStrTabSec()), RelaDynSec(RelaDynSec), + SymTab(SymTab) { + typename Base::HeaderT &Header = this->Header; + Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; + Header.sh_entsize = ELFT::Is64Bits ? 16 : 8; + } + + void finalize() override; + void writeTo(uint8_t *Buf) override; + +private: + HashTableSection<ELFT> &HashSec; + SymbolTableSection<ELFT> &DynSymSec; + StringTableSection<ELFT::Is64Bits> &DynStrSec; + RelocationSection<ELFT> &RelaDynSec; + SymbolTable &SymTab; +}; +} +} +#endif diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 00d99890b5d..5253bace667 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -8,17 +8,11 @@ //===----------------------------------------------------------------------===// #include "Writer.h" -#include "Chunks.h" #include "Config.h" -#include "Error.h" -#include "Symbols.h" +#include "OutputSections.h" #include "SymbolTable.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::ELF; @@ -39,525 +33,6 @@ static const int PageSize = 4096; static const int VAStart = 0x10000; namespace { -// OutputSection represents a section in an output file. It's a -// container of chunks. OutputSection and Chunk are 1:N relationship. -// Chunks cannot belong to more than one OutputSections. The writer -// creates multiple OutputSections and assign them unique, -// non-overlapping file offsets and VAs. -template <bool Is64Bits> class OutputSectionBase { -public: - typedef - typename std::conditional<Is64Bits, Elf64_Dyn, Elf32_Dyn>::type Elf_Dyn; - typedef typename std::conditional<Is64Bits, uint64_t, uint32_t>::type uintX_t; - typedef - typename std::conditional<Is64Bits, Elf64_Shdr, Elf32_Shdr>::type HeaderT; - - OutputSectionBase(StringRef Name, uint32_t sh_type, uintX_t sh_flags) - : Name(Name) { - memset(&Header, 0, sizeof(HeaderT)); - Header.sh_type = sh_type; - Header.sh_flags = sh_flags; - } - void setVA(uintX_t VA) { Header.sh_addr = VA; } - uintX_t getVA() const { return Header.sh_addr; } - void setFileOffset(uintX_t Off) { Header.sh_offset = Off; } - template <endianness E> - void writeHeaderTo(typename ELFFile<ELFType<E, Is64Bits>>::Elf_Shdr *SHdr); - StringRef getName() { return Name; } - void setNameOffset(uintX_t Offset) { Header.sh_name = Offset; } - - unsigned getSectionIndex() const { return SectionIndex; } - void setSectionIndex(unsigned I) { SectionIndex = I; } - - // Returns the size of the section in the output file. - uintX_t getSize() { return Header.sh_size; } - void setSize(uintX_t Val) { Header.sh_size = Val; } - uintX_t getFlags() { return Header.sh_flags; } - uintX_t getFileOff() { return Header.sh_offset; } - uintX_t getAlign() { - // The ELF spec states that a value of 0 means the section has no alignment - // constraits. - return std::max<uintX_t>(Header.sh_addralign, 1); - } - uint32_t getType() { return Header.sh_type; } - - static unsigned getAddrSize() { return Is64Bits ? 8 : 4; } - - virtual void finalize() {} - virtual void writeTo(uint8_t *Buf) = 0; - -protected: - StringRef Name; - HeaderT Header; - unsigned SectionIndex; - ~OutputSectionBase() = default; -}; -template <class ELFT> class SymbolTableSection; - -template <class ELFT> struct DynamicReloc { - typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel; - const InputSection<ELFT> &C; - const Elf_Rel &RI; -}; - -static bool relocNeedsPLT(uint32_t Type) { - switch (Type) { - default: - return false; - case R_X86_64_PLT32: - return true; - } -} - -static bool relocNeedsGOT(uint32_t Type) { - if (relocNeedsPLT(Type)) - return true; - switch (Type) { - default: - return false; - case R_X86_64_GOTPCREL: - return true; - } -} - -template <class ELFT> -class GotSection final : public OutputSectionBase<ELFT::Is64Bits> { - typedef OutputSectionBase<ELFT::Is64Bits> Base; - typedef typename Base::uintX_t uintX_t; - -public: - GotSection() - : OutputSectionBase<ELFT::Is64Bits>(".got", SHT_PROGBITS, - SHF_ALLOC | SHF_WRITE) { - this->Header.sh_addralign = this->getAddrSize(); - } - void finalize() override { - this->Header.sh_size = Entries.size() * this->getAddrSize(); - } - void writeTo(uint8_t *Buf) override {} - void addEntry(SymbolBody *Sym) { - Sym->setGotIndex(Entries.size()); - Entries.push_back(Sym); - } - bool empty() const { return Entries.empty(); } - uintX_t getEntryAddr(const SymbolBody &B) const { - return this->getVA() + B.getGotIndex() * this->getAddrSize(); - } - -private: - std::vector<const SymbolBody *> Entries; -}; - -template <class ELFT> -class PltSection final : public OutputSectionBase<ELFT::Is64Bits> { - typedef OutputSectionBase<ELFT::Is64Bits> Base; - typedef typename Base::uintX_t uintX_t; - -public: - PltSection(const GotSection<ELFT> &GotSec) - : OutputSectionBase<ELFT::Is64Bits>(".plt", SHT_PROGBITS, - SHF_ALLOC | SHF_EXECINSTR), - GotSec(GotSec) { - this->Header.sh_addralign = 16; - } - void finalize() override { - this->Header.sh_size = Entries.size() * EntrySize; - } - void writeTo(uint8_t *Buf) override { - uintptr_t Start = reinterpret_cast<uintptr_t>(Buf); - ArrayRef<uint8_t> Jmp = {0xff, 0x25}; // jmpq *val(%rip) - for (const SymbolBody *E : Entries) { - uintptr_t InstPos = reinterpret_cast<uintptr_t>(Buf); - - memcpy(Buf, Jmp.data(), Jmp.size()); - Buf += Jmp.size(); - - uintptr_t OffsetInPLT = (InstPos + 6) - Start; - uintptr_t Delta = GotSec.getEntryAddr(*E) - (this->getVA() + OffsetInPLT); - assert(isInt<32>(Delta)); - support::endian::write32le(Buf, Delta); - Buf += 4; - - *Buf = 0x90; // nop - ++Buf; - *Buf = 0x90; // nop - ++Buf; - } - } - void addEntry(SymbolBody *Sym) { - Sym->setPltIndex(Entries.size()); - Entries.push_back(Sym); - } - bool empty() const { return Entries.empty(); } - uintX_t getEntryAddr(const SymbolBody &B) const { - return this->getVA() + B.getPltIndex() * EntrySize; - } - - static const unsigned EntrySize = 8; - -private: - std::vector<const SymbolBody *> Entries; - const GotSection<ELFT> &GotSec; -}; - -template <class ELFT> -class RelocationSection final : public OutputSectionBase<ELFT::Is64Bits> { - typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel; - typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela; - -public: - RelocationSection(SymbolTableSection<ELFT> &DynSymSec, - const GotSection<ELFT> &GotSec, bool IsRela) - : OutputSectionBase<ELFT::Is64Bits>(IsRela ? ".rela.dyn" : ".rel.dyn", - IsRela ? SHT_RELA : SHT_REL, - SHF_ALLOC), - DynSymSec(DynSymSec), GotSec(GotSec), IsRela(IsRela) { - this->Header.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); - this->Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; - } - - void addReloc(const DynamicReloc<ELFT> &Reloc) { Relocs.push_back(Reloc); } - void finalize() override { - this->Header.sh_link = DynSymSec.getSectionIndex(); - this->Header.sh_size = Relocs.size() * this->Header.sh_entsize; - } - void writeTo(uint8_t *Buf) override { - auto *P = reinterpret_cast<Elf_Rela *>(Buf); - bool IsMips64EL = Relocs[0].C.getFile()->getObj()->isMips64EL(); - for (const DynamicReloc<ELFT> &Rel : Relocs) { - const InputSection<ELFT> &C = Rel.C; - const Elf_Rel &RI = Rel.RI; - OutputSection<ELFT> *Out = C.getOutputSection(); - uint32_t SymIndex = RI.getSymbol(IsMips64EL); - const SymbolBody *Body = C.getFile()->getSymbolBody(SymIndex); - uint32_t Type = RI.getType(IsMips64EL); - if (relocNeedsGOT(Type)) { - P->r_offset = GotSec.getEntryAddr(*Body); - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), - R_X86_64_GLOB_DAT, IsMips64EL); - } else { - P->r_offset = RI.r_offset + C.getOutputSectionOff() + Out->getVA(); - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), Type, - IsMips64EL); - if (IsRela) - P->r_addend = static_cast<const Elf_Rela &>(RI).r_addend; - } - - ++P; - } - } - bool hasRelocs() const { return !Relocs.empty(); } - bool isRela() const { return IsRela; } - -private: - std::vector<DynamicReloc<ELFT>> Relocs; - SymbolTableSection<ELFT> &DynSymSec; - const GotSection<ELFT> &GotSec; - const bool IsRela; -}; -} - -template <class ELFT> -class lld::elf2::OutputSection final - : public OutputSectionBase<ELFT::Is64Bits> { -public: - typedef typename OutputSectionBase<ELFT::Is64Bits>::uintX_t uintX_t; - typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr; - typedef typename ELFFile<ELFT>::Elf_Sym Elf_Sym; - typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel; - typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela; - OutputSection(const PltSection<ELFT> &PltSec, const GotSection<ELFT> &GotSec, - StringRef Name, uint32_t sh_type, uintX_t sh_flags) - : OutputSectionBase<ELFT::Is64Bits>(Name, sh_type, sh_flags), - PltSec(PltSec), GotSec(GotSec) {} - - void addChunk(InputSection<ELFT> *C); - void writeTo(uint8_t *Buf) override; - - template <bool isRela> - void relocate(uint8_t *Buf, - iterator_range<const Elf_Rel_Impl<ELFT, isRela> *> Rels, - const ObjectFile<ELFT> &File, uintX_t BaseAddr); - - void relocateOne(uint8_t *Buf, const Elf_Rela &Rel, uint32_t Type, - uintX_t BaseAddr, uintX_t SymVA); - void relocateOne(uint8_t *Buf, const Elf_Rel &Rel, uint32_t Type, - uintX_t BaseAddr, uintX_t SymVA); - -private: - std::vector<InputSection<ELFT> *> Chunks; - const PltSection<ELFT> &PltSec; - const GotSection<ELFT> &GotSec; -}; - -namespace { -template <bool Is64Bits> -class InterpSection final : public OutputSectionBase<Is64Bits> { -public: - InterpSection() - : OutputSectionBase<Is64Bits>(".interp", SHT_PROGBITS, SHF_ALLOC) { - this->Header.sh_size = Config->DynamicLinker.size() + 1; - this->Header.sh_addralign = 1; - } - - void writeTo(uint8_t *Buf) override { - memcpy(Buf, Config->DynamicLinker.data(), Config->DynamicLinker.size()); - } -}; - -template <bool Is64Bits> -class StringTableSection final : public OutputSectionBase<Is64Bits> { -public: - typedef typename OutputSectionBase<Is64Bits>::uintX_t uintX_t; - StringTableSection(bool Dynamic) - : OutputSectionBase<Is64Bits>(Dynamic ? ".dynstr" : ".strtab", SHT_STRTAB, - Dynamic ? (uintX_t)SHF_ALLOC : 0), - Dynamic(Dynamic) { - this->Header.sh_addralign = 1; - } - - void add(StringRef S) { StrTabBuilder.add(S); } - size_t getFileOff(StringRef S) const { return StrTabBuilder.getOffset(S); } - StringRef data() const { return StrTabBuilder.data(); } - void writeTo(uint8_t *Buf) override; - - void finalize() override { - StrTabBuilder.finalize(StringTableBuilder::ELF); - this->Header.sh_size = StrTabBuilder.data().size(); - } - - bool isDynamic() const { return Dynamic; } - -private: - const bool Dynamic; - llvm::StringTableBuilder StrTabBuilder; -}; - -template <class ELFT> class Writer; - -template <class ELFT> -class SymbolTableSection final : public OutputSectionBase<ELFT::Is64Bits> { -public: - typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr; - typedef typename ELFFile<ELFT>::Elf_Sym Elf_Sym; - typedef typename ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range; - typedef typename OutputSectionBase<ELFT::Is64Bits>::uintX_t uintX_t; - SymbolTableSection(Writer<ELFT> &W, SymbolTable &Table, - StringTableSection<ELFT::Is64Bits> &StrTabSec) - : OutputSectionBase<ELFT::Is64Bits>( - StrTabSec.isDynamic() ? ".dynsym" : ".symtab", - StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB, - StrTabSec.isDynamic() ? (uintX_t)SHF_ALLOC : 0), - Table(Table), StrTabSec(StrTabSec), W(W) { - typedef OutputSectionBase<ELFT::Is64Bits> Base; - typename Base::HeaderT &Header = this->Header; - - Header.sh_entsize = sizeof(Elf_Sym); - Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; - } - - void finalize() override { - this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym); - this->Header.sh_link = StrTabSec.getSectionIndex(); - this->Header.sh_info = NumLocals + 1; - } - - void writeTo(uint8_t *Buf) override; - - const SymbolTable &getSymTable() const { return Table; } - - void addSymbol(StringRef Name, bool isLocal = false) { - StrTabSec.add(Name); - ++NumVisible; - if (isLocal) - ++NumLocals; - } - - StringTableSection<ELFT::Is64Bits> &getStrTabSec() { return StrTabSec; } - unsigned getNumSymbols() const { return NumVisible + 1; } - -private: - SymbolTable &Table; - StringTableSection<ELFT::Is64Bits> &StrTabSec; - unsigned NumVisible = 0; - unsigned NumLocals = 0; - const Writer<ELFT> &W; -}; - -template <class ELFT> -class HashTableSection final : public OutputSectionBase<ELFT::Is64Bits> { - typedef typename ELFFile<ELFT>::Elf_Word Elf_Word; - -public: - HashTableSection(SymbolTableSection<ELFT> &DynSymSec) - : OutputSectionBase<ELFT::Is64Bits>(".hash", SHT_HASH, SHF_ALLOC), - DynSymSec(DynSymSec) { - this->Header.sh_entsize = sizeof(Elf_Word); - this->Header.sh_addralign = sizeof(Elf_Word); - } - - void addSymbol(SymbolBody *S) { - StringRef Name = S->getName(); - DynSymSec.addSymbol(Name); - Hashes.push_back(hash(Name)); - S->setDynamicSymbolTableIndex(Hashes.size()); - } - - void finalize() override { - this->Header.sh_link = DynSymSec.getSectionIndex(); - - assert(DynSymSec.getNumSymbols() == Hashes.size() + 1); - unsigned NumEntries = 2; // nbucket and nchain. - NumEntries += DynSymSec.getNumSymbols(); // The chain entries. - - // Create as many buckets as there are symbols. - // FIXME: This is simplistic. We can try to optimize it, but implementing - // support for SHT_GNU_HASH is probably even more profitable. - NumEntries += DynSymSec.getNumSymbols(); - this->Header.sh_size = NumEntries * sizeof(Elf_Word); - } - - void writeTo(uint8_t *Buf) override { - unsigned NumSymbols = DynSymSec.getNumSymbols(); - auto *P = reinterpret_cast<Elf_Word *>(Buf); - *P++ = NumSymbols; // nbucket - *P++ = NumSymbols; // nchain - - Elf_Word *Buckets = P; - Elf_Word *Chains = P + NumSymbols; - - for (unsigned I = 1; I < NumSymbols; ++I) { - uint32_t Hash = Hashes[I - 1] % NumSymbols; - Chains[I] = Buckets[Hash]; - Buckets[Hash] = I; - } - } - - SymbolTableSection<ELFT> &getDynSymSec() { return DynSymSec; } - -private: - uint32_t hash(StringRef Name) { - uint32_t H = 0; - for (char C : Name) { - H = (H << 4) + C; - uint32_t G = H & 0xf0000000; - if (G) - H ^= G >> 24; - H &= ~G; - } - return H; - } - SymbolTableSection<ELFT> &DynSymSec; - std::vector<uint32_t> Hashes; -}; - -template <class ELFT> -class DynamicSection final : public OutputSectionBase<ELFT::Is64Bits> { - typedef OutputSectionBase<ELFT::Is64Bits> Base; - typedef typename Base::HeaderT HeaderT; - typedef typename Base::Elf_Dyn Elf_Dyn; - -public: - DynamicSection(SymbolTable &SymTab, HashTableSection<ELFT> &HashSec, - RelocationSection<ELFT> &RelaDynSec) - : OutputSectionBase<ELFT::Is64Bits>(".dynamic", SHT_DYNAMIC, - SHF_ALLOC | SHF_WRITE), - HashSec(HashSec), DynSymSec(HashSec.getDynSymSec()), - DynStrSec(DynSymSec.getStrTabSec()), RelaDynSec(RelaDynSec), - SymTab(SymTab) { - typename Base::HeaderT &Header = this->Header; - Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; - Header.sh_entsize = ELFT::Is64Bits ? 16 : 8; - } - - void finalize() override { - typename Base::HeaderT &Header = this->Header; - Header.sh_link = DynStrSec.getSectionIndex(); - - unsigned NumEntries = 0; - if (RelaDynSec.hasRelocs()) { - ++NumEntries; // DT_RELA / DT_REL - ++NumEntries; // DT_RELASZ / DTRELSZ - } - ++NumEntries; // DT_SYMTAB - ++NumEntries; // DT_STRTAB - ++NumEntries; // DT_STRSZ - ++NumEntries; // DT_HASH - - StringRef RPath = Config->RPath; - if (!RPath.empty()) { - ++NumEntries; // DT_RUNPATH - DynStrSec.add(RPath); - } - - const std::vector<std::unique_ptr<SharedFileBase>> &SharedFiles = - SymTab.getSharedFiles(); - for (const std::unique_ptr<SharedFileBase> &File : SharedFiles) - DynStrSec.add(File->getName()); - NumEntries += SharedFiles.size(); - - ++NumEntries; // DT_NULL - - Header.sh_size = NumEntries * Header.sh_entsize; - } - - void writeTo(uint8_t *Buf) override { - auto *P = reinterpret_cast<Elf_Dyn *>(Buf); - - if (RelaDynSec.hasRelocs()) { - bool IsRela = RelaDynSec.isRela(); - P->d_tag = IsRela ? DT_RELA : DT_REL; - P->d_un.d_ptr = RelaDynSec.getVA(); - ++P; - - P->d_tag = IsRela ? DT_RELASZ : DT_RELSZ; - P->d_un.d_val = RelaDynSec.getSize(); - ++P; - } - - P->d_tag = DT_SYMTAB; - P->d_un.d_ptr = DynSymSec.getVA(); - ++P; - - P->d_tag = DT_STRTAB; - P->d_un.d_ptr = DynStrSec.getVA(); - ++P; - - P->d_tag = DT_STRSZ; - P->d_un.d_val = DynStrSec.data().size(); - ++P; - - P->d_tag = DT_HASH; - P->d_un.d_ptr = HashSec.getVA(); - ++P; - - StringRef RPath = Config->RPath; - if (!RPath.empty()) { - P->d_tag = DT_RUNPATH; - P->d_un.d_val = DynStrSec.getFileOff(RPath); - ++P; - } - - const std::vector<std::unique_ptr<SharedFileBase>> &SharedFiles = - SymTab.getSharedFiles(); - for (const std::unique_ptr<SharedFileBase> &File : SharedFiles) { - P->d_tag = DT_NEEDED; - P->d_un.d_val = DynStrSec.getFileOff(File->getName()); - ++P; - } - - P->d_tag = DT_NULL; - P->d_un.d_val = 0; - ++P; - } - -private: - HashTableSection<ELFT> &HashSec; - SymbolTableSection<ELFT> &DynSymSec; - StringTableSection<ELFT::Is64Bits> &DynStrSec; - RelocationSection<ELFT> &RelaDynSec; - SymbolTable &SymTab; -}; static uint32_t convertSectionFlagsToPHDRFlags(uint64_t Flags) { uint32_t Ret = PF_R; @@ -621,16 +96,11 @@ public: typedef typename ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range; typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela; Writer(SymbolTable *T) - : SymTabSec(*this, *T, StrTabSec), DynSymSec(*this, *T, DynStrSec), + : SymTabSec(*T, StrTabSec), DynSymSec(*T, DynStrSec), RelaDynSec(DynSymSec, GotSec, T->shouldUseRela()), PltSec(GotSec), HashSec(DynSymSec), DynamicSec(*T, HashSec, RelaDynSec) {} void run(); - const OutputSection<ELFT> &getBSS() const { - assert(BSSSec); - return *BSSSec; - } - private: void createSections(); template <bool isRela> @@ -669,8 +139,8 @@ private: StringTableSection<ELFT::Is64Bits> StrTabSec = { /*dynamic=*/false }; StringTableSection<ELFT::Is64Bits> DynStrSec = { /*dynamic=*/true }; - SymbolTableSection<ELFT> SymTabSec; - SymbolTableSection<ELFT> DynSymSec; + lld::elf2::SymbolTableSection<ELFT> SymTabSec; + lld::elf2::SymbolTableSection<ELFT> DynSymSec; RelocationSection<ELFT> RelaDynSec; @@ -712,174 +182,6 @@ template <class ELFT> void Writer<ELFT>::run() { } template <class ELFT> -void OutputSection<ELFT>::addChunk(InputSection<ELFT> *C) { - Chunks.push_back(C); - C->setOutputSection(this); - uint32_t Align = C->getAlign(); - if (Align > this->Header.sh_addralign) - this->Header.sh_addralign = Align; - - uintX_t Off = this->Header.sh_size; - Off = RoundUpToAlignment(Off, Align); - C->setOutputSectionOff(Off); - Off += C->getSize(); - this->Header.sh_size = Off; -} - -template <class ELFT> -static typename ELFFile<ELFT>::uintX_t -getSymVA(const DefinedRegular<ELFT> *DR) { - const InputSection<ELFT> *SC = &DR->Section; - OutputSection<ELFT> *OS = SC->getOutputSection(); - return OS->getVA() + SC->getOutputSectionOff() + DR->Sym.st_value; -} - -template <class ELFT> -static typename ELFFile<ELFT>::uintX_t -getLocalSymVA(const typename ELFFile<ELFT>::Elf_Sym *Sym, - const ObjectFile<ELFT> &File) { - uint32_t SecIndex = Sym->st_shndx; - - if (SecIndex == SHN_XINDEX) - SecIndex = File.getObj()->getExtendedSymbolTableIndex( - Sym, File.getSymbolTable(), File.getSymbolTableShndx()); - ArrayRef<InputSection<ELFT> *> Chunks = File.getChunks(); - InputSection<ELFT> *Section = Chunks[SecIndex]; - OutputSection<ELFT> *Out = Section->getOutputSection(); - return Out->getVA() + Section->getOutputSectionOff() + Sym->st_value; -} - -template <class ELFT> -void OutputSection<ELFT>::relocateOne(uint8_t *Buf, const Elf_Rel &Rel, - uint32_t Type, uintX_t BaseAddr, - uintX_t SymVA) { - uintX_t Offset = Rel.r_offset; - uint8_t *Location = Buf + Offset; - switch (Type) { - case R_386_32: - support::endian::write32le(Location, SymVA); - break; - default: - llvm::errs() << Twine("unrecognized reloc ") + Twine(Type) << '\n'; - break; - } -} - -template <class ELFT> -void OutputSection<ELFT>::relocateOne(uint8_t *Buf, const Elf_Rela &Rel, - uint32_t Type, uintX_t BaseAddr, - uintX_t SymVA) { - uintX_t Offset = Rel.r_offset; - uint8_t *Location = Buf + Offset; - switch (Type) { - case R_X86_64_PC32: - support::endian::write32le(Location, - SymVA + (Rel.r_addend - (BaseAddr + Offset))); - break; - case R_X86_64_64: - support::endian::write64le(Location, SymVA + Rel.r_addend); - break; - case R_X86_64_32: { - case R_X86_64_32S: - uint64_t VA = SymVA + Rel.r_addend; - if (Type == R_X86_64_32 && !isUInt<32>(VA)) - error("R_X86_64_32 out of range"); - else if (!isInt<32>(VA)) - error("R_X86_64_32S out of range"); - - support::endian::write32le(Location, VA); - break; - } - default: - llvm::errs() << Twine("unrecognized reloc ") + Twine(Type) << '\n'; - break; - } -} - -template <class ELFT> -template <bool isRela> -void OutputSection<ELFT>::relocate( - uint8_t *Buf, iterator_range<const Elf_Rel_Impl<ELFT, isRela> *> Rels, - const ObjectFile<ELFT> &File, uintX_t BaseAddr) { - typedef Elf_Rel_Impl<ELFT, isRela> RelType; - bool IsMips64EL = File.getObj()->isMips64EL(); - for (const RelType &RI : Rels) { - uint32_t SymIndex = RI.getSymbol(IsMips64EL); - uint32_t Type = RI.getType(IsMips64EL); - uintX_t SymVA; - - // Handle relocations for local symbols -- they never get - // resolved so we don't allocate a SymbolBody. - const Elf_Shdr *SymTab = File.getSymbolTable(); - if (SymIndex < SymTab->sh_info) { - const Elf_Sym *Sym = File.getObj()->getRelocationSymbol(&RI, SymTab); - if (!Sym) - continue; - SymVA = getLocalSymVA(Sym, File); - } else { - const SymbolBody *Body = File.getSymbolBody(SymIndex); - if (!Body) - continue; - switch (Body->kind()) { - case SymbolBody::DefinedRegularKind: - SymVA = getSymVA<ELFT>(cast<DefinedRegular<ELFT>>(Body)); - break; - case SymbolBody::DefinedAbsoluteKind: - SymVA = cast<DefinedAbsolute<ELFT>>(Body)->Sym.st_value; - break; - case SymbolBody::DefinedCommonKind: { - auto *DC = cast<DefinedCommon<ELFT>>(Body); - SymVA = DC->OutputSec->getVA() + DC->OffsetInBSS; - break; - } - case SymbolBody::SharedKind: - if (relocNeedsPLT(Type)) { - SymVA = PltSec.getEntryAddr(*Body); - Type = R_X86_64_PC32; - } else if (relocNeedsGOT(Type)) { - SymVA = GotSec.getEntryAddr(*Body); - Type = R_X86_64_PC32; - } else { - continue; - } - break; - case SymbolBody::UndefinedKind: - assert(Body->isWeak() && "Undefined symbol reached writer"); - SymVA = 0; - break; - case SymbolBody::LazyKind: - llvm_unreachable("Lazy symbol reached writer"); - } - } - - relocateOne(Buf, RI, Type, BaseAddr, SymVA); - } -} - -template <class ELFT> void OutputSection<ELFT>::writeTo(uint8_t *Buf) { - for (InputSection<ELFT> *C : Chunks) { - C->writeTo(Buf); - const ObjectFile<ELFT> *File = C->getFile(); - ELFFile<ELFT> *EObj = File->getObj(); - uint8_t *Base = Buf + C->getOutputSectionOff(); - uintX_t BaseAddr = this->getVA() + C->getOutputSectionOff(); - // Iterate over all relocation sections that apply to this section. - for (const Elf_Shdr *RelSec : C->RelocSections) { - if (RelSec->sh_type == SHT_RELA) - relocate(Base, EObj->relas(RelSec), *File, BaseAddr); - else - relocate(Base, EObj->rels(RelSec), *File, BaseAddr); - } - } -} - -template <bool Is64Bits> -void StringTableSection<Is64Bits>::writeTo(uint8_t *Buf) { - StringRef Data = StrTabBuilder.data(); - memcpy(Buf, Data.data(), Data.size()); -} - -template <class ELFT> static int compareSym(const typename ELFFile<ELFT>::Elf_Sym *A, const typename ELFFile<ELFT>::Elf_Sym *B) { uint32_t AN = A->st_name; @@ -888,126 +190,6 @@ static int compareSym(const typename ELFFile<ELFT>::Elf_Sym *A, return AN - BN; } -static bool includeInSymtab(const SymbolBody &B) { - if (B.isLazy()) - return false; - if (!B.isUsedInRegularObj()) - return false; - uint8_t V = B.getMostConstrainingVisibility(); - if (V != STV_DEFAULT && V != STV_PROTECTED) - return false; - return true; -} - -template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { - const OutputSection<ELFT> *Out = nullptr; - const InputSection<ELFT> *Section = nullptr; - Buf += sizeof(Elf_Sym); - - // All symbols with STB_LOCAL binding precede the weak and global symbols. - // .dynsym only contains global symbols. - if (!Config->DiscardAll && !StrTabSec.isDynamic()) { - for (const std::unique_ptr<ObjectFileBase> &FileB : - Table.getObjectFiles()) { - auto &File = cast<ObjectFile<ELFT>>(*FileB); - Elf_Sym_Range Syms = File.getLocalSymbols(); - for (const Elf_Sym &Sym : Syms) { - auto *ESym = reinterpret_cast<Elf_Sym *>(Buf); - uint32_t SecIndex = Sym.st_shndx; - ErrorOr<StringRef> SymName = Sym.getName(File.getStringTable()); - if (Config->DiscardLocals && SymName->startswith(".L")) - continue; - ESym->st_name = (SymName) ? StrTabSec.getFileOff(*SymName) : 0; - ESym->st_size = Sym.st_size; - ESym->setBindingAndType(Sym.getBinding(), Sym.getType()); - if (SecIndex == SHN_XINDEX) - SecIndex = File.getObj()->getExtendedSymbolTableIndex( - &Sym, File.getSymbolTable(), File.getSymbolTableShndx()); - ArrayRef<InputSection<ELFT> *> Chunks = File.getChunks(); - Section = Chunks[SecIndex]; - assert(Section != nullptr); - Out = Section->getOutputSection(); - assert(Out != nullptr); - ESym->st_shndx = Out->getSectionIndex(); - ESym->st_value = - Out->getVA() + Section->getOutputSectionOff() + Sym.st_value; - Buf += sizeof(Elf_Sym); - } - } - } - - for (auto &P : Table.getSymbols()) { - StringRef Name = P.first; - Symbol *Sym = P.second; - SymbolBody *Body = Sym->Body; - if (!includeInSymtab(*Body)) - continue; - const Elf_Sym &InputSym = cast<ELFSymbolBody<ELFT>>(Body)->Sym; - - auto *ESym = reinterpret_cast<Elf_Sym *>(Buf); - ESym->st_name = StrTabSec.getFileOff(Name); - - Out = nullptr; - Section = nullptr; - - switch (Body->kind()) { - case SymbolBody::DefinedRegularKind: - Section = &cast<DefinedRegular<ELFT>>(Body)->Section; - break; - case SymbolBody::DefinedCommonKind: - Out = &W.getBSS(); - break; - case SymbolBody::UndefinedKind: - case SymbolBody::DefinedAbsoluteKind: - case SymbolBody::SharedKind: - break; - case SymbolBody::LazyKind: - llvm_unreachable("Lazy symbol got to output symbol table!"); - } - - ESym->setBindingAndType(InputSym.getBinding(), InputSym.getType()); - ESym->st_size = InputSym.st_size; - ESym->setVisibility(Body->getMostConstrainingVisibility()); - if (InputSym.isAbsolute()) { - ESym->st_shndx = SHN_ABS; - ESym->st_value = InputSym.st_value; - } - - if (Section) - Out = Section->getOutputSection(); - - if (Out) { - ESym->st_shndx = Out->getSectionIndex(); - uintX_t VA = Out->getVA(); - if (Section) - VA += Section->getOutputSectionOff(); - if (auto *C = dyn_cast<DefinedCommon<ELFT>>(Body)) - VA += C->OffsetInBSS; - else - VA += InputSym.st_value; - ESym->st_value = VA; - } - - Buf += sizeof(Elf_Sym); - } -} - -template <bool Is64Bits> -template <endianness E> -void OutputSectionBase<Is64Bits>::writeHeaderTo( - typename ELFFile<ELFType<E, Is64Bits>>::Elf_Shdr *SHdr) { - SHdr->sh_name = Header.sh_name; - SHdr->sh_type = Header.sh_type; - SHdr->sh_flags = Header.sh_flags; - SHdr->sh_addr = Header.sh_addr; - SHdr->sh_offset = Header.sh_offset; - SHdr->sh_size = Header.sh_size; - SHdr->sh_link = Header.sh_link; - SHdr->sh_info = Header.sh_info; - SHdr->sh_addralign = Header.sh_addralign; - SHdr->sh_entsize = Header.sh_entsize; -} - namespace { template <bool Is64Bits> struct SectionKey { typedef typename std::conditional<Is64Bits, uint64_t, uint32_t>::type uintX_t; @@ -1163,6 +345,9 @@ template <class ELFT> void Writer<ELFT>::createSections() { } BSSSec = getSection(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); + SymTabSec.setBssSec(BSSSec); + DynSymSec.setBssSec(BSSSec); + // Sort the common symbols by alignment as an heuristic to pack them better. std::stable_sort(CommonSymbols.begin(), CommonSymbols.end(), cmpAlign<ELFT>); uintX_t Off = BSSSec->getSize(); |