Index: include/llvm/Object/ELF.h =================================================================== --- include/llvm/Object/ELF.h +++ include/llvm/Object/ELF.h @@ -64,6 +64,8 @@ return "[unknown index]"; } +static Error defaultWarningHandler(const Twine &Msg) { return createError(Msg); } + template class ELFFile { public: @@ -95,6 +97,13 @@ using Elf_Relr_Range = typename ELFT::RelrRange; using Elf_Phdr_Range = typename ELFT::PhdrRange; + // This is a callback that can be passed to a number of functions. + // It can be used to ignore non-critical errors (warnings), which is + // useful for dumpers, like llvm-readobj. + // It accepts a warning message string and returns a success + // when the warning should be ignored or an error otherwise. + using WarningHandler = llvm::function_ref; + const uint8_t *base() const { return Buf.bytes_begin(); } size_t getBufSize() const { return Buf.size(); } @@ -114,7 +123,9 @@ template Expected getEntry(const Elf_Shdr *Section, uint32_t Entry) const; - Expected getStringTable(const Elf_Shdr *Section) const; + Expected + getStringTable(const Elf_Shdr *Section, + WarningHandler WarnHandler = &defaultWarningHandler) const; Expected getStringTableForSymtab(const Elf_Shdr &Section) const; Expected getStringTableForSymtab(const Elf_Shdr &Section, Elf_Shdr_Range Sections) const; @@ -261,7 +272,9 @@ return make_range(notes_begin(Shdr, Err), notes_end()); } - Expected getSectionStringTable(Elf_Shdr_Range Sections) const; + Expected getSectionStringTable( + Elf_Shdr_Range Sections, + WarningHandler WarnHandler = &defaultWarningHandler) const; Expected getSectionIndex(const Elf_Sym *Sym, Elf_Sym_Range Syms, ArrayRef ShndxTable) const; Expected getSection(const Elf_Sym *Sym, @@ -275,7 +288,9 @@ Expected getSymbol(const Elf_Shdr *Sec, uint32_t Index) const; - Expected getSectionName(const Elf_Shdr *Section) const; + Expected + getSectionName(const Elf_Shdr *Section, + WarningHandler WarnHandler = &defaultWarningHandler) const; Expected getSectionName(const Elf_Shdr *Section, StringRef DotShstrtab) const; template @@ -458,7 +473,8 @@ template Expected -ELFFile::getSectionStringTable(Elf_Shdr_Range Sections) const { +ELFFile::getSectionStringTable(Elf_Shdr_Range Sections, + WarningHandler WarnHandler) const { uint32_t Index = getHeader()->e_shstrndx; if (Index == ELF::SHN_XINDEX) Index = Sections[0].sh_link; @@ -468,7 +484,7 @@ if (Index >= Sections.size()) return createError("section header string table index " + Twine(Index) + " does not exist"); - return getStringTable(&Sections[Index]); + return getStringTable(&Sections[Index], WarnHandler); } template ELFFile::ELFFile(StringRef Object) : Buf(Object) {} @@ -569,13 +585,16 @@ template Expected -ELFFile::getStringTable(const Elf_Shdr *Section) const { +ELFFile::getStringTable(const Elf_Shdr *Section, + WarningHandler WarnHandler) const { if (Section->sh_type != ELF::SHT_STRTAB) - return createError("invalid sh_type for string table section " + - getSecIndexForError(this, Section) + - ": expected SHT_STRTAB, but got " + - object::getELFSectionTypeName(getHeader()->e_machine, - Section->sh_type)); + if (Error E = WarnHandler("invalid sh_type for string table section " + + getSecIndexForError(this, Section) + + ": expected SHT_STRTAB, but got " + + object::getELFSectionTypeName( + getHeader()->e_machine, Section->sh_type))) + return std::move(E); + auto V = getSectionContentsAsArray(Section); if (!V) return V.takeError(); @@ -650,11 +669,12 @@ template Expected -ELFFile::getSectionName(const Elf_Shdr *Section) const { +ELFFile::getSectionName(const Elf_Shdr *Section, + WarningHandler WarnHandler) const { auto SectionsOrErr = sections(); if (!SectionsOrErr) return SectionsOrErr.takeError(); - auto Table = getSectionStringTable(*SectionsOrErr); + auto Table = getSectionStringTable(*SectionsOrErr, WarnHandler); if (!Table) return Table.takeError(); return getSectionName(Section, *Table); Index: test/Object/invalid.test =================================================================== --- test/Object/invalid.test +++ test/Object/invalid.test @@ -344,9 +344,9 @@ ## overflows the section name string table. # RUN: yaml2obj %s --docnum=17 -o %t17 -# RUN: not llvm-readobj --sections %t17 2>&1 | FileCheck --check-prefix=BROKEN-SECNAME %s +# RUN: not llvm-readobj --sections %t17 2>&1 | FileCheck -DFILE=%t17 --check-prefix=BROKEN-SECNAME %s -## BROKEN-SECNAME: error: a section [index 1] has an invalid sh_name (0x1) offset which goes past the end of the section name string table +## BROKEN-SECNAME: error: '[[FILE]]': a section [index 1] has an invalid sh_name (0x1) offset which goes past the end of the section name string table --- !ELF FileHeader: Index: test/tools/llvm-readobj/elf-wrong-shstrtab-type.test =================================================================== --- test/tools/llvm-readobj/elf-wrong-shstrtab-type.test +++ test/tools/llvm-readobj/elf-wrong-shstrtab-type.test @@ -1,15 +1,64 @@ ## Check we do not fail to dump the section headers when ## a .shstrtab section does not have a SHT_STRTAB type. +## Check we report a warning about issue only once for an input object. + # RUN: yaml2obj %s -o %t1 -# RUN: llvm-readobj -S %t1 | FileCheck %s --check-prefix LLVM -# RUN: llvm-readelf -S %t1 | FileCheck %s --check-prefix GNU +# RUN: llvm-readobj -S %t1 2>&1 | FileCheck %s -DFILE=%t1 --implicit-check-not warning --check-prefix LLVM +# RUN: llvm-readelf -S %t1 2>&1 | FileCheck %s -DFILE=%t1 --implicit-check-not warning --check-prefix GNU + +# LLVM: warning: '[[FILE]]': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# LLVM: Section { +# LLVM: Name: .shstrtab +# LLVM: Type: SHT_PROGBITS + +# GNU: Section Headers: +# GNU: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# GNU: warning: '[[FILE]]': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# GNU: [ 1] .shstrtab PROGBITS 0000000000000000 000140 00001b 00 0 0 0 + +## Test we report multiple identical warnings (a one for each object) when dumping an archive. + +# RUN: rm -f %t.a +# RUN: cp %t1 %t2 +# RUN: llvm-ar rc %t.a %t1 %t2 %t1 +# RUN: llvm-readobj -S %t.a 2>&1 | FileCheck %s --implicit-check-not warning --check-prefix AR-LLVM +# RUN: llvm-readelf -S %t.a 2>&1 | FileCheck %s --implicit-check-not warning --check-prefix AR-GNU + +# AR-LLVM: warning: '{{.*}}1': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# AR-LLVM: Section { +# AR-LLVM: Name: .shstrtab +# AR-LLVM: Type: SHT_PROGBITS +# AR-LLVM: warning: '{{.*}}2': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# AR-LLVM: Section { +# AR-LLVM: Name: .shstrtab +# AR-LLVM: Type: SHT_PROGBITS +# AR-LLVM: warning: '{{.*}}1': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# AR-LLVM: Section { +# AR-LLVM: Name: .shstrtab +# AR-LLVM: Type: SHT_PROGBITS + +# AR-GNU: Section Headers: +# AR-GNU: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# AR-GNU: warning: '{{.*}}1': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# AR-GNU: [ 1] .shstrtab PROGBITS 0000000000000000 000140 00001b 00 0 0 0 +# AR-GNU: Section Headers: +# AR-GNU: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# AR-GNU: warning: '{{.*}}2': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# AR-GNU: [ 1] .shstrtab PROGBITS 0000000000000000 000140 00001b 00 0 0 0 +# AR-GNU: Section Headers: +# AR-GNU: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# AR-GNU: warning: '{{.*}}1': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# AR-GNU: [ 1] .shstrtab PROGBITS 0000000000000000 000140 00001b 00 0 0 0 + +## Test we report the warning for each input file specified in the command line. -# LLVM: Name: .shstrtab -# LLVM-NEXT: Type: SHT_PROGBITS +# RUN: llvm-readobj -S %t1 %t2 %t1 2>&1 | FileCheck %s --implicit-check-not warning --check-prefix MULTIPLE +# RUN: llvm-readelf -S %t1 %t2 %t1 2>&1 | FileCheck %s --implicit-check-not warning --check-prefix MULTIPLE -# GNU: [Nr] Name Type -# GNU: [ 1] .shstrtab PROGBITS +# MULTIPLE: warning: '{{.*}}1': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# MULTIPLE: warning: '{{.*}}2': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS +# MULTIPLE: warning: '{{.*}}1': invalid sh_type for string table section [index 1]: expected SHT_STRTAB, but got SHT_PROGBITS --- !ELF FileHeader: Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include using namespace llvm; @@ -352,7 +353,17 @@ using Elf_Sym = typename ELFT::Sym; using Elf_Addr = typename ELFT::Addr; - DumpStyle(ELFDumper *Dumper) : Dumper(Dumper) {} + DumpStyle(ELFDumper *Dumper) : Dumper(Dumper) { + // Dumper reports all non-critical errors as warnings. + // It does not print the same warning more than once. + WarningHandler = [this](const Twine &Msg) { + StringRef FileName = this->Dumper->getElfObject()->getFileName(); + if (Warnings.insert(Msg.str()).second) + reportWarning(FileName, createError(Msg)); + return Error::success(); + }; + } + virtual ~DumpStyle() = default; virtual void printFileHeaders(const ELFFile *Obj) = 0; @@ -401,7 +412,11 @@ virtual void printMipsPLT(const MipsGOTParser &Parser) = 0; const ELFDumper *dumper() const { return Dumper; } +protected: + std::function WarningHandler; + private: + std::unordered_set Warnings; const ELFDumper *Dumper; }; @@ -3031,26 +3046,6 @@ return ""; } -template -static StringRef getSectionName(const typename ELFT::Shdr &Sec, - const ELFObjectFile &ElfObj, - ArrayRef Sections) { - const ELFFile &Obj = *ElfObj.getELFFile(); - uint32_t Index = Obj.getHeader()->e_shstrndx; - if (Index == ELF::SHN_XINDEX) - Index = Sections[0].sh_link; - if (!Index) // no section string table. - return ""; - // TODO: Test a case when the sh_link of the section with index 0 is broken. - if (Index >= Sections.size()) - reportError(ElfObj.getFileName(), - createError("section header string table index " + - Twine(Index) + " does not exist")); - StringRef Data = toStringRef(unwrapOrError( - Obj.template getSectionContentsAsArray(&Sections[Index]))); - return unwrapOrError(Obj.getSectionName(&Sec, Data)); -} - template void GNUStyle::printSectionHeaders(const ELFO *Obj) { unsigned Bias = ELFT::Is64Bits ? 0 : 8; @@ -3072,7 +3067,8 @@ size_t SectionIndex = 0; for (const Elf_Shdr &Sec : Sections) { Fields[0].Str = to_string(SectionIndex); - Fields[1].Str = getSectionName(Sec, *ElfObj, Sections); + Fields[1].Str = unwrapOrError( + ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); Fields[2].Str = getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type); Fields[3].Str = @@ -4993,7 +4989,8 @@ ArrayRef Sections = unwrapOrError(Obj->sections()); const ELFObjectFile *ElfObj = this->dumper()->getElfObject(); for (const Elf_Shdr &Sec : Sections) { - StringRef Name = getSectionName(Sec, *ElfObj, Sections); + StringRef Name = unwrapOrError( + ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); DictScope SectionD(W, "Section"); W.printNumber("Index", ++SectionIndex); W.printNumber("Name", Name, Sec.sh_name); Index: tools/llvm-readobj/llvm-readobj.h =================================================================== --- tools/llvm-readobj/llvm-readobj.h +++ tools/llvm-readobj/llvm-readobj.h @@ -24,6 +24,7 @@ LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg); void reportError(StringRef Input, Error Err); void reportWarning(Twine Msg); + void reportWarning(StringRef Input, Error Err); void warn(llvm::Error Err); void error(std::error_code EC); void error(llvm::Error EC); @@ -37,6 +38,8 @@ return *EO; reportError(EO.getError().message()); } + + // TODO: This one is deprecated. Use one with a Input name below. template T unwrapOrError(Expected EO) { if (EO) return *EO; @@ -46,6 +49,13 @@ OS.flush(); reportError(Buf); } + + template T unwrapOrError(StringRef Input, Expected EO) { + if (EO) + return *EO; + reportError(Input, EO.takeError()); + llvm_unreachable("reportError shouldn't return in this case"); + } } // namespace llvm namespace opts { Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -394,6 +394,12 @@ WithColor::warning(errs()) << Msg << "\n"; } +void reportWarning(StringRef Input, Error Err) { + if (Input == "-") + Input = ""; + warn(createFileError(Input, std::move(Err))); +} + void warn(Error Err) { handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { reportWarning(EI.message());