diff --git a/llvm/docs/CommandGuide/llvm-ifs.rst b/llvm/docs/CommandGuide/llvm-ifs.rst index e3582b365b61d..d310e62b14eb6 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. @@ -81,12 +91,20 @@ A minimum ELF file that can be used by linker should have following sections pro * ELF header. +* Program headers. (optional) + * Section headers. * Dynamic symbol table (``.dynsym`` section). * 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 +117,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 +187,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..881a9d02c17aa 100644 --- a/llvm/include/llvm/InterfaceStub/IFSStub.h +++ b/llvm/include/llvm/InterfaceStub/IFSStub.h @@ -52,8 +52,10 @@ enum class IFSBitWidthType { struct IFSSymbol { IFSSymbol() = default; - explicit IFSSymbol(std::string SymbolName) : Name(std::move(SymbolName)) {} + explicit IFSSymbol(std::string SymbolName, std::string SymVer) + : Name(std::move(SymbolName)), Version(std::move(SymVer)) {} std::string Name; + std::string Version; std::optional Size; IFSSymbolType Type = IFSSymbolType::NoType; bool Undefined = false; @@ -62,6 +64,36 @@ struct IFSSymbol { bool operator<(const IFSSymbol &RHS) const { return Name < RHS.Name; } }; +struct IFSVerDef { + std::string Name; + std::vector Parents; +}; + +inline bool operator==(const IFSVerDef &Lhs, const IFSVerDef &Rhs) { + if (Lhs.Name != Rhs.Name || Lhs.Parents != Rhs.Parents) + return false; + return true; +} + +inline bool operator!=(const IFSVerDef &Lhs, const IFSVerDef &Rhs) { + return !(Lhs == Rhs); +} + +struct IFSVerNeed { + std::string File; + std::vector Names; +}; + +inline bool operator==(const IFSVerNeed &Lhs, const IFSVerNeed &Rhs) { + if (Lhs.File != Rhs.File || Lhs.Names != Rhs.Names) + return false; + return true; +} + +inline bool operator!=(const IFSVerNeed &Lhs, const IFSVerNeed &Rhs) { + return !(Lhs == Rhs); +} + struct IFSTarget { std::optional Triple; std::optional ObjectFormat; @@ -94,6 +126,8 @@ struct IFSStub { IFSTarget Target; std::vector NeededLibs; std::vector Symbols; + std::vector VersionDefinitions; + std::vector 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..7f57a8a5f4f82 100644 --- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp +++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp @@ -39,6 +39,12 @@ struct DynamicEntries { // Hash tables: std::optional ElfHash; std::optional GnuHash; + // Version tables: + std::optional VerSym; + std::optional VerDef; + std::optional VerDefNum; + std::optional VerNeed; + std::optional VerNeedNum; }; /// This initializes an ELF file header with information specific to a binary @@ -124,6 +130,148 @@ template class ELFSymbolTableBuilder { llvm::SmallVector Symbols; }; +template class ELFVersionSymbolBuilder { +public: + using Elf_Versym = typename ELFT::Versym; + + ELFVersionSymbolBuilder() { Versions.push_back({}); } + + void add(uint16_t Index) { + Elf_Versym VerSym; + VerSym.vs_index = Index; + Versions.push_back(VerSym); + } + + size_t getSize() const { return Versions.size() * sizeof(Elf_Versym); } + + void write(uint8_t *Buf) const { + memcpy(Buf, Versions.data(), Versions.size() * sizeof(Elf_Versym)); + } + +private: + llvm::SmallVector Versions; +}; + +template class ELFVersionDefinitionBuilder { +public: + using Elf_Verdef = typename ELFT::Verdef; + using Elf_Verdaux = typename ELFT::Verdaux; + + ELFVersionDefinitionBuilder() { VerDAux.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); + this->VerDef.push_back(VerDef); + this->VerDAux.push_back({}); + } + + void addAux(uint16_t Vdndx, uint32_t Name) { + Elf_Verdaux VerDAux; + VerDAux.vda_name = Name; + VerDAux.vda_next = sizeof(Elf_Verdaux); + this->VerDAux[Vdndx].push_back(VerDAux); + } + + void finalize() { + if (!VerDef.empty()) + VerDef.back().vd_next = 0; + for (llvm::SmallVector &VerDAux : VerDAux) + if (!VerDAux.empty()) + VerDAux.back().vda_next = 0; + } + + size_t getSize() const { + size_t Count = 0; + for (const llvm::SmallVector &I : VerDAux) + Count += I.size(); + return Count * sizeof(Elf_Verdaux) + VerDef.size() * sizeof(Elf_Verdef); + } + + void write(uint8_t *Buf) const { + uint8_t *Ptr = Buf; + size_t Count = 0; + for (const Elf_Verdef &VerDef : VerDef) { + Count = sizeof(Elf_Verdef); + memcpy(Ptr, &VerDef, Count); + Ptr += Count; + Count = VerDef.vd_cnt * sizeof(Elf_Verdaux); + memcpy(Ptr, VerDAux[VerDef.vd_ndx].data(), Count); + Ptr += Count; + } + } + +private: + llvm::SmallVector VerDef; + std::vector> VerDAux; +}; + +template 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); + this->VerNeed.push_back(VerNeed); + this->VerNAux.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); + this->VerNAux[Vnndx].push_back(VerNAux); + } + + void finalize() { + if (!VerNeed.empty()) + VerNeed.back().vn_next = 0; + for (llvm::SmallVector &VerNAux : VerNAux) + if (!VerNAux.empty()) + VerNAux.back().vna_next = 0; + } + + size_t getSize() const { + size_t Count = 0; + for (const llvm::SmallVector &I : VerNAux) + Count += I.size(); + return Count * sizeof(Elf_Vernaux) + VerNeed.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 : VerNeed) { + Count = sizeof(Elf_Verneed); + memcpy(Ptr, &VerNeed, Count); + Ptr += Count; + Count = VerNeed.vn_cnt * sizeof(Elf_Vernaux); + memcpy(Ptr, VerNAux[Vnndx].data(), Count); + Ptr += Count; + } + } + +private: + llvm::SmallVector VerNeed; + std::vector> VerNAux; +}; + template class ELFDynamicTableBuilder { public: using Elf_Dyn = typename ELFT::Dyn; @@ -188,17 +336,43 @@ template 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 *> Sections = {&DynSym, &DynStr, &DynTab, - &ShStrTab}; + for (const IFSSymbol &Sym : Stub.Symbols) + if (!Sym.Version.empty()) + WriteVerSym = true; + WriteVerDef = !Stub.VersionDefinitions.empty(); + WriteVerNeed = !Stub.VersionRequirements.empty(); + + std::vector *> Sections; + Sections.push_back(&DynSym); + Sections.push_back(&DynStr); + if (WriteVerSym) + Sections.push_back(&VerSym); + if (WriteVerNeed) + Sections.push_back(&VerDef); + if (WriteVerNeed) + Sections.push_back(&VerNeed); + Sections.push_back(&DynTab); + Sections.push_back(&ShStrTab); const OutputSection *LastSection = Sections.back(); // Now set the Index and put sections names into ".shstrtab". uint64_t Index = 1; @@ -224,18 +398,65 @@ template class ELFStubBuilder { } DynSym.Size = DynSym.Content.getSize(); + std::map 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); + uint64_t CurrentOffset = sizeof(Elf_Ehdr) + ElfPhnum * sizeof(Elf_Phdr); for (OutputSection *Sec : Sections) { Sec->Offset = alignTo(CurrentOffset, Sec->Align); Sec->Addr = Sec->Offset; @@ -244,18 +465,50 @@ template 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(ElfHeader, static_cast(*Stub.Target.Arch)); ElfHeader.e_shstrndx = ShStrTab.Index; ElfHeader.e_shnum = LastSection->Index + 1; + ElfHeader.e_phnum = ElfPhnum; + ElfHeader.e_phoff = sizeof(Elf_Ehdr); ElfHeader.e_shoff = alignTo(LastSection->Offset + LastSection->Size, sizeof(Elf_Addr)); + + ElfProgramHeaders[0].p_type = PT_LOAD; + ElfProgramHeaders[0].p_offset = 0; + ElfProgramHeaders[0].p_vaddr = 0; + ElfProgramHeaders[0].p_paddr = 0; + ElfProgramHeaders[0].p_filesz = DynTab.Shdr.sh_offset; + ElfProgramHeaders[0].p_memsz = DynTab.Shdr.sh_offset; + ElfProgramHeaders[0].p_flags = PF_R; + ElfProgramHeaders[0].p_align = 0x1000; + + ElfProgramHeaders[1].p_type = PT_DYNAMIC; + ElfProgramHeaders[1].p_offset = DynTab.Shdr.sh_offset; + ElfProgramHeaders[1].p_vaddr = DynTab.Shdr.sh_offset; + ElfProgramHeaders[1].p_paddr = DynTab.Shdr.sh_offset; + ElfProgramHeaders[1].p_filesz = DynTab.Shdr.sh_size; + ElfProgramHeaders[1].p_memsz = DynTab.Shdr.sh_size; + ElfProgramHeaders[1].p_flags = PF_R | PF_W; + ElfProgramHeaders[1].p_align = DynTab.Shdr.sh_addralign; } size_t getSize() const { @@ -264,22 +517,45 @@ template class ELFStubBuilder { void write(uint8_t *Data) const { write(Data, ElfHeader); + for (size_t I = 0; I < ElfPhnum; I++) + write(Data + ElfHeader.e_phoff + I * sizeof(Elf_Phdr), + ElfProgramHeaders[I]); DynSym.Content.write(Data + DynSym.Shdr.sh_offset); 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: Elf_Ehdr ElfHeader; + static constexpr auto ElfPhnum = 2; + Elf_Phdr ElfProgramHeaders[ElfPhnum]; ContentSection DynStr; ContentSection ShStrTab; ContentSection, ELFT> DynSym; ContentSection, ELFT> DynTab; + ContentSection, ELFT> VerSym; + ContentSection, ELFT> VerDef; + ContentSection, ELFT> VerNeed; + bool WriteVerSym = false; + bool WriteVerDef = false; + bool WriteVerNeed = false; template static void write(uint8_t *Data, const T &Value) { *reinterpret_cast(Data) = Value; @@ -298,6 +574,7 @@ template class ELFStubBuilder { StrTab.Shdr.sh_entsize = 0; StrTab.Shdr.sh_link = 0; } + void fillSymTabShdr(ContentSection, ELFT> &SymTab, uint32_t ShType) const { SymTab.Shdr.sh_type = ShType; @@ -314,6 +591,7 @@ template class ELFStubBuilder { SymTab.Shdr.sh_entsize = sizeof(Elf_Sym); SymTab.Shdr.sh_link = this->DynStr.Index; } + void fillDynTabShdr( ContentSection, ELFT> &DynTab) const { DynTab.Shdr.sh_type = SHT_DYNAMIC; @@ -327,6 +605,51 @@ template class ELFStubBuilder { DynTab.Shdr.sh_entsize = sizeof(Elf_Dyn); DynTab.Shdr.sh_link = this->DynStr.Index; } + + void fillVerSymShdr( + ContentSection, 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, 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, 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 &Sec) const { return ElfHeader.e_shoff + Sec.Index * sizeof(Elf_Shdr); } @@ -371,6 +694,12 @@ template 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 getDynStr() { if (DynSymHdr) return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs); @@ -386,11 +715,11 @@ template class DynSym { DynSym(const ELFFile &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) { 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 +807,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 +860,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 -static IFSSymbol createELFSym(StringRef SymName, +static IFSSymbol createELFSym(StringRef SymName, StringRef SymVer, const typename ELFT::Sym &RawSym) { - IFSSymbol TargetSym{std::string(SymName)}; + IFSSymbol TargetSym{std::string(SymName), std::string(SymVer)}; uint8_t Binding = RawSym.getBinding(); if (Binding == STB_WEAK) TargetSym.Weak = true; @@ -541,14 +887,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 -static Error populateSymbols(IFSStub &TargetStub, - const typename ELFT::SymRange DynSym, - StringRef DynStr) { +static Error +populateSymbols(IFSStub &TargetStub, const ELFFile &ElfFile, + const typename ELFT::SymRange DynSym, + const typename ELFT::Shdr *VerSymPtr, + const SmallVector> &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 +916,22 @@ static Error populateSymbols(IFSStub &TargetStub, Expected SymName = terminatedSubstr(DynStr, RawSym.st_name); if (!SymName) return SymName.takeError(); - IFSSymbol Sym = createELFSym(*SymName, RawSym); + std::string VersionName; + if (VerSymPtr) { + Expected VerEntryOrErr = + ElfFile.template getEntry(*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(*SymName, VersionName, RawSym); TargetStub.Symbols.push_back(std::move(Sym)); // TODO: Populate symbol warning. } @@ -577,6 +946,7 @@ buildStub(const ELFObjectFile &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 DestStub = std::make_unique(); const ELFFile &ElfFile = ElfObj.getELFFile(); // Fetch .dynamic table. @@ -627,11 +997,50 @@ buildStub(const ELFObjectFile &ElfObj) { DestStub->NeededLibs.push_back(std::string(*LibNameOrErr)); } + const Elf_Shdr *VerDefPtr = EDynSym->getVerDef(); + if (VerDefPtr) { + Expected> VerDefOrError = + ElfFile.getVersionDefinitions(*VerDefPtr); + if (!VerDefOrError) + return appendToError(VerDefOrError.takeError(), + "when reading symbol version definitions"); + for (const VerDef &VerDef : *VerDefOrError) { + std::vector Parents; + for (const VerdAux &VerDAux : VerDef.AuxV) { + Parents.push_back(VerDAux.Name); + } + DestStub->VersionDefinitions.push_back({VerDef.Name, std::move(Parents)}); + } + } + + const Elf_Shdr *VerNeedPtr = EDynSym->getVerNeed(); + if (VerNeedPtr) { + Expected> VerNeedOrError = + ElfFile.getVersionDependencies(*VerNeedPtr); + if (!VerNeedOrError) + return appendToError(VerNeedOrError.takeError(), + "when reading symbol version needs"); + for (const VerNeed &VerNeed : *VerNeedOrError) { + std::string File = VerNeed.File; + IFSVerNeed IFSVerNeed; + IFSVerNeed.File = VerNeed.File; + for (const VernAux &VerNAux : VerNeed.AuxV) { + IFSVerNeed.Names.push_back(VerNAux.Name.c_str()); + } + DestStub->VersionRequirements.push_back(IFSVerNeed); + } + } + // Populate Symbols from .dynsym table and dynamic string table. Expected SymCount = ElfFile.getDynSymtabSize(); if (!SymCount) return SymCount.takeError(); if (*SymCount > 0) { + Expected>> 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 DynSymPtr = EDynSym->getDynSym(); if (!DynSymPtr) @@ -639,7 +1048,9 @@ buildStub(const ELFObjectFile &ElfObj) { "when locating .dynsym section contents"); Elf_Sym_Range DynSyms = ArrayRef( reinterpret_cast(*DynSymPtr), *SymCount); - Error SymReadError = populateSymbols(*DestStub, DynSyms, DynStr); + Error SymReadError = + populateSymbols(*DestStub, ElfFile, DynSyms, EDynSym->getVerSym(), + *VersionsOrError, DynStr); if (SymReadError) return appendToError(std::move(SymReadError), "when reading dynamic symbols"); diff --git a/llvm/lib/InterfaceStub/IFSHandler.cpp b/llvm/lib/InterfaceStub/IFSHandler.cpp index 39dc46601b2a0..fa4b5506be4c3 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 { + 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 { static void enumeration(IO &IO, IFSSymbolType &SymbolType) { @@ -116,6 +132,7 @@ template <> struct MappingTraits { template <> struct MappingTraits { 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,44 @@ template <> struct MappingTraits { static const bool flow = true; // NOLINT(readability-identifier-naming) }; +/// YAML traits for ELFVersionDefinition. +template <> struct MappingTraits { + static void mapping(IO &IO, IFSVerDef &VerDef) { + IO.mapRequired("Name", VerDef.Name); + if (IO.outputting()) { + std::vector Parents; + for (const std::string &Parent : VerDef.Parents) { + Parents.push_back(Parent); + } + 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 { + static void mapping(IO &IO, IFSVerNeed &VerNeed) { + IO.mapRequired("File", VerNeed.File); + if (IO.outputting()) { + std::vector Names; + for (const std::string &Parent : VerNeed.Names) { + Names.push_back(Parent); + } + 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 { static void mapping(IO &IO, IFSStub &Stub) { @@ -145,6 +200,8 @@ template <> struct MappingTraits { 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 +215,8 @@ template <> struct MappingTraits { 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/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/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp index e12016c51e906..243ee47ad1c3c 100644 --- a/llvm/tools/llvm-ifs/llvm-ifs.cpp +++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp @@ -393,7 +393,7 @@ int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) { // Attempt to merge input. IFSStub Stub; - std::map SymbolMap; + std::map, IFSSymbol> SymbolMap; std::string PreviousInputFilePath; for (const std::string &InputFilePath : Config.InputFilePaths) { Expected> StubOrErr = @@ -407,6 +407,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()) { @@ -440,10 +442,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; 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);