Index: lld/trunk/ELF/InputFiles.h =================================================================== --- lld/trunk/ELF/InputFiles.h +++ lld/trunk/ELF/InputFiles.h @@ -24,6 +24,8 @@ #include "llvm/Object/IRObjectFile.h" #include "llvm/Support/StringSaver.h" +#include + namespace lld { namespace elf { @@ -245,10 +247,14 @@ typedef typename ELFT::Sym Elf_Sym; typedef typename ELFT::Word Elf_Word; typedef typename ELFT::SymRange Elf_Sym_Range; + typedef typename ELFT::Versym Elf_Versym; + typedef typename ELFT::Verdef Elf_Verdef; std::vector> SymbolBodies; std::vector Undefs; StringRef SoName; + const Elf_Shdr *VersymSec = nullptr; + const Elf_Shdr *VerdefSec = nullptr; public: StringRef getSoName() const { return SoName; } @@ -266,6 +272,19 @@ void parseSoName(); void parseRest(); + std::vector parseVerdefs(const Elf_Versym *&Versym); + + struct NeededVer { + // The string table offset of the version name in the output file. + size_t StrTab; + + // The version identifier for this version name. + uint16_t Index; + }; + + // Mapping from Elf_Verdef data structures to information about Elf_Vernaux + // data structures in the output file. + std::map VerdefMap; // Used for --as-needed bool AsNeeded = false; Index: lld/trunk/ELF/InputFiles.cpp =================================================================== --- lld/trunk/ELF/InputFiles.cpp +++ lld/trunk/ELF/InputFiles.cpp @@ -407,6 +407,12 @@ case SHT_SYMTAB_SHNDX: this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec)); break; + case SHT_GNU_versym: + this->VersymSec = &Sec; + break; + case SHT_GNU_verdef: + this->VerdefSec = &Sec; + break; } } @@ -430,17 +436,70 @@ } } +// 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. The array +// always has at least length 1. +template +std::vector +SharedFile::parseVerdefs(const Elf_Versym *&Versym) { + std::vector Verdefs(1); + // We only need to process symbol versions for this DSO if it has both a + // versym and a verdef section, which indicates that the DSO contains symbol + // version definitions. + if (!VersymSec || !VerdefSec) + return Verdefs; + + // The location of the first global versym entry. + Versym = reinterpret_cast(this->ELFObj.base() + + VersymSec->sh_offset) + + this->Symtab->sh_info; + + // 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; + Verdefs.resize(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 = this->ELFObj.base() + VerdefSec->sh_offset; + for (unsigned I = 0; I != VerdefCount; ++I) { + auto *CurVerdef = reinterpret_cast(Verdef); + Verdef += CurVerdef->vd_next; + unsigned VerdefIndex = CurVerdef->vd_ndx; + if (Verdefs.size() <= VerdefIndex) + Verdefs.resize(VerdefIndex + 1); + Verdefs[VerdefIndex] = CurVerdef; + } + + return Verdefs; +} + // Fully parse the shared object file. This must be called after parseSoName(). template void SharedFile::parseRest() { + // Create mapping from version identifiers to Elf_Verdef entries. + const Elf_Versym *Versym = nullptr; + std::vector Verdefs = parseVerdefs(Versym); + Elf_Sym_Range Syms = this->getElfSymbols(true); uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end()); SymbolBodies.reserve(NumSymbols); for (const Elf_Sym &Sym : Syms) { + unsigned VersymIndex = 0; + if (Versym) { + VersymIndex = Versym->vs_index; + ++Versym; + // Ignore local symbols and non-default versions. + if (VersymIndex == 0 || (VersymIndex & VERSYM_HIDDEN)) + continue; + } StringRef Name = check(Sym.getName(this->StringTable)); if (Sym.isUndefined()) Undefs.push_back(Name); else - SymbolBodies.emplace_back(this, Name, Sym); + SymbolBodies.emplace_back(this, Name, Sym, Verdefs[VersymIndex]); } } Index: lld/trunk/ELF/OutputSections.h =================================================================== --- lld/trunk/ELF/OutputSections.h +++ lld/trunk/ELF/OutputSections.h @@ -33,6 +33,8 @@ template class MipsReginfoInputSection; template class OutputSection; template class ObjectFile; +template class SharedFile; +template class SharedSymbol; template class DefinedRegular; template @@ -222,6 +224,49 @@ std::vector> Symbols; }; +// For more information about .gnu.version and .gnu.version_r see: +// https://www.akkadia.org/drepper/symbol-versioning + +// The .gnu.version section specifies the required version of each symbol in the +// dynamic symbol table. It contains one Elf_Versym for each dynamic symbol +// table entry. An Elf_Versym is just a 16-bit integer that refers to a version +// identifier defined in the .gnu.version_r section. +template +class VersionTableSection final : public OutputSectionBase { + typedef typename ELFT::Versym Elf_Versym; + +public: + VersionTableSection(); + void finalize() override; + void writeTo(uint8_t *Buf) override; +}; + +// The .gnu.version_r section defines the version identifiers used by +// .gnu.version. It contains a linked list of Elf_Verneed data structures. Each +// Elf_Verneed specifies the version requirements for a single DSO, and contains +// a reference to a linked list of Elf_Vernaux data structures which define the +// mapping from version identifiers to version names. +template +class VersionNeedSection final : public OutputSectionBase { + typedef typename ELFT::Verneed Elf_Verneed; + typedef typename ELFT::Vernaux Elf_Vernaux; + + // A vector of shared files that need Elf_Verneed data structures and the + // string table offsets of their sonames. + std::vector *, size_t>> Needed; + + // The next available version identifier. Identifiers start at 2 because 0 and + // 1 are reserved. + unsigned NextIndex = 2; + +public: + VersionNeedSection(); + void addSymbol(SharedSymbol *SS); + void finalize() override; + void writeTo(uint8_t *Buf) override; + size_t getNeedNum() const { return Needed.size(); } +}; + template class RelocationSection final : public OutputSectionBase { typedef typename ELFT::Rel Elf_Rel; @@ -562,6 +607,8 @@ static StringTableSection *StrTab; static SymbolTableSection *DynSymTab; static SymbolTableSection *SymTab; + static VersionTableSection *VerSym; + static VersionNeedSection *VerNeed; static Elf_Phdr *TlsPhdr; static OutputSectionBase *ElfHeader; static OutputSectionBase *ProgramHeaders; @@ -587,6 +634,8 @@ template StringTableSection *Out::StrTab; template SymbolTableSection *Out::DynSymTab; template SymbolTableSection *Out::SymTab; +template VersionTableSection *Out::VerSym; +template VersionNeedSection *Out::VerNeed; template typename ELFT::Phdr *Out::TlsPhdr; template OutputSectionBase *Out::ElfHeader; template OutputSectionBase *Out::ProgramHeaders; Index: lld/trunk/ELF/OutputSections.cpp =================================================================== --- lld/trunk/ELF/OutputSections.cpp +++ lld/trunk/ELF/OutputSections.cpp @@ -654,6 +654,12 @@ if (!Config->Entry.empty()) Add({DT_DEBUG, (uint64_t)0}); + if (size_t NeedNum = Out::VerNeed->getNeedNum()) { + Add({DT_VERSYM, Out::VerSym}); + Add({DT_VERNEED, Out::VerNeed}); + Add({DT_VERNEEDNUM, NeedNum}); + } + if (Config->EMachine == EM_MIPS) { Add({DT_MIPS_RLD_VERSION, 1}); Add({DT_MIPS_FLAGS, RHF_NOTPOT}); @@ -1514,6 +1520,102 @@ } template +VersionTableSection::VersionTableSection() + : OutputSectionBase(".gnu.version", SHT_GNU_versym, SHF_ALLOC) {} + +template void VersionTableSection::finalize() { + this->Header.sh_size = + sizeof(Elf_Versym) * (Out::DynSymTab->getSymbols().size() + 1); + this->Header.sh_entsize = sizeof(Elf_Versym); +} + +template void VersionTableSection::writeTo(uint8_t *Buf) { + auto *OutVersym = reinterpret_cast(Buf) + 1; + for (const std::pair &P : + Out::DynSymTab->getSymbols()) { + if (auto *SS = dyn_cast>(P.first)) + OutVersym->vs_index = SS->VersionId; + else + // The reserved identifier for a non-versioned global symbol. + OutVersym->vs_index = 1; + ++OutVersym; + } +} + +template +VersionNeedSection::VersionNeedSection() + : OutputSectionBase(".gnu.version_r", SHT_GNU_verneed, SHF_ALLOC) {} + +template +void VersionNeedSection::addSymbol(SharedSymbol *SS) { + if (!SS->Verdef) { + // The reserved identifier for a non-versioned global symbol. + SS->VersionId = 1; + 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->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) { @@ -1666,6 +1768,16 @@ 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 BuildIdSection; template class BuildIdSection; template class BuildIdSection; Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -324,6 +324,7 @@ template class SharedSymbol : public Defined { typedef typename ELFT::Sym Elf_Sym; + typedef typename ELFT::Verdef Elf_Verdef; typedef typename ELFT::uint uintX_t; public: @@ -331,10 +332,11 @@ return S->kind() == SymbolBody::SharedKind; } - SharedSymbol(SharedFile *F, StringRef Name, const Elf_Sym &Sym) + SharedSymbol(SharedFile *F, StringRef Name, const Elf_Sym &Sym, + const Elf_Verdef *Verdef) : Defined(SymbolBody::SharedKind, Name, Sym.getBinding(), Sym.st_other, Sym.getType()), - File(F), Sym(Sym) { + File(F), Sym(Sym), Verdef(Verdef) { // IFuncs defined in DSOs are treated as functions by the static linker. if (isGnuIFunc()) Type = llvm::ELF::STT_FUNC; @@ -343,6 +345,14 @@ SharedFile *File; const Elf_Sym &Sym; + // This field is initially a pointer to the symbol's version definition. As + // symbols are added to the version table, this field is replaced with the + // version identifier to be stored in .gnu.version in the output file. + union { + const Elf_Verdef *Verdef; + uint16_t VersionId; + }; + // OffsetInBss is significant only when needsCopy() is true. uintX_t OffsetInBss = 0; Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -132,6 +132,8 @@ StringTableSection DynStrTab(".dynstr", true); StringTableSection ShStrTab(".shstrtab", false); SymbolTableSection DynSymTab(*Symtab, DynStrTab); + VersionTableSection VerSym; + VersionNeedSection VerNeed; OutputSectionBase ElfHeader("", 0, SHF_ALLOC); ElfHeader.setSize(sizeof(Elf_Ehdr)); @@ -195,6 +197,8 @@ Out::ShStrTab = &ShStrTab; Out::StrTab = StrTab.get(); Out::SymTab = SymTabSec.get(); + Out::VerSym = &VerSym; + Out::VerNeed = &VerNeed; Out::Bss = nullptr; Out::MipsRldMap = MipsRldMap.get(); Out::Opd = nullptr; @@ -1367,8 +1371,11 @@ if (Out::SymTab) Out::SymTab->addSymbol(Body); - if (isOutputDynamic() && S->includeInDynsym()) + if (isOutputDynamic() && S->includeInDynsym()) { Out::DynSymTab->addSymbol(Body); + if (auto *SS = dyn_cast>(Body)) + Out::VerNeed->addSymbol(SS); + } } // Do not proceed if there was an undefined symbol. @@ -1436,6 +1443,10 @@ Add(Out::StrTab); if (isOutputDynamic()) { Add(Out::DynSymTab); + if (Out::VerNeed->getNeedNum() != 0) { + Add(Out::VerSym); + Add(Out::VerNeed); + } Add(Out::GnuHashTab); Add(Out::HashTab); Add(Out::Dynamic); Index: lld/trunk/test/ELF/Inputs/verneed.so.sh =================================================================== --- lld/trunk/test/ELF/Inputs/verneed.so.sh +++ lld/trunk/test/ELF/Inputs/verneed.so.sh @@ -0,0 +1,58 @@ +#!/bin/sh -eu + +# This script was used to produce the verneed{1,2}.so files. + +tmp=$(mktemp -d) + +echo "v1 {}; v2 {}; v3 {}; { local: *; };" > $tmp/verneed.script + +cat > $tmp/verneed1.s < $tmp/verneed2.s <&1 | FileCheck %s + +# CHECK: undefined symbol: f3 in +.globl _start +_start: +call f3 Index: lld/trunk/test/ELF/verneed.s =================================================================== --- lld/trunk/test/ELF/verneed.s +++ lld/trunk/test/ELF/verneed.s @@ -0,0 +1,104 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: ld.lld %t.o %S/Inputs/verneed1.so %S/Inputs/verneed2.so -o %t +# RUN: llvm-readobj -sections -dyn-symbols -dynamic-table %t | FileCheck %s +# RUN: llvm-objdump -s %t | FileCheck --check-prefix=CONTENTS %s + +# CHECK: Index: 2 +# CHECK-NEXT: Name: .gnu.version (9) +# CHECK-NEXT: Type: SHT_GNU_versym (0x6FFFFFFF) +# CHECK-NEXT: Flags [ (0x2) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x10228 +# CHECK-NEXT: Offset: 0x228 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 2 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 3 +# CHECK-NEXT: Name: .gnu.version_r (22) +# CHECK-NEXT: Type: SHT_GNU_verneed (0x6FFFFFFE) +# CHECK-NEXT: Flags [ (0x2) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x10230 +# CHECK-NEXT: Offset: 0x230 +# CHECK-NEXT: Size: 80 +# CHECK-NEXT: Link: 5 +# CHECK-NEXT: Info: 2 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: f1@v3 (1) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: f2@v2 (21) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: g1@v1 (27) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: 0x000000006FFFFFF0 VERSYM 0x10228 +# CHECK-NEXT: 0x000000006FFFFFFE VERNEED 0x10230 +# CHECK-NEXT: 0x000000006FFFFFFF VERNEEDNUM 2 + +# CONTENTS: Contents of section .gnu.version: +# CONTENTS-NEXT: 10228 00000200 03000400 +# CONTENTS-NEXT: Contents of section .gnu.version_r: +# vn_version +# vn_cnt +# vn_file vn_aux vn_next +# CONTENTS-NEXT: 10230 01000200 04000000 20000000 10000000 ........ ....... +# CONTENTS-NEXT: 10240 01000100 1e000000 30000000 00000000 ........0....... +# vna_hash vna_flags +# vna_other +# vna_name +# vna_next +# CONTENTS-NEXT: 10250 92070000 00000300 18000000 10000000 ................ +# CONTENTS-NEXT: 10260 93070000 00000200 12000000 00000000 ................ +# CONTENTS-NEXT: 10270 91070000 00000400 2c000000 00000000 ........,....... +# CONTENTS: Contents of section .dynstr: +# CONTENTS-NEXT: 102a8 00663100 7665726e 65656431 2e736f2e .f1.verneed1.so. +# CONTENTS-NEXT: 102b8 30007633 00663200 76320067 31007665 0.v3.f2.v2.g1.ve +# CONTENTS-NEXT: 102c8 726e6565 64322e73 6f2e3000 763100 rneed2.so.0.v1. + +.globl _start +_start: +call f1 +call f2 +call g1