-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[Object] Parsing and dumping of SFrame Frame Row Entries #151301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
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/pr-subscribers-llvm-binary-utilities Author: Pavel Labath (labath) ChangesThe trickiest part here is that the FREs have a variable size, in two (or three?) dimensions:
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 and the related RFC. Patch is 23.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151301.diff 7 Files Affected:
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..73fdb643199ba 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,116 @@ 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;
+}
+
+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: ...
[truncated]
|
@dwblaikie, @efriedma-quic, I am tagging you because you were involved in the DataExtractor-vs-reinterpret_cast discussion. @jh7370, I am also going to be out for the rest of this week, so there's no rush. I appreciate all your reviews so far. |
ArrayRef<EnumEntry<sframe::BaseReg>> sframe::getBaseRegisters() { | ||
static constexpr EnumEntry<sframe::BaseReg> BaseRegs[] = { | ||
{"FP", sframe::BaseReg::FP}, | ||
{"SP", sframe::BaseReg::SP}, | ||
}; | ||
return ArrayRef(BaseRegs); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm probably missing something, but why not just have the static local exposed as a constant without the function wrapper?
@@ -100,6 +111,116 @@ uint64_t SFrameParser<E>::getAbsoluteStartAddress( | |||
return Result; | |||
} | |||
|
|||
template <typename EndianT> | |||
static Error readArray(ArrayRef<uint8_t> Data, uint64_t Count, uint64_t &Offset, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this returning an Error
rather than an Expected
?
} | ||
|
||
template <typename T, endianness E> | ||
static Error readFRE(ArrayRef<uint8_t> Data, uint64_t &Offset, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question.
template <endianness E> | ||
std::optional<int32_t> | ||
SFrameParser<E>::getCFAOffset(const FrameRowEntry &FRE) const { | ||
return getOffset(FRE.Offsets, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little concerned about this and the similar code in the following blocks, mostly because future ABIs/architectures won't necessarily have the same meanings for the different offsets. It's probably sufficient for now to add a comment or two highlighting that all supported ABIs use the specified offset.
The trickiest part here is that the FREs have a variable size, in two (or three?) dimensions:
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 and the related RFC.