[llvm] [llvm-readobj] Dump SFrame relocations as well (PR #153161)

Pavel Labath via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 15 02:57:59 PDT 2025


https://github.com/labath updated https://github.com/llvm/llvm-project/pull/153161

>From 09dcafe40ca1466dea5d6d2c6ef020d1872aad21 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 11 Jul 2025 14:49:45 +0200
Subject: [PATCH 1/2] [llvm-readobj] Dump SFrame relocations as well

If there is a relocation for a particular FDE, print it as well. This
is mainly meant for human consumption (otherwise, there's no way to tell
which function a given (relocatable) FDE refers to). For testing of
relocation generation, I'd still recommend using the regular relocation
dumper, as this code will not detect (e.g.) any superfluous relocations.

I've considered handling relocations inside the SFrameParser class, but
I couldn't find an elegant way to do that. Right now, I don't have a use
case for resolving relocations there as lldb (my other use case for
SFrameParser) will always operate on linked objects.
---
 llvm/include/llvm/Object/SFrameParser.h       |  4 +
 llvm/lib/Object/SFrameParser.cpp              | 19 ++--
 .../tools/llvm-readobj/ELF/sframe-reloc.test  | 94 +++++++++++++++++++
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 76 ++++++++++++++-
 4 files changed, 180 insertions(+), 13 deletions(-)
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test

diff --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h
index 539bb1872cc99..3ce5d70142a9f 100644
--- a/llvm/include/llvm/Object/SFrameParser.h
+++ b/llvm/include/llvm/Object/SFrameParser.h
@@ -45,6 +45,10 @@ template <endianness E> class SFrameParser {
   // objects returned by the `fdes()` function.
   uint64_t getAbsoluteStartAddress(typename FDERange::iterator FDE) const;
 
+  // Returns the offset (in the SFrame section) of the given FDE, which must be
+  // one of the objects returned by the `fdes()` function.
+  uint64_t offsetOf(typename FDERange::iterator FDE) const;
+
   struct FrameRowEntry {
     uint32_t StartAddress;
     sframe::FREInfo<endianness::native> Info;
diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp
index 847067a7ae989..0c5638d776ef9 100644
--- a/llvm/lib/Object/SFrameParser.cpp
+++ b/llvm/lib/Object/SFrameParser.cpp
@@ -98,17 +98,20 @@ uint64_t SFrameParser<E>::getAbsoluteStartAddress(
   uint64_t Result = SectionAddress + FDE->StartAddress;
 
   if ((getPreamble().Flags.value() & sframe::Flags::FDEFuncStartPCRel) ==
-      sframe::Flags::FDEFuncStartPCRel) {
-    uintptr_t DataPtr = reinterpret_cast<uintptr_t>(Data.data());
-    uintptr_t FDEPtr = reinterpret_cast<uintptr_t>(&*FDE);
+      sframe::Flags::FDEFuncStartPCRel)
+    Result += offsetOf(FDE);
 
-    assert(DataPtr <= FDEPtr && FDEPtr < DataPtr + Data.size() &&
-           "Iterator does not belong to this object!");
+  return Result;
+}
 
-    Result += FDEPtr - DataPtr;
-  }
+template <endianness E>
+uint64_t SFrameParser<E>::offsetOf(typename FDERange::iterator FDE) const {
+  uintptr_t DataPtr = reinterpret_cast<uintptr_t>(Data.data());
+  uintptr_t FDEPtr = reinterpret_cast<uintptr_t>(&*FDE);
 
-  return Result;
+  assert(DataPtr <= FDEPtr && FDEPtr < DataPtr + Data.size() &&
+         "Iterator does not belong to this object!");
+  return FDEPtr - DataPtr;
 }
 
 template <typename EndianT>
diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test b/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test
new file mode 100644
index 0000000000000..5a9a888938c62
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test
@@ -0,0 +1,94 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-readobj --sframe %t 2>&1 | \
+# RUN:   FileCheck %s --strict-whitespace --match-full-lines -DFILE=%t
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_X86_64
+Sections:
+  - Name:  .text
+    Type:  SHT_PROGBITS
+    Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+    Size:  0x1000
+  - Name:  .sframe
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x04,  # Preamble (magic, version, flags)
+      # Header:
+      0x03, 0x42, 0x40, 0x00,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x03, 0x00, 0x00, 0x00,  # Number of FDEs
+      0x01, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0x10, 0x00, 0x00,  # FRE length
+      0x00, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x00, 0x00, 0x00,  # FRE offset
+
+      # FDE[0]:
+      0x00, 0x00, 0xde, 0x00,  # Start Address
+      0xbe, 0x01, 0x00, 0x00,  # Size
+      0x00, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x01, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+
+      # FDE[1]:
+      0x00, 0x00, 0xad, 0x00,  # Start Address
+      0xbe, 0x01, 0x00, 0x00,  # Size
+      0x00, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x00, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+
+      # FDE[2]:
+      0x00, 0x00, 0xbe, 0x00,  # Start Address
+      0xbe, 0x01, 0x00, 0x00,  # Size
+      0x00, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x00, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+
+      # FRE[0]:
+      0x05, 0x02, 0x10,        # Start Address, Info, Offset[0]
+
+    ]
+  - Name:            .rela.sframe
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .sframe
+    Relocations:
+      - Offset:          0x1c
+        Symbol:          .text
+        Type:            R_X86_64_PC32
+        Addend:          0x42
+      - Offset:          0x30
+        Symbol:          .text
+        Type:            R_X86_64_PC32
+      - Offset:          0x30
+        Symbol:          .text
+        Type:            R_X86_64_PC32
+      - Offset:          0x44
+        Symbol:          4747
+        Type:            R_X86_64_PC32
+Symbols:
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+# CHECK-LABEL:SFrame section '.sframe' {
+#       CHECK:    ABI: AMD64EndianLittle (0x3)
+#       CHECK:    FuncDescEntry [0] {
+#  CHECK-NEXT:      PC {
+#  CHECK-NEXT:        Relocation: R_X86_64_PC32
+#  CHECK-NEXT:        Symbol Name: .text
+#  CHECK-NEXT:        Start Address: 0xDE0042
+#  CHECK-NEXT:      }
+#       CHECK:      FREs [
+#  CHECK-NEXT:        Frame Row Entry {
+#  CHECK-NEXT:          Start Address: 0xDE0047
+#       CHECK:    FuncDescEntry [1] {
+#  CHECK-NEXT:{{.*}}: warning: '[[FILE]]': more than one relocation at offset 0x30
+#  CHECK-NEXT:      PC: 0xAD0030
+#       CHECK:    FuncDescEntry [2] {
+#  CHECK-NEXT:{{.*}}: warning: '[[FILE]]': unable to read an entry with index 4747 from SHT_SYMTAB section with index {{[0-9]*}}: can't read an entry at 0x{{[0-9a-f]*}}: it goes past the end of the section (0x30)
+
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index ed29e0bf3f7cd..28d53ef622075 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -425,7 +425,13 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   ArrayRef<Elf_Word> getShndxTable(const Elf_Shdr *Symtab) const;
 
   void printSFrameHeader(const SFrameParser<ELFT::Endianness> &Parser);
-  void printSFrameFDEs(const SFrameParser<ELFT::Endianness> &Parser);
+  void printSFrameFDEs(const SFrameParser<ELFT::Endianness> &Parser,
+                       ArrayRef<Relocation<ELFT>> Relocations,
+                       const Elf_Shdr *RelocSymTab);
+  uint64_t getAndPrintSFrameFDEStartAddress(
+      const SFrameParser<ELFT::Endianness> &Parser,
+      const typename SFrameParser<ELFT::Endianness>::FDERange::iterator FDE,
+      ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab);
 
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
@@ -6484,7 +6490,8 @@ void ELFDumper<ELFT>::printSFrameHeader(
 
 template <typename ELFT>
 void ELFDumper<ELFT>::printSFrameFDEs(
-    const SFrameParser<ELFT::Endianness> &Parser) {
+    const SFrameParser<ELFT::Endianness> &Parser,
+    ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab) {
   typename SFrameParser<ELFT::Endianness>::FDERange FDEs;
   if (Error Err = Parser.fdes().moveInto(FDEs)) {
     reportWarning(std::move(Err), FileName);
@@ -6497,8 +6504,8 @@ void ELFDumper<ELFT>::printSFrameFDEs(
         W,
         formatv("FuncDescEntry [{0}]", std::distance(FDEs.begin(), It)).str());
 
-    uint64_t FDEStartAddress = Parser.getAbsoluteStartAddress(It);
-    W.printHex("PC", FDEStartAddress);
+    uint64_t FDEStartAddress =
+        getAndPrintSFrameFDEStartAddress(Parser, It, Relocations, RelocSymTab);
     W.printHex("Size", It->Size);
     W.printHex("Start FRE Offset", It->StartFREOff);
     W.printNumber("Num FREs", It->NumFREs);
@@ -6557,9 +6564,46 @@ void ELFDumper<ELFT>::printSFrameFDEs(
   }
 }
 
+template <typename ELFT>
+uint64_t ELFDumper<ELFT>::getAndPrintSFrameFDEStartAddress(
+    const SFrameParser<ELFT::Endianness> &Parser,
+    const typename SFrameParser<ELFT::Endianness>::FDERange::iterator FDE,
+    ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab) {
+  uint64_t Address = Parser.getAbsoluteStartAddress(FDE);
+  uint64_t Offset = Parser.offsetOf(FDE);
+
+  auto Reloc = llvm::lower_bound(
+      Relocations, Offset, [](auto R, uint64_t O) { return R.Offset < O; });
+  if (Reloc == Relocations.end() || Reloc->Offset != Offset) {
+    W.printHex("PC", Address);
+  } else if (std::next(Reloc) != Relocations.end() &&
+             std::next(Reloc)->Offset == Offset) {
+    reportWarning(createError(formatv(
+                      "more than one relocation at offset {0:x+}", Offset)),
+                  FileName);
+    W.printHex("PC", Address);
+  } else if (Expected<RelSymbol<ELFT>> RelSym =
+                 getRelocationTarget(*Reloc, RelocSymTab);
+             !RelSym) {
+    reportWarning(RelSym.takeError(), FileName);
+    W.printHex("PC", Address);
+  } else {
+    // Exactly one relocation at the given offset. Print it.
+    DictScope PCScope(W, "PC");
+    SmallString<32> RelocName;
+    Obj.getRelocationTypeName(Reloc->Type, RelocName);
+    W.printString("Relocation", RelocName);
+    W.printString("Symbol Name", RelSym->Name);
+    Address = FDE->StartAddress + Reloc->Addend.value_or(0);
+    W.printHex("Start Address", Address);
+  }
+  return Address;
+}
+
 template <typename ELFT>
 void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
   constexpr endianness E = ELFT::Endianness;
+
   for (object::SectionRef Section :
        getSectionRefsByNameOrIndex(ObjF, Sections)) {
     // Validity of sections names checked in getSectionRefsByNameOrIndex.
@@ -6583,8 +6627,30 @@ void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
       continue;
     }
 
+    const Elf_Shdr *ELFSection = ObjF.getSection(Section.getRawDataRefImpl());
+    MapVector<const Elf_Shdr *, const Elf_Shdr *> RelocationMap;
+    if (Error Err = Obj.getSectionAndRelocations(
+                           [&](const Elf_Shdr &S) { return &S == ELFSection; })
+                        .moveInto(RelocationMap)) {
+      reportWarning(std::move(Err), FileName);
+    }
+
+    std::vector<Relocation<ELFT>> Relocations;
+    const Elf_Shdr *RelocSymTab = nullptr;
+    if (const Elf_Shdr *RelocSection = RelocationMap.lookup(ELFSection)) {
+      forEachRelocationDo(*RelocSection,
+                          [&](const Relocation<ELFT> &R, unsigned Ndx,
+                              const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
+                            RelocSymTab = SymTab;
+                            Relocations.push_back(R);
+                          });
+      llvm::stable_sort(Relocations, [](const auto &LHS, const auto &RHS) {
+        return LHS.Offset < RHS.Offset;
+      });
+    }
+
     printSFrameHeader(*Parser);
-    printSFrameFDEs(*Parser);
+    printSFrameFDEs(*Parser, Relocations, RelocSymTab);
   }
 }
 

>From 0e300262fc979b9f8c68528e9dba78848e6ab0d8 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 15 Aug 2025 11:57:26 +0200
Subject: [PATCH 2/2] use unique warnings

---
 .../tools/llvm-readobj/ELF/sframe-reloc.test  |  1 -
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 22 +++++++++----------
 2 files changed, 10 insertions(+), 13 deletions(-)

diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test b/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test
index 5a9a888938c62..e96ae023e460f 100644
--- a/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test
@@ -91,4 +91,3 @@ Symbols:
 #  CHECK-NEXT:      PC: 0xAD0030
 #       CHECK:    FuncDescEntry [2] {
 #  CHECK-NEXT:{{.*}}: warning: '[[FILE]]': unable to read an entry with index 4747 from SHT_SYMTAB section with index {{[0-9]*}}: can't read an entry at 0x{{[0-9a-f]*}}: it goes past the end of the section (0x30)
-
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 28d53ef622075..c1a2c38ea9b7c 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -6485,7 +6485,7 @@ void ELFDumper<ELFT>::printSFrameHeader(
   if (Expected<ArrayRef<uint8_t>> Aux = Parser.getAuxHeader())
     W.printHexList("Auxiliary header", *Aux);
   else
-    reportWarning(Aux.takeError(), FileName);
+    reportUniqueWarning(Aux.takeError());
 }
 
 template <typename ELFT>
@@ -6494,7 +6494,7 @@ void ELFDumper<ELFT>::printSFrameFDEs(
     ArrayRef<Relocation<ELFT>> Relocations, const Elf_Shdr *RelocSymTab) {
   typename SFrameParser<ELFT::Endianness>::FDERange FDEs;
   if (Error Err = Parser.fdes().moveInto(FDEs)) {
-    reportWarning(std::move(Err), FileName);
+    reportUniqueWarning(std::move(Err));
     return;
   }
 
@@ -6560,7 +6560,7 @@ void ELFDumper<ELFT>::printSFrameFDEs(
         W.printList("Extra Offsets", Offs);
     }
     if (Err)
-      reportWarning(std::move(Err), FileName);
+      reportUniqueWarning(std::move(Err));
   }
 }
 
