forked from OSchip/llvm-project
[llvm-readelf/obj] - Allow dumping of ELF header even if some elements are corrupt.
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
This commit is contained in:
parent
c9d036ad4a
commit
ea8a0b8b29
|
@ -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
|
||||
|
||||
|
|
|
@ -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 Obj;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,9 +314,11 @@ 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: error: {{.*}}: section table goes past the end of file
|
||||
# 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
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
|
@ -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,7 +592,7 @@ 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:
|
||||
|
|
|
@ -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.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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue