diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test b/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test --- a/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test +++ b/llvm/test/tools/llvm-readobj/ELF/hash-symbols.test @@ -509,3 +509,131 @@ Sections: - Section: .gnu.hash - Section: .dynamic + +## Check the behavior when the dynamic symbol table is empty or not found. + +## Case A.1: Check we report a warning when the dynamic symbol table is empty and we attempt to print hash symbols +## from the .hash table. The number of symbols in the dynamic symbol table can be calculated from its size +## or derived from the Chain vector of the .hash table. Make both ways to return a zero to do the check. +# RUN: yaml2obj --docnum=9 %s -o %t9.1.so +# RUN: llvm-readelf --hash-symbols %t9.1.so 2>&1 | FileCheck %s -DFILE=%t9.1.so --check-prefix=DYNSYM-EMPTY-HASH + +# DYNSYM-EMPTY-HASH: Symbol table of .hash for image: +# DYNSYM-EMPTY-HASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# DYNSYM-EMPTY-HASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .hash table: the dynamic symbol table is empty +# DYNSYM-EMPTY-HASH-NOT: {{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH + Flags: [ SHF_ALLOC ] + Bucket: [ 0 ] + Chain: [ ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Entries: + - Tag: DT_HASH + Value: 0x0 + - Tag: DT_STRTAB +## PT_LOAD p_offset == .hash offset == 0x54. +## 0x54 + 0x2c == 0x80 == .dynstr offset. + Value: 0x2c + - Tag: DT_STRSZ + Value: 0x1 + - Tag: DT_NULL + Value: 0x0 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + - Name: .dynsym + Type: [[DYNSYMTYPE=SHT_DYNSYM]] + Flags: [ SHF_ALLOC ] + Size: 0 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .hash + - Section: .dynamic + - Section: .dynstr + +## Case A.2: similar to A.1, but now check that we report a warning when the dynamic symbol table was not found. +## To do that, set the type of the .dynsym to SHT_PROGBITS to hide it. +# RUN: yaml2obj --docnum=9 -DDYNSYMTYPE=SHT_PROGBITS %s -o %t9.2.so +# RUN: llvm-readelf --hash-symbols %t9.2.so 2>&1 | FileCheck %s -DFILE=%t9.2.so --check-prefix=DYNSYM-NOTFOUND-HASH + +# DYNSYM-NOTFOUND-HASH: Symbol table of .hash for image: +# DYNSYM-NOTFOUND-HASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# DYNSYM-NOTFOUND-HASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .hash table: the dynamic symbol table was not found +# DYNSYM-NOTFOUND-HASH-NOT: {{.}} + +## Case B.1: Check we report a warning when the dynamic symbol table is empty and we attempt to print +## hash symbols from the .gnu.hash table. +# RUN: yaml2obj --docnum=10 %s -o %t10.1.so +# RUN: llvm-readelf --hash-symbols %t10.1.so 2>&1 | FileCheck %s -DFILE=%t10.1.so --check-prefix=DYNSYM-EMPTY-GNUHASH + +# DYNSYM-EMPTY-GNUHASH: Symbol table of .gnu.hash for image: +# DYNSYM-EMPTY-GNUHASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# DYNSYM-EMPTY-GNUHASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .gnu.hash table: the dynamic symbol table is empty +# DYNSYM-EMPTY-GNUHASH-NOT: {{.}} + +## Case B.2: similar to B.1, but now check that we report a warning when the dynamic symbol table was not found. +## To do that, set the type of the .dynsym to SHT_PROGBITS to hide it. +# RUN: yaml2obj --docnum=10 -DDYNSYMTYPE=SHT_PROGBITS %s -o %t10.2.so +# RUN: llvm-readelf --hash-symbols %t10.2.so 2>&1 | FileCheck %s -DFILE=%t10.2.so --check-prefix=DYNSYM-NOTFOUND-GNUHASH + +# DYNSYM-NOTFOUND-GNUHASH: Symbol table of .gnu.hash for image: +# DYNSYM-NOTFOUND-GNUHASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# DYNSYM-NOTFOUND-GNUHASH-NEXT: warning: '[[FILE]]': unable to print symbols for the .gnu.hash table: the dynamic symbol table was not found +# DYNSYM-NOTFOUND-GNUHASH-NOT: {{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x1 ] + HashValues: [ 0x0 ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Entries: + - Tag: DT_GNU_HASH + Value: 0x0 + - Tag: DT_STRTAB +## PT_LOAD p_offset == .hash offset == 0x54. +## 0x54 + 0x3c == 0x80 == .dynstr offset. + Value: 0x3c + - Tag: DT_STRSZ + Value: 0x1 + - Tag: DT_NULL + Value: 0x0 + - Name: .dynstr + Type: SHT_STRTAB + - Name: .dynsym + Type: [[DYNSYMTYPE=SHT_DYNSYM]] + Flags: [ SHF_ALLOC ] + Size: 0 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic + - Section: .dynstr 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 @@ -4066,7 +4066,7 @@ if (this->dumper()->getDynamicStringTable().empty()) return; auto StringTable = this->dumper()->getDynamicStringTable(); - auto DynSyms = this->dumper()->dynamic_symbols(); + Elf_Sym_Range DynSyms = this->dumper()->dynamic_symbols(); auto PrintHashTable = [&](const Elf_Hash *SysVHash) { if (ELFT::Is64Bits) @@ -4075,6 +4075,16 @@ OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; OS << "\n"; + const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + if (!FirstSym) { + Optional DynSymRegion = this->dumper()->getDynSymRegion(); + this->reportUniqueWarning( + createError(Twine("unable to print symbols for the .hash table: the " + "dynamic symbol table ") + + (DynSymRegion ? "is empty" : "was not found"))); + return; + } + auto Buckets = SysVHash->buckets(); auto Chains = SysVHash->chains(); for (uint32_t Buc = 0; Buc < SysVHash->nbucket; Buc++) { @@ -4093,7 +4103,7 @@ break; } - printHashedSymbol(Obj, &DynSyms[0], Ch, StringTable, Buc); + printHashedSymbol(Obj, FirstSym, Ch, StringTable, Buc); Visited[Ch] = true; } } @@ -4124,6 +4134,16 @@ return; } + const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + if (!FirstSym) { + Optional DynSymRegion = this->dumper()->getDynSymRegion(); + this->reportUniqueWarning(createError( + Twine("unable to print symbols for the .gnu.hash table: the " + "dynamic symbol table ") + + (DynSymRegion ? "is empty" : "was not found"))); + return; + } + auto Buckets = GnuHash->buckets(); for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) { if (Buckets[Buc] == ELF::STN_UNDEF) @@ -4132,7 +4152,7 @@ uint32_t GnuHashable = Index - GnuHash->symndx; // Print whole chain while (true) { - printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc); + printHashedSymbol(Obj, FirstSym, Index++, StringTable, Buc); // Chain ends at symbol with stopper bit if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1) break;