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

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 15 03:30:44 PDT 2025


Author: Pavel Labath
Date: 2025-08-15T10:30:41Z
New Revision: dab971ed23064c915941b463d4ffaf2356418726

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

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

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.

Added: 
    llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test

Modified: 
    llvm/include/llvm/Object/SFrameParser.h
    llvm/lib/Object/SFrameParser.cpp
    llvm/tools/llvm-readobj/ELFDumper.cpp

Removed: 
    


################################################################################
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..e96ae023e460f
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-reloc.test
@@ -0,0 +1,93 @@
+# 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..c1a2c38ea9b7c 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;
@@ -6479,15 +6485,16 @@ 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>
 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);
+    reportUniqueWarning(std::move(Err));
     return;
   }
 
@@ -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);
@@ -6553,13 +6560,49 @@ void ELFDumper<ELFT>::printSFrameFDEs(
         W.printList("Extra Offsets", Offs);
     }
     if (Err)
-      reportWarning(std::move(Err), FileName);
+      reportUniqueWarning(std::move(Err));
+  }
+}
+
+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) {
+    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) {
+    reportUniqueWarning(RelSym.takeError());
+    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.
@@ -6570,21 +6613,42 @@ 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;
     }
 
+    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)) {
+      reportUniqueWarning(std::move(Err));
+    }
+
+    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);
   }
 }
 


        


More information about the llvm-commits mailing list