diff --git a/llvm/test/tools/llvm-readobj/elf-invalid-versioning.test b/llvm/test/tools/llvm-readobj/elf-invalid-versioning.test --- a/llvm/test/tools/llvm-readobj/elf-invalid-versioning.test +++ b/llvm/test/tools/llvm-readobj/elf-invalid-versioning.test @@ -1,42 +1,311 @@ -# RUN: yaml2obj %s -o %t -# RUN: llvm-readelf -V %t | FileCheck %s --check-prefix=INVALID +## Here we test how llvm-readelf/llvm-readobj behave when inputs have +## invalid versioning sections. -# INVALID: Version symbols section '.gnu.version' contains 2 entries: -# INVALID-NEXT: Addr: 0000000000200210 Offset: 0x000040 Link: 5 (.dynsym) -# INVALID-NEXT: 000: 0 (*local*) 3 (*invalid*) +## In the first case we have a SHT_GNU_versym section that refers to +## a version listed in a SHT_GNU_verneed section. That version has an +## empty name, making it invalid. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readelf -V %t1 | FileCheck %s --check-prefix=GNU-VERNEED-NAME +# RUN: llvm-readobj -V %t1 | FileCheck %s --check-prefix=LLVM-VERNEED-NAME + +# GNU-VERNEED-NAME: Version symbols section '.gnu.version' contains 2 entries: +# GNU-VERNEED-NAME-NEXT: Addr: 0000000000200210 Offset: 0x000040 Link: 5 (.dynsym) +# GNU-VERNEED-NAME-NEXT: 000: 0 (*local*) 2 (*invalid*) + +# GNU-VERNEED-NAME: Version needs section '.gnu.version_r' contains 1 entries: +# GNU-VERNEED-NAME-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 6 (.dynstr) +# GNU-VERNEED-NAME-NEXT: 0x0000: Version: 1 File: somefile Cnt: 1 +# GNU-VERNEED-NAME-NEXT: 0x0010: Name: Flags: none Version: 2 + +# LLVM-VERNEED-NAME: VersionSymbols [ +# LLVM-VERNEED-NAME: Symbol { +# LLVM-VERNEED-NAME-NEXT: Version: 0 +# LLVM-VERNEED-NAME-NEXT: Name: +# LLVM-VERNEED-NAME-NEXT: } +# LLVM-VERNEED-NAME-NEXT: Symbol { +# LLVM-VERNEED-NAME-NEXT: Version: 2 +# LLVM-VERNEED-NAME-NEXT: Name: foo +# LLVM-VERNEED-NAME-NEXT: } +# LLVM-VERNEED-NAME-NEXT: ] + +# LLVM-VERNEED-NAME: VersionRequirements [ +# LLVM-VERNEED-NAME-NEXT: Dependency { +# LLVM-VERNEED-NAME-NEXT: Version: 1 +# LLVM-VERNEED-NAME-NEXT: Count: 1 +# LLVM-VERNEED-NAME-NEXT: FileName: somefile +# LLVM-VERNEED-NAME-NEXT: Entries [ +# LLVM-VERNEED-NAME-NEXT: Entry { +# LLVM-VERNEED-NAME-NEXT: Hash: 0 +# LLVM-VERNEED-NAME-NEXT: Flags: 0x0 +# LLVM-VERNEED-NAME-NEXT: Index: 2 +# LLVM-VERNEED-NAME-NEXT: Name: {{$}} +# LLVM-VERNEED-NAME-NEXT: } +# LLVM-VERNEED-NAME-NEXT: ] +# LLVM-VERNEED-NAME-NEXT: } +# LLVM-VERNEED-NAME-NEXT: ] --- !ELF FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_EXEC - Machine: EM_X86_64 - Entry: 0x0000000000201000 + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 Sections: - - Name: .gnu.version - Type: SHT_GNU_versym - Flags: [ SHF_ALLOC ] - Address: 0x0000000000200210 - Link: .dynsym - AddressAlign: 0x0000000000000002 - EntSize: 0x0000000000000002 - Entries: [ 0, 3 ] - - Name: .gnu.version_r - Type: SHT_GNU_verneed - Flags: [ SHF_ALLOC ] - Address: 0x0000000000200250 - Link: .dynstr - AddressAlign: 0x0000000000000004 - Info: 0x0000000000000001 + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Address: 0x200210 + Link: .dynsym + Entries: [ 0, 2 ] + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Link: .dynstr + Info: 1 + AddressAlign: 4 Dependencies: - - Version: 1 - File: somefile + - Version: 1 + File: somefile Entries: - - Name: '' # invalid name - Hash: 1937 - Flags: 233 - Other: 3 + - Name: '' ## invalid name + Hash: 0 + Flags: 0 + Other: 2 DynamicSymbols: - - Name: f - Binding: STB_GLOBAL + - Name: foo + Binding: STB_GLOBAL ... + +## In this case SHT_GNU_verneed is not linked to a dynamic string table. We check we handle +## this situation properly. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-readelf -V %t2 | FileCheck %s --check-prefix=GNU-NOLINK +# RUN: llvm-readobj -V %t2 | FileCheck %s --check-prefix=LLVM-NOLINK + +# 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: Version needs section '.gnu.version_r' contains 1 entries: +# GNU-NOLINK-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 0 () +# GNU-NOLINK-NEXT: 0x0000: Version: 1 File: Cnt: 1 +# GNU-NOLINK-NEXT: 0x0010: Name: Flags: none Version: 2 + +# LLVM-NOLINK: VersionSymbols [ +# LLVM-NOLINK: Symbol { +# LLVM-NOLINK-NEXT: Version: 0 +# LLVM-NOLINK-NEXT: Name: +# LLVM-NOLINK-NEXT: } +# LLVM-NOLINK-NEXT: Symbol { +# LLVM-NOLINK-NEXT: Version: 2 +# LLVM-NOLINK-NEXT: Name: foo@bar +# LLVM-NOLINK-NEXT: } +# LLVM-NOLINK-NEXT: ] + +# LLVM-NOLINK: VersionRequirements [ +# LLVM-NOLINK-NEXT: Dependency { +# LLVM-NOLINK-NEXT: Version: 1 +# LLVM-NOLINK-NEXT: Count: 1 +# LLVM-NOLINK-NEXT: FileName: +# LLVM-NOLINK-NEXT: Entries [ +# LLVM-NOLINK-NEXT: Entry { +# LLVM-NOLINK-NEXT: Hash: 0 +# LLVM-NOLINK-NEXT: Flags: 0 +# LLVM-NOLINK-NEXT: Index: 2 +# LLVM-NOLINK-NEXT: Name: +# LLVM-NOLINK-NEXT: } +# LLVM-NOLINK-NEXT: ] +# LLVM-NOLINK-NEXT: } +# LLVM-NOLINK-NEXT: ] + +--- !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: 0 + Info: 1 + AddressAlign: 4 + Dependencies: + - Version: 1 + File: somefile + Entries: + - Name: 'bar' + Hash: 0 + Flags: 0 + Other: 2 +DynamicSymbols: + - Name: foo + 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 + +--- !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: [ 2 ] + - Name: .gnu.version_r + 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 +DynamicSymbols: + - Name: foo + +## Here we check that we can properly dump the case when a dependency file name +## and/or a dependency name string offset is equal to the string table size. +## +## We set the version dependency vn_file field to the offset of string 'foo' in +## the .dynstr, which is 1. We create a custom string table .mystrtab of size 1 +## and link it with the .gnu.version_r section. For the vna_name we use the same trick. + +# RUN: yaml2obj --docnum=4 %s -o %t4 +# RUN: llvm-readobj --sections --section-data -V %t4 | FileCheck %s --check-prefix=LLVM-OFFSET-EQ +# RUN: llvm-readelf --sections -V %t4 | FileCheck %s --check-prefix=GNU-OFFSET-EQ + +# LLVM-OFFSET-EQ: Name: .mystrtab +# LLVM-OFFSET-EQ: Size: +# LLVM-OFFSET-EQ-SAME: 1 + +# LLVM-OFFSET-EQ: Name: .dynstr +# LLVM-OFFSET-EQ: SectionData ( +# LLVM-OFFSET-EQ-NEXT: 0000: 00666F6F 00 |.foo.| +# LLVM-OFFSET-EQ-NEXT: ) + +# LLVM-OFFSET-EQ: VersionRequirements [ +# LLVM-OFFSET-EQ-NEXT: Dependency { +# LLVM-OFFSET-EQ-NEXT: Version: 1 +# LLVM-OFFSET-EQ-NEXT: Count: 1 +# LLVM-OFFSET-EQ-NEXT: FileName: +# LLVM-OFFSET-EQ-NEXT: Entries [ +# LLVM-OFFSET-EQ-NEXT: Entry { +# LLVM-OFFSET-EQ-NEXT: Hash: 0 +# LLVM-OFFSET-EQ-NEXT: Flags: 0x0 +# LLVM-OFFSET-EQ-NEXT: Index: 0 +# LLVM-OFFSET-EQ-NEXT: Name: +# LLVM-OFFSET-EQ-NEXT: } +# LLVM-OFFSET-EQ-NEXT: ] +# LLVM-OFFSET-EQ-NEXT: } +# LLVM-OFFSET-EQ-NEXT: ] + +# GNU-OFFSET-EQ: Version needs section '.gnu.version_r' contains 1 entries: +# GNU-OFFSET-EQ-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 1 (.mystrtab) +# GNU-OFFSET-EQ-NEXT: 0x0000: Version: 1 File: Cnt: 1 +# GNU-OFFSET-EQ-NEXT: 0x0010: Name: Flags: none Version: 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .mystrtab + Type: SHT_STRTAB + Content: "00" + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .mystrtab + AddressAlign: 4 + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo + +## Here we check that we can properly dump the case when a dependency file name +## and/or a dependency name string offset is greater than the string table size. +## +# RUN: yaml2obj --docnum=5 %s -o %t5 +# RUN: llvm-readobj --sections -V %t5 | FileCheck %s --check-prefix=LLVM-OFFSET-GR +# RUN: llvm-readelf --sections -V %t5 | FileCheck %s --check-prefix=GNU-OFFSET-GR + +# LLVM-OFFSET-GR: VersionRequirements [ +# LLVM-OFFSET-GR-NEXT: Dependency { +# LLVM-OFFSET-GR-NEXT: Version: 1 +# LLVM-OFFSET-GR-NEXT: Count: 1 +# LLVM-OFFSET-GR-NEXT: FileName: +# LLVM-OFFSET-GR-NEXT: Entries [ +# LLVM-OFFSET-GR-NEXT: Entry { +# LLVM-OFFSET-GR-NEXT: Hash: 0 +# LLVM-OFFSET-GR-NEXT: Flags: 0x0 +# LLVM-OFFSET-GR-NEXT: Index: 0 +# LLVM-OFFSET-GR-NEXT: Name: +# LLVM-OFFSET-GR-NEXT: } +# LLVM-OFFSET-GR-NEXT: ] +# LLVM-OFFSET-GR-NEXT: } +# LLVM-OFFSET-GR-NEXT: ] + +# GNU-OFFSET-GR: Version needs section '.gnu.version_r' contains 1 entries: +# GNU-OFFSET-GR-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 1 (.mystrtab) +# GNU-OFFSET-GR-NEXT: 0x0000: Version: 1 File: Cnt: 1 +# GNU-OFFSET-GR-NEXT: 0x0010: Name: Flags: none Version: 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .mystrtab + Type: SHT_STRTAB + Content: "" + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .mystrtab + AddressAlign: 4 + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -667,6 +667,13 @@ 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(VernauxBuf); size_t Index = Vernaux->vna_other & ELF::VERSYM_VERSION; @@ -3921,10 +3928,13 @@ const Elf_Verneed *Verneed = reinterpret_cast(VerneedBuf); + StringRef File = StringTable.size() > Verneed->vn_file + ? StringTable.drop_front(Verneed->vn_file) + : ""; + OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", reinterpret_cast(Verneed) - SecData.begin(), - (unsigned)Verneed->vn_version, - StringTable.drop_front(Verneed->vn_file).data(), + (unsigned)Verneed->vn_version, File.data(), (unsigned)Verneed->vn_cnt); const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; @@ -3932,10 +3942,13 @@ const Elf_Vernaux *Vernaux = reinterpret_cast(VernauxBuf); + StringRef Name = StringTable.size() > Vernaux->vna_name + ? StringTable.drop_front(Vernaux->vna_name) + : ""; + OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", reinterpret_cast(Vernaux) - SecData.begin(), - StringTable.drop_front(Vernaux->vna_name).data(), - versionFlagToString(Vernaux->vna_flags).c_str(), + Name.data(), versionFlagToString(Vernaux->vna_flags).c_str(), (unsigned)Vernaux->vna_other); VernauxBuf += Vernaux->vna_next; } @@ -5693,8 +5706,11 @@ const uint8_t *SecData = reinterpret_cast(Obj->base() + Sec->sh_offset); - const Elf_Shdr *StrTab = + const Elf_Shdr *StrTabSec = unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + StringRef StringTable = { + reinterpret_cast(Obj->base() + StrTabSec->sh_offset), + (size_t)StrTabSec->sh_size}; const uint8_t *VerneedBuf = SecData; unsigned VerneedNum = Sec->sh_info; @@ -5704,9 +5720,11 @@ DictScope Entry(W, "Dependency"); W.printNumber("Version", Verneed->vn_version); W.printNumber("Count", Verneed->vn_cnt); - W.printString("FileName", - StringRef(reinterpret_cast( - Obj->base() + StrTab->sh_offset + Verneed->vn_file))); + + StringRef FileName = StringTable.size() > Verneed->vn_file + ? StringTable.drop_front(Verneed->vn_file) + : ""; + W.printString("FileName", FileName.data()); const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; ListScope L(W, "Entries"); @@ -5717,9 +5735,11 @@ W.printNumber("Hash", Vernaux->vna_hash); W.printEnum("Flags", Vernaux->vna_flags, makeArrayRef(SymVersionFlags)); W.printNumber("Index", Vernaux->vna_other); - W.printString("Name", - StringRef(reinterpret_cast( - Obj->base() + StrTab->sh_offset + Vernaux->vna_name))); + + StringRef Name = StringTable.size() > Vernaux->vna_name + ? StringTable.drop_front(Vernaux->vna_name) + : ""; + W.printString("Name", Name.data()); VernauxBuf += Vernaux->vna_next; } VerneedBuf += Verneed->vn_next;