@@ -6578,14 +6578,13 @@ uint64_t ELFDumper<ELFT>::getAndPrintSFrameFDEStartAddress(
     W.printHex("PC", Address);
   } else if (std::next(Reloc) != Relocations.end() &&
              std::next(Reloc)->Offset == Offset) {
-    reportWarning(createError(formatv(
-                      "more than one relocation at offset {0:x+}", Offset)),
-                  FileName);
+    reportUniqueWarning(
+        formatv("more than one relocation at offset {0:x+}", Offset));
     W.printHex("PC", Address);
   } else if (Expected<RelSymbol<ELFT>> RelSym =
                  getRelocationTarget(*Reloc, RelocSymTab);
              !RelSym) {
-    reportWarning(RelSym.takeError(), FileName);
+    reportUniqueWarning(RelSym.takeError());
     W.printHex("PC", Address);
   } else {
     // Exactly one relocation at the given offset. Print it.
@@ -6614,16 +6613,15 @@ void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
 
     StringRef SectionContent;
     if (Error Err = Section.getContents().moveInto(SectionContent)) {
-      reportWarning(std::move(Err), FileName);
+      reportUniqueWarning(std::move(Err));
       continue;
     }
 
     Expected<object::SFrameParser<E>> Parser = object::SFrameParser<E>::create(
         arrayRefFromStringRef(SectionContent), Section.getAddress());
     if (!Parser) {
-      reportWarning(createError("invalid sframe section: " +
-                                toString(Parser.takeError())),
-                    FileName);
+      reportUniqueWarning("invalid sframe section: " +
+                          toString(Parser.takeError()));
       continue;
     }
 
@@ -6632,7 +6630,7 @@ void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
     if (Error Err = Obj.getSectionAndRelocations(
                            [&](const Elf_Shdr &S) { return &S == ELFSection; })
                         .moveInto(RelocationMap)) {
-      reportWarning(std::move(Err), FileName);
+      reportUniqueWarning(std::move(Err));
     }
 
     std::vector<Relocation<ELFT>> Relocations;



More information about the llvm-commits mailing list