Commit e19f19b0 authored by Georgii Rymar's avatar Georgii Rymar
Browse files

[llvm-readobj/llvm-readelf] - Simplify the code that dumps versions.

After changes introduced in D70495 and D70826 its now possible
to significantly simplify the code we have.

This also fixes an issue: previous code assumed that version strings
should always be read from the dynamic string table. While it is
normally true, the string table should be taken from the corresponding
sh_link field.

Differential revision: https://reviews.llvm.org/D70855
parent 3d5ba7c6
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -262,3 +262,48 @@ Sections:
        Names: []
DynamicSymbols:
  - Name: foo

## Check we error out when trying to print version symbols, but SHT_GNU_verdef is invalid due to any reason.

# RUN: yaml2obj %s --docnum=10 -o %t10
# RUN: not llvm-readobj -V %t10 2>&1 | FileCheck %s --check-prefix=INVALID-VERDEF-LLVM -DFILE=%t10
# RUN: not llvm-readelf -V %t10 2>&1 | FileCheck %s --check-prefix=INVALID-VERDEF-GNU -DFILE=%t10

# INVALID-VERDEF-LLVM:      VersionSymbols [
# INVALID-VERDEF-LLVM-NEXT:    Symbol {
# INVALID-VERDEF-LLVM-NEXT:      Version: 0
# INVALID-VERDEF-LLVM-NEXT:      Name:
# INVALID-VERDEF-LLVM-NEXT:    }
# INVALID-VERDEF-LLVM-NEXT:    Symbol {
# INVALID-VERDEF-LLVM-EMPTY:
# INVALID-VERDEF-LLVM-NEXT:  error: '[[FILE]]': invalid SHT_GNU_verdef section with index 2: version definition 1 goes past the end of the section

# INVALID-VERDEF-GNU:      Version symbols section '.gnu.version' contains 2 entries:
# INVALID-VERDEF-GNU-NEXT:  Addr: 0000000000000000  Offset: 0x000040  Link: 5 (.dynsym)
# INVALID-VERDEF-GNU-NEXT:   000:   0 (*local*)
# INVALID-VERDEF-GNU-NEXT: error: '[[FILE]]': invalid SHT_GNU_verdef section with index 2: version definition 1 goes past the end of the section

--- !ELF
FileHeader:
  Class:   ELFCLASS64
  Data:    ELFDATA2LSB
  Type:    ET_DYN
  Machine: EM_X86_64
Sections:
  - Name:         .gnu.version
    Type:         SHT_GNU_versym
    Flags:        [ SHF_ALLOC ]
    Link:         .dynsym
    AddressAlign: 0x0000000000000002
    EntSize:      0x0000000000000002
    Entries:      [ 0, 2 ]
  - Name:         .gnu.version_d
    Type:         SHT_GNU_verdef
    Flags:        [ SHF_ALLOC ]
    Link:         .dynstr
    AddressAlign: 0x4
    Info:         0x1
    Entries: []
DynamicSymbols:
  - Name:    foo
    Binding: STB_GLOBAL
+76 −22
Original line number Diff line number Diff line
@@ -86,11 +86,12 @@ DynamicSymbols:

# GNU-NOLINK:      Version symbols section '.gnu.version' contains 2 entries:
# GNU-NOLINK-NEXT:  Addr: 0000000000000000  Offset: 0x000040  Link: 5 (.dynsym)
# GNU-NOLINK-NEXT:   000:   0 (*local*)       2 (bar)
# GNU-NOLINK-NEXT:   000:   0 (*local*)
# GNU-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL
# GNU-NOLINK-NEXT:   2 (<corrupt>)
# GNU-NOLINK-EMPTY:
# GNU-NOLINK:      Version needs section '.gnu.version_r' contains 1 entries:
# GNU-NOLINK-NEXT:  Addr: 0000000000000000  Offset: 0x000044  Link: 0 ()
# GNU-NOLINK-EMPTY:
# GNU-NOLINK-NEXT:  warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL
# GNU-NOLINK-NEXT:   0x0000: Version: 1  File: <corrupt vn_file: 9>  Cnt: 1
# GNU-NOLINK-NEXT:   0x0010:   Name: <corrupt>  Flags: none Version: 2

