[llvm] [Object] Parsing and dumping of SFrame FDEs (PR #149828)

Pavel Labath via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 30 00:26:07 PDT 2025


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

>From c797a3e0c87de65d281b77f19bceb0911b6eb0ce Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 11 Jul 2025 11:21:07 +0200
Subject: [PATCH 1/4] [Object] Parsing and dumping of SFrame FDEs

Also known as Function Description Entries. The entries occupy a
contiguous piece of the section, so the code is mostly straight-forward.

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).
---
 llvm/include/llvm/BinaryFormat/SFrame.h       |  24 +-
 .../llvm/BinaryFormat/SFrameConstants.def     |  41 +++-
 llvm/include/llvm/Object/SFrameParser.h       |  19 +-
 llvm/lib/BinaryFormat/SFrame.cpp              |  33 +++
 llvm/lib/Object/SFrameParser.cpp              |  64 ++++-
 .../tools/llvm-readobj/ELF/sframe-fde.test    | 232 ++++++++++++++++++
 .../tools/llvm-readobj/ELF/sframe-header.test |  67 ++++-
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 115 ++++++---
 8 files changed, 530 insertions(+), 65 deletions(-)
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/sframe-fde.test

diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h
index 98dbe38fb2bc4..3ecaa32f373cf 100644
--- a/llvm/include/llvm/BinaryFormat/SFrame.h
+++ b/llvm/include/llvm/BinaryFormat/SFrame.h
@@ -49,29 +49,27 @@ enum class ABI : uint8_t {
 
 /// SFrame FRE Types. Bits 0-3 of FuncDescEntry.Info.
 enum class FREType : uint8_t {
-  Addr1 = 0,
-  Addr2 = 1,
-  Addr4 = 2,
+#define HANDLE_SFRAME_FRE_TYPE(CODE, NAME) NAME = CODE,
+#include "llvm/BinaryFormat/SFrameConstants.def"
 };
 
 /// SFrame FDE Types. Bit 4 of FuncDescEntry.Info.
 enum class FDEType : uint8_t {
-  PCInc = 0,
-  PCMask = 1,
+#define HANDLE_SFRAME_FDE_TYPE(CODE, NAME) NAME = CODE,
+#include "llvm/BinaryFormat/SFrameConstants.def"
 };
 
 /// Speficies key used for signing return addresses. Bit 5 of
 /// FuncDescEntry.Info.
 enum class AArch64PAuthKey : uint8_t {
-  A = 0,
-  B = 1,
+#define HANDLE_SFRAME_AARCH64_PAUTH_KEY(CODE, NAME) NAME = CODE,
+#include "llvm/BinaryFormat/SFrameConstants.def"
 };
 
-/// Size of stack offsets. Bits 5-6 of FREInfo.Info.
+/// Size of stack offsets. Bits 6-7 of FREInfo.Info.
 enum class FREOffset : uint8_t {
-  B1 = 0,
-  B2 = 1,
-  B4 = 2,
+#define HANDLE_SFRAME_FRE_OFFSET(CODE, NAME) NAME = CODE,
+#include "llvm/BinaryFormat/SFrameConstants.def"
 };
 
 /// Stack frame base register. Bit 0 of FREInfo.Info.
@@ -166,6 +164,10 @@ template <endianness E> using FrameRowEntryAddr4 = FrameRowEntry<uint32_t, E>;
 ArrayRef<EnumEntry<Version>> getVersions();
 ArrayRef<EnumEntry<Flags>> getFlags();
 ArrayRef<EnumEntry<ABI>> getABIs();
+ArrayRef<EnumEntry<FREType>> getFRETypes();
+ArrayRef<EnumEntry<FDEType>> getFDETypes();
+ArrayRef<EnumEntry<AArch64PAuthKey>> getAArch64PAuthKeys();
+ArrayRef<EnumEntry<FREOffset>> getFREOffsets();
 
 } // namespace sframe
 } // namespace llvm
diff --git a/llvm/include/llvm/BinaryFormat/SFrameConstants.def b/llvm/include/llvm/BinaryFormat/SFrameConstants.def
index 643b15f438c86..fddd440e41f32 100644
--- a/llvm/include/llvm/BinaryFormat/SFrameConstants.def
+++ b/llvm/include/llvm/BinaryFormat/SFrameConstants.def
@@ -6,8 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#if !(defined(HANDLE_SFRAME_VERSION) || defined(HANDLE_SFRAME_FLAG) ||  \
-      defined(HANDLE_SFRAME_ABI))
+#if !(defined(HANDLE_SFRAME_VERSION) || defined(HANDLE_SFRAME_FLAG) ||         \
+      defined(HANDLE_SFRAME_ABI) || defined(HANDLE_SFRAME_FRE_TYPE) ||         \
+      defined(HANDLE_SFRAME_FDE_TYPE) ||                                       \
+      defined(HANDLE_SFRAME_AARCH64_PAUTH_KEY) ||                              \
+      defined(HANDLE_SFRAME_FRE_OFFSET))
 #error "Missing HANDLE_SFRAME definition"
 #endif
 
@@ -23,6 +26,22 @@
 #define HANDLE_SFRAME_ABI(CODE, NAME)
 #endif
 
+#ifndef HANDLE_SFRAME_FRE_TYPE
+#define HANDLE_SFRAME_FRE_TYPE(CODE, NAME)
+#endif
+
+#ifndef HANDLE_SFRAME_FDE_TYPE
+#define HANDLE_SFRAME_FDE_TYPE(CODE, NAME)
+#endif
+
+#ifndef HANDLE_SFRAME_AARCH64_PAUTH_KEY
+#define HANDLE_SFRAME_AARCH64_PAUTH_KEY(CODE, NAME)
+#endif
+
+#ifndef HANDLE_SFRAME_FRE_OFFSET
+#define HANDLE_SFRAME_FRE_OFFSET(CODE, NAME)
+#endif
+
 HANDLE_SFRAME_VERSION(0x01, V1)
 HANDLE_SFRAME_VERSION(0x02, V2)
 
@@ -34,6 +53,24 @@ HANDLE_SFRAME_ABI(0x01, AArch64EndianBig)
 HANDLE_SFRAME_ABI(0x02, AArch64EndianLittle)
 HANDLE_SFRAME_ABI(0x03, AMD64EndianLittle)
 
