//===- 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 "SymbolTable.h" #include "Target.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf2; template OutputSectionBase::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 GotSection::GotSection() : OutputSectionBase(".got", llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE) { this->Header.sh_addralign = sizeof(uintX_t); } template void GotSection::addEntry(SymbolBody *Sym) { Sym->GotIndex = Entries.size(); Entries.push_back(Sym); } template typename GotSection::uintX_t GotSection::getEntryAddr(const SymbolBody &B) const { return this->getVA() + B.GotIndex * sizeof(uintX_t); } template void GotSection::writeTo(uint8_t *Buf) { for (const SymbolBody *B : Entries) { uint8_t *Entry = Buf; Buf += sizeof(uintX_t); if (canBePreempted(B, false)) continue; // The dynamic linker will take care of it. uintX_t VA = getSymVA(*B); write(Entry, VA); } } template PltSection::PltSection() : OutputSectionBase(".plt", llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR) { this->Header.sh_addralign = 16; } template void PltSection::writeTo(uint8_t *Buf) { size_t Off = 0; for (const SymbolBody *E : Entries) { uint64_t Got = Out::Got->getEntryAddr(*E); uint64_t Plt = this->getVA() + Off; Target->writePltEntry(Buf + Off, Got, Plt); Off += Target->getPltEntrySize(); } } template void PltSection::addEntry(SymbolBody *Sym) { Sym->PltIndex = Entries.size(); Entries.push_back(Sym); } template typename PltSection::uintX_t PltSection::getEntryAddr(const SymbolBody &B) const { return this->getVA() + B.PltIndex * Target->getPltEntrySize(); } template void PltSection::finalize() { this->Header.sh_size = Entries.size() * Target->getPltEntrySize(); } template RelocationSection::RelocationSection(bool IsRela) : OutputSectionBase(IsRela ? ".rela.dyn" : ".rel.dyn", IsRela ? llvm::ELF::SHT_RELA : llvm::ELF::SHT_REL, llvm::ELF::SHF_ALLOC), IsRela(IsRela) { this->Header.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); this->Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; } template void RelocationSection::writeTo(uint8_t *Buf) { const unsigned EntrySize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); bool IsMips64EL = Relocs[0].C.getFile()->getObj().isMips64EL(); for (const DynamicReloc &Rel : Relocs) { auto *P = reinterpret_cast(Buf); Buf += EntrySize; const InputSection &C = Rel.C; const Elf_Rel &RI = Rel.RI; uint32_t SymIndex = RI.getSymbol(IsMips64EL); const ObjectFile &File = *C.getFile(); SymbolBody *Body = File.getSymbolBody(SymIndex); if (Body) Body = Body->repl(); uint32_t Type = RI.getType(IsMips64EL); bool NeedsGot = Body && Target->relocNeedsGot(Type, *Body); bool CanBePreempted = canBePreempted(Body, NeedsGot); uintX_t Addend = 0; if (!CanBePreempted) { if (IsRela) { if (Body) Addend += getSymVA(cast>(*Body)); else Addend += getLocalRelTarget(File, RI); } P->setSymbolAndType(0, Target->getRelativeReloc(), IsMips64EL); } if (NeedsGot) { P->r_offset = Out::Got->getEntryAddr(*Body); if (CanBePreempted) P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), Target->getGotReloc(), IsMips64EL); } else { if (IsRela) Addend += static_cast(RI).r_addend; P->r_offset = RI.r_offset + C.OutSec->getVA() + C.OutSecOff; if (CanBePreempted) P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), Type, IsMips64EL); } if (IsRela) static_cast(P)->r_addend = Addend; } } template void RelocationSection::finalize() { this->Header.sh_link = Out::DynSymTab->getSectionIndex(); this->Header.sh_size = Relocs.size() * this->Header.sh_entsize; } template InterpSection::InterpSection() : OutputSectionBase(".interp", llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC) { this->Header.sh_size = Config->DynamicLinker.size() + 1; this->Header.sh_addralign = 1; } template template void OutputSectionBase::writeHeaderTo( typename ELFFile>::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 void InterpSection::writeTo(uint8_t *Buf) { memcpy(Buf, Config->DynamicLinker.data(), Config->DynamicLinker.size()); } template HashTableSection::HashTableSection() : OutputSectionBase(".hash", llvm::ELF::SHT_HASH, llvm::ELF::SHF_ALLOC) { this->Header.sh_entsize = sizeof(Elf_Word); this->Header.sh_addralign = sizeof(Elf_Word); } template void HashTableSection::addSymbol(SymbolBody *S) { StringRef Name = S->getName(); Out::DynSymTab->addSymbol(Name); Hashes.push_back(hash(Name)); S->setDynamicSymbolTableIndex(Hashes.size()); } template void HashTableSection::finalize() { this->Header.sh_link = Out::DynSymTab->getSectionIndex(); assert(Out::DynSymTab->getNumSymbols() == Hashes.size() + 1); 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 (unsigned I = 1; I < NumSymbols; ++I) { uint32_t Hash = Hashes[I - 1] % NumSymbols; Chains[I] = Buckets[Hash]; Buckets[Hash] = I; } } template DynamicSection::DynamicSection(SymbolTable &SymTab) : OutputSectionBase(".dynamic", llvm::ELF::SHT_DYNAMIC, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE), SymTab(SymTab) { typename Base::HeaderT &Header = this->Header; Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; Header.sh_entsize = ELFT::Is64Bits ? 16 : 8; } template void DynamicSection::finalize() { if (this->Header.sh_size) return; // Already finalized. typename Base::HeaderT &Header = this->Header; Header.sh_link = Out::DynStrTab->getSectionIndex(); unsigned NumEntries = 0; if (Out::RelaDyn->hasRelocs()) { ++NumEntries; // DT_RELA / DT_REL ++NumEntries; // DT_RELASZ / DT_RELSZ ++NumEntries; // DT_RELAENT / DT_RELENT } ++NumEntries; // DT_SYMTAB ++NumEntries; // DT_SYMENT ++NumEntries; // DT_STRTAB ++NumEntries; // DT_STRSZ ++NumEntries; // DT_HASH if (!Config->RPath.empty()) { ++NumEntries; // DT_RUNPATH / DT_RPATH Out::DynStrTab->add(Config->RPath); } if (!Config->SoName.empty()) { ++NumEntries; // DT_SONAME Out::DynStrTab->add(Config->SoName); } if (PreInitArraySec) NumEntries += 2; if (InitArraySec) NumEntries += 2; if (FiniArraySec) NumEntries += 2; for (const std::unique_ptr> &F : SymTab.getSharedFiles()) { if (!F->isNeeded()) continue; Out::DynStrTab->add(F->getSoName()); ++NumEntries; } if (Symbol *S = SymTab.getSymbols().lookup(Config->Init)) InitSym = dyn_cast>(S->Body); if (Symbol *S = SymTab.getSymbols().lookup(Config->Fini)) FiniSym = dyn_cast>(S->Body); if (InitSym) ++NumEntries; // DT_INIT if (FiniSym) ++NumEntries; // DT_FINI if (Config->ZNow || Config->Bsymbolic) ++NumEntries; // DT_FLAGS_1 ++NumEntries; // DT_NULL Header.sh_size = NumEntries * Header.sh_entsize; } template void DynamicSection::writeTo(uint8_t *Buf) { auto *P = reinterpret_cast(Buf); auto WritePtr = [&](int32_t Tag, uint64_t Val) { P->d_tag = Tag; P->d_un.d_ptr = Val; ++P; }; auto WriteVal = [&](int32_t Tag, uint32_t Val) { P->d_tag = Tag; P->d_un.d_val = Val; ++P; }; if (Out::RelaDyn->hasRelocs()) { bool IsRela = Out::RelaDyn->isRela(); WritePtr(IsRela ? DT_RELA : DT_REL, Out::RelaDyn->getVA()); WriteVal(IsRela ? DT_RELASZ : DT_RELSZ, Out::RelaDyn->getSize()); WriteVal(IsRela ? DT_RELAENT : DT_RELENT, IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel)); } WritePtr(DT_SYMTAB, Out::DynSymTab->getVA()); WritePtr(DT_SYMENT, sizeof(Elf_Sym)); WritePtr(DT_STRTAB, Out::DynStrTab->getVA()); WriteVal(DT_STRSZ, Out::DynStrTab->data().size()); WritePtr(DT_HASH, Out::HashTab->getVA()); if (!Config->RPath.empty()) // If --enable-new-dtags is set lld emits DT_RUNPATH // instead of DT_RPATH. The two tags are functionally // equivalent except for the following: // - DT_RUNPATH is searched after LD_LIBRARY_PATH, while // DT_RPATH is searched before. // - DT_RUNPATH is used only to search for direct // dependencies of the object it's contained in, while // DT_RPATH is used for indirect dependencies as well. WriteVal(Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH, Out::DynStrTab->getFileOff(Config->RPath)); if (!Config->SoName.empty()) WriteVal(DT_SONAME, Out::DynStrTab->getFileOff(Config->SoName)); auto WriteArray = [&](int32_t T1, int32_t T2, const OutputSectionBase *Sec) { if (!Sec) return; WritePtr(T1, Sec->getVA()); WriteVal(T2, Sec->getSize()); }; WriteArray(DT_PREINIT_ARRAY, DT_PREINIT_ARRAYSZ, PreInitArraySec); WriteArray(DT_INIT_ARRAY, DT_INIT_ARRAYSZ, InitArraySec); WriteArray(DT_FINI_ARRAY, DT_FINI_ARRAYSZ, FiniArraySec); for (const std::unique_ptr> &F : SymTab.getSharedFiles()) if (F->isNeeded()) WriteVal(DT_NEEDED, Out::DynStrTab->getFileOff(F->getSoName())); if (InitSym) WritePtr(DT_INIT, getSymVA(*InitSym)); if (FiniSym) WritePtr(DT_FINI, getSymVA(*FiniSym)); uint32_t Flags = 0; if (Config->Bsymbolic) Flags |= DF_SYMBOLIC; if (Config->ZNow) Flags |= DF_1_NOW; if (Flags) WriteVal(DT_FLAGS_1, Flags); WriteVal(DT_NULL, 0); } template OutputSection::OutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags) : OutputSectionBase(Name, sh_type, sh_flags) {} template void OutputSection::addSection(InputSection *C) { Sections.push_back(C); C->OutSec = 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->OutSecOff = Off; Off += C->getSize(); this->Header.sh_size = Off; } template typename ELFFile::uintX_t lld::elf2::getSymVA(const SymbolBody &S) { switch (S.kind()) { case SymbolBody::DefinedSyntheticKind: { auto &D = cast>(S); return D.Section.getVA() + D.Sym.st_value; } case SymbolBody::DefinedAbsoluteKind: return cast>(S).Sym.st_value; case SymbolBody::DefinedRegularKind: { const auto &DR = cast>(S); const InputSection *SC = &DR.Section; return SC->OutSec->getVA() + SC->OutSecOff + DR.Sym.st_value; } case SymbolBody::DefinedCommonKind: return Out::Bss->getVA() + cast>(S).OffsetInBSS; case SymbolBody::SharedKind: case SymbolBody::UndefinedKind: return 0; case SymbolBody::LazyKind: assert(S.isUsedInRegularObj() && "Lazy symbol reached writer"); return 0; } llvm_unreachable("Invalid symbol kind"); } // Returns a VA which a relocatin RI refers to. Used only for local symbols. // For non-local symbols, use getSymVA instead. template typename ELFFile::uintX_t lld::elf2::getLocalRelTarget(const ObjectFile &File, const typename ELFFile::Elf_Rel &RI) { typedef typename ELFFile::Elf_Sym Elf_Sym; const Elf_Sym *Sym = File.getObj().getRelocationSymbol(&RI, File.getSymbolTable()); // For certain special relocations, such as R_PPC64_TOC, there's no // corresponding symbol. Just return 0 in that case. if (!Sym) return 0; uint32_t SecIndex = Sym->st_shndx; if (SecIndex == SHN_XINDEX) SecIndex = File.getObj().getExtendedSymbolTableIndex( Sym, File.getSymbolTable(), File.getSymbolTableShndx()); ArrayRef *> Sections = File.getSections(); InputSection *Section = Sections[SecIndex]; // According to the ELF spec reference to a local symbol from outside // the group are not allowed. Unfortunately .eh_frame breaks that rule // and must be treated specially. For now we just replace the symbol with // 0. if (Section == &InputSection::Discarded) return 0; return Section->OutSec->getVA() + Section->OutSecOff + Sym->st_value; } // Returns true if a symbol can be replaced at load-time by a symbol // with the same name defined in other ELF executable or DSO. bool lld::elf2::canBePreempted(const SymbolBody *Body, bool NeedsGot) { if (!Body) return false; // Body is a local symbol. if (Body->isShared()) return true; if (Body->isUndefined()) { if (!Body->isWeak()) return true; // This is an horrible corner case. Ideally we would like to say that any // undefined symbol can be preempted so that the dynamic linker has a // chance of finding it at runtime. // // The problem is that the code sequence used to test for weak undef // functions looks like // if (func) func() // If the code is -fPIC the first reference is a load from the got and // everything works. // If the code is not -fPIC there is no reasonable way to solve it: // * A relocation writing to the text segment will fail (it is ro). // * A copy relocation doesn't work for functions. // * The trick of using a plt entry as the address would fail here since // the plt entry would have a non zero address. // Since we cannot do anything better, we just resolve the symbol to 0 and // don't produce a dynamic relocation. return NeedsGot; } if (!Config->Shared) return false; return Body->getMostConstrainingVisibility() == STV_DEFAULT; } template void OutputSection::writeTo(uint8_t *Buf) { for (InputSection *C : Sections) C->writeTo(Buf); } template StringTableSection::StringTableSection(bool Dynamic) : OutputSectionBase(Dynamic ? ".dynstr" : ".strtab", llvm::ELF::SHT_STRTAB, Dynamic ? (uintX_t)llvm::ELF::SHF_ALLOC : 0), Dynamic(Dynamic) { this->Header.sh_addralign = 1; } template void StringTableSection::writeTo(uint8_t *Buf) { StringRef Data = StrTabBuilder.data(); memcpy(Buf, Data.data(), Data.size()); } template bool lld::elf2::includeInSymtab(const SymbolBody &B) { if (!B.isUsedInRegularObj()) return false; // Don't include synthetic symbols like __init_array_start in every output. if (auto *U = dyn_cast>(&B)) if (&U->Sym == &DefinedAbsolute::IgnoreUndef) return false; return true; } bool lld::elf2::includeInDynamicSymtab(const SymbolBody &B) { uint8_t V = B.getMostConstrainingVisibility(); if (V != STV_DEFAULT && V != STV_PROTECTED) return false; if (Config->ExportDynamic || Config->Shared) return true; return B.isUsedInDynamicReloc(); } template bool lld::elf2::shouldKeepInSymtab(const ObjectFile &File, StringRef SymName, const typename ELFFile::Elf_Sym &Sym) { if (Sym.getType() == STT_SECTION) return false; // If sym references a section in a discarded group, don't keep it. uint32_t SecIndex = Sym.st_shndx; if (SecIndex != SHN_ABS) { if (SecIndex == SHN_XINDEX) SecIndex = File.getObj().getExtendedSymbolTableIndex( &Sym, File.getSymbolTable(), File.getSymbolTableShndx()); ArrayRef *> Sections = File.getSections(); const InputSection *Section = Sections[SecIndex]; if (Section == &InputSection::Discarded) return false; } if (Config->DiscardNone) return true; // ELF defines dynamic locals as symbols which name starts with ".L". return !(Config->DiscardLocals && SymName.startswith(".L")); } template SymbolTableSection::SymbolTableSection( SymbolTable &Table, StringTableSection &StrTabSec) : OutputSectionBase( 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 Base; typename Base::HeaderT &Header = this->Header; Header.sh_entsize = sizeof(Elf_Sym); Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; } template void SymbolTableSection::finalize() { this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym); this->Header.sh_link = StrTabSec.getSectionIndex(); this->Header.sh_info = NumLocals + 1; } template void SymbolTableSection::addSymbol(StringRef Name, bool isLocal) { StrTabSec.add(Name); ++NumVisible; if (isLocal) ++NumLocals; } 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 : Table.getObjectFiles()) { Elf_Sym_Range Syms = File->getLocalSymbols(); for (const Elf_Sym &Sym : Syms) { ErrorOr SymNameOrErr = Sym.getName(File->getStringTable()); error(SymNameOrErr); StringRef SymName = *SymNameOrErr; if (!shouldKeepInSymtab(*File, SymName, Sym)) continue; auto *ESym = reinterpret_cast(Buf); Buf += sizeof(*ESym); ESym->st_name = StrTabSec.getFileOff(SymName); ESym->st_size = Sym.st_size; ESym->setBindingAndType(Sym.getBinding(), Sym.getType()); uint32_t SecIndex = Sym.st_shndx; uintX_t VA = Sym.st_value; if (SecIndex == SHN_ABS) { ESym->st_shndx = SHN_ABS; } else { if (SecIndex == SHN_XINDEX) SecIndex = File->getObj().getExtendedSymbolTableIndex( &Sym, File->getSymbolTable(), File->getSymbolTableShndx()); ArrayRef *> Sections = File->getSections(); const InputSection *Sec = Sections[SecIndex]; ESym->st_shndx = Sec->OutSec->getSectionIndex(); VA += Sec->OutSec->getVA() + Sec->OutSecOff; } ESym->st_value = VA; } } } template void SymbolTableSection::writeGlobalSymbols(uint8_t *&Buf) { // Write the internal symbol table contents to the output symbol table // pointed by Buf. uint8_t *Start = Buf; for (const std::pair &P : Table.getSymbols()) { StringRef Name = P.first; Symbol *Sym = P.second; SymbolBody *Body = Sym->Body; if (!includeInSymtab(*Body)) continue; if (StrTabSec.isDynamic() && !includeInDynamicSymtab(*Body)) continue; auto *ESym = reinterpret_cast(Buf); Buf += sizeof(*ESym); ESym->st_name = StrTabSec.getFileOff(Name); const OutputSectionBase *OutSec = nullptr; const InputSection *Section = nullptr; switch (Body->kind()) { case SymbolBody::DefinedSyntheticKind: OutSec = &cast>(Body)->Section; break; case SymbolBody::DefinedRegularKind: Section = &cast>(Body)->Section; break; case SymbolBody::DefinedCommonKind: OutSec = Out::Bss; break; case SymbolBody::UndefinedKind: case SymbolBody::DefinedAbsoluteKind: case SymbolBody::SharedKind: case SymbolBody::LazyKind: break; } unsigned char Binding = Body->isWeak() ? STB_WEAK : STB_GLOBAL; unsigned char Type = STT_NOTYPE; uintX_t Size = 0; if (const auto *EBody = dyn_cast>(Body)) { const Elf_Sym &InputSym = EBody->Sym; Binding = InputSym.getBinding(); Type = InputSym.getType(); Size = InputSym.st_size; } unsigned char Visibility = Body->getMostConstrainingVisibility(); if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED) Binding = STB_LOCAL; ESym->setBindingAndType(Binding, Type); ESym->st_size = Size; ESym->setVisibility(Visibility); ESym->st_value = getSymVA(*Body); if (Section) OutSec = Section->OutSec; if (isa>(Body)) ESym->st_shndx = SHN_ABS; else if (OutSec) ESym->st_shndx = OutSec->getSectionIndex(); } if (!StrTabSec.isDynamic()) std::stable_sort( reinterpret_cast(Start), reinterpret_cast(Buf), [](const Elf_Sym &A, const Elf_Sym &B) -> bool { return A.getBinding() == STB_LOCAL && B.getBinding() != STB_LOCAL; }); } namespace lld { namespace elf2 { template class OutputSectionBase; template class OutputSectionBase; template void OutputSectionBase::writeHeaderTo( ELFFile>::Elf_Shdr *SHdr); template void OutputSectionBase::writeHeaderTo( ELFFile>::Elf_Shdr *SHdr); template void OutputSectionBase::writeHeaderTo( ELFFile>::Elf_Shdr *SHdr); template void OutputSectionBase::writeHeaderTo( ELFFile>::Elf_Shdr *SHdr); 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 HashTableSection; template class HashTableSection; template class HashTableSection; template class HashTableSection; template class DynamicSection; template class DynamicSection; template class DynamicSection; template class DynamicSection; template class OutputSection; template class OutputSection; template class OutputSection; template class OutputSection; template class StringTableSection; template class StringTableSection; template class SymbolTableSection; template class SymbolTableSection; template class SymbolTableSection; template class SymbolTableSection; template ELFFile::uintX_t getSymVA(const SymbolBody &); template ELFFile::uintX_t getSymVA(const SymbolBody &); template ELFFile::uintX_t getSymVA(const SymbolBody &); template ELFFile::uintX_t getSymVA(const SymbolBody &); template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); template ELFFile::uintX_t getLocalRelTarget(const ObjectFile &, const ELFFile::Elf_Rel &); template bool includeInSymtab(const SymbolBody &); template bool includeInSymtab(const SymbolBody &); template bool includeInSymtab(const SymbolBody &); template bool includeInSymtab(const SymbolBody &); template bool shouldKeepInSymtab(const ObjectFile &, StringRef, const ELFFile::Elf_Sym &); template bool shouldKeepInSymtab(const ObjectFile &, StringRef, const ELFFile::Elf_Sym &); template bool shouldKeepInSymtab(const ObjectFile &, StringRef, const ELFFile::Elf_Sym &); template bool shouldKeepInSymtab(const ObjectFile &, StringRef, const ELFFile::Elf_Sym &); } }