[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