+HANDLE_SFRAME_FRE_TYPE(0x00, Addr1)
+HANDLE_SFRAME_FRE_TYPE(0x01, Addr2)
+HANDLE_SFRAME_FRE_TYPE(0x02, Addr4)
+
+HANDLE_SFRAME_FDE_TYPE(0, PCInc)
+HANDLE_SFRAME_FDE_TYPE(1, PCMask)
+
+HANDLE_SFRAME_AARCH64_PAUTH_KEY(0, A)
+HANDLE_SFRAME_AARCH64_PAUTH_KEY(1, B)
+
+HANDLE_SFRAME_FRE_OFFSET(0, B1)
+HANDLE_SFRAME_FRE_OFFSET(1, B2)
+HANDLE_SFRAME_FRE_OFFSET(2, B4)
+
 #undef HANDLE_SFRAME_VERSION
 #undef HANDLE_SFRAME_FLAG
 #undef HANDLE_SFRAME_ABI
+#undef HANDLE_SFRAME_FRE_TYPE
+#undef HANDLE_SFRAME_FDE_TYPE
+#undef HANDLE_SFRAME_AARCH64_PAUTH_KEY
+#undef HANDLE_SFRAME_FRE_OFFSET
diff --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h
index cf4fe20e84431..974c757419334 100644
--- a/llvm/include/llvm/Object/SFrameParser.h
+++ b/llvm/include/llvm/Object/SFrameParser.h
@@ -19,11 +19,14 @@ namespace object {
 
 template <endianness E> class SFrameParser {
 public:
-  static Expected<SFrameParser> create(ArrayRef<uint8_t> Contents);
+  static Expected<SFrameParser> create(ArrayRef<uint8_t> Contents,
+                                       uint64_t SectionAddress);
 
   const sframe::Preamble<E> &getPreamble() const { return Header.Preamble; }
   const sframe::Header<E> &getHeader() const { return Header; }
 
+  Expected<ArrayRef<uint8_t>> getAuxHeader() const;
+
   bool usesFixedRAOffset() const {
     return getHeader().ABIArch == sframe::ABI::AMD64EndianLittle;
   }
@@ -31,12 +34,22 @@ template <endianness E> class SFrameParser {
     return false; // Not used in any currently defined ABI.
   }
 
+  using FDERange = ArrayRef<sframe::FuncDescEntry<E>>;
+  Expected<FDERange> fdes() const;
+
+  // Decodes the start address of the given FDE, which must be one of the
+  // objects returned by the `fdes()` function.
+  uint64_t getAbsoluteStartAddress(typename FDERange::iterator FDE) const;
+
 private:
   ArrayRef<uint8_t> Data;
+  uint64_t SectionAddress;
   const sframe::Header<E> &Header;
 
-  SFrameParser(ArrayRef<uint8_t> Data, const sframe::Header<E> &Header)
-      : Data(Data), Header(Header) {}
+  SFrameParser(ArrayRef<uint8_t> Data, uint64_t SectionAddress, const sframe::Header<E> &Header)
+      : Data(Data), SectionAddress(SectionAddress), Header(Header) {}
+
+  uint64_t getFDEBegin() const { return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff; }
 };
 
 extern template class SFrameParser<endianness::big>;
diff --git a/llvm/lib/BinaryFormat/SFrame.cpp b/llvm/lib/BinaryFormat/SFrame.cpp
index 3b436afd32083..f1765d7f3e852 100644
--- a/llvm/lib/BinaryFormat/SFrame.cpp
+++ b/llvm/lib/BinaryFormat/SFrame.cpp
@@ -35,3 +35,36 @@ ArrayRef<EnumEntry<sframe::ABI>> sframe::getABIs() {
   };
   return ArrayRef(ABIs);
 }
+
+ArrayRef<EnumEntry<sframe::FREType>> sframe::getFRETypes() {
+  static constexpr EnumEntry<sframe::FREType> FRETypes[] = {
+#define HANDLE_SFRAME_FRE_TYPE(CODE, NAME) {#NAME, sframe::FREType::NAME},
+#include "llvm/BinaryFormat/SFrameConstants.def"
+  };
+  return ArrayRef(FRETypes);
+}
+
+ArrayRef<EnumEntry<sframe::FDEType>> sframe::getFDETypes() {
+  static constexpr EnumEntry<sframe::FDEType> FDETypes[] = {
+#define HANDLE_SFRAME_FDE_TYPE(CODE, NAME) {#NAME, sframe::FDEType::NAME},
+#include "llvm/BinaryFormat/SFrameConstants.def"
+  };
+  return ArrayRef(FDETypes);
+}
+
+ArrayRef<EnumEntry<sframe::AArch64PAuthKey>> sframe::getAArch64PAuthKeys() {
+  static constexpr EnumEntry<sframe::AArch64PAuthKey> AArch64PAuthKeys[] = {
+#define HANDLE_SFRAME_AARCH64_PAUTH_KEY(CODE, NAME)                            \
+  {#NAME, sframe::AArch64PAuthKey::NAME},
+#include "llvm/BinaryFormat/SFrameConstants.def"
+  };
+  return ArrayRef(AArch64PAuthKeys);
+}
+
+ArrayRef<EnumEntry<sframe::FREOffset>> sframe::getFREOffsets() {
+  static constexpr EnumEntry<sframe::FREOffset> FREOffsets[] = {
+#define HANDLE_SFRAME_FRE_OFFSET(CODE, NAME) {#NAME, sframe::FREOffset::NAME},
+#include "llvm/BinaryFormat/SFrameConstants.def"
+  };
+  return ArrayRef(FREOffsets);
+}
diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp
index 2d74d1d6b3827..aa3c481d35db4 100644
--- a/llvm/lib/Object/SFrameParser.cpp
+++ b/llvm/lib/Object/SFrameParser.cpp
@@ -14,23 +14,34 @@
 using namespace llvm;
 using namespace llvm::object;
 
-template <typename T>
-static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
-                                          uint64_t Offset) {
-  static_assert(std::is_trivial_v<T>);
-  if (Data.size() < Offset + sizeof(T)) {
+static Expected<ArrayRef<uint8_t>>
+getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) {
+  // Check for overflow.
+  if (Offset + Size < Offset || Offset + Size < Size ||
+      Offset + Size > Data.size()) {
     return createStringError(
         formatv("unexpected end of data at offset {0:x} while reading [{1:x}, "
                 "{2:x})",
-                Data.size(), Offset, Offset + sizeof(T))
+                Data.size(), Offset, Offset + Size)
             .str(),
         object_error::unexpected_eof);
   }
-  return *reinterpret_cast<const T *>(Data.data() + Offset);
+  return Data.slice(Offset, Size);
+}
+
+template <typename T>
+static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
+                                          uint64_t Offset) {
+  static_assert(std::is_trivial_v<T>);
+  Expected<ArrayRef<uint8_t>> Slice = getDataSlice(Data, Offset, sizeof(T));
+  if (!Slice)
+    return Slice.takeError();
+
+  return *reinterpret_cast<const T *>(Slice->data());
 }
 
 template <endianness E>
-Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents) {
+Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents, uint64_t SectionAddress) {
   Expected<const sframe::Preamble<E> &> Preamble =
       getDataSliceAs<sframe::Preamble<E>>(Contents, 0);
   if (!Preamble)
@@ -48,7 +59,42 @@ Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents) {
       getDataSliceAs<sframe::Header<E>>(Contents, 0);
   if (!Header)
     return Header.takeError();
-  return SFrameParser(Contents, *Header);
+  return SFrameParser(Contents, SectionAddress, *Header);
+}
+
+
+template<endianness E>
+Expected<ArrayRef<uint8_t>> SFrameParser<E>::getAuxHeader() const {
+  return getDataSlice(Data, sizeof(Header), Header.AuxHdrLen);
+}
+
+template<endianness E>
+Expected<ArrayRef<sframe::FuncDescEntry<E>>> SFrameParser<E>::fdes() const {
+  Expected<ArrayRef<uint8_t>> Slice = getDataSlice(
+      Data, getFDEBegin(), Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>));
+  if (!Slice)
+    return Slice.takeError();
+  return ArrayRef(
+      reinterpret_cast<const sframe::FuncDescEntry<E> *>(Slice->data()),
+      Header.NumFDEs);
+}
+
+template<endianness E>
+uint64_t SFrameParser<E>::getAbsoluteStartAddress(typename FDERange::iterator FDE) const {
+  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);
+
+    assert(DataPtr <= FDEPtr);
+    assert(FDEPtr < DataPtr + Data.size());
+
+    Result += FDEPtr - DataPtr;
+  }
+
+  return Result;
 }
 
 template class llvm::object::SFrameParser<endianness::big>;
diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test b/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
new file mode 100644
index 0000000000000..294b28d8f04b4
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
@@ -0,0 +1,232 @@
+## Check parsing and dumping of SFrame Function Descriptor Entries.
+# RUN: yaml2obj --docnum=1 %s -o %t.1
+# RUN: llvm-readobj --sframe=.sframe_short --sframe=.sframe_section_relative \
+# RUN:   --sframe=.sframe_fde_relative %t.1 2>&1 | \
+# RUN:   FileCheck %s --strict-whitespace --match-full-lines \
+# RUN:   -DFILE=%t.1 --check-prefix=CASE1
+
+## Check big-endian support.
+# 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_short
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x00,  # Preamble (magic, version, flags)
+      # Header:
+      0x03, 0x42, 0x47, 0x04,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x01, 0x00, 0x00, 0x00,  # Number of FDEs
+      0x10, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0x10, 0x00, 0x00,  # FRE length
+      0x00, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x01, 0x00, 0x00,  # FRE offset
+      0xde, 0xad, 0xbe, 0xef,  # AUX header
+      0x01, 0x02, 0x03, 0x04,  # Short FDE
+    ]
+# CASE1-LABEL:SFrame section '.sframe_short' {
+#       CASE1:  Header {
+#  CASE1-NEXT:    Magic: 0xDEE2
+#  CASE1-NEXT:    Version: V2 (0x2)
+#  CASE1-NEXT:    Flags [ (0x0)
+#  CASE1-NEXT:    ]
+#  CASE1-NEXT:    ABI: AMD64EndianLittle (0x3)
+#  CASE1-NEXT:    CFA fixed FP offset (unused): 66
+#  CASE1-NEXT:    CFA fixed RA offset: 71
+#  CASE1-NEXT:    Auxiliary header length: 4
+#  CASE1-NEXT:    Num FDEs: 1
+#  CASE1-NEXT:    Num FREs: 16
+#  CASE1-NEXT:    FRE subsection length: 4096
+#  CASE1-NEXT:    FDE subsection offset: 0
+#  CASE1-NEXT:    FRE subsection offset: 256
+#  CASE1-NEXT:    Auxiliary header: [0xDE, 0xAD, 0xBE, 0xEF]
+#  CASE1-NEXT:  }
+#  CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x24 while reading [0x20, 0x34)
+#  CASE1-NEXT:}
+
+  - Name:  .sframe_section_relative
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x00,  # 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
+      0x10, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0x10, 0x00, 0x00,  # FRE length
+      0x04, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x01, 0x00, 0x00,  # FRE offset
+
+      0xff, 0xff, 0xff, 0xff,  # Unused data skipped due to the FDE offset field
+
+      # FDE:
+      0x00, 0xde, 0xad, 0x00,  # Start Address
+      0xbe, 0x01, 0x00, 0x00,  # Size
+      0x10, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x00, 0x00, 0x00, 0x00,  # Number of FREs
+      0x31, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+    ]
+## Also testing:
+## - dead space between the header and the FDE subsection.
+## - PCMask FDE types
+## - unused PAuth key handling
+# CASE1-LABEL:SFrame section '.sframe_section_relative' {
+#       CASE1:  Header {
+#  CASE1-NEXT:    Magic: 0xDEE2
+#  CASE1-NEXT:    Version: V2 (0x2)
+#  CASE1-NEXT:    Flags [ (0x0)
+#  CASE1-NEXT:    ]
+#  CASE1-NEXT:    ABI: AMD64EndianLittle (0x3)
+#  CASE1-NEXT:    CFA fixed FP offset (unused): 66
+#  CASE1-NEXT:    CFA fixed RA offset: 71
+#  CASE1-NEXT:    Auxiliary header length: 0
+#  CASE1-NEXT:    Num FDEs: 1
+#  CASE1-NEXT:    Num FREs: 16
+#  CASE1-NEXT:    FRE subsection length: 4096
+#  CASE1-NEXT:    FDE subsection offset: 4
+#  CASE1-NEXT:    FRE subsection offset: 256
+#  CASE1-NEXT:    Auxiliary header: []
+#  CASE1-NEXT:  }
+#  CASE1-NEXT:  Function Index [
+#  CASE1-NEXT:    FuncDescEntry [0] {
+#  CASE1-NEXT:      PC: 0xADDE24
+#  CASE1-NEXT:      Size: 0x1BE
+#  CASE1-NEXT:      Start FRE Offset: 0x10
+#  CASE1-NEXT:      Num FREs: 0
+#  CASE1-NEXT:      Info: 0x31
+#  CASE1-NEXT:      FRE Type: Addr2 (0x1)
+#  CASE1-NEXT:      FDE Type: PCMask (0x1)
+#  CASE1-NEXT:      PAuth Key (unused): 1
+#  CASE1-NEXT:      Repetitive block size: 0xDE
+#  CASE1-NEXT:      Padding2: 0xAD
+#  CASE1-NEXT:    }
+#  CASE1-NEXT:  ]
+#  CASE1-NEXT:}
+
+  - Name:  .sframe_fde_relative
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x04,  # Preamble (magic, version, flags)
+      # Header:
+      0x02, 0x42, 0x47, 0x00,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x01, 0x00, 0x00, 0x00,  # Number of FDEs
+      0x10, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0x10, 0x00, 0x00,  # FRE length
+      0x04, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x01, 0x00, 0x00,  # FRE offset
+
+      0xff, 0xff, 0xff, 0xff,  # Unused data skipped due to the FDE offset field
+
+      # FDE:
+      0x00, 0xde, 0xad, 0x00,  # Start Address
+      0xbe, 0x01, 0x00, 0x00,  # Size
+      0x10, 0x00, 0x00, 0x00,  # Start FRE Offset
+      0x00, 0x00, 0x00, 0x00,  # Number of FREs
+      0x02, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+    ]
+## Also testing:
+## - PCInc FDE type
+## - AArch64 PAuth key handling
+# CASE1-LABEL:SFrame section '.sframe_fde_relative' {
+#       CASE1:  Header {
+#  CASE1-NEXT:    Magic: 0xDEE2
+#  CASE1-NEXT:    Version: V2 (0x2)
+#  CASE1-NEXT:    Flags [ (0x4)
+#  CASE1-NEXT:      FDEFuncStartPCRel (0x4){{ *}}
+#  CASE1-NEXT:    ]
+#  CASE1-NEXT:    ABI: AArch64EndianLittle (0x2)
+#  CASE1-NEXT:    CFA fixed FP offset (unused): 66
+#  CASE1-NEXT:    CFA fixed RA offset (unused): 71
+#  CASE1-NEXT:    Auxiliary header length: 0
+#  CASE1-NEXT:    Num FDEs: 1
+#  CASE1-NEXT:    Num FREs: 16
+#  CASE1-NEXT:    FRE subsection length: 4096
+#  CASE1-NEXT:    FDE subsection offset: 4
+#  CASE1-NEXT:    FRE subsection offset: 256
+#  CASE1-NEXT:    Auxiliary header: []
+#  CASE1-NEXT:  }
+#  CASE1-NEXT:  Function Index [
+#  CASE1-NEXT:    FuncDescEntry [0] {
+#  CASE1-NEXT:      PC: 0xADDE78
+#  CASE1-NEXT:      Size: 0x1BE
+#  CASE1-NEXT:      Start FRE Offset: 0x10
+#  CASE1-NEXT:      Num FREs: 0
+#  CASE1-NEXT:      Info: 0x2
+#  CASE1-NEXT:      FRE Type: Addr4 (0x2)
+#  CASE1-NEXT:      FDE Type: PCInc (0x0)
+#  CASE1-NEXT:      PAuth Key: A (0x0)
+#  CASE1-NEXT:      Repetitive block size (unused): 0xDE
+#  CASE1-NEXT:      Padding2: 0xAD
+#  CASE1-NEXT:    }
+#  CASE1-NEXT:  ]
+#  CASE1-NEXT:}
+
+--- !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, 0xad, 0x00,  # Start Address
+      0x00, 0x00, 0x01, 0xbe,  # Size
+      0x00, 0x00, 0x00, 0x10,  # Start FRE Offset
+      0x00, 0x00, 0x00, 0x10,  # Number of FREs
+      0x02, 0xde, 0xad, 0x00,  # Info, RepSize, Padding2
+    ]
+# CASE2-LABEL:SFrame section '.sframe' {
+#       CASE2:  Header {
+#  CASE2-NEXT:    Magic: 0xDEE2
+#  CASE2-NEXT:    Version: V2 (0x2)
+#  CASE2-NEXT:    Flags [ (0x5)
+#  CASE2-NEXT:      FDEFuncStartPCRel (0x4){{ *}}
+#  CASE2-NEXT:      FDESorted (0x1){{ *}}
+#  CASE2-NEXT:    ]
+#  CASE2-NEXT:    ABI: AArch64EndianBig (0x1)
+#  CASE2-NEXT:    CFA fixed FP offset (unused): 66
+#  CASE2-NEXT:    CFA fixed RA offset (unused): 71
+#  CASE2-NEXT:    Auxiliary header length: 0
+#  CASE2-NEXT:    Num FDEs: 1
+#  CASE2-NEXT:    Num FREs: 16
+#  CASE2-NEXT:    FRE subsection length: 4096
+#  CASE2-NEXT:    FDE subsection offset: 0
+#  CASE2-NEXT:    FRE subsection offset: 256
+#  CASE2-NEXT:    Auxiliary header: []
+#  CASE2-NEXT:  }
+#  CASE2-NEXT:  Function Index [
+#  CASE2-NEXT:    FuncDescEntry [0] {
+#  CASE2-NEXT:      PC: 0xDEAD1C
+#  CASE2-NEXT:      Size: 0x1BE
+#  CASE2-NEXT:      Start FRE Offset: 0x10
+#  CASE2-NEXT:      Num FREs: 16
+#  CASE2-NEXT:      Info: 0x2
+#  CASE2-NEXT:      FRE Type: Addr4 (0x2)
+#  CASE2-NEXT:      FDE Type: PCInc (0x0)
+#  CASE2-NEXT:      PAuth Key: A (0x0)
+#  CASE2-NEXT:      Repetitive block size (unused): 0xDE
+#  CASE2-NEXT:      Padding2: 0xAD00
+#  CASE2-NEXT:    }
+#  CASE2-NEXT:  ]
+#  CASE2-NEXT:}
diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-header.test b/llvm/test/tools/llvm-readobj/ELF/sframe-header.test
index f827296b1c399..e7c0db0d957c1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/sframe-header.test
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-header.test
@@ -2,7 +2,8 @@
 # RUN: yaml2obj --docnum=1 %s -o %t.1
 # RUN: llvm-readobj --sframe=.sframe_bad_sh_size --sframe=.sframe_1b \
 # RUN:   --sframe=.sframe_bad_magic --sframe=.sframe_bad_version \
