forked from OSchip/llvm-project
[llvm-readobj] - Add a few warnings for --gnu-hash-table.
The current implementation stops dumping in case of a single error it handles, though we can continue dumping. This patch refines it: it adds a few warnings and a few test cases. Differential revision: https://reviews.llvm.org/D73269
This commit is contained in:
parent
7fd7a9a636
commit
5f8e51a9d4
|
@ -100,3 +100,217 @@ ProgramHeaders:
|
|||
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,
|
||||
## which is one larger than the index of the last dynamic symbol.
|
||||
## For empty tables however, this value is unimportant and can be ignored.
|
||||
|
||||
# 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
|
||||
|
|
|
@ -2495,12 +2495,50 @@ template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() {
|
|||
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<typename ELFT::Off> BloomFilter = GnuHashTable->filter();
|
||||
W.printHexList("Bloom Filter", BloomFilter);
|
||||
|
||||
ArrayRef<Elf_Word> 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 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 the GNU hash table is empty. They just skip the
|
||||
// whole object during symbol lookup. In such cases, the symndx value is
|
||||
// irrelevant and we should not report a warning.
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue