From: Georgii Rymar Date: Mon, 9 Nov 2020 09:18:18 +0000 (+0300) Subject: Recommit: [llvm-readelf/obj] - Allow dumping of ELF header even if some elements... X-Git-Tag: llvmorg-13-init~6742 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=99a6401acc57300c6490a094f777fe0a23b270c5;p=platform%2Fupstream%2Fllvm.git Recommit: [llvm-readelf/obj] - Allow dumping of ELF header even if some elements are corrupt. This is recommit for D90903 with fixes for BB: 1) Used std::move<> when returning Expected<> (http://lab.llvm.org:8011/#/builders/112/builds/913) 2) Fixed the name of temporarily file in the file-headers.test (http://lab.llvm.org:8011/#/builders/36/builds/1269) (a local old temporarily file was used before) For creating `ELFObjectFile` instances we have the factory method `ELFObjectFile::create(MemoryBufferRef Object)`. The problem of this method is that it scans the section header to locate some sections. When a file is truncated or has broken fields in the ELF header, this approach does not allow us to create the `ELFObjectFile` and dump the ELF header. This is https://bugs.llvm.org/show_bug.cgi?id=40804 This patch suggests a solution - it allows to delay scaning sections in the `ELFObjectFile::create`. It now allows user code to call an object initialization (`initContent()`) later. With that it is possible, for example, for dumpers just to dump the file header and exit. By default initialization is still performed as before, what helps to keep the logic of existent callers untouched. I've experimented with different approaches when worked on this patch. I think this approach is better than doing initialization of sections (i.e. scan of them) on demand, because normally users of `ELFObjectFile` API expect to work with a valid object. In most cases when a section header table can't be read (because of an error), we don't have to continue to work with object. So we probably don't need to implement a more complex API. Differential revision: https://reviews.llvm.org/D90903 --- diff --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h index de0f48e..e12e512 100644 --- a/llvm/include/llvm/Object/Binary.h +++ b/llvm/include/llvm/Object/Binary.h @@ -91,6 +91,8 @@ public: Binary(const Binary &other) = delete; virtual ~Binary(); + virtual Error initContent() { return Error::success(); }; + StringRef getData() const; StringRef getFileName() const; MemoryBufferRef getMemoryBufferRef() const; @@ -178,7 +180,8 @@ DEFINE_ISA_CONVERSION_FUNCTIONS(Binary, LLVMBinaryRef) /// /// @param Source The data to create the Binary from. Expected> createBinary(MemoryBufferRef Source, - LLVMContext *Context = nullptr); + LLVMContext *Context = nullptr, + bool InitContent = true); template class OwningBinary { std::unique_ptr Bin; @@ -229,7 +232,8 @@ template const T* OwningBinary::getBinary() const { } Expected> createBinary(StringRef Path, - LLVMContext *Context = nullptr); + LLVMContext *Context = nullptr, + bool InitContent = true); } // end namespace object diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 0f6604b..7d0002f 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -246,11 +246,15 @@ public: return SectionRef(toDRI(Sec), this); } + bool IsContentValid() const { return ContentValid; } + private: ELFObjectFile(MemoryBufferRef Object, ELFFile EF, const Elf_Shdr *DotDynSymSec, const Elf_Shdr *DotSymtabSec, const Elf_Shdr *DotSymtabShndxSec); + bool ContentValid = false; + protected: ELFFile EF; @@ -258,6 +262,8 @@ protected: const Elf_Shdr *DotSymtabSec = nullptr; // Symbol table section. const Elf_Shdr *DotSymtabShndxSec = nullptr; // SHT_SYMTAB_SHNDX section. + Error initContent() override; + void moveSymbolNext(DataRefImpl &Symb) const override; Expected getSymbolName(DataRefImpl Symb) const override; Expected getSymbolAddress(DataRefImpl Symb) const override; @@ -400,7 +406,8 @@ protected: public: ELFObjectFile(ELFObjectFile &&Other); - static Expected> create(MemoryBufferRef Object); + static Expected> create(MemoryBufferRef Object, + bool InitContent = true); const Elf_Rel *getRel(DataRefImpl Rel) const; const Elf_Rela *getRela(DataRefImpl Rela) const; @@ -457,6 +464,35 @@ void ELFObjectFile::moveSymbolNext(DataRefImpl &Sym) const { ++Sym.d.b; } +template Error ELFObjectFile::initContent() { + auto SectionsOrErr = EF.sections(); + if (!SectionsOrErr) + return SectionsOrErr.takeError(); + + for (const Elf_Shdr &Sec : *SectionsOrErr) { + switch (Sec.sh_type) { + case ELF::SHT_DYNSYM: { + if (!DotDynSymSec) + DotDynSymSec = &Sec; + break; + } + case ELF::SHT_SYMTAB: { + if (!DotSymtabSec) + DotSymtabSec = &Sec; + break; + } + case ELF::SHT_SYMTAB_SHNDX: { + if (!DotSymtabShndxSec) + DotSymtabShndxSec = &Sec; + break; + } + } + } + + ContentValid = true; + return Error::success(); +} + template Expected ELFObjectFile::getSymbolName(DataRefImpl Sym) const { const Elf_Sym *ESym = getSymbol(Sym); @@ -992,40 +1028,17 @@ ELFObjectFile::getRela(DataRefImpl Rela) const { template Expected> -ELFObjectFile::create(MemoryBufferRef Object) { +ELFObjectFile::create(MemoryBufferRef Object, bool InitContent) { auto EFOrErr = ELFFile::create(Object.getBuffer()); if (Error E = EFOrErr.takeError()) return std::move(E); - auto EF = std::move(*EFOrErr); - - auto SectionsOrErr = EF.sections(); - if (!SectionsOrErr) - return SectionsOrErr.takeError(); - const Elf_Shdr *DotDynSymSec = nullptr; - const Elf_Shdr *DotSymtabSec = nullptr; - const Elf_Shdr *DotSymtabShndxSec = nullptr; - for (const Elf_Shdr &Sec : *SectionsOrErr) { - switch (Sec.sh_type) { - case ELF::SHT_DYNSYM: { - if (!DotDynSymSec) - DotDynSymSec = &Sec; - break; - } - case ELF::SHT_SYMTAB: { - if (!DotSymtabSec) - DotSymtabSec = &Sec; - break; - } - case ELF::SHT_SYMTAB_SHNDX: { - if (!DotSymtabShndxSec) - DotSymtabShndxSec = &Sec; - break; - } - } - } - return ELFObjectFile(Object, EF, DotDynSymSec, DotSymtabSec, - DotSymtabShndxSec); + ELFObjectFile Obj = {Object, std::move(*EFOrErr), nullptr, nullptr, + nullptr}; + if (InitContent) + if (Error E = Obj.initContent()) + return std::move(E); + return std::move(Obj); } template diff --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h index 744e33d..27e40cb 100644 --- a/llvm/include/llvm/Object/ObjectFile.h +++ b/llvm/include/llvm/Object/ObjectFile.h @@ -350,7 +350,8 @@ public: createObjectFile(StringRef ObjectPath); static Expected> - createObjectFile(MemoryBufferRef Object, llvm::file_magic Type); + createObjectFile(MemoryBufferRef Object, llvm::file_magic Type, + bool InitContent = true); static Expected> createObjectFile(MemoryBufferRef Object) { return createObjectFile(Object, llvm::file_magic::unknown); @@ -367,7 +368,7 @@ public: createXCOFFObjectFile(MemoryBufferRef Object, unsigned FileType); static Expected> - createELFObjectFile(MemoryBufferRef Object); + createELFObjectFile(MemoryBufferRef Object, bool InitContent = true); static Expected> createMachOObjectFile(MemoryBufferRef Object, diff --git a/llvm/include/llvm/Object/SymbolicFile.h b/llvm/include/llvm/Object/SymbolicFile.h index 5c96461..012f9f7 100644 --- a/llvm/include/llvm/Object/SymbolicFile.h +++ b/llvm/include/llvm/Object/SymbolicFile.h @@ -161,14 +161,12 @@ public: // construction aux. static Expected> createSymbolicFile(MemoryBufferRef Object, llvm::file_magic Type, - LLVMContext *Context); + LLVMContext *Context, bool InitContent = true); static Expected> createSymbolicFile(MemoryBufferRef Object) { return createSymbolicFile(Object, llvm::file_magic::unknown, nullptr); } - static Expected> - createSymbolicFile(StringRef ObjectPath); static bool classof(const Binary *v) { return v->isSymbolic(); diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp index 384df4b..e741cbb 100644 --- a/llvm/lib/Object/Binary.cpp +++ b/llvm/lib/Object/Binary.cpp @@ -44,7 +44,8 @@ StringRef Binary::getFileName() const { return Data.getBufferIdentifier(); } MemoryBufferRef Binary::getMemoryBufferRef() const { return Data; } Expected> object::createBinary(MemoryBufferRef Buffer, - LLVMContext *Context) { + LLVMContext *Context, + bool InitContent) { file_magic Type = identify_magic(Buffer.getBuffer()); switch (Type) { @@ -73,7 +74,7 @@ Expected> object::createBinary(MemoryBufferRef Buffer, case file_magic::xcoff_object_32: case file_magic::xcoff_object_64: case file_magic::wasm_object: - return ObjectFile::createSymbolicFile(Buffer, Type, Context); + return ObjectFile::createSymbolicFile(Buffer, Type, Context, InitContent); case file_magic::macho_universal_binary: return MachOUniversalBinary::create(Buffer); case file_magic::windows_resource: @@ -93,8 +94,8 @@ Expected> object::createBinary(MemoryBufferRef Buffer, llvm_unreachable("Unexpected Binary File Type"); } -Expected> object::createBinary(StringRef Path, - LLVMContext *Context) { +Expected> +object::createBinary(StringRef Path, LLVMContext *Context, bool InitContent) { ErrorOr> FileOrErr = MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); @@ -103,7 +104,7 @@ Expected> object::createBinary(StringRef Path, std::unique_ptr &Buffer = FileOrErr.get(); Expected> BinOrErr = - createBinary(Buffer->getMemBufferRef(), Context); + createBinary(Buffer->getMemBufferRef(), Context, InitContent); if (!BinOrErr) return BinOrErr.takeError(); std::unique_ptr &Bin = BinOrErr.get(); diff --git a/llvm/lib/Object/ELFObjectFile.cpp b/llvm/lib/Object/ELFObjectFile.cpp index 6dee3ab..c654c3f 100644 --- a/llvm/lib/Object/ELFObjectFile.cpp +++ b/llvm/lib/Object/ELFObjectFile.cpp @@ -61,15 +61,15 @@ ELFObjectFileBase::ELFObjectFileBase(unsigned int Type, MemoryBufferRef Source) template static Expected>> -createPtr(MemoryBufferRef Object) { - auto Ret = ELFObjectFile::create(Object); +createPtr(MemoryBufferRef Object, bool InitContent) { + auto Ret = ELFObjectFile::create(Object, InitContent); if (Error E = Ret.takeError()) return std::move(E); return std::make_unique>(std::move(*Ret)); } Expected> -ObjectFile::createELFObjectFile(MemoryBufferRef Obj) { +ObjectFile::createELFObjectFile(MemoryBufferRef Obj, bool InitContent) { std::pair Ident = getElfArchType(Obj.getBuffer()); std::size_t MaxAlignment = @@ -80,16 +80,16 @@ ObjectFile::createELFObjectFile(MemoryBufferRef Obj) { if (Ident.first == ELF::ELFCLASS32) { if (Ident.second == ELF::ELFDATA2LSB) - return createPtr(Obj); + return createPtr(Obj, InitContent); else if (Ident.second == ELF::ELFDATA2MSB) - return createPtr(Obj); + return createPtr(Obj, InitContent); else return createError("Invalid ELF data"); } else if (Ident.first == ELF::ELFCLASS64) { if (Ident.second == ELF::ELFDATA2LSB) - return createPtr(Obj); + return createPtr(Obj, InitContent); else if (Ident.second == ELF::ELFDATA2MSB) - return createPtr(Obj); + return createPtr(Obj, InitContent); else return createError("Invalid ELF data"); } diff --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp index 61b36ea..cf09a66 100644 --- a/llvm/lib/Object/ObjectFile.cpp +++ b/llvm/lib/Object/ObjectFile.cpp @@ -132,7 +132,8 @@ Triple ObjectFile::makeTriple() const { } Expected> -ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type) { +ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type, + bool InitContent) { StringRef Data = Object.getBuffer(); if (Type == file_magic::unknown) Type = identify_magic(Data); @@ -154,7 +155,7 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type) { case file_magic::elf_executable: case file_magic::elf_shared_object: case file_magic::elf_core: - return createELFObjectFile(Object); + return createELFObjectFile(Object, InitContent); case file_magic::macho_object: case file_magic::macho_executable: case file_magic::macho_fixed_virtual_memory_shared_lib: diff --git a/llvm/lib/Object/SymbolicFile.cpp b/llvm/lib/Object/SymbolicFile.cpp index 72014fd..34a2c5e 100644 --- a/llvm/lib/Object/SymbolicFile.cpp +++ b/llvm/lib/Object/SymbolicFile.cpp @@ -36,7 +36,7 @@ SymbolicFile::~SymbolicFile() = default; Expected> SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type, - LLVMContext *Context) { + LLVMContext *Context, bool InitContent) { StringRef Data = Object.getBuffer(); if (Type == file_magic::unknown) Type = identify_magic(Data); @@ -67,14 +67,14 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type, case file_magic::xcoff_object_32: case file_magic::xcoff_object_64: case file_magic::wasm_object: - return ObjectFile::createObjectFile(Object, Type); + return ObjectFile::createObjectFile(Object, Type, InitContent); case file_magic::coff_import_library: return std::unique_ptr(new COFFImportFile(Object)); case file_magic::elf_relocatable: case file_magic::macho_object: case file_magic::coff_object: { Expected> Obj = - ObjectFile::createObjectFile(Object, Type); + ObjectFile::createObjectFile(Object, Type, InitContent); if (!Obj || !Context) return std::move(Obj); diff --git a/llvm/test/Object/invalid.test b/llvm/test/Object/invalid.test index b6bee98..881145a 100644 --- a/llvm/test/Object/invalid.test +++ b/llvm/test/Object/invalid.test @@ -175,9 +175,11 @@ Sections: ## when the e_shentsize field is broken. # RUN: yaml2obj %s --docnum=9 -o %t9 -# RUN: not llvm-readobj -S %t9 2>&1 | FileCheck --check-prefix=INVALID-SH-ENTSIZE %s +# RUN: not llvm-readobj -S %t9 2>&1 | \ +# RUN: FileCheck -DFILE=%t9 --implicit-check-not=warning: --check-prefix=INVALID-SH-ENTSIZE %s -# INVALID-SH-ENTSIZE: error: {{.*}}: invalid e_shentsize in ELF header: 1 +# INVALID-SH-ENTSIZE: LoadName: +# INVALID-SH-ENTSIZE-NEXT: error: '[[FILE]]': unable to continue dumping, the file is corrupt: invalid e_shentsize in ELF header: 1 --- !ELF FileHeader: @@ -312,10 +314,12 @@ Symbols: ## when the e_shnum field is broken (is greater than the actual number of sections). # RUN: yaml2obj %s --docnum=15 -o %t15 -# RUN: not llvm-readobj -S %t15 2>&1 | FileCheck --check-prefix=INVALID-SECTION-NUM %s +# RUN: not llvm-readobj -S %t15 2>&1 | \ +# RUN: FileCheck -DFILE=%t15 --implicit-check-not=warning: --check-prefix=INVALID-SECTION-NUM %s + +# INVALID-SECTION-NUM: LoadName: +# INVALID-SECTION-NUM-NEXT: error: '[[FILE]]': unable to continue dumping, the file is corrupt: section table goes past the end of file -# INVALID-SECTION-NUM: error: {{.*}}: section table goes past the end of file - --- !ELF FileHeader: Class: ELFCLASS64 @@ -551,7 +555,7 @@ Sections: # RUN: yaml2obj --docnum=25 %s -o %t25 # RUN: not llvm-readobj -h %t25 2>&1 | FileCheck -DFILE=%t25 --check-prefix=INVALID-SEC-NUM1 %s -# INVALID-SEC-NUM1: error: '[[FILE]]': invalid section header table offset (e_shoff = 0x58) or invalid number of sections specified in the first section header's sh_size field (0x3ffffffffffffff) +# INVALID-SEC-NUM1: error: '[[FILE]]': unable to continue dumping, the file is corrupt: invalid section header table offset (e_shoff = 0x58) or invalid number of sections specified in the first section header's sh_size field (0x3ffffffffffffff) --- !ELF FileHeader: @@ -570,7 +574,7 @@ Sections: # RUN: yaml2obj --docnum=26 %s -o %t26 # RUN: not llvm-readobj -h %t26 2>&1 | FileCheck -DFILE=%t26 --check-prefix=INVALID-SEC-NUM2 %s -# INVALID-SEC-NUM2: error: '[[FILE]]': invalid number of sections specified in the NULL section's sh_size field (288230376151711744) +# INVALID-SEC-NUM2: error: '[[FILE]]': unable to continue dumping, the file is corrupt: invalid number of sections specified in the NULL section's sh_size field (288230376151711744) --- !ELF FileHeader: @@ -588,8 +592,8 @@ Sections: # RUN: yaml2obj --docnum=27 %s -o %t27 # RUN: not llvm-readobj -h %t27 2>&1 | FileCheck -DFILE=%t27 --check-prefix=INVALID-SEC-NUM3 %s -# INVALID-SEC-NUM3: error: '[[FILE]]': section header table goes past the end of the file: e_shoff = 0xffffffffffffffff - +# INVALID-SEC-NUM3: error: '[[FILE]]': unable to continue dumping, the file is corrupt: section header table goes past the end of the file: e_shoff = 0xffffffffffffffff + --- !ELF FileHeader: Class: ELFCLASS64 diff --git a/llvm/test/tools/llvm-readobj/ELF/file-headers.test b/llvm/test/tools/llvm-readobj/ELF/file-headers.test index 99cbfdf..4b78007 100644 --- a/llvm/test/tools/llvm-readobj/ELF/file-headers.test +++ b/llvm/test/tools/llvm-readobj/ELF/file-headers.test @@ -137,3 +137,98 @@ FileHeader: # LANAI-NEXT: StringTableSectionIndex: 2 # LANAI-NEXT:} # LANAI-NOT:{{.}} + +## Check we are able to dump the file header when the section header table can't be read. + +# RUN: yaml2obj %s --docnum=4 -o %t.invalid1 +# RUN: not llvm-readobj --file-headers %t.invalid1 2>&1 \ +# RUN: | FileCheck %s --implicit-check-not=warning: -DFILE=%t.invalid1 \ +# RUN: -DSECHDRCOUNT=8192 -DSECHDRSTRTABINDEX=12288 --check-prefix=INVALID-LLVM +# RUN: not llvm-readelf --file-headers %t.invalid1 2>&1 \ +# RUN: | FileCheck %s --implicit-check-not=warning: -DFILE=%t.invalid1 \ +# RUN: -DSECHDRCOUNT=8192 -DSECHDRSTRTABINDEX=12288 --check-prefix=INVALID-GNU + +# INVALID-LLVM: File: [[FILE]] +# INVALID-LLVM-NEXT: Format: elf64-unknown +# INVALID-LLVM-NEXT: Arch: unknown +# INVALID-LLVM-NEXT: AddressSize: 64bit +# INVALID-LLVM-NEXT: LoadName: +# INVALID-LLVM-NEXT: ElfHeader { +# INVALID-LLVM-NEXT: Ident { +# INVALID-LLVM-NEXT: Magic: (7F 45 4C 46) +# INVALID-LLVM-NEXT: Class: 64-bit (0x2) +# INVALID-LLVM-NEXT: DataEncoding: LittleEndian (0x1) +# INVALID-LLVM-NEXT: FileVersion: 1 +# INVALID-LLVM-NEXT: OS/ABI: SystemV (0x0) +# INVALID-LLVM-NEXT: ABIVersion: 0 +# INVALID-LLVM-NEXT: Unused: (00 00 00 00 00 00 00) +# INVALID-LLVM-NEXT: } +# INVALID-LLVM-NEXT: Type: Relocatable (0x1) +# INVALID-LLVM-NEXT: Machine: EM_NONE (0x0) +# INVALID-LLVM-NEXT: Version: 1 +# INVALID-LLVM-NEXT: Entry: 0x0 +# INVALID-LLVM-NEXT: ProgramHeaderOffset: 0x0 +# INVALID-LLVM-NEXT: SectionHeaderOffset: 0x1000 +# INVALID-LLVM-NEXT: Flags [ (0x0) +# INVALID-LLVM-NEXT: ] +# INVALID-LLVM-NEXT: HeaderSize: 64 +# INVALID-LLVM-NEXT: ProgramHeaderEntrySize: 0 +# INVALID-LLVM-NEXT: ProgramHeaderCount: 0 +# INVALID-LLVM-NEXT: SectionHeaderEntrySize: 64 +# INVALID-LLVM-NEXT: SectionHeaderCount: [[SECHDRCOUNT]] +# INVALID-LLVM-NEXT: StringTableSectionIndex: [[SECHDRSTRTABINDEX]] +# INVALID-LLVM-NEXT: } +# INVALID-LLVM-NEXT: error: '[[FILE]]': unable to continue dumping, the file is corrupt: section header table goes past the end of the file: e_shoff = 0x1000 + +# INVALID-GNU: ELF Header: +# INVALID-GNU-NEXT: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 +# INVALID-GNU-NEXT: Class: ELF64 +# INVALID-GNU-NEXT: Data: 2's complement, little endian +# INVALID-GNU-NEXT: Version: 1 (current) +# INVALID-GNU-NEXT: OS/ABI: UNIX - System V +# INVALID-GNU-NEXT: ABI Version: 0 +# INVALID-GNU-NEXT: Type: REL (Relocatable file) +# INVALID-GNU-NEXT: Machine: None +# INVALID-GNU-NEXT: Version: 0x1 +# INVALID-GNU-NEXT: Entry point address: 0x0 +# INVALID-GNU-NEXT: Start of program headers: 0 (bytes into file) +# INVALID-GNU-NEXT: Start of section headers: 4096 (bytes into file) +# INVALID-GNU-NEXT: Flags: 0x0 +# INVALID-GNU-NEXT: Size of this header: 64 (bytes) +# INVALID-GNU-NEXT: Size of program headers: 0 (bytes) +# INVALID-GNU-NEXT: Number of program headers: 0 +# INVALID-GNU-NEXT: Size of section headers: 64 (bytes) +# INVALID-GNU-NEXT: Number of section headers: [[SECHDRCOUNT]] +# INVALID-GNU-NEXT: Section header string table index: [[SECHDRSTRTABINDEX]] +# INVALID-GNU-NEXT: error: '[[FILE]]': unable to continue dumping, the file is corrupt: section header table goes past the end of the file: e_shoff = 0x1000 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +## The section header table offset goes past the EOF. + EShOff: 0x1000 +## The number of section headers is too large, the file is +## too little to contain so many sections. + EShNum: [[SHNUM=0x2000]] +## The index of the section name string table is too large. +## The section would be past the EOF. + EShStrNdx: [[SHSTRNDX=0x3000]] +SectionHeaderTable: + NoHeaders: true + +## Check we don't dump anything except the file header when the section header table can't be read. + +# RUN: not llvm-readobj -a %t.invalid1 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.invalid1 -DSECHDRCOUNT=8192 -DSECHDRSTRTABINDEX=12288 --check-prefix=INVALID-LLVM +# RUN: not llvm-readelf -a %t.invalid1 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.invalid1 -DSECHDRCOUNT=8192 -DSECHDRSTRTABINDEX=12288 --check-prefix=INVALID-GNU + +## Check what we print when e_shnum == 0, e_shstrndx == SHN_XINDEX and the section header table can't be read. + +# RUN: yaml2obj %s -DSHNUM=0 -DSHSTRNDX=0xffff --docnum=4 -o %t.invalid2 +# RUN: not llvm-readobj --file-headers %t.invalid2 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.invalid2 -DSECHDRCOUNT="" -DSECHDRSTRTABINDEX="" --check-prefix=INVALID-LLVM +# RUN: not llvm-readelf --file-headers %t.invalid2 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.invalid2 -DSECHDRCOUNT="" -DSECHDRSTRTABINDEX="" --check-prefix=INVALID-GNU diff --git a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml index c90ffe3..7eadb4a 100644 --- a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml +++ b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml @@ -174,7 +174,7 @@ SectionHeaderTable: [[VAL]] ## Test that we are still able to override e_shoff, e_shnum and e_shstrndx ## fields even when we do not produce section headers. # RUN: yaml2obj %s --docnum=6 -o %t4 -# RUN: llvm-readelf --file-headers %t4 | FileCheck %s --check-prefix=NO-HEADERS-OVERRIDE +# RUN: not llvm-readelf --file-headers %t4 | FileCheck %s --check-prefix=NO-HEADERS-OVERRIDE # NO-HEADERS-OVERRIDE: Start of section headers: 2 (bytes into file) # NO-HEADERS-OVERRIDE: Number of section headers: 3 @@ -188,13 +188,6 @@ FileHeader: EShOff: 0x2 EShNum: 0x3 EShStrNdx: 0x4 -Sections: - - Name: .foo - Type: SHT_PROGBITS -## FIXME: we have to set an arbitrary size to create a -## piece of dummy data to make llvm-readelf happy. -## See: https://bugs.llvm.org/show_bug.cgi?id=40804 - Size: 0x100 SectionHeaderTable: NoHeaders: true diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 1214f0b..255e633e 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1998,6 +1998,9 @@ ELFDumper::ELFDumper(const object::ELFObjectFile &O, else ELFDumperStyle.reset(new LLVMStyle(Writer, *this)); + if (!O.IsContentValid()) + return; + typename ELFT::ShdrRange Sections = cantFail(Obj.sections()); for (const Elf_Shdr &Sec : Sections) { switch (Sec.sh_type) { @@ -3447,10 +3450,17 @@ static std::string getSectionHeadersNumString(const ELFFile &Obj, if (ElfHeader.e_shnum != 0) return to_string(ElfHeader.e_shnum); - ArrayRef Arr = cantFail(Obj.sections()); - if (Arr.empty()) + Expected> ArrOrErr = Obj.sections(); + if (!ArrOrErr) { + // In this case we can ignore an error, because we have already reported a + // warning about the broken section header table earlier. + consumeError(ArrOrErr.takeError()); + return ""; + } + + if (ArrOrErr->empty()) return "0"; - return "0 (" + to_string(Arr[0].sh_size) + ")"; + return "0 (" + to_string((*ArrOrErr)[0].sh_size) + ")"; } template @@ -3460,11 +3470,18 @@ static std::string getSectionHeaderTableIndexString(const ELFFile &Obj, if (ElfHeader.e_shstrndx != SHN_XINDEX) return to_string(ElfHeader.e_shstrndx); - ArrayRef Arr = cantFail(Obj.sections()); - if (Arr.empty()) + Expected> ArrOrErr = Obj.sections(); + if (!ArrOrErr) { + // In this case we can ignore an error, because we have already reported a + // warning about the broken section header table earlier. + consumeError(ArrOrErr.takeError()); + return ""; + } + + if (ArrOrErr->empty()) return "65535 (corrupt: out of range)"; - return to_string(ElfHeader.e_shstrndx) + " (" + to_string(Arr[0].sh_link) + - ")"; + return to_string(ElfHeader.e_shstrndx) + " (" + + to_string((*ArrOrErr)[0].sh_link) + ")"; } template void GNUStyle::printFileHeaders() { diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index ea549dc..fa1767f 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -35,6 +35,8 @@ public: ObjDumper(ScopedPrinter &Writer); virtual ~ObjDumper(); + virtual bool canDumpContent() { return true; } + virtual void printFileHeaders() = 0; virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index a307735..41cd441 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -458,12 +458,17 @@ createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) { } /// Dumps the specified object file. -static void dumpObject(const ObjectFile &Obj, ScopedPrinter &Writer, +static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, const Archive *A = nullptr) { std::string FileStr = A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")").str() : Obj.getFileName().str(); + std::string ContentErrString; + if (Error ContentErr = Obj.initContent()) + ContentErrString = "unable to continue dumping, the file is corrupt: " + + toString(std::move(ContentErr)); + ObjDumper *Dumper; Expected> DumperOrErr = createDumper(Obj, Writer); if (!DumperOrErr) @@ -486,6 +491,11 @@ static void dumpObject(const ObjectFile &Obj, ScopedPrinter &Writer, if (opts::FileHeaders) Dumper->printFileHeaders(); + // This is only used for ELF currently. In some cases, when an object is + // corrupt (e.g. truncated), we can't dump anything except the file header. + if (!ContentErrString.empty()) + reportError(createError(ContentErrString), FileStr); + if (opts::SectionDetails || opts::SectionHeaders) { if (opts::Output == opts::GNU && opts::SectionDetails) Dumper->printSectionDetails(); @@ -637,7 +647,8 @@ static void dumpWindowsResourceFile(WindowsResource *WinRes, /// Opens \a File and dumps it. static void dumpInput(StringRef File, ScopedPrinter &Writer) { // Attempt to open the binary. - Expected> BinaryOrErr = createBinary(File); + Expected> BinaryOrErr = + createBinary(File, /*Context=*/nullptr, /*InitContent=*/false); if (!BinaryOrErr) reportError(BinaryOrErr.takeError(), File); Binary &Binary = *BinaryOrErr.get().getBinary();