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 @@ -125,7 +125,7 @@ # SYMNDX-NEXT: Shift Count: 0 # SYMNDX-NEXT: Bloom Filter: [0x1] # SYMNDX-NEXT: Buckets: [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: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: the first hashed symbol index (2) is larger or equal to the number of dynamic symbols (2) # SYMNDX-NEXT: } --- !ELF 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 @@ -331,7 +331,7 @@ # 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) +# ERR6: warning: '[[FILE]]': unable to print the GNU hash table histogram: the first hashed symbol index (16) is larger or equal to 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 Index: llvm/test/tools/llvm-readobj/ELF/hash-symbols.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/hash-symbols.test +++ llvm/test/tools/llvm-readobj/ELF/hash-symbols.test @@ -655,3 +655,81 @@ - Section: .gnu.hash - Section: .dynamic - Section: .dynstr + +## In this case we have a broken value in the hash buckets array. Normally it contains an +## index into the dynamic symbol table and also is used to get a hash value from the hash values array. +## llvm-readelf attempts to read a symbol that is past the end of the dynamic symbol table. + +# RUN: yaml2obj --docnum=11 -DVALUE=0x2 %s -o %t11.past.dynsym.so +# RUN: llvm-readelf --hash-symbols %t11.past.dynsym.so 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t11.past.dynsym.so --check-prefix=BUCKET-PAST-DYNSYM + +# BUCKET-PAST-DYNSYM: Symbol table of .gnu.hash for image: +# BUCKET-PAST-DYNSYM-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# BUCKET-PAST-DYNSYM-NEXT: warning: '[[FILE]]': unable to print hashed symbol with index 2, which is greater or equal to the number of dynamic symbols (2) +# BUCKET-PAST-DYNSYM-NEXT: 1 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo +# BUCKET-PAST-DYNSYM-NOT: {{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Link: .dynsym + Header: + SymNdx: [[SYMNDX=0x0]] + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0, [[VALUE]], 0x1 ] + 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: + - Name: foo + Binding: STB_GLOBAL +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + Sections: + - Section: .gnu.hash + - Section: .dynamic + +## In this case we are unable to read a hash value for a symbol with +## an index that is less than the index of the first hashed symbol. + +# RUN: yaml2obj --docnum=11 -DSYMNDX=0x2 -DVALUE=0x1 %s -o %t11.first.hashed.so +# RUN: llvm-readelf --hash-symbols %t11.first.hashed.so 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t11.first.hashed.so --check-prefix=FIRST-HASHED + +# FIRST-HASHED: Symbol table of .gnu.hash for image: +# FIRST-HASHED-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# FIRST-HASHED-NEXT: warning: '[[FILE]]': unable to get 'Values' for the SHT_GNU_HASH section: the first hashed symbol index (2) is larger or equal to the number of dynamic symbols (2) +# FIRST-HASHED-NEXT: 1 1: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo +# FIRST-HASHED-NEXT: warning: '[[FILE]]': unable to read the hash value for symbol with index 1, which is less than the index of the first hashed symbol (2) +# FIRST-HASHED-NEXT: 1 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo +# FIRST-HASHED-NOT: {{.}} + +## In this case one of chain values doesn't end with a stopper bit and llvm-readelf attempts to read +## a dynamic symbol with index, that is equal to the number of dynamic symbols. + +# RUN: yaml2obj --docnum=11 -DSYMNDX=0x1 -DVALUE=0x1 %s -o %t11.chain.bit.so +# RUN: llvm-readelf --hash-symbols %t11.chain.bit.so 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t11.chain.bit.so --check-prefix=CHAIN-BIT + +# CHAIN-BIT: Symbol table of .gnu.hash for image: +# CHAIN-BIT-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +# CHAIN-BIT-NEXT: 1 1: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo +# CHAIN-BIT-NEXT: warning: '[[FILE]]': unable to print hashed symbol with index 2, which is greater or equal to the number of dynamic symbols (2) +# CHAIN-BIT-NEXT: 1 2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo +# CHAIN-BIT-NOT: {{.}} Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -2745,7 +2745,7 @@ if (!llvm::all_of(Buckets, [](typename ELFT::Word V) { return V == 0; })) return createError("the first hashed symbol index (" + Twine(GnuHashTable->symndx) + - ") is larger than the number of dynamic symbols (" + + ") is larger or equal to the number of dynamic symbols (" + Twine(NumSyms) + ")"); // There is no way to represent an array of (dynamic symbols count - symndx) // length. @@ -4044,8 +4044,8 @@ Elf_Sym_Range DynSyms = this->dumper().dynamic_symbols(); const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + Optional DynSymRegion = this->dumper().getDynSymRegion(); if (!FirstSym) { - Optional DynSymRegion = this->dumper().getDynSymRegion(); this->reportUniqueWarning(createError( Twine("unable to print symbols for the .gnu.hash table: the " "dynamic symbol table ") + @@ -4053,18 +4053,53 @@ return; } + auto GetSymbol = [&](uint64_t SymIndex, + uint64_t SymsTotal) -> const Elf_Sym * { + if (SymIndex >= SymsTotal) { + this->reportUniqueWarning(createError( + "unable to print hashed symbol with index " + Twine(SymIndex) + + ", which is greater or equal to the number of dynamic symbols (" + + Twine::utohexstr(SymsTotal) + ")")); + return nullptr; + } + return FirstSym + SymIndex; + }; + + Expected> ValuesOrErr = + getGnuHashTableChains(DynSymRegion, &GnuHash); + ArrayRef Values; + if (!ValuesOrErr) + this->reportUniqueWarning( + createError("unable to get 'Values' for the SHT_GNU_HASH " + "section: " + + toString(ValuesOrErr.takeError()))); + else + Values = *ValuesOrErr; + ArrayRef Buckets = GnuHash.buckets(); for (uint32_t Buc = 0; Buc < GnuHash.nbuckets; Buc++) { if (Buckets[Buc] == ELF::STN_UNDEF) continue; uint32_t Index = Buckets[Buc]; - uint32_t GnuHashable = Index - GnuHash.symndx; - // Print whole chain + // Print whole chain. while (true) { uint32_t SymIndex = Index++; - printHashedSymbol(FirstSym + SymIndex, SymIndex, StringTable, Buc); - // Chain ends at symbol with stopper bit - if ((GnuHash.values(DynSyms.size())[GnuHashable++] & 1) == 1) + if (const Elf_Sym *Sym = GetSymbol(SymIndex, DynSyms.size())) + printHashedSymbol(Sym, SymIndex, StringTable, Buc); + else + break; + + if (SymIndex < GnuHash.symndx) { + this->reportUniqueWarning(createError( + "unable to read the hash value for symbol with index " + + Twine(SymIndex) + + ", which is less than the index of the first hashed symbol (" + + Twine(GnuHash.symndx) + ")")); + break; + } + + // Chain ends at symbol with stopper bit. + if ((Values[SymIndex - GnuHash.symndx] & 1) == 1) break; } }