Index: llvm/test/tools/llvm-readobj/elf-verneed-invalid.test =================================================================== --- llvm/test/tools/llvm-readobj/elf-verneed-invalid.test +++ llvm/test/tools/llvm-readobj/elf-verneed-invalid.test @@ -81,16 +81,18 @@ ## this situation properly. # RUN: yaml2obj --docnum=2 %s -o %t2 -# RUN: llvm-readelf -V %t2 | FileCheck %s --check-prefix=GNU-NOLINK -# RUN: llvm-readobj -V %t2 | FileCheck %s --check-prefix=LLVM-NOLINK +# RUN: llvm-readelf -V %t2 2>&1 | FileCheck %s -DFILE=%t2 --check-prefix=GNU-NOLINK +# RUN: llvm-readobj -V %t2 2>&1 | FileCheck %s -DFILE=%t2 --check-prefix=LLVM-NOLINK # GNU-NOLINK: Version symbols section '.gnu.version' contains 2 entries: # GNU-NOLINK-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 5 (.dynsym) # GNU-NOLINK-NEXT: 000: 0 (*local*) 2 (bar) # GNU-NOLINK: Version needs section '.gnu.version_r' contains 1 entries: # GNU-NOLINK-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 0 () -# GNU-NOLINK-NEXT: 0x0000: Version: 1 File: Cnt: 1 -# GNU-NOLINK-NEXT: 0x0010: Name: Flags: none Version: 2 +# GNU-NOLINK-EMPTY: +# GNU-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL +# GNU-NOLINK-NEXT: 0x0000: Version: 1 File: Cnt: 1 +# GNU-NOLINK-NEXT: 0x0010: Name: Flags: none Version: 2 # LLVM-NOLINK: VersionSymbols [ # LLVM-NOLINK: Symbol { @@ -104,17 +106,19 @@ # LLVM-NOLINK-NEXT: ] # LLVM-NOLINK: VersionRequirements [ +# LLVM-NOLINK-EMPTY: +# LLVM-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL # LLVM-NOLINK-NEXT: Dependency { # LLVM-NOLINK-NEXT: Version: 1 # LLVM-NOLINK-NEXT: Count: 1 -# LLVM-NOLINK-NEXT: FileName: +# LLVM-NOLINK-NEXT: FileName: # LLVM-NOLINK-NEXT: Entries [ # LLVM-NOLINK-NEXT: Entry { # LLVM-NOLINK-NEXT: Hash: 0 # LLVM-NOLINK-NEXT: Flags [ (0x0) # LLVM-NOLINK-NEXT: ] # LLVM-NOLINK-NEXT: Index: 2 -# LLVM-NOLINK-NEXT: Name: +# LLVM-NOLINK-NEXT: Name: # LLVM-NOLINK-NEXT: } # LLVM-NOLINK-NEXT: ] # LLVM-NOLINK-NEXT: } @@ -212,14 +216,14 @@ # LLVM-OFFSET-EQ-NEXT: Dependency { # LLVM-OFFSET-EQ-NEXT: Version: 1 # LLVM-OFFSET-EQ-NEXT: Count: 1 -# LLVM-OFFSET-EQ-NEXT: FileName: +# LLVM-OFFSET-EQ-NEXT: FileName: # LLVM-OFFSET-EQ-NEXT: Entries [ # LLVM-OFFSET-EQ-NEXT: Entry { # LLVM-OFFSET-EQ-NEXT: Hash: 0 # LLVM-OFFSET-EQ-NEXT: Flags [ (0x0) # LLVM-OFFSET-EQ-NEXT: ] # LLVM-OFFSET-EQ-NEXT: Index: 0 -# LLVM-OFFSET-EQ-NEXT: Name: +# LLVM-OFFSET-EQ-NEXT: Name: # LLVM-OFFSET-EQ-NEXT: } # LLVM-OFFSET-EQ-NEXT: ] # LLVM-OFFSET-EQ-NEXT: } @@ -227,8 +231,8 @@ # GNU-OFFSET-EQ: Version needs section '.gnu.version_r' contains 1 entries: # GNU-OFFSET-EQ-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 1 (.mystrtab) -# GNU-OFFSET-EQ-NEXT: 0x0000: Version: 1 File: Cnt: 1 -# GNU-OFFSET-EQ-NEXT: 0x0010: Name: Flags: none Version: 0 +# GNU-OFFSET-EQ-NEXT: 0x0000: Version: 1 File: Cnt: 1 +# GNU-OFFSET-EQ-NEXT: 0x0010: Name: Flags: none Version: 0 --- !ELF FileHeader: @@ -268,14 +272,14 @@ # LLVM-OFFSET-GR-NEXT: Dependency { # LLVM-OFFSET-GR-NEXT: Version: 1 # LLVM-OFFSET-GR-NEXT: Count: 1 -# LLVM-OFFSET-GR-NEXT: FileName: +# LLVM-OFFSET-GR-NEXT: FileName: # LLVM-OFFSET-GR-NEXT: Entries [ # LLVM-OFFSET-GR-NEXT: Entry { # LLVM-OFFSET-GR-NEXT: Hash: 0 # LLVM-OFFSET-GR-NEXT: Flags [ (0x0) # LLVM-OFFSET-GR-NEXT: ] # LLVM-OFFSET-GR-NEXT: Index: 0 -# LLVM-OFFSET-GR-NEXT: Name: +# LLVM-OFFSET-GR-NEXT: Name: # LLVM-OFFSET-GR-NEXT: } # LLVM-OFFSET-GR-NEXT: ] # LLVM-OFFSET-GR-NEXT: } @@ -283,8 +287,8 @@ # GNU-OFFSET-GR: Version needs section '.gnu.version_r' contains 1 entries: # GNU-OFFSET-GR-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 1 (.mystrtab) -# GNU-OFFSET-GR-NEXT: 0x0000: Version: 1 File: Cnt: 1 -# GNU-OFFSET-GR-NEXT: 0x0010: Name: Flags: none Version: 0 +# GNU-OFFSET-GR-NEXT: 0x0000: Version: 1 File: Cnt: 1 +# GNU-OFFSET-GR-NEXT: 0x0010: Name: Flags: none Version: 0 --- !ELF FileHeader: @@ -312,3 +316,190 @@ Other: 0 DynamicSymbols: - Name: foo + +## Check that we report a warning when sh_link references a non-existent section. + +# RUN: yaml2obj --docnum=6 %s -o %t6 +# RUN: llvm-readobj --sections -V %t6 2>&1 | FileCheck %s -DFILE=%t6 --implicit-check-not="warning:" --check-prefix=INVALID-LINK +# RUN: llvm-readelf --sections -V %t6 2>&1 | FileCheck %s -DFILE=%t6 --implicit-check-not="warning:" --check-prefix=INVALID-LINK + +# INVALID-LINK: warning: '[[FILE]]': invalid section linked to SHT_GNU_verneed section with index 1: invalid section index: 255 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: 0xFF + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo + +## Check that we report a warning when we can't read the content of the SHT_GNU_verneed section. + +# RUN: yaml2obj --docnum=7 %s -o %t7 +# RUN: llvm-readobj --sections -V %t7 2>&1 | FileCheck %s -DFILE=%t7 --check-prefix=INVALID-DATA +# RUN: llvm-readelf --sections -V %t7 2>&1 | FileCheck %s -DFILE=%t7 --check-prefix=INVALID-DATA + +# INVALID-DATA: warning: '[[FILE]]': cannot read content of SHT_GNU_verneed section with index 1: section [index 1] has a sh_offset (0xffffffff) + sh_size (0x20) that cannot be represented + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .dynstr + ShOffset: 0xFFFFFFFF + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo + +## Check that we report a warning when a SHT_GNU_verneed section contains a version dependency +## that goes past the end of the section. + +# RUN: yaml2obj --docnum=8 %s -o %t8 +# RUN: llvm-readobj --sections -V %t8 2>&1 | FileCheck %s -DFILE=%t8 --check-prefix=DEP-PAST-END +# RUN: llvm-readelf --sections -V %t8 2>&1 | FileCheck %s -DFILE=%t8 --check-prefix=DEP-PAST-END + +# DEP-PAST-END: warning: '[[FILE]]': invalid SHT_GNU_verneed section with index 1: version dependency 1 goes past the end of the section + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .dynstr + ShSize: 0x1 + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo + +## Check we report a warning when a version dependency is not correctly aligned in memory. + +# RUN: yaml2obj --docnum=9 %s -o %t9 +# RUN: llvm-readobj --sections -V %t9 2>&1 | FileCheck %s -DFILE=%t9 --check-prefix=MISALIGNED-DEP +# RUN: llvm-readelf --sections -V %t9 2>&1 | FileCheck %s -DFILE=%t9 --check-prefix=MISALIGNED-DEP + +# MISALIGNED-DEP: warning: '[[FILE]]': invalid SHT_GNU_verneed section with index 1: found a misaligned version dependency entry at offset 0x0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Type: Fill + Size: 0x1 + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .dynstr + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo + +## Check that we report a warning when a SHT_GNU_verneed section contains a dependency definition +## that refers to an auxiliary entry that goes past the end of the section. + +# RUN: yaml2obj --docnum=10 %s -o %t10 +# RUN: llvm-readobj --sections -V %t10 2>&1 | FileCheck %s -DFILE=%t10 --check-prefix=AUX-PAST-END +# RUN: llvm-readelf --sections -V %t10 2>&1 | FileCheck %s -DFILE=%t10 --check-prefix=AUX-PAST-END + +# AUX-PAST-END: warning: '[[FILE]]': invalid SHT_GNU_verneed section with index 1: version dependency 1 refers to an auxiliary entry that goes past the end of the section + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .dynstr + ShSize: 21 + Dependencies: + - Version: 1 + File: foo + Entries: + - Name: 'foo' + Hash: 0 + Flags: 0 + Other: 0 +DynamicSymbols: + - Name: foo + +## Check we report a warning when an auxiliary entry is not correctly aligned in memory. + +# RUN: yaml2obj %s --docnum=11 -o %t11 +# RUN: llvm-readobj -V %t11 2>&1 | FileCheck %s --check-prefix=MISALIGNED-AUX -DFILE=%t11 +# RUN: llvm-readelf -V %t11 2>&1 | FileCheck %s --check-prefix=MISALIGNED-AUX -DFILE=%t11 + +# MISALIGNED-AUX: warning: '[[FILE]]': invalid SHT_GNU_verneed section with index 1: found a misaligned auxiliary entry at offset 0x11 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .dynstr +## The byte offset to the auxiliary entry is 0x11, i.e. it is not correctly aligned in memory. + Content: "0100010001000000110000000000000000000000" +DynamicSymbols: + - Name: foo Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -167,6 +167,23 @@ std::string Name; std::vector AuxV; }; + +struct VernAux { + unsigned Hash; + unsigned Flags; + unsigned Other; + unsigned Offset; + std::string Name; +}; + +struct VerNeed { + unsigned Version; + unsigned Cnt; + unsigned Offset; + std::string File; + std::vector AuxV; +}; + } // namespace template class ELFDumper : public ObjDumper { @@ -345,6 +362,8 @@ Expected> getVersionDefinitions(const Elf_Shdr *Sec) const; + Expected> + getVersionDependencies(const Elf_Shdr *Sec) const; }; template @@ -442,6 +461,98 @@ return Ret; } +template +Expected> +ELFDumper::getVersionDependencies(const Elf_Shdr *Sec) const { + const ELFFile *Obj = ObjF->getELFFile(); + unsigned SecNdx = Sec - &cantFail(Obj->sections()).front(); + + Expected StrTabSecOrErr = Obj->getSection(Sec->sh_link); + if (!StrTabSecOrErr) + return createError( + "invalid section linked to SHT_GNU_verneed section with index " + + Twine(SecNdx) + ": " + toString(StrTabSecOrErr.takeError())); + + StringRef StrTab; + Expected StrTabOrErr = Obj->getStringTable(*StrTabSecOrErr); + if (!StrTabOrErr) + ELFDumperStyle->reportUniqueWarning(createError( + "invalid string table linked to SHT_GNU_verneed section with index " + + Twine(SecNdx) + ": " + toString(StrTabOrErr.takeError()))); + else + StrTab = *StrTabOrErr; + + Expected> ContentsOrErr = Obj->getSectionContents(Sec); + if (!ContentsOrErr) + return createError( + "cannot read content of SHT_GNU_verneed section with index " + + Twine(SecNdx) + ": " + toString(ContentsOrErr.takeError())); + + const uint8_t *Start = ContentsOrErr->data(); + const uint8_t *End = Start + ContentsOrErr->size(); + const uint8_t *VerneedBuf = Start; + + std::vector Ret; + for (unsigned I = 1; I <= /*VerneedNum=*/Sec->sh_info; ++I) { + if (VerneedBuf + sizeof(Elf_Verdef) > End) + return createError("invalid SHT_GNU_verneed section with index " + + Twine(SecNdx) + ": version dependency " + Twine(I) + + " goes past the end of the section"); + + if (uintptr_t(VerneedBuf) % sizeof(uint32_t) != 0) + return createError( + "invalid SHT_GNU_verneed section with index " + Twine(SecNdx) + + ": found a misaligned version dependency entry at offset 0x" + + Twine::utohexstr(VerneedBuf - Start)); + + const Elf_Verneed *Verneed = + reinterpret_cast(VerneedBuf); + + VerNeed &VN = *Ret.emplace(Ret.end()); + VN.Version = Verneed->vn_version; + VN.Cnt = Verneed->vn_cnt; + VN.Offset = VerneedBuf - Start; + + if (Verneed->vn_file < StrTab.size()) + VN.File = StrTab.drop_front(Verneed->vn_file); + else + VN.File = "vn_file) + ">"; + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { + if (uintptr_t(VernauxBuf) % sizeof(uint32_t) != 0) + return createError("invalid SHT_GNU_verneed section with index " + + Twine(SecNdx) + + ": found a misaligned auxiliary entry at offset 0x" + + Twine::utohexstr(VernauxBuf - Start)); + + if (VernauxBuf + sizeof(Elf_Vernaux) > End) + return createError( + "invalid SHT_GNU_verneed section with index " + Twine(SecNdx) + + ": version dependency " + Twine(I) + + " refers to an auxiliary entry that goes past the end " + "of the section"); + + const Elf_Vernaux *Vernaux = + reinterpret_cast(VernauxBuf); + + VernAux &Aux = *VN.AuxV.emplace(VN.AuxV.end()); + Aux.Hash = Vernaux->vna_hash; + Aux.Flags = Vernaux->vna_flags; + Aux.Other = Vernaux->vna_other; + Aux.Offset = VernauxBuf - Start; + if (StrTab.size() <= Vernaux->vna_name) + Aux.Name = ""; + else + Aux.Name = StrTab.drop_front(Vernaux->vna_name); + + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } + return Ret; +} + template void ELFDumper::printSymbolsHelper(bool IsDynamic) const { StringRef StrTable, SymtabName; @@ -3923,6 +4034,16 @@ } } +static StringRef getVerSecTypeName(unsigned Type) { + if (Type == ELF::SHT_GNU_versym) + return "SHT_GNU_versym"; + if (Type == ELF::SHT_GNU_verdef) + return "SHT_GNU_verdef"; + if (Type == ELF::SHT_GNU_verneed) + return "SHT_GNU_verneed"; + llvm_unreachable("unexpected section type"); +} + template void GNUStyle::printGNUVersionSectionProlog( const ELFFile *Obj, const typename ELFT::Shdr *Sec, @@ -3940,9 +4061,10 @@ SymTabName = unwrapOrError(this->FileName, Obj->getSectionName(*SymTabOrErr)); else - this->reportUniqueWarning(createError( - "invalid section linked to SHT_GNU_verdef section with index " + - Twine(SecNdx) + ": " + toString(SymTabOrErr.takeError()))); + this->reportUniqueWarning( + createError("invalid section linked to " + + getVerSecTypeName(Sec->sh_type) + " section with index " + + Twine(SecNdx) + ": " + toString(SymTabOrErr.takeError()))); OS << " Addr: " << format_hex_no_prefix(Sec->sh_addr, 16) << " Offset: " << format_hex(Sec->sh_offset, 8) @@ -4058,45 +4180,20 @@ unsigned VerneedNum = Sec->sh_info; printGNUVersionSectionProlog(Obj, Sec, "Version needs", VerneedNum); - ArrayRef SecData = - unwrapOrError(this->FileName, Obj->getSectionContents(Sec)); - - const Elf_Shdr *StrTabSec = - unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); - StringRef StringTable = { - reinterpret_cast(Obj->base() + StrTabSec->sh_offset), - (size_t)StrTabSec->sh_size}; - - const uint8_t *VerneedBuf = SecData.data(); - for (unsigned I = 0; I < VerneedNum; ++I) { - const Elf_Verneed *Verneed = - reinterpret_cast(VerneedBuf); - - StringRef File = StringTable.size() > Verneed->vn_file - ? StringTable.drop_front(Verneed->vn_file) - : ""; - - OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", - reinterpret_cast(Verneed) - SecData.begin(), - (unsigned)Verneed->vn_version, File.data(), - (unsigned)Verneed->vn_cnt); - - const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; - for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { - const Elf_Vernaux *Vernaux = - reinterpret_cast(VernauxBuf); - - StringRef Name = StringTable.size() > Vernaux->vna_name - ? StringTable.drop_front(Vernaux->vna_name) - : ""; + Expected> V = + this->dumper()->getVersionDependencies(Sec); + if (!V) { + this->reportUniqueWarning(V.takeError()); + return; + } - OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", - reinterpret_cast(Vernaux) - SecData.begin(), - Name.data(), versionFlagToString(Vernaux->vna_flags).c_str(), - (unsigned)Vernaux->vna_other); - VernauxBuf += Vernaux->vna_next; - } - VerneedBuf += Verneed->vn_next; + for (const VerNeed &VN : *V) { + OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", VN.Offset, + VN.Version, VN.File.data(), VN.Cnt); + for (const VernAux &Aux : VN.AuxV) + OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", Aux.Offset, + Aux.Name.data(), versionFlagToString(Aux.Flags).c_str(), + Aux.Other); } OS << '\n'; } @@ -5847,45 +5944,27 @@ if (!Sec) return; - const uint8_t *SecData = - reinterpret_cast(Obj->base() + Sec->sh_offset); - const Elf_Shdr *StrTabSec = - unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); - StringRef StringTable = { - reinterpret_cast(Obj->base() + StrTabSec->sh_offset), - (size_t)StrTabSec->sh_size}; + Expected> V = + this->dumper()->getVersionDependencies(Sec); + if (!V) { + this->reportUniqueWarning(V.takeError()); + return; + } - const uint8_t *VerneedBuf = SecData; - unsigned VerneedNum = Sec->sh_info; - for (unsigned I = 0; I < VerneedNum; ++I) { - const Elf_Verneed *Verneed = - reinterpret_cast(VerneedBuf); + for (const VerNeed &VN : *V) { DictScope Entry(W, "Dependency"); - W.printNumber("Version", Verneed->vn_version); - W.printNumber("Count", Verneed->vn_cnt); - - StringRef FileName = StringTable.size() > Verneed->vn_file - ? StringTable.drop_front(Verneed->vn_file) - : ""; - W.printString("FileName", FileName.data()); + W.printNumber("Version", VN.Version); + W.printNumber("Count", VN.Cnt); + W.printString("FileName", VN.File.c_str()); - const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; ListScope L(W, "Entries"); - for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { - const Elf_Vernaux *Vernaux = - reinterpret_cast(VernauxBuf); + for (const VernAux &Aux : VN.AuxV) { DictScope Entry(W, "Entry"); - W.printNumber("Hash", Vernaux->vna_hash); - W.printFlags("Flags", Vernaux->vna_flags, makeArrayRef(SymVersionFlags)); - W.printNumber("Index", Vernaux->vna_other); - - StringRef Name = StringTable.size() > Vernaux->vna_name - ? StringTable.drop_front(Vernaux->vna_name) - : ""; - W.printString("Name", Name.data()); - VernauxBuf += Vernaux->vna_next; + W.printNumber("Hash", Aux.Hash); + W.printFlags("Flags", Aux.Flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Aux.Other); + W.printString("Name", Aux.Name.c_str()); } - VerneedBuf += Verneed->vn_next; } }