-# RUN:   --sframe=.sframe_6b --sframe=.sframe_header %t.1 2>&1 | \
+# RUN:   --sframe=.sframe_6b --sframe=.sframe_short_auxheader \
+# RUN:   --sframe=.sframe_header %t.1 2>&1 | \
 # RUN:   FileCheck %s --strict-whitespace --match-full-lines \
 # RUN:   -DFILE=%t.1 --check-prefix=CASE1
 
@@ -60,24 +61,24 @@ Sections:
       0xe2, 0xde, 0x02, 0x00,  # Preamble (magic, version, flags)
       0x01, 0x02
     ]
-
 # CASE1-LABEL:SFrame section '.sframe_6b' {
 #       CASE1:{{.*}}: warning: '[[FILE]]': invalid sframe section: unexpected end of data at offset 0x6 while reading [0x0, 0x1c)
 
-  - Name:  .sframe_header
+  - Name:  .sframe_short_auxheader
     Type:  SHT_GNU_SFRAME
     Flags: [ SHF_ALLOC ]
     ContentArray: [
       0xe2, 0xde, 0x02, 0x06,  # Preamble (magic, version, flags)
       # Header:
-      0x03, 0x42, 0x47, 0x00,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x03, 0x42, 0x47, 0x08,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
       0x01, 0x00, 0x00, 0x00,  # Number of FDEs
       0x10, 0x00, 0x00, 0x00,  # Number of FREs
       0x00, 0x10, 0x00, 0x00,  # FRE length
-      0x04, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x00, 0x00, 0x00,  # FDE offset
       0x00, 0x01, 0x00, 0x00,  # FRE offset
+      0xde, 0xad, 0xbe, 0xef,  # AUX header
     ]
-# CASE1-LABEL:SFrame section '.sframe_header' {
+# CASE1-LABEL:SFrame section '.sframe_short_auxheader' {
 #       CASE1:  Header {
 #  CASE1-NEXT:    Magic: 0xDEE2
 #  CASE1-NEXT:    Version: V2 (0x2)
@@ -88,13 +89,52 @@ Sections:
 #  CASE1-NEXT:    ABI: AMD64EndianLittle (0x3)
 #  CASE1-NEXT:    CFA fixed FP offset (unused): 66
 #  CASE1-NEXT:    CFA fixed RA offset: 71
-#  CASE1-NEXT:    Auxiliary header length: 0
+#  CASE1-NEXT:    Auxiliary header length: 8
 #  CASE1-NEXT:    Num FDEs: 1
 #  CASE1-NEXT:    Num FREs: 16
 #  CASE1-NEXT:    FRE subsection length: 4096
-#  CASE1-NEXT:    FDE subsection offset: 4
+#  CASE1-NEXT:    FDE subsection offset: 0
+#  CASE1-NEXT:    FRE subsection offset: 256
+#  CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x20 while reading [0x1c, 0x24)
+#  CASE1-NEXT:  }
+#  CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x20 while reading [0x24, 0x38)
+#  CASE1-NEXT:}
+
+  - Name:  .sframe_header
+    Type:  SHT_GNU_SFRAME
+    Flags: [ SHF_ALLOC ]
+    ContentArray: [
+      0xe2, 0xde, 0x02, 0x06,  # Preamble (magic, version, flags)
+      # Header:
+      0x03, 0x42, 0x47, 0x04,  # ABI, Fixed FP offset, Fixed RA Offset, AUX header length
+      0x00, 0x00, 0x00, 0x00,  # Number of FDEs
+      0x10, 0x00, 0x00, 0x00,  # Number of FREs
+      0x00, 0x10, 0x00, 0x00,  # FRE length
+      0x00, 0x00, 0x00, 0x00,  # FDE offset
+      0x00, 0x01, 0x00, 0x00,  # FRE offset
+      0xde, 0xad, 0xbe, 0xef,  # AUX header
+    ]
+# CASE1-LABEL:SFrame section '.sframe_header' {
+#       CASE1:  Header {
+#  CASE1-NEXT:    Magic: 0xDEE2
+#  CASE1-NEXT:    Version: V2 (0x2)
+#  CASE1-NEXT:    Flags [ (0x6)
+#  CASE1-NEXT:      FDEFuncStartPCRel (0x4){{ *}}
+#  CASE1-NEXT:      FramePointer (0x2){{ *}}
+#  CASE1-NEXT:    ]
+#  CASE1-NEXT:    ABI: AMD64EndianLittle (0x3)
+#  CASE1-NEXT:    CFA fixed FP offset (unused): 66
+#  CASE1-NEXT:    CFA fixed RA offset: 71
+#  CASE1-NEXT:    Auxiliary header length: 4
+#  CASE1-NEXT:    Num FDEs: 0
+#  CASE1-NEXT:    Num FREs: 16
+#  CASE1-NEXT:    FRE subsection length: 4096
+#  CASE1-NEXT:    FDE subsection offset: 0
 #  CASE1-NEXT:    FRE subsection offset: 256
+#  CASE1-NEXT:    Auxiliary header: [0xDE, 0xAD, 0xBE, 0xEF]
 #  CASE1-NEXT:  }
+#  CASE1-NEXT:  Function Index [
+#  CASE1-NEXT:  ]
 #  CASE1-NEXT:}
 
 --- !ELF
@@ -110,10 +150,10 @@ Sections:
       0xde, 0xe2, 0x02, 0x01,  # 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, 0x00,  # Number of FDEs
       0x00, 0x00, 0x00, 0x10,  # Number of FREs
       0x00, 0x00, 0x10, 0x00,  # FRE length
-      0x00, 0x00, 0x00, 0x04,  # FDE offset
+      0x00, 0x00, 0x00, 0x00,  # FDE offset
       0x00, 0x00, 0x01, 0x00,  # FRE offset
     ]
 # CASE2-LABEL:SFrame section '.sframe' {
@@ -127,12 +167,15 @@ Sections:
 #  CASE2-NEXT:    CFA fixed FP offset (unused): 66
 #  CASE2-NEXT:    CFA fixed RA offset (unused): 71
 #  CASE2-NEXT:    Auxiliary header length: 0
-#  CASE2-NEXT:    Num FDEs: 1
+#  CASE2-NEXT:    Num FDEs: 0
 #  CASE2-NEXT:    Num FREs: 16
 #  CASE2-NEXT:    FRE subsection length: 4096
-#  CASE2-NEXT:    FDE subsection offset: 4
+#  CASE2-NEXT:    FDE subsection offset: 0
 #  CASE2-NEXT:    FRE subsection offset: 256
+#  CASE2-NEXT:    Auxiliary header: []
 #  CASE2-NEXT:  }
+#  CASE2-NEXT:  Function Index [
+#  CASE2-NEXT:  ]
 #  CASE2-NEXT:}
 
 --- !ELF
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 2867d48a400da..fd48958dade45 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -424,6 +424,9 @@ 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);
+
 private:
   mutable SmallVector<std::optional<VersionEntry>, 0> VersionMap;
 };
@@ -6433,6 +6436,86 @@ template <typename ELFT> void ELFDumper<ELFT>::printMemtag() {
   printMemtag(DynamicEntries, AndroidNoteDesc, GlobalDescriptors);
 }
 
+template <typename ELFT>
+void ELFDumper<ELFT>::printSFrameHeader(
+    const SFrameParser<ELFT::Endianness> &Parser) {
+  DictScope HeaderScope(W, "Header");
+
+  const sframe::Preamble<ELFT::Endianness> &Preamble = Parser.getPreamble();
+  W.printHex("Magic", Preamble.Magic.value());
+  W.printEnum("Version", Preamble.Version.value(), sframe::getVersions());
+  W.printFlags("Flags", Preamble.Flags.value(), sframe::getFlags());
+
+  const sframe::Header<ELFT::Endianness> &Header = Parser.getHeader();
+  W.printEnum("ABI", Header.ABIArch.value(), sframe::getABIs());
+
+  W.printNumber(("CFA fixed FP offset" +
+                 Twine(Parser.usesFixedFPOffset() ? "" : " (unused)"))
+                    .str(),
+                Header.CFAFixedFPOffset.value());
+
+  W.printNumber(("CFA fixed RA offset" +
+                 Twine(Parser.usesFixedRAOffset() ? "" : " (unused)"))
+                    .str(),
+                Header.CFAFixedRAOffset.value());
+
+  W.printNumber("Auxiliary header length", Header.AuxHdrLen.value());
+  W.printNumber("Num FDEs", Header.NumFDEs.value());
+  W.printNumber("Num FREs", Header.NumFREs.value());
+  W.printNumber("FRE subsection length", Header.FRELen.value());
+  W.printNumber("FDE subsection offset", Header.FDEOff.value());
+  W.printNumber("FRE subsection offset", Header.FREOff.value());
+
+  if (Expected<ArrayRef<uint8_t>> Aux = Parser.getAuxHeader())
+    W.printHexList("Auxiliary header", *Aux);
+  else
+    reportWarning(Aux.takeError(), FileName);
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::printSFrameFDEs(
+    const SFrameParser<ELFT::Endianness> &Parser) {
+  typename SFrameParser<ELFT::Endianness>::FDERange FDEs;
+  if (Error Err = Parser.fdes().moveInto(FDEs)) {
+    reportWarning(std::move(Err), FileName);
+    return;
+  }
+
+  ListScope IndexScope(W, "Function Index");
+  for (auto It = FDEs.begin(); It != FDEs.end(); ++It) {
+    DictScope FDEScope(
+        W,
+        formatv("FuncDescEntry [{0}]", std::distance(FDEs.begin(), It)).str());
+
+    W.printHex("PC", Parser.getAbsoluteStartAddress(It));
+    W.printHex("Size", It->Size);
+    W.printHex("Start FRE Offset", It->StartFREOff);
+    W.printNumber("Num FREs", It->NumFREs);
+
+    W.printHex("Info", It->Info);
+    W.printEnum("FRE Type", It->getFREType(), sframe::getFRETypes());
+    W.printEnum("FDE Type", It->getFDEType(), sframe::getFDETypes());
+    switch (Parser.getHeader().ABIArch) {
+    case sframe::ABI::AArch64EndianBig:
+    case sframe::ABI::AArch64EndianLittle:
+      W.printEnum("PAuth Key", sframe::AArch64PAuthKey(It->getPAuthKey()),
+                  sframe::getAArch64PAuthKeys());
+      break;
+    default:
+      W.printNumber("PAuth Key (unused)", It->getPAuthKey());
+      break;
+    }
+
+    W.printHex(
+        ("Repetitive block size" +
+         Twine(It->getFDEType() == sframe::FDEType::PCMask ? "" : " (unused)"))
+            .str(),
+        It->RepSize);
+
+    W.printHex("Padding2", It->Padding2);
+  }
+}
+
 template <typename ELFT>
 void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
   constexpr endianness E = ELFT::Endianness;
@@ -6450,8 +6533,8 @@ void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
       continue;
     }
 
