Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -53,7 +53,6 @@ std::vector DynamicList; std::vector SearchPaths; std::vector Undefined; - std::vector VersionScriptGlobals; std::vector BuildIdVector; bool AllowMultipleDefinition; bool AsNeeded = false; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -42,9 +42,11 @@ Configuration C; LinkerDriver D; ScriptConfiguration SC; + VersionScriptConfiguration VSC; Config = &C; Driver = &D; ScriptConfig = ≻ + VersionScriptConfig = &VSC; Driver->main(Args); return !HasError; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -11,6 +11,7 @@ #include "Driver.h" #include "Error.h" #include "InputSection.h" +#include "SymbolListFile.h" #include "SymbolTable.h" #include "Symbols.h" #include "llvm/ADT/STLExtras.h" @@ -334,6 +335,15 @@ return S->Repl; } +static StringRef filterVersion(StringRef Name) { + StringRef Version; + std::tie(Name, Version) = Name.split('@'); + // If name contains @@ that means default version. + if (Version.startswith("@")) + Version == Version.drop_front(1); + return Name; +} + template SymbolBody *elf::ObjectFile::createSymbolBody(const Elf_Sym *Sym) { unsigned char Binding = Sym->getBinding(); @@ -344,7 +354,7 @@ return new (Alloc) DefinedRegular(*Sym, Sec); } - StringRef Name = check(Sym->getName(this->StringTable)); + StringRef Name = filterVersion(check(Sym->getName(this->StringTable))); switch (Sym->st_shndx) { case SHN_UNDEF: Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -24,6 +24,7 @@ class SymbolBody; struct SectionPiece; +struct Version; template class SymbolTable; template class SymbolTableSection; template class StringTableSection; @@ -230,6 +231,37 @@ // For more information about .gnu.version and .gnu.version_r see: // https://www.akkadia.org/drepper/symbol-versioning +// The .gnu.version_d section which has a section type of SHT_GNU_verdef shall +// contain symbol version definitions. The number of entries in this section +// shall be contained in the DT_VERDEFNUM entry of the .dynamic section. +// The section shall contain an array of Elf_Verdef structures, optionally +// followed by an array of Elf_Verdaux structures. +template +class VersionDefinitionSection final : public OutputSectionBase { + typedef typename ELFT::Verdef Elf_Verdef; + typedef typename ELFT::Verdaux Elf_Verdaux; + + struct VerDefEntry { + // Version definition flags. + unsigned Flags; + // Name hash. + uint32_t Hash; + // Offset in string table. + size_t NameOff; + // Version definition index. + size_t Ndx; + }; + llvm::DenseMap VerDefs; + +public: + VersionDefinitionSection(); + void finalize() override; + void writeTo(uint8_t *Buf) override; + void createDefs(); + + unsigned getVerDefId(Version *Ver); +}; + // 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 @@ -242,6 +274,7 @@ VersionTableSection(); void finalize() override; void writeTo(uint8_t *Buf) override; + bool isRequired(); }; // The .gnu.version_r section defines the version identifiers used by @@ -258,9 +291,8 @@ // 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; + // The next available version identifier. + unsigned NextIndex; public: VersionNeedSection(); @@ -612,6 +644,7 @@ static StringTableSection *StrTab; static SymbolTableSection *DynSymTab; static SymbolTableSection *SymTab; + static VersionDefinitionSection *VerDef; static VersionTableSection *VerSym; static VersionNeedSection *VerNeed; static Elf_Phdr *TlsPhdr; @@ -640,6 +673,7 @@ template StringTableSection *Out::StrTab; template SymbolTableSection *Out::DynSymTab; template SymbolTableSection *Out::SymTab; +template VersionDefinitionSection *Out::VerDef; template VersionTableSection *Out::VerSym; template VersionNeedSection *Out::VerNeed; template typename ELFT::Phdr *Out::TlsPhdr; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "EhFrame.h" #include "LinkerScript.h" +#include "SymbolListFile.h" #include "SymbolTable.h" #include "Target.h" #include "lld/Core/Parallel.h" @@ -565,6 +566,16 @@ V.push_back({Sym.Body, Sym.STName}); } +static unsigned getVerDefNum() { + // At least one version dependency must exist. Additional version dependencies + // can be present, the number being indicated by the vn_cnt value of + // Elf_Verdef. We do not support version hierarchies currently, so each + // Elf_Verdef associated exactly with single Elf_Verdaux for us. + // The base version definition is always present when version definitions, or + // symbol auto-reduction, have been applied to the file. + return VersionScriptConfig->Versions.size() + 1; +} + template DynamicSection::DynamicSection() : OutputSectionBase(".dynamic", SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE) { @@ -667,8 +678,15 @@ if (!Config->Entry.empty()) Add({DT_DEBUG, (uint64_t)0}); - if (size_t NeedNum = Out::VerNeed->getNeedNum()) { + if (Out::VerSym->isRequired()) Add({DT_VERSYM, Out::VerSym}); + + if (VersionScriptConfig->HasNamedVersions) { + Add({DT_VERDEF, Out::VerDef}); + Add({DT_VERDEFNUM, getVerDefNum()}); + } + + if (size_t NeedNum = Out::VerNeed->getNeedNum()) { Add({DT_VERNEED, Out::VerNeed}); Add({DT_VERNEEDNUM, NeedNum}); } @@ -1409,6 +1427,65 @@ } template +VersionDefinitionSection::VersionDefinitionSection() + : OutputSectionBase(".gnu.version_d", SHT_GNU_verdef, SHF_ALLOC) {} + +template void VersionDefinitionSection::createDefs() { + size_t I = 1; + VerDefs[nullptr] = {VER_FLG_BASE, hashSysv(Config->OutputFile), + Out::DynStrTab->addString(Config->OutputFile), I++}; + for (Version &V : VersionScriptConfig->Versions) { + VerDefs[&V] = {0, hashSysv(V.Name), Out::DynStrTab->addString(V.Name), + I++}; + } // Without this and opening bracket it does not compile under VS2015. +} + +template void VersionDefinitionSection::finalize() { + this->Header.sh_size = + (sizeof(Elf_Verdef) + sizeof(Elf_Verdaux)) * getVerDefNum(); + this->Header.sh_link = Out::DynStrTab->SectionIndex; + + // sh_info should be set to amount 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::writeTo(uint8_t *Buf) { + Elf_Verdef *Verdef = reinterpret_cast(Buf); + Elf_Verdaux *Verdaux = + reinterpret_cast(Verdef + getVerDefNum()); + + for (std::pair &VD : VerDefs) { + VerDefEntry& Def = VD.second; + + Verdef->vd_version = 1; + Verdef->vd_flags = Def.Flags; + Verdef->vd_ndx = Def.Ndx; + Verdef->vd_cnt = 1; + Verdef->vd_hash = Def.Hash; + + Verdef->vd_aux = + reinterpret_cast(Verdaux) - reinterpret_cast(Verdef); + Verdef->vd_next = sizeof(Elf_Verdef); + ++Verdef; + + // Now write the single Elf_Verdaux entry. + Verdaux->vda_name = Def.NameOff; + Verdaux->vda_next = 0; + ++Verdaux; + } + + Verdef[-1].vd_next = 0; +} + +template +unsigned VersionDefinitionSection::getVerDefId(Version *Ver) { + return VerDefs.find(Ver)->second.Ndx; +} + +template VersionTableSection::VersionTableSection() : OutputSectionBase(".gnu.version", SHT_GNU_versym, SHF_ALLOC) { this->Header.sh_addralign = sizeof(uint16_t); @@ -1424,22 +1501,42 @@ } template void VersionTableSection::writeTo(uint8_t *Buf) { - auto *OutVersym = reinterpret_cast(Buf) + 1; + auto *OutVersym = reinterpret_cast(Buf); 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; + + SymbolBody *Body = P.first; + if (auto *SS = dyn_cast>(Body)) { + OutVersym->vs_index = SS->VersionId; + continue; + } + if (!VersionScriptConfig->HasNamedVersions || + !Body->symbol()->VersionScriptGlobal) { + OutVersym->vs_index = VER_NDX_GLOBAL; + continue; + } + + OutVersym->vs_index = Out::VerDef->getVerDefId(Body->VerDef); } } +template bool VersionTableSection::isRequired() { + return VersionScriptConfig->HasNamedVersions || + Out::VerNeed->getNeedNum() != 0; +} + 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 is exist. + NextIndex = 2; + if (VersionScriptConfig->HasNamedVersions) + NextIndex = std::max(getVerDefNum() + 1, NextIndex); } template @@ -1720,6 +1817,11 @@ 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; Index: ELF/SymbolListFile.h =================================================================== --- ELF/SymbolListFile.h +++ ELF/SymbolListFile.h @@ -11,6 +11,7 @@ #define LLD_ELF_SYMBOL_LIST_FILE_H #include "lld/Core/LLVM.h" +#include "llvm/ADT/MapVector.h" #include "llvm/Support/MemoryBuffer.h" namespace lld { @@ -19,6 +20,19 @@ void parseDynamicList(MemoryBufferRef MB); void parseVersionScript(MemoryBufferRef MB); +struct Version { + StringRef Name; + std::vector Globals; +}; + +// VersionScriptConfiguration holds version script parse results. +struct VersionScriptConfiguration { + std::vector Versions; + bool HasNamedVersions = false; +}; + +extern VersionScriptConfiguration *VersionScriptConfig; + } // namespace elf } // namespace lld Index: ELF/SymbolListFile.cpp =================================================================== --- ELF/SymbolListFile.cpp +++ ELF/SymbolListFile.cpp @@ -22,6 +22,8 @@ using namespace lld; using namespace lld::elf; +VersionScriptConfiguration *elf::VersionScriptConfig; + // Parse the --dynamic-list argument. A dynamic list is in the form // // { symbol1; symbol2; [...]; symbolN }; @@ -71,6 +73,8 @@ // is also not supported. class VersionScriptParser final : public ScriptParserBase { + VersionScriptConfiguration &Opt = *VersionScriptConfig; + public: VersionScriptParser(StringRef S) : ScriptParserBase(S) {} @@ -78,23 +82,44 @@ }; void VersionScriptParser::run() { - expect("{"); - if (peek() == "global:") { - next(); - while (!Error) { - Config->VersionScriptGlobals.push_back(next()); + bool HasAnonymousTag = false; + while (!atEOF() && !Error) { + Opt.Versions.push_back(Version()); + Version &V = Opt.Versions.back(); + + StringRef Peek = peek(); + if (Peek != "{") + V.Name = next(); + + HasAnonymousTag |= V.Name == ""; + // It can be only single anonymous version at once. + if (HasAnonymousTag && Opt.Versions.size() > 1) + setError("anonymous version tag cannot be " + "combined with other version tag"); + + expect("{"); + Peek = peek(); + if (Peek == "global:") { + next(); + while (!Error) { + V.Globals.push_back(next()); + expect(";"); + Peek = peek(); + if (Peek == "local:" || Peek == "}") + break; + } + } + + if (Peek == "local:") { + expect("local:"); + expect("*"); expect(";"); - if (peek() == "local:") - break; } + + expect("}"); + expect(";"); } - expect("local:"); - expect("*"); - expect(";"); - expect("}"); - expect(";"); - if (!atEOF()) - setError("expected EOF"); + Opt.HasNamedVersions = !HasAnonymousTag && !Opt.Versions.empty(); } void elf::parseVersionScript(MemoryBufferRef MB) { Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -17,6 +17,7 @@ #include "SymbolTable.h" #include "Config.h" #include "Error.h" +#include "SymbolListFile.h" #include "Symbols.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/Support/StringSaver.h" @@ -504,9 +505,12 @@ // symbols with the VersionScriptGlobal flag, which acts as a filter on the // dynamic symbol table. template void SymbolTable::scanVersionScript() { - for (StringRef S : Config->VersionScriptGlobals) - if (SymbolBody *B = find(S)) - B->symbol()->VersionScriptGlobal = true; + for (Version &V : VersionScriptConfig->Versions) + for (StringRef Name : V.Globals) + if (SymbolBody *B = find(Name)) { + B->symbol()->VersionScriptGlobal = true; + B->VerDef = &V; + } } template class elf::SymbolTable; Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -29,6 +29,7 @@ class BitcodeFile; class InputFile; class SymbolBody; +struct Version; template class ObjectFile; template class OutputSection; template class OutputSectionBase; @@ -137,6 +138,9 @@ // symbol's type in order to check for TLS mismatches. enum { UnknownType = 255 }; + // This is reference to version definition for symbol. + Version* VerDef = nullptr; + bool isSection() const { return Type == llvm::ELF::STT_SECTION; } bool isTls() const { return Type == llvm::ELF::STT_TLS; } bool isFunc() const { return Type == llvm::ELF::STT_FUNC; } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -12,6 +12,7 @@ #include "LinkerScript.h" #include "OutputSections.h" #include "Relocations.h" +#include "SymbolListFile.h" #include "SymbolTable.h" #include "Target.h" @@ -118,6 +119,7 @@ StringTableSection DynStrTab(".dynstr", true); StringTableSection ShStrTab(".shstrtab", false); SymbolTableSection DynSymTab(DynStrTab); + VersionDefinitionSection VerDef; VersionTableSection VerSym; VersionNeedSection VerNeed; @@ -189,6 +191,7 @@ Out::ShStrTab = &ShStrTab; Out::StrTab = StrTab.get(); Out::SymTab = SymTabSec.get(); + Out::VerDef = &VerDef; Out::VerSym = &VerSym; Out::VerNeed = &VerNeed; Out::MipsRldMap = MipsRldMap.get(); @@ -807,6 +810,9 @@ }); } + if (VersionScriptConfig->HasNamedVersions) + Out::VerDef->createDefs(); + // Now that we have defined all possible symbols including linker- // synthesized ones. Visit all symbols to give the finishing touches. std::vector CommonSymbols; @@ -908,10 +914,14 @@ Add(Out::StrTab); if (isOutputDynamic()) { Add(Out::DynSymTab); - if (Out::VerNeed->getNeedNum() != 0) { + + if (Out::VerSym->isRequired()) Add(Out::VerSym); + if (VersionScriptConfig->HasNamedVersions) + Add(Out::VerDef); + if (Out::VerNeed->getNeedNum() != 0) Add(Out::VerNeed); - } + Add(Out::GnuHashTab); Add(Out::HashTab); Add(Out::Dynamic); Index: test/ELF/Inputs/verdef.s =================================================================== --- test/ELF/Inputs/verdef.s +++ test/ELF/Inputs/verdef.s @@ -0,0 +1,6 @@ +.text +.globl _start +_start: + callq a + callq b + callq c Index: test/ELF/verdef.s =================================================================== --- test/ELF/verdef.s +++ test/ELF/verdef.s @@ -0,0 +1,129 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "LIBSAMPLE_1.0{ \ +# RUN: global: a; \ +# RUN: local: *; }; \ +# RUN: LIBSAMPLE_2.0{ \ +# RUN: global: b; \ +# RUN: local: *; }; \ +# RUN: LIBSAMPLE_3.0{ \ +# RUN: global: c; \ +# RUN: local: *; };" > %t.script +# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so +# RUN: llvm-readobj -V %t.so | FileCheck --check-prefix=DSO %s + +# DSO: Version symbols { +# DSO-NEXT: Section Name: .gnu.version +# DSO-NEXT: Address: 0x228 +# DSO-NEXT: Offset: 0x228 +# DSO-NEXT: Link: 1 +# DSO-NEXT: Symbols [ +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 0 +# DSO-NEXT: Name: @ +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 2 +# DSO-NEXT: Name: a@@LIBSAMPLE_1.0 +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 3 +# DSO-NEXT: Name: b@@LIBSAMPLE_2.0 +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 4 +# DSO-NEXT: Name: c@@LIBSAMPLE_3.0 +# DSO-NEXT: } +# DSO-NEXT: ] +# DSO-NEXT: } +# DSO-NEXT: Version definition { +# DSO-NEXT: Section Name: .gnu.version_d +# DSO-NEXT: Address: 0x230 +# DSO-NEXT: Offset: 0x230 +# DSO-NEXT: Link: 5 +# DSO-NEXT: Entries [ +# DSO-NEXT: Entry { +# DSO-NEXT: Offset: 0x0 +# DSO-NEXT: Rev: 1 +# DSO-NEXT: Flags: 1 +# DSO-NEXT: Index: 1 +# DSO-NEXT: Cnt: 1 +# DSO-NEXT: Hash: +# DSO-NEXT: Name: +# DSO-NEXT: } +# DSO-NEXT: Entry { +# DSO-NEXT: Offset: 0x14 +# DSO-NEXT: Rev: 1 +# DSO-NEXT: Flags: 0 +# DSO-NEXT: Index: 4 +# DSO-NEXT: Cnt: 1 +# DSO-NEXT: Hash: 98456672 +# DSO-NEXT: Name: LIBSAMPLE_3.0 +# DSO-NEXT: } +# DSO-NEXT: Entry { +# DSO-NEXT: Offset: 0x28 +# DSO-NEXT: Rev: 1 +# DSO-NEXT: Flags: 0 +# DSO-NEXT: Index: 3 +# DSO-NEXT: Cnt: 1 +# DSO-NEXT: Hash: 98456416 +# DSO-NEXT: Name: LIBSAMPLE_2.0 +# DSO-NEXT: } +# DSO-NEXT: Entry { +# DSO-NEXT: Offset: 0x3C +# DSO-NEXT: Rev: 1 +# DSO-NEXT: Flags: 0 +# DSO-NEXT: Index: 2 +# DSO-NEXT: Cnt: 1 +# DSO-NEXT: Hash: 98457184 +# DSO-NEXT: Name: LIBSAMPLE_1.0 +# DSO-NEXT: } +# DSO-NEXT: ] +# DSO-NEXT: } + +## Check that we can link agains DSO we produced. +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/verdef.s -o %tmain.o +# RUN: ld.lld %tmain.o %t.so -o %tout +# RUN: llvm-readobj -V %tout | FileCheck --check-prefix=MAIN %s + +# MAIN: Version symbols { +# MAIN-NEXT: Section Name: .gnu.version +# MAIN-NEXT: Address: 0x10228 +# MAIN-NEXT: Offset: 0x228 +# MAIN-NEXT: Link: 1 +# MAIN-NEXT: Symbols [ +# MAIN-NEXT: Symbol { +# MAIN-NEXT: Version: 0 +# MAIN-NEXT: Name: @ +# MAIN-NEXT: } +# MAIN-NEXT: Symbol { +# MAIN-NEXT: Version: 2 +# MAIN-NEXT: Name: a@LIBSAMPLE_1.0 +# MAIN-NEXT: } +# MAIN-NEXT: Symbol { +# MAIN-NEXT: Version: 3 +# MAIN-NEXT: Name: b@LIBSAMPLE_2.0 +# MAIN-NEXT: } +# MAIN-NEXT: Symbol { +# MAIN-NEXT: Version: 4 +# MAIN-NEXT: Name: c@LIBSAMPLE_3.0 +# MAIN-NEXT: } +# MAIN-NEXT: ] +# MAIN-NEXT: } +# MAIN-NEXT: Version definition { +# MAIN-NEXT: } + +.globl a +.type a,@function +a: +retq + +.globl b +.type b,@function +b: +retq + +.globl c +.type c,@function +c: +retq