[llvm] Add symbol version support to llvm-ifs (PR #163030)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 4 04:58:34 PDT 2026
https://github.com/ur4t updated https://github.com/llvm/llvm-project/pull/163030
>From 658bc2ab6cd7a45eb8832e27082a3a8a84ae80c2 Mon Sep 17 00:00:00 2001
From: ur4t <46435411+ur4t at users.noreply.github.com>
Date: Thu, 25 Jun 2026 03:08:46 +0000
Subject: [PATCH 1/2] Add ELF symbol version to IFS
---
llvm/docs/CommandGuide/llvm-ifs.md | 9 +
llvm/include/llvm/InterfaceStub/IFSStub.h | 27 +-
llvm/lib/InterfaceStub/ELFObjHandler.cpp | 403 +++++++++++++++----
llvm/lib/InterfaceStub/IFSHandler.cpp | 2 +
llvm/test/tools/llvm-ifs/versioned.ifs | 32 ++
llvm/tools/llvm-ifs/llvm-ifs.cpp | 9 +-
llvm/unittests/InterfaceStub/ELFYAMLTest.cpp | 9 +
7 files changed, 402 insertions(+), 89 deletions(-)
create mode 100644 llvm/test/tools/llvm-ifs/versioned.ifs
diff --git a/llvm/docs/CommandGuide/llvm-ifs.md b/llvm/docs/CommandGuide/llvm-ifs.md
index 9e2617d52e1754..6294fbf988551a 100644
--- a/llvm/docs/CommandGuide/llvm-ifs.md
+++ b/llvm/docs/CommandGuide/llvm-ifs.md
@@ -35,6 +35,7 @@ Symbols:
- { Name: sym2, Type: Func, Weak: false }
- { Name: sym3, Type: TLS }
- { Name: sym4, Type: Unknown, Warning: foo }
+ - { Name: sym5, Version: VER_1, Type: Func }
...
```
@@ -52,8 +53,10 @@ Symbols:
- `Symbols`: A collection of all data needed to link objects for each symbol, sorted by name in ascending order.
- `Name`: Symbol name.
+ - `Version` (optional): Symbol version.
- `Type`: Whether the symbol is an object, function, no-type, thread local storage, or unknown. Symbol types not explicitly supported are mapped as unknown to improve signal-to-noise ratio.
- `Size`: The size of the symbol in question, doesn't apply to functions, and is optional for NoType symbols.
+ - `Default`: Whether or not the symbol is a default version symbol in this shared object file.
- `Undefined`: Whether or not the symbol is defined in this shared object file.
- `Weak`: Whether or not the symbol should be treated as weak.
- `Warning` (optional): Warning text to output when this symbol is linked against.
@@ -76,6 +79,10 @@ 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)
+
- Dynamic table (`.dynamic` section).
- `DT_SYMTAB` entry.
@@ -83,6 +90,8 @@ A minimum ELF file that can be used by linker should have following sections pro
- `DT_STRSZ` entry.
- `DT_NEEDED` entries. (optional)
- `DT_SONAME` entry. (optional)
+ - `DT_VERSYM` entry. (optional)
+ - `DT_VERDEF` entry. (optional)
- Section header string table (`.shstrtab` section)
diff --git a/llvm/include/llvm/InterfaceStub/IFSStub.h b/llvm/include/llvm/InterfaceStub/IFSStub.h
index ddde889d468906..840a574191afd7 100644
--- a/llvm/include/llvm/InterfaceStub/IFSStub.h
+++ b/llvm/include/llvm/InterfaceStub/IFSStub.h
@@ -52,14 +52,18 @@ enum class IFSBitWidthType {
struct IFSSymbol {
IFSSymbol() = default;
- explicit IFSSymbol(std::string SymbolName) : Name(std::move(SymbolName)) {}
+ explicit IFSSymbol(const std::string &Name) : Name(Name) {}
std::string Name;
+ std::string Version;
std::optional<uint64_t> Size;
IFSSymbolType Type = IFSSymbolType::NoType;
+ bool Default = false;
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 IFSTarget {
@@ -70,21 +74,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 {
diff --git a/llvm/lib/InterfaceStub/ELFObjHandler.cpp b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
index 9c81a8832c0f2a..1affc037dd1ee4 100644
--- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp
+++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
@@ -39,6 +39,10 @@ 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;
};
/// This initializes an ELF file header with information specific to a binary
@@ -116,12 +120,90 @@ template <class ELFT> class ELFSymbolTableBuilder {
size_t getSize() const { return Symbols.size() * sizeof(Elf_Sym); }
+ void write(uint8_t *Buf) const { memcpy(Buf, Symbols.data(), getSize()); }
+
+private:
+ 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(), getSize()); }
+
+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 {
- memcpy(Buf, Symbols.data(), sizeof(Elf_Sym) * Symbols.size());
+ 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_Sym, 8> Symbols;
+ llvm::SmallVector<Elf_Verdef, 8> VerDefs;
+ llvm::SmallVector<llvm::SmallVector<Elf_Verdaux, 8>, 8> VerDAuxes;
};
template <class ELFT> class ELFDynamicTableBuilder {
@@ -177,9 +259,10 @@ template <class ELFT> class ELFStubBuilder {
using Elf_Dyn = typename ELFT::Dyn;
ELFStubBuilder(const ELFStubBuilder &) = delete;
- ELFStubBuilder(ELFStubBuilder &&) = default;
+ ELFStubBuilder(ELFStubBuilder &&) = delete;
+ ELFStubBuilder() = default;
- explicit ELFStubBuilder(const IFSStub &Stub) {
+ Error populate(const IFSStub &Stub) {
DynSym.Name = ".dynsym";
DynSym.Align = sizeof(Elf_Addr);
DynStr.Name = ".dynstr";
@@ -188,17 +271,33 @@ 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);
// Populate string tables.
- for (const IFSSymbol &Sym : Stub.Symbols)
+ for (const IFSSymbol &Sym : Stub.Symbols) {
DynStr.Content.add(Sym.Name);
+ if (!Sym.Version.empty()) {
+ DynStr.Content.add(Sym.Version);
+ HasVersion = true;
+ }
+ }
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};
+ std::vector<OutputSection<ELFT> *> Sections;
+ Sections.push_back(&DynSym);
+ Sections.push_back(&DynStr);
+ if (HasVersion) {
+ Sections.push_back(&VerSym);
+ Sections.push_back(&VerDef);
+ }
+ 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 +323,52 @@ template <class ELFT> class ELFStubBuilder {
}
DynSym.Size = DynSym.Content.getSize();
+ std::map<std::string, size_t> VerDefMap;
+ size_t Vdndx = 0;
+
+ {
+ // VER_NDX_LOCAL
+ Vdndx++;
+ // VER_NDX_GLOBAL
+ const std::string Name = Stub.SoName.value_or("");
+ VerDef.Content.addDef(Vdndx, 1, hashSysV(Name));
+ VerDef.Content.addAux(Vdndx, DynStr.Content.getOffset(Name));
+ Vdndx++;
+ }
+
+ // Populate dynamic symbol version table and version definition table.
+ for (const IFSSymbol &Sym : Stub.Symbols)
+ if (Sym.Version.empty())
+ VerSym.Content.add(VER_NDX_GLOBAL);
+ else {
+ const auto &[VI, Inserted] = VerDefMap.insert({Sym.Version, Vdndx});
+ const auto &[Name, Ndx] = *VI;
+ if (Inserted) {
+ VerDef.Content.addDef(Vdndx, 1, hashSysV(Name));
+ VerDef.Content.addAux(Vdndx, DynStr.Content.getOffset(Name));
+ Vdndx++;
+ }
+ VerSym.Content.add(Sym.Default ? Ndx : (Ndx | VERSYM_HIDDEN));
+ }
+ VerDef.Content.finalize();
+ VerDef.Size = VerDef.Content.getSize();
+ 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;
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 (HasVersion) {
+ VerSymIndex = DynTab.Content.addAddr(DT_VERSYM, 0);
+ VerDefIndex = DynTab.Content.addAddr(DT_VERDEF, 0);
+ }
DynTab.Size = DynTab.Content.getSize();
// Calculate sections' addresses and offsets.
uint64_t CurrentOffset = sizeof(Elf_Ehdr);
@@ -244,11 +380,19 @@ template <class ELFT> class ELFStubBuilder {
// Fill Addr back to dynamic table.
DynTab.Content.modifyAddr(DynSymIndex, DynSym.Addr);
DynTab.Content.modifyAddr(DynStrIndex, DynStr.Addr);
+ if (HasVersion) {
+ DynTab.Content.modifyAddr(VerSymIndex, VerSym.Addr);
+ DynTab.Content.modifyAddr(VerDefIndex, VerDef.Addr);
+ }
// Write section headers of string tables.
fillSymTabShdr(DynSym, SHT_DYNSYM);
fillStrTabShdr(DynStr, SHF_ALLOC);
fillDynTabShdr(DynTab);
fillStrTabShdr(ShStrTab);
+ if (HasVersion) {
+ fillVerSymShdr(VerSym);
+ fillVerDefShdr(VerDef, Vdndx);
+ }
// Finish initializing the ELF header.
initELFHeader<ELFT>(ElfHeader, static_cast<uint16_t>(*Stub.Target.Arch));
@@ -256,6 +400,8 @@ template <class ELFT> class ELFStubBuilder {
ElfHeader.e_shnum = LastSection->Index + 1;
ElfHeader.e_shoff =
alignTo(LastSection->Offset + LastSection->Size, sizeof(Elf_Addr));
+
+ return Error::success();
}
size_t getSize() const {
@@ -268,10 +414,18 @@ 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 (HasVersion)
+ VerSym.Content.write(Data + VerSym.Shdr.sh_offset);
+ if (HasVersion)
+ VerDef.Content.write(Data + VerDef.Shdr.sh_offset);
writeShdr(Data, DynSym);
writeShdr(Data, DynStr);
writeShdr(Data, DynTab);
writeShdr(Data, ShStrTab);
+ if (HasVersion)
+ writeShdr(Data, VerSym);
+ if (HasVersion)
+ writeShdr(Data, VerDef);
}
private:
@@ -280,6 +434,9 @@ 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;
+ bool HasVersion = false;
template <class T> static void write(uint8_t *Data, const T &Value) {
*reinterpret_cast<T *>(Data) = Value;
@@ -298,6 +455,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 +472,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 +486,36 @@ 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;
+ }
+
uint64_t shdrOffset(const OutputSection<ELFT> &Sec) const {
return ElfHeader.e_shoff + Sec.Index * sizeof(Elf_Shdr);
}
@@ -371,6 +560,10 @@ 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); }
+
Expected<StringRef> getDynStr() {
if (DynSymHdr)
return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs);
@@ -386,11 +579,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 +671,16 @@ 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;
}
}
@@ -515,17 +718,22 @@ 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 Default Whether the IFSSymbol is a default version symbol.
/// @param RawSym ELFT::Sym to extract symbol information from.
template <class ELFT>
-static IFSSymbol createELFSym(StringRef SymName,
+static IFSSymbol createELFSym(StringRef SymName, StringRef SymVer, bool Default,
const typename ELFT::Sym &RawSym) {
- IFSSymbol TargetSym{std::string(SymName)};
+ IFSSymbol TargetSym{SymName.str()};
uint8_t Binding = RawSym.getBinding();
if (Binding == STB_WEAK)
TargetSym.Weak = true;
else
TargetSym.Weak = false;
+ TargetSym.Version = SymVer;
+
+ TargetSym.Default = Default;
TargetSym.Undefined = RawSym.isUndefined();
TargetSym.Type = convertELFSymbolTypeToIFS(RawSym.st_info);
@@ -537,38 +745,6 @@ static IFSSymbol createELFSym(StringRef SymName,
return TargetSym;
}
-/// This function populates an IFSStub with symbols using information read
-/// from an ELF binary.
-///
-/// @param TargetStub IFSStub to add symbols to.
-/// @param DynSym Range of dynamic symbols to add to TargetStub.
-/// @param DynStr StringRef to the dynamic string table.
-template <class ELFT>
-static Error populateSymbols(IFSStub &TargetStub,
- const typename ELFT::SymRange DynSym,
- StringRef DynStr) {
- // Skips the first symbol since it's the NULL symbol.
- for (auto RawSym : DynSym.drop_front(1)) {
- // If a symbol does not have global or weak binding, ignore it.
- uint8_t Binding = RawSym.getBinding();
- if (!(Binding == STB_GLOBAL || Binding == STB_WEAK))
- continue;
- // If a symbol doesn't have default or protected visibility, ignore it.
- uint8_t Visibility = RawSym.getVisibility();
- if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED))
- continue;
- // Create an IFSSymbol and populate it with information from the symbol
- // table entry.
- Expected<StringRef> SymName = terminatedSubstr(DynStr, RawSym.st_name);
- if (!SymName)
- return SymName.takeError();
- IFSSymbol Sym = createELFSym<ELFT>(*SymName, RawSym);
- TargetStub.Symbols.push_back(std::move(Sym));
- // TODO: Populate symbol warning.
- }
- return Error::success();
-}
-
/// Returns a new IFSStub with all members populated from an ELFObjectFile.
/// @param ElfObj Source ELFObjectFile.
template <class ELFT>
@@ -577,6 +753,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,22 +804,76 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
DestStub->NeededLibs.push_back(std::string(*LibNameOrErr));
}
+ // [VER_NDX_LOCAL, VER_NDX_GLOBAL]
+ std::vector<std::string> Versions{{}, {}};
+
+ if (const Elf_Shdr *VerDefPtr = EDynSym->getVerDef()) {
+ Expected<std::vector<VerDef>> VerDefOrError =
+ ElfFile.getVersionDefinitions(*VerDefPtr);
+ if (!VerDefOrError)
+ return appendToError(VerDefOrError.takeError(),
+ "when reading dynamic symbol version definitions");
+ for (const VerDef &VerDef : *VerDefOrError) {
+ size_t N = VerDef.Ndx & VERSYM_VERSION;
+ if (N >= Versions.size())
+ Versions.resize(N + 1);
+ Versions[N] = VerDef.Name.c_str();
+ }
+ }
+
// Populate Symbols from .dynsym table and dynamic string table.
Expected<uint64_t> SymCount = ElfFile.getDynSymtabSize();
if (!SymCount)
return SymCount.takeError();
- if (*SymCount > 0) {
- // Get pointer to in-memory location of .dynsym section.
- Expected<const uint8_t *> DynSymPtr = EDynSym->getDynSym();
- if (!DynSymPtr)
- return appendToError(DynSymPtr.takeError(),
- "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);
- if (SymReadError)
- return appendToError(std::move(SymReadError),
- "when reading dynamic symbols");
+ if (*SymCount == 0)
+ return std::move(DestStub);
+
+ // Get pointer to in-memory location of .dynsym section.
+ Expected<const uint8_t *> DynSymPtr = EDynSym->getDynSym();
+ if (!DynSymPtr)
+ return appendToError(DynSymPtr.takeError(),
+ "when locating .dynsym section contents");
+ Elf_Sym_Range DynSyms = ArrayRef<Elf_Sym>(
+ reinterpret_cast<const Elf_Sym *>(*DynSymPtr), *SymCount);
+
+ size_t SymbolIndex = 0;
+ // Skips the first symbol since it's the NULL symbol.
+ for (const Elf_Sym &DynSym : DynSyms.drop_front(1)) {
+ SymbolIndex++;
+ // If a symbol does not have global or weak binding, ignore it.
+ uint8_t Binding = DynSym.getBinding();
+ if (!(Binding == STB_GLOBAL || Binding == STB_WEAK))
+ continue;
+ // If a symbol doesn't have default or protected visibility, ignore it.
+ uint8_t Visibility = DynSym.getVisibility();
+ if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED))
+ continue;
+ Expected<StringRef> SymName = terminatedSubstr(DynStr, DynSym.st_name);
+ if (!SymName)
+ return appendToError(SymName.takeError(), "when reading dynamic symbols");
+
+ bool Default = false;
+ std::string Version;
+ if (const Elf_Shdr *VerSymPtr = EDynSym->getVerSym()) {
+ using Elf_VerSym = typename ELFT::Versym;
+ Expected<const Elf_VerSym *> VerEntryOrErr =
+ ElfFile.template getEntry<Elf_VerSym>(*VerSymPtr, SymbolIndex);
+ if (!VerEntryOrErr)
+ return appendToError(VerEntryOrErr.takeError(),
+ "when reading symbol versions");
+ uint16_t VSIndex = (*VerEntryOrErr)->vs_index;
+ size_t VersionIndex = VSIndex & VERSYM_VERSION;
+ if (VersionIndex > VER_NDX_GLOBAL && VersionIndex < Versions.size()) {
+ Default = !(VSIndex & VERSYM_HIDDEN);
+ Version = Versions[VersionIndex];
+ if (Version.empty())
+ return createError(
+ "SHT_GNU_versym section refers to a version index " +
+ Twine(VersionIndex) + " which is missing");
+ }
+ }
+ DestStub->Symbols.push_back(
+ createELFSym<ELFT>(*SymName, Version, Default, DynSym));
}
return std::move(DestStub);
@@ -656,7 +887,9 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
template <class ELFT>
static Error writeELFBinaryToFile(StringRef FilePath, const IFSStub &Stub,
bool WriteIfChanged) {
- ELFStubBuilder<ELFT> Builder{Stub};
+ ELFStubBuilder<ELFT> Builder;
+ if (Error Err = Builder.populate(Stub))
+ return Err;
// Write Stub to memory first.
std::vector<uint8_t> Buf(Builder.getSize());
Builder.write(Buf.data());
@@ -695,13 +928,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 +950,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 39dc46601b2a02..9d284abb324e65 100644
--- a/llvm/lib/InterfaceStub/IFSHandler.cpp
+++ b/llvm/lib/InterfaceStub/IFSHandler.cpp
@@ -116,6 +116,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) {
@@ -126,6 +127,7 @@ template <> struct MappingTraits<IFSSymbol> {
} else if (Symbol.Type != IFSSymbolType::Func) {
IO.mapOptional("Size", Symbol.Size);
}
+ IO.mapOptional("Default", Symbol.Default, false);
IO.mapOptional("Undefined", Symbol.Undefined, false);
IO.mapOptional("Weak", Symbol.Weak, false);
IO.mapOptional("Warning", Symbol.Warning);
diff --git a/llvm/test/tools/llvm-ifs/versioned.ifs b/llvm/test/tools/llvm-ifs/versioned.ifs
new file mode 100644
index 00000000000000..6c9aebb815ed49
--- /dev/null
+++ b/llvm/test/tools/llvm-ifs/versioned.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, Default: true }
+# CHECK-IFS-NEXT: - { Name: def, Version: VER, Type: Func, Default: true }
+# CHECK-IFS-NEXT: - { Name: func, Version: VER, Type: Func }
+# CHECK-IFS-NEXT: ...
+
+# CHECK-ELF: OBJECT GLOBAL DEFAULT 1 VER@@VER
+# CHECK-ELF: FUNC GLOBAL DEFAULT 1 def@@VER
+# CHECK-ELF: FUNC GLOBAL DEFAULT 1 func at 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, Default: true }
+ - { Name: func, Version: VER, Type: Func }
+ - { Name: def, Version: VER, Type: Func, Default: true }
+...
diff --git a/llvm/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp
index 575a1117363fe5..2cadd5fe554cee 100644
--- a/llvm/tools/llvm-ifs/llvm-ifs.cpp
+++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp
@@ -379,7 +379,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 =
@@ -429,7 +429,8 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
}
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;
@@ -470,8 +471,8 @@ 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);
// Change SoName before emitting stubs.
if (Config.SoName)
diff --git a/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp b/llvm/unittests/InterfaceStub/ELFYAMLTest.cpp
index 39aec132db8617..efdb859ae7123a 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);
@@ -225,11 +226,19 @@ TEST(ElfYamlTextAPI, YAMLWritesTBESymbols) {
SymNot.Undefined = false;
SymNot.Weak = false;
+ IFSSymbol SymVer("ver");
+ SymVer.Version = "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 2821dc90c709d5a064a175b00a43837dba3e934e Mon Sep 17 00:00:00 2001
From: ur4t <46435411+ur4t at users.noreply.github.com>
Date: Sun, 4 Oct 2026 11:26:40 +0000
Subject: [PATCH 2/2] Fix VerDefNum and Default
---
llvm/lib/InterfaceStub/ELFObjHandler.cpp | 10 ++++++----
llvm/tools/llvm-ifs/llvm-ifs.cpp | 8 ++++++++
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/InterfaceStub/ELFObjHandler.cpp b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
index 1affc037dd1ee4..48b289e7c52b31 100644
--- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp
+++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp
@@ -17,6 +17,7 @@
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
+#include <map>
#include <optional>
using llvm::object::ELFObjectFile;
@@ -368,6 +369,7 @@ template <class ELFT> class ELFStubBuilder {
if (HasVersion) {
VerSymIndex = DynTab.Content.addAddr(DT_VERSYM, 0);
VerDefIndex = DynTab.Content.addAddr(DT_VERDEF, 0);
+ DynTab.Content.addValue(DT_VERDEFNUM, Vdndx - 1);
}
DynTab.Size = DynTab.Content.getSize();
// Calculate sections' addresses and offsets.
@@ -391,7 +393,7 @@ template <class ELFT> class ELFStubBuilder {
fillStrTabShdr(ShStrTab);
if (HasVersion) {
fillVerSymShdr(VerSym);
- fillVerDefShdr(VerDef, Vdndx);
+ fillVerDefShdr(VerDef, Vdndx - 1);
}
// Finish initializing the ELF header.
@@ -503,16 +505,16 @@ template <class ELFT> class ELFStubBuilder {
void fillVerDefShdr(
ContentSection<ELFVersionDefinitionBuilder<ELFT>, ELFT> &VerDef,
- size_t Size) const {
+ size_t VerDefNum) 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_info = VerDefNum;
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_entsize = 0;
VerDef.Shdr.sh_link = this->DynStr.Index;
}
diff --git a/llvm/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp
index 2cadd5fe554cee..24099a0841c419 100644
--- a/llvm/tools/llvm-ifs/llvm-ifs.cpp
+++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp
@@ -453,6 +453,14 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
return -1;
}
+ if (Symbol.Default != SI->second.Default) {
+ WithColor::error() << "Interface Stub: Default Mismatch for "
+ << Symbol.Name << ".\nFilename: " << InputFilePath
+ << "\nDefault Values: " << SI->second.Default << " "
+ << Symbol.Default << "\n";
+
+ return -1;
+ }
if (Symbol.Weak != SI->second.Weak) {
Symbol.Weak = false;
continue;
More information about the llvm-commits
mailing list