1//===- StackMapParser.h - StackMap Parsing Support --------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_OBJECT_STACKMAPPARSER_H
10#define LLVM_OBJECT_STACKMAPPARSER_H
11
12#include "llvm/ADT/ArrayRef.h"
13#include "llvm/ADT/iterator_range.h"
14#include "llvm/Object/ELF.h"
15#include "llvm/Support/Endian.h"
16#include <cassert>
17#include <cstddef>
18#include <cstdint>
19#include <vector>
20
21namespace llvm {
22
23/// A parser for the latest stackmap format. At the moment, latest=V3.
24template <support::endianness Endianness>
25class StackMapParser {
26public:
27 template <typename AccessorT>
28 class AccessorIterator {
29 public:
30 AccessorIterator(AccessorT A) : A(A) {}
31
32 AccessorIterator& operator++() { A = A.next(); return *this; }
33 AccessorIterator operator++(int) {
34 auto tmp = *this;
35 ++*this;
36 return tmp;
37 }
38
39 bool operator==(const AccessorIterator &Other) const {
40 return A.P == Other.A.P;
41 }
42
43 bool operator!=(const AccessorIterator &Other) const {
44 return !(*this == Other);
45 }
46
47 AccessorT& operator*() { return A; }
48 AccessorT* operator->() { return &A; }
49
50 private:
51 AccessorT A;
52 };
53
54 /// Accessor for function records.
55 class FunctionAccessor {
56 friend class StackMapParser;
57
58 public:
59 /// Get the function address.
60 uint64_t getFunctionAddress() const {
61 return read<uint64_t>(P);
62 }
63
64 /// Get the function's stack size.
65 uint64_t getStackSize() const {
66 return read<uint64_t>(P + sizeof(uint64_t));
67 }
68
69 /// Get the number of callsite records.
70 uint64_t getRecordCount() const {
71 return read<uint64_t>(P + (2 * sizeof(uint64_t)));
72 }
73
74 private:
75 FunctionAccessor(const uint8_t *P) : P(P) {}
76
77 const static int FunctionAccessorSize = 3 * sizeof(uint64_t);
78
79 FunctionAccessor next() const {
80 return FunctionAccessor(P + FunctionAccessorSize);
81 }
82
83 const uint8_t *P;
84 };
85
86 /// Accessor for constants.
87 class ConstantAccessor {
88 friend class StackMapParser;
89
90 public:
91 /// Return the value of this constant.
92 uint64_t getValue() const { return read<uint64_t>(P); }
93
94 private:
95 ConstantAccessor(const uint8_t *P) : P(P) {}
96
97 const static int ConstantAccessorSize = sizeof(uint64_t);
98
99 ConstantAccessor next() const {
100 return ConstantAccessor(P + ConstantAccessorSize);
101 }
102
103 const uint8_t *P;
104 };
105
106 enum class LocationKind : uint8_t {
107 Register = 1, Direct = 2, Indirect = 3, Constant = 4, ConstantIndex = 5
108 };
109
110 /// Accessor for location records.
111 class LocationAccessor {
112 friend class StackMapParser;
113 friend class RecordAccessor;
114
115 public:
116 /// Get the Kind for this location.
117 LocationKind getKind() const {
118 return LocationKind(P[KindOffset]);
119 }
120
121 /// Get the Size for this location.
122 unsigned getSizeInBytes() const {
123 return read<uint16_t>(P + SizeOffset);
124
125 }
126
127 /// Get the Dwarf register number for this location.
128 uint16_t getDwarfRegNum() const {
129 return read<uint16_t>(P + DwarfRegNumOffset);
130 }
131
132 /// Get the small-constant for this location. (Kind must be Constant).
133 uint32_t getSmallConstant() const {
134 assert(getKind() == LocationKind::Constant && "Not a small constant.");
135 return read<uint32_t>(P + SmallConstantOffset);
136 }
137
138 /// Get the constant-index for this location. (Kind must be ConstantIndex).
139 uint32_t getConstantIndex() const {
140 assert(getKind() == LocationKind::ConstantIndex &&
141 "Not a constant-index.");
142 return read<uint32_t>(P + SmallConstantOffset);
143 }
144
145 /// Get the offset for this location. (Kind must be Direct or Indirect).
146 int32_t getOffset() const {
147 assert((getKind() == LocationKind::Direct ||
148 getKind() == LocationKind::Indirect) &&
149 "Not direct or indirect.");
150 return read<int32_t>(P + SmallConstantOffset);
151 }
152
153 private:
154 LocationAccessor(const uint8_t *P) : P(P) {}
155
156 LocationAccessor next() const {
157 return LocationAccessor(P + LocationAccessorSize);
158 }
159
160 static const int KindOffset = 0;
161 static const int SizeOffset = KindOffset + sizeof(uint16_t);
162 static const int DwarfRegNumOffset = SizeOffset + sizeof(uint16_t);
163 static const int SmallConstantOffset = DwarfRegNumOffset + sizeof(uint32_t);
164 static const int LocationAccessorSize = sizeof(uint64_t) + sizeof(uint32_t);
165
166 const uint8_t *P;
167 };
168
169 /// Accessor for stackmap live-out fields.
170 class LiveOutAccessor {
171 friend class StackMapParser;
172 friend class RecordAccessor;
173
174 public:
175 /// Get the Dwarf register number for this live-out.
176 uint16_t getDwarfRegNum() const {
177 return read<uint16_t>(P + DwarfRegNumOffset);
178 }
179
180 /// Get the size in bytes of live [sub]register.
181 unsigned getSizeInBytes() const {
182 return read<uint8_t>(P + SizeOffset);
183 }
184
185 private:
186 LiveOutAccessor(const uint8_t *P) : P(P) {}
187
188 LiveOutAccessor next() const {
189 return LiveOutAccessor(P + LiveOutAccessorSize);
190 }
191
192 static const int DwarfRegNumOffset = 0;
193 static const int SizeOffset =
194 DwarfRegNumOffset + sizeof(uint16_t) + sizeof(uint8_t);
195 static const int LiveOutAccessorSize = sizeof(uint32_t);
196
197 const uint8_t *P;
198 };
199
200 /// Accessor for stackmap records.
201 class RecordAccessor {
202 friend class StackMapParser;
203
204 public:
205 using location_iterator = AccessorIterator<LocationAccessor>;
206 using liveout_iterator = AccessorIterator<LiveOutAccessor>;
207
208 /// Get the patchpoint/stackmap ID for this record.
209 uint64_t getID() const {
210 return read<uint64_t>(P + PatchpointIDOffset);
211 }
212
213 /// Get the instruction offset (from the start of the containing function)
214 /// for this record.
215 uint32_t getInstructionOffset() const {
216 return read<uint32_t>(P + InstructionOffsetOffset);
217 }
218
219 /// Get the number of locations contained in this record.
220 uint16_t getNumLocations() const {
221 return read<uint16_t>(P + NumLocationsOffset);
222 }
223
224 /// Get the location with the given index.
225 LocationAccessor getLocation(unsigned LocationIndex) const {
226 unsigned LocationOffset =
227 LocationListOffset + LocationIndex * LocationSize;
228 return LocationAccessor(P + LocationOffset);
229 }
230
231 /// Begin iterator for locations.
232 location_iterator location_begin() const {
233 return location_iterator(getLocation(0));
234 }
235
236 /// End iterator for locations.
237 location_iterator location_end() const {
238 return location_iterator(getLocation(getNumLocations()));
239 }
240
241 /// Iterator range for locations.
242 iterator_range<location_iterator> locations() const {
243 return make_range(location_begin(), location_end());
244 }
245
246 /// Get the number of liveouts contained in this record.
247 uint16_t getNumLiveOuts() const {
248 return read<uint16_t>(P + getNumLiveOutsOffset());
249 }
250
251 /// Get the live-out with the given index.
252 LiveOutAccessor getLiveOut(unsigned LiveOutIndex) const {
253 unsigned LiveOutOffset =
254 getNumLiveOutsOffset() + sizeof(uint16_t) + LiveOutIndex * LiveOutSize;
255 return LiveOutAccessor(P + LiveOutOffset);
256 }
257
258 /// Begin iterator for live-outs.
259 liveout_iterator liveouts_begin() const {
260 return liveout_iterator(getLiveOut(0));
261 }
262
263 /// End iterator for live-outs.
264 liveout_iterator liveouts_end() const {
265 return liveout_iterator(getLiveOut(getNumLiveOuts()));
266 }
267
268 /// Iterator range for live-outs.
269 iterator_range<liveout_iterator> liveouts() const {
270 return make_range(liveouts_begin(), liveouts_end());
271 }
272
273 private:
274 RecordAccessor(const uint8_t *P) : P(P) {}
275
276 unsigned getNumLiveOutsOffset() const {
277 unsigned LocOffset =
278 ((LocationListOffset + LocationSize * getNumLocations()) + 7) & ~0x7;
279 return LocOffset + sizeof(uint16_t);
280 }
281
282 unsigned getSizeInBytes() const {
283 unsigned RecordSize =
284 getNumLiveOutsOffset() + sizeof(uint16_t) + getNumLiveOuts() * LiveOutSize;
285 return (RecordSize + 7) & ~0x7;
286 }
287
288 RecordAccessor next() const {
289 return RecordAccessor(P + getSizeInBytes());
290 }
291
292 static const unsigned PatchpointIDOffset = 0;
293 static const unsigned InstructionOffsetOffset =
294 PatchpointIDOffset + sizeof(uint64_t);
295 static const unsigned NumLocationsOffset =
296 InstructionOffsetOffset + sizeof(uint32_t) + sizeof(uint16_t);
297 static const unsigned LocationListOffset =
298 NumLocationsOffset + sizeof(uint16_t);
299 static const unsigned LocationSize = sizeof(uint64_t) + sizeof(uint32_t);
300 static const unsigned LiveOutSize = sizeof(uint32_t);
301
302 const uint8_t *P;
303 };
304
305 /// Construct a parser for a version-3 stackmap. StackMap data will be read
306 /// from the given array.
307 StackMapParser(ArrayRef<uint8_t> StackMapSection)
308 : StackMapSection(StackMapSection) {
309 ConstantsListOffset = FunctionListOffset + getNumFunctions() * FunctionSize;
310
311 assert(StackMapSection[0] == 3 &&
312 "StackMapParser can only parse version 3 stackmaps");
313
314 unsigned CurrentRecordOffset =
315 ConstantsListOffset + getNumConstants() * ConstantSize;
316
317 for (unsigned I = 0, E = getNumRecords(); I != E; ++I) {
318 StackMapRecordOffsets.push_back(CurrentRecordOffset);
319 CurrentRecordOffset +=
320 RecordAccessor(&StackMapSection[CurrentRecordOffset]).getSizeInBytes();
321 }
322 }
323
324 /// Validates the header of the specified stack map section.
325 static Error validateHeader(ArrayRef<uint8_t> StackMapSection) {
326 // See the comment for StackMaps::emitStackmapHeader().
327 if (StackMapSection.size() < 16)
328 return object::createError(
329 "the stack map section size (" + Twine(StackMapSection.size()) +
330 ") is less than the minimum possible size of its header (16)");
331
332 unsigned Version = StackMapSection[0];
333 if (Version != 3)
334 return object::createError(
335 "the version (" + Twine(Version) +
336 ") of the stack map section is unsupported, the "
337 "supported version is 3");
338 return Error::success();
339 }
340
341 using function_iterator = AccessorIterator<FunctionAccessor>;
342 using constant_iterator = AccessorIterator<ConstantAccessor>;
343 using record_iterator = AccessorIterator<RecordAccessor>;
344
345 /// Get the version number of this stackmap. (Always returns 3).
346 unsigned getVersion() const { return 3; }
347
348 /// Get the number of functions in the stack map.
349 uint32_t getNumFunctions() const {
350 return read<uint32_t>(&StackMapSection[NumFunctionsOffset]);
351 }
352
353 /// Get the number of large constants in the stack map.
354 uint32_t getNumConstants() const {
355 return read<uint32_t>(&StackMapSection[NumConstantsOffset]);
356 }
357
358 /// Get the number of stackmap records in the stackmap.
359 uint32_t getNumRecords() const {
360 return read<uint32_t>(&StackMapSection[NumRecordsOffset]);
361 }
362
363 /// Return an FunctionAccessor for the given function index.
364 FunctionAccessor getFunction(unsigned FunctionIndex) const {
365 return FunctionAccessor(StackMapSection.data() +
366 getFunctionOffset(FunctionIndex));
367 }
368
369 /// Begin iterator for functions.
370 function_iterator functions_begin() const {
371 return function_iterator(getFunction(0));
372 }
373
374 /// End iterator for functions.
375 function_iterator functions_end() const {
376 return function_iterator(
377 FunctionAccessor(StackMapSection.data() +
378 getFunctionOffset(getNumFunctions())));
379 }
380
381 /// Iterator range for functions.
382 iterator_range<function_iterator> functions() const {
383 return make_range(functions_begin(), functions_end());
384 }
385
386 /// Return the large constant at the given index.
387 ConstantAccessor getConstant(unsigned ConstantIndex) const {
388 return ConstantAccessor(StackMapSection.data() +
389 getConstantOffset(ConstantIndex));
390 }
391
392 /// Begin iterator for constants.
393 constant_iterator constants_begin() const {
394 return constant_iterator(getConstant(0));
395 }
396
397 /// End iterator for constants.
398 constant_iterator constants_end() const {
399 return constant_iterator(
400 ConstantAccessor(StackMapSection.data() +
401 getConstantOffset(getNumConstants())));
402 }
403
404 /// Iterator range for constants.
405 iterator_range<constant_iterator> constants() const {
406 return make_range(constants_begin(), constants_end());
407 }
408
409 /// Return a RecordAccessor for the given record index.
410 RecordAccessor getRecord(unsigned RecordIndex) const {
411 std::size_t RecordOffset = StackMapRecordOffsets[RecordIndex];
412 return RecordAccessor(StackMapSection.data() + RecordOffset);
413 }
414
415 /// Begin iterator for records.
416 record_iterator records_begin() const {
417 if (getNumRecords() == 0)
418 return record_iterator(RecordAccessor(nullptr));
419 return record_iterator(getRecord(0));
420 }
421
422 /// End iterator for records.
423 record_iterator records_end() const {
424 // Records need to be handled specially, since we cache the start addresses
425 // for them: We can't just compute the 1-past-the-end address, we have to
426 // look at the last record and use the 'next' method.
427 if (getNumRecords() == 0)
428 return record_iterator(RecordAccessor(nullptr));
429 return record_iterator(getRecord(getNumRecords() - 1).next());
430 }
431
432 /// Iterator range for records.
433 iterator_range<record_iterator> records() const {
434 return make_range(records_begin(), records_end());
435 }
436
437private:
438 template <typename T>
439 static T read(const uint8_t *P) {
440 return support::endian::read<T, Endianness, 1>(P);
441 }
442
443 static const unsigned HeaderOffset = 0;
444 static const unsigned NumFunctionsOffset = HeaderOffset + sizeof(uint32_t);
445 static const unsigned NumConstantsOffset = NumFunctionsOffset + sizeof(uint32_t);
446 static const unsigned NumRecordsOffset = NumConstantsOffset + sizeof(uint32_t);
447 static const unsigned FunctionListOffset = NumRecordsOffset + sizeof(uint32_t);
448
449 static const unsigned FunctionSize = 3 * sizeof(uint64_t);
450 static const unsigned ConstantSize = sizeof(uint64_t);
451
452 std::size_t getFunctionOffset(unsigned FunctionIndex) const {
453 return FunctionListOffset + FunctionIndex * FunctionSize;
454 }
455
456 std::size_t getConstantOffset(unsigned ConstantIndex) const {
457 return ConstantsListOffset + ConstantIndex * ConstantSize;
458 }
459
460 ArrayRef<uint8_t> StackMapSection;
461 unsigned ConstantsListOffset;
462 std::vector<unsigned> StackMapRecordOffsets;
463};
464
465} // end namespace llvm
466
467#endif // LLVM_OBJECT_STACKMAPPARSER_H
468