[llvm] Add symbol version support to llvm-ifs (PR #163030)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 9 04:05:38 PST 2026
https://github.com/ur4t updated https://github.com/llvm/llvm-project/pull/163030
>From bd0295fd2f08445e7a5f861d6a8d901049880b2c Mon Sep 17 00:00:00 2001
From: ur4t <ur4t at protonmail.com>
Date: Sun, 12 Oct 2025 03:30:27 +0000
Subject: [PATCH 1/4] Add symbol version support to llvm-ifs
---
llvm/docs/CommandGuide/llvm-ifs.rst | 24 +-
llvm/include/llvm/InterfaceStub/IFSStub.h | 49 ++-
llvm/lib/InterfaceStub/ELFObjHandler.cpp | 468 ++++++++++++++++++++--
llvm/lib/InterfaceStub/IFSHandler.cpp | 55 +++
llvm/lib/InterfaceStub/IFSStub.cpp | 10 +
llvm/tools/llvm-ifs/llvm-ifs.cpp | 19 +-
6 files changed, 582 insertions(+), 43 deletions(-)
diff --git a/llvm/docs/CommandGuide/llvm-ifs.rst b/llvm/docs/CommandGuide/llvm-ifs.rst
index e3582b365b61d..6bd0d2be952a8 100644
--- a/llvm/docs/CommandGuide/llvm-ifs.rst
+++ b/llvm/docs/CommandGuide/llvm-ifs.rst
@@ -40,6 +40,12 @@ by the :program:`llvm-ifs`:
- { Name: sym2, Type: Func, Weak: false }
- { Name: sym3, Type: TLS }
- { Name: sym4, Type: Unknown, Warning: foo }
+ - { Name: sym5, Version: VER_1, Type: Func }
+ VersionDefinitions: /* Optional */
+ - { Name: libtest.so }
+ - { Name: VER_1 }
+ VersionRequirements: /* Optional */
+ - { File: libc.so.6, Names: [ GLIBC_2.14, GLIBC_2.4, GLIBC_2.2.5, GLIBC_2.3.4 ] }
...
* ``IFSVersion``: Version of the IFS file for reader compatibility.
@@ -67,6 +73,10 @@ by the :program:`llvm-ifs`:
+ ``Warning`` (optional): Warning text to output when this symbol is linked against.
+* ``VersionDefinitions`` (optional): A collection of symbol versions defined in this shared object file.
+
+* ``VersionRequirements`` (optional): A collection of symbol versions defined in external shared objects that this library depends on.
+
This YAML based text format contains everything that is needed to generate a
linkable ELF shared object as well as an Apple TAPI format file. The ordering
of symbols is sorted, so these files can be easily compared using diff tools.
@@ -87,6 +97,12 @@ A minimum ELF file that can be used by linker should have following sections pro
* Dynamic string table (``.dynstr`` section).
+* Version symbol table (``.gnu.version`` section). (optional)
+
+* Version definition table (``.gnu.version_d`` section). (optional)
+
+* Version requirement table (``.gnu.version_r`` section). (optional)
+
* Dynamic table (``.dynamic`` section).
+ ``DT_SYMTAB`` entry.
@@ -99,6 +115,12 @@ A minimum ELF file that can be used by linker should have following sections pro
+ ``DT_SONAME`` entry. (optional)
+ + ``DT_VERSYM`` entry. (optional)
+
+ + ``DT_VERDEF`` entry. (optional)
+
+ + ``DT_VERNEED`` entry. (optional)
+
* Section header string table (``.shstrtab`` section)
This ELF file may have compatibility issues with ELF analysis tools that rely on the program headers.
@@ -163,7 +185,7 @@ OPTIONS
triple in the output IFS file. If the value matches the target information
from the object file, this value will be used in the 'Target:' filed in the
generated IFS. If it conflicts with the input object file, an error will be
- reported and the program will stop.
+ reported and the program will stop.
.. option:: --hint-ifs-target
diff --git a/llvm/include/llvm/InterfaceStub/IFSStub.h b/llvm/include/llvm/InterfaceStub/IFSStub.h
index ddde889d46890..6ad79e32a9285 100644
--- a/llvm/include/llvm/InterfaceStub/IFSStub.h
+++ b/llvm/include/llvm/InterfaceStub/IFSStub.h
@@ -52,14 +52,38 @@ enum class IFSBitWidthType {
struct IFSSymbol {
IFSSymbol() = default;
- explicit IFSSymbol(std::string SymbolName) : Name(std::move(SymbolName)) {}
+ explicit IFSSymbol(const std::string &Name, const std::string &Version)
+ : Name(Name), Version(Version) {}
std::string Name;
+ std::string Version;
std::optional<uint64_t> Size;
IFSSymbolType Type = IFSSymbolType::NoType;
bool Undefined = false;
bool Weak = false;
std::optional<std::string> Warning;
- bool operator<(const IFSSymbol &RHS) const { return Name < RHS.Name; }
+ bool operator<(const IFSSymbol &Other) const {
+ return std::tie(Name, Version) < std::tie(Other.Name, Other.Version);
+ }
+};
+
+struct IFSVerDef {
+ std::string Name;
+ std::vector<std::string> Parents;
+
+ bool operator==(const IFSVerDef &Other) const {
+ return std::tie(Name, Parents) == std::tie(Other.Name, Other.Parents);
+ }
+ bool operator!=(const IFSVerDef &Other) const { return !(*this == Other); }
+};
+
+struct IFSVerNeed {
+ std::string File;
+ std::vector<std::string> Names;
+
+ bool operator==(const IFSVerNeed &Other) const {
+ return std::tie(File, Names) == std::tie(Other.File, Other.Names);
+ }
+ bool operator!=(const IFSVerNeed &Other) const { return !(*this == Other); }
};
struct IFSTarget {
@@ -70,21 +94,16 @@ struct IFSTarget {
std::optional<IFSEndiannessType> Endianness;
std::optional<IFSBitWidthType> BitWidth;
+ bool operator==(const IFSTarget &Other) const {
+ return std::tie(Arch, BitWidth, Endianness, ObjectFormat, Triple) ==
+ std::tie(Other.Arch, Other.BitWidth, Other.Endianness,
+ Other.ObjectFormat, Other.Triple);
+ }
+ bool operator!=(const IFSTarget &Other) const { return !(*this == Other); }
+
LLVM_ABI bool empty();
};
-inline bool operator==(const IFSTarget &Lhs, const IFSTarget &Rhs) {
- if (Lhs.Arch != Rhs.Arch || Lhs.BitWidth != Rhs.BitWidth ||
- Lhs.Endianness != Rhs.Endianness ||
- Lhs.ObjectFormat != Rhs.ObjectFormat || Lhs.Triple != Rhs.Triple)
- return false;
- return true;
-}
-
-inline bool operator!=(const IFSTarget &Lhs, const IFSTarget &Rhs) {
- return !(Lhs == Rhs);
-}
-
// A cumulative representation of InterFace stubs.
// Both textual and binary stubs will read into and write from this object.
struct IFSStub {
@@ -94,6 +113,8 @@ struct IFSStub {
IFSTarget Target;
std::vector<std::string> NeededLibs;
std::vector<IFSSymbol> Symbols;
+ std::vector<IFSVerDef> VersionDefinitions;
+ std::vector<IFSVerNeed> VersionRequirements;
IFSStub() = default;
LLVM_ABI IFSStub(const IFSStub &Stub);
diff --git a/llvm/lib/InterfaceStub/ELFObjHandler.cpp b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
index 9c81a8832c0f2..447f7ab736f96 100644
--- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp
+++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
@@ -39,6 +39,12 @@ struct DynamicEntries {
// Hash tables:
std::optional<uint64_t> ElfHash;
std::optional<uint64_t> GnuHash;
+ // Version tables:
+ std::optional<uint64_t> VerSym;
+ std::optional<uint64_t> VerDef;
+ std::optional<uint64_t> VerDefNum;
+ std::optional<uint64_t> VerNeed;
+ std::optional<uint64_t> VerNeedNum;
};
/// This initializes an ELF file header with information specific to a binary
@@ -124,6 +130,148 @@ template <class ELFT> class ELFSymbolTableBuilder {
llvm::SmallVector<Elf_Sym, 8> Symbols;
};
+template <class ELFT> class ELFVersionSymbolBuilder {
+public:
+ using Elf_Versym = typename ELFT::Versym;
+
+ ELFVersionSymbolBuilder() { VerSyms.push_back({}); }
+
+ void add(uint16_t Index) {
+ Elf_Versym VerSym;
+ VerSym.vs_index = Index;
+ VerSyms.push_back(VerSym);
+ }
+
+ size_t getSize() const { return VerSyms.size() * sizeof(Elf_Versym); }
+
+ void write(uint8_t *Buf) const {
+ memcpy(Buf, VerSyms.data(), VerSyms.size() * sizeof(Elf_Versym));
+ }
+
+private:
+ llvm::SmallVector<Elf_Versym, 8> VerSyms;
+};
+
+template <class ELFT> class ELFVersionDefinitionBuilder {
+public:
+ using Elf_Verdef = typename ELFT::Verdef;
+ using Elf_Verdaux = typename ELFT::Verdaux;
+
+ ELFVersionDefinitionBuilder() { VerDAuxes.push_back({}); }
+
+ void addDef(uint16_t Index, uint16_t Count, uint32_t Hash) {
+ Elf_Verdef VerDef;
+ VerDef.vd_version = VER_DEF_CURRENT;
+ VerDef.vd_flags = Index == 1 ? VER_FLG_BASE : 0;
+ VerDef.vd_ndx = Index;
+ VerDef.vd_cnt = Count;
+ VerDef.vd_hash = Hash;
+ VerDef.vd_aux = sizeof(Elf_Verdef);
+ VerDef.vd_next = sizeof(Elf_Verdef) + Count * sizeof(Elf_Verdaux);
+ VerDefs.push_back(VerDef);
+ VerDAuxes.push_back({});
+ }
+
+ void addAux(uint16_t Vdndx, uint32_t Name) {
+ Elf_Verdaux VerDAux;
+ VerDAux.vda_name = Name;
+ VerDAux.vda_next = sizeof(Elf_Verdaux);
+ VerDAuxes[Vdndx].push_back(VerDAux);
+ }
+
+ void finalize() {
+ if (!VerDefs.empty())
+ VerDefs.back().vd_next = 0;
+ for (llvm::SmallVector<Elf_Verdaux, 8> &VerDAux : VerDAuxes)
+ if (!VerDAux.empty())
+ VerDAux.back().vda_next = 0;
+ }
+
+ size_t getSize() const {
+ size_t Count = 0;
+ for (const llvm::SmallVector<Elf_Verdaux, 8> &VerDAux : VerDAuxes)
+ Count += VerDAux.size();
+ return Count * sizeof(Elf_Verdaux) + VerDefs.size() * sizeof(Elf_Verdef);
+ }
+
+ void write(uint8_t *Buf) const {
+ uint8_t *Ptr = Buf;
+ size_t Count = 0;
+ for (const Elf_Verdef &VerDef : VerDefs) {
+ Count = sizeof(Elf_Verdef);
+ memcpy(Ptr, &VerDef, Count);
+ Ptr += Count;
+ Count = VerDef.vd_cnt * sizeof(Elf_Verdaux);
+ memcpy(Ptr, VerDAuxes[VerDef.vd_ndx].data(), Count);
+ Ptr += Count;
+ }
+ }
+
+private:
+ llvm::SmallVector<Elf_Verdef, 8> VerDefs;
+ llvm::SmallVector<llvm::SmallVector<Elf_Verdaux, 8>, 8> VerDAuxes;
+};
+
+template <class ELFT> class ELFVersionRequirementBuilder {
+public:
+ using Elf_Verneed = typename ELFT::Verneed;
+ using Elf_Vernaux = typename ELFT::Vernaux;
+
+ void addFile(uint16_t Count, uint32_t File) {
+ Elf_Verneed VerNeed;
+ VerNeed.vn_version = VER_NEED_CURRENT;
+ VerNeed.vn_cnt = Count;
+ VerNeed.vn_file = File;
+ VerNeed.vn_aux = sizeof(Elf_Verneed);
+ VerNeed.vn_next = sizeof(Elf_Verneed) + Count * sizeof(Elf_Vernaux);
+ VerNeeds.push_back(VerNeed);
+ VerNAuxes.push_back({});
+ }
+
+ void addAux(uint16_t Vnndx, uint32_t Hash, uint16_t Vdndx, uint32_t Name) {
+ Elf_Vernaux VerNAux;
+ VerNAux.vna_hash = Hash;
+ VerNAux.vna_flags = 0;
+ VerNAux.vna_other = Vdndx;
+ VerNAux.vna_name = Name;
+ VerNAux.vna_next = sizeof(Elf_Vernaux);
+ VerNAuxes[Vnndx].push_back(VerNAux);
+ }
+
+ void finalize() {
+ if (!VerNeeds.empty())
+ VerNeeds.back().vn_next = 0;
+ for (llvm::SmallVector<Elf_Vernaux, 8> &VerNAux : VerNAuxes)
+ if (!VerNAux.empty())
+ VerNAux.back().vna_next = 0;
+ }
+
+ size_t getSize() const {
+ size_t Count = 0;
+ for (const llvm::SmallVector<Elf_Vernaux, 8> &VerNAux : VerNAuxes)
+ Count += VerNAux.size();
+ return Count * sizeof(Elf_Vernaux) + VerNeeds.size() * sizeof(Elf_Verneed);
+ }
+
+ void write(uint8_t *Buf) const {
+ uint8_t *Ptr = Buf;
+ size_t Count = 0;
+ size_t Vnndx = 0;
+ for (const Elf_Verneed &VerNeed : VerNeeds) {
+ Count = sizeof(Elf_Verneed);
+ memcpy(Ptr, &VerNeed, Count);
+ Ptr += Count;
+ Count = VerNeed.vn_cnt * sizeof(Elf_Vernaux);
+ memcpy(Ptr, VerNAuxes[Vnndx].data(), Count);
+ Ptr += Count;
+ }
+ }
+
+private:
+ llvm::SmallVector<Elf_Verneed, 8> VerNeeds;
+ llvm::SmallVector<llvm::SmallVector<Elf_Vernaux, 8>, 8> VerNAuxes;
+};
+
template <class ELFT> class ELFDynamicTableBuilder {
public:
using Elf_Dyn = typename ELFT::Dyn;
@@ -188,17 +336,43 @@ template <class ELFT> class ELFStubBuilder {
DynTab.Align = sizeof(Elf_Addr);
ShStrTab.Name = ".shstrtab";
ShStrTab.Align = 1;
+ VerSym.Name = ".gnu.version";
+ VerSym.Align = 2;
+ VerDef.Name = ".gnu.version_d";
+ VerDef.Align = sizeof(Elf_Addr);
+ VerNeed.Name = ".gnu.version_r";
+ VerNeed.Align = sizeof(Elf_Addr);
// Populate string tables.
for (const IFSSymbol &Sym : Stub.Symbols)
DynStr.Content.add(Sym.Name);
+ for (const IFSVerDef &VerDef : Stub.VersionDefinitions)
+ DynStr.Content.add(VerDef.Name);
+ for (const IFSVerNeed &VerNeed : Stub.VersionRequirements)
+ for (const std::string &VerNeedName : VerNeed.Names)
+ DynStr.Content.add(VerNeedName);
for (const std::string &Lib : Stub.NeededLibs)
DynStr.Content.add(Lib);
if (Stub.SoName)
DynStr.Content.add(*Stub.SoName);
- std::vector<OutputSection<ELFT> *> Sections = {&DynSym, &DynStr, &DynTab,
- &ShStrTab};
+ WriteVerSym = any_of(Stub.Symbols, [](const IFSSymbol &Sym) {
+ return !Sym.Version.empty();
+ });
+ WriteVerDef = !Stub.VersionDefinitions.empty();
+ WriteVerNeed = !Stub.VersionRequirements.empty();
+
+ std::vector<OutputSection<ELFT> *> Sections;
+ Sections.push_back(&DynSym);
+ Sections.push_back(&DynStr);
+ if (WriteVerSym)
+ Sections.push_back(&VerSym);
+ if (WriteVerDef)
+ Sections.push_back(&VerDef);
+ if (WriteVerNeed)
+ Sections.push_back(&VerNeed);
+ Sections.push_back(&DynTab);
+ Sections.push_back(&ShStrTab);
const OutputSection<ELFT> *LastSection = Sections.back();
// Now set the Index and put sections names into ".shstrtab".
uint64_t Index = 1;
@@ -224,15 +398,62 @@ template <class ELFT> class ELFStubBuilder {
}
DynSym.Size = DynSym.Content.getSize();
+ std::map<std::string, uint16_t> VerDefMap;
+ size_t Vdndx = 0;
+ size_t Vnndx = 0;
+
+ // Populate symbol version definition table.
+ for (const IFSVerDef &IFSVerDef : Stub.VersionDefinitions) {
+ Vdndx++;
+ VerDef.Content.addDef(Vdndx, IFSVerDef.Parents.size() + 1,
+ hashSysV(IFSVerDef.Name));
+ VerDef.Content.addAux(Vdndx, DynStr.Content.getOffset(IFSVerDef.Name));
+ for (const std::string &Parent : IFSVerDef.Parents) {
+ VerDef.Content.addAux(Vdndx, DynStr.Content.getOffset(Parent));
+ }
+ VerDefMap.insert({IFSVerDef.Name, Vdndx});
+ }
+ VerDef.Content.finalize();
+ VerDef.Size = VerDef.Content.getSize();
+
+ // Populate symbol version requirement table.
+ for (const IFSVerNeed &IFSVerNeed : Stub.VersionRequirements) {
+ VerNeed.Content.addFile(IFSVerNeed.Names.size(),
+ DynStr.Content.getOffset(IFSVerNeed.File));
+ for (const std::string &Name : IFSVerNeed.Names) {
+ Vdndx++;
+ VerNeed.Content.addAux(Vnndx, hashSysV(Name), Vdndx,
+ DynStr.Content.getOffset(Name));
+ VerDefMap.insert({Name, Vdndx});
+ }
+ Vnndx++;
+ }
+ VerNeed.Content.finalize();
+ VerNeed.Size = VerNeed.Content.getSize();
+
+ // Populate dynamic symbol version table.
+ for (const IFSSymbol &Sym : Stub.Symbols)
+ VerSym.Content.add(Sym.Version.empty() ? 1 : VerDefMap[Sym.Version]);
+ VerSym.Size = VerSym.Content.getSize();
+
// Poplulate dynamic table.
size_t DynSymIndex = DynTab.Content.addAddr(DT_SYMTAB, 0);
size_t DynStrIndex = DynTab.Content.addAddr(DT_STRTAB, 0);
+ size_t VerSymIndex;
+ size_t VerDefIndex;
+ size_t VerNeedIndex;
DynTab.Content.addValue(DT_STRSZ, DynSym.Size);
for (const std::string &Lib : Stub.NeededLibs)
DynTab.Content.addValue(DT_NEEDED, DynStr.Content.getOffset(Lib));
if (Stub.SoName)
DynTab.Content.addValue(DT_SONAME,
DynStr.Content.getOffset(*Stub.SoName));
+ if (WriteVerSym)
+ VerSymIndex = DynTab.Content.addAddr(DT_VERSYM, 0);
+ if (WriteVerDef)
+ VerDefIndex = DynTab.Content.addAddr(DT_VERDEF, 0);
+ if (WriteVerNeed)
+ VerNeedIndex = DynTab.Content.addAddr(DT_VERNEED, 0);
DynTab.Size = DynTab.Content.getSize();
// Calculate sections' addresses and offsets.
uint64_t CurrentOffset = sizeof(Elf_Ehdr);
@@ -244,11 +465,23 @@ template <class ELFT> class ELFStubBuilder {
// Fill Addr back to dynamic table.
DynTab.Content.modifyAddr(DynSymIndex, DynSym.Addr);
DynTab.Content.modifyAddr(DynStrIndex, DynStr.Addr);
+ if (WriteVerSym)
+ DynTab.Content.modifyAddr(VerSymIndex, VerSym.Addr);
+ if (WriteVerDef)
+ DynTab.Content.modifyAddr(VerDefIndex, VerDef.Addr);
+ if (WriteVerNeed)
+ DynTab.Content.modifyAddr(VerNeedIndex, VerNeed.Addr);
// Write section headers of string tables.
fillSymTabShdr(DynSym, SHT_DYNSYM);
fillStrTabShdr(DynStr, SHF_ALLOC);
fillDynTabShdr(DynTab);
fillStrTabShdr(ShStrTab);
+ if (WriteVerSym)
+ fillVerSymShdr(VerSym);
+ if (WriteVerDef)
+ fillVerDefShdr(VerDef, Stub.VersionDefinitions.size());
+ if (WriteVerNeed)
+ fillVerNeedShdr(VerNeed, Stub.VersionRequirements.size());
// Finish initializing the ELF header.
initELFHeader<ELFT>(ElfHeader, static_cast<uint16_t>(*Stub.Target.Arch));
@@ -268,10 +501,22 @@ template <class ELFT> class ELFStubBuilder {
DynStr.Content.write(Data + DynStr.Shdr.sh_offset);
DynTab.Content.write(Data + DynTab.Shdr.sh_offset);
ShStrTab.Content.write(Data + ShStrTab.Shdr.sh_offset);
+ if (WriteVerSym)
+ VerSym.Content.write(Data + VerSym.Shdr.sh_offset);
+ if (WriteVerDef)
+ VerDef.Content.write(Data + VerDef.Shdr.sh_offset);
+ if (WriteVerNeed)
+ VerNeed.Content.write(Data + VerNeed.Shdr.sh_offset);
writeShdr(Data, DynSym);
writeShdr(Data, DynStr);
writeShdr(Data, DynTab);
writeShdr(Data, ShStrTab);
+ if (WriteVerSym)
+ writeShdr(Data, VerSym);
+ if (WriteVerDef)
+ writeShdr(Data, VerDef);
+ if (WriteVerNeed)
+ writeShdr(Data, VerNeed);
}
private:
@@ -280,6 +525,12 @@ template <class ELFT> class ELFStubBuilder {
ContentSection<ELFStringTableBuilder, ELFT> ShStrTab;
ContentSection<ELFSymbolTableBuilder<ELFT>, ELFT> DynSym;
ContentSection<ELFDynamicTableBuilder<ELFT>, ELFT> DynTab;
+ ContentSection<ELFVersionSymbolBuilder<ELFT>, ELFT> VerSym;
+ ContentSection<ELFVersionDefinitionBuilder<ELFT>, ELFT> VerDef;
+ ContentSection<ELFVersionRequirementBuilder<ELFT>, ELFT> VerNeed;
+ bool WriteVerSym = false;
+ bool WriteVerDef = false;
+ bool WriteVerNeed = false;
template <class T> static void write(uint8_t *Data, const T &Value) {
*reinterpret_cast<T *>(Data) = Value;
@@ -298,6 +549,7 @@ template <class ELFT> class ELFStubBuilder {
StrTab.Shdr.sh_entsize = 0;
StrTab.Shdr.sh_link = 0;
}
+
void fillSymTabShdr(ContentSection<ELFSymbolTableBuilder<ELFT>, ELFT> &SymTab,
uint32_t ShType) const {
SymTab.Shdr.sh_type = ShType;
@@ -314,6 +566,7 @@ template <class ELFT> class ELFStubBuilder {
SymTab.Shdr.sh_entsize = sizeof(Elf_Sym);
SymTab.Shdr.sh_link = this->DynStr.Index;
}
+
void fillDynTabShdr(
ContentSection<ELFDynamicTableBuilder<ELFT>, ELFT> &DynTab) const {
DynTab.Shdr.sh_type = SHT_DYNAMIC;
@@ -327,6 +580,51 @@ template <class ELFT> class ELFStubBuilder {
DynTab.Shdr.sh_entsize = sizeof(Elf_Dyn);
DynTab.Shdr.sh_link = this->DynStr.Index;
}
+
+ void fillVerSymShdr(
+ ContentSection<ELFVersionSymbolBuilder<ELFT>, ELFT> &VerSym) const {
+ VerSym.Shdr.sh_type = SHT_GNU_versym;
+ VerSym.Shdr.sh_flags = SHF_ALLOC;
+ VerSym.Shdr.sh_addr = VerSym.Addr;
+ VerSym.Shdr.sh_offset = VerSym.Offset;
+ VerSym.Shdr.sh_info = 0;
+ VerSym.Shdr.sh_size = VerSym.Size;
+ VerSym.Shdr.sh_name = this->ShStrTab.Content.getOffset(VerSym.Name);
+ VerSym.Shdr.sh_addralign = VerSym.Align;
+ VerSym.Shdr.sh_entsize = sizeof(uint16_t);
+ VerSym.Shdr.sh_link = this->DynSym.Index;
+ }
+
+ void fillVerDefShdr(
+ ContentSection<ELFVersionDefinitionBuilder<ELFT>, ELFT> &VerDef,
+ size_t Size) const {
+ VerDef.Shdr.sh_type = SHT_GNU_verdef;
+ VerDef.Shdr.sh_flags = SHF_ALLOC;
+ VerDef.Shdr.sh_addr = VerDef.Addr;
+ VerDef.Shdr.sh_offset = VerDef.Offset;
+ VerDef.Shdr.sh_info = Size;
+ VerDef.Shdr.sh_size = VerDef.Size;
+ VerDef.Shdr.sh_name = this->ShStrTab.Content.getOffset(VerDef.Name);
+ VerDef.Shdr.sh_addralign = VerDef.Align;
+ VerDef.Shdr.sh_entsize = sizeof(Elf_Dyn);
+ VerDef.Shdr.sh_link = this->DynStr.Index;
+ }
+
+ void fillVerNeedShdr(
+ ContentSection<ELFVersionRequirementBuilder<ELFT>, ELFT> &VerNeed,
+ size_t Size) const {
+ VerNeed.Shdr.sh_type = SHT_GNU_verneed;
+ VerNeed.Shdr.sh_flags = SHF_ALLOC;
+ VerNeed.Shdr.sh_addr = VerNeed.Addr;
+ VerNeed.Shdr.sh_offset = VerNeed.Offset;
+ VerNeed.Shdr.sh_info = Size;
+ VerNeed.Shdr.sh_size = VerNeed.Size;
+ VerNeed.Shdr.sh_name = this->ShStrTab.Content.getOffset(VerNeed.Name);
+ VerNeed.Shdr.sh_addralign = VerNeed.Align;
+ VerNeed.Shdr.sh_entsize = sizeof(Elf_Dyn);
+ VerNeed.Shdr.sh_link = this->DynStr.Index;
+ }
+
uint64_t shdrOffset(const OutputSection<ELFT> &Sec) const {
return ElfHeader.e_shoff + Sec.Index * sizeof(Elf_Shdr);
}
@@ -371,6 +669,12 @@ template <class ELFT> class DynSym {
return getDynamicData(DynEnt.DynSymAddr, "dynamic symbol table");
}
+ const Elf_Shdr *getVerSym() { return findHdr(SHT_GNU_versym); }
+
+ const Elf_Shdr *getVerDef() { return findHdr(SHT_GNU_verdef); }
+
+ const Elf_Shdr *getVerNeed() { return findHdr(SHT_GNU_verneed); }
+
Expected<StringRef> getDynStr() {
if (DynSymHdr)
return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs);
@@ -386,11 +690,11 @@ template <class ELFT> class DynSym {
DynSym(const ELFFile<ELFT> &ElfFile, const DynamicEntries &DynEnt,
Elf_Shdr_Range Shdrs)
: ElfFile(ElfFile), DynEnt(DynEnt), Shdrs(Shdrs),
- DynSymHdr(findDynSymHdr()) {}
+ DynSymHdr(findHdr(SHT_DYNSYM)) {}
- const Elf_Shdr *findDynSymHdr() {
+ const Elf_Shdr *findHdr(uint32_t Type) const {
for (const Elf_Shdr &Sec : Shdrs)
- if (Sec.sh_type == SHT_DYNSYM) {
+ if (Sec.sh_type == Type) {
// If multiple .dynsym are present, use the first one.
// This behavior aligns with llvm::object::ELFFile::getDynSymtabSize()
return &Sec;
@@ -478,6 +782,22 @@ static Error populateDynamic(DynamicEntries &Dyn,
break;
case DT_GNU_HASH:
Dyn.GnuHash = Entry.d_un.d_ptr;
+ break;
+ case DT_VERSYM:
+ Dyn.VerSym = Entry.d_un.d_ptr;
+ break;
+ case DT_VERDEF:
+ Dyn.VerDef = Entry.d_un.d_ptr;
+ break;
+ case DT_VERDEFNUM:
+ Dyn.VerDefNum = Entry.d_un.d_val;
+ break;
+ case DT_VERNEED:
+ Dyn.VerNeed = Entry.d_un.d_ptr;
+ break;
+ case DT_VERNEEDNUM:
+ Dyn.VerNeedNum = Entry.d_un.d_val;
+ break;
}
}
@@ -515,11 +835,12 @@ static Error populateDynamic(DynamicEntries &Dyn,
/// information from a binary ELFT::Sym.
///
/// @param SymName The desired name of the IFSSymbol.
+/// @param SymVer The desired version of the IFSSymbol.
/// @param RawSym ELFT::Sym to extract symbol information from.
template <class ELFT>
-static IFSSymbol createELFSym(StringRef SymName,
+static IFSSymbol createELFSym(StringRef SymName, StringRef SymVer,
const typename ELFT::Sym &RawSym) {
- IFSSymbol TargetSym{std::string(SymName)};
+ IFSSymbol TargetSym{SymName.str(), SymVer.str()};
uint8_t Binding = RawSym.getBinding();
if (Binding == STB_WEAK)
TargetSym.Weak = true;
@@ -541,14 +862,22 @@ static IFSSymbol createELFSym(StringRef SymName,
/// from an ELF binary.
///
/// @param TargetStub IFSStub to add symbols to.
+/// @param ElfFile Elf File object.
/// @param DynSym Range of dynamic symbols to add to TargetStub.
+/// @param VerSymPtr Pointer to symbol version table in Elf File.
+/// @param VersionMap Versions of dynamic symbols.
/// @param DynStr StringRef to the dynamic string table.
template <class ELFT>
-static Error populateSymbols(IFSStub &TargetStub,
- const typename ELFT::SymRange DynSym,
- StringRef DynStr) {
+static Error
+populateSymbols(IFSStub &TargetStub, const ELFFile<ELFT> &ElfFile,
+ const typename ELFT::SymRange DynSym,
+ const typename ELFT::Shdr *VerSymPtr,
+ const SmallVector<std::optional<VersionEntry>> &VersionMap,
+ StringRef DynStr) {
// Skips the first symbol since it's the NULL symbol.
+ size_t I = 0;
for (auto RawSym : DynSym.drop_front(1)) {
+ I++;
// If a symbol does not have global or weak binding, ignore it.
uint8_t Binding = RawSym.getBinding();
if (!(Binding == STB_GLOBAL || Binding == STB_WEAK))
@@ -562,7 +891,22 @@ static Error populateSymbols(IFSStub &TargetStub,
Expected<StringRef> SymName = terminatedSubstr(DynStr, RawSym.st_name);
if (!SymName)
return SymName.takeError();
- IFSSymbol Sym = createELFSym<ELFT>(*SymName, RawSym);
+ std::string VersionName;
+ if (VerSymPtr) {
+ Expected<const typename ELFT::Versym *> VerEntryOrErr =
+ ElfFile.template getEntry<typename ELFT::Versym>(*VerSymPtr, I);
+ if (!VerEntryOrErr)
+ return appendToError(VerEntryOrErr.takeError(),
+ "when reading symbol versions");
+ size_t VersionIndex = (*VerEntryOrErr)->vs_index & VERSYM_VERSION;
+ if (VersionIndex > VersionMap.size() || !VersionMap[VersionIndex]) {
+ return createError("SHT_GNU_versym section refers to a version index " +
+ Twine(VersionIndex) + " which is missing");
+ }
+ if (VersionIndex != VER_NDX_LOCAL && VersionIndex != VER_NDX_GLOBAL)
+ VersionName = VersionMap[VersionIndex]->Name.c_str();
+ }
+ IFSSymbol Sym = createELFSym<ELFT>(*SymName, VersionName, RawSym);
TargetStub.Symbols.push_back(std::move(Sym));
// TODO: Populate symbol warning.
}
@@ -577,6 +921,7 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
using Elf_Dyn_Range = typename ELFT::DynRange;
using Elf_Sym_Range = typename ELFT::SymRange;
using Elf_Sym = typename ELFT::Sym;
+ using Elf_Shdr = typename ELFT::Shdr;
std::unique_ptr<IFSStub> DestStub = std::make_unique<IFSStub>();
const ELFFile<ELFT> &ElfFile = ElfObj.getELFFile();
// Fetch .dynamic table.
@@ -627,11 +972,52 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
DestStub->NeededLibs.push_back(std::string(*LibNameOrErr));
}
+ const Elf_Shdr *VerDefPtr = EDynSym->getVerDef();
+ if (VerDefPtr) {
+ Expected<std::vector<VerDef>> VerDefOrError =
+ ElfFile.getVersionDefinitions(*VerDefPtr);
+ if (!VerDefOrError)
+ return appendToError(VerDefOrError.takeError(),
+ "when reading symbol version definitions");
+ llvm::transform(
+ *VerDefOrError, std::back_inserter(DestStub->VersionDefinitions),
+ [](const VerDef &VerDef) {
+ std::vector<std::string> Parents;
+ llvm::transform(VerDef.AuxV, std::back_inserter(Parents),
+ [](const VerdAux &VerDAux) { return VerDAux.Name; });
+ return IFSVerDef{VerDef.Name, std::move(Parents)};
+ });
+ }
+
+ const Elf_Shdr *VerNeedPtr = EDynSym->getVerNeed();
+ if (VerNeedPtr) {
+ Expected<std::vector<VerNeed>> VerNeedOrError =
+ ElfFile.getVersionDependencies(*VerNeedPtr);
+ if (!VerNeedOrError)
+ return appendToError(VerNeedOrError.takeError(),
+ "when reading symbol version needs");
+ llvm::transform(*VerNeedOrError,
+ std::back_inserter(DestStub->VersionRequirements),
+ [](const VerNeed &VerNeed) {
+ std::vector<std::string> Names;
+ llvm::transform(VerNeed.AuxV, std::back_inserter(Names),
+ [](const VernAux &VerNAux) {
+ return VerNAux.Name.c_str();
+ });
+ return IFSVerNeed{VerNeed.File, std::move(Names)};
+ });
+ }
+
// Populate Symbols from .dynsym table and dynamic string table.
Expected<uint64_t> SymCount = ElfFile.getDynSymtabSize();
if (!SymCount)
return SymCount.takeError();
if (*SymCount > 0) {
+ Expected<SmallVector<std::optional<VersionEntry>>> VersionsOrError =
+ ElfFile.loadVersionMap(VerNeedPtr, VerDefPtr);
+ if (!VersionsOrError)
+ return appendToError(VersionsOrError.takeError(),
+ "when reading dynamic symbol versions");
// Get pointer to in-memory location of .dynsym section.
Expected<const uint8_t *> DynSymPtr = EDynSym->getDynSym();
if (!DynSymPtr)
@@ -639,7 +1025,9 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
"when locating .dynsym section contents");
Elf_Sym_Range DynSyms = ArrayRef<Elf_Sym>(
reinterpret_cast<const Elf_Sym *>(*DynSymPtr), *SymCount);
- Error SymReadError = populateSymbols<ELFT>(*DestStub, DynSyms, DynStr);
+ Error SymReadError =
+ populateSymbols<ELFT>(*DestStub, ElfFile, DynSyms, EDynSym->getVerSym(),
+ *VersionsOrError, DynStr);
if (SymReadError)
return appendToError(std::move(SymReadError),
"when reading dynamic symbols");
@@ -695,13 +1083,16 @@ Expected<std::unique_ptr<IFSStub>> readELFFile(MemoryBufferRef Buf) {
}
Binary *Bin = BinOrErr->get();
- if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
+ if (auto *Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
return buildStub(*Obj);
- } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
+ }
+ if (auto *Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
return buildStub(*Obj);
- } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
+ }
+ if (auto *Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
return buildStub(*Obj);
- } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
+ }
+ if (auto *Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
return buildStub(*Obj);
}
return createStringError(errc::not_supported, "unsupported binary format");
@@ -714,18 +1105,43 @@ Error writeBinaryStub(StringRef FilePath, const IFSStub &Stub,
assert(Stub.Target.Arch);
assert(Stub.Target.BitWidth);
assert(Stub.Target.Endianness);
- if (Stub.Target.BitWidth == IFSBitWidthType::IFS32) {
- if (Stub.Target.Endianness == IFSEndiannessType::Little) {
- return writeELFBinaryToFile<ELF32LE>(FilePath, Stub, WriteIfChanged);
- } else {
- return writeELFBinaryToFile<ELF32BE>(FilePath, Stub, WriteIfChanged);
+ Error (*WriteELF)(StringRef, const IFSStub &, bool) = nullptr;
+ switch (*Stub.Target.BitWidth) {
+ case IFSBitWidthType::IFS32: {
+ switch (*Stub.Target.Endianness) {
+ case IFSEndiannessType::Little: {
+ WriteELF = writeELFBinaryToFile<ELF32LE>;
+ break;
}
- } else {
- if (Stub.Target.Endianness == IFSEndiannessType::Little) {
- return writeELFBinaryToFile<ELF64LE>(FilePath, Stub, WriteIfChanged);
- } else {
- return writeELFBinaryToFile<ELF64BE>(FilePath, Stub, WriteIfChanged);
+ case IFSEndiannessType::Big: {
+ WriteELF = writeELFBinaryToFile<ELF32BE>;
+ break;
}
+ case IFSEndiannessType::Unknown:
+ break;
+ }
+ break;
+ }
+ case IFSBitWidthType::IFS64: {
+ switch (*Stub.Target.Endianness) {
+ case IFSEndiannessType::Little: {
+ WriteELF = writeELFBinaryToFile<ELF64LE>;
+ break;
+ }
+ case IFSEndiannessType::Big: {
+ WriteELF = writeELFBinaryToFile<ELF64BE>;
+ break;
+ }
+ case IFSEndiannessType::Unknown:
+ break;
+ }
+ break;
+ }
+ case IFSBitWidthType::Unknown:
+ break;
+ }
+ if (WriteELF) {
+ return WriteELF(FilePath, Stub, WriteIfChanged);
}
llvm_unreachable("invalid binary output target");
}
diff --git a/llvm/lib/InterfaceStub/IFSHandler.cpp b/llvm/lib/InterfaceStub/IFSHandler.cpp
index 39dc46601b2a0..cfb7a71cae1de 100644
--- a/llvm/lib/InterfaceStub/IFSHandler.cpp
+++ b/llvm/lib/InterfaceStub/IFSHandler.cpp
@@ -24,10 +24,26 @@ using namespace llvm;
using namespace llvm::ifs;
LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol)
+LLVM_YAML_IS_SEQUENCE_VECTOR(IFSVerDef)
+LLVM_YAML_IS_SEQUENCE_VECTOR(IFSVerNeed)
+LLVM_YAML_STRONG_TYPEDEF(std::string, FlowString)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowString)
namespace llvm {
namespace yaml {
+template <> struct ScalarTraits<FlowString> {
+ static void output(const FlowString &Value, void *, llvm::raw_ostream &Out) {
+ Out << Value;
+ }
+
+ static StringRef input(StringRef Scalar, void *, FlowString &Value) {
+ return Scalar;
+ }
+
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
/// YAML traits for ELFSymbolType.
template <> struct ScalarEnumerationTraits<IFSSymbolType> {
static void enumeration(IO &IO, IFSSymbolType &SymbolType) {
@@ -116,6 +132,7 @@ template <> struct MappingTraits<IFSTarget> {
template <> struct MappingTraits<IFSSymbol> {
static void mapping(IO &IO, IFSSymbol &Symbol) {
IO.mapRequired("Name", Symbol.Name);
+ IO.mapOptional("Version", Symbol.Version, "");
IO.mapRequired("Type", Symbol.Type);
// The need for symbol size depends on the symbol type.
if (Symbol.Type == IFSSymbolType::NoType) {
@@ -135,6 +152,40 @@ template <> struct MappingTraits<IFSSymbol> {
static const bool flow = true; // NOLINT(readability-identifier-naming)
};
+/// YAML traits for ELFVersionDefinition.
+template <> struct MappingTraits<IFSVerDef> {
+ static void mapping(IO &IO, IFSVerDef &VerDef) {
+ IO.mapRequired("Name", VerDef.Name);
+ if (IO.outputting()) {
+ std::vector<FlowString> Parents;
+ llvm::copy(VerDef.Parents, std::back_inserter(Parents));
+ IO.mapOptional("Parents", Parents);
+ } else {
+ IO.mapOptional("Parents", VerDef.Parents);
+ }
+ }
+
+ // Compacts symbol information into a single line.
+ static const bool flow = true; // NOLINT(readability-identifier-naming)
+};
+
+/// YAML traits for ELFVersionRequirements.
+template <> struct MappingTraits<IFSVerNeed> {
+ static void mapping(IO &IO, IFSVerNeed &VerNeed) {
+ IO.mapRequired("File", VerNeed.File);
+ if (IO.outputting()) {
+ std::vector<FlowString> Names;
+ llvm::copy(VerNeed.Names, std::back_inserter(Names));
+ IO.mapOptional("Names", Names);
+ } else {
+ IO.mapOptional("Names", VerNeed.Names);
+ }
+ }
+
+ // Compacts symbol information into a single line.
+ static const bool flow = true; // NOLINT(readability-identifier-naming)
+};
+
/// YAML traits for ELFStub objects.
template <> struct MappingTraits<IFSStub> {
static void mapping(IO &IO, IFSStub &Stub) {
@@ -145,6 +196,8 @@ template <> struct MappingTraits<IFSStub> {
IO.mapOptional("Target", Stub.Target);
IO.mapOptional("NeededLibs", Stub.NeededLibs);
IO.mapRequired("Symbols", Stub.Symbols);
+ IO.mapOptional("VersionDefinitions", Stub.VersionDefinitions);
+ IO.mapOptional("VersionRequirements", Stub.VersionRequirements);
}
};
@@ -158,6 +211,8 @@ template <> struct MappingTraits<IFSStubTriple> {
IO.mapOptional("Target", Stub.Target.Triple);
IO.mapOptional("NeededLibs", Stub.NeededLibs);
IO.mapRequired("Symbols", Stub.Symbols);
+ IO.mapOptional("VersionDefinitions", Stub.VersionDefinitions);
+ IO.mapOptional("VersionRequirements", Stub.VersionRequirements);
}
};
} // end namespace yaml
diff --git a/llvm/lib/InterfaceStub/IFSStub.cpp b/llvm/lib/InterfaceStub/IFSStub.cpp
index cde15ccfdba06..d797a9d55593b 100644
--- a/llvm/lib/InterfaceStub/IFSStub.cpp
+++ b/llvm/lib/InterfaceStub/IFSStub.cpp
@@ -19,6 +19,8 @@ IFSStub::IFSStub(IFSStub const &Stub) {
SoName = Stub.SoName;
NeededLibs = Stub.NeededLibs;
Symbols = Stub.Symbols;
+ VersionDefinitions = Stub.VersionDefinitions;
+ VersionRequirements = Stub.VersionRequirements;
}
IFSStub::IFSStub(IFSStub &&Stub) {
@@ -27,6 +29,8 @@ IFSStub::IFSStub(IFSStub &&Stub) {
SoName = std::move(Stub.SoName);
NeededLibs = std::move(Stub.NeededLibs);
Symbols = std::move(Stub.Symbols);
+ VersionDefinitions = std::move(Stub.VersionDefinitions);
+ VersionRequirements = std::move(Stub.VersionRequirements);
}
IFSStubTriple::IFSStubTriple(IFSStubTriple const &Stub) : IFSStub() {
@@ -35,6 +39,8 @@ IFSStubTriple::IFSStubTriple(IFSStubTriple const &Stub) : IFSStub() {
SoName = Stub.SoName;
NeededLibs = Stub.NeededLibs;
Symbols = Stub.Symbols;
+ VersionDefinitions = Stub.VersionDefinitions;
+ VersionRequirements = Stub.VersionRequirements;
}
IFSStubTriple::IFSStubTriple(IFSStub const &Stub) {
@@ -43,6 +49,8 @@ IFSStubTriple::IFSStubTriple(IFSStub const &Stub) {
SoName = Stub.SoName;
NeededLibs = Stub.NeededLibs;
Symbols = Stub.Symbols;
+ VersionDefinitions = Stub.VersionDefinitions;
+ VersionRequirements = Stub.VersionRequirements;
}
IFSStubTriple::IFSStubTriple(IFSStubTriple &&Stub) {
@@ -51,6 +59,8 @@ IFSStubTriple::IFSStubTriple(IFSStubTriple &&Stub) {
SoName = std::move(Stub.SoName);
NeededLibs = std::move(Stub.NeededLibs);
Symbols = std::move(Stub.Symbols);
+ VersionDefinitions = std::move(Stub.VersionDefinitions);
+ VersionRequirements = std::move(Stub.VersionRequirements);
}
bool IFSTarget::empty() {
diff --git a/llvm/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp
index 1244bfb788ae5..1b90e5c11255e 100644
--- a/llvm/tools/llvm-ifs/llvm-ifs.cpp
+++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp
@@ -392,7 +392,7 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
// Attempt to merge input.
IFSStub Stub;
- std::map<std::string, IFSSymbol> SymbolMap;
+ std::map<std::tuple<std::string, std::string>, IFSSymbol> SymbolMap;
std::string PreviousInputFilePath;
for (const std::string &InputFilePath : Config.InputFilePaths) {
Expected<std::unique_ptr<IFSStub>> StubOrErr =
@@ -406,6 +406,8 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
Stub.Target = TargetStub->Target;
Stub.SoName = TargetStub->SoName;
Stub.NeededLibs = TargetStub->NeededLibs;
+ Stub.VersionDefinitions = TargetStub->VersionDefinitions;
+ Stub.VersionRequirements = TargetStub->VersionRequirements;
} else {
if (Stub.IfsVersion != TargetStub->IfsVersion) {
if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
@@ -439,10 +441,23 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
<< InputFilePath << "\n";
return -1;
}
+ if (Stub.VersionDefinitions != TargetStub->VersionDefinitions) {
+ WithColor::error() << "Interface Stub: VersionDefinitions Mismatch."
+ << "\nFilenames: " << PreviousInputFilePath << " "
+ << InputFilePath << "\n";
+ return -1;
+ }
+ if (Stub.VersionRequirements != TargetStub->VersionRequirements) {
+ WithColor::error() << "Interface Stub: VersionRequirements Mismatch."
+ << "\nFilenames: " << PreviousInputFilePath << " "
+ << InputFilePath << "\n";
+ return -1;
+ }
}
for (auto Symbol : TargetStub->Symbols) {
- auto [SI, Inserted] = SymbolMap.try_emplace(Symbol.Name, Symbol);
+ auto [SI, Inserted] =
+ SymbolMap.try_emplace({Symbol.Name, Symbol.Version}, Symbol);
if (Inserted)
continue;
>From d053aa783f5211d9a95b1f4bd962a3dc8c9e010f Mon Sep 17 00:00:00 2001
From: ur4t <ur4t at protonmail.com>
Date: Sun, 12 Oct 2025 03:30:36 +0000
Subject: [PATCH 2/4] Fix IFS tests
---
.../llvm-ifs/binary-read-syms-gnu-hash.test | 7 +++++--
.../llvm-ifs/binary-read-syms-sysv-hash.test | 7 +++++--
.../tools/llvm-ifs/strip-undefined-symbols.test | 3 +++
llvm/unittests/InterfaceStub/ELFYAMLTest.cpp | 16 ++++++++++++----
4 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test b/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test
index 4fa86dcb0418c..4d98e796af9f1 100644
--- a/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test
+++ b/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test
@@ -14,9 +14,12 @@
# CHECK-NEXT: - { Name: _ITM_deregisterTMCloneTable, Type: NoType, Undefined: true, Weak: true }
# CHECK-NEXT: - { Name: _ITM_registerTMCloneTable, Type: NoType, Undefined: true, Weak: true }
# CHECK-NEXT: - { Name: _Z11rotateArrayPii, Type: Func }
-# CHECK-NEXT: - { Name: __cxa_finalize, Type: Func, Undefined: true, Weak: true }
+# CHECK-NEXT: - { Name: __cxa_finalize, Version: GLIBC_2.2.5, Type: Func, Undefined: true, Weak: true }
# CHECK-NEXT: - { Name: __gmon_start__, Type: NoType, Undefined: true, Weak: true }
-# CHECK-NEXT: - { Name: __tls_get_addr, Type: Func, Undefined: true }
+# CHECK-NEXT: - { Name: __tls_get_addr, Version: GLIBC_2.3, Type: Func, Undefined: true }
# CHECK-NEXT: - { Name: _fini, Type: Func }
# CHECK-NEXT: - { Name: _init, Type: Func }
+# CHECK-NEXT: VersionRequirements:
+# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
+# CHECK-NEXT: - { File: ld-linux-x86-64.so.2, Names: [ GLIBC_2.3 ] }
# CHECK-NEXT: ...
diff --git a/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test b/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test
index 94f1f60a1a751..1763595309f6e 100644
--- a/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test
+++ b/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test
@@ -14,9 +14,12 @@
# CHECK-NEXT: - { Name: _ITM_deregisterTMCloneTable, Type: NoType, Undefined: true, Weak: true }
# CHECK-NEXT: - { Name: _ITM_registerTMCloneTable, Type: NoType, Undefined: true, Weak: true }
# CHECK-NEXT: - { Name: _Z11rotateArrayPii, Type: Func }
-# CHECK-NEXT: - { Name: __cxa_finalize, Type: Func, Undefined: true, Weak: true }
+# CHECK-NEXT: - { Name: __cxa_finalize, Version: GLIBC_2.2.5, Type: Func, Undefined: true, Weak: true }
# CHECK-NEXT: - { Name: __gmon_start__, Type: NoType, Undefined: true, Weak: true }
-# CHECK-NEXT: - { Name: __tls_get_addr, Type: Func, Undefined: true }
+# CHECK-NEXT: - { Name: __tls_get_addr, Version: GLIBC_2.3, Type: Func, Undefined: true }
# CHECK-NEXT: - { Name: _fini, Type: Func }
# CHECK-NEXT: - { Name: _init, Type: Func }
+# CHECK-NEXT: VersionRequirements:
+# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
+# CHECK-NEXT: - { File: ld-linux-x86-64.so.2, Names: [ GLIBC_2.3 ] }
# CHECK-NEXT: ...
diff --git a/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test b/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test
index 010a1b91b9fce..0e138bd12f0b3 100644
--- a/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test
+++ b/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test
@@ -14,4 +14,7 @@
# CHECK-NEXT: - { Name: _Z11rotateArrayPii, Type: Func }
# CHECK-NEXT: - { Name: _fini, Type: Func }
# CHECK-NEXT: - { Name: _init, Type: Func }
+# CHECK-NEXT: VersionRequirements:
+# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
+# CHECK-NEXT: - { File: ld-linux-x86-64.so.2, Names: [ GLIBC_2.3 ] }
# CHECK-NEXT: ...
diff --git a/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp b/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp
index 39aec132db861..f89abf6e6a617 100644
--- a/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp
+++ b/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp
@@ -192,6 +192,7 @@ TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) {
" - { Name: foo, Type: NoType, Size: 99, Warning: Does nothing }\n"
" - { Name: nor, Type: Func, Undefined: true }\n"
" - { Name: not, Type: Unknown, Size: 12345678901234 }\n"
+ " - { Name: ver, Version: VER, Type: Func }\n"
"...\n";
IFSStub Stub;
Stub.IfsVersion = VersionTuple(1, 0);
@@ -200,36 +201,43 @@ TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) {
Stub.Target.Endianness = IFSEndiannessType::Little;
Stub.Target.ObjectFormat = "ELF";
- IFSSymbol SymBar("bar");
+ IFSSymbol SymBar("bar", "");
SymBar.Size = 128u;
SymBar.Type = IFSSymbolType::Func;
SymBar.Undefined = false;
SymBar.Weak = true;
- IFSSymbol SymFoo("foo");
+ IFSSymbol SymFoo("foo", "");
SymFoo.Size = 99u;
SymFoo.Type = IFSSymbolType::NoType;
SymFoo.Undefined = false;
SymFoo.Weak = false;
SymFoo.Warning = "Does nothing";
- IFSSymbol SymNor("nor");
+ IFSSymbol SymNor("nor", "");
SymNor.Size = 1234u;
SymNor.Type = IFSSymbolType::Func;
SymNor.Undefined = true;
SymNor.Weak = false;
- IFSSymbol SymNot("not");
+ IFSSymbol SymNot("not", "");
SymNot.Size = 12345678901234u;
SymNot.Type = IFSSymbolType::Unknown;
SymNot.Undefined = false;
SymNot.Weak = false;
+ IFSSymbol SymVer("ver", "VER");
+ SymVer.Size = 128u;
+ SymVer.Type = IFSSymbolType::Func;
+ SymVer.Undefined = false;
+ SymVer.Weak = false;
+
// Symbol order is preserved instead of being sorted.
Stub.Symbols.push_back(SymBar);
Stub.Symbols.push_back(SymFoo);
Stub.Symbols.push_back(SymNor);
Stub.Symbols.push_back(SymNot);
+ Stub.Symbols.push_back(SymVer);
// Ensure move constructor works as expected.
IFSStub Moved = std::move(Stub);
>From 3b4356078a0760e49a68e29d0d449e93d9f425e2 Mon Sep 17 00:00:00 2001
From: ur4t <ur4t at protonmail.com>
Date: Thu, 8 Jan 2026 07:50:40 +0000
Subject: [PATCH 3/4] Add IFS tests for versioned symbols
---
.../tools/llvm-ifs/version-symbol-func.ifs | 35 +++++++++++++++++
.../tools/llvm-ifs/version-symbol-need.ifs | 38 +++++++++++++++++++
.../tools/llvm-ifs/version-symbol-object.ifs | 32 ++++++++++++++++
3 files changed, 105 insertions(+)
create mode 100644 llvm/test/tools/llvm-ifs/version-symbol-func.ifs
create mode 100644 llvm/test/tools/llvm-ifs/version-symbol-need.ifs
create mode 100644 llvm/test/tools/llvm-ifs/version-symbol-object.ifs
diff --git a/llvm/test/tools/llvm-ifs/version-symbol-func.ifs b/llvm/test/tools/llvm-ifs/version-symbol-func.ifs
new file mode 100644
index 0000000000000..ed74e64a2e3e8
--- /dev/null
+++ b/llvm/test/tools/llvm-ifs/version-symbol-func.ifs
@@ -0,0 +1,35 @@
+# RUN: llvm-ifs --input-format=IFS --output-ifs - %s | \
+# RUN: FileCheck %s --check-prefixes=CHECK-IFS
+
+# RUN: llvm-ifs --input-format=IFS --output-elf - %s | \
+# RUN: llvm-readelf --all - | FileCheck %s --check-prefixes=CHECK-ELF
+
+# CHECK-IFS: --- !ifs-v1
+# CHECK-IFS-NEXT: IfsVersion: 3.0
+# CHECK-IFS-NEXT: SoName: libfoo.so
+# CHECK-IFS-NEXT: Target: x86_64-unknown-linux-gnu
+# CHECK-IFS-NEXT: Symbols:
+# CHECK-IFS-NEXT: - { Name: VER, Version: VER, Type: Object, Size: 0 }
+# CHECK-IFS-NEXT: - { Name: func, Version: VER, Type: Func }
+# CHECK-IFS-NEXT: VersionDefinitions:
+# CHECK-IFS-NEXT: - { Name: libfoo.so }
+# CHECK-IFS-NEXT: - { Name: VER }
+# CHECK-IFS-NEXT: ...
+
+# CHECK-ELF: OBJECT GLOBAL DEFAULT 1 VER@@VER
+# CHECK-ELF: FUNC GLOBAL DEFAULT 1 func@@VER
+
+# CHECK-ELF: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libfoo.so
+# CHECK-ELF: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VER
+
+--- !ifs-v1
+IfsVersion: 3.0
+SoName: libfoo.so
+Target: x86_64-unknown-linux-gnu
+Symbols:
+ - { Name: VER, Version: VER, Type: Object, Size: 0 }
+ - { Name: func, Version: VER, Type: Func }
+VersionDefinitions:
+ - { Name: libfoo.so }
+ - { Name: VER }
+...
diff --git a/llvm/test/tools/llvm-ifs/version-symbol-need.ifs b/llvm/test/tools/llvm-ifs/version-symbol-need.ifs
new file mode 100644
index 0000000000000..881e6d1f1ca12
--- /dev/null
+++ b/llvm/test/tools/llvm-ifs/version-symbol-need.ifs
@@ -0,0 +1,38 @@
+# RUN: llvm-ifs --input-format=IFS --output-ifs - %s | \
+# RUN: FileCheck %s --check-prefixes=CHECK-IFS
+
+# RUN: llvm-ifs --input-format=IFS --output-elf - %s | \
+# RUN: llvm-readelf --all - | FileCheck %s --check-prefixes=CHECK-ELF
+
+# CHECK-IFS: --- !ifs-v1
+# CHECK-IFS-NEXT: IfsVersion: 3.0
+# CHECK-IFS-NEXT: SoName: libfoo.so
+# CHECK-IFS-NEXT: Target: x86_64-unknown-linux-gnu
+# CHECK-IFS-NEXT: NeededLibs:
+# CHECK-IFS-NEXT: - libbar.so
+# CHECK-IFS-NEXT: Symbols:
+# CHECK-IFS-NEXT: - { Name: func, Version: VER, Type: Func }
+# CHECK-IFS-NEXT: VersionDefinitions:
+# CHECK-IFS-NEXT: - { Name: libfoo.so }
+# CHECK-IFS-NEXT: VersionRequirements:
+# CHECK-IFS-NEXT: - { File: libbar.so, Names: [ VER ] }
+# CHECK-IFS-NEXT: ...
+
+# CHECK-ELF: FUNC GLOBAL DEFAULT 1 func at VER
+
+# CHECK-ELF: Version: 1 File: libbar.so Cnt: 1
+# CHECK-ELF: Name: VER Flags: none Version: 2
+
+--- !ifs-v1
+IfsVersion: 3.0
+SoName: libfoo.so
+Target: x86_64-unknown-linux-gnu
+NeededLibs:
+ - libbar.so
+Symbols:
+ - { Name: func, Version: VER, Type: Func }
+VersionDefinitions:
+ - { Name: libfoo.so }
+VersionRequirements:
+ - { File: libbar.so, Names: [ VER ] }
+...
diff --git a/llvm/test/tools/llvm-ifs/version-symbol-object.ifs b/llvm/test/tools/llvm-ifs/version-symbol-object.ifs
new file mode 100644
index 0000000000000..c36c2ba6119a6
--- /dev/null
+++ b/llvm/test/tools/llvm-ifs/version-symbol-object.ifs
@@ -0,0 +1,32 @@
+# RUN: llvm-ifs --input-format=IFS --output-ifs - %s | \
+# RUN: FileCheck %s --check-prefixes=CHECK-IFS
+
+# RUN: llvm-ifs --input-format=IFS --output-elf - %s | \
+# RUN: llvm-readelf --all - | FileCheck %s --check-prefixes=CHECK-ELF
+
+# CHECK-IFS: --- !ifs-v1
+# CHECK-IFS-NEXT: IfsVersion: 3.0
+# CHECK-IFS-NEXT: SoName: libfoo.so
+# CHECK-IFS-NEXT: Target: x86_64-unknown-linux-gnu
+# CHECK-IFS-NEXT: Symbols:
+# CHECK-IFS-NEXT: - { Name: VER, Version: VER, Type: Object, Size: 0 }
+# CHECK-IFS-NEXT: VersionDefinitions:
+# CHECK-IFS-NEXT: - { Name: libfoo.so }
+# CHECK-IFS-NEXT: - { Name: VER }
+# CHECK-IFS-NEXT: ...
+
+# CHECK-ELF: OBJECT GLOBAL DEFAULT 1 VER@@VER
+
+# CHECK-ELF: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libfoo.so
+# CHECK-ELF: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VER
+
+--- !ifs-v1
+IfsVersion: 3.0
+SoName: libfoo.so
+Target: x86_64-unknown-linux-gnu
+Symbols:
+ - { Name: VER, Version: VER, Type: Object, Size: 0 }
+VersionDefinitions:
+ - { Name: libfoo.so }
+ - { Name: VER }
+...
>From ad1aa3a10b828b4181d9c58780890a1381ea2ebc Mon Sep 17 00:00:00 2001
From: ur4t <ur4t at protonmail.com>
Date: Fri, 9 Jan 2026 11:58:05 +0000
Subject: [PATCH 4/4] Sort VerDefs and VerNeeds
---
llvm/lib/InterfaceStub/ELFObjHandler.cpp | 5 ++-
.../llvm-ifs/binary-read-syms-gnu-hash.test | 2 +-
.../llvm-ifs/binary-read-syms-sysv-hash.test | 2 +-
.../llvm-ifs/strip-undefined-symbols.test | 2 +-
.../tools/llvm-ifs/version-symbol-need.ifs | 18 +++++----
llvm/tools/llvm-ifs/llvm-ifs.cpp | 37 +++++++++++++++++--
6 files changed, 50 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/InterfaceStub/ELFObjHandler.cpp b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
index 447f7ab736f96..4e7d17131efdf 100644
--- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp
+++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
@@ -359,7 +359,7 @@ template <class ELFT> class ELFStubBuilder {
WriteVerSym = any_of(Stub.Symbols, [](const IFSSymbol &Sym) {
return !Sym.Version.empty();
});
- WriteVerDef = !Stub.VersionDefinitions.empty();
+ WriteVerDef = Stub.VersionDefinitions.size() > 1;
WriteVerNeed = !Stub.VersionRequirements.empty();
std::vector<OutputSection<ELFT> *> Sections;
@@ -416,6 +416,9 @@ template <class ELFT> class ELFStubBuilder {
VerDef.Content.finalize();
VerDef.Size = VerDef.Content.getSize();
+ if (!WriteVerDef && WriteVerNeed)
+ Vdndx++;
+
// Populate symbol version requirement table.
for (const IFSVerNeed &IFSVerNeed : Stub.VersionRequirements) {
VerNeed.Content.addFile(IFSVerNeed.Names.size(),
diff --git a/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test b/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test
index 4d98e796af9f1..176afc655cc16 100644
--- a/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test
+++ b/llvm/test/tools/llvm-ifs/binary-read-syms-gnu-hash.test
@@ -20,6 +20,6 @@
# CHECK-NEXT: - { Name: _fini, Type: Func }
# CHECK-NEXT: - { Name: _init, Type: Func }
# CHECK-NEXT: VersionRequirements:
-# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
# CHECK-NEXT: - { File: ld-linux-x86-64.so.2, Names: [ GLIBC_2.3 ] }
+# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
# CHECK-NEXT: ...
diff --git a/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test b/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test
index 1763595309f6e..97a5f4f4a7f07 100644
--- a/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test
+++ b/llvm/test/tools/llvm-ifs/binary-read-syms-sysv-hash.test
@@ -20,6 +20,6 @@
# CHECK-NEXT: - { Name: _fini, Type: Func }
# CHECK-NEXT: - { Name: _init, Type: Func }
# CHECK-NEXT: VersionRequirements:
-# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
# CHECK-NEXT: - { File: ld-linux-x86-64.so.2, Names: [ GLIBC_2.3 ] }
+# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
# CHECK-NEXT: ...
diff --git a/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test b/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test
index 0e138bd12f0b3..89d3f4771ea40 100644
--- a/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test
+++ b/llvm/test/tools/llvm-ifs/strip-undefined-symbols.test
@@ -15,6 +15,6 @@
# CHECK-NEXT: - { Name: _fini, Type: Func }
# CHECK-NEXT: - { Name: _init, Type: Func }
# CHECK-NEXT: VersionRequirements:
-# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
# CHECK-NEXT: - { File: ld-linux-x86-64.so.2, Names: [ GLIBC_2.3 ] }
+# CHECK-NEXT: - { File: libc.so.6, Names: [ GLIBC_2.2.5 ] }
# CHECK-NEXT: ...
diff --git a/llvm/test/tools/llvm-ifs/version-symbol-need.ifs b/llvm/test/tools/llvm-ifs/version-symbol-need.ifs
index 881e6d1f1ca12..ec3fbb5d40d56 100644
--- a/llvm/test/tools/llvm-ifs/version-symbol-need.ifs
+++ b/llvm/test/tools/llvm-ifs/version-symbol-need.ifs
@@ -11,17 +11,17 @@
# CHECK-IFS-NEXT: NeededLibs:
# CHECK-IFS-NEXT: - libbar.so
# CHECK-IFS-NEXT: Symbols:
-# CHECK-IFS-NEXT: - { Name: func, Version: VER, Type: Func }
-# CHECK-IFS-NEXT: VersionDefinitions:
-# CHECK-IFS-NEXT: - { Name: libfoo.so }
+# CHECK-IFS-NEXT: - { Name: func, Version: VER_F, Type: Func }
+# CHECK-IFS-NEXT: - { Name: obj, Version: VER_O, Type: Object, Size: 0 }
# CHECK-IFS-NEXT: VersionRequirements:
-# CHECK-IFS-NEXT: - { File: libbar.so, Names: [ VER ] }
+# CHECK-IFS-NEXT: - { File: libbar.so, Names: [ VER_F, VER_O ] }
# CHECK-IFS-NEXT: ...
# CHECK-ELF: FUNC GLOBAL DEFAULT 1 func at VER
-# CHECK-ELF: Version: 1 File: libbar.so Cnt: 1
-# CHECK-ELF: Name: VER Flags: none Version: 2
+# CHECK-ELF: Version: 1 File: libbar.so Cnt: 2
+# CHECK-ELF: Name: VER_F Flags: none Version: 2
+# CHECK-ELF: Name: VER_O Flags: none Version: 3
--- !ifs-v1
IfsVersion: 3.0
@@ -30,9 +30,11 @@ Target: x86_64-unknown-linux-gnu
NeededLibs:
- libbar.so
Symbols:
- - { Name: func, Version: VER, Type: Func }
+ - { Name: obj, Version: VER_O, Type: Object, Size: 0 }
+ - { Name: func, Version: VER_F, Type: Func }
VersionDefinitions:
- { Name: libfoo.so }
VersionRequirements:
- - { File: libbar.so, Names: [ VER ] }
+ - { File: libbar.so, Names: [ VER_O ] }
+ - { File: libbar.so, Names: [ VER_F ] }
...
diff --git a/llvm/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp
index 1b90e5c11255e..7586e5f6359d4 100644
--- a/llvm/tools/llvm-ifs/llvm-ifs.cpp
+++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp
@@ -393,6 +393,8 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
// Attempt to merge input.
IFSStub Stub;
std::map<std::tuple<std::string, std::string>, IFSSymbol> SymbolMap;
+ std::map<std::string, std::set<std::string>> VersionDefinitionMap;
+ std::map<std::string, std::set<std::string>> VersionRequirementMap;
std::string PreviousInputFilePath;
for (const std::string &InputFilePath : Config.InputFilePaths) {
Expected<std::unique_ptr<IFSStub>> StubOrErr =
@@ -406,8 +408,6 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
Stub.Target = TargetStub->Target;
Stub.SoName = TargetStub->SoName;
Stub.NeededLibs = TargetStub->NeededLibs;
- Stub.VersionDefinitions = TargetStub->VersionDefinitions;
- Stub.VersionRequirements = TargetStub->VersionRequirements;
} else {
if (Stub.IfsVersion != TargetStub->IfsVersion) {
if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
@@ -487,6 +487,20 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
// TODO: Not checking Warning. Will be dropped.
}
+ for (const auto &[Name, Parents] : TargetStub->VersionDefinitions) {
+ if (TargetStub->SoName == Name)
+ continue;
+ auto [Iter, Inserted] = VersionDefinitionMap.insert({Name, {}});
+ auto &[Key, Value] = *Iter;
+ llvm::copy(Parents, std::inserter(Value, Value.end()));
+ }
+
+ for (const auto &[File, Names] : TargetStub->VersionRequirements) {
+ auto [Iter, Inserted] = VersionRequirementMap.insert({File, {}});
+ auto &[Key, Value] = *Iter;
+ llvm::copy(Names, std::inserter(Value, Value.end()));
+ }
+
PreviousInputFilePath = InputFilePath;
}
@@ -498,8 +512,23 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
return -1;
}
- for (auto &Entry : SymbolMap)
- Stub.Symbols.push_back(Entry.second);
+ for (const auto &[Name, Symbol] : SymbolMap)
+ Stub.Symbols.push_back(Symbol);
+
+ if (!VersionDefinitionMap.empty())
+ Stub.VersionDefinitions.push_back({Stub.SoName.value_or(""), {}});
+
+ for (const auto &[Name, ParentSet] : VersionDefinitionMap) {
+ std::vector<std::string> Parents;
+ llvm::copy(ParentSet, std::back_inserter(Parents));
+ Stub.VersionDefinitions.push_back({Name, std::move(Parents)});
+ }
+
+ for (const auto &[File, NameSet] : VersionRequirementMap) {
+ std::vector<std::string> Names;
+ llvm::copy(NameSet, std::back_inserter(Names));
+ Stub.VersionRequirements.push_back({File, std::move(Names)});
+ }
// Change SoName before emitting stubs.
if (Config.SoName)
More information about the llvm-commits
mailing list