Recommit: [llvm-readelf/obj] - Allow dumping of ELF header even if some elements...
authorGeorgii Rymar <grimar@accesssoftek.com>
Mon, 9 Nov 2020 09:18:18 +0000 (12:18 +0300)
committerGeorgii Rymar <grimar@accesssoftek.com>
Mon, 9 Nov 2020 09:53:53 +0000 (12:53 +0300)
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<ELFT>::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<ELFT>::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

14 files changed:
llvm/include/llvm/Object/Binary.h
llvm/include/llvm/Object/ELFObjectFile.h
llvm/include/llvm/Object/ObjectFile.h
llvm/include/llvm/Object/SymbolicFile.h
llvm/lib/Object/Binary.cpp
llvm/lib/Object/ELFObjectFile.cpp
llvm/lib/Object/ObjectFile.cpp
llvm/lib/Object/SymbolicFile.cpp
llvm/test/Object/invalid.test
llvm/test/tools/llvm-readobj/ELF/file-headers.test
llvm/test/tools/yaml2obj/ELF/section-headers.yaml
llvm/tools/llvm-readobj/ELFDumper.cpp
llvm/tools/llvm-readobj/ObjDumper.h
llvm/tools/llvm-readobj/llvm-readobj.cpp

index de0f48e..e12e512 100644 (file)
@@ -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<std::unique_ptr<Binary>> createBinary(MemoryBufferRef Source,
-                                               LLVMContext *Context = nullptr);
+                                               LLVMContext *Context = nullptr,
+                                               bool InitContent = true);
 
 template <typename T> class OwningBinary {
   std::unique_ptr<T> Bin;
@@ -229,7 +232,8 @@ template <typename T> const T* OwningBinary<T>::getBinary() const {
 }
 
 Expected<OwningBinary<Binary>> createBinary(StringRef Path,
-                                            LLVMContext *Context = nullptr);
+                                            LLVMContext *Context = nullptr,
+                                            bool InitContent = true);
 
 } // end namespace object
 
index 0f6604b..7d0002f 100644 (file)
@@ -246,11 +246,15 @@ public:
     return SectionRef(toDRI(Sec), this);
   }
 
+  bool IsContentValid() const { return ContentValid; }
+
 private:
   ELFObjectFile(MemoryBufferRef Object, ELFFile<ELFT> EF,
                 const Elf_Shdr *DotDynSymSec, const Elf_Shdr *DotSymtabSec,
                 const Elf_Shdr *DotSymtabShndxSec);
 
+  bool ContentValid = false;
+
 protected:
   ELFFile<ELFT> 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<StringRef> getSymbolName(DataRefImpl Symb) const override;
   Expected<uint64_t> getSymbolAddress(DataRefImpl Symb) const override;
@@ -400,7 +406,8 @@ protected:
 
 public:
   ELFObjectFile(ELFObjectFile<ELFT> &&Other);
-  static Expected<ELFObjectFile<ELFT>> create(MemoryBufferRef Object);
+  static Expected<ELFObjectFile<ELFT>> 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<ELFT>::moveSymbolNext(DataRefImpl &Sym) const {
   ++Sym.d.b;
 }
 
