From e7f11016a8ec9b06533b970383b7c3f152cf9aee Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 16 Jul 2025 14:10:23 +0200 Subject: [PATCH 1/2] [Object] Parsing and dumping of SFrame Frame Row Entries 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). --- llvm/include/llvm/BinaryFormat/SFrame.h | 1 + llvm/include/llvm/Object/SFrameParser.h | 51 ++++ llvm/lib/BinaryFormat/SFrame.cpp | 8 + llvm/lib/Object/SFrameParser.cpp | 129 +++++++- .../tools/llvm-readobj/ELF/sframe-fde.test | 10 +- .../tools/llvm-readobj/ELF/sframe-fre.test | 284 ++++++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 29 +- 7 files changed, 505 insertions(+), 7 deletions(-) create mode 100644 llvm/test/tools/llvm-readobj/ELF/sframe-fre.test 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> getFRETypes(); LLVM_ABI ArrayRef> getFDETypes(); LLVM_ABI ArrayRef> getAArch64PAuthKeys(); LLVM_ABI ArrayRef> getFREOffsets(); +LLVM_ABI ArrayRef> 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 class SFrameParser { + class FallibleFREIterator; + public: static Expected create(ArrayRef Contents, uint64_t SectionAddress); @@ -42,6 +45,21 @@ template class SFrameParser { // objects returned by the `fdes()` function. uint64_t getAbsoluteStartAddress(typename FDERange::iterator FDE) const; + struct FrameRowEntry { + uint32_t StartAddress; + sframe::FREInfo Info; + SmallVector Offsets; + }; + + using fre_iterator = fallible_iterator; + iterator_range fres(const sframe::FuncDescEntry &FDE, + Error &Err) const; + + std::optional getCFAOffset(const FrameRowEntry &FRE) const; + std::optional getRAOffset(const FrameRowEntry &FRE) const; + std::optional getFPOffset(const FrameRowEntry &FRE) const; + ArrayRef getExtraOffsets(const FrameRowEntry &FRE) const; + private: ArrayRef Data; uint64_t SectionAddress; @@ -54,6 +72,39 @@ template class SFrameParser { uint64_t getFDEBase() const { return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff; } + + uint64_t getFREBase() const { + return getFDEBase() + Header.NumFDEs * sizeof(sframe::FuncDescEntry); + } +}; + +template class SFrameParser::FallibleFREIterator { +public: + // NB: This iterator starts out in the before_begin() state. It must be + // ++'ed to reach the first element. + FallibleFREIterator(ArrayRef 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 Data; + sframe::FREType FREType; + uint32_t Idx; + uint32_t Size; + uint64_t Offset; + FrameRowEntry FRE; }; extern template class LLVM_TEMPLATE_ABI SFrameParser; 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> sframe::getFREOffsets() { }; return ArrayRef(FREOffsets); } + +ArrayRef> sframe::getBaseRegisters() { + static constexpr EnumEntry 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..73fdb643199ba 100644 --- a/llvm/lib/Object/SFrameParser.cpp +++ b/llvm/lib/Object/SFrameParser.cpp @@ -32,14 +32,25 @@ getDataSlice(ArrayRef Data, uint64_t Offset, uint64_t Size) { } template -static Expected getDataSliceAs(ArrayRef Data, - uint64_t Offset) { +static Expected> +getDataSliceAsArrayOf(ArrayRef Data, uint64_t Offset, uint64_t Count) { static_assert(std::is_trivial_v); - Expected> Slice = getDataSlice(Data, Offset, sizeof(T)); + Expected> Slice = + getDataSlice(Data, Offset, sizeof(T) * Count); if (!Slice) return Slice.takeError(); - return *reinterpret_cast(Slice->data()); + return ArrayRef(reinterpret_cast(Slice->data()), Count); +} + +template +static Expected getDataSliceAs(ArrayRef Data, + uint64_t Offset) { + Expected> Array = getDataSliceAsArrayOf(Data, Offset, 1); + if (!Array) + return Array.takeError(); + + return Array->front(); } template @@ -100,6 +111,116 @@ uint64_t SFrameParser::getAbsoluteStartAddress( return Result; } +template +static Error readArray(ArrayRef Data, uint64_t Count, uint64_t &Offset, + SmallVectorImpl &Vec) { + Expected> RawArray = + getDataSliceAsArrayOf(Data, Offset, Count); + if (!RawArray) + return RawArray.takeError(); + Offset += Count * sizeof(EndianT); + Vec.resize(Count); + llvm::copy(*RawArray, Vec.begin()); + return Error::success(); +} + +template +static Error readFRE(ArrayRef Data, uint64_t &Offset, + typename SFrameParser::FrameRowEntry &FRE) { + Expected> RawFRE = + getDataSliceAs>(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>( + Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); + case sframe::FREOffset::B2: + return readArray>( + Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); + case sframe::FREOffset::B4: + return readArray>( + Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); + default: + return createError("unsupported/unknown offset size"); + } +} + +template Error SFrameParser::FallibleFREIterator::inc() { + if (++Idx == Size) + return Error::success(); + + switch (FREType) { + case sframe::FREType::Addr1: + return readFRE(Data, Offset, FRE); + case sframe::FREType::Addr2: + return readFRE(Data, Offset, FRE); + case sframe::FREType::Addr4: + return readFRE(Data, Offset, FRE); + default: + return createError("invalid/unsupported FRE type"); + } +} + +template +iterator_range::fre_iterator> +SFrameParser::fres(const sframe::FuncDescEntry &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 getOffset(ArrayRef Offsets, size_t Idx) { + if (Offsets.size() > Idx) + return Offsets[Idx]; + return std::nullopt; +} + +template +std::optional +SFrameParser::getCFAOffset(const FrameRowEntry &FRE) const { + return getOffset(FRE.Offsets, 0); +} + +template +std::optional +SFrameParser::getRAOffset(const FrameRowEntry &FRE) const { + if (usesFixedRAOffset()) + return Header.CFAFixedRAOffset; + return getOffset(FRE.Offsets, 1); +} + +template +std::optional +SFrameParser::getFPOffset(const FrameRowEntry &FRE) const { + if (usesFixedFPOffset()) + return Header.CFAFixedFPOffset; + return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2); +} + +template +ArrayRef +SFrameParser::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; template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser; 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 94ce38605f5c9..ca6fb08d833db 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -6493,7 +6493,8 @@ void ELFDumper::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); @@ -6523,6 +6524,32 @@ void ELFDumper::printSFrameFDEs( It->RepSize); W.printHex("Padding2", It->Padding2); + + ListScope FREListScope(W, "FREs"); + Error Err = Error::success(); + for (const typename SFrameParser::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 Off = Parser.getCFAOffset(FRE)) + W.printNumber("CFA Offset", *Off); + if (std::optional Off = Parser.getRAOffset(FRE)) + W.printNumber("RA Offset", *Off); + if (std::optional Off = Parser.getFPOffset(FRE)) + W.printNumber("FP Offset", *Off); + if (ArrayRef Offs = Parser.getExtraOffsets(FRE); !Offs.empty()) + W.printList("Extra Offsets", Offs); + } + if (Err) + reportWarning(std::move(Err), FileName); } } From a26107c7f92070faf3b53185a8c2682703604cc0 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 6 Aug 2025 11:28:45 +0200 Subject: [PATCH 2/2] add comment --- llvm/lib/Object/SFrameParser.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp index 73fdb643199ba..825b7af368211 100644 --- a/llvm/lib/Object/SFrameParser.cpp +++ b/llvm/lib/Object/SFrameParser.cpp @@ -186,6 +186,9 @@ static std::optional getOffset(ArrayRef Offsets, size_t 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 std::optional SFrameParser::getCFAOffset(const FrameRowEntry &FRE) const {