1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

[llvm-readobj] - Add a validation of the GNU hash table to printGnuHashHistogram().

Similar to D81937, we might crash when printing a histogram for a GNU hash table
with a 'symndx' index that is larger than the number of dynamic symbols.

This patch adopts and reuses the `getGnuHashTableChains()` helper which performs
a validation of the table. As a side effect the warning reported for
the --gnu-hash-table was improved.

Also with this change we start to report a warning when the histogram is requested for
the GNU hash table, but the dynamic symbols table is empty (size == 0).

Differential revision: https://reviews.llvm.org/D82010
This commit is contained in:
Georgii Rymar 2020-06-17 13:47:37 +03:00
parent 21433e6c3f
commit 40767ae0bd
3 changed files with 135 additions and 18 deletions

View File

@ -127,7 +127,7 @@ ProgramHeaders:
# 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: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: the first hashed symbol index (2) is larger than the number of dynamic symbols (2)
# SYMNDX-NEXT: }
--- !ELF

View File

@ -318,3 +318,112 @@ ProgramHeaders:
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.
## Check the case when a 'symndx' index of the first symbol in the dynamic symbol
## table is larger than the number of dynamic symbols.
## Case A: when the buckets array is not empty and has a non-zero value we report a warning.
# RUN: yaml2obj --docnum=7 -DVAL=0x1 %s -o %t9
# RUN: llvm-readelf --elf-hash-histogram %t9 2>&1 | \
# RUN: FileCheck %s -DFILE=%t9 --check-prefix=ERR6
# ERR6: warning: '[[FILE]]': unable to print the GNU hash table histogram: the first hashed symbol index (16) is larger than the number of dynamic symbols (1)
## Case B: we do not report a warning when the buckets array contains only zero values.
# RUN: yaml2obj --docnum=7 -DVAL=0x0 %s -o %t10
# RUN: llvm-readelf --elf-hash-histogram %t10 2>&1 | \
# RUN: FileCheck %s --allow-empty --implicit-check-not="Histogram"
## Case C: we do not report a warning when the buckets array is empty.
# RUN: yaml2obj --docnum=7 -DVAL="" %s -o %t11
# RUN: llvm-readelf --elf-hash-histogram %t11 2>&1 | \
# RUN: FileCheck %s --allow-empty --implicit-check-not="Histogram"
--- !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: 0x10
Shift2: 0x0
BloomFilter: [ 0x0 ]
HashBuckets: [ [[VAL]] ]
HashValues: [ 0x0 ]
- Name: .dynamic
Type: SHT_DYNAMIC
Flags: [ SHF_ALLOC ]
Link: .dynstr
Entries:
- Tag: DT_GNU_HASH
Value: 0x0
- Tag: DT_NULL
Value: 0x0
DynamicSymbols: []
ProgramHeaders:
- Type: PT_LOAD
Sections:
- Section: .gnu.hash
- Section: .dynamic
## Check we report warnings when the dynamic symbol table is absent or empty.
## The code locates the dynamic symbol table by the section type. Use SHT_PROGBITS to hide it.
# RUN: yaml2obj --docnum=8 -DTYPE=SHT_PROGBITS %s -o %t12
# RUN: llvm-readelf --elf-hash-histogram %t12 2>&1 | \
# RUN: FileCheck %s -DFILE=%t12 --check-prefix=ERR7
# ERR7: warning: '[[FILE]]': unable to print the GNU hash table histogram: no dynamic symbol table found
# RUN: yaml2obj --docnum=8 -DTYPE=SHT_DYNSYM %s -o %t13
# RUN: llvm-readelf --elf-hash-histogram %t13 2>&1 | \
# RUN: FileCheck %s -DFILE=%t13 --check-prefix=ERR8
# ERR8: warning: '[[FILE]]': unable to print the GNU hash table histogram: the dynamic symbol table is empty
--- !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
- Name: .dynsym
Type: [[TYPE]]
Size: 0
ProgramHeaders:
- Type: PT_LOAD
Sections:
- Section: .gnu.hash

View File

@ -325,6 +325,8 @@ public:
return Table.slice(0, Size);
}
Optional<DynRegionInfo> getDynSymRegion() const { return DynSymRegion; }
Elf_Sym_Range dynamic_symbols() const {
if (!DynSymRegion)
return Elf_Sym_Range();
@ -2718,12 +2720,16 @@ template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
template <class ELFT>
static Expected<ArrayRef<typename ELFT::Word>>
getGnuHashTableChains(const typename ELFT::SymRange DynSymTable,
getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion,
const typename ELFT::GnuHash *GnuHashTable) {
if (!DynSymRegion)
return createError("no dynamic symbol table found");
ArrayRef<typename ELFT::Sym> DynSymTable =
DynSymRegion->getAsArrayRef<typename ELFT::Sym>();
size_t NumSyms = DynSymTable.size();
if (!NumSyms)
return createError("unable to dump 'Values' for the SHT_GNU_HASH "
"section: the dynamic symbol table is empty");
return createError("the dynamic symbol table is empty");
if (GnuHashTable->symndx < NumSyms)
return GnuHashTable->values(NumSyms);
@ -2773,17 +2779,13 @@ void ELFDumper<ELFT>::printGnuHashTable(const object::ObjectFile *Obj) {
ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
W.printList("Buckets", Buckets);
if (!DynSymRegion) {
reportWarning(createError("unable to dump 'Values' for the SHT_GNU_HASH "
"section: no dynamic symbol table found"),
ObjF->getFileName());
return;
}
Expected<ArrayRef<Elf_Word>> Chains =
getGnuHashTableChains<ELFT>(dynamic_symbols(), GnuHashTable);
getGnuHashTableChains<ELFT>(DynSymRegion, GnuHashTable);
if (!Chains) {
reportUniqueWarning(Chains.takeError());
reportUniqueWarning(
createError("unable to dump 'Values' for the SHT_GNU_HASH "
"section: " +
toString(Chains.takeError())));
return;
}
@ -4660,20 +4662,26 @@ void GNUStyle<ELFT>::printHashHistogram(const Elf_Hash &HashTable) {
template <class ELFT>
void GNUStyle<ELFT>::printGnuHashHistogram(const Elf_GnuHash &GnuHashTable) {
size_t NBucket = GnuHashTable.nbuckets;
ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets();
unsigned NumSyms = this->dumper()->dynamic_symbols().size();
if (!NumSyms)
Expected<ArrayRef<Elf_Word>> ChainsOrErr = getGnuHashTableChains<ELFT>(
this->dumper()->getDynSymRegion(), &GnuHashTable);
if (!ChainsOrErr) {
this->reportUniqueWarning(
createError("unable to print the GNU hash table histogram: " +
toString(ChainsOrErr.takeError())));
return;
ArrayRef<Elf_Word> Chains = GnuHashTable.values(NumSyms);
}
ArrayRef<Elf_Word> Chains = *ChainsOrErr;
size_t Symndx = GnuHashTable.symndx;
size_t TotalSyms = 0;
size_t MaxChain = 1;
size_t CumulativeNonZero = 0;
size_t NBucket = GnuHashTable.nbuckets;
if (Chains.empty() || NBucket == 0)
return;
ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets();
std::vector<size_t> ChainLen(NBucket, 0);
for (size_t B = 0; B < NBucket; B++) {
if (!Buckets[B])