From ea8a0b8b29eb08d3f0f6ac40942a2d8e98ab57ee Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Tue, 3 Nov 2020 14:02:01 +0300 Subject: [PATCH] [llvm-readelf/obj] - Allow dumping of ELF header even if some elements are corrupt. 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 --- llvm/include/llvm/Object/Binary.h | 8 +- llvm/include/llvm/Object/ELFObjectFile.h | 75 +++++++++------ llvm/include/llvm/Object/ObjectFile.h | 5 +- llvm/include/llvm/Object/SymbolicFile.h | 4 +- llvm/lib/Object/Binary.cpp | 11 ++- llvm/lib/Object/ELFObjectFile.cpp | 14 +-- llvm/lib/Object/ObjectFile.cpp | 5 +- llvm/lib/Object/SymbolicFile.cpp | 6 +- llvm/test/Object/invalid.test | 22 +++-- .../tools/llvm-readobj/ELF/file-headers.test | 95 +++++++++++++++++++ .../tools/yaml2obj/ELF/section-headers.yaml | 9 +- llvm/tools/llvm-readobj/ELFDumper.cpp | 31 ++++-- llvm/tools/llvm-readobj/ObjDumper.h | 2 + llvm/tools/llvm-readobj/llvm-readobj.cpp | 15 ++- 14 files changed, 221 insertions(+), 81 deletions(-) diff --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h index de0f48e7f4f5..e12e512d68b8 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 0f6604bd510a..abb065a04080 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 Obj; } template diff --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h index 744e33d2d9f7..27e40cbdbece 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 5c964615e9d3..012f9f7fad07 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 384df4b84358..e741cbba2882 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 6dee3ab81b64..c654c3fd3d6c 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 61b36ea0f448..cf09a66d9c7d 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 72014fd34a4d..34a2c5e1c125 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 b6bee98681b5..881145a8d8c7 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 99cbfdf5e8c7..9cd431878d32 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.invalid 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.invalid -DSECHDRCOUNT=8192 -DSECHDRSTRTABINDEX=12288 --check-prefix=INVALID-LLVM +# RUN: not llvm-readelf -a %t.invalid 2>&1 \ +# RUN: | FileCheck %s -DFILE=%t.invalid -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 c90ffe381228..7eadb4a881b3 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 1214f0b14e72..255e633ec2e3 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 ea549dcca219..fa1767fe8902 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 a30773525c1c..41cd4414d051 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();