Skip to content

Commit 66aa46d

Browse files
authored
Reapply "[Object] Parsing and dumping of SFrame Frame Row Entries" (#152650) (#152695)
This reapplies #152650 with a build fix for clang-11 (need explicit template parameters for ArrayRef construction) and avoiding the default-in-a-switch-covering-enum warning. It also adds two new tests. The original commit message was: 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).
1 parent 9e8f7ac commit 66aa46d

File tree

7 files changed

+528
-7
lines changed

7 files changed

+528
-7
lines changed

llvm/include/llvm/BinaryFormat/SFrame.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ LLVM_ABI ArrayRef<EnumEntry<FREType>> getFRETypes();
169169
LLVM_ABI ArrayRef<EnumEntry<FDEType>> getFDETypes();
170170
LLVM_ABI ArrayRef<EnumEntry<AArch64PAuthKey>> getAArch64PAuthKeys();
171171
LLVM_ABI ArrayRef<EnumEntry<FREOffset>> getFREOffsets();
172+
LLVM_ABI ArrayRef<EnumEntry<BaseReg>> getBaseRegisters();
172173

173174
} // namespace sframe
174175
} // namespace llvm

llvm/include/llvm/Object/SFrameParser.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_OBJECT_SFRAME_H
1111

1212
#include "llvm/ADT/ArrayRef.h"
13+
#include "llvm/ADT/fallible_iterator.h"
1314
#include "llvm/BinaryFormat/SFrame.h"
1415
#include "llvm/Support/Compiler.h"
1516
#include "llvm/Support/Error.h"
@@ -19,6 +20,8 @@ namespace llvm {
1920
namespace object {
2021

2122
template <endianness E> class SFrameParser {
23+
class FallibleFREIterator;
24+
2225
public:
2326
static Expected<SFrameParser> create(ArrayRef<uint8_t> Contents,
2427
uint64_t SectionAddress);
@@ -42,6 +45,21 @@ template <endianness E> class SFrameParser {
4245
// objects returned by the `fdes()` function.
4346
uint64_t getAbsoluteStartAddress(typename FDERange::iterator FDE) const;
4447

48+
struct FrameRowEntry {
49+
uint32_t StartAddress;
50+
sframe::FREInfo<endianness::native> Info;
51+
SmallVector<int32_t, 3> Offsets;
52+
};
53+
54+
using fre_iterator = fallible_iterator<FallibleFREIterator>;
55+
iterator_range<fre_iterator> fres(const sframe::FuncDescEntry<E> &FDE,
56+
Error &Err) const;
57+
58+
std::optional<int32_t> getCFAOffset(const FrameRowEntry &FRE) const;
59+
std::optional<int32_t> getRAOffset(const FrameRowEntry &FRE) const;
60+
std::optional<int32_t> getFPOffset(const FrameRowEntry &FRE) const;
61+
ArrayRef<int32_t> getExtraOffsets(const FrameRowEntry &FRE) const;
62+
4563
private:
4664
ArrayRef<uint8_t> Data;
4765
uint64_t SectionAddress;
@@ -54,6 +72,39 @@ template <endianness E> class SFrameParser {
5472
uint64_t getFDEBase() const {
5573
return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff;
5674
}
75+
76+
uint64_t getFREBase() const {
77+
return getFDEBase() + Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>);
78+
}
79+
};
80+
81+
template <endianness E> class SFrameParser<E>::FallibleFREIterator {
82+
public:
83+
// NB: This iterator starts out in the before_begin() state. It must be
84+
// ++'ed to reach the first element.
85+
FallibleFREIterator(ArrayRef<uint8_t> Data, sframe::FREType FREType,
86+
uint32_t Idx, uint32_t Size, uint64_t Offset)
87+
: Data(Data), FREType(FREType), Idx(Idx), Size(Size), Offset(Offset) {}
88+
89+
Error inc();
90+
const FrameRowEntry &operator*() const { return FRE; }
91+
92+
friend bool operator==(const FallibleFREIterator &LHS,
93+
const FallibleFREIterator &RHS) {
94+
assert(LHS.Data.data() == RHS.Data.data());
95+
assert(LHS.Data.size() == RHS.Data.size());
96+
assert(LHS.FREType == RHS.FREType);
97+
assert(LHS.Size == RHS.Size);
98+
return LHS.Idx == RHS.Idx;
99+
}
100+
101+
private:
102+
ArrayRef<uint8_t> Data;
103+
sframe::FREType FREType;
104+
uint32_t Idx;
105+
uint32_t Size;
106+
uint64_t Offset;
107+
FrameRowEntry FRE;
57108
};
58109

59110
extern template class LLVM_TEMPLATE_ABI SFrameParser<endianness::big>;

llvm/lib/BinaryFormat/SFrame.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,11 @@ ArrayRef<EnumEntry<sframe::FREOffset>> sframe::getFREOffsets() {
6868
};
6969
return ArrayRef(FREOffsets);
7070
}
71+
72+
ArrayRef<EnumEntry<sframe::BaseReg>> sframe::getBaseRegisters() {
73+
static constexpr EnumEntry<sframe::BaseReg> BaseRegs[] = {
74+
{"FP", sframe::BaseReg::FP},
75+
{"SP", sframe::BaseReg::SP},
76+
};
77+
return ArrayRef(BaseRegs);
78+
}

llvm/lib/Object/SFrameParser.cpp

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,25 @@ getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) {
3232
}
3333

3434
template <typename T>
35-
static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
36-
uint64_t Offset) {
35+
static Expected<ArrayRef<T>>
36+
getDataSliceAsArrayOf(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Count) {
3737
static_assert(std::is_trivial_v<T>);
38-
Expected<ArrayRef<uint8_t>> Slice = getDataSlice(Data, Offset, sizeof(T));
38+
Expected<ArrayRef<uint8_t>> Slice =
39+
getDataSlice(Data, Offset, sizeof(T) * Count);
3940
if (!Slice)
4041
return Slice.takeError();
4142

42-
return *reinterpret_cast<const T *>(Slice->data());
43+
return ArrayRef(reinterpret_cast<const T *>(Slice->data()), Count);
44+
}
45+
46+
template <typename T>
47+
static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
48+
uint64_t Offset) {
49+
Expected<ArrayRef<T>> Array = getDataSliceAsArrayOf<T>(Data, Offset, 1);
50+
if (!Array)
51+
return Array.takeError();
52+
53+
return Array->front();
4354
}
4455

