diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -184,6 +184,8 @@ ELFFile(StringRef Object); + std::vector FakeSections; + public: const Elf_Ehdr &getHeader() const { return *reinterpret_cast(base()); @@ -389,6 +391,8 @@ Expected> getSectionContents(const Elf_Shdr &Sec) const; Expected> getSegmentContents(const Elf_Phdr &Phdr) const; Expected> decodeBBAddrMap(const Elf_Shdr &Sec) const; + + void createFakeSections(); }; using ELF32LEFile = ELFFile; @@ -757,11 +761,37 @@ return ELFFile(Object); } +/// Used by llvm-objdump -d (which needs sections for disassembly) to +/// disassemble objects without a section header table (e.g. ET_CORE objects +/// analyzed by linux perf or ET_EXEC with llvm-strip --strip-sections). +template void ELFFile::createFakeSections() { + if (!FakeSections.empty()) + return; + auto PhdrsOrErr = program_headers(); + if (!PhdrsOrErr) + return; + + for (auto Phdr : *PhdrsOrErr) { + if (!(Phdr.p_type & ELF::PT_LOAD) || !(Phdr.p_flags & ELF::PF_X)) + continue; + Elf_Shdr FakeShdr = {}; + FakeShdr.sh_type = ELF::SHT_PROGBITS; + FakeShdr.sh_flags = ELF::SHF_ALLOC | ELF::SHF_EXECINSTR; + FakeShdr.sh_addr = Phdr.p_vaddr; + FakeShdr.sh_size = Phdr.p_memsz; + FakeShdr.sh_offset = Phdr.p_offset; + FakeSections.push_back(FakeShdr); + } +} + template Expected ELFFile::sections() const { const uintX_t SectionTableOffset = getHeader().e_shoff; - if (SectionTableOffset == 0) + if (SectionTableOffset == 0) { + if (!FakeSections.empty()) + return makeArrayRef(FakeSections.data(), FakeSections.size()); return ArrayRef(); + } if (getHeader().e_shentsize != sizeof(Elf_Shdr)) return createError("invalid e_shentsize in ELF header: " + diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -457,6 +457,8 @@ elf_symbol_iterator_range getDynamicSymbolIterators() const override; bool isRelocatableObject() const override; + + void createFakeSections() { EF.createFakeSections(); } }; using ELF32LEObjectFile = ELFObjectFile; diff --git a/llvm/test/tools/llvm-objdump/X86/disassemble-no-section.test b/llvm/test/tools/llvm-objdump/X86/disassemble-no-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/X86/disassemble-no-section.test @@ -0,0 +1,45 @@ +## This test checks -d disassembles an ELF file without section headers. +## Such files include kcore files extracted by linux perf tools, or +## executables with section headers stripped by e.g. +## llvm-strip --strip-sections. + +# RUN: yaml2obj %s -o %t +# RUN: llvm-objdump -d %t | FileCheck %s + +# CHECK: <>: +# CHECK-NEXT: 55 pushq %rbp +# CHECK-NEXT: 48 89 e5 movq %rsp, %rbp +# CHECK-NEXT: 0f 1f 40 00 nopl (%rax) +# CHECK-NEXT: 5d popq %rbp +# CHECK-NEXT: c3 retq + +## Check disassembly with an address range. + +# RUN: llvm-objdump -d --start-address=0xffffffff00000000 \ +# RUN: --stop-address=0xffffffff00000004 %t | \ +# RUN: FileCheck %s --check-prefix RANGE + +# RANGE: 55 pushq %rbp +# RANGE-NEXT: 48 89 e5 movq %rsp, %rbp +# RANGE-EMPTY: + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_CORE + Machine: EM_X86_64 +Sections: + - Type: SectionHeaderTable + NoHeaders: true + - Type: Fill + Name: code + Pattern: "554889E50F1F40005DC3" + Size: 10 + Offset: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X ] + VAddr: 0xFFFFFFFF00000000 + FirstSec: code + LastSec: code diff --git a/llvm/test/tools/llvm-objdump/no-section-headers.test b/llvm/test/tools/llvm-objdump/no-section-headers.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/no-section-headers.test @@ -0,0 +1,39 @@ +## This test checks -h generates proper section headers from a binary that +## stripped the headers by e.g. llvm-strip --strip-sections. + +# RUN: yaml2obj %s -o %t +# RUN: llvm-strip --strip-sections -o %t2 %t +# RUN: llvm-objdump -h %t2 | FileCheck %s + +# CHECK: Sections: +# CHECK-NEXT: Idx Name Size VMA Type +# CHECK-NEXT: 0 0000000a 0000000000100000 TEXT + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Type: Fill + Name: code + Pattern: "554889E50F1F40005DC3" + Size: 10 + Offset: 0x100000 + - Type: Fill + Name: data + Pattern: "1122334455667788" + Size: 8 + Offset: 0x200000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X ] + VAddr: 0x100000 + FirstSec: code + LastSec: code + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x200000 + FirstSec: data + LastSec: data diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -124,7 +124,7 @@ bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B); void printRelocations(const object::ObjectFile *O); void printDynamicRelocations(const object::ObjectFile *O); -void printSectionHeaders(const object::ObjectFile *O); +void printSectionHeaders(object::ObjectFile *O); void printSectionContents(const object::ObjectFile *O); void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName = StringRef(), diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -1131,14 +1131,28 @@ FOS.flush(); } -static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, +static void createFakeELFSections(ObjectFile *Obj) { + assert(Obj->isELF()); + if (auto *Elf32LEObj = dyn_cast(Obj)) + Elf32LEObj->createFakeSections(); + else if (auto *Elf64LEObj = dyn_cast(Obj)) + Elf64LEObj->createFakeSections(); + else if (auto *Elf32BEObj = dyn_cast(Obj)) + Elf32BEObj->createFakeSections(); + else if (auto *Elf64BEObj = cast(Obj)) + Elf64BEObj->createFakeSections(); + else + llvm_unreachable("Unsupported binary format"); +} + +static void disassembleObject(const Target *TheTarget, ObjectFile *Obj, MCContext &Ctx, MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, const MCInstrAnalysis *MIA, MCInstPrinter *IP, const MCSubtargetInfo *PrimarySTI, const MCSubtargetInfo *SecondarySTI, - PrettyPrinter &PIP, - SourcePrinter &SP, bool InlineRelocs) { + PrettyPrinter &PIP, SourcePrinter &SP, + bool InlineRelocs) { const MCSubtargetInfo *STI = PrimarySTI; MCDisassembler *DisAsm = PrimaryDisAsm; bool PrimaryIsThumb = false; @@ -1198,6 +1212,9 @@ if (Obj->isWasm()) addMissingWasmCodeSymbols(cast(Obj), AllSymbols); + if (Obj->isELF() && Obj->sections().empty()) + createFakeELFSections(Obj); + BumpPtrAllocator A; StringSaver Saver(A); addPltEntries(Obj, AllSymbols, Saver); @@ -1688,7 +1705,7 @@ reportWarning("failed to disassemble missing symbol " + Sym, FileName); } -static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { +static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) { const Target *TheTarget = getTarget(Obj); // Package up features to be passed to target/subtarget @@ -1890,7 +1907,7 @@ return MaxWidth; } -void objdump::printSectionHeaders(const ObjectFile *Obj) { +void objdump::printSectionHeaders(ObjectFile *Obj) { size_t NameWidth = getMaxSectionNameWidth(Obj); size_t AddressWidth = 2 * Obj->getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); @@ -1903,6 +1920,9 @@ outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " Type\n"; + if (Obj->isELF() && Obj->sections().empty()) + createFakeELFSections(Obj); + uint64_t Idx; for (const SectionRef &Section : ToolSectionFilter(*Obj, &Idx)) { StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName());