From d93e1dfac8711cfed1a9d9cd1876a788b83945cd Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Mon, 2 Jan 2017 19:19:15 +0000 Subject: Vendor import of lld trunk r290819: https://llvm.org/svn/llvm-project/lld/trunk@290819 --- ELF/OutputSections.cpp | 1768 +++++++----------------------------------------- 1 file changed, 227 insertions(+), 1541 deletions(-) (limited to 'ELF/OutputSections.cpp') diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp index 50b94015f229..bf7f9c29a29a 100644 --- a/ELF/OutputSections.cpp +++ b/ELF/OutputSections.cpp @@ -11,15 +11,16 @@ #include "Config.h" #include "EhFrame.h" #include "LinkerScript.h" +#include "Memory.h" #include "Strings.h" #include "SymbolTable.h" +#include "SyntheticSections.h" #include "Target.h" -#include "lld/Core/Parallel.h" +#include "Threads.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/SHA1.h" -#include using namespace llvm; using namespace llvm::dwarf; @@ -30,855 +31,152 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -template -OutputSectionBase::OutputSectionBase(StringRef Name, uint32_t Type, - uintX_t Flags) +OutputSectionBase::OutputSectionBase(StringRef Name, uint32_t Type, + uint64_t Flags) : Name(Name) { - memset(&Header, 0, sizeof(Elf_Shdr)); - Header.sh_type = Type; - Header.sh_flags = Flags; - Header.sh_addralign = 1; + this->Type = Type; + this->Flags = Flags; + this->Addralign = 1; } -template -void OutputSectionBase::writeHeaderTo(Elf_Shdr *Shdr) { - *Shdr = Header; +uint32_t OutputSectionBase::getPhdrFlags() const { + uint32_t Ret = PF_R; + if (Flags & SHF_WRITE) + Ret |= PF_W; + if (Flags & SHF_EXECINSTR) + Ret |= PF_X; + return Ret; } template -GotPltSection::GotPltSection() - : OutputSectionBase(".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) { - this->Header.sh_addralign = Target->GotPltEntrySize; -} - -template void GotPltSection::addEntry(SymbolBody &Sym) { - Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size(); - Entries.push_back(&Sym); -} - -template bool GotPltSection::empty() const { - return Entries.empty(); -} - -template void GotPltSection::finalize() { - this->Header.sh_size = (Target->GotPltHeaderEntriesNum + Entries.size()) * - Target->GotPltEntrySize; -} - -template void GotPltSection::writeTo(uint8_t *Buf) { - Target->writeGotPltHeader(Buf); - Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize; - for (const SymbolBody *B : Entries) { - Target->writeGotPlt(Buf, *B); - Buf += sizeof(uintX_t); +void OutputSectionBase::writeHeaderTo(typename ELFT::Shdr *Shdr) { + Shdr->sh_entsize = Entsize; + Shdr->sh_addralign = Addralign; + Shdr->sh_type = Type; + Shdr->sh_offset = Offset; + Shdr->sh_flags = Flags; + Shdr->sh_info = Info; + Shdr->sh_link = Link; + Shdr->sh_addr = Addr; + Shdr->sh_size = Size; + Shdr->sh_name = ShName; +} + +template static uint64_t getEntsize(uint32_t Type) { + switch (Type) { + case SHT_RELA: + return sizeof(typename ELFT::Rela); + case SHT_REL: + return sizeof(typename ELFT::Rel); + case SHT_MIPS_REGINFO: + return sizeof(Elf_Mips_RegInfo); + case SHT_MIPS_OPTIONS: + return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo); + case SHT_MIPS_ABIFLAGS: + return sizeof(Elf_Mips_ABIFlags); + default: + return 0; } } template -GotSection::GotSection() - : OutputSectionBase(".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) { - if (Config->EMachine == EM_MIPS) - this->Header.sh_flags |= SHF_MIPS_GPREL; - this->Header.sh_addralign = Target->GotEntrySize; -} - -template -void GotSection::addEntry(SymbolBody &Sym) { - Sym.GotIndex = Entries.size(); - Entries.push_back(&Sym); -} - -template -void GotSection::addMipsEntry(SymbolBody &Sym, uintX_t Addend, - RelExpr Expr) { - // For "true" local symbols which can be referenced from the same module - // only compiler creates two instructions for address loading: - // - // lw $8, 0($gp) # R_MIPS_GOT16 - // addi $8, $8, 0 # R_MIPS_LO16 - // - // The first instruction loads high 16 bits of the symbol address while - // the second adds an offset. That allows to reduce number of required - // GOT entries because only one global offset table entry is necessary - // for every 64 KBytes of local data. So for local symbols we need to - // allocate number of GOT entries to hold all required "page" addresses. - // - // All global symbols (hidden and regular) considered by compiler uniformly. - // It always generates a single `lw` instruction and R_MIPS_GOT16 relocation - // to load address of the symbol. So for each such symbol we need to - // allocate dedicated GOT entry to store its address. - // - // If a symbol is preemptible we need help of dynamic linker to get its - // final address. The corresponding GOT entries are allocated in the - // "global" part of GOT. Entries for non preemptible global symbol allocated - // in the "local" part of GOT. - // - // See "Global Offset Table" in Chapter 5: - // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - if (Expr == R_MIPS_GOT_LOCAL_PAGE) { - // At this point we do not know final symbol value so to reduce number - // of allocated GOT entries do the following trick. Save all output - // sections referenced by GOT relocations. Then later in the `finalize` - // method calculate number of "pages" required to cover all saved output - // section and allocate appropriate number of GOT entries. - auto *OutSec = cast>(&Sym)->Section->OutSec; - MipsOutSections.insert(OutSec); - return; - } - if (Sym.isTls()) { - // GOT entries created for MIPS TLS relocations behave like - // almost GOT entries from other ABIs. They go to the end - // of the global offset table. - Sym.GotIndex = Entries.size(); - Entries.push_back(&Sym); - return; - } - auto AddEntry = [&](SymbolBody &S, uintX_t A, MipsGotEntries &Items) { - if (S.isInGot() && !A) - return; - size_t NewIndex = Items.size(); - if (!MipsGotMap.insert({{&S, A}, NewIndex}).second) - return; - Items.emplace_back(&S, A); - if (!A) - S.GotIndex = NewIndex; - }; - if (Sym.isPreemptible()) { - // Ignore addends for preemptible symbols. They got single GOT entry anyway. - AddEntry(Sym, 0, MipsGlobal); - Sym.IsInGlobalMipsGot = true; - } else - AddEntry(Sym, Addend, MipsLocal); -} - -template bool GotSection::addDynTlsEntry(SymbolBody &Sym) { - if (Sym.GlobalDynIndex != -1U) - return false; - Sym.GlobalDynIndex = Entries.size(); - // Global Dynamic TLS entries take two GOT slots. - Entries.push_back(nullptr); - Entries.push_back(&Sym); - return true; +OutputSection::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags) + : OutputSectionBase(Name, Type, Flags) { + this->Entsize = getEntsize(Type); } -// Reserves TLS entries for a TLS module ID and a TLS block offset. -// In total it takes two GOT slots. -template bool GotSection::addTlsIndex() { - if (TlsIndexOff != uint32_t(-1)) +template +static bool compareByFilePosition(InputSection *A, + InputSection *B) { + // Synthetic doesn't have link order dependecy, stable_sort will keep it last + if (A->kind() == InputSectionData::Synthetic || + B->kind() == InputSectionData::Synthetic) return false; - TlsIndexOff = Entries.size() * sizeof(uintX_t); - Entries.push_back(nullptr); - Entries.push_back(nullptr); - return true; -} - -template -typename GotSection::uintX_t -GotSection::getMipsLocalPageOffset(uintX_t EntryValue) { - // Initialize the entry by the %hi(EntryValue) expression - // but without right-shifting. - EntryValue = (EntryValue + 0x8000) & ~0xffff; - // Take into account MIPS GOT header. - // See comment in the GotSection::writeTo. - size_t NewIndex = MipsLocalGotPos.size() + 2; - auto P = MipsLocalGotPos.insert(std::make_pair(EntryValue, NewIndex)); - assert(!P.second || MipsLocalGotPos.size() <= MipsPageEntries); - return (uintX_t)P.first->second * sizeof(uintX_t) - MipsGPOffset; -} - -template -typename GotSection::uintX_t -GotSection::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const { - uintX_t Off = MipsPageEntries; - if (B.isTls()) - Off += MipsLocal.size() + MipsGlobal.size() + B.GotIndex; - else if (B.IsInGlobalMipsGot) - Off += MipsLocal.size() + B.GotIndex; - else if (B.isInGot()) - Off += B.GotIndex; - else { - auto It = MipsGotMap.find({&B, Addend}); - assert(It != MipsGotMap.end()); - Off += It->second; - } - return Off * sizeof(uintX_t) - MipsGPOffset; -} - -template -typename GotSection::uintX_t GotSection::getMipsTlsOffset() { - return (MipsPageEntries + MipsLocal.size() + MipsGlobal.size()) * - sizeof(uintX_t); -} - -template -typename GotSection::uintX_t -GotSection::getGlobalDynAddr(const SymbolBody &B) const { - return this->getVA() + B.GlobalDynIndex * sizeof(uintX_t); -} - -template -typename GotSection::uintX_t -GotSection::getGlobalDynOffset(const SymbolBody &B) const { - return B.GlobalDynIndex * sizeof(uintX_t); -} - -template -const SymbolBody *GotSection::getMipsFirstGlobalEntry() const { - return MipsGlobal.empty() ? nullptr : MipsGlobal.front().first; -} - -template -unsigned GotSection::getMipsLocalEntriesNum() const { - return MipsPageEntries + MipsLocal.size(); + auto *LA = cast>(A->getLinkOrderDep()); + auto *LB = cast>(B->getLinkOrderDep()); + OutputSectionBase *AOut = LA->OutSec; + OutputSectionBase *BOut = LB->OutSec; + if (AOut != BOut) + return AOut->SectionIndex < BOut->SectionIndex; + return LA->OutSecOff < LB->OutSecOff; } -template void GotSection::finalize() { - size_t EntriesNum = Entries.size(); - if (Config->EMachine == EM_MIPS) { - // Take into account MIPS GOT header. - // See comment in the GotSection::writeTo. - MipsPageEntries += 2; - for (const OutputSectionBase *OutSec : MipsOutSections) { - // Calculate an upper bound of MIPS GOT entries required to store page - // addresses of local symbols. We assume the worst case - each 64kb - // page of the output section has at least one GOT relocation against it. - // Add 0x8000 to the section's size because the page address stored - // in the GOT entry is calculated as (value + 0x8000) & ~0xffff. - MipsPageEntries += (OutSec->getSize() + 0x8000 + 0xfffe) / 0xffff; - } - EntriesNum += MipsPageEntries + MipsLocal.size() + MipsGlobal.size(); - } - this->Header.sh_size = EntriesNum * sizeof(uintX_t); -} - -template void GotSection::writeMipsGot(uint8_t *&Buf) { - // Set the MSB of the second GOT slot. This is not required by any - // MIPS ABI documentation, though. - // - // There is a comment in glibc saying that "The MSB of got[1] of a - // gnu object is set to identify gnu objects," and in GNU gold it - // says "the second entry will be used by some runtime loaders". - // But how this field is being used is unclear. - // - // We are not really willing to mimic other linkers behaviors - // without understanding why they do that, but because all files - // generated by GNU tools have this special GOT value, and because - // we've been doing this for years, it is probably a safe bet to - // keep doing this for now. We really need to revisit this to see - // if we had to do this. - auto *P = reinterpret_cast(Buf); - P[1] = uintX_t(1) << (ELFT::Is64Bits ? 63 : 31); - // Write 'page address' entries to the local part of the GOT. - for (std::pair &L : MipsLocalGotPos) { - uint8_t *Entry = Buf + L.second * sizeof(uintX_t); - write(Entry, L.first); - } - Buf += MipsPageEntries * sizeof(uintX_t); - auto AddEntry = [&](const MipsGotEntry &SA) { - uint8_t *Entry = Buf; - Buf += sizeof(uintX_t); - const SymbolBody* Body = SA.first; - uintX_t VA = Body->template getVA(SA.second); - write(Entry, VA); - }; - std::for_each(std::begin(MipsLocal), std::end(MipsLocal), AddEntry); - std::for_each(std::begin(MipsGlobal), std::end(MipsGlobal), AddEntry); -} - -template void GotSection::writeTo(uint8_t *Buf) { - if (Config->EMachine == EM_MIPS) - writeMipsGot(Buf); - for (const SymbolBody *B : Entries) { - uint8_t *Entry = Buf; - Buf += sizeof(uintX_t); - if (!B) - continue; - if (B->isPreemptible()) - continue; // The dynamic linker will take care of it. - uintX_t VA = B->getVA(); - write(Entry, VA); - } -} - -template -PltSection::PltSection() - : OutputSectionBase(".plt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR) { - this->Header.sh_addralign = 16; -} - -template void PltSection::writeTo(uint8_t *Buf) { - // At beginning of PLT, we have code to call the dynamic linker - // to resolve dynsyms at runtime. Write such code. - Target->writePltHeader(Buf); - size_t Off = Target->PltHeaderSize; - - for (auto &I : Entries) { - const SymbolBody *B = I.first; - unsigned RelOff = I.second; - uint64_t Got = B->getGotPltVA(); - uint64_t Plt = this->getVA() + Off; - Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff); - Off += Target->PltEntrySize; - } -} - -template void PltSection::addEntry(SymbolBody &Sym) { - Sym.PltIndex = Entries.size(); - unsigned RelOff = Out::RelaPlt->getRelocOffset(); - Entries.push_back(std::make_pair(&Sym, RelOff)); -} - -template void PltSection::finalize() { - this->Header.sh_size = - Target->PltHeaderSize + Entries.size() * Target->PltEntrySize; -} - -template -RelocationSection::RelocationSection(StringRef Name, bool Sort) - : OutputSectionBase(Name, Config->Rela ? SHT_RELA : SHT_REL, - SHF_ALLOC), - Sort(Sort) { - this->Header.sh_entsize = Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); - this->Header.sh_addralign = sizeof(uintX_t); -} - -template -void RelocationSection::addReloc(const DynamicReloc &Reloc) { - Relocs.push_back(Reloc); -} - -template -static bool compRelocations(const RelTy &A, const RelTy &B) { - return A.getSymbol(Config->Mips64EL) < B.getSymbol(Config->Mips64EL); -} - -template void RelocationSection::writeTo(uint8_t *Buf) { - uint8_t *BufBegin = Buf; - for (const DynamicReloc &Rel : Relocs) { - auto *P = reinterpret_cast(Buf); - Buf += Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); - - if (Config->Rela) - P->r_addend = Rel.getAddend(); - P->r_offset = Rel.getOffset(); - if (Config->EMachine == EM_MIPS && Rel.getOutputSec() == Out::Got) - // Dynamic relocation against MIPS GOT section make deal TLS entries - // allocated in the end of the GOT. We need to adjust the offset to take - // in account 'local' and 'global' GOT entries. - P->r_offset += Out::Got->getMipsTlsOffset(); - P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->Mips64EL); - } - - if (Sort) { - if (Config->Rela) - std::stable_sort((Elf_Rela *)BufBegin, - (Elf_Rela *)BufBegin + Relocs.size(), - compRelocations); - else - std::stable_sort((Elf_Rel *)BufBegin, (Elf_Rel *)BufBegin + Relocs.size(), - compRelocations); - } -} - -template unsigned RelocationSection::getRelocOffset() { - return this->Header.sh_entsize * Relocs.size(); -} - -template void RelocationSection::finalize() { - this->Header.sh_link = Static ? Out::SymTab->SectionIndex - : Out::DynSymTab->SectionIndex; - this->Header.sh_size = Relocs.size() * this->Header.sh_entsize; -} - -template -InterpSection::InterpSection() - : OutputSectionBase(".interp", SHT_PROGBITS, SHF_ALLOC) { - this->Header.sh_size = Config->DynamicLinker.size() + 1; -} - -template void InterpSection::writeTo(uint8_t *Buf) { - StringRef S = Config->DynamicLinker; - memcpy(Buf, S.data(), S.size()); -} - -template -HashTableSection::HashTableSection() - : OutputSectionBase(".hash", SHT_HASH, SHF_ALLOC) { - this->Header.sh_entsize = sizeof(Elf_Word); - this->Header.sh_addralign = sizeof(Elf_Word); -} - -static uint32_t hashSysv(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; -} - -template void HashTableSection::finalize() { - this->Header.sh_link = Out::DynSymTab->SectionIndex; - - unsigned NumEntries = 2; // nbucket and nchain. - NumEntries += Out::DynSymTab->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 += Out::DynSymTab->getNumSymbols(); - this->Header.sh_size = NumEntries * sizeof(Elf_Word); -} - -template void HashTableSection::writeTo(uint8_t *Buf) { - unsigned NumSymbols = Out::DynSymTab->getNumSymbols(); - auto *P = reinterpret_cast(Buf); - *P++ = NumSymbols; // nbucket - *P++ = NumSymbols; // nchain - - Elf_Word *Buckets = P; - Elf_Word *Chains = P + NumSymbols; - - for (const std::pair &P : - Out::DynSymTab->getSymbols()) { - SymbolBody *Body = P.first; - StringRef Name = Body->getName(); - unsigned I = Body->DynsymIndex; - uint32_t Hash = hashSysv(Name) % NumSymbols; - Chains[I] = Buckets[Hash]; - Buckets[Hash] = I; - } -} - -static uint32_t hashGnu(StringRef Name) { - uint32_t H = 5381; - for (uint8_t C : Name) - H = (H << 5) + H + C; - return H; -} - -template -GnuHashTableSection::GnuHashTableSection() - : OutputSectionBase(".gnu.hash", SHT_GNU_HASH, SHF_ALLOC) { - this->Header.sh_entsize = ELFT::Is64Bits ? 0 : 4; - this->Header.sh_addralign = sizeof(uintX_t); -} - -template -unsigned GnuHashTableSection::calcNBuckets(unsigned NumHashed) { - if (!NumHashed) - return 0; - - // These values are prime numbers which are not greater than 2^(N-1) + 1. - // In result, for any particular NumHashed we return a prime number - // which is not greater than NumHashed. - static const unsigned Primes[] = { - 1, 1, 3, 3, 7, 13, 31, 61, 127, 251, - 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071}; - - return Primes[std::min(Log2_32_Ceil(NumHashed), - array_lengthof(Primes) - 1)]; -} - -// Bloom filter estimation: at least 8 bits for each hashed symbol. -// GNU Hash table requirement: it should be a power of 2, -// the minimum value is 1, even for an empty table. -// Expected results for a 32-bit target: -// calcMaskWords(0..4) = 1 -// calcMaskWords(5..8) = 2 -// calcMaskWords(9..16) = 4 -// For a 64-bit target: -// calcMaskWords(0..8) = 1 -// calcMaskWords(9..16) = 2 -// calcMaskWords(17..32) = 4 -template -unsigned GnuHashTableSection::calcMaskWords(unsigned NumHashed) { - if (!NumHashed) - return 1; - return NextPowerOf2((NumHashed - 1) / sizeof(Elf_Off)); -} - -template void GnuHashTableSection::finalize() { - unsigned NumHashed = Symbols.size(); - NBuckets = calcNBuckets(NumHashed); - MaskWords = calcMaskWords(NumHashed); - // Second hash shift estimation: just predefined values. - Shift2 = ELFT::Is64Bits ? 6 : 5; - - this->Header.sh_link = Out::DynSymTab->SectionIndex; - this->Header.sh_size = sizeof(Elf_Word) * 4 // Header - + sizeof(Elf_Off) * MaskWords // Bloom Filter - + sizeof(Elf_Word) * NBuckets // Hash Buckets - + sizeof(Elf_Word) * NumHashed; // Hash Values -} - -template void GnuHashTableSection::writeTo(uint8_t *Buf) { - writeHeader(Buf); - if (Symbols.empty()) - return; - writeBloomFilter(Buf); - writeHashTable(Buf); -} - -template -void GnuHashTableSection::writeHeader(uint8_t *&Buf) { - auto *P = reinterpret_cast(Buf); - *P++ = NBuckets; - *P++ = Out::DynSymTab->getNumSymbols() - Symbols.size(); - *P++ = MaskWords; - *P++ = Shift2; - Buf = reinterpret_cast(P); -} - -template -void GnuHashTableSection::writeBloomFilter(uint8_t *&Buf) { - unsigned C = sizeof(Elf_Off) * 8; - - auto *Masks = reinterpret_cast(Buf); - for (const SymbolData &Sym : Symbols) { - size_t Pos = (Sym.Hash / C) & (MaskWords - 1); - uintX_t V = (uintX_t(1) << (Sym.Hash % C)) | - (uintX_t(1) << ((Sym.Hash >> Shift2) % C)); - Masks[Pos] |= V; - } - Buf += sizeof(Elf_Off) * MaskWords; -} +template void OutputSection::finalize() { + if ((this->Flags & SHF_LINK_ORDER) && !this->Sections.empty()) { + std::sort(Sections.begin(), Sections.end(), compareByFilePosition); + Size = 0; + assignOffsets(); -template -void GnuHashTableSection::writeHashTable(uint8_t *Buf) { - Elf_Word *Buckets = reinterpret_cast(Buf); - Elf_Word *Values = Buckets + NBuckets; - - int PrevBucket = -1; - int I = 0; - for (const SymbolData &Sym : Symbols) { - int Bucket = Sym.Hash % NBuckets; - assert(PrevBucket <= Bucket); - if (Bucket != PrevBucket) { - Buckets[Bucket] = Sym.Body->DynsymIndex; - PrevBucket = Bucket; - if (I > 0) - Values[I - 1] |= 1; - } - Values[I] = Sym.Hash & ~1; - ++I; + // We must preserve the link order dependency of sections with the + // SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We + // need to translate the InputSection sh_link to the OutputSection sh_link, + // all InputSections in the OutputSection have the same dependency. + if (auto *D = this->Sections.front()->getLinkOrderDep()) + this->Link = D->OutSec->SectionIndex; } - if (I > 0) - Values[I - 1] |= 1; -} -// Add symbols to this symbol hash table. Note that this function -// destructively sort a given vector -- which is needed because -// GNU-style hash table places some sorting requirements. -template -void GnuHashTableSection::addSymbols( - std::vector> &V) { - // Ideally this will just be 'auto' but GCC 6.1 is not able - // to deduce it correctly. - std::vector>::iterator Mid = - std::stable_partition(V.begin(), V.end(), - [](std::pair &P) { - return P.first->isUndefined(); - }); - if (Mid == V.end()) + uint32_t Type = this->Type; + if (!Config->Relocatable || (Type != SHT_RELA && Type != SHT_REL)) return; - for (auto I = Mid, E = V.end(); I != E; ++I) { - SymbolBody *B = I->first; - size_t StrOff = I->second; - Symbols.push_back({B, StrOff, hashGnu(B->getName())}); - } - - unsigned NBuckets = calcNBuckets(Symbols.size()); - std::stable_sort(Symbols.begin(), Symbols.end(), - [&](const SymbolData &L, const SymbolData &R) { - return L.Hash % NBuckets < R.Hash % NBuckets; - }); - - V.erase(Mid, V.end()); - for (const SymbolData &Sym : Symbols) - V.push_back({Sym.Body, Sym.STName}); -} - -// Returns the number of version definition entries. Because the first entry -// is for the version definition itself, it is the number of versioned symbols -// plus one. Note that we don't support multiple versions yet. -static unsigned getVerDefNum() { return Config->VersionDefinitions.size() + 1; } - -template -DynamicSection::DynamicSection() - : OutputSectionBase(".dynamic", SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE) { - Elf_Shdr &Header = this->Header; - Header.sh_addralign = sizeof(uintX_t); - Header.sh_entsize = ELFT::Is64Bits ? 16 : 8; - - // .dynamic section is not writable on MIPS. - // See "Special Section" in Chapter 4 in the following document: - // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - if (Config->EMachine == EM_MIPS) - Header.sh_flags = SHF_ALLOC; -} - -template void DynamicSection::finalize() { - if (this->Header.sh_size) - return; // Already finalized. - - Elf_Shdr &Header = this->Header; - Header.sh_link = Out::DynStrTab->SectionIndex; - - auto Add = [=](Entry E) { Entries.push_back(E); }; - - // Add strings. We know that these are the last strings to be added to - // DynStrTab and doing this here allows this function to set DT_STRSZ. - if (!Config->RPath.empty()) - Add({Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH, - Out::DynStrTab->addString(Config->RPath)}); - for (const std::unique_ptr> &F : - Symtab::X->getSharedFiles()) - if (F->isNeeded()) - Add({DT_NEEDED, Out::DynStrTab->addString(F->getSoName())}); - if (!Config->SoName.empty()) - Add({DT_SONAME, Out::DynStrTab->addString(Config->SoName)}); - - Out::DynStrTab->finalize(); - - if (Out::RelaDyn->hasRelocs()) { - bool IsRela = Config->Rela; - Add({IsRela ? DT_RELA : DT_REL, Out::RelaDyn}); - Add({IsRela ? DT_RELASZ : DT_RELSZ, Out::RelaDyn->getSize()}); - Add({IsRela ? DT_RELAENT : DT_RELENT, - uintX_t(IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel))}); - } - if (Out::RelaPlt && Out::RelaPlt->hasRelocs()) { - Add({DT_JMPREL, Out::RelaPlt}); - Add({DT_PLTRELSZ, Out::RelaPlt->getSize()}); - Add({Config->EMachine == EM_MIPS ? DT_MIPS_PLTGOT : DT_PLTGOT, - Out::GotPlt}); - Add({DT_PLTREL, uint64_t(Config->Rela ? DT_RELA : DT_REL)}); - } - Add({DT_SYMTAB, Out::DynSymTab}); - Add({DT_SYMENT, sizeof(Elf_Sym)}); - Add({DT_STRTAB, Out::DynStrTab}); - Add({DT_STRSZ, Out::DynStrTab->getSize()}); - if (Out::GnuHashTab) - Add({DT_GNU_HASH, Out::GnuHashTab}); - if (Out::HashTab) - Add({DT_HASH, Out::HashTab}); - - if (PreInitArraySec) { - Add({DT_PREINIT_ARRAY, PreInitArraySec}); - Add({DT_PREINIT_ARRAYSZ, PreInitArraySec->getSize()}); - } - if (InitArraySec) { - Add({DT_INIT_ARRAY, InitArraySec}); - Add({DT_INIT_ARRAYSZ, (uintX_t)InitArraySec->getSize()}); - } - if (FiniArraySec) { - Add({DT_FINI_ARRAY, FiniArraySec}); - Add({DT_FINI_ARRAYSZ, (uintX_t)FiniArraySec->getSize()}); - } - - if (SymbolBody *B = Symtab::X->find(Config->Init)) - Add({DT_INIT, B}); - if (SymbolBody *B = Symtab::X->find(Config->Fini)) - Add({DT_FINI, B}); - - uint32_t DtFlags = 0; - uint32_t DtFlags1 = 0; - if (Config->Bsymbolic) - DtFlags |= DF_SYMBOLIC; - if (Config->ZNodelete) - DtFlags1 |= DF_1_NODELETE; - if (Config->ZNow) { - DtFlags |= DF_BIND_NOW; - DtFlags1 |= DF_1_NOW; - } - if (Config->ZOrigin) { - DtFlags |= DF_ORIGIN; - DtFlags1 |= DF_1_ORIGIN; - } - - if (DtFlags) - Add({DT_FLAGS, DtFlags}); - if (DtFlags1) - Add({DT_FLAGS_1, DtFlags1}); - - if (!Config->Entry.empty()) - Add({DT_DEBUG, (uint64_t)0}); - - bool HasVerNeed = Out::VerNeed->getNeedNum() != 0; - if (HasVerNeed || Out::VerDef) - Add({DT_VERSYM, Out::VerSym}); - if (Out::VerDef) { - Add({DT_VERDEF, Out::VerDef}); - Add({DT_VERDEFNUM, getVerDefNum()}); - } - if (HasVerNeed) { - Add({DT_VERNEED, Out::VerNeed}); - Add({DT_VERNEEDNUM, Out::VerNeed->getNeedNum()}); - } - - if (Config->EMachine == EM_MIPS) { - Add({DT_MIPS_RLD_VERSION, 1}); - Add({DT_MIPS_FLAGS, RHF_NOTPOT}); - Add({DT_MIPS_BASE_ADDRESS, Config->ImageBase}); - Add({DT_MIPS_SYMTABNO, Out::DynSymTab->getNumSymbols()}); - Add({DT_MIPS_LOCAL_GOTNO, Out::Got->getMipsLocalEntriesNum()}); - if (const SymbolBody *B = Out::Got->getMipsFirstGlobalEntry()) - Add({DT_MIPS_GOTSYM, B->DynsymIndex}); - else - Add({DT_MIPS_GOTSYM, Out::DynSymTab->getNumSymbols()}); - Add({DT_PLTGOT, Out::Got}); - if (Out::MipsRldMap) - Add({DT_MIPS_RLD_MAP, Out::MipsRldMap}); - } - - // +1 for DT_NULL - Header.sh_size = (Entries.size() + 1) * Header.sh_entsize; -} - -template void DynamicSection::writeTo(uint8_t *Buf) { - auto *P = reinterpret_cast(Buf); - - for (const Entry &E : Entries) { - P->d_tag = E.Tag; - switch (E.Kind) { - case Entry::SecAddr: - P->d_un.d_ptr = E.OutSec->getVA(); - break; - case Entry::SymAddr: - P->d_un.d_ptr = E.Sym->template getVA(); - break; - case Entry::PlainInt: - P->d_un.d_val = E.Val; - break; - } - ++P; - } -} - -template -EhFrameHeader::EhFrameHeader() - : OutputSectionBase(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC) {} - -// .eh_frame_hdr contains a binary search table of pointers to FDEs. -// Each entry of the search table consists of two values, -// the starting PC from where FDEs covers, and the FDE's address. -// It is sorted by PC. -template void EhFrameHeader::writeTo(uint8_t *Buf) { - const endianness E = ELFT::TargetEndianness; - - // Sort the FDE list by their PC and uniqueify. Usually there is only - // one FDE for a PC (i.e. function), but if ICF merges two functions - // into one, there can be more than one FDEs pointing to the address. - auto Less = [](const FdeData &A, const FdeData &B) { return A.Pc < B.Pc; }; - std::stable_sort(Fdes.begin(), Fdes.end(), Less); - auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; }; - Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end()); - - Buf[0] = 1; - Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; - Buf[2] = DW_EH_PE_udata4; - Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; - write32(Buf + 4, Out::EhFrame->getVA() - this->getVA() - 4); - write32(Buf + 8, Fdes.size()); - Buf += 12; - - uintX_t VA = this->getVA(); - for (FdeData &Fde : Fdes) { - write32(Buf, Fde.Pc - VA); - write32(Buf + 4, Fde.FdeVA - VA); - Buf += 8; - } -} - -template void EhFrameHeader::finalize() { - // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs. - this->Header.sh_size = 12 + Out::EhFrame->NumFdes * 8; -} - -template -void EhFrameHeader::addFde(uint32_t Pc, uint32_t FdeVA) { - Fdes.push_back({Pc, FdeVA}); -} - -template -OutputSection::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags) - : OutputSectionBase(Name, Type, Flags) { - if (Type == SHT_RELA) - this->Header.sh_entsize = sizeof(Elf_Rela); - else if (Type == SHT_REL) - this->Header.sh_entsize = sizeof(Elf_Rel); -} - -template void OutputSection::finalize() { - uint32_t Type = this->Header.sh_type; - if (Type != SHT_RELA && Type != SHT_REL) - return; - this->Header.sh_link = Out::SymTab->SectionIndex; + this->Link = In::SymTab->OutSec->SectionIndex; // sh_info for SHT_REL[A] sections should contain the section header index of // the section to which the relocation applies. InputSectionBase *S = Sections[0]->getRelocatedSection(); - this->Header.sh_info = S->OutSec->SectionIndex; + this->Info = S->OutSec->SectionIndex; } template -void OutputSection::addSection(InputSectionBase *C) { +void OutputSection::addSection(InputSectionData *C) { assert(C->Live); auto *S = cast>(C); Sections.push_back(S); S->OutSec = this; this->updateAlignment(S->Alignment); -} - -// If an input string is in the form of "foo.N" where N is a number, -// return N. Otherwise, returns 65536, which is one greater than the -// lowest priority. -static int getPriority(StringRef S) { - size_t Pos = S.rfind('.'); - if (Pos == StringRef::npos) - return 65536; - int V; - if (S.substr(Pos + 1).getAsInteger(10, V)) - return 65536; - return V; + // Keep sh_entsize value of the input section to be able to perform merging + // later during a final linking using the generated relocatable object. + if (Config->Relocatable && (S->Flags & SHF_MERGE)) + this->Entsize = S->Entsize; } // This function is called after we sort input sections // and scan relocations to setup sections' offsets. template void OutputSection::assignOffsets() { - uintX_t Off = this->Header.sh_size; + uintX_t Off = this->Size; for (InputSection *S : Sections) { Off = alignTo(Off, S->Alignment); S->OutSecOff = Off; Off += S->getSize(); } - this->Header.sh_size = Off; + this->Size = Off; } -// Sorts input sections by section name suffixes, so that .foo.N comes -// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections. -// We want to keep the original order if the priorities are the same -// because the compiler keeps the original initialization order in a -// translation unit and we need to respect that. -// For more detail, read the section of the GCC's manual about init_priority. -template void OutputSection::sortInitFini() { - // Sort sections by priority. - typedef std::pair *> Pair; +template +void OutputSection::sort( + std::function *S)> Order) { + typedef std::pair *> Pair; auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; }; std::vector V; for (InputSection *S : Sections) - V.push_back({getPriority(S->getSectionName()), S}); + V.push_back({Order(S), S}); std::stable_sort(V.begin(), V.end(), Comp); Sections.clear(); for (Pair &P : V) Sections.push_back(P.second); } +// Sorts input sections by section name suffixes, so that .foo.N comes +// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections. +// We want to keep the original order if the priorities are the same +// because the compiler keeps the original initialization order in a +// translation unit and we need to respect that. +// For more detail, read the section of the GCC's manual about init_priority. +template void OutputSection::sortInitFini() { + // Sort sections by priority. + sort([](InputSection *S) { return getPriority(S->Name); }); +} + // Returns true if S matches /Filename.?\.o$/. static bool isCrtBeginEnd(StringRef S, StringRef Filename) { if (!S.endswith(".o")) @@ -921,8 +219,8 @@ static bool compCtors(const InputSection *A, bool EndB = isCrtend(B->getFile()->getName()); if (EndA != EndB) return EndB; - StringRef X = A->getSectionName(); - StringRef Y = B->getSectionName(); + StringRef X = A->Name; + StringRef Y = B->Name; assert(X.startswith(".ctors") || X.startswith(".dtors")); assert(Y.startswith(".ctors") || Y.startswith(".dtors")); X = X.substr(6); @@ -939,65 +237,50 @@ template void OutputSection::sortCtorsDtors() { std::stable_sort(Sections.begin(), Sections.end(), compCtors); } -static void fill(uint8_t *Buf, size_t Size, ArrayRef A) { +// Fill [Buf, Buf + Size) with Filler. Filler is written in big +// endian order. This is used for linker script "=fillexp" command. +void fill(uint8_t *Buf, size_t Size, uint32_t Filler) { + uint8_t V[4]; + write32be(V, Filler); size_t I = 0; - for (; I + A.size() < Size; I += A.size()) - memcpy(Buf + I, A.data(), A.size()); - memcpy(Buf + I, A.data(), Size - I); + for (; I + 4 < Size; I += 4) + memcpy(Buf + I, V, 4); + memcpy(Buf + I, V, Size - I); } template void OutputSection::writeTo(uint8_t *Buf) { - ArrayRef Filler = Script::X->getFiller(this->Name); - if (!Filler.empty()) - fill(Buf, this->getSize(), Filler); - if (Config->Threads) { - parallel_for_each(Sections.begin(), Sections.end(), - [=](InputSection *C) { C->writeTo(Buf); }); - } else { - for (InputSection *C : Sections) - C->writeTo(Buf); - } + Loc = Buf; + if (uint32_t Filler = Script::X->getFiller(this->Name)) + fill(Buf, this->Size, Filler); + + auto Fn = [=](InputSection *IS) { IS->writeTo(Buf); }; + forEach(Sections.begin(), Sections.end(), Fn); + + // Linker scripts may have BYTE()-family commands with which you + // can write arbitrary bytes to the output. Process them if any. + Script::X->writeDataBytes(this->Name, Buf); } template EhOutputSection::EhOutputSection() - : OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {} - -// Returns the first relocation that points to a region -// between Begin and Begin+Size. -template -static const RelTy *getReloc(IntTy Begin, IntTy Size, ArrayRef &Rels) { - for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) { - if (I->r_offset < Begin) - continue; - - // Truncate Rels for fast access. That means we expect that the - // relocations are sorted and we are looking up symbols in - // sequential order. It is naturally satisfied for .eh_frame. - Rels = Rels.slice(I - Rels.begin()); - if (I->r_offset < Begin + Size) - return I; - return nullptr; - } - Rels = ArrayRef(); - return nullptr; -} + : OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {} // Search for an existing CIE record or create a new one. // CIE records from input object files are uniquified by their contents // and where their relocations point to. template template -CieRecord *EhOutputSection::addCie(SectionPiece &Piece, - EhInputSection *Sec, - ArrayRef &Rels) { +CieRecord *EhOutputSection::addCie(EhSectionPiece &Piece, + ArrayRef Rels) { + auto *Sec = cast>(Piece.ID); const endianness E = ELFT::TargetEndianness; if (read32(Piece.data().data() + 4) != 0) - fatal("CIE expected at beginning of .eh_frame: " + Sec->getSectionName()); + fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame"); SymbolBody *Personality = nullptr; - if (const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels)) - Personality = &Sec->getFile()->getRelocTargetSym(*Rel); + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI != (unsigned)-1) + Personality = &Sec->getFile()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. CieRecord *Cie = &CieMap[{Piece.data(), Personality}]; @@ -1014,13 +297,14 @@ CieRecord *EhOutputSection::addCie(SectionPiece &Piece, // points to a live function. template template -bool EhOutputSection::isFdeLive(SectionPiece &Piece, - EhInputSection *Sec, - ArrayRef &Rels) { - const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels); - if (!Rel) - fatal("FDE doesn't reference another section"); - SymbolBody &B = Sec->getFile()->getRelocTargetSym(*Rel); +bool EhOutputSection::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels) { + auto *Sec = cast>(Piece.ID); + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI == (unsigned)-1) + fatal(toString(Sec) + ": FDE doesn't reference another section"); + const RelTy &Rel = Rels[FirstRelI]; + SymbolBody &B = Sec->getFile()->getRelocTargetSym(Rel); auto *D = dyn_cast>(&B); if (!D || !D->Section) return false; @@ -1039,7 +323,7 @@ void EhOutputSection::addSectionAux(EhInputSection *Sec, const endianness E = ELFT::TargetEndianness; DenseMap OffsetToCie; - for (SectionPiece &Piece : Sec->Pieces) { + for (EhSectionPiece &Piece : Sec->Pieces) { // The empty record is the end marker. if (Piece.size() == 4) return; @@ -1047,16 +331,16 @@ void EhOutputSection::addSectionAux(EhInputSection *Sec, size_t Offset = Piece.InputOff; uint32_t ID = read32(Piece.data().data() + 4); if (ID == 0) { - OffsetToCie[Offset] = addCie(Piece, Sec, Rels); + OffsetToCie[Offset] = addCie(Piece, Rels); continue; } uint32_t CieOffset = Offset + 4 - ID; CieRecord *Cie = OffsetToCie[CieOffset]; if (!Cie) - fatal("invalid CIE reference"); + fatal(toString(Sec) + ": invalid CIE reference"); - if (!isFdeLive(Piece, Sec, Rels)) + if (!isFdeLive(Piece, Rels)) continue; Cie->FdePieces.push_back(&Piece); NumFdes++; @@ -1064,7 +348,7 @@ void EhOutputSection::addSectionAux(EhInputSection *Sec, } template -void EhOutputSection::addSection(InputSectionBase *C) { +void EhOutputSection::addSection(InputSectionData *C) { auto *Sec = cast>(C); Sec->OutSec = this; this->updateAlignment(Sec->Alignment); @@ -1077,12 +361,11 @@ void EhOutputSection::addSection(InputSectionBase *C) { if (Sec->Pieces.empty()) return; - if (const Elf_Shdr *RelSec = Sec->RelocSection) { - ELFFile &Obj = Sec->getFile()->getObj(); - if (RelSec->sh_type == SHT_RELA) - addSectionAux(Sec, Obj.relas(RelSec)); + if (Sec->NumRelocations) { + if (Sec->AreRelocsRela) + addSectionAux(Sec, Sec->relas()); else - addSectionAux(Sec, Obj.rels(RelSec)); + addSectionAux(Sec, Sec->rels()); return; } addSectionAux(Sec, makeArrayRef(nullptr, nullptr)); @@ -1098,7 +381,7 @@ static void writeCieFde(uint8_t *Buf, ArrayRef D) { } template void EhOutputSection::finalize() { - if (this->Header.sh_size) + if (this->Size) return; // Already finalized. size_t Off = 0; @@ -1106,12 +389,12 @@ template void EhOutputSection::finalize() { Cie->Piece->OutputOff = Off; Off += alignTo(Cie->Piece->size(), sizeof(uintX_t)); - for (SectionPiece *Fde : Cie->FdePieces) { + for (EhSectionPiece *Fde : Cie->FdePieces) { Fde->OutputOff = Off; Off += alignTo(Fde->size(), sizeof(uintX_t)); } } - this->Header.sh_size = Off; + this->Size = Off; } template static uint64_t readFdeAddr(uint8_t *Buf, int Size) { @@ -1143,7 +426,7 @@ typename ELFT::uint EhOutputSection::getFdePc(uint8_t *Buf, size_t FdeOff, if ((Enc & 0x70) == DW_EH_PE_absptr) return Addr; if ((Enc & 0x70) == DW_EH_PE_pcrel) - return Addr + this->getVA() + Off; + return Addr + this->Addr + Off; fatal("unknown FDE size relative encoding"); } @@ -1153,7 +436,7 @@ template void EhOutputSection::writeTo(uint8_t *Buf) { size_t CieOffset = Cie->Piece->OutputOff; writeCieFde(Buf + CieOffset, Cie->Piece->data()); - for (SectionPiece *Fde : Cie->FdePieces) { + for (EhSectionPiece *Fde : Cie->FdePieces) { size_t Off = Fde->OutputOff; writeCieFde(Buf + Off, Fde->data()); @@ -1169,13 +452,13 @@ template void EhOutputSection::writeTo(uint8_t *Buf) { // Construct .eh_frame_hdr. .eh_frame_hdr is a binary search table // to get a FDE from an address to which FDE is applied. So here // we obtain two addresses and pass them to EhFrameHdr object. - if (Out::EhFrameHdr) { + if (In::EhFrameHdr) { for (CieRecord *Cie : Cies) { - uint8_t Enc = getFdeEncoding(Cie->Piece->data()); + uint8_t Enc = getFdeEncoding(Cie->Piece); for (SectionPiece *Fde : Cie->FdePieces) { uintX_t Pc = getFdePc(Buf, Fde->OutputOff, Enc); - uintX_t FdeVA = this->getVA() + Fde->OutputOff; - Out::EhFrameHdr->addFde(Pc, FdeVA); + uintX_t FdeVA = this->Addr + Fde->OutputOff; + In::EhFrameHdr->addFde(Pc, FdeVA); } } } @@ -1184,620 +467,127 @@ template void EhOutputSection::writeTo(uint8_t *Buf) { template MergeOutputSection::MergeOutputSection(StringRef Name, uint32_t Type, uintX_t Flags, uintX_t Alignment) - : OutputSectionBase(Name, Type, Flags), + : OutputSectionBase(Name, Type, Flags), Builder(StringTableBuilder::RAW, Alignment) {} template void MergeOutputSection::writeTo(uint8_t *Buf) { - if (shouldTailMerge()) { - StringRef Data = Builder.data(); - memcpy(Buf, Data.data(), Data.size()); - return; - } - for (const std::pair, size_t> &P : Builder.getMap()) { - StringRef Data = P.first.Val; - memcpy(Buf + P.second, Data.data(), Data.size()); - } -} - -static StringRef toStringRef(ArrayRef A) { - return {(const char *)A.data(), A.size()}; + Builder.write(Buf); } template -void MergeOutputSection::addSection(InputSectionBase *C) { +void MergeOutputSection::addSection(InputSectionData *C) { auto *Sec = cast>(C); Sec->OutSec = this; this->updateAlignment(Sec->Alignment); - this->Header.sh_entsize = Sec->getSectionHdr()->sh_entsize; + this->Entsize = Sec->Entsize; Sections.push_back(Sec); - - bool IsString = this->Header.sh_flags & SHF_STRINGS; - - for (SectionPiece &Piece : Sec->Pieces) { - if (!Piece.Live) - continue; - uintX_t OutputOffset = Builder.add(toStringRef(Piece.data())); - if (!IsString || !shouldTailMerge()) - Piece.OutputOff = OutputOffset; - } -} - -template -unsigned MergeOutputSection::getOffset(StringRef Val) { - return Builder.getOffset(Val); } template bool MergeOutputSection::shouldTailMerge() const { - return Config->Optimize >= 2 && this->Header.sh_flags & SHF_STRINGS; + return (this->Flags & SHF_STRINGS) && Config->Optimize >= 2; } -template void MergeOutputSection::finalize() { - if (shouldTailMerge()) - Builder.finalize(); - this->Header.sh_size = Builder.getSize(); -} - -template void MergeOutputSection::finalizePieces() { +template void MergeOutputSection::finalizeTailMerge() { + // Add all string pieces to the string table builder to create section + // contents. for (MergeInputSection *Sec : Sections) - Sec->finalizePieces(); -} - -template -StringTableSection::StringTableSection(StringRef Name, bool Dynamic) - : OutputSectionBase(Name, SHT_STRTAB, - Dynamic ? (uintX_t)SHF_ALLOC : 0), - Dynamic(Dynamic) {} - -// Adds a string to the string table. If HashIt is true we hash and check for -// duplicates. It is optional because the name of global symbols are already -// uniqued and hashing them again has a big cost for a small value: uniquing -// them with some other string that happens to be the same. -template -unsigned StringTableSection::addString(StringRef S, bool HashIt) { - if (HashIt) { - auto R = StringMap.insert(std::make_pair(S, Size)); - if (!R.second) - return R.first->second; - } - unsigned Ret = Size; - Size += S.size() + 1; - Strings.push_back(S); - return Ret; -} - -template void StringTableSection::writeTo(uint8_t *Buf) { - // ELF string tables start with NUL byte, so advance the pointer by one. - ++Buf; - for (StringRef S : Strings) { - memcpy(Buf, S.data(), S.size()); - Buf += S.size() + 1; - } -} - -template -typename ELFT::uint DynamicReloc::getOffset() const { - if (OutputSec) - return OutputSec->getVA() + OffsetInSec; - return InputSec->OutSec->getVA() + InputSec->getOffset(OffsetInSec); -} - -template -typename ELFT::uint DynamicReloc::getAddend() const { - if (UseSymVA) - return Sym->getVA(Addend); - return Addend; -} - -template uint32_t DynamicReloc::getSymIndex() const { - if (Sym && !UseSymVA) - return Sym->DynsymIndex; - return 0; -} - -template -SymbolTableSection::SymbolTableSection( - StringTableSection &StrTabSec) - : OutputSectionBase(StrTabSec.isDynamic() ? ".dynsym" : ".symtab", - StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB, - StrTabSec.isDynamic() ? (uintX_t)SHF_ALLOC : 0), - StrTabSec(StrTabSec) { - this->Header.sh_entsize = sizeof(Elf_Sym); - this->Header.sh_addralign = sizeof(uintX_t); -} - -// Orders symbols according to their positions in the GOT, -// in compliance with MIPS ABI rules. -// See "Global Offset Table" in Chapter 5 in the following document -// for detailed description: -// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf -static bool sortMipsSymbols(const std::pair &L, - const std::pair &R) { - // Sort entries related to non-local preemptible symbols by GOT indexes. - // All other entries go to the first part of GOT in arbitrary order. - bool LIsInLocalGot = !L.first->IsInGlobalMipsGot; - bool RIsInLocalGot = !R.first->IsInGlobalMipsGot; - if (LIsInLocalGot || RIsInLocalGot) - return !RIsInLocalGot; - return L.first->GotIndex < R.first->GotIndex; -} - -static uint8_t getSymbolBinding(SymbolBody *Body) { - Symbol *S = Body->symbol(); - uint8_t Visibility = S->Visibility; - if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED) - return STB_LOCAL; - if (Config->NoGnuUnique && S->Binding == STB_GNU_UNIQUE) - return STB_GLOBAL; - return S->Binding; -} - -template void SymbolTableSection::finalize() { - if (this->Header.sh_size) - return; // Already finalized. - - this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym); - this->Header.sh_link = StrTabSec.SectionIndex; - this->Header.sh_info = NumLocals + 1; - - if (Config->Relocatable) { - size_t I = NumLocals; - for (const std::pair &P : Symbols) - P.first->DynsymIndex = ++I; - return; - } - - if (!StrTabSec.isDynamic()) { - std::stable_sort(Symbols.begin(), Symbols.end(), - [](const std::pair &L, - const std::pair &R) { - return getSymbolBinding(L.first) == STB_LOCAL && - getSymbolBinding(R.first) != STB_LOCAL; - }); - return; - } - if (Out::GnuHashTab) - // NB: It also sorts Symbols to meet the GNU hash table requirements. - Out::GnuHashTab->addSymbols(Symbols); - else if (Config->EMachine == EM_MIPS) - std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols); - size_t I = 0; - for (const std::pair &P : Symbols) - P.first->DynsymIndex = ++I; -} - -template -void SymbolTableSection::addSymbol(SymbolBody *B) { - Symbols.push_back({B, StrTabSec.addString(B->getName(), false)}); -} - -template void SymbolTableSection::writeTo(uint8_t *Buf) { - 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()) - writeLocalSymbols(Buf); - - writeGlobalSymbols(Buf); -} - -template -void SymbolTableSection::writeLocalSymbols(uint8_t *&Buf) { - // Iterate over all input object files to copy their local symbols - // to the output symbol table pointed by Buf. - for (const std::unique_ptr> &File : - Symtab::X->getObjectFiles()) { - for (const std::pair *, size_t> &P : - File->KeptLocalSyms) { - const DefinedRegular &Body = *P.first; - InputSectionBase *Section = Body.Section; - auto *ESym = reinterpret_cast(Buf); - - if (!Section) { - ESym->st_shndx = SHN_ABS; - ESym->st_value = Body.Value; - } else { - const OutputSectionBase *OutSec = Section->OutSec; - ESym->st_shndx = OutSec->SectionIndex; - ESym->st_value = OutSec->getVA() + Section->getOffset(Body); - } - ESym->st_name = P.second; - ESym->st_size = Body.template getSize(); - ESym->setBindingAndType(STB_LOCAL, Body.Type); - Buf += sizeof(*ESym); - } - } -} - -template -void SymbolTableSection::writeGlobalSymbols(uint8_t *Buf) { - // Write the internal symbol table contents to the output symbol table - // pointed by Buf. - auto *ESym = reinterpret_cast(Buf); - for (const std::pair &P : Symbols) { - SymbolBody *Body = P.first; - size_t StrOff = P.second; - - uint8_t Type = Body->Type; - uintX_t Size = Body->getSize(); - - ESym->setBindingAndType(getSymbolBinding(Body), Type); - ESym->st_size = Size; - ESym->st_name = StrOff; - ESym->setVisibility(Body->symbol()->Visibility); - ESym->st_value = Body->getVA(); - - if (const OutputSectionBase *OutSec = getOutputSection(Body)) - ESym->st_shndx = OutSec->SectionIndex; - else if (isa>(Body)) - ESym->st_shndx = SHN_ABS; - - // On MIPS we need to mark symbol which has a PLT entry and requires pointer - // equality by STO_MIPS_PLT flag. That is necessary to help dynamic linker - // distinguish such symbols and MIPS lazy-binding stubs. - // https://sourceware.org/ml/binutils/2008-07/txt00000.txt - if (Config->EMachine == EM_MIPS && Body->isInPlt() && - Body->NeedsCopyOrPltAddr) - ESym->st_other |= STO_MIPS_PLT; - ++ESym; - } -} - -template -const OutputSectionBase * -SymbolTableSection::getOutputSection(SymbolBody *Sym) { - switch (Sym->kind()) { - case SymbolBody::DefinedSyntheticKind: - return cast>(Sym)->Section; - case SymbolBody::DefinedRegularKind: { - auto &D = cast>(*Sym); - if (D.Section) - return D.Section->OutSec; - break; - } - case SymbolBody::DefinedCommonKind: - return Out::Bss; - case SymbolBody::SharedKind: - if (cast>(Sym)->needsCopy()) - return Out::Bss; - break; - case SymbolBody::UndefinedKind: - case SymbolBody::LazyArchiveKind: - case SymbolBody::LazyObjectKind: - break; - case SymbolBody::DefinedBitcodeKind: - llvm_unreachable("should have been replaced"); - } - return nullptr; -} - -template -VersionDefinitionSection::VersionDefinitionSection() - : OutputSectionBase(".gnu.version_d", SHT_GNU_verdef, SHF_ALLOC) { - this->Header.sh_addralign = sizeof(uint32_t); -} - -static StringRef getFileDefName() { - if (!Config->SoName.empty()) - return Config->SoName; - return Config->OutputFile; -} - -template void VersionDefinitionSection::finalize() { - FileDefNameOff = Out::DynStrTab->addString(getFileDefName()); - for (VersionDefinition &V : Config->VersionDefinitions) - V.NameOff = Out::DynStrTab->addString(V.Name); - - this->Header.sh_size = - (sizeof(Elf_Verdef) + sizeof(Elf_Verdaux)) * getVerDefNum(); - this->Header.sh_link = Out::DynStrTab->SectionIndex; - - // sh_info should be set to the number of definitions. This fact is missed in - // documentation, but confirmed by binutils community: - // https://sourceware.org/ml/binutils/2014-11/msg00355.html - this->Header.sh_info = getVerDefNum(); -} - -template -void VersionDefinitionSection::writeOne(uint8_t *Buf, uint32_t Index, - StringRef Name, size_t NameOff) { - auto *Verdef = reinterpret_cast(Buf); - Verdef->vd_version = 1; - Verdef->vd_cnt = 1; - Verdef->vd_aux = sizeof(Elf_Verdef); - Verdef->vd_next = sizeof(Elf_Verdef) + sizeof(Elf_Verdaux); - Verdef->vd_flags = (Index == 1 ? VER_FLG_BASE : 0); - Verdef->vd_ndx = Index; - Verdef->vd_hash = hashSysv(Name); - - auto *Verdaux = reinterpret_cast(Buf + sizeof(Elf_Verdef)); - Verdaux->vda_name = NameOff; - Verdaux->vda_next = 0; -} - -template -void VersionDefinitionSection::writeTo(uint8_t *Buf) { - writeOne(Buf, 1, getFileDefName(), FileDefNameOff); - - for (VersionDefinition &V : Config->VersionDefinitions) { - Buf += sizeof(Elf_Verdef) + sizeof(Elf_Verdaux); - writeOne(Buf, V.Id, V.Name, V.NameOff); - } - - // Need to terminate the last version definition. - Elf_Verdef *Verdef = reinterpret_cast(Buf); - Verdef->vd_next = 0; -} - -template -VersionTableSection::VersionTableSection() - : OutputSectionBase(".gnu.version", SHT_GNU_versym, SHF_ALLOC) { - this->Header.sh_addralign = sizeof(uint16_t); -} - -template void VersionTableSection::finalize() { - this->Header.sh_size = - sizeof(Elf_Versym) * (Out::DynSymTab->getSymbols().size() + 1); - this->Header.sh_entsize = sizeof(Elf_Versym); - // At the moment of june 2016 GNU docs does not mention that sh_link field - // should be set, but Sun docs do. Also readelf relies on this field. - this->Header.sh_link = Out::DynSymTab->SectionIndex; -} - -template void VersionTableSection::writeTo(uint8_t *Buf) { - auto *OutVersym = reinterpret_cast(Buf) + 1; - for (const std::pair &P : - Out::DynSymTab->getSymbols()) { - OutVersym->vs_index = P.first->symbol()->VersionId; - ++OutVersym; - } -} - -template -VersionNeedSection::VersionNeedSection() - : OutputSectionBase(".gnu.version_r", SHT_GNU_verneed, SHF_ALLOC) { - this->Header.sh_addralign = sizeof(uint32_t); - - // Identifiers in verneed section start at 2 because 0 and 1 are reserved - // for VER_NDX_LOCAL and VER_NDX_GLOBAL. - // First identifiers are reserved by verdef section if it exist. - NextIndex = getVerDefNum() + 1; -} - -template -void VersionNeedSection::addSymbol(SharedSymbol *SS) { - if (!SS->Verdef) { - SS->symbol()->VersionId = VER_NDX_GLOBAL; - return; - } - SharedFile *F = SS->file(); - // If we don't already know that we need an Elf_Verneed for this DSO, prepare - // to create one by adding it to our needed list and creating a dynstr entry - // for the soname. - if (F->VerdefMap.empty()) - Needed.push_back({F, Out::DynStrTab->addString(F->getSoName())}); - typename SharedFile::NeededVer &NV = F->VerdefMap[SS->Verdef]; - // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef, - // prepare to create one by allocating a version identifier and creating a - // dynstr entry for the version name. - if (NV.Index == 0) { - NV.StrTab = Out::DynStrTab->addString( - SS->file()->getStringTable().data() + SS->Verdef->getAux()->vda_name); - NV.Index = NextIndex++; - } - SS->symbol()->VersionId = NV.Index; -} - -template void VersionNeedSection::writeTo(uint8_t *Buf) { - // The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs. - auto *Verneed = reinterpret_cast(Buf); - auto *Vernaux = reinterpret_cast(Verneed + Needed.size()); - - for (std::pair *, size_t> &P : Needed) { - // Create an Elf_Verneed for this DSO. - Verneed->vn_version = 1; - Verneed->vn_cnt = P.first->VerdefMap.size(); - Verneed->vn_file = P.second; - Verneed->vn_aux = - reinterpret_cast(Vernaux) - reinterpret_cast(Verneed); - Verneed->vn_next = sizeof(Elf_Verneed); - ++Verneed; - - // Create the Elf_Vernauxs for this Elf_Verneed. The loop iterates over - // VerdefMap, which will only contain references to needed version - // definitions. Each Elf_Vernaux is based on the information contained in - // the Elf_Verdef in the source DSO. This loop iterates over a std::map of - // pointers, but is deterministic because the pointers refer to Elf_Verdef - // data structures within a single input file. - for (auto &NV : P.first->VerdefMap) { - Vernaux->vna_hash = NV.first->vd_hash; - Vernaux->vna_flags = 0; - Vernaux->vna_other = NV.second.Index; - Vernaux->vna_name = NV.second.StrTab; - Vernaux->vna_next = sizeof(Elf_Vernaux); - ++Vernaux; - } - - Vernaux[-1].vna_next = 0; - } - Verneed[-1].vn_next = 0; -} - -template void VersionNeedSection::finalize() { - this->Header.sh_link = Out::DynStrTab->SectionIndex; - this->Header.sh_info = Needed.size(); - unsigned Size = Needed.size() * sizeof(Elf_Verneed); - for (std::pair *, size_t> &P : Needed) - Size += P.first->VerdefMap.size() * sizeof(Elf_Vernaux); - this->Header.sh_size = Size; -} - -template -BuildIdSection::BuildIdSection(size_t HashSize) - : OutputSectionBase(".note.gnu.build-id", SHT_NOTE, SHF_ALLOC), - HashSize(HashSize) { - // 16 bytes for the note section header. - this->Header.sh_size = 16 + HashSize; -} - -template void BuildIdSection::writeTo(uint8_t *Buf) { - const endianness E = ELFT::TargetEndianness; - write32(Buf, 4); // Name size - write32(Buf + 4, HashSize); // Content size - write32(Buf + 8, NT_GNU_BUILD_ID); // Type - memcpy(Buf + 12, "GNU", 4); // Name string - HashBuf = Buf + 16; -} - -template -void BuildIdFnv1::writeBuildId(ArrayRef> Bufs) { - const endianness E = ELFT::TargetEndianness; + for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) + if (Sec->Pieces[I].Live) + Builder.add(Sec->getData(I)); - // 64-bit FNV-1 hash - uint64_t Hash = 0xcbf29ce484222325; - for (ArrayRef Buf : Bufs) { - for (uint8_t B : Buf) { - Hash *= 0x100000001b3; - Hash ^= B; - } - } - write64(this->HashBuf, Hash); -} + // Fix the string table content. After this, the contents will never change. + Builder.finalize(); + this->Size = Builder.getSize(); -template -void BuildIdMd5::writeBuildId(ArrayRef> Bufs) { - MD5 Hash; - for (ArrayRef Buf : Bufs) - Hash.update(Buf); - MD5::MD5Result Res; - Hash.final(Res); - memcpy(this->HashBuf, Res, 16); -} - -template -void BuildIdSha1::writeBuildId(ArrayRef> Bufs) { - SHA1 Hash; - for (ArrayRef Buf : Bufs) - Hash.update(Buf); - memcpy(this->HashBuf, Hash.final().data(), 20); + // finalize() fixed tail-optimized strings, so we can now get + // offsets of strings. Get an offset for each string and save it + // to a corresponding StringPiece for easy access. + for (MergeInputSection *Sec : Sections) + for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) + if (Sec->Pieces[I].Live) + Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I)); } -template -BuildIdHexstring::BuildIdHexstring() - : BuildIdSection(Config->BuildIdVector.size()) {} +template void MergeOutputSection::finalizeNoTailMerge() { + // Add all string pieces to the string table builder to create section + // contents. Because we are not tail-optimizing, offsets of strings are + // fixed when they are added to the builder (string table builder contains + // a hash table from strings to offsets). + for (MergeInputSection *Sec : Sections) + for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) + if (Sec->Pieces[I].Live) + Sec->Pieces[I].OutputOff = Builder.add(Sec->getData(I)); -template -void BuildIdHexstring::writeBuildId(ArrayRef> Bufs) { - memcpy(this->HashBuf, Config->BuildIdVector.data(), - Config->BuildIdVector.size()); + Builder.finalizeInOrder(); + this->Size = Builder.getSize(); } -template -MipsReginfoOutputSection::MipsReginfoOutputSection() - : OutputSectionBase(".reginfo", SHT_MIPS_REGINFO, SHF_ALLOC) { - this->Header.sh_addralign = 4; - this->Header.sh_entsize = sizeof(Elf_Mips_RegInfo); - this->Header.sh_size = sizeof(Elf_Mips_RegInfo); +template void MergeOutputSection::finalize() { + if (shouldTailMerge()) + finalizeTailMerge(); + else + finalizeNoTailMerge(); } template -void MipsReginfoOutputSection::writeTo(uint8_t *Buf) { - auto *R = reinterpret_cast(Buf); - R->ri_gp_value = Out::Got->getVA() + MipsGPOffset; - R->ri_gprmask = GprMask; +static typename ELFT::uint getOutFlags(InputSectionBase *S) { + return S->Flags & ~SHF_GROUP & ~SHF_COMPRESSED; } template -void MipsReginfoOutputSection::addSection(InputSectionBase *C) { - // Copy input object file's .reginfo gprmask to output. - auto *S = cast>(C); - GprMask |= S->Reginfo->ri_gprmask; - S->OutSec = this; -} +static SectionKey createKey(InputSectionBase *C, + StringRef OutsecName) { + typedef typename ELFT::uint uintX_t; + uintX_t Flags = getOutFlags(C); -template -MipsOptionsOutputSection::MipsOptionsOutputSection() - : OutputSectionBase(".MIPS.options", SHT_MIPS_OPTIONS, - SHF_ALLOC | SHF_MIPS_NOSTRIP) { - this->Header.sh_addralign = 8; - this->Header.sh_entsize = 1; - this->Header.sh_size = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo); -} + // For SHF_MERGE we create different output sections for each alignment. + // This makes each output section simple and keeps a single level mapping from + // input to output. + // In case of relocatable object generation we do not try to perform merging + // and treat SHF_MERGE sections as regular ones, but also create different + // output sections for them to allow merging at final linking stage. + uintX_t Alignment = 0; + if (isa>(C) || + (Config->Relocatable && (C->Flags & SHF_MERGE))) + Alignment = std::max(C->Alignment, C->Entsize); -template -void MipsOptionsOutputSection::writeTo(uint8_t *Buf) { - auto *Opt = reinterpret_cast(Buf); - Opt->kind = ODK_REGINFO; - Opt->size = this->Header.sh_size; - Opt->section = 0; - Opt->info = 0; - auto *Reg = reinterpret_cast(Buf + sizeof(*Opt)); - Reg->ri_gp_value = Out::Got->getVA() + MipsGPOffset; - Reg->ri_gprmask = GprMask; + return SectionKey{OutsecName, C->Type, Flags, Alignment}; } template -void MipsOptionsOutputSection::addSection(InputSectionBase *C) { - auto *S = cast>(C); - if (S->Reginfo) - GprMask |= S->Reginfo->ri_gprmask; - S->OutSec = this; -} - -template -std::pair *, bool> +std::pair OutputSectionFactory::create(InputSectionBase *C, StringRef OutsecName) { SectionKey Key = createKey(C, OutsecName); - OutputSectionBase *&Sec = Map[Key]; - if (Sec) + return create(Key, C); +} + +template +std::pair +OutputSectionFactory::create(const SectionKey &Key, + InputSectionBase *C) { + uintX_t Flags = getOutFlags(C); + OutputSectionBase *&Sec = Map[Key]; + if (Sec) { + Sec->Flags |= Flags; return {Sec, false}; + } - switch (C->SectionKind) { + uint32_t Type = C->Type; + switch (C->kind()) { case InputSectionBase::Regular: - Sec = new OutputSection(Key.Name, Key.Type, Key.Flags); + case InputSectionBase::Synthetic: + Sec = make>(Key.Name, Type, Flags); break; case InputSectionBase::EHFrame: return {Out::EhFrame, false}; case InputSectionBase::Merge: - Sec = new MergeOutputSection(Key.Name, Key.Type, Key.Flags, - Key.Alignment); - break; - case InputSectionBase::MipsReginfo: - Sec = new MipsReginfoOutputSection(); - break; - case InputSectionBase::MipsOptions: - Sec = new MipsOptionsOutputSection(); + Sec = make>(Key.Name, Type, Flags, Key.Alignment); break; } return {Sec, true}; } -template -OutputSectionBase *OutputSectionFactory::lookup(StringRef Name, - uint32_t Type, - uintX_t Flags) { - return Map.lookup({Name, Type, Flags, 0}); -} - -template -SectionKey -OutputSectionFactory::createKey(InputSectionBase *C, - StringRef OutsecName) { - const Elf_Shdr *H = C->getSectionHdr(); - uintX_t Flags = H->sh_flags & ~SHF_GROUP & ~SHF_COMPRESSED; - - // For SHF_MERGE we create different output sections for each alignment. - // This makes each output section simple and keeps a single level mapping from - // input to output. - uintX_t Alignment = 0; - if (isa>(C)) - Alignment = std::max(H->sh_addralign, H->sh_entsize); - - uint32_t Type = H->sh_type; - return SectionKey{OutsecName, Type, Flags, Alignment}; -} - template typename lld::elf::SectionKey DenseMapInfo>::getEmptyKey() { @@ -1832,55 +622,11 @@ template struct DenseMapInfo>; namespace lld { namespace elf { -template class OutputSectionBase; -template class OutputSectionBase; -template class OutputSectionBase; -template class OutputSectionBase; - -template class EhFrameHeader; -template class EhFrameHeader; -template class EhFrameHeader; -template class EhFrameHeader; - -template class GotPltSection; -template class GotPltSection; -template class GotPltSection; -template class GotPltSection; - -template class GotSection; -template class GotSection; -template class GotSection; -template class GotSection; - -template class PltSection; -template class PltSection; -template class PltSection; -template class PltSection; - -template class RelocationSection; -template class RelocationSection; -template class RelocationSection; -template class RelocationSection; - -template class InterpSection; -template class InterpSection; -template class InterpSection; -template class InterpSection; - -template class GnuHashTableSection; -template class GnuHashTableSection; -template class GnuHashTableSection; -template class GnuHashTableSection; - -template class HashTableSection; -template class HashTableSection; -template class HashTableSection; -template class HashTableSection; - -template class DynamicSection; -template class DynamicSection; -template class DynamicSection; -template class DynamicSection; + +template void OutputSectionBase::writeHeaderTo(ELF32LE::Shdr *Shdr); +template void OutputSectionBase::writeHeaderTo(ELF32BE::Shdr *Shdr); +template void OutputSectionBase::writeHeaderTo(ELF64LE::Shdr *Shdr); +template void OutputSectionBase::writeHeaderTo(ELF64BE::Shdr *Shdr); template class OutputSection; template class OutputSection; @@ -1892,71 +638,11 @@ template class EhOutputSection; template class EhOutputSection; template class EhOutputSection; -template class MipsReginfoOutputSection; -template class MipsReginfoOutputSection; -template class MipsReginfoOutputSection; -template class MipsReginfoOutputSection; - -template class MipsOptionsOutputSection; -template class MipsOptionsOutputSection; -template class MipsOptionsOutputSection; -template class MipsOptionsOutputSection; - template class MergeOutputSection; template class MergeOutputSection; template class MergeOutputSection; template class MergeOutputSection; -template class StringTableSection; -template class StringTableSection; -template class StringTableSection; -template class StringTableSection; - -template class SymbolTableSection; -template class SymbolTableSection; -template class SymbolTableSection; -template class SymbolTableSection; - -template class VersionTableSection; -template class VersionTableSection; -template class VersionTableSection; -template class VersionTableSection; - -template class VersionNeedSection; -template class VersionNeedSection; -template class VersionNeedSection; -template class VersionNeedSection; - -template class VersionDefinitionSection; -template class VersionDefinitionSection; -template class VersionDefinitionSection; -template class VersionDefinitionSection; - -template class BuildIdSection; -template class BuildIdSection; -template class BuildIdSection; -template class BuildIdSection; - -template class BuildIdFnv1; -template class BuildIdFnv1; -template class BuildIdFnv1; -template class BuildIdFnv1; - -template class BuildIdMd5; -template class BuildIdMd5; -template class BuildIdMd5; -template class BuildIdMd5; - -template class BuildIdSha1; -template class BuildIdSha1; -template class BuildIdSha1; -template class BuildIdSha1; - -template class BuildIdHexstring; -template class BuildIdHexstring; -template class BuildIdHexstring; -template class BuildIdHexstring; - template class OutputSectionFactory; template class OutputSectionFactory; template class OutputSectionFactory; -- cgit v1.2.3