4556
template <endianness E>
@@ -100,6 +111,120 @@ uint64_t SFrameParser<E>::getAbsoluteStartAddress(
100111
return Result;
101112
}
102113

114+
template <typename EndianT>
115+
static Error readArray(ArrayRef<uint8_t> Data, uint64_t Count, uint64_t &Offset,
116+
SmallVectorImpl<int32_t> &Vec) {
117+
Expected<ArrayRef<EndianT>> RawArray =
118+
getDataSliceAsArrayOf<EndianT>(Data, Offset, Count);
119+
if (!RawArray)
120+
return RawArray.takeError();
121+
Offset += Count * sizeof(EndianT);
122+
Vec.resize(Count);
123+
llvm::copy(*RawArray, Vec.begin());
124+
return Error::success();
125+
}
126+
127+
template <typename T, endianness E>
128+
static Error readFRE(ArrayRef<uint8_t> Data, uint64_t &Offset,
129+
typename SFrameParser<E>::FrameRowEntry &FRE) {
130+
Expected<sframe::FrameRowEntry<T, E>> RawFRE =
131+
getDataSliceAs<sframe::FrameRowEntry<T, E>>(Data, Offset);
132+
if (!RawFRE)
133+
return RawFRE.takeError();
134+
135+
Offset += sizeof(*RawFRE);
136+
FRE.StartAddress = RawFRE->StartAddress;
137+
FRE.Info.Info = RawFRE->Info.Info;
138+
139+
switch (FRE.Info.getOffsetSize()) {
140+
case sframe::FREOffset::B1:
141+
return readArray<sframe::detail::packed<int8_t, E>>(
142+
Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
143+
case sframe::FREOffset::B2:
144+
return readArray<sframe::detail::packed<int16_t, E>>(
145+
Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
146+
case sframe::FREOffset::B4:
147+
return readArray<sframe::detail::packed<int32_t, E>>(
148+
Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
149+
}
150+
return createError(formatv("unsupported FRE offset size {0} at offset {1:x+}",
151+
static_cast<unsigned>(FRE.Info.getOffsetSize()),
152+
Offset));
153+
}
154+
155+
template <endianness E> Error SFrameParser<E>::FallibleFREIterator::inc() {
156+
if (++Idx == Size)
157+
return Error::success();
158+
159+
switch (FREType) {
160+
case sframe::FREType::Addr1:
161+
return readFRE<uint8_t, E>(Data, Offset, FRE);
162+
case sframe::FREType::Addr2:
163+
return readFRE<uint16_t, E>(Data, Offset, FRE);
164+
case sframe::FREType::Addr4:
165+
return readFRE<uint32_t, E>(Data, Offset, FRE);
166+
}
167+
return createError(formatv("unsupported FRE type {0} at offset {1:x+}",
168+
static_cast<unsigned>(FREType), Offset));
169+
}
170+
171+
template <endianness E>
172+
iterator_range<typename SFrameParser<E>::fre_iterator>
173+
SFrameParser<E>::fres(const sframe::FuncDescEntry<E> &FDE, Error &Err) const {
174+
uint64_t Offset = getFREBase() + FDE.StartFREOff;
175+
fre_iterator BeforeBegin = make_fallible_itr(
176+
FallibleFREIterator(Data, FDE.getFREType(), -1, FDE.NumFREs, Offset),
177+
Err);
178+
fre_iterator End = make_fallible_end(
179+
FallibleFREIterator(Data, FDE.getFREType(), FDE.NumFREs, FDE.NumFREs,
180+
/*Offset=*/0));
181+
return {++BeforeBegin, End};
182+
}
183+
184+
static std::optional<int32_t> getOffset(ArrayRef<int32_t> Offsets, size_t Idx) {
185+
if (Offsets.size() > Idx)
186+
return Offsets[Idx];
187+
return std::nullopt;
188+
}
189+
190+
// The interpretation of offsets is ABI-specific. The implementation of this and
191+
// the following functions may need to be adjusted when adding support for a new
192+
// ABI.
193+
template <endianness E>
194+
std::optional<int32_t>
195+
SFrameParser<E>::getCFAOffset(const FrameRowEntry &FRE) const {
196+
return getOffset(FRE.Offsets, 0);
197+
}
198+
199+
template <endianness E>
200+
std::optional<int32_t>
201+
SFrameParser<E>::getRAOffset(const FrameRowEntry &FRE) const {
202+
if (usesFixedRAOffset())
203+
return Header.CFAFixedRAOffset;
204+
return getOffset(FRE.Offsets, 1);
205+
}
206+
207+
template <endianness E>
208+
std::optional<int32_t>
209+
SFrameParser<E>::getFPOffset(const FrameRowEntry &FRE) const {
210+
if (usesFixedFPOffset())
211+
return Header.CFAFixedFPOffset;
212+
return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2);
213+
}
214+
215+
template <endianness E>
216+
ArrayRef<int32_t>
217+
SFrameParser<E>::getExtraOffsets(const FrameRowEntry &FRE) const {
218+
size_t UsedOffsets = 1; // CFA
219+
if (!usesFixedRAOffset())
220+
++UsedOffsets;
221+
if (!usesFixedFPOffset())
222+
++UsedOffsets;
223+
if (FRE.Offsets.size() > UsedOffsets)
224+
return ArrayRef<int32_t>(FRE.Offsets).drop_front(UsedOffsets);
225+
return {};
226+
}
227+
103228
template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser<endianness::big>;
104229
template class LLVM_EXPORT_TEMPLATE
105230
llvm::object::SFrameParser<endianness::little>;

llvm/test/tools/llvm-readobj/ELF/sframe-fde.test

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ Sections:
108108
# CASE1-NEXT: }
109109
# CASE1-NEXT: Repetitive block size: 0xDE
110110
# CASE1-NEXT: Padding2: 0xAD
111+
# CASE1-NEXT: FREs [
112+
# CASE1-NEXT: ]
111113
# CASE1-NEXT: }
112114
# CASE1-NEXT: ]
113115
# CASE1-NEXT:}
@@ -169,6 +171,8 @@ Sections:
169171
# CASE1-NEXT: }
170172
# CASE1-NEXT: Repetitive block size (unused): 0xDE
171173
# CASE1-NEXT: Padding2: 0xAD
174+
# CASE1-NEXT: FREs [
175+
# CASE1-NEXT: ]
172176
# CASE1-NEXT: }
173177
# CASE1-NEXT: ]
174178
# CASE1-NEXT:}
@@ -196,7 +200,7 @@ Sections:
196200
0x00, 0xde, 0xad, 0x00, # Start Address
197201
0x00, 0x00, 0x01, 0xbe, # Size
198202
0x00, 0x00, 0x00, 0x10, # Start FRE Offset
199-
0x00, 0x00, 0x00, 0x10, # Number of FREs
203+
0x00, 0x00, 0x00, 0x00, # Number of FREs
200204
0x02, 0xde, 0xad, 0x00, # Info, RepSize, Padding2
201205
]
202206
# CASE2-LABEL:SFrame section '.sframe' {
@@ -223,7 +227,7 @@ Sections:
223227
# CASE2-NEXT: PC: 0xDEAD1C
224228
# CASE2-NEXT: Size: 0x1BE
225229
# CASE2-NEXT: Start FRE Offset: 0x10
226-
# CASE2-NEXT: Num FREs: 16
230+
# CASE2-NEXT: Num FREs: 0
227231
# CASE2-NEXT: Info {
228232
# CASE2-NEXT: FRE Type: Addr4 (0x2)
229233
# CASE2-NEXT: FDE Type: PCInc (0x0)
@@ -232,6 +236,8 @@ Sections:
232236
# CASE2-NEXT: }
233237
# CASE2-NEXT: Repetitive block size (unused): 0xDE
234238
# CASE2-NEXT: Padding2: 0xAD00
239+
# CASE2-NEXT: FREs [
240+
# CASE2-NEXT: ]
235241
# CASE2-NEXT: }
236242
# CASE2-NEXT: ]
237243
# CASE2-NEXT:}

0 commit comments

Comments
 (0)