-    Expected<object::SFrameParser<E>> Parser =
-        object::SFrameParser<E>::create(arrayRefFromStringRef(SectionContent));
+    Expected<object::SFrameParser<E>> Parser = object::SFrameParser<E>::create(
+        arrayRefFromStringRef(SectionContent), Section.getAddress());
     if (!Parser) {
       reportWarning(createError("invalid sframe section: " +
                                 toString(Parser.takeError())),
@@ -6459,32 +6542,8 @@ void ELFDumper<ELFT>::printSectionsAsSFrame(ArrayRef<std::string> Sections) {
       continue;
     }
 
-    DictScope HeaderScope(W, "Header");
-
-    const sframe::Preamble<E> &Preamble = Parser->getPreamble();
-    W.printHex("Magic", Preamble.Magic.value());
-    W.printEnum("Version", Preamble.Version.value(), sframe::getVersions());
-    W.printFlags("Flags", Preamble.Flags.value(), sframe::getFlags());
-
-    const sframe::Header<E> &Header = Parser->getHeader();
-    W.printEnum("ABI", Header.ABIArch.value(), sframe::getABIs());
-
-    W.printNumber(("CFA fixed FP offset" +
-                   Twine(Parser->usesFixedFPOffset() ? "" : " (unused)"))
-                      .str(),
-                  Header.CFAFixedFPOffset.value());
-
-    W.printNumber(("CFA fixed RA offset" +
-                   Twine(Parser->usesFixedRAOffset() ? "" : " (unused)"))
-                      .str(),
-                  Header.CFAFixedRAOffset.value());
-
-    W.printNumber("Auxiliary header length", Header.AuxHdrLen.value());
-    W.printNumber("Num FDEs", Header.NumFDEs.value());
-    W.printNumber("Num FREs", Header.NumFREs.value());
-    W.printNumber("FRE subsection length", Header.FRELen.value());
-    W.printNumber("FDE subsection offset", Header.FDEOff.value());
-    W.printNumber("FRE subsection offset", Header.FREOff.value());
+    printSFrameHeader(*Parser);
+    printSFrameFDEs(*Parser);
   }
 }
 

>From fa55874b324d68e2347f0dfb13eb42820574c6b4 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Mon, 21 Jul 2025 14:55:38 +0000
Subject: [PATCH 2/4] format

---
 llvm/include/llvm/Object/SFrameParser.h |  7 +++++--
 llvm/lib/Object/SFrameParser.cpp        | 13 +++++++------
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h
index 974c757419334..c55cf8198ddca 100644
--- a/llvm/include/llvm/Object/SFrameParser.h
+++ b/llvm/include/llvm/Object/SFrameParser.h
@@ -46,10 +46,13 @@ template <endianness E> class SFrameParser {
   uint64_t SectionAddress;
   const sframe::Header<E> &Header;
 
-  SFrameParser(ArrayRef<uint8_t> Data, uint64_t SectionAddress, const sframe::Header<E> &Header)
+  SFrameParser(ArrayRef<uint8_t> Data, uint64_t SectionAddress,
+               const sframe::Header<E> &Header)
       : Data(Data), SectionAddress(SectionAddress), Header(Header) {}
 
-  uint64_t getFDEBegin() const { return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff; }
+  uint64_t getFDEBegin() const {
+    return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff;
+  }
 };
 
 extern template class SFrameParser<endianness::big>;
diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp
index aa3c481d35db4..fbe35d6d81b6f 100644
--- a/llvm/lib/Object/SFrameParser.cpp
+++ b/llvm/lib/Object/SFrameParser.cpp
@@ -41,7 +41,8 @@ static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
 }
 
 template <endianness E>
-Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents, uint64_t SectionAddress) {
+Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents,
+                                                  uint64_t SectionAddress) {
   Expected<const sframe::Preamble<E> &> Preamble =
       getDataSliceAs<sframe::Preamble<E>>(Contents, 0);
   if (!Preamble)
@@ -62,13 +63,12 @@ Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents, ui
   return SFrameParser(Contents, SectionAddress, *Header);
 }
 
-
-template<endianness E>
+template <endianness E>
 Expected<ArrayRef<uint8_t>> SFrameParser<E>::getAuxHeader() const {
   return getDataSlice(Data, sizeof(Header), Header.AuxHdrLen);
 }
 
-template<endianness E>
+template <endianness E>
 Expected<ArrayRef<sframe::FuncDescEntry<E>>> SFrameParser<E>::fdes() const {
   Expected<ArrayRef<uint8_t>> Slice = getDataSlice(
       Data, getFDEBegin(), Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>));
@@ -79,8 +79,9 @@ Expected<ArrayRef<sframe::FuncDescEntry<E>>> SFrameParser<E>::fdes() const {
       Header.NumFDEs);
 }
 
