Index: llvm/test/tools/llvm-readobj/ELF/gnuhash.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/gnuhash.test +++ llvm/test/tools/llvm-readobj/ELF/gnuhash.test @@ -100,3 +100,217 @@ Sections: - Section: .gnu.hash - Section: .dynamic + +## Check we report a warning if there is no dynamic symbol section in the object. + +# RUN: yaml2obj --docnum=3 %s -o %t.nodynsym +# RUN: llvm-readobj --gnu-hash-table %t.nodynsym 2>&1 | FileCheck %s -DFILE=%t.nodynsym --check-prefix=NODYNSYM +# RUN: llvm-readelf --gnu-hash-table %t.nodynsym 2>&1 | FileCheck %s -DFILE=%t.nodynsym --check-prefix=NODYNSYM + +# NODYNSYM: GnuHashTable { +# NODYNSYM-NEXT: Num Buckets: 1 +# NODYNSYM-NEXT: First Hashed Symbol Index: 0 +# NODYNSYM-NEXT: Num Mask Words: 1 +# NODYNSYM-NEXT: Shift Count: 0 +# NODYNSYM-NEXT: Bloom Filter: [0x0] +# NODYNSYM-NEXT: Buckets: [0] +# NODYNSYM-NEXT: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: no dynamic symbol table found +# NODYNSYM-NEXT: } + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0 ] + HashValues: [ 0x0 ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Entries: + - Tag: DT_GNU_HASH + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic + +## Check what we do when the index of the first symbol in the dynamic symbol table +## included in the hash table is larger than the number of dynamic symbols. + +# RUN: yaml2obj --docnum=4 %s -o %t.brokensymndx +# RUN: llvm-readobj --gnu-hash-table %t.brokensymndx 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.brokensymndx --check-prefix=SYMNDX +# RUN: llvm-readelf --gnu-hash-table %t.brokensymndx 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.brokensymndx --check-prefix=SYMNDX + +# SYMNDX: GnuHashTable { +# SYMNDX-NEXT: Num Buckets: 1 +# SYMNDX-NEXT: First Hashed Symbol Index: 2 +# SYMNDX-NEXT: Num Mask Words: 1 +# SYMNDX-NEXT: Shift Count: 0 +# SYMNDX-NEXT: Bloom Filter: [0x1] +# SYMNDX-NEXT: Buckets: [2] +# SYMNDX-NEXT: warning: '[[FILE]]': the first hashed symbol index (2) is larger than the number of dynamic symbols (2) +# SYMNDX-NEXT: } + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x2 + Shift2: 0x0 + BloomFilter: [ 0x1 ] + HashBuckets: [ 0x2 ] + HashValues: [ 0x3 ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Link: 0 + Entries: + - Tag: DT_GNU_HASH + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 +DynamicSymbols: + - Name: aaa + Binding: STB_GLOBAL +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic + +## Check we emit a warning when the dynamic symbol table is empty. +## A valid dynamic symbol table should have at least one symbol: the symbol with index 0. + +# RUN: yaml2obj --docnum=5 %s -o %t.emptydynsym +# RUN: llvm-readobj --gnu-hash-table %t.emptydynsym 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.emptydynsym --check-prefix=EMPTY-DYNSYM +# RUN: llvm-readelf --gnu-hash-table %t.emptydynsym 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.emptydynsym --check-prefix=EMPTY-DYNSYM + +# EMPTY-DYNSYM: GnuHashTable { +# EMPTY-DYNSYM-NEXT: Num Buckets: 1 +# EMPTY-DYNSYM-NEXT: First Hashed Symbol Index: 0 +# EMPTY-DYNSYM-NEXT: Num Mask Words: 1 +# EMPTY-DYNSYM-NEXT: Shift Count: 0 +# EMPTY-DYNSYM-NEXT: Bloom Filter: [0x0] +# EMPTY-DYNSYM-NEXT: Buckets: [0] +# EMPTY-DYNSYM-NEXT: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: the dynamic symbol table is empty +# EMPTY-DYNSYM-NEXT: } + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0 ] + HashValues: [ 0x0 ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Link: 0 + Entries: + - Tag: DT_GNU_HASH + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 + - Name: .dynsym + Type: SHT_DYNSYM + Size: 0 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic + +## Linkers might produce an empty no-op SHT_GNU_HASH section when +## there are no dynamic symbols or when all dynamic symbols are undefined. +## Such sections normally have a single zero entry in the bloom +## filter, a single zero entry in the hash bucket and no values. +## +## The index of the first symbol in the dynamic symbol table +## included in the hash table can be set to the number of dynamic symbols, +## what is one larger that the index of the last dynamic symbol and we +## normally should report it, but not in this case. + +# RUN: yaml2obj --docnum=6 %s -o %t.empty +# RUN: llvm-readobj --gnu-hash-table %t.empty 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.empty --check-prefix=EMPTY --implicit-check-not="warning:" +# RUN: llvm-readelf --gnu-hash-table %t.empty 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.empty --check-prefix=EMPTY --implicit-check-not="warning:" + +# EMPTY: GnuHashTable { +# EMPTY-NEXT: Num Buckets: 1 +# EMPTY-NEXT: First Hashed Symbol Index: 1 +# EMPTY-NEXT: Num Mask Words: 1 +# EMPTY-NEXT: Shift Count: 0 +# EMPTY-NEXT: Bloom Filter: [0x0] +# EMPTY-NEXT: Buckets: [0] +# EMPTY-NEXT: Values: [] +# EMPTY-NEXT: } + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x1 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0 ] + HashValues: [ ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Link: 0 + Entries: + - Tag: DT_GNU_HASH + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 +DynamicSymbols: [] +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -2495,12 +2495,50 @@ W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx); W.printNumber("Num Mask Words", GnuHashTable->maskwords); W.printNumber("Shift Count", GnuHashTable->shift2); - W.printHexList("Bloom Filter", GnuHashTable->filter()); - W.printList("Buckets", GnuHashTable->buckets()); - Elf_Sym_Range Syms = dynamic_symbols(); - unsigned NumSyms = std::distance(Syms.begin(), Syms.end()); - if (!NumSyms) - reportError(createError("No dynamic symbol section"), ObjF->getFileName()); + + ArrayRef BloomFilter = GnuHashTable->filter(); + W.printHexList("Bloom Filter", BloomFilter); + + ArrayRef Buckets = GnuHashTable->buckets(); + W.printList("Buckets", Buckets); + + if (!DynSymRegion.Addr) { + reportWarning(createError("unable to dump 'Values' for the SHT_GNU_HASH " + "section: no dynamic symbol table found"), + ObjF->getFileName()); + return; + } + + size_t NumSyms = dynamic_symbols().size(); + if (!NumSyms) { + reportWarning(createError("unable to dump 'Values' for the SHT_GNU_HASH " + "section: the dynamic symbol table is empty"), + ObjF->getFileName()); + return; + } + + if (GnuHashTable->symndx >= NumSyms) { + // A normal empty GNU hash table section produced by linker might have + // symndx set to the number of dynamic symbols + 1 (for the zero symbol) + // and have the dummy null values in the Bloom filter and in the buckets + // vector. It happens because the value of symndx is not important for + // dynamic loaders when they finds that the GNU hash table is empty. They + // just skips the whole object during symbol lookup then, and so linkers do + // not care much too. We should not report a warning in this case. + bool IsEmptyHashTable = + llvm::all_of(Buckets, [](Elf_Word V) { return V == 0; }); + + if (!IsEmptyHashTable) { + reportWarning( + createError("the first hashed symbol index (" + + Twine(GnuHashTable->symndx) + + ") is larger than the number of dynamic symbols (" + + Twine(NumSyms) + ")"), + ObjF->getFileName()); + return; + } + } + W.printHexList("Values", GnuHashTable->values(NumSyms)); }