Skip to content

Commit

Permalink
[llvm-objdump] Create fake sections for a ELF core file
Browse files Browse the repository at this point in the history
The linux perf tools use /proc/kcore for disassembly kernel functions.
Actually it copies the relevant parts to a temp file and then pass it to
objdump. But it doesn't have section headers so llvm-objdump cannot
handle it.

Let's create fake section headers for the program headers. It'd have a
single section for each segment to cover the entire range. And for this
purpose we can consider only executable code segments.

With this change, I can see the following command shows proper outputs.

perf annotate --stdio --objdump=/path/to/llvm-objdump

Differential Revision: https://reviews.llvm.org/D128705
  • Loading branch information
namhyung authored and jh7370 committed Jul 14, 2022
1 parent 5a00113 commit 69b312c
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 8 deletions.
31 changes: 30 additions & 1 deletion llvm/include/llvm/Object/ELF.h
Expand Up @@ -181,6 +181,7 @@ class ELFFile {

private:
StringRef Buf;
std::vector<Elf_Shdr> FakeSections;

ELFFile(StringRef Object);

Expand Down Expand Up @@ -389,6 +390,8 @@ class ELFFile {
Expected<ArrayRef<uint8_t>> getSectionContents(const Elf_Shdr &Sec) const;
Expected<ArrayRef<uint8_t>> getSegmentContents(const Elf_Phdr &Phdr) const;
Expected<std::vector<BBAddrMap>> decodeBBAddrMap(const Elf_Shdr &Sec) const;

void createFakeSections();
};

using ELF32LEFile = ELFFile<ELF32LE>;
Expand Down Expand Up @@ -757,11 +760,37 @@ Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
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 <class ELFT> void ELFFile<ELFT>::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 <class ELFT>
Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::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<Elf_Shdr>();
}

if (getHeader().e_shentsize != sizeof(Elf_Shdr))
return createError("invalid e_shentsize in ELF header: " +
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Object/ELFObjectFile.h
Expand Up @@ -457,6 +457,8 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
elf_symbol_iterator_range getDynamicSymbolIterators() const override;

bool isRelocatableObject() const override;

void createFakeSections() { EF.createFakeSections(); }
};

using ELF32LEObjectFile = ELFObjectFile<ELF32LE>;
Expand Down
8 changes: 5 additions & 3 deletions 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: {{.}}
51 changes: 51 additions & 0 deletions 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
26 changes: 23 additions & 3 deletions llvm/tools/llvm-objdump/llvm-objdump.cpp
Expand Up @@ -1131,7 +1131,21 @@ static void emitPostInstructionInfo(formatted_raw_ostream &FOS,
FOS.flush();
}

static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj,
static void createFakeELFSections(ObjectFile &Obj) {
assert(Obj.isELF());
if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(&Obj))
Elf32LEObj->createFakeSections();
else if (auto *Elf64LEObj = dyn_cast<ELF64LEObjectFile>(&Obj))
Elf64LEObj->createFakeSections();
else if (auto *Elf32BEObj = dyn_cast<ELF32BEObjectFile>(&Obj))
Elf32BEObj->createFakeSections();
else if (auto *Elf64BEObj = cast<ELF64BEObjectFile>(&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,
Expand Down Expand Up @@ -1198,6 +1212,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj,
if (Obj.isWasm())
addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols);

if (Obj.isELF() && Obj.sections().empty())
createFakeELFSections(Obj);

BumpPtrAllocator A;
StringSaver Saver(A);
addPltEntries(Obj, AllSymbols, Saver);
Expand Down Expand Up @@ -1688,7 +1705,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj,
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
Expand Down Expand Up @@ -1890,7 +1907,7 @@ static size_t getMaxSectionNameWidth(const ObjectFile &Obj) {
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);
Expand All @@ -1903,6 +1920,9 @@ void objdump::printSectionHeaders(const ObjectFile &Obj) {
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());
Expand Down
2 changes: 1 addition & 1 deletion llvm/tools/llvm-objdump/llvm-objdump.h
Expand Up @@ -124,7 +124,7 @@ SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O,
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(),
Expand Down

0 comments on commit 69b312c

Please sign in to comment.