-template<endianness E>
-uint64_t SFrameParser<E>::getAbsoluteStartAddress(typename FDERange::iterator FDE) const {
+template <endianness E>
+uint64_t SFrameParser<E>::getAbsoluteStartAddress(
+    typename FDERange::iterator FDE) const {
   uint64_t Result = SectionAddress + FDE->StartAddress;
 
   if ((getPreamble().Flags.value() & sframe::Flags::FDEFuncStartPCRel) ==

>From c9845ee8de80c2c9a063188096090f6e3aa8f76a Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Thu, 24 Jul 2025 16:39:24 +0200
Subject: [PATCH 3/4] saturate, scope

---
 llvm/lib/Object/SFrameParser.cpp              | 10 ++++---
 .../tools/llvm-readobj/ELF/sframe-fde.test    | 29 +++++++++++--------
 llvm/tools/llvm-readobj/ELFDumper.cpp         | 28 ++++++++++--------
 3 files changed, 39 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp
index fbe35d6d81b6f..bf0e2dc7e3629 100644
--- a/llvm/lib/Object/SFrameParser.cpp
+++ b/llvm/lib/Object/SFrameParser.cpp
@@ -10,19 +10,21 @@
 #include "llvm/BinaryFormat/SFrame.h"
 #include "llvm/Object/Error.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/MathExtras.h"
 
 using namespace llvm;
 using namespace llvm::object;
 
 static Expected<ArrayRef<uint8_t>>
 getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) {
-  // Check for overflow.
-  if (Offset + Size < Offset || Offset + Size < Size ||
-      Offset + Size > Data.size()) {
+  uint64_t End = SaturatingAdd(Offset, Size);
+  // Data.size() cannot be UINT64_MAX, as it would occupy the whole address
+  // space.
+  if (End > Data.size()) {
     return createStringError(
         formatv("unexpected end of data at offset {0:x} while reading [{1:x}, "
                 "{2:x})",
-                Data.size(), Offset, Offset + Size)
+                Data.size(), Offset, End)
             .str(),
         object_error::unexpected_eof);
   }
diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test b/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
index 294b28d8f04b4..dee40180c42e6 100644
--- a/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
+++ b/llvm/test/tools/llvm-readobj/ELF/sframe-fde.test
@@ -101,10 +101,11 @@ Sections:
 #  CASE1-NEXT:      Size: 0x1BE
 #  CASE1-NEXT:      Start FRE Offset: 0x10
 #  CASE1-NEXT:      Num FREs: 0
-#  CASE1-NEXT:      Info: 0x31
-#  CASE1-NEXT:      FRE Type: Addr2 (0x1)
-#  CASE1-NEXT:      FDE Type: PCMask (0x1)
-#  CASE1-NEXT:      PAuth Key (unused): 1
+#  CASE1-NEXT:      Info {
+#  CASE1-NEXT:        FRE Type: Addr2 (0x1)
+#  CASE1-NEXT:        FDE Type: PCMask (0x1)
+#  CASE1-NEXT:        Raw: 0x31
+#  CASE1-NEXT:      }
 #  CASE1-NEXT:      Repetitive block size: 0xDE
 #  CASE1-NEXT:      Padding2: 0xAD
 #  CASE1-NEXT:    }
@@ -160,10 +161,12 @@ Sections:
 #  CASE1-NEXT:      Size: 0x1BE
 #  CASE1-NEXT:      Start FRE Offset: 0x10
 #  CASE1-NEXT:      Num FREs: 0
-#  CASE1-NEXT:      Info: 0x2
-#  CASE1-NEXT:      FRE Type: Addr4 (0x2)
-#  CASE1-NEXT:      FDE Type: PCInc (0x0)
-#  CASE1-NEXT:      PAuth Key: A (0x0)
+#  CASE1-NEXT:      Info {
+#  CASE1-NEXT:        FRE Type: Addr4 (0x2)
+#  CASE1-NEXT:        FDE Type: PCInc (0x0)
+#  CASE1-NEXT:        PAuth Key: A (0x0)
+#  CASE1-NEXT:        Raw: 0x2
+#  CASE1-NEXT:      }
 #  CASE1-NEXT:      Repetitive block size (unused): 0xDE
 #  CASE1-NEXT:      Padding2: 0xAD
 #  CASE1-NEXT:    }
@@ -221,10 +224,12 @@ Sections:
 #  CASE2-NEXT:      Size: 0x1BE
 #  CASE2-NEXT:      Start FRE Offset: 0x10
 #  CASE2-NEXT:      Num FREs: 16
-#  CASE2-NEXT:      Info: 0x2
-#  CASE2-NEXT:      FRE Type: Addr4 (0x2)
-#  CASE2-NEXT:      FDE Type: PCInc (0x0)
-#  CASE2-NEXT:      PAuth Key: A (0x0)
+#  CASE2-NEXT:      Info {
+#  CASE2-NEXT:        FRE Type: Addr4 (0x2)
+#  CASE2-NEXT:        FDE Type: PCInc (0x0)
+#  CASE2-NEXT:        PAuth Key: A (0x0)
+#  CASE2-NEXT:        Raw: 0x2
+#  CASE2-NEXT:      }
 #  CASE2-NEXT:      Repetitive block size (unused): 0xDE
 #  CASE2-NEXT:      Padding2: 0xAD00
 #  CASE2-NEXT:    }
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 41c7f04fd194a..94ce38605f5c9 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -6498,18 +6498,22 @@ void ELFDumper<ELFT>::printSFrameFDEs(
     W.printHex("Start FRE Offset", It->StartFREOff);
     W.printNumber("Num FREs", It->NumFREs);
 
-    W.printHex("Info", It->Info);
-    W.printEnum("FRE Type", It->getFREType(), sframe::getFRETypes());
-    W.printEnum("FDE Type", It->getFDEType(), sframe::getFDETypes());
-    switch (Parser.getHeader().ABIArch) {
-    case sframe::ABI::AArch64EndianBig:
-    case sframe::ABI::AArch64EndianLittle:
-      W.printEnum("PAuth Key", sframe::AArch64PAuthKey(It->getPAuthKey()),
-                  sframe::getAArch64PAuthKeys());
-      break;
-    default:
-      W.printNumber("PAuth Key (unused)", It->getPAuthKey());
-      break;
+    {
+      DictScope InfoScope(W, "Info");
+      W.printEnum("FRE Type", It->getFREType(), sframe::getFRETypes());
+      W.printEnum("FDE Type", It->getFDEType(), sframe::getFDETypes());
+      switch (Parser.getHeader().ABIArch) {
+      case sframe::ABI::AArch64EndianBig:
+      case sframe::ABI::AArch64EndianLittle:
+        W.printEnum("PAuth Key", sframe::AArch64PAuthKey(It->getPAuthKey()),
+                    sframe::getAArch64PAuthKeys());
+        break;
+      case sframe::ABI::AMD64EndianLittle:
+        // unused
+        break;
+      }
+
+      W.printHex("Raw", It->Info);
     }
 
     W.printHex(

>From ee148a6bf530f520e409762d522906b5c6c481b7 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Wed, 30 Jul 2025 09:25:20 +0200
Subject: [PATCH 4/4] s/getFDEBegin/getFDEBase

---
 llvm/include/llvm/Object/SFrameParser.h | 2 +-
 llvm/lib/Object/SFrameParser.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h
index 05d213c971c7e..245e7ba40d0b8 100644
--- a/llvm/include/llvm/Object/SFrameParser.h
+++ b/llvm/include/llvm/Object/SFrameParser.h
@@ -51,7 +51,7 @@ template <endianness E> class SFrameParser {
                const sframe::Header<E> &Header)
       : Data(Data), SectionAddress(SectionAddress), Header(Header) {}
 
-  uint64_t getFDEBegin() const {
+  uint64_t getFDEBase() const {
     return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff;
   }
 };
diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp
index bc9a1a10ca4d7..1befac0b8eddf 100644
--- a/llvm/lib/Object/SFrameParser.cpp
+++ b/llvm/lib/Object/SFrameParser.cpp
@@ -73,7 +73,7 @@ Expected<ArrayRef<uint8_t>> SFrameParser<E>::getAuxHeader() const {
 template <endianness E>
 Expected<ArrayRef<sframe::FuncDescEntry<E>>> SFrameParser<E>::fdes() const {
   Expected<ArrayRef<uint8_t>> Slice = getDataSlice(
-      Data, getFDEBegin(), Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>));
+      Data, getFDEBase(), Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>));
   if (!Slice)
     return Slice.takeError();
   return ArrayRef(



More information about the llvm-commits mailing list