[llvm] 69b312c - [llvm-objdump] Create fake sections for a ELF core file

James Henderson via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 14 05:40:13 PDT 2022


Author: Namhyung Kim
Date: 2022-07-14T13:39:59+01:00
New Revision: 69b312cde428056186928a3c1b6ad84e45de0353

URL: https://github.com/llvm/llvm-project/commit/69b312cde428056186928a3c1b6ad84e45de0353
DIFF: https://github.com/llvm/llvm-project/commit/69b312cde428056186928a3c1b6ad84e45de0353.diff

LOG: [llvm-objdump] Create fake sections for a ELF core file

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

Added: 
    llvm/test/tools/llvm-objdump/X86/disassemble-no-section.test

Modified: 
    llvm/include/llvm/Object/ELF.h
    llvm/include/llvm/Object/ELFObjectFile.h
    llvm/test/Object/objdump-no-sectionheaders.test
    llvm/tools/llvm-objdump/llvm-objdump.cpp
    llvm/tools/llvm-objdump/llvm-objdump.h

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 1a59ba94098f3..794d29fd99133 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -181,6 +181,7 @@ class ELFFile {
 
 private:
   StringRef Buf;
+  std::vector<Elf_Shdr> FakeSections;
 
   ELFFile(StringRef Object);
 
@@ -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>;
@@ -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: " +

diff  --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h
index c449a3dafc0ce..ed2f70b0da251 100644
--- a/llvm/include/llvm/Object/ELFObjectFile.h
+++ b/llvm/include/llvm/Object/ELFObjectFile.h
@@ -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>;

diff  --git a/llvm/test/Object/objdump-no-sectionheaders.test b/llvm/test/Object/objdump-no-sectionheaders.test
index ebefa760bf14d..b6f09355395d1 100644
--- 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
index 0000000000000..b1811b3626ddf
--- /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.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 1245f9e182069..39962240fdf7a 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -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,
@@ -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);
@@ -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
@@ -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);
@@ -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());

diff  --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h
index dd9f58aa33087..c64c042d513e7 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -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(),


        


More information about the llvm-commits mailing list