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 @@ -181,6 +181,7 @@ private: StringRef Buf; + std::vector FakeSections; ELFFile(StringRef Object); @@ -389,6 +390,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 +760,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/Object/objdump-no-sectionheaders.test b/llvm/test/Object/objdump-no-sectionheaders.test --- a/llvm/test/Object/objdump-no-sectionheaders.test +++ b/llvm/test/Object/objdump-no-sectionheaders.test @@ -1,6 +1,8 @@ ; RUN: llvm-objdump -h %p/Inputs/no-sections.elf-x86-64 \ ; RUN: | FileCheck %s -; CHECK: Sections: -; CHECK: Idx Name Size VMA Type -; CHECK-NOT: {{.}} +; CHECK: Sections: +; CHECK-NEXT: Idx Name Size VMA Type +; CHECK-NEXT: 0 000006ec 0000000000400000 TEXT +; CHECK-NEXT: 1 00000000 0000000000000000 TEXT +; CHECK-NOT: {{.}} 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,51 @@ +## 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: Disassembly of section : +# CHECK-EMPTY: +# CHECK-NEXT: <>: +# 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 2>&1 | \ +# RUN: FileCheck %s --check-prefix RANGE + +# RANGE: no section overlaps the range +# RANGE-EMPTY: +# RANGE-NEXT: Disassembly of section : +# RANGE-EMPTY: +# RANGE-NEXT: <>: +# RANGE-NEXT: 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/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,7 +1131,21 @@ 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, @@ -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());