@@ -100,14 +101,14 @@ DynamicSymbols:
# LLVM-NOLINK-NEXT:     Name:
# LLVM-NOLINK-NEXT:   }
# LLVM-NOLINK-NEXT:   Symbol {
# LLVM-NOLINK-EMPTY:
# LLVM-NOLINK-NEXT:  warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL
# LLVM-NOLINK-NEXT:     Version: 2
# LLVM-NOLINK-NEXT:     Name: foo@bar
# LLVM-NOLINK-NEXT:     Name: foo@<corrupt>
# LLVM-NOLINK-NEXT:   }
# LLVM-NOLINK-NEXT: ]

# LLVM-NOLINK:      VersionRequirements [
# LLVM-NOLINK-EMPTY:
# LLVM-NOLINK-NEXT:  warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL
# LLVM-NOLINK-NEXT:   Dependency {
# LLVM-NOLINK-NEXT:     Version: 1
# LLVM-NOLINK-NEXT:     Count: 1
@@ -155,14 +156,12 @@ DynamicSymbols:
    Binding: STB_GLOBAL

## We can't parse misaligned auxiliary version records.
## Here we have a SHT_GNU_verneed section aligned by 1 byte.
## This makes the first auxiliary record offset % 4 be non-zero.

# RUN: yaml2obj --docnum=3 %s -o %t3
# RUN: not llvm-readelf -V %t3 2>&1 | FileCheck %s -DFILE=%t3 --check-prefix=BROKEN-AUX
# RUN: not llvm-readobj -V %t3 2>&1 | FileCheck %s -DFILE=%t3 --check-prefix=BROKEN-AUX

# BROKEN-AUX: error: '[[FILE]]': SHT_GNU_verneed: the vn_aux field of the entry with index 0 references a misaligned auxiliary record
# BROKEN-AUX: error: '[[FILE]]': invalid SHT_GNU_verneed section with index 2: found a misaligned auxiliary entry at offset 0x11

--- !ELF
FileHeader:
@@ -180,15 +179,10 @@ Sections:
    Type:         SHT_GNU_verneed
    Flags:        [ SHF_ALLOC ]
    Info:         1
    AddressAlign: 1
    Dependencies:
      - Version: 1
        File:    somefile
        Entries:
          - Name:  'bar'
            Hash:  0
            Flags: 0
            Other: 2
    Link:         .dynstr
    AddressAlign: 4
## The byte offset to the auxiliary entry is 0x11, i.e. it is not correctly aligned in memory.
    Content: "0100010001000000110000000000000000000000"
DynamicSymbols:
  - Name: foo

@@ -551,3 +545,63 @@ Sections:
            Other: 0
DynamicSymbols:
  - Name: foo

## In this case SHT_GNU_verneed is linked to a custom dynamic string table, which is not
## called ".dynstr". Check we handle this case properly.

# RUN: yaml2obj --docnum=13 %s -o %t13
# RUN: llvm-readelf -V %t13 2>&1 | FileCheck %s -DFILE=%t13 --check-prefix=GNU-CUSTOM-DYNSTR
# RUN: llvm-readobj -V %t13 2>&1 | FileCheck %s -DFILE=%t13 --check-prefix=LLVM-CUSTOM-DYNSTR

# GNU-CUSTOM-DYNSTR:      Version symbols section '.gnu.version' contains 2 entries:
# GNU-CUSTOM-DYNSTR-NEXT:  Addr: 0000000000000000  Offset: 0x000040  Link: 6 (.dynsym)
# GNU-CUSTOM-DYNSTR-NEXT:   000:   0 (*local*)       2 (bcdefghij)
# GNU-CUSTOM-DYNSTR:      Version needs section '.gnu.version_r' contains 1 entries:
# GNU-CUSTOM-DYNSTR-NEXT:  Addr: 0000000000000000  Offset: 0x000044  Link: 3 (.custom.dynstr)
# GNU-CUSTOM-DYNSTR-NEXT:   0x0000: Version: 1  File: j  Cnt: 1
# GNU-CUSTOM-DYNSTR-NEXT:   0x0010:   Name: bcdefghij  Flags: none  Version: 2

# LLVM-CUSTOM-DYNSTR:      VersionSymbols [
# LLVM-CUSTOM-DYNSTR:      Symbol {
# LLVM-CUSTOM-DYNSTR:        Version: 2
# LLVM-CUSTOM-DYNSTR-NEXT:   Name: foo@bcdefghij

# LLVM-CUSTOM-DYNSTR:      VersionRequirements [
# LLVM-CUSTOM-DYNSTR:        Dependency {
# LLVM-CUSTOM-DYNSTR:          Entries [
# LLVM-CUSTOM-DYNSTR:            Entry {
# LLVM-CUSTOM-DYNSTR:              Index: 2
# LLVM-CUSTOM-DYNSTR-NEXT:          Name: bcdefghij

--- !ELF
FileHeader:
  Class:   ELFCLASS64
  Data:    ELFDATA2LSB
  Type:    ET_EXEC
  Machine: EM_X86_64
Sections:
  - Name:    .gnu.version
    Type:    SHT_GNU_versym
    Flags:   [ SHF_ALLOC ]
    Link:    .dynsym
    Entries: [ 0, 2 ]
  - Name:         .gnu.version_r
    Type:         SHT_GNU_verneed
    Flags:        [ SHF_ALLOC ]
    Link:         .custom.dynstr
    Info:         1
    AddressAlign: 4
    Dependencies:
      - Version: 1
        File:    zed
        Entries:
          - Name:  'bar'
            Hash:  0
            Flags: 0
            Other: 2
  - Name: .custom.dynstr
    Type: SHT_STRTAB
    Content: "6162636465666768696a00" ## 'a','b','c','d','e','f','g','h','i','j',NIL
DynamicSymbols:
  - Name:    foo
    Binding: STB_GLOBAL
+45 −131
Original line number Diff line number Diff line
@@ -258,11 +258,8 @@ private:
  void loadDynamicTable(const ELFFile<ELFT> *Obj);
  void parseDynamicTable();

  StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb,
                             bool &IsDefault) const;
  StringRef getSymbolVersion(const Elf_Sym *symb, bool &IsDefault) const;
  void LoadVersionMap() const;
  void LoadVersionNeeds(const Elf_Shdr *ec) const;
  void LoadVersionDefs(const Elf_Shdr *sec) const;

  const object::ELFObjectFile<ELFT> *ObjF;
  DynRegionInfo DynRelRegion;
@@ -285,29 +282,11 @@ private:
  const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r
  const Elf_Shdr *SymbolVersionDefSection = nullptr; // .gnu.version_d

  // Records for each version index the corresponding Verdef or Vernaux entry.
  // This is filled the first time LoadVersionMap() is called.
  class VersionMapEntry : public PointerIntPair<const void *, 1> {
  public:
    // If the integer is 0, this is an Elf_Verdef*.
    // If the integer is 1, this is an Elf_Vernaux*.
    VersionMapEntry() : PointerIntPair<const void *, 1>(nullptr, 0) {}
    VersionMapEntry(const Elf_Verdef *verdef)
        : PointerIntPair<const void *, 1>(verdef, 0) {}
    VersionMapEntry(const Elf_Vernaux *vernaux)
        : PointerIntPair<const void *, 1>(vernaux, 1) {}

    bool isNull() const { return getPointer() == nullptr; }
    bool isVerdef() const { return !isNull() && getInt() == 0; }
    bool isVernaux() const { return !isNull() && getInt() == 1; }
    const Elf_Verdef *getVerdef() const {
      return isVerdef() ? (const Elf_Verdef *)getPointer() : nullptr;
    }
    const Elf_Vernaux *getVernaux() const {
      return isVernaux() ? (const Elf_Vernaux *)getPointer() : nullptr;
    }
  struct VersionEntry {
    std::string Name;
    bool IsVerDef;
  };
  mutable SmallVector<VersionMapEntry, 16> VersionMap;
  mutable SmallVector<Optional<VersionEntry>, 16> VersionMap;

public:
  Elf_Dyn_Range dynamic_table() const {
@@ -340,8 +319,7 @@ public:
                                           unsigned SectionIndex) const;
  Expected<std::string> getStaticSymbolName(uint32_t Index) const;
  std::string getDynamicString(uint64_t Value) const;
  StringRef getSymbolVersionByIndex(StringRef StrTab,
                                    uint32_t VersionSymbolIndex,
  StringRef getSymbolVersionByIndex(uint32_t VersionSymbolIndex,
                                    bool &IsDefault) const;

  void printSymbolsHelper(bool IsDynamic) const;
@@ -909,78 +887,6 @@ std::error_code createELFDumper(const object::ObjectFile *Obj,

} // end namespace llvm

// Iterate through the versions needed section, and place each Elf_Vernaux
// in the VersionMap according to its index.
template <class ELFT>
void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *Sec) const {
  unsigned VerneedSize = Sec->sh_size;    // Size of section in bytes
  unsigned VerneedEntries = Sec->sh_info; // Number of Verneed entries
  const uint8_t *VerneedStart = reinterpret_cast<const uint8_t *>(
      ObjF->getELFFile()->base() + Sec->sh_offset);
  const uint8_t *VerneedEnd = VerneedStart + VerneedSize;
  // The first Verneed entry is at the start of the section.
  const uint8_t *VerneedBuf = VerneedStart;
  for (unsigned VerneedIndex = 0; VerneedIndex < VerneedEntries;
       ++VerneedIndex) {
    if (VerneedBuf + sizeof(Elf_Verneed) > VerneedEnd)
      report_fatal_error("Section ended unexpectedly while scanning "
                         "version needed records.");
    const Elf_Verneed *Verneed =
        reinterpret_cast<const Elf_Verneed *>(VerneedBuf);
    if (Verneed->vn_version != ELF::VER_NEED_CURRENT)
      report_fatal_error("Unexpected verneed version");
    // Iterate through the Vernaux entries
    const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux;
    for (unsigned VernauxIndex = 0; VernauxIndex < Verneed->vn_cnt;
         ++VernauxIndex) {
      if (VernauxBuf + sizeof(Elf_Vernaux) > VerneedEnd)
        report_fatal_error("Section ended unexpected while scanning auxiliary "
                           "version needed records.");
      if ((ptrdiff_t)VernauxBuf % sizeof(uint32_t) != 0)
        reportError(createError("SHT_GNU_verneed: the vn_aux field of the "
                                "entry with index " +
                                Twine(VerneedIndex) +
                                " references a misaligned auxiliary record"),
                    ObjF->getFileName());

      const Elf_Vernaux *Vernaux =
          reinterpret_cast<const Elf_Vernaux *>(VernauxBuf);
      size_t Index = Vernaux->vna_other & ELF::VERSYM_VERSION;
      if (Index >= VersionMap.size())
        VersionMap.resize(Index + 1);
      VersionMap[Index] = VersionMapEntry(Vernaux);
      VernauxBuf += Vernaux->vna_next;
    }
    VerneedBuf += Verneed->vn_next;
  }
}

// Iterate through the version definitions, and place each Elf_Verdef
// in the VersionMap according to its index.
template <class ELFT>
void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *Sec) const {
  unsigned VerdefSize = Sec->sh_size;    // Size of section in bytes
  unsigned VerdefEntries = Sec->sh_info; // Number of Verdef entries
  const uint8_t *VerdefStart = reinterpret_cast<const uint8_t *>(
      ObjF->getELFFile()->base() + Sec->sh_offset);
  const uint8_t *VerdefEnd = VerdefStart + VerdefSize;
  // The first Verdef entry is at the start of the section.
  const uint8_t *VerdefBuf = VerdefStart;
  for (unsigned VerdefIndex = 0; VerdefIndex < VerdefEntries; ++VerdefIndex) {
    if (VerdefBuf + sizeof(Elf_Verdef) > VerdefEnd)
      report_fatal_error("Section ended unexpectedly while scanning "
                         "version definitions.");
    const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf);
    if (Verdef->vd_version != ELF::VER_DEF_CURRENT)
      report_fatal_error("Unexpected verdef version");
    size_t Index = Verdef->vd_ndx & ELF::VERSYM_VERSION;
    if (Index >= VersionMap.size())
      VersionMap.resize(Index + 1);
    VersionMap[Index] = VersionMapEntry(Verdef);
    VerdefBuf += Verdef->vd_next;
  }
}

