[llvm] a82ca1b - [Object] Parsing and dumping of SFrame Frame Row Entries (#151301)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 7 23:22:11 PDT 2025


Author: Pavel Labath
Date: 2025-08-08T08:22:08+02:00
New Revision: a82ca1b5603a4ed9598b784f703d908f32e970b8

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

LOG: [Object] Parsing and dumping of SFrame Frame Row Entries (#151301)

The trickiest part here is that the FREs have a variable size, in two
(or three?) dimensions:
- the size of the StartAddress field. This determined by the FDE they
are in, so it is uniform across all FREs in one FDE.
- the number and sizes of offsets following the FRE. This can be
different for each FRE.

While vending this information through a template API would be possible,
I believe such an approach would be very unwieldy, and it would still
require a sequential scan through the FRE list. This is why I'm
implementing this by reading the data into a common data structure using
the fallible iterator pattern.

For more information about the SFrame unwind format, see the
[specification](https://sourceware.org/binutils/wiki/sframe) and the
related
[RFC](https://discourse.llvm.org/t/rfc-adding-sframe-support-to-llvm/86900).

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

Modified: 
    llvm/include/llvm/BinaryFormat/SFrame.h
    llvm/include/llvm/Object/SFrameParser.h
    llvm/lib/BinaryFormat/SFrame.cpp
    llvm/lib/Object/SFrameParser.cpp
    llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
    llvm/tools/llvm-readobj/ELFDumper.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h
index 0c6c4d176eaec..74e47ea8acca9 100644
--- a/llvm/include/llvm/BinaryFormat/SFrame.h
+++ b/llvm/include/llvm/BinaryFormat/SFrame.h
@@ -169,6 +169,7 @@ LLVM_ABI ArrayRef<EnumEntry<FREType>> getFRETypes();
 LLVM_ABI ArrayRef<EnumEntry<FDEType>> getFDETypes();
 LLVM_ABI ArrayRef<EnumEntry<AArch64PAuthKey>> getAArch64PAuthKeys();
 LLVM_ABI ArrayRef<EnumEntry<FREOffset>> getFREOffsets();
+LLVM_ABI ArrayRef<EnumEntry<BaseReg>> getBaseRegisters();
 
 } // namespace sframe
 } // namespace llvm

diff  --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h
index 245e7ba40d0b8..539bb1872cc99 100644
--- a/llvm/include/llvm/Object/SFrameParser.h
+++ b/llvm/include/llvm/Object/SFrameParser.h
@@ -10,6 +10,7 @@
 #define LLVM_OBJECT_SFRAME_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/fallible_iterator.h"
 #include "llvm/BinaryFormat/SFrame.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
@@ -19,6 +20,8 @@ namespace llvm {
 namespace object {
 
 template <endianness E> class SFrameParser {
+  class FallibleFREIterator;
+
 public:
   static Expected<SFrameParser> create(ArrayRef<uint8_t> Contents,
                                        uint64_t SectionAddress);
@@ -42,6 +45,21 @@ template <endianness E> class SFrameParser {
   // objects returned by the `fdes()` function.
   uint64_t getAbsoluteStartAddress(typename FDERange::iterator FDE) const;
 
+  struct FrameRowEntry {
+    uint32_t StartAddress;
+    sframe::FREInfo<endianness::native> Info;
+    SmallVector<int32_t, 3> Offsets;
+  };
+
+  using fre_iterator = fallible_iterator<FallibleFREIterator>;
+  iterator_range<fre_iterator> fres(const sframe::FuncDescEntry<E> &FDE,
+                                    Error &Err) const;
+
+  std::optional<int32_t> getCFAOffset(const FrameRowEntry &FRE) const;
+  std::optional<int32_t> getRAOffset(const FrameRowEntry &FRE) const;
+  std::optional<int32_t> getFPOffset(const FrameRowEntry &FRE) const;
+  ArrayRef<int32_t> getExtraOffsets(const FrameRowEntry &FRE) const;
+
 private:
   ArrayRef<uint8_t> Data;
   uint64_t SectionAddress;
@@ -54,6 +72,39 @@ template <endianness E> class SFrameParser {
   uint64_t getFDEBase() const {
     return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff;
   }
+
+  uint64_t getFREBase() const {
+    return getFDEBase() + Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>);
+  }
+};
+
+template <endianness E> class SFrameParser<E>::FallibleFREIterator {
+public:
+  // NB: This iterator starts out in the before_begin() state. It must be
+  // ++'ed to reach the first element.
+  FallibleFREIterator(ArrayRef<uint8_t> Data, sframe::FREType FREType,
+                      uint32_t Idx, uint32_t Size, uint64_t Offset)
+      : Data(Data), FREType(FREType), Idx(Idx), Size(Size), Offset(Offset) {}
+
+  Error inc();
+  const FrameRowEntry &operator*() const { return FRE; }
+
+  friend bool operator==(const FallibleFREIterator &LHS,
+                         const FallibleFREIterator &RHS) {
+    assert(LHS.Data.data() == RHS.Data.data());
+    assert(LHS.Data.size() == RHS.Data.size());
+    assert(LHS.FREType == RHS.FREType);
+    assert(LHS.Size == RHS.Size);
+    return LHS.Idx == RHS.Idx;
+  }
+
+private:
+  ArrayRef<uint8_t> Data;
+  sframe::FREType FREType;
+  uint32_t Idx;
+  uint32_t Size;
+  uint64_t Offset;
+  FrameRowEntry FRE;
 };
 
 extern template class LLVM_TEMPLATE_ABI SFrameParser<endianness::big>;

diff  --git a/llvm/lib/BinaryFormat/SFrame.cpp b/llvm/lib/BinaryFormat/SFrame.cpp
index f1765d7f3e852..8076a26f27f32 100644
--- a/llvm/lib/BinaryFormat/SFrame.cpp
+++ b/llvm/lib/BinaryFormat/SFrame.cpp
@@ -68,3 +68,11 @@ ArrayRef<EnumEntry<sframe::FREOffset>> sframe::getFREOffsets() {
   };
   return ArrayRef(FREOffsets);
 }
+
+ArrayRef<EnumEntry<sframe::BaseReg>> sframe::getBaseRegisters() {
+  static constexpr EnumEntry<sframe::BaseReg> BaseRegs[] = {
+      {"FP", sframe::BaseReg::FP},
+      {"SP", sframe::BaseReg::SP},
+  };
+  return ArrayRef(BaseRegs);
+}

diff  --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp
index 5863490634e32..825b7af368211 100644
--- a/llvm/lib/Object/SFrameParser.cpp
+++ b/llvm/lib/Object/SFrameParser.cpp
@@ -32,14 +32,25 @@ getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) {
 }
 
 template <typename T>
-static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
-                                          uint64_t Offset) {
+static Expected<ArrayRef<T>>
+getDataSliceAsArrayOf(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Count) {
   static_assert(std::is_trivial_v<T>);
-  Expected<ArrayRef<uint8_t>> Slice = getDataSlice(Data, Offset, sizeof(T));
+  Expected<ArrayRef<uint8_t>> Slice =
+      getDataSlice(Data, Offset, sizeof(T) * Count);
   if (!Slice)
     return Slice.takeError();
 
-  return *reinterpret_cast<const T *>(Slice->data());
+  return ArrayRef(reinterpret_cast<const T *>(Slice->data()), Count);
+}
+
+template <typename T>
+static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
+                                          uint64_t Offset) {
+  Expected<ArrayRef<T>> Array = getDataSliceAsArrayOf<T>(Data, Offset, 1);
+  if (!Array)
+    return Array.takeError();
+
+  return Array->front();
 }
 
 template <endianness E>
@@ -100,6 +111,119 @@ uint64_t SFrameParser<E>::getAbsoluteStartAddress(
   return Result;
 }
 
+template <typename EndianT>
+static Error readArray(ArrayRef<uint8_t> Data, uint64_t Count, uint64_t &Offset,
+                       SmallVectorImpl<int32_t> &Vec) {
+  Expected<ArrayRef<EndianT>> RawArray =
+      getDataSliceAsArrayOf<EndianT>(Data, Offset, Count);
+  if (!RawArray)
+    return RawArray.takeError();
+  Offset += Count * sizeof(EndianT);
+  Vec.resize(Count);
+  llvm::copy(*RawArray, Vec.begin());
+  return Error::success();
+}
+
+template <typename T, endianness E>
+static Error readFRE(ArrayRef<uint8_t> Data, uint64_t &Offset,
+                     typename SFrameParser<E>::FrameRowEntry &FRE) {
+  Expected<sframe::FrameRowEntry<T, E>> RawFRE =
+      getDataSliceAs<sframe::FrameRowEntry<T, E>>(Data, Offset);
+  if (!RawFRE)
+    return RawFRE.takeError();
+
+  Offset += sizeof(*RawFRE);
+  FRE.StartAddress = RawFRE->StartAddress;
+  FRE.Info.Info = RawFRE->Info.Info;
+
+  switch (FRE.Info.getOffsetSize()) {
+  case sframe::FREOffset::B1:
+    return readArray<sframe::detail::packed<int8_t, E>>(
+        Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
+  case sframe::FREOffset::B2:
+    return readArray<sframe::detail::packed<int16_t, E>>(
+        Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
+  case sframe::FREOffset::B4:
+    return readArray<sframe::detail::packed<int32_t, E>>(
+        Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
+  default:
+    return createError("unsupported/unknown offset size");
+  }
+}
+
+template <endianness E> Error SFrameParser<E>::FallibleFREIterator::inc() {
+  if (++Idx == Size)
+    return Error::success();
+
+  switch (FREType) {
+  case sframe::FREType::Addr1:
+    return readFRE<uint8_t, E>(Data, Offset, FRE);
+  case sframe::FREType::Addr2:
+    return readFRE<uint16_t, E>(Data, Offset, FRE);
+  case sframe::FREType::Addr4:
+    return readFRE<uint32_t, E>(Data, Offset, FRE);
+  default:
+    return createError("invalid/unsupported FRE type");
+  }
+}
+
+template <endianness E>
+iterator_range<typename SFrameParser<E>::fre_iterator>
+SFrameParser<E>::fres(const sframe::FuncDescEntry<E> &FDE, Error &Err) const {
+  uint64_t Offset = getFREBase() + FDE.StartFREOff;
+  fre_iterator BeforeBegin = make_fallible_itr(
+      FallibleFREIterator(Data, FDE.getFREType(), -1, FDE.NumFREs, Offset),
+      Err);
+  fre_iterator End = make_fallible_end(
+      FallibleFREIterator(Data, FDE.getFREType(), FDE.NumFREs, FDE.NumFREs,
+                          /*Offset=*/0));
+  return {++BeforeBegin, End};
+}
+
+static std::optional<int32_t> getOffset(ArrayRef<int32_t> Offsets, size_t Idx) {
+  if (Offsets.size() > Idx)
+    return Offsets[Idx];
+  return std::nullopt;
+}
+
+// The interpretation of offsets is ABI-specific. The implementation of this and
+// the following functions may need to be adjusted when adding support for a new
+// ABI.
+template <endianness E>
+std::optional<int32_t>
+SFrameParser<E>::getCFAOffset(const FrameRowEntry &FRE) const {
+  return getOffset(FRE.Offsets, 0);
+}
+
+template <endianness E>
+std::optional<int32_t>
+SFrameParser<E>::getRAOffset(const FrameRowEntry &FRE) const {
+  if (usesFixedRAOffset())
+    return Header.CFAFixedRAOffset;
+  return getOffset(FRE.Offsets, 1);
+}
+
+template <endianness E>
+std::optional<int32_t>
+SFrameParser<E>::getFPOffset(const FrameRowEntry &FRE) const {
+  if (usesFixedFPOffset())
+    return Header.CFAFixedFPOffset;
+  return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2);
+}
+
+template <endianness E>
+ArrayRef<int32_t>
+SFrameParser<E>::getExtraOffsets(const FrameRowEntry &FRE) const {
+  size_t UsedOffsets = 1; // CFA
+  if (!usesFixedRAOffset())
+    ++UsedOffsets;
+  if (!usesFixedFPOffset())
+    ++UsedOffsets;
+  if (FRE.Offsets.size() > UsedOffsets)
+    return ArrayRef(FRE.Offsets).drop_front(UsedOffsets);
+  return {};
+}
+
 template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser<endianness::big>;
 template class LLVM_EXPORT_TEMPLATE
     llvm::object::SFrameParser<endianness::little>;

diff  --git a/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test b/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
index dee40180c42e6..b9075a81eba4b 100644
--- a/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
@@ -108,6 +108,8 @@ Sections:
 #  CASE1-NEXT:      }
 #  CASE1-NEXT:      Repetitive block size: 0xDE
 #  CASE1-NEXT:      Padding2: 0xAD
+#  CASE1-NEXT:      FREs [
+#  CASE1-NEXT:      ]
 #  CASE1-NEXT:    }
 #  CASE1-NEXT:  ]
 #  CASE1-NEXT:}
@@ -169,6 +171,8 @@ Sections:
 #  CASE1-NEXT:      }
 #  CASE1-NEXT:      Repetitive block size (unused): 0xDE
 #  CASE1-NEXT:      Padding2: 0xAD
+#  CASE1-NEXT:      FREs [
+#  CASE1-NEXT:      ]
 #  CASE1-NEXT:    }
 #  CASE1-NEXT:  ]
 #  CASE1-NEXT:}
@@ -196,7 +200,7 @@ Sections:
       0x00, 0xde, 0xad, 0x00,  # Start Address
       0x00, 0x00, 0x01, 0xbe,  # Size
       0x00, 0x00, 0x00, 0x10,  # Start FRE Offset
-      0x00, 0x00, 0x00, 0x10,  # Number of FREs
+      0x00, 0x00, 0x00, 0x00,  # Number of FREs
       0x02, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
     ]
 # CASE2-LABEL:SFrame section '.sframe' {
@@ -223,7 +227,7 @@ Sections:
 #  CASE2-NEXT:      PC: 0xDEAD1C
 #  CASE2-NEXT:      Size: 0x1BE
 #  CASE2-NEXT:      Start FRE Offset: 0x10
-#  CASE2-NEXT:      Num FREs: 16
+#  CASE2-NEXT:      Num FREs: 0
 #  CASE2-NEXT:      Info {
 #  CASE2-NEXT:        FRE Type: Addr4 (0x2)
 #  CASE2-NEXT:        FDE Type: PCInc (0x0)
@@ -232,6 +236,8 @@ Sections:
 #  CASE2-NEXT:      }
 #  CASE2-NEXT:      Repetitive block size (unused): 0xDE
 #  CASE2-NEXT:      Padding2: 0xAD00
+#  CASE2-NEXT:      FREs [
+#  CASE2-NEXT:      ]
 #  CASE2-NEXT:    }
 #  CASE2-NEXT:  ]
 #  CASE2-NEXT:}

diff  --git a/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test b/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test
new file mode 100644
index 0000000000000..9711d04fe7974
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test
@@ -0,0 +1,284 @@
+# RUN: yaml2obj --docnum=1 %s -o %t.1
+# RUN: llvm-readobj --sframe=.sframe_eof_address --sframe=.sframe_eof_offset --sframe \
+# RUN:   %t.1 2>&1 | \
+# RUN:   FileCheck %s --strict-whitespace --match-full-lines \
+# RUN:   -DFILE=%t.1 --check-prefix=CASE1
+
+# RUN: yaml2obj --docnum=2 %s -o %t.2
+# RUN: llvm-readobj --sframe %t.2 2>&1 | \
+# RUN:   FileCheck %s --strict-whitespace --match-full-lines \
+# RUN:   -DFILE=%t.2 --check-prefix=CASE2
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+Sections:
+  - Name:  .sframe_eof_address
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x04,  # Preamble (magic, version, flags)
+      # Header:
+      0x03, 0x42, 0x47, 0x00,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x01, 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, 0xde, 0xad, 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
+
+      # FRE[0]:
+      0x00                     # Start Address, (missing) Info
+    ]
+# CASE1-LABEL:SFrame section '.sframe_eof_address' {
+#       CASE1:    FuncDescEntry [0] {
+#       CASE1:      Info {
+#  CASE1-NEXT:        FRE Type: Addr1 (0x0)
+#  CASE1-NEXT:        FDE Type: PCInc (0x0)
+#       CASE1:      FREs [
+#  CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x31 while reading [0x30, 0x32)
+#  CASE1-NEXT:      ]
+
+  - Name:  .sframe_eof_offset
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x04,  # Preamble (magic, version, flags)
+      # Header:
+      0x03, 0x42, 0x47, 0x00,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x01, 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, 0xde, 0xad, 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
+
+      # FRE[0]:
+      0x00, 0x02               # Start Address, Info, (missing) Offsets
+    ]
+# CASE1-LABEL:SFrame section '.sframe_eof_offset' {
+#       CASE1:    FuncDescEntry [0] {
+#       CASE1:      Info {
+#  CASE1-NEXT:        FRE Type: Addr1 (0x0)
+#  CASE1-NEXT:        FDE Type: PCInc (0x0)
+#       CASE1:      FREs [
+#  CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x32 while reading [0x32, 0x33)
+#  CASE1-NEXT:      ]
+
+  - 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
+      0x02, 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
+      0x02, 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
+      0x08, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x03, 0x00, 0x00, 0x00,  # Number of FREs
+      0x11, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+
+      # FRE[0,0]: Zero offsets
+      0x00, 0x00,              # Start Address, Info
+
+      # FRE[0,1]: One four-byte offset, SP-based
+      0x01, 0x43,              # Start Address, Info
+      0x74, 0x00, 0x00, 0x00,  # Offset[0]
+
+      # FRE[1,0]: Two two-byte offsets, FP-based
+      0x00, 0x00, 0x24,        # Start Address, Info
+      0x10, 0x00, 0x20, 0x00,  # Offset[0], Offset[1]
+
+      # FRE[1,1]: Four one-byte offsets, FP-based
+      0x00, 0x01, 0x08,        # Start Address, Info
+      0x10, 0x20, 0x30, 0x40,  # Offset[0-3]
+    ]
+## Testing:
+## - little-endian support
+## - AMD64 ABI
+## - one and two byte address sizes
+## - PCInc and PCMask FDE types
+## - all offset sizes
+## - various offset counts
+## - hitting EOF after printing some number of FREs
+# CASE1-LABEL:SFrame section '.sframe' {
+#       CASE1:    ABI: AMD64EndianLittle (0x3)
+#       CASE1:    CFA fixed RA offset: 64
+#       CASE1:    FuncDescEntry [0] {
+#  CASE1-NEXT:      PC: 0xDE007F
+#       CASE1:      Info {
+#  CASE1-NEXT:        FRE Type: Addr1 (0x0)
+#  CASE1-NEXT:        FDE Type: PCInc (0x0)
+#       CASE1:      FREs [
+#  CASE1-NEXT:        Frame Row Entry {
+#  CASE1-NEXT:          Start Address: 0xDE007F
+#  CASE1-NEXT:          Return Address Signed: No
+#  CASE1-NEXT:          Offset Size: B1 (0x0)
+#  CASE1-NEXT:          Base Register: FP (0x0)
+#  CASE1-NEXT:          RA Offset: 64
+#  CASE1-NEXT:        }
+#  CASE1-NEXT:        Frame Row Entry {
+#  CASE1-NEXT:          Start Address: 0xDE0080
+#  CASE1-NEXT:          Return Address Signed: No
+#  CASE1-NEXT:          Offset Size: B4 (0x2)
+#  CASE1-NEXT:          Base Register: SP (0x1)
+#  CASE1-NEXT:          CFA Offset: 116
+#  CASE1-NEXT:          RA Offset: 64
+#  CASE1-NEXT:        }
+#  CASE1-NEXT:      ]
+#       CASE1:    FuncDescEntry [1] {
+#  CASE1-NEXT:      PC: 0xAD0093
+#       CASE1:      Info {
+#  CASE1-NEXT:        FRE Type: Addr2 (0x1)
+#  CASE1-NEXT:        FDE Type: PCMask (0x1)
+#       CASE1:      FREs [
+#  CASE1-NEXT:        Frame Row Entry {
+#  CASE1-NEXT:          Start Address: 0x0
+#  CASE1-NEXT:          Return Address Signed: No
+#  CASE1-NEXT:          Offset Size: B2 (0x1)
+#  CASE1-NEXT:          Base Register: FP (0x0)
+#  CASE1-NEXT:          CFA Offset: 16
+#  CASE1-NEXT:          RA Offset: 64
+#  CASE1-NEXT:          FP Offset: 32
+#  CASE1-NEXT:        }
+#  CASE1-NEXT:        Frame Row Entry {
+#  CASE1-NEXT:          Start Address: 0x100
+#  CASE1-NEXT:          Return Address Signed: No
+#  CASE1-NEXT:          Offset Size: B1 (0x0)
+#  CASE1-NEXT:          Base Register: FP (0x0)
+#  CASE1-NEXT:          CFA Offset: 16
+#  CASE1-NEXT:          RA Offset: 64
+#  CASE1-NEXT:          FP Offset: 32
+#  CASE1-NEXT:          Extra Offsets: [48, 64]
+#  CASE1-NEXT:        }
+#  CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x5a while reading [0x5a, 0x5d)
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2MSB
+  Type:  ET_EXEC
+Sections:
+  - Name:  .sframe
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xde, 0xe2, 0x02, 0x05,  # Preamble (magic, version, flags)
+      # Header:
+      0x01, 0x42, 0x47, 0x00,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x00, 0x00, 0x00, 0x01,  # Number of FDEs
+      0x00, 0x00, 0x00, 0x10,  # Number of FREs
+      0x00, 0x00, 0x10, 0x00,  # FRE length
+      0x00, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x00, 0x01, 0x00,  # FRE offset
+
+      # FDE:
+      0x00, 0xde, 0x00, 0x00,  # Start Address
+      0x00, 0x00, 0x01, 0xbe,  # Size
+      0x00, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x00, 0x00, 0x00, 0x05,  # Number of FREs
+      0x02, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+
+      # FRE[0]: Zero offsets
+      0x00, 0x00, 0x00, 0x00,  # Start Address
+      0x00,                    # Info
+
+      # FRE[1]: One offset
+      0x00, 0x00, 0x00, 0x01,  # Start Address
+      0x82, 0x10,              # Info, Offset[0]
+
+      # FRE[2]: Two offsets
+      0x00, 0x00, 0x00, 0x02,  # Start Address
+      0x04, 0x10, 0x20,        # Info, Offset[0-1]
+
+      # FRE[3]: Three offsets
+      0x00, 0x00, 0x00, 0x03,  # Start Address
+      0x86, 0x10, 0x20, 0x30,  # Info, Offset[0-2]
+
+      # FRE[4]: Four offsets
+      0x00, 0x00, 0x00, 0x04,  # Start Address
+      0x08,                    # Info
+      0x10, 0x20, 0x30, 0x40,  # Offset[0-3]
+    ]
+## Testing:
+## - big-endian support
+## - ARM64 ABI
+## - four-byte address sizes
+## - return address signing
+## - various offset counts
+# CASE2-LABEL:SFrame section '.sframe' {
+#       CASE2:    ABI: AArch64EndianBig (0x1)
+#       CASE2:    FuncDescEntry [0] {
+#  CASE2-NEXT:      PC: 0xDE001C
+#       CASE2:      Info {
+#  CASE2-NEXT:        FRE Type: Addr4 (0x2)
+#  CASE2-NEXT:        FDE Type: PCInc (0x0)
+#       CASE2:      FREs [
+#  CASE2-NEXT:        Frame Row Entry {
+#  CASE2-NEXT:          Start Address: 0xDE001C
+#  CASE2-NEXT:          Return Address Signed: No
+#  CASE2-NEXT:          Offset Size: B1 (0x0)
+#  CASE2-NEXT:          Base Register: FP (0x0)
+#  CASE2-NEXT:        }
+#  CASE2-NEXT:        Frame Row Entry {
+#  CASE2-NEXT:          Start Address: 0xDE001D
+#  CASE2-NEXT:          Return Address Signed: Yes
+#  CASE2-NEXT:          Offset Size: B1 (0x0)
+#  CASE2-NEXT:          Base Register: FP (0x0)
+#  CASE2-NEXT:          CFA Offset: 16
+#  CASE2-NEXT:        }
+#  CASE2-NEXT:        Frame Row Entry {
+#  CASE2-NEXT:          Start Address: 0xDE001E
+#  CASE2-NEXT:          Return Address Signed: No
+#  CASE2-NEXT:          Offset Size: B1 (0x0)
+#  CASE2-NEXT:          Base Register: FP (0x0)
+#  CASE2-NEXT:          CFA Offset: 16
+#  CASE2-NEXT:          RA Offset: 32
+#  CASE2-NEXT:        }
+#  CASE2-NEXT:        Frame Row Entry {
+#  CASE2-NEXT:          Start Address: 0xDE001F
+#  CASE2-NEXT:          Return Address Signed: Yes
+#  CASE2-NEXT:          Offset Size: B1 (0x0)
+#  CASE2-NEXT:          Base Register: FP (0x0)
+#  CASE2-NEXT:          CFA Offset: 16
+#  CASE2-NEXT:          RA Offset: 32
+#  CASE2-NEXT:          FP Offset: 48
+#  CASE2-NEXT:        }
+#  CASE2-NEXT:        Frame Row Entry {
+#  CASE2-NEXT:          Start Address: 0xDE0020
+#  CASE2-NEXT:          Return Address Signed: No
+#  CASE2-NEXT:          Offset Size: B1 (0x0)
+#  CASE2-NEXT:          Base Register: FP (0x0)
+#  CASE2-NEXT:          CFA Offset: 16
+#  CASE2-NEXT:          RA Offset: 32
+#  CASE2-NEXT:          FP Offset: 48
+#  CASE2-NEXT:          Extra Offsets: [64]
+#  CASE2-NEXT:        }

