diff --git a/include/eld/Diagnostics/DiagSymbolVersioning.inc b/include/eld/Diagnostics/DiagSymbolVersioning.inc index 1af67fdc5..8d36f0418 100644 --- a/include/eld/Diagnostics/DiagSymbolVersioning.inc +++ b/include/eld/Diagnostics/DiagSymbolVersioning.inc @@ -21,4 +21,6 @@ DIAG(trace_clear_export_due_to_local_scope, DiagnosticEngine::Trace, DIAG(error_missing_version_node, DiagnosticEngine::Error, "%0: symbol %1 has undefined version %2") DIAG(trace_assign_output_version_ids, DiagnosticEngine::Trace, - "Assigning version IDs to output symbols") \ No newline at end of file + "Assigning version IDs to output symbols") +DIAG(trace_adding_verneed_entry, DiagnosticEngine::Trace, + "Adding verneed entry: '%0' -> '%1'") diff --git a/include/eld/Fragment/Fragment.h b/include/eld/Fragment/Fragment.h index b9601bc01..2bd20326d 100644 --- a/include/eld/Fragment/Fragment.h +++ b/include/eld/Fragment/Fragment.h @@ -52,6 +52,7 @@ class Fragment { #ifdef ELD_ENABLE_SYMBOL_VERSIONING GNUVerDef, GNUVerSym, + GNUVerNeed #endif }; diff --git a/include/eld/Fragment/GNUVerNeedFragment.h b/include/eld/Fragment/GNUVerNeedFragment.h new file mode 100644 index 000000000..2911cb51f --- /dev/null +++ b/include/eld/Fragment/GNUVerNeedFragment.h @@ -0,0 +1,72 @@ +//===- GNUVerNeedFragment.h----------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===--------------------------------------------------------------------------===// +// +// The MCLinker Project +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------------===// + +#ifndef ELD_FRAGMENT_GNUVERNEEDFRAGMENT_H +#define ELD_FRAGMENT_GNUVERNEEDFRAGMENT_H + +#include "eld/Fragment/Fragment.h" +#include "llvm/ADT/StringRef.h" + +namespace eld { +class DiagnosticEngine; +class ELFSection; +class InputFile; +class ELFFileFormat; + +/** \class VerNeedFragment + * \brief VerNeedFragment is a kind of Fragment containing input memory region + */ +// Region fragment expression +class GNUVerNeedFragment : public Fragment { +public: + GNUVerNeedFragment(ELFSection *S); + + static bool classof(const Fragment *F) { + return F->getKind() == Fragment::Type::GNUVerNeed; + } + + template + eld::Expected + computeVersionNeeds(const std::vector &DynamicObjectFiles, + ELFFileFormat *FileFormat, DiagnosticEngine &DE); + + eld::Expected emit(MemoryRegion &Mr, Module &M) override; + + size_t size() const override; + + template + eld::Expected emitImpl(uint8_t *Buf, Module &M); + + size_t needCount() { return VersionNeeds.size(); } + +public: + struct VernAuxInfo { + uint32_t VersionNameOffset = 0; + uint32_t VersionID = 0; + uint32_t VersionNameHash = 0; + }; + + struct VerNeedInfo { + uint32_t SONameOffset = 0; + std::vector Vernauxs; + }; + +protected: + std::vector VersionNeeds; + size_t VerNeedEntrySize = 0; + size_t VernAuxEntrySize = 0; +}; + +} // namespace eld + +#endif diff --git a/include/eld/Input/ELFDynObjectFile.h b/include/eld/Input/ELFDynObjectFile.h index 62da60aa6..dff86aa8d 100644 --- a/include/eld/Input/ELFDynObjectFile.h +++ b/include/eld/Input/ELFDynObjectFile.h @@ -8,6 +8,12 @@ #include "eld/Input/ELFFileBase.h" #include "eld/Input/Input.h" +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +// FIXME: Are we using these headers? +#include "llvm/BinaryFormat/ELF.h" +#include +#include +#endif namespace eld { @@ -35,8 +41,112 @@ class ELFDynObjectFile : public ELFFileBase { std::string getFallbackSOName() const; +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + void setVerDefSection(ELFSection *S) { VerDefSection = S; } + + ELFSection *getVerDefSection() const { return VerDefSection; } + + void setVerNeedSection(ELFSection *S) { VerNeedSection = S; } + + ELFSection *getVerNeedSection() const { return VerNeedSection; } + + void setVerSymSection(ELFSection *S) { VerSymSection = S; } + + ELFSection *getVerSymSection() const { return VerSymSection; } + + void setVerDefs(std::vector VDefs) { VerDefs = VDefs; } + + const std::vector &getVerDefs() const { return VerDefs; } + + void setVerNeeds(std::vector VNeeds) { VerNeeds = VNeeds; } + + const std::vector &getVerNeeds() const { return VerNeeds; } + + void setVerSyms(std::vector VSyms) { VerSyms = VSyms; } + + const std::vector &getVerSyms() const { return VerSyms; } + + void setDynStrTabSection(ELFSection *S) { DynStrTabSection = S; } + + llvm::StringRef getDynStringTable() const; + + template + llvm::StringRef getSymbolVersionName(uint32_t SymIdx, + ResolveInfo::Desc SymDesc) const { + uint16_t SymVerID = getSymbolVersionID(SymIdx); + llvm::StringRef VerName; + if (SymVerID == llvm::ELF::VER_NDX_LOCAL || + SymVerID == llvm::ELF::VER_NDX_GLOBAL) + return VerName; + if (SymDesc == ResolveInfo::Desc::Undefined) + VerName = getDynStringTable().data() + VerNeeds[SymVerID]; + else { + auto VerNameOffset = + reinterpret_cast(VerDefs[SymVerID]) + ->getAux() + ->vda_name; + VerName = getDynStringTable().data() + VerNameOffset; + } + return VerName; + } + + uint16_t getSymbolVersionID(uint32_t SymIdx) const { + assert(SymIdx < VerSyms.size() && "Invalid SymIdx"); + return VerSyms[SymIdx] & ~llvm::ELF::VERSYM_HIDDEN; + } + + bool isDefaultVersionedSymbol(uint32_t SymIdx) const { + assert(SymIdx < VerSyms.size() && "Invalid SymIdx"); + return VerSyms[SymIdx] == getSymbolVersionID(SymIdx); + } + + uint16_t getOutputVernAuxID(uint16_t InputVerID) { + if (InputVerID >= OutputVernAuxIDMap.size()) + OutputVernAuxIDMap.resize(InputVerID + 1, 0); + return OutputVernAuxIDMap[InputVerID]; + } + + void setOutputVernAuxID(uint16_t InputVerID, uint16_t OutputVerID) { + if (InputVerID >= OutputVernAuxIDMap.size()) + OutputVernAuxIDMap.resize(InputVerID + 1, 0); + OutputVernAuxIDMap[InputVerID] = OutputVerID; + } + + const std::vector &getOutputVernAuxIDMap() const { + return OutputVernAuxIDMap; + } + + const void *getVerDef(uint16_t InputVerID) { + assert(InputVerID < VerDefs.size() && "Invalid InputVerID"); + return VerDefs[InputVerID]; + } + + bool hasSymbolVersioningInfo() const { return VerSymSection != nullptr; } + + void addNonCanonicalSymbol(LDSymbol *Sym) { + NonCanonicalSymbols.push_back(Sym); + } + + const std::vector &getNonCanonicalSymbols() const { + return NonCanonicalSymbols; + } +#endif + private: std::vector Sections; +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + ELFSection *VerDefSection = nullptr; + ELFSection *VerNeedSection = nullptr; + ELFSection *VerSymSection = nullptr; + ELFSection *DynStrTabSection = nullptr; + std::vector VerDefs; + std::vector VerNeeds; + std::vector VerSyms; + + std::vector OutputVernAuxIDMap; + + std::vector NonCanonicalSymbols; +#endif }; } // namespace eld diff --git a/include/eld/Input/ELFFileBase.h b/include/eld/Input/ELFFileBase.h index efb03732c..97931470d 100644 --- a/include/eld/Input/ELFFileBase.h +++ b/include/eld/Input/ELFFileBase.h @@ -46,6 +46,8 @@ class ELFFileBase : public ObjectFile { ELFSection *getStringTable() const { return StringTable; } + llvm::StringRef getStringTableData() const; + /// ------------ Extended Symbol Table --------------------------------------- void setExtendedSymbolTable(ELFSection *SymTab) { ExtendedSymbolTable = SymTab; diff --git a/include/eld/Readers/DynamicELFReader.h b/include/eld/Readers/DynamicELFReader.h index 7f7aa8c6e..f1bc199c4 100644 --- a/include/eld/Readers/DynamicELFReader.h +++ b/include/eld/Readers/DynamicELFReader.h @@ -53,9 +53,23 @@ template class DynamicELFReader : public ELFReader { eld::Expected createSection(typename ELFReader::Elf_Shdr rawSectHdr) override; +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + void + setSectionInInputFile(ELFSection *S, + typename ELFReader::Elf_Shdr rawSectHdr) override; + + eld::Expected readSections() override; +#endif + protected: explicit DynamicELFReader(Module &module, InputFile &inputFile, plugin::DiagnosticEntry &diagEntry); +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +private: + eld::Expected readVerDefSection(); + eld::Expected readVerSymSection(); + eld::Expected readVerNeedSection(); +#endif }; } // namespace eld #endif diff --git a/include/eld/Readers/ELFReader.h b/include/eld/Readers/ELFReader.h index e5ad2d488..44f50f88e 100644 --- a/include/eld/Readers/ELFReader.h +++ b/include/eld/Readers/ELFReader.h @@ -147,7 +147,7 @@ template class ELFReader : public ELFReaderBase { /// - SHT_SYMTAB /// - SHT_SYMTAB_SHNDX /// - SHT_STRTAB - void setSectionInInputFile(ELFSection *S, Elf_Shdr rawSectHdr); + virtual void setSectionInInputFile(ELFSection *S, Elf_Shdr rawSectHdr); /// Set link and info attributes to the sections. bool setLinkInfoAttributes(); diff --git a/include/eld/Readers/ELFReaderBase.h b/include/eld/Readers/ELFReaderBase.h index adb9f8ff0..1364a6b6c 100644 --- a/include/eld/Readers/ELFReaderBase.h +++ b/include/eld/Readers/ELFReaderBase.h @@ -109,6 +109,8 @@ class ELFReaderBase { virtual eld::Expected readRelocationSection(ELFSection *RS); + virtual eld::Expected readSections(); + /// Returns the symbol type. static ResolveInfo::Type getSymbolType(uint8_t info, uint32_t shndx); diff --git a/include/eld/SymbolResolver/IRBuilder.h b/include/eld/SymbolResolver/IRBuilder.h index f19d163b1..e916327b8 100644 --- a/include/eld/SymbolResolver/IRBuilder.h +++ b/include/eld/SymbolResolver/IRBuilder.h @@ -96,6 +96,10 @@ class IRBuilder { Relocation::Type Type, LDSymbol &PSym, uint32_t POffset, Relocation::Address CurAddend); +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + void normalizeSymbols(); +#endif + private: LDSymbol * addSymbolFromObject(InputFile &Input, const std::string &SymbolName, @@ -111,7 +115,8 @@ class IRBuilder { ResolveInfo::SizeType Size, LDSymbol::ValueType Value, ResolveInfo::Visibility Visibility, - uint32_t Shndx, bool IsPostLtoPhase); + uint32_t Shndx, bool IsPostLtoPhase, + uint32_t SymIdx); public: void addToCref(InputFile &Input, Resolver::Result PResult); @@ -126,6 +131,12 @@ class IRBuilder { LinkerConfig &ThisConfig; bool IsGarbageCollected; InputBuilder ThisInputBuilder; + +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + // 0th element -> Canonical version symbol, 1st element -> Non-canonical + // version symbol. + std::vector> VersionedSymbols; +#endif }; template <> diff --git a/include/eld/SymbolResolver/NamePool.h b/include/eld/SymbolResolver/NamePool.h index 4240940a0..0e7f2edb6 100644 --- a/include/eld/SymbolResolver/NamePool.h +++ b/include/eld/SymbolResolver/NamePool.h @@ -122,6 +122,12 @@ class NamePool { SharedLibsSymbols[Sym->resolveInfo()] = Sym; } +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + void addSharedLibSymbol(LDSymbol *Sym, ResolveInfo *RI) { + SharedLibsSymbols[RI] = Sym; + } +#endif + LDSymbol *getSharedLibSymbol(const ResolveInfo *RI) { auto Iter = SharedLibsSymbols.find(RI); if (Iter != SharedLibsSymbols.end()) diff --git a/include/eld/SymbolResolver/ResolveInfo.h b/include/eld/SymbolResolver/ResolveInfo.h index 3a79b66ee..8e581d1d5 100644 --- a/include/eld/SymbolResolver/ResolveInfo.h +++ b/include/eld/SymbolResolver/ResolveInfo.h @@ -254,6 +254,8 @@ class ResolveInfo { llvm::StringRef getName() const { return SymbolName; } + void setName(llvm::StringRef SymName) { SymbolName = SymName; } + unsigned int nameSize() const { return SymbolName.size(); } uint32_t info() const { return (ThisBitField & InfoMask); } diff --git a/include/eld/Target/GNULDBackend.h b/include/eld/Target/GNULDBackend.h index 5bc4fa39f..5425b27d4 100644 --- a/include/eld/Target/GNULDBackend.h +++ b/include/eld/Target/GNULDBackend.h @@ -55,6 +55,7 @@ class ELFObjectFileFormat; class ELFSegmentFactory; #ifdef ELD_ENABLE_SYMBOL_VERSIONING class GNUVerDefFragment; +class GNUVerNeedFragment; #endif class TargetInfo; class Layout; @@ -846,6 +847,8 @@ class GNULDBackend { ELFSection *getGNUVerSymSection() const { return GNUVerSymSection; } ELFSection *getGNUVerDefSection() const { return GNUVerDefSection; } GNUVerDefFragment *getGNUVerDefFragment() const { return GNUVerDefFrag; } + ELFSection *getGNUVerNeedSection() const { return GNUVerNeedSection; } + GNUVerNeedFragment *getGNUVerNeedFragment() const { return GNUVerNeedFrag; } void setShouldEmitVersioningSections(bool Should) { ShouldEmitVersioningSections = Should; } @@ -1182,6 +1185,8 @@ class GNULDBackend { ELFSection *GNUVerSymSection = nullptr; ELFSection *GNUVerDefSection = nullptr; GNUVerDefFragment *GNUVerDefFrag = nullptr; + ELFSection *GNUVerNeedSection = nullptr; + GNUVerNeedFragment *GNUVerNeedFrag = nullptr; std::unordered_map OutputVersionIDs; #endif }; diff --git a/lib/Core/Linker.cpp b/lib/Core/Linker.cpp index bbaf23828..7d68d166d 100644 --- a/lib/Core/Linker.cpp +++ b/lib/Core/Linker.cpp @@ -422,6 +422,9 @@ bool Linker::normalize() { if (!ObjLinker->normalize()) return false; } +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + IR->normalizeSymbols(); +#endif return true; } diff --git a/lib/Fragment/CMakeLists.txt b/lib/Fragment/CMakeLists.txt index d49e303ed..cac9ebd25 100644 --- a/lib/Fragment/CMakeLists.txt +++ b/lib/Fragment/CMakeLists.txt @@ -24,7 +24,8 @@ llvm_add_library( PARTIAL_SOURCES_INTENDED) if (ELD_ENABLE_SYMBOL_VERSIONING) - target_sources(ELDFragment PRIVATE GNUVerDefFragment.cpp GNUVerSymFragment.cpp) + target_sources(ELDFragment PRIVATE GNUVerDefFragment.cpp GNUVerNeedFragment.cpp + GNUVerSymFragment.cpp) endif() target_link_libraries(ELDFragment PRIVATE ELDSymbolResolver) diff --git a/lib/Fragment/GNUVerNeedFragment.cpp b/lib/Fragment/GNUVerNeedFragment.cpp new file mode 100644 index 000000000..0aa3aa7f1 --- /dev/null +++ b/lib/Fragment/GNUVerNeedFragment.cpp @@ -0,0 +1,118 @@ +//===- GNUVerNeedFragment.cpp---------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===---------------------------------------------------------------------------===// +// +// The MCLinker Project +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------------===// +#include "eld/Fragment/GNUVerNeedFragment.h" +#include "eld/Core/Module.h" +#include "eld/Diagnostics/DiagnosticEngine.h" +#include "eld/Input/ELFDynObjectFile.h" +#include "eld/Input/InputFile.h" +#include "eld/Target/ELFFileFormat.h" +#include "llvm/Object/ELF.h" +#include + +using namespace eld; + +GNUVerNeedFragment::GNUVerNeedFragment(ELFSection *S) + : Fragment(Fragment::Type::GNUVerNeed, S) {} + +template +eld::Expected GNUVerNeedFragment::computeVersionNeeds( + const std::vector &DynamicObjectFiles, + ELFFileFormat *FileFormat, DiagnosticEngine &DE) { + VerNeedEntrySize = sizeof(typename ELFT::Verneed); + VernAuxEntrySize = sizeof(typename ELFT::Vernaux); + bool traceSV = DE.getPrinter()->traceSymbolVersioning(); + for (auto *IF : DynamicObjectFiles) { + auto *DynObjFile = llvm::cast(IF); + const auto &VernAuxIDMap = DynObjFile->getOutputVernAuxIDMap(); + VerNeedInfo VNI; + VNI.SONameOffset = + FileFormat->addStringToDynStrTab(DynObjFile->getSOName()); + for (std::size_t i = 0; i < VernAuxIDMap.size(); ++i) { + if (VernAuxIDMap[i] == 0) + continue; + auto *verdef = reinterpret_cast( + DynObjFile->getVerDef(i)); + llvm::StringRef VerName = DynObjFile->getDynStringTable().data() + + static_cast(verdef->getAux()->vda_name); + VernAuxInfo AuxInfo = { + static_cast( + FileFormat->addStringToDynStrTab(VerName.str())), + VernAuxIDMap[i], verdef->vd_hash}; + if (traceSV) + DE.raise(Diag::trace_adding_verneed_entry) + << VernAuxIDMap[i] << VerName.str(); + VNI.Vernauxs.push_back(AuxInfo); + } + if (!VNI.Vernauxs.empty()) + VersionNeeds.push_back(VNI); + } + return {}; +} + +size_t GNUVerNeedFragment::size() const { + size_t VerNeedSize = VersionNeeds.size() * VerNeedEntrySize; + size_t VernAuxSize = 0; + for (const auto &VNI : VersionNeeds) { + VernAuxSize += VNI.Vernauxs.size() * VernAuxEntrySize; + } + return VerNeedSize + VernAuxSize; +} + +eld::Expected GNUVerNeedFragment::emit(MemoryRegion &Mr, Module &M) { + uint8_t *Buf = Mr.begin() + getOffset(M.getConfig().getDiagEngine()); + bool Is32Bits = M.getConfig().targets().is32Bits(); + if (Is32Bits) { + return emitImpl(Buf, M); + } else { + return emitImpl(Buf, M); + } + return {}; +} + +template +eld::Expected GNUVerNeedFragment::emitImpl(uint8_t *Buf, Module &M) { + auto *VerNeedBuf = reinterpret_cast(Buf); + auto *VernAuxBuf = reinterpret_cast( + VerNeedBuf + VersionNeeds.size()); + for (const auto &VN : VersionNeeds) { + VerNeedBuf->vn_version = 1; + VerNeedBuf->vn_cnt = VN.Vernauxs.size(); + VerNeedBuf->vn_file = VN.SONameOffset; + VerNeedBuf->vn_aux = reinterpret_cast(VernAuxBuf) - + reinterpret_cast(VerNeedBuf); + VerNeedBuf->vn_next = sizeof(typename ELFT::Verneed); + ++VerNeedBuf; + for (const auto &VA : VN.Vernauxs) { + llvm::errs() << "[GNUVerNeedFragment::emitImpl] Writing " << VA.VersionID + << " -> " << VA.VersionNameOffset << "\n"; + VernAuxBuf->vna_hash = VA.VersionNameHash; + VernAuxBuf->vna_flags = 0; + VernAuxBuf->vna_other = VA.VersionID; + VernAuxBuf->vna_name = VA.VersionNameOffset; + VernAuxBuf->vna_next = sizeof(typename ELFT::Vernaux); + ++VernAuxBuf; + } + VernAuxBuf[-1].vna_next = 0; + } + VerNeedBuf[-1].vn_next = 0; + return {}; +} + +template eld::Expected +GNUVerNeedFragment::computeVersionNeeds( + const std::vector &DynamicObjectFiles, + ELFFileFormat *FileFormat, DiagnosticEngine &DE); +template eld::Expected +GNUVerNeedFragment::computeVersionNeeds( + const std::vector &DynamicObjectFiles, + ELFFileFormat *FileFormat, DiagnosticEngine &DE); \ No newline at end of file diff --git a/lib/Input/ELFDynObjectFile.cpp b/lib/Input/ELFDynObjectFile.cpp index 3b4726051..9a9eda336 100644 --- a/lib/Input/ELFDynObjectFile.cpp +++ b/lib/Input/ELFDynObjectFile.cpp @@ -6,6 +6,9 @@ #include "eld/Input/ELFDynObjectFile.h" #include "eld/Support/Memory.h" +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +#include "eld/Readers/ELFSection.h" +#endif using namespace eld; @@ -29,4 +32,12 @@ std::string ELFDynObjectFile::getFallbackSOName() const { if (filename[0] == ':') return filename.substr(1); return I->getResolvedPath().filename().native(); -} \ No newline at end of file +} + +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +llvm::StringRef ELFDynObjectFile::getDynStringTable() const { + if (DynStrTabSection) + return DynStrTabSection->getContents(); + return llvm::StringRef(); +} +#endif \ No newline at end of file diff --git a/lib/Input/ELFFileBase.cpp b/lib/Input/ELFFileBase.cpp index 34cdc8895..8d17499f0 100644 --- a/lib/Input/ELFFileBase.cpp +++ b/lib/Input/ELFFileBase.cpp @@ -22,3 +22,9 @@ void ELFFileBase::addSection(ELFSection *S) { S->setIndex(MSectionTable.size()); ObjectFile::addSection(S); } + +llvm::StringRef ELFFileBase::getStringTableData() const { + if (StringTable) + return StringTable->getContents(); + return llvm::StringRef(); +} diff --git a/lib/Object/ObjectLinker.cpp b/lib/Object/ObjectLinker.cpp index 839858cf4..335d3ac73 100644 --- a/lib/Object/ObjectLinker.cpp +++ b/lib/Object/ObjectLinker.cpp @@ -3671,6 +3671,11 @@ bool ObjectLinker::readAndProcessInput(Input *Input, bool IsPostLto) { ThisConfig.raiseDiagEntry(std::move(ExpRead.error())); return false; } +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + ELFDynObjectFile *DynObjFile = llvm::cast(CurInput); + if (DynObjFile->hasSymbolVersioningInfo()) + getTargetBackend().setShouldEmitVersioningSections(true); +#endif ThisModule->getDynLibraryList().push_back(CurInput); } else if (CurInput->getKind() == InputFile::GNUArchiveFileKind) { eld::RegisterTimer T("Read Archive Files", "Read all Input files", diff --git a/lib/Readers/DynamicELFReader.cpp b/lib/Readers/DynamicELFReader.cpp index b94e4671a..ce82dff71 100644 --- a/lib/Readers/DynamicELFReader.cpp +++ b/lib/Readers/DynamicELFReader.cpp @@ -11,6 +11,9 @@ #include "eld/Readers/ELFSection.h" #include "eld/SymbolResolver/LDSymbol.h" #include "eld/SymbolResolver/ResolveInfo.h" +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +#include "llvm/BinaryFormat/ELF.h" +#endif namespace eld { template @@ -129,6 +132,8 @@ eld::Expected DynamicELFReader::readSectionHeaders() { return true; /// Create all sections, including the first null section. + // FIXME: We probably do not need to create section headers objects + // for sections in shared libraries. for (const typename ELFReader::Elf_Shdr &rawSectHdr : this->m_RawSectHdrs.value()) { eld::Expected expSection = this->createSection(rawSectHdr); @@ -174,6 +179,136 @@ eld::Expected DynamicELFReader::createSection( return section; } +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +template +void DynamicELFReader::setSectionInInputFile( + ELFSection *S, typename ELFReader::Elf_Shdr RawSectHdr) { + ELFReader::setSectionInInputFile(S, RawSectHdr); + ELFDynObjectFile *DynObjFile = + llvm::cast(&this->m_InputFile); + + switch (RawSectHdr.sh_type) { + case llvm::ELF::SHT_GNU_verdef: + DynObjFile->setVerDefSection(S); + break; + case llvm::ELF::SHT_GNU_verneed: + DynObjFile->setVerNeedSection(S); + break; + case llvm::ELF::SHT_GNU_versym: + DynObjFile->setVerSymSection(S); + break; + case llvm::ELF::SHT_STRTAB: + if (S->name() == ".dynstr") + DynObjFile->setDynStrTabSection(S); + } +} + +template +eld::Expected DynamicELFReader::readSections() { + ELFDynObjectFile *DynObjFile = + llvm::cast(&this->m_InputFile); + if (DynObjFile->getVerDefSection()) { + auto expReadVerDef = readVerDefSection(); + ELDEXP_RETURN_DIAGENTRY_IF_ERROR(expReadVerDef); + } + if (DynObjFile->getVerNeedSection()) { + auto expReadVerNeed = readVerNeedSection(); + ELDEXP_RETURN_DIAGENTRY_IF_ERROR(expReadVerNeed); + } + if (DynObjFile->getVerSymSection()) { + auto expReadVerSym = readVerSymSection(); + ELDEXP_RETURN_DIAGENTRY_IF_ERROR(expReadVerSym); + } + return {}; +} + +template +eld::Expected DynamicELFReader::readVerDefSection() { + ELFDynObjectFile *DynObjFile = + llvm::cast(&this->m_InputFile); + const ELFSection *S = DynObjFile->getVerDefSection(); + if (!S) + return {}; + ASSERT(S->getType() == llvm::ELF::SHT_GNU_verdef, + "S must be SHT_GNU_verdef section"); + std::vector Verdefs; + const uint8_t *Verdef = + reinterpret_cast(S->getContents().data()); + for (unsigned i = 0, e = S->getInfo(); i < e; ++i) { + auto *CurVerDef = reinterpret_cast(Verdef); + Verdef += CurVerDef->vd_next; + unsigned CurVerDefIndex = CurVerDef->vd_ndx; + if (CurVerDefIndex >= Verdefs.size()) + Verdefs.resize(CurVerDefIndex + 1); + Verdefs[CurVerDefIndex] = CurVerDef; + } + DynObjFile->setVerDefs(std::move(Verdefs)); + return {}; +} + +template +eld::Expected DynamicELFReader::readVerNeedSection() { + ELFDynObjectFile *DynObjFile = + llvm::cast(&this->m_InputFile); + const ELFSection *S = DynObjFile->getVerNeedSection(); + if (!S) + return {}; + ASSERT(S->getType() == llvm::ELF::SHT_GNU_verneed, + "S must be SHT_GNU_verneed section"); + std::vector VerNeeds; + const uint8_t *VerNeedBuf = + reinterpret_cast(S->getContents().data()); + for (unsigned i = 0, e = S->getInfo(); i < e; ++i) { + // FIXME: Perhaps add the below error check? + // if (verneedBuf + sizeof(typename ELFT::Verneed) > data.end()) { + // Err(ctx) << this << " has an invalid Verneed"; + // break; + // } + auto *CurVerNeed = + reinterpret_cast(VerNeedBuf); + const uint8_t *VernAuxBuf = VerNeedBuf + CurVerNeed->vn_aux; + for (unsigned j = 0; j != CurVerNeed->vn_cnt; ++j) { + auto *CurVernAux = + reinterpret_cast(VernAuxBuf); + uint16_t VersionIdentifier = + CurVernAux->vna_other & llvm::ELF::VERSYM_VERSION; + if (VersionIdentifier > VerNeeds.size()) + VerNeeds.resize(VersionIdentifier + 1); + VerNeeds[VersionIdentifier] = CurVernAux->vna_name; + VernAuxBuf += CurVernAux->vna_next; + } + VerNeedBuf += CurVerNeed->vn_next; + } + DynObjFile->setVerNeeds(std::move(VerNeeds)); + return {}; +} + +template +eld::Expected DynamicELFReader::readVerSymSection() { + ELFDynObjectFile *DynObjFile = + llvm::cast(&this->m_InputFile); + const ELFSection *S = DynObjFile->getVerSymSection(); + if (!S) + return {}; + ASSERT(S->getType() == llvm::ELF::SHT_GNU_versym, + "S must be SHT_GNU_versym section"); + std::vector VerSyms; + typename ELFReader::Elf_Shdr RawSectHdr = + this->m_RawSectHdrs.value()[S->getIndex()]; + llvm::Expected::Elf_Versym>> + ExpRawVerSym = this->m_LLVMELFFile->template getSectionContentsAsArray< + typename ELFReader::Elf_Versym>(RawSectHdr); + LLVMEXP_RETURN_DIAGENTRY_IF_ERROR(ExpRawVerSym); + llvm::ArrayRef::Elf_Versym> RawVerSym = + std::move(ExpRawVerSym.get()); + VerSyms.resize(RawVerSym.size()); + for (size_t i = 0; i < RawVerSym.size(); ++i) + VerSyms[i] = RawVerSym[i].vs_index; + DynObjFile->setVerSyms(std::move(VerSyms)); + return {}; +} +#endif + template class DynamicELFReader; template class DynamicELFReader; } // namespace eld diff --git a/lib/Readers/ELFDynObjParser.cpp b/lib/Readers/ELFDynObjParser.cpp index 764584ef0..61018f1aa 100644 --- a/lib/Readers/ELFDynObjParser.cpp +++ b/lib/Readers/ELFDynObjParser.cpp @@ -50,6 +50,12 @@ eld::Expected ELFDynObjParser::parseFile(InputFile &inputFile) { eld::Expected expReadHeaders = readSectionHeaders(*ELFReader); if (!expReadHeaders.has_value() || !expReadHeaders.value()) return expReadHeaders; + +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + eld::Expected expReadSections = ELFReader->readSections(); + ELDEXP_RETURN_DIAGENTRY_IF_ERROR(expReadSections); +#endif + eld::Expected expReadSymbols = readSymbols(*ELFReader); if (!expReadSymbols || !expReadSymbols.value()) return expReadSymbols; diff --git a/lib/Readers/ELFReaderBase.cpp b/lib/Readers/ELFReaderBase.cpp index f835de81c..e2053eff9 100644 --- a/lib/Readers/ELFReaderBase.cpp +++ b/lib/Readers/ELFReaderBase.cpp @@ -231,3 +231,8 @@ ELFReaderBase::inspectELFKind(const InputFile &I) { return (endian == llvm::ELF::ELFDATA2LSB) ? ObjectFile::ELFKind::ELF64LEKind : ObjectFile::ELFKind::ELF64BEKind; } + +// FIXME: Move ELFRelocObjParser::readSections to RelocELFReader::readSections +eld::Expected ELFReaderBase::readSections() { + ASSERT(0, "readSections must only be called for shared object files."); +} \ No newline at end of file diff --git a/lib/SymbolResolver/IRBuilder.cpp b/lib/SymbolResolver/IRBuilder.cpp index 088de4066..a41333a69 100644 --- a/lib/SymbolResolver/IRBuilder.cpp +++ b/lib/SymbolResolver/IRBuilder.cpp @@ -21,6 +21,7 @@ #include "eld/Input/ArchiveMemberInput.h" #include "eld/Input/ELFDynObjectFile.h" #include "eld/Input/ELFObjectFile.h" +#include "eld/Input/ObjectFile.h" #include "eld/Object/ObjectBuilder.h" #include "eld/Support/Memory.h" #include "eld/Support/MemoryArea.h" @@ -28,11 +29,14 @@ #include "eld/Support/RegisterTimer.h" #include "eld/SymbolResolver/LDSymbol.h" #include "eld/SymbolResolver/NamePool.h" +#include "eld/SymbolResolver/ResolveInfo.h" #include "eld/SymbolResolver/SymbolInfo.h" #include "eld/SymbolResolver/SymbolResolutionInfo.h" #include "eld/Target/Relocator.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/StringSaver.h" #include using namespace eld; @@ -212,7 +216,7 @@ LDSymbol *IRBuilder::addSymbol(InputFile &Input, const std::string &SymbolName, LDSymbol *InputSym = addSymbolFromDynObj(Input, Name, Type, Desc, Binding, Size, Value, - Vis, Shndx, IsPostLtoPhase); + Vis, Shndx, IsPostLtoPhase, Idx); if (InputSym) llvm::cast(&Input)->addSymbol(InputSym); return InputSym; @@ -308,7 +312,8 @@ LDSymbol *IRBuilder::addSymbolFromDynObj( InputFile &Input, const std::string &SymbolName, ResolveInfo::Type Type, ResolveInfo::Desc Desc, ResolveInfo::Binding Binding, ResolveInfo::SizeType Size, LDSymbol::ValueType Value, - ResolveInfo::Visibility Visibility, uint32_t Shndx, bool IsPostLtoPhase) { + ResolveInfo::Visibility Visibility, uint32_t Shndx, bool IsPostLtoPhase, + uint32_t SymIdx) { // We don't need sections of dynamic objects. So we ignore section symbols. if (Type == ResolveInfo::Section) return nullptr; @@ -321,62 +326,106 @@ LDSymbol *IRBuilder::addSymbolFromDynObj( eld::RegisterTimer T("Create && Resolve dynamic symbols", "Symbol Resolution", ThisConfig.options().printTimingStats()); - // create an input LDSymbol. - LDSymbol *InputSym = makeLDSymbol(nullptr); - InputSym->setFragmentRef(FragmentRef::null()); - InputSym->setSectionIndex(Shndx); - - SymbolInfo SymInfo(&Input, Size, Binding, Type, Visibility, Desc, - /*isBitcode=*/false); - - if (ThisModule.getLayoutInfo() && - ThisModule.getLayoutInfo()->showSymbolResolution()) - ThisModule.getNamePool().getSRI().recordSymbolInfo(InputSym, SymInfo); - // insert symbol and resolve it immediately - Resolver::Result ResolvedResult = {nullptr, false, false}; NamePool &NP = ThisModule.getNamePool(); ResolveInfo InputSymbolResolveInfo = NP.createInputSymbolRI( SymbolName, Input, /*isDyn=*/true, Type, Desc, Binding, Size, Visibility, Value, /*isPatchable=*/false); - auto &PM = ThisModule.getPluginManager(); - DiagnosticPrinter *DP = ThisConfig.getPrinter(); - auto OldErrorCount = DP->getNumErrors() + DP->getNumFatalErrors(); - PM.callVisitSymbolHook(InputSym, InputSymbolResolveInfo.getName(), SymInfo); - auto NewErrorCount = DP->getNumErrors() + DP->getNumFatalErrors(); - if (NewErrorCount != OldErrorCount) - return nullptr; - // Resolve symbol - bool S = NP.insertNonLocalSymbol(InputSymbolResolveInfo, *InputSym, - IsPostLtoPhase, ResolvedResult); - if (!S) - return nullptr; - - // the return ResolveInfo should not nullptr - assert(nullptr != ResolvedResult.Info); - if (ThisConfig.options().cref() || ThisConfig.options().buildCRef()) - addToCref(Input, ResolvedResult); - - InputSym->setResolveInfo(*(ResolvedResult.Info)); - - if (ResolvedResult.Overriden || !ResolvedResult.Existent) { - ResolvedResult.Info->setValue(Value, false); - Input.setNeeded(); - NP.addSharedLibSymbol(InputSym); +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + ELFDynObjectFile *DynObjFile = llvm::cast(&Input); + bool IsDefaultVersionedSymbol = false; + std::optional OptVersionedName; + if (DynObjFile->hasSymbolVersioningInfo()) { + llvm::StringRef VerName = + (ThisConfig.targets().is32Bits() + ? DynObjFile->getSymbolVersionName(SymIdx, + Desc) + : DynObjFile->getSymbolVersionName(SymIdx, + Desc)); + // Version name is empty for the VER_NDX_GLOBAL and VER_NDX_LOCAL versions. + if (!VerName.empty()) + OptVersionedName = SymbolName + "@" + VerName.str(); + + IsDefaultVersionedSymbol = DynObjFile->isDefaultVersionedSymbol(SymIdx); } - if (ResolvedResult.Overriden && ResolvedResult.Existent) { - ResolvedResult.Info->setOutSymbol(InputSym); +#endif + SymbolInfo SymInfo(&Input, Size, Binding, Type, Visibility, Desc, + /*isBitcode=*/false); + auto AddSymbol = [&Input, IsPostLtoPhase, &NP, Shndx, SymIdx, SymInfo, this, + Value](ResolveInfo RI) -> LDSymbol * { + // insert symbol and resolve it immediately + // create an input LDSymbol. + LDSymbol *InputSym = makeLDSymbol(nullptr); + InputSym->setFragmentRef(FragmentRef::null()); + InputSym->setSectionIndex(Shndx); + InputSym->setSymbolIndex(SymIdx); + + if (ThisModule.getLayoutInfo() && + ThisModule.getLayoutInfo()->showSymbolResolution()) + ThisModule.getNamePool().getSRI().recordSymbolInfo(InputSym, SymInfo); + + Resolver::Result ResolvedResult = {nullptr, false, false}; + auto &PM = ThisModule.getPluginManager(); + DiagnosticPrinter *DP = ThisConfig.getPrinter(); + auto OldErrorCount = DP->getNumErrors() + DP->getNumFatalErrors(); + PM.callVisitSymbolHook(InputSym, RI.getName(), SymInfo); + auto NewErrorCount = DP->getNumErrors() + DP->getNumFatalErrors(); + if (NewErrorCount != OldErrorCount) + return nullptr; + bool S = + NP.insertNonLocalSymbol(RI, *InputSym, IsPostLtoPhase, ResolvedResult); + // Resolve symbol + if (!S) + return nullptr; + // the return ResolveInfo should not nullptr + assert(nullptr != ResolvedResult.Info); + if (ThisConfig.options().cref() || ThisConfig.options().buildCRef()) + addToCref(Input, ResolvedResult); + + InputSym->setResolveInfo(*(ResolvedResult.Info)); + if (ResolvedResult.Overriden || !ResolvedResult.Existent) { + ResolvedResult.Info->setValue(Value, false); + Input.setNeeded(); + NP.addSharedLibSymbol(InputSym); + } + if (ResolvedResult.Overriden && ResolvedResult.Existent) { + ResolvedResult.Info->setOutSymbol(InputSym); + } + // If the symbol is from dynamic library and we are not making a dynamic + // library, we either need to export the symbol by dynamic list or sometimes + // we export it since the dynamic library may be referring it defined in + // executable, either case it must be in .dynsym + ResolveInfo *InputSymRI = InputSym->resolveInfo(); + LDSymbol *OutSym = InputSymRI->outSymbol(); + if (ThisConfig.codeGenType() != LinkerConfig::DynObj && + ((OutSym && OutSym->hasFragRef()) || InputSymRI->isCommon())) + InputSymRI->setExportToDyn(); + return InputSym; + }; + +#ifdef ELD_ENABLE_SYMBOL_VERSIONING + // foo + LDSymbol *SymbolWithoutVerName = nullptr; + // foo@VerName + LDSymbol *CanonicalSymbol = nullptr; + if (IsDefaultVersionedSymbol) { + SymbolWithoutVerName = AddSymbol(InputSymbolResolveInfo); + if (!SymbolWithoutVerName) + return nullptr; + DynObjFile->addNonCanonicalSymbol(SymbolWithoutVerName); } - // If the symbol is from dynamic library and we are not making a dynamic - // library, we either need to export the symbol by dynamic list or sometimes - // we export it since the dynamic library may be referring it defined in - // executable, either case it must be in .dynsym - LDSymbol *OutSym = InputSym->resolveInfo()->outSymbol(); - ResolveInfo *RI = InputSym->resolveInfo(); - if (ThisConfig.codeGenType() != LinkerConfig::DynObj && - ((OutSym && OutSym->hasFragRef()) || RI->isCommon())) - RI->setExportToDyn(); - return InputSym; + if (OptVersionedName) + InputSymbolResolveInfo.setName(Saver.save(OptVersionedName.value())); + CanonicalSymbol = AddSymbol(InputSymbolResolveInfo); + if (!CanonicalSymbol) + return nullptr; + if (SymbolWithoutVerName && CanonicalSymbol) + VersionedSymbols.push_back({CanonicalSymbol, SymbolWithoutVerName}); + return CanonicalSymbol; +#else + LDSymbol *Sym = AddSymbol(InputSymbolResolveInfo); + return Sym; +#endif } void IRBuilder::addToCref(InputFile &Input, Resolver::Result PResult) { @@ -649,3 +698,61 @@ LDSymbol *IRBuilder::addSymbol( Value, CurFragmentRef, Visibility, IsPostLtoPhase, IsBitCode, IsPatchable); } + +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +void IRBuilder::normalizeSymbols() { + std::unordered_map RIReplacementMap; + for (const auto &P : VersionedSymbols) { + // P.first is the canonical version symbol, P.second is the non-canonical + // version symbol. + LDSymbol *CanonicalSym = P.first; + LDSymbol *NonCanonicalSym = P.second; + assert(CanonicalSym && NonCanonicalSym && "must not be null!"); + + if (CanonicalSym->resolveInfo()->outSymbol() == CanonicalSym && + NonCanonicalSym->resolveInfo()->outSymbol() == NonCanonicalSym) { + ResolveInfo *CanonicalRI = P.first->resolveInfo(); + ResolveInfo *NonCanonicalRI = P.second->resolveInfo(); + if (NonCanonicalRI->exportToDyn()) + CanonicalRI->setExportToDyn(); + RIReplacementMap[NonCanonicalRI] = CanonicalRI; + } + } + + const auto &ObjectFiles = ThisModule.getObjectList(); + const auto &DynObjectFiles = ThisModule.getDynLibraryList(); + + std::vector AllInputs; + AllInputs.insert(AllInputs.end(), ObjectFiles.begin(), ObjectFiles.end()); + AllInputs.insert(AllInputs.end(), DynObjectFiles.begin(), + DynObjectFiles.end()); + + for (InputFile *Input : AllInputs) { + ObjectFile *ObjFile = llvm::dyn_cast(Input); + if (ObjFile) { + for (LDSymbol *Sym : ObjFile->getSymbols()) { + ResolveInfo *RI = Sym->resolveInfo(); + auto it = RIReplacementMap.find(RI); + + if (it != RIReplacementMap.end()) { + Sym->setResolveInfo(*(it->second)); + } + } + } + + ELFDynObjectFile *DynObjFile = llvm::dyn_cast(Input); + if (!DynObjFile) + continue; + + const auto &NonCanonicalSymbols = DynObjFile->getNonCanonicalSymbols(); + for (LDSymbol *NonCanonicalSym : NonCanonicalSymbols) { + ResolveInfo *NonCanonicalRI = NonCanonicalSym->resolveInfo(); + auto it = RIReplacementMap.find(NonCanonicalRI); + + if (it != RIReplacementMap.end()) { + NonCanonicalSym->setResolveInfo(*(it->second)); + } + } + } +} +#endif \ No newline at end of file diff --git a/lib/Target/ELFDynamic.cpp b/lib/Target/ELFDynamic.cpp index e258510cd..a5f2a1d89 100644 --- a/lib/Target/ELFDynamic.cpp +++ b/lib/Target/ELFDynamic.cpp @@ -21,6 +21,9 @@ #include "eld/Target/GNULDBackend.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/ErrorHandling.h" +#ifdef ELD_ENABLE_SYMBOL_VERSIONING +#include "eld/Fragment/GNUVerNeedFragment.h" +#endif using namespace eld; using namespace elf_dynamic; @@ -248,6 +251,13 @@ void ELFDynamic::reserveEntries(ELFFileFormat &pFormat, Module &pModule) { reserveOne(llvm::ELF::DT_VERDEFNUM); } } + + if (auto verNeed = m_Backend.getGNUVerNeedSection()) { + if (verNeed->size()) { + reserveOne(llvm::ELF::DT_VERNEED); + reserveOne(llvm::ELF::DT_VERNEEDNUM); + } + } #endif reserveOne(llvm::ELF::DT_DEBUG); // for Debugging @@ -429,6 +439,15 @@ void ELFDynamic::applyEntries(const ELFFileFormat &pFormat, applyOne(llvm::ELF::DT_VERDEFNUM, S->getInfo()); } } + + if (ELFSection *S = m_Backend.getGNUVerNeedSection()) { + if (S->size()) { + applyOne(llvm::ELF::DT_VERNEED, S->addr()); + GNUVerNeedFragment *F = m_Backend.getGNUVerNeedFragment(); + ASSERT(F, "Must not be null!"); + applyOne(llvm::ELF::DT_VERNEEDNUM, F->needCount()); + } + } #endif if (!m_Config.options().isCompactDyn()) diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp index 13358b2a9..e017a6a75 100644 --- a/lib/Target/GNULDBackend.cpp +++ b/lib/Target/GNULDBackend.cpp @@ -25,6 +25,7 @@ #include "eld/Fragment/GNUHashFragment.h" #ifdef ELD_ENABLE_SYMBOL_VERSIONING #include "eld/Fragment/GNUVerDefFragment.h" +#include "eld/Fragment/GNUVerNeedFragment.h" #include "eld/Fragment/GNUVerSymFragment.h" #endif #include "eld/Fragment/RegionFragmentEx.h" @@ -865,6 +866,23 @@ void GNULDBackend::sizeDynNamePools() { GNUVerDefFrag = F; GNUVerDefSection->setInfo(F->defCount()); } + + if (GNUVerNeedSection) { + if (DP->traceSymbolVersioning()) + config().raise(Diag::trace_creating_symbol_versioning_fragment) << + GNUVerNeedSection->name(); + GNUVerNeedFragment *F = make(GNUVerNeedSection); + bool is32Bits = config().targets().is32Bits(); + if (is32Bits) + F->computeVersionNeeds( + m_Module.getDynLibraryList(), getOutputFormat(), *DE); + else + F->computeVersionNeeds( + m_Module.getDynLibraryList(), getOutputFormat(), *DE); + GNUVerNeedSection->addFragmentAndUpdateSize(F); + GNUVerNeedFrag = F; + GNUVerNeedSection->setInfo(F->needCount()); + } #endif } @@ -928,6 +946,7 @@ void GNULDBackend::sizeDynamic() { if (addedLibs.count(dynObjFile->getInput()->getMemArea())) continue; addedLibs.insert(dynObjFile->getInput()->getMemArea()); + llvm::errs() << "Adding DT_NEEDED: " << dynObjFile->getSOName() << "\n"; std::size_t SONameOffset = FileFormat->addStringToDynStrTab(dynObjFile->getSOName()); auto DTEntry = dynamic()->reserveNeedEntry(); @@ -5276,6 +5295,16 @@ void GNULDBackend::initSymbolVersioningSections() { llvm::ELF::SHT_GNU_verdef, llvm::ELF::SHF_ALLOC, /*Align=*/sizeof(uint32_t)); GNUVerDefSection->setLink(getOutputFormat()->getDynStrTab()); + + if (DP->traceSymbolVersioning()) + config().raise(Diag::trace_creating_symbol_versioning_section) + << ".gnu.version_r"; + GNUVerNeedSection = m_Module.createInternalSection( + Module::InternalInputType::SymbolVersioning, + LDFileFormat::Kind::SymbolVersion, ".gnu.version_r", + llvm::ELF::SHT_GNU_verneed, llvm::ELF::SHF_ALLOC, + /*Align=*/sizeof(uint32_t)); + GNUVerNeedSection->setLink(getOutputFormat()->getDynStrTab()); } #endif @@ -5346,5 +5375,27 @@ void GNULDBackend::assignOutputVersionIDs() { VernID |= llvm::ELF::VERSYM_HIDDEN; setSymbolVersionID(R, VernID); } + + for (std::size_t i = 1, e = DynamicSymbols.size(); i < e; ++i) { + ResolveInfo *R = DynamicSymbols[i]; + ELFDynObjectFile *DynObjFile = + llvm::dyn_cast(R->resolvedOrigin()); + if (!DynObjFile) + continue; + LDSymbol *sym = R->outSymbol(); + ASSERT(sym, "Must not be null!"); + uint16_t InputVerID = DynObjFile->getSymbolVersionID(sym->getSymbolIndex()); + if (R->isUndef()) + continue; + if (InputVerID == llvm::ELF::VER_NDX_LOCAL || + InputVerID == llvm::ELF::VER_NDX_GLOBAL) + continue; + auto VernAuxID = DynObjFile->getOutputVernAuxID(InputVerID); + if (VernAuxID == 0) { + VernAuxID = NextVerID++; + DynObjFile->setOutputVernAuxID(InputVerID, VernAuxID); + } + setSymbolVersionID(R, VernAuxID); + } } #endif diff --git a/lib/Writers/ELFObjectWriter.cpp b/lib/Writers/ELFObjectWriter.cpp index e82fe35f6..88a928c05 100644 --- a/lib/Writers/ELFObjectWriter.cpp +++ b/lib/Writers/ELFObjectWriter.cpp @@ -18,6 +18,7 @@ #include "eld/Fragment/FillFragment.h" #ifdef ELD_ENABLE_SYMBOL_VERSIONING #include "eld/Fragment/GNUVerDefFragment.h" +#include "eld/Fragment/GNUVerNeedFragment.h" #endif #include "eld/Fragment/RegionFragment.h" #include "eld/Fragment/StringFragment.h" @@ -703,6 +704,8 @@ uint64_t ELFObjectWriter::getSectEntrySize(ELFSection *CurSection) const { return CurSection->getEntSize(); if (CurSection->getType() == llvm::ELF::SHT_GNU_verdef) return CurSection->getEntSize(); + if (CurSection->getType() == llvm::ELF::SHT_GNU_verneed) + return CurSection->getEntSize(); #endif return 0x0; } @@ -728,6 +731,8 @@ uint64_t ELFObjectWriter::getSectLink(const ELFSection *S) const { Link = ThisModule.getBackend().getOutputFormat()->getDynSymTab(); if (llvm::ELF::SHT_GNU_verdef == S->getType()) Link = ThisModule.getBackend().getOutputFormat()->getDynStrTab(); + if (llvm::ELF::SHT_GNU_verneed == S->getType()) + Link = ThisModule.getBackend().getOutputFormat()->getDynStrTab(); #endif if (ThisModule.getConfig().isLinkPartial() && llvm::ELF::SHF_LINK_ORDER & S->getFlags()) @@ -759,6 +764,8 @@ uint64_t ELFObjectWriter::getSectInfo(ELFSection *CurSection) const { if (llvm::ELF::SHT_GNU_verdef == CurSection->getType()) { return ThisModule.getBackend().getGNUVerDefFragment()->defCount(); } + if (llvm::ELF::SHT_GNU_verneed == CurSection->getType()) + return ThisModule.getBackend().getGNUVerNeedFragment()->needCount(); #endif if (CurSection->isRelocationSection()) { diff --git a/test/x86_64/linux/SymbolVersioning/BuildingSharedLibsWithSymbolVersioning/BuildingSharedLibsWithSymbolVersioning.test b/test/x86_64/linux/SymbolVersioning/BuildingSharedLibsWithSymbolVersioning/BuildingSharedLibsWithSymbolVersioning.test index 7b676c88d..eed467909 100644 --- a/test/x86_64/linux/SymbolVersioning/BuildingSharedLibsWithSymbolVersioning/BuildingSharedLibsWithSymbolVersioning.test +++ b/test/x86_64/linux/SymbolVersioning/BuildingSharedLibsWithSymbolVersioning/BuildingSharedLibsWithSymbolVersioning.test @@ -9,21 +9,21 @@ REQUIRES: symbol_versioning RUN: %clang %clangopts -o %t1.1.o %p/Inputs/1.c -c -fPIC RUN: %link %linkopts -o %t1.lib1.so %t1.1.o -shared --version-script %p/Inputs/vs.1.t RUN: %clang %clangopts -o %t1.main.1.o %p/Inputs/main.1.c -c -RUN: %clang %clangopts -o %t1.main.1.out %t1.main.1.o %t1.lib1.so -Wl,-rpath=$(dirname %t1.lib1.so) +RUN: %clang %clangopts -fuse-ld=%link -o %t1.main.1.out %t1.main.1.o %t1.lib1.so -Wl,-rpath=$(dirname %t1.lib1.so) RUN: %t1.main.1.out | %filecheck %s --check-prefix=CHECK1 RUN: %clang %clangopts -o %t1.main.2.o %p/Inputs/main.2.c -c -RUN: %clang %clangopts -o %t1.main.2.out %t1.main.2.o %t1.lib1.so -Wl,-rpath=$(dirname %t1.lib1.so) +RUN: %clang %clangopts -fuse-ld=%link -o %t1.main.2.out %t1.main.2.o %t1.lib1.so -Wl,-rpath=$(dirname %t1.lib1.so) RUN: %t1.main.2.out | %filecheck %s --check-prefix=CHECK2 RUN: %clang %clangopts -o %t1.main.3.o %p/Inputs/main.3.c -c -RUN: %clang %clangopts -o %t1.main.3.out %t1.main.3.o %t1.lib1.so -Wl,-rpath=$(dirname %t1.lib1.so) +RUN: %clang %clangopts -fuse-ld=%link -o %t1.main.3.out %t1.main.3.o %t1.lib1.so -Wl,-rpath=$(dirname %t1.lib1.so) RUN: %t1.main.3.out | %filecheck %s --check-prefix=CHECK3 RUN: %clang %clangopts -o %t1.2.o %p/Inputs/2.c -c -fPIC RUN: %link %linkopts -o %t1.lib2.so %t1.1.o -shared --version-script %p/Inputs/vs.1.t -RUN: %clang %clangopts -o %t1.main.1.2.out %t1.main.1.o %t1.lib2.so -Wl,-rpath=$(dirname %t1.lib2.so) +RUN: %clang %clangopts -fuse-ld=%link -o %t1.main.1.2.out %t1.main.1.o %t1.lib2.so -Wl,-rpath=$(dirname %t1.lib2.so) RUN: %t1.main.1.2.out | %filecheck %s --check-prefix=CHECK1 -RUN: %clang %clangopts -o %t1.main.2.2.out %t1.main.2.o %t1.lib2.so -Wl,-rpath=$(dirname %t1.lib2.so) +RUN: %clang %clangopts -fuse-ld=%link -o %t1.main.2.2.out %t1.main.2.o %t1.lib2.so -Wl,-rpath=$(dirname %t1.lib2.so) RUN: %t1.main.2.2.out | %filecheck %s --check-prefix=CHECK2 -RUN: %clang %clangopts -o %t1.main.3.2.out %t1.main.3.o %t1.lib2.so -Wl,-rpath=$(dirname %t1.lib2.so) +RUN: %clang %clangopts -fuse-ld=%link -o %t1.main.3.2.out %t1.main.3.o %t1.lib2.so -Wl,-rpath=$(dirname %t1.lib2.so) RUN: %t1.main.3.2.out | %filecheck %s --check-prefix=CHECK3 #END_TEST