Index: llvm/test/tools/llvm-readobj/ELF/hash-histogram.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/hash-histogram.test +++ llvm/test/tools/llvm-readobj/ELF/hash-histogram.test @@ -207,3 +207,64 @@ Sections: - Section: .hash - Section: .dynamic + +## Check we dump a histogram for the .gnu.hash table even when the .hash table is skipped. + +## Case A: the .hash table has no data to build histogram and it is skipped. +# RUN: yaml2obj --docnum=5 -DNBUCKET=0x1 %s -o %t5.o +# RUN: llvm-readelf --elf-hash-histogram %t5.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=GNU-HASH-A --implicit-check-not="Histogram" + +# GNU-HASH-A: Histogram for `.gnu.hash' bucket list length (total of 1 buckets) + +## Case B: the .hash table has a broken nbucket field. We report a warning +## and skip dumping of the .hash table. +# RUN: yaml2obj --docnum=5 -DNBUCKET=0xffffffff %s -o %t6.o +# RUN: llvm-readelf --elf-hash-histogram %t6.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t6.o --check-prefix=GNU-HASH-B + +# GNU-HASH-B: warning: '[[FILE]]': the hash table at offset 0x78 goes past the end of the file (0x350), nbucket = 4294967295, nchain = 1 +# GNU-HASH-B: Histogram for `.gnu.hash' bucket list length (total of 1 buckets) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .hash + Type: SHT_HASH + Flags: [ SHF_ALLOC ] + Bucket: [ 0 ] +## NBUCKET == 1 is a no-op. + NBucket: [[NBUCKET]] + Chain: [ 0 ] + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x1 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x00000001, 0x00000004, 0x00000000 ] + HashValues: [ 0x0B887388 ] + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_WRITE, SHF_ALLOC ] + Entries: + - Tag: DT_HASH + Value: 0x0 + - Tag: DT_GNU_HASH +## sizeof(.hash) == 0x28. + Value: 0x28 + - Tag: DT_NULL + Value: 0x0 +DynamicSymbols: + - Name: foo +ProgramHeaders: + - Type: PT_LOAD + Sections: + - Section: .hash + - Section: .gnu.hash + - Section: .dynamic Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -4562,15 +4562,11 @@ // Additionally cumulative coverage of symbols for each set of buckets. template void GNUStyle::printHashHistogram(const ELFFile *Obj) { - // Print histogram for .hash section - if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) { - if (!checkHashTable(Obj, HashTable, this->FileName)) - return; - - size_t NBucket = HashTable->nbucket; - size_t NChain = HashTable->nchain; - ArrayRef Buckets = HashTable->buckets(); - ArrayRef Chains = HashTable->chains(); + auto PrintHashHist = [&](const Elf_Hash &HashTable) { + size_t NBucket = HashTable.nbucket; + size_t NChain = HashTable.nchain; + ArrayRef Buckets = HashTable.buckets(); + ArrayRef Chains = HashTable.chains(); size_t TotalSyms = 0; // If hash table is correct, we have at least chains with 0 length size_t MaxChain = 1; @@ -4604,7 +4600,7 @@ if (!TotalSyms) return; - std::vector Count(MaxChain, 0) ; + std::vector Count(MaxChain, 0); // Count how long is the chain for each bucket for (size_t B = 0; B < NBucket; B++) ++Count[ChainLen[B]]; @@ -4619,17 +4615,16 @@ (Count[I] * 100.0) / NBucket, (CumulativeNonZero * 100.0) / TotalSyms); } - } + }; - // Print histogram for .gnu.hash section - if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) { - size_t NBucket = GnuHashTable->nbuckets; - ArrayRef Buckets = GnuHashTable->buckets(); + auto PrintGnuHashHist = [&](const Elf_GnuHash &GnuHashTable) { + size_t NBucket = GnuHashTable.nbuckets; + ArrayRef Buckets = GnuHashTable.buckets(); unsigned NumSyms = this->dumper()->dynamic_symbols().size(); if (!NumSyms) return; - ArrayRef Chains = GnuHashTable->values(NumSyms); - size_t Symndx = GnuHashTable->symndx; + ArrayRef Chains = GnuHashTable.values(NumSyms); + size_t Symndx = GnuHashTable.symndx; size_t TotalSyms = 0; size_t MaxChain = 1; size_t CumulativeNonZero = 0; @@ -4655,7 +4650,7 @@ if (!TotalSyms) return; - std::vector Count(MaxChain, 0) ; + std::vector Count(MaxChain, 0); for (size_t B = 0; B < NBucket; B++) ++Count[ChainLen[B]]; // Print Number of buckets with each chain lengths and their cumulative @@ -4663,13 +4658,22 @@ OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket << " buckets)\n" << " Length Number % of total Coverage\n"; - for (size_t I = 0; I dumper()->getHashTable()) + if (checkHashTable(Obj, HashTable, this->FileName)) + PrintHashHist(*HashTable); + + // Print histogram for the .gnu.hash section. + if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) + PrintGnuHashHist(*GnuHashTable); } template