[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