+template <class ELFT> Error ELFObjectFile<ELFT>::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 <class ELFT>
 Expected<StringRef> ELFObjectFile<ELFT>::getSymbolName(DataRefImpl Sym) const {
   const Elf_Sym *ESym = getSymbol(Sym);
@@ -992,40 +1028,17 @@ ELFObjectFile<ELFT>::getRela(DataRefImpl Rela) const {
 
 template <class ELFT>
 Expected<ELFObjectFile<ELFT>>
-ELFObjectFile<ELFT>::create(MemoryBufferRef Object) {
+ELFObjectFile<ELFT>::create(MemoryBufferRef Object, bool InitContent) {
   auto EFOrErr = ELFFile<ELFT>::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<ELFT>(Object, EF, DotDynSymSec, DotSymtabSec,
-                             DotSymtabShndxSec);
+  ELFObjectFile<ELFT> Obj = {Object, std::move(*EFOrErr), nullptr, nullptr,
+                             nullptr};
+  if (InitContent)
+    if (Error E = Obj.initContent())
+      return std::move(E);
+  return std::move(Obj);
 }
 
 template <class ELFT>
index 744e33d..27e40cb 100644 (file)
@@ -350,7 +350,8 @@ public:
   createObjectFile(StringRef ObjectPath);
 
   static Expected<std::unique_ptr<ObjectFile>>
-  createObjectFile(MemoryBufferRef Object, llvm::file_magic Type);
+  createObjectFile(MemoryBufferRef Object, llvm::file_magic Type,
+                   bool InitContent = true);
   static Expected<std::unique_ptr<ObjectFile>>
   createObjectFile(MemoryBufferRef Object) {
     return createObjectFile(Object, llvm::file_magic::unknown);
@@ -367,7 +368,7 @@ public:
   createXCOFFObjectFile(MemoryBufferRef Object, unsigned FileType);
 
   static Expected<std::unique_ptr<ObjectFile>>
-  createELFObjectFile(MemoryBufferRef Object);
+  createELFObjectFile(MemoryBufferRef Object, bool InitContent = true);
 
   static Expected<std::unique_ptr<MachOObjectFile>>
   createMachOObjectFile(MemoryBufferRef Object,
index 5c96461..012f9f7 100644 (file)
@@ -161,14 +161,12 @@ public:
   // construction aux.
   static Expected<std::unique_ptr<SymbolicFile>>
   createSymbolicFile(MemoryBufferRef Object, llvm::file_magic Type,
-                     LLVMContext *Context);
+                     LLVMContext *Context, bool InitContent = true);
 
   static Expected<std::unique_ptr<SymbolicFile>>
   createSymbolicFile(MemoryBufferRef Object) {
     return createSymbolicFile(Object, llvm::file_magic::unknown, nullptr);
   }
-  static Expected<OwningBinary<SymbolicFile>>
-  createSymbolicFile(StringRef ObjectPath);
 
   static bool classof(const Binary *v) {
     return v->isSymbolic();
index 384df4b..e741cbb 100644 (file)
@@ -44,7 +44,8 @@ StringRef Binary::getFileName() const { return Data.getBufferIdentifier(); }
 MemoryBufferRef Binary::getMemoryBufferRef() const { return Data; }
 
 Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
-                                                      LLVMContext *Context) {
+                                                       LLVMContext *Context,
+                                                       bool InitContent) {
   file_magic Type = identify_magic(Buffer.getBuffer());
 
   switch (Type) {
@@ -73,7 +74,7 @@ Expected<std::unique_ptr<Binary>> 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<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
   llvm_unreachable("Unexpected Binary File Type");
 }
 
-Expected<OwningBinary<Binary>> object::createBinary(StringRef Path,
-                                                    LLVMContext *Context) {
+Expected<OwningBinary<Binary>>
+object::createBinary(StringRef Path, LLVMContext *Context, bool InitContent) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
       MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
                                    /*RequiresNullTerminator=*/false);
@@ -103,7 +104,7 @@ Expected<OwningBinary<Binary>> object::createBinary(StringRef Path,
   std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
 
   Expected<std::unique_ptr<Binary>> BinOrErr =
-      createBinary(Buffer->getMemBufferRef(), Context);
+      createBinary(Buffer->getMemBufferRef(), Context, InitContent);
   if (!BinOrErr)
     return BinOrErr.takeError();
   std::unique_ptr<Binary> &Bin = BinOrErr.get();
index 6dee3ab..c654c3f 100644 (file)
@@ -61,15 +61,15 @@ ELFObjectFileBase::ELFObjectFileBase(unsigned int Type, MemoryBufferRef Source)
 
 template <class ELFT>
 static Expected<std::unique_ptr<ELFObjectFile<ELFT>>>
-createPtr(MemoryBufferRef Object) {
-  auto Ret = ELFObjectFile<ELFT>::create(Object);
+createPtr(MemoryBufferRef Object, bool InitContent) {
+  auto Ret = ELFObjectFile<ELFT>::create(Object, InitContent);
   if (Error E = Ret.takeError())
     return std::move(E);
   return std::make_unique<ELFObjectFile<ELFT>>(std::move(*Ret));
 }
 
 Expected<std::unique_ptr<ObjectFile>>
-ObjectFile::createELFObjectFile(MemoryBufferRef Obj) {
+ObjectFile::createELFObjectFile(MemoryBufferRef Obj, bool InitContent) {
   std::pair<unsigned char, unsigned char> 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<ELF32LE>(Obj);
+      return createPtr<ELF32LE>(Obj, InitContent);
     else if (Ident.second == ELF::ELFDATA2MSB)
-      return createPtr<ELF32BE>(Obj);
+      return createPtr<ELF32BE>(Obj, InitContent);
     else
       return createError("Invalid ELF data");
   } else if (Ident.first == ELF::ELFCLASS64) {
     if (Ident.second == ELF::ELFDATA2LSB)
-      return createPtr<ELF64LE>(Obj);
+      return createPtr<ELF64LE>(Obj, InitContent);
     else if (Ident.second == ELF::ELFDATA2MSB)
-      return createPtr<ELF64BE>(Obj);
+      return createPtr<ELF64BE>(Obj, InitContent);
     else
       return createError("Invalid ELF data");
   }
index 61b36ea..cf09a66 100644 (file)
@@ -132,7 +132,8 @@ Triple ObjectFile::makeTriple() const {
 }
 
 Expected<std::unique_ptr<ObjectFile>>
-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:
index 72014fd..34a2c5e 100644 (file)
@@ -36,7 +36,7 @@ SymbolicFile::~SymbolicFile() = default;
 
 Expected<std::unique_ptr<SymbolicFile>>
 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<SymbolicFile>(new COFFImportFile(Object));
   case file_magic::elf_relocatable:
   case file_magic::macho_object:
   case file_magic::coff_object: {
     Expected<std::unique_ptr<ObjectFile>> Obj =
-        ObjectFile::createObjectFile(Object, Type);
+        ObjectFile::createObjectFile(Object, Type, InitContent);
     if (!Obj || !Context)
       return std::move(Obj);
 
index b6bee98..881145a 100644 (file)
@@ -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: <Not found>
+# 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: <Not found>
+# 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
index 99cbfdf..4b78007 100644 (file)
@@ -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: <Not found>
+# 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
index c90ffe3..7eadb4a 100644 (file)
@@ -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
 
index 1214f0b..255e633 100644 (file)
@@ -1998,6 +1998,9 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> &O,
   else
     ELFDumperStyle.reset(new LLVMStyle<ELFT>(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<ELFT> &Obj,
   if (ElfHeader.e_shnum != 0)
     return to_string(ElfHeader.e_shnum);
 
-  ArrayRef<typename ELFT::Shdr> Arr = cantFail(Obj.sections());
-  if (Arr.empty())
+  Expected<ArrayRef<typename ELFT::Shdr>> 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 <class ELFT>
@@ -3460,11 +3470,18 @@ static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> &Obj,
   if (ElfHeader.e_shstrndx != SHN_XINDEX)
     return to_string(ElfHeader.e_shstrndx);
 
-  ArrayRef<typename ELFT::Shdr> Arr = cantFail(Obj.sections());
-  if (Arr.empty())
+  Expected<ArrayRef<typename ELFT::Shdr>> 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 <class ELFT> void GNUStyle<ELFT>::printFileHeaders() {
index ea549dc..fa1767f 100644 (file)
@@ -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;
index a307735..41cd441 100644 (file)
@@ -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<std::unique_ptr<ObjDumper>> 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<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
+  Expected<OwningBinary<Binary>> BinaryOrErr =
+      createBinary(File, /*Context=*/nullptr, /*InitContent=*/false);
   if (!BinaryOrErr)
     reportError(BinaryOrErr.takeError(), File);
   Binary &Binary = *BinaryOrErr.get().getBinary();