diff  --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 26a84fae5f7e0..ed29e0bf3f7cd 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -6497,7 +6497,8 @@ void ELFDumper<ELFT>::printSFrameFDEs(
         W,
         formatv("FuncDescEntry [{0}]", std::distance(FDEs.begin(), It)).str());
 
-    W.printHex("PC", Parser.getAbsoluteStartAddress(It));
+    uint64_t FDEStartAddress = Parser.getAbsoluteStartAddress(It);
+    W.printHex("PC", FDEStartAddress);
     W.printHex("Size", It->Size);
     W.printHex("Start FRE Offset", It->StartFREOff);
     W.printNumber("Num FREs", It->NumFREs);
@@ -6527,6 +6528,32 @@ void ELFDumper<ELFT>::printSFrameFDEs(
         It->RepSize);
 
     W.printHex("Padding2", It->Padding2);
+
+    ListScope FREListScope(W, "FREs");
+    Error Err = Error::success();
+    for (const typename SFrameParser<ELFT::Endianness>::FrameRowEntry &FRE :
+         Parser.fres(*It, Err)) {
+      DictScope FREScope(W, "Frame Row Entry");
+      W.printHex(
+          "Start Address",
+          (It->getFDEType() == sframe::FDEType::PCInc ? FDEStartAddress : 0) +
+              FRE.StartAddress);
+      W.printBoolean("Return Address Signed", FRE.Info.isReturnAddressSigned());
+      W.printEnum("Offset Size", FRE.Info.getOffsetSize(),
+                  sframe::getFREOffsets());
+      W.printEnum("Base Register", FRE.Info.getBaseRegister(),
+                  sframe::getBaseRegisters());
+      if (std::optional<int32_t> Off = Parser.getCFAOffset(FRE))
+        W.printNumber("CFA Offset", *Off);
+      if (std::optional<int32_t> Off = Parser.getRAOffset(FRE))
+        W.printNumber("RA Offset", *Off);
+      if (std::optional<int32_t> Off = Parser.getFPOffset(FRE))
+        W.printNumber("FP Offset", *Off);
+      if (ArrayRef<int32_t> Offs = Parser.getExtraOffsets(FRE); !Offs.empty())
+        W.printList("Extra Offsets", Offs);
+    }
+    if (Err)
+      reportWarning(std::move(Err), FileName);
   }
 }
 


        


More information about the llvm-commits mailing list