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 <support::endianness Endianness> |

25 | class StackMapParser { |

26 | public: |

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 | |

437 | private: |

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 |