diff options
Diffstat (limited to 'lld/ELF/InputFiles.cpp')
| -rw-r--r-- | lld/ELF/InputFiles.cpp | 223 |
1 files changed, 121 insertions, 102 deletions
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 8e6fe89d6eb..e09d6133353 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -43,7 +43,7 @@ std::vector<BinaryFile *> elf::BinaryFiles; std::vector<BitcodeFile *> elf::BitcodeFiles; std::vector<LazyObjFile *> elf::LazyObjFiles; std::vector<InputFile *> elf::ObjectFiles; -std::vector<InputFile *> elf::SharedFiles; +std::vector<SharedFile *> elf::SharedFiles; std::unique_ptr<TarWriter> elf::Tar; @@ -869,20 +869,83 @@ InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) { return File; } -template <class ELFT> -SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName) +SharedFile::SharedFile(MemoryBufferRef M, StringRef DefaultSoName) : ELFFileBase(SharedKind, M), SoName(DefaultSoName), - IsNeeded(!Config->AsNeeded) { - parseHeader<ELFT>(); + IsNeeded(!Config->AsNeeded) {} + +// Parse the version definitions in the object file if present, and return a +// vector whose nth element contains a pointer to the Elf_Verdef for version +// identifier n. Version identifiers that are not definitions map to nullptr. +template <typename ELFT> +static std::vector<const void *> parseVerdefs(const uint8_t *Base, + const typename ELFT::Shdr *Sec) { + if (!Sec) + return {}; + + // We cannot determine the largest verdef identifier without inspecting + // every Elf_Verdef, but both bfd and gold assign verdef identifiers + // sequentially starting from 1, so we predict that the largest identifier + // will be VerdefCount. + unsigned VerdefCount = Sec->sh_info; + std::vector<const void *> Verdefs(VerdefCount + 1); + + // Build the Verdefs array by following the chain of Elf_Verdef objects + // from the start of the .gnu.version_d section. + const uint8_t *Verdef = Base + Sec->sh_offset; + for (unsigned I = 0; I != VerdefCount; ++I) { + auto *CurVerdef = reinterpret_cast<const typename ELFT::Verdef *>(Verdef); + Verdef += CurVerdef->vd_next; + unsigned VerdefIndex = CurVerdef->vd_ndx; + Verdefs.resize(VerdefIndex + 1); + Verdefs[VerdefIndex] = CurVerdef; + } + return Verdefs; } -// Partially parse the shared object file so that we can call -// getSoName on this object. -template <class ELFT> void SharedFile<ELFT>::parseDynamic() { +// We do not usually care about alignments of data in shared object +// files because the loader takes care of it. However, if we promote a +// DSO symbol to point to .bss due to copy relocation, we need to keep +// the original alignment requirements. We infer it in this function. +template <typename ELFT> +static uint64_t getAlignment(ArrayRef<typename ELFT::Shdr> Sections, + const typename ELFT::Sym &Sym) { + uint64_t Ret = UINT64_MAX; + if (Sym.st_value) + Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value); + if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) + Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign); + return (Ret > UINT32_MAX) ? 0 : Ret; +} + +// Fully parse the shared object file. +// +// This function parses symbol versions. If a DSO has version information, +// the file has a ".gnu.version_d" section which contains symbol version +// definitions. Each symbol is associated to one version through a table in +// ".gnu.version" section. That table is a parallel array for the symbol +// table, and each table entry contains an index in ".gnu.version_d". +// +// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for +// VER_NDX_GLOBAL. There's no table entry for these special versions in +// ".gnu.version_d". +// +// The file format for symbol versioning is perhaps a bit more complicated +// than necessary, but you can easily understand the code if you wrap your +// head around the data structure described above. +template <class ELFT> void SharedFile::parse() { + using Elf_Dyn = typename ELFT::Dyn; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Verdef = typename ELFT::Verdef; + using Elf_Versym = typename ELFT::Versym; + ArrayRef<Elf_Dyn> DynamicTags; const ELFFile<ELFT> Obj = this->getObj<ELFT>(); ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this); + const Elf_Shdr *VersymSec = nullptr; + const Elf_Shdr *VerdefSec = nullptr; + // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. for (const Elf_Shdr &Sec : Sections) { switch (Sec.sh_type) { @@ -896,16 +959,18 @@ template <class ELFT> void SharedFile<ELFT>::parseDynamic() { CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(&Sec), this); break; case SHT_GNU_versym: - this->VersymSec = &Sec; + VersymSec = &Sec; break; case SHT_GNU_verdef: - this->VerdefSec = &Sec; + VerdefSec = &Sec; break; } } - if (this->VersymSec && this->getELFSyms<ELFT>().empty()) + if (VersymSec && this->getELFSyms<ELFT>().empty()) { error("SHT_GNU_versym should be associated with symbol table"); + return; + } // Search for a DT_SONAME tag to initialize this->SoName. for (const Elf_Dyn &Dyn : DynamicTags) { @@ -921,91 +986,38 @@ template <class ELFT> void SharedFile<ELFT>::parseDynamic() { SoName = this->StringTable.data() + Val; } } -} -// Parses ".gnu.version" section which is a parallel array for the symbol table. -// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL. -template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() { - size_t Size = this->getELFSyms<ELFT>().size() - this->FirstGlobal; - if (!VersymSec) - return std::vector<uint32_t>(Size, VER_NDX_GLOBAL); - - const char *Base = this->MB.getBuffer().data(); - const Elf_Versym *Versym = - reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) + - this->FirstGlobal; - - std::vector<uint32_t> Ret(Size); - for (size_t I = 0; I < Size; ++I) - Ret[I] = Versym[I].vs_index; - return Ret; -} + // DSOs are uniquified not by filename but by soname. + DenseMap<StringRef, SharedFile *>::iterator It; + bool WasInserted; + std::tie(It, WasInserted) = Symtab->SoNames.try_emplace(SoName, this); + + // If a DSO appears more than once on the command line with and without + // --as-needed, --no-as-needed takes precedence over --as-needed because a + // user can add an extra DSO with --no-as-needed to force it to be added to + // the dependency list. + It->second->IsNeeded |= IsNeeded; + if (!WasInserted) + return; -// Parse the version definitions in the object file if present. Returns a vector -// whose nth element contains a pointer to the Elf_Verdef for version identifier -// n. Version identifiers that are not definitions map to nullptr. -template <class ELFT> -std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() { - if (!VerdefSec) - return {}; + SharedFiles.push_back(this); - // We cannot determine the largest verdef identifier without inspecting - // every Elf_Verdef, but both bfd and gold assign verdef identifiers - // sequentially starting from 1, so we predict that the largest identifier - // will be VerdefCount. - unsigned VerdefCount = VerdefSec->sh_info; - std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1); + Verdefs = parseVerdefs<ELFT>(Obj.base(), VerdefSec); - // Build the Verdefs array by following the chain of Elf_Verdef objects - // from the start of the .gnu.version_d section. - const char *Base = this->MB.getBuffer().data(); - const char *Verdef = Base + VerdefSec->sh_offset; - for (unsigned I = 0; I != VerdefCount; ++I) { - auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef); - Verdef += CurVerdef->vd_next; - unsigned VerdefIndex = CurVerdef->vd_ndx; - Verdefs.resize(VerdefIndex + 1); - Verdefs[VerdefIndex] = CurVerdef; + // Parse ".gnu.version" section which is a parallel array for the symbol + // table. If a given file doesn't have a ".gnu.version" section, we use + // VER_NDX_GLOBAL. + size_t Size = this->getELFSyms<ELFT>().size() - this->FirstGlobal; + std::vector<uint32_t> Versyms(Size, VER_NDX_GLOBAL); + if (VersymSec) { + ArrayRef<Elf_Versym> Versym = + CHECK(Obj.template getSectionContentsAsArray<Elf_Versym>(VersymSec), + this) + .slice(FirstGlobal); + for (size_t I = 0; I < Size; ++I) + Versyms[I] = Versym[I].vs_index; } - return Verdefs; -} - -// We do not usually care about alignments of data in shared object -// files because the loader takes care of it. However, if we promote a -// DSO symbol to point to .bss due to copy relocation, we need to keep -// the original alignment requirements. We infer it in this function. -template <class ELFT> -uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections, - const Elf_Sym &Sym) { - uint64_t Ret = UINT64_MAX; - if (Sym.st_value) - Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value); - if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) - Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign); - return (Ret > UINT32_MAX) ? 0 : Ret; -} - -// Fully parse the shared object file. This must be called after parseDynamic(). -// -// This function parses symbol versions. If a DSO has version information, -// the file has a ".gnu.version_d" section which contains symbol version -// definitions. Each symbol is associated to one version through a table in -// ".gnu.version" section. That table is a parallel array for the symbol -// table, and each table entry contains an index in ".gnu.version_d". -// -// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for -// VER_NDX_GLOBAL. There's no table entry for these special versions in -// ".gnu.version_d". -// -// The file format for symbol versioning is perhaps a bit more complicated -// than necessary, but you can easily understand the code if you wrap your -// head around the data structure described above. -template <class ELFT> void SharedFile<ELFT>::parseRest() { - Verdefs = parseVerdefs(); // parse .gnu.version_d - std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version - ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj<ELFT>().sections(), this); - // System libraries can have a lot of symbols with versions. Using a // fixed buffer for computing the versions name (foo@ver) can save a // lot of allocations. @@ -1043,9 +1055,9 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() { Name == "_gp_disp") continue; - uint64_t Alignment = getAlignment(Sections, Sym); + uint64_t Alignment = getAlignment<ELFT>(Sections, Sym); if (!(Versyms[I] & VERSYM_HIDDEN)) - Symtab->addShared(Name, *this, Sym, Alignment, Idx); + Symtab->addShared<ELFT>(Name, *this, Sym, Alignment, Idx); // Also add the symbol with the versioned name to handle undefined symbols // with explicit versions. @@ -1060,10 +1072,11 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() { } StringRef VerName = - this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name; + this->StringTable.data() + + reinterpret_cast<const Elf_Verdef *>(Verdefs[Idx])->getAux()->vda_name; VersionedNameBuffer.clear(); Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer); - Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx); + Symtab->addShared<ELFT>(Saver.save(Name), *this, Sym, Alignment, Idx); } } @@ -1259,18 +1272,24 @@ InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, } InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) { + auto *F = make<SharedFile>(MB, DefaultSoName); switch (getELFKind(MB, "")) { case ELF32LEKind: - return make<SharedFile<ELF32LE>>(MB, DefaultSoName); + F->parseHeader<ELF32LE>(); + break; case ELF32BEKind: - return make<SharedFile<ELF32BE>>(MB, DefaultSoName); + F->parseHeader<ELF32BE>(); + break; case ELF64LEKind: - return make<SharedFile<ELF64LE>>(MB, DefaultSoName); + F->parseHeader<ELF64LE>(); + break; case ELF64BEKind: - return make<SharedFile<ELF64BE>>(MB, DefaultSoName); + F->parseHeader<ELF64BE>(); + break; default: llvm_unreachable("getELFKind"); } + return F; } MemoryBufferRef LazyObjFile::getBuffer() { @@ -1355,7 +1374,7 @@ template class elf::ObjFile<ELF32BE>; template class elf::ObjFile<ELF64LE>; template class elf::ObjFile<ELF64BE>; -template class elf::SharedFile<ELF32LE>; -template class elf::SharedFile<ELF32BE>; -template class elf::SharedFile<ELF64LE>; -template class elf::SharedFile<ELF64BE>; +template void SharedFile::parse<ELF32LE>(); +template void SharedFile::parse<ELF32BE>(); +template void SharedFile::parse<ELF64LE>(); +template void SharedFile::parse<ELF64BE>(); |

