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