summaryrefslogtreecommitdiffstats
path: root/lld/ELF/InputFiles.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/InputFiles.cpp')
-rw-r--r--lld/ELF/InputFiles.cpp223
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>();
OpenPOWER on IntegriCloud