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
return Table.slice(0, Size);
}
+ Optional<DynRegionInfo> getDynSymRegion() const { return DynSymRegion; }
+
Elf_Sym_Range dynamic_symbols() const {
if (!DynSymRegion)
return Elf_Sym_Range();
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);
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;
}
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])