template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const {
  // If there is no dynamic symtab or version table, there is nothing to do.
  if (!DynSymRegion.Addr || !SymbolVersionSection)
@@ -992,19 +898,37 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const {

  // The first two version indexes are reserved.
  // Index 0 is LOCAL, index 1 is GLOBAL.
  VersionMap.push_back(VersionMapEntry());
  VersionMap.push_back(VersionMapEntry());
  VersionMap.push_back(VersionEntry());
  VersionMap.push_back(VersionEntry());

  if (SymbolVersionDefSection)
    LoadVersionDefs(SymbolVersionDefSection);
  auto InsertEntry = [this](unsigned N, StringRef Version, bool IsVerdef) {
    if (N >= VersionMap.size())
      VersionMap.resize(N + 1);
    VersionMap[N] = {Version, IsVerdef};
  };

  if (SymbolVersionNeedSection)
    LoadVersionNeeds(SymbolVersionNeedSection);
  if (SymbolVersionDefSection) {
    Expected<std::vector<VerDef>> Defs =
        this->getVersionDefinitions(SymbolVersionDefSection);
    if (!Defs)
      reportError(Defs.takeError(), ObjF->getFileName());
    for (const VerDef &Def : *Defs)
      InsertEntry(Def.Ndx & ELF::VERSYM_VERSION, Def.Name, true);
  }

  if (SymbolVersionNeedSection) {
    Expected<std::vector<VerNeed>> Deps =
        this->getVersionDependencies(SymbolVersionNeedSection);
    if (!Deps)
      reportError(Deps.takeError(), ObjF->getFileName());
    for (const VerNeed &Dep : *Deps)
      for (const VernAux &Aux : Dep.AuxV)
        InsertEntry(Aux.Other & ELF::VERSYM_VERSION, Aux.Name, false);
  }
}

template <typename ELFT>
StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
                                            const Elf_Sym *Sym,
StringRef ELFDumper<ELFT>::getSymbolVersion(const Elf_Sym *Sym,
                                            bool &IsDefault) const {
  // This is a dynamic symbol. Look in the GNU symbol version table.
  if (!SymbolVersionSection) {
@@ -1022,7 +946,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
  const Elf_Versym *Versym = unwrapOrError(
      ObjF->getFileName(), ObjF->getELFFile()->template getEntry<Elf_Versym>(
                               SymbolVersionSection, EntryIndex));
  return this->getSymbolVersionByIndex(StrTab, Versym->vs_index, IsDefault);
  return this->getSymbolVersionByIndex(Versym->vs_index, IsDefault);
}

static std::string maybeDemangle(StringRef Name) {
@@ -1049,8 +973,7 @@ ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
}

template <typename ELFT>
StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(StringRef StrTab,
                                                   uint32_t SymbolVersionIndex,
StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(uint32_t SymbolVersionIndex,
                                                   bool &IsDefault) const {
  size_t VersionIndex = SymbolVersionIndex & VERSYM_VERSION;

@@ -1062,23 +985,15 @@ StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(StringRef StrTab,

  // Lookup this symbol in the version table.
  LoadVersionMap();
  if (VersionIndex >= VersionMap.size() || VersionMap[VersionIndex].isNull())
  if (VersionIndex >= VersionMap.size() || !VersionMap[VersionIndex])
    reportError(createError("Invalid version entry"), ObjF->getFileName());
  const VersionMapEntry &Entry = VersionMap[VersionIndex];

  // Get the version name string.
  size_t NameOffset;
  if (Entry.isVerdef()) {
    // The first Verdaux entry holds the name.
    NameOffset = Entry.getVerdef()->getAux()->vda_name;
  const VersionEntry &Entry = *VersionMap[VersionIndex];
  if (Entry.IsVerDef)
    IsDefault = !(SymbolVersionIndex & VERSYM_HIDDEN);
  } else {
    NameOffset = Entry.getVernaux()->vna_name;
  else
    IsDefault = false;
  }
  if (NameOffset >= StrTab.size())
    reportError(createError("Invalid string offset"), ObjF->getFileName());
  return StrTab.data() + NameOffset;
  return Entry.Name.c_str();
}

template <typename ELFT>
@@ -1109,7 +1024,7 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
    return SymbolName;

  bool IsDefault;
  StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault);
  StringRef Version = getSymbolVersion(&*Symbol, IsDefault);
  if (!Version.empty()) {
    SymbolName += (IsDefault ? "@@" : "@");
    SymbolName += Version;
@@ -4100,7 +4015,6 @@ void GNUStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj,
  const uint8_t *VersymBuf =
      reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset);
  const ELFDumper<ELFT> *Dumper = this->dumper();
  StringRef StrTable = Dumper->getDynamicStringTable();

  // readelf prints 4 entries per line.
  for (uint64_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) {
@@ -4119,17 +4033,17 @@ void GNUStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj,
        OS << "   1 (*global*)   ";
        break;
      default:
        OS << format("%4x%c", Versym->vs_index & VERSYM_VERSION,
                     Versym->vs_index & VERSYM_HIDDEN ? 'h' : ' ');

        bool IsDefault = true;
        std::string VersionName = Dumper->getSymbolVersionByIndex(
            StrTable, Versym->vs_index, IsDefault);
        std::string VersionName =
            Dumper->getSymbolVersionByIndex(Versym->vs_index, IsDefault);

        if (!VersionName.empty())
          VersionName = "(" + VersionName + ")";
        else
          VersionName = "(*invalid*)";

        OS << format("%4x%c", Versym->vs_index & VERSYM_VERSION,
                     Versym->vs_index & VERSYM_HIDDEN ? 'h' : ' ');
        OS << left_justify(VersionName, 13);
      }
      VersymBuf += sizeof(Elf_Versym);