1 | //===- BTFParser.cpp ------------------------------------------------------===// |
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 | // BTFParser reads/interprets .BTF and .BTF.ext ELF sections. |
10 | // Refer to BTFParser.h for API description. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/DebugInfo/BTF/BTFParser.h" |
15 | #include "llvm/ADT/StringExtras.h" |
16 | #include "llvm/Support/Endian.h" |
17 | #include "llvm/Support/Errc.h" |
18 | |
19 | #define DEBUG_TYPE "debug-info-btf-parser" |
20 | |
21 | using namespace llvm; |
22 | using object::ObjectFile; |
23 | using object::SectionedAddress; |
24 | using object::SectionRef; |
25 | |
26 | const char BTFSectionName[] = ".BTF" ; |
27 | const char BTFExtSectionName[] = ".BTF.ext" ; |
28 | |
29 | // Utility class with API similar to raw_ostream but can be cast |
30 | // to Error, e.g.: |
31 | // |
32 | // Error foo(...) { |
33 | // ... |
34 | // if (Error E = bar(...)) |
35 | // return Err("error while foo(): ") << E; |
36 | // ... |
37 | // } |
38 | // |
39 | namespace { |
40 | class Err { |
41 | std::string Buffer; |
42 | raw_string_ostream Stream; |
43 | |
44 | public: |
45 | Err(const char *InitialMsg) : Buffer(InitialMsg), Stream(Buffer) {} |
46 | (const char *SectionName, DataExtractor::Cursor &C) |
47 | : Buffer(), Stream(Buffer) { |
48 | *this << "error while reading " << SectionName |
49 | << " section: " << C.takeError(); |
50 | }; |
51 | |
52 | template <typename T> Err &operator<<(T Val) { |
53 | Stream << Val; |
54 | return *this; |
55 | } |
56 | |
57 | Err &write_hex(unsigned long long Val) { |
58 | Stream.write_hex(N: Val); |
59 | return *this; |
60 | } |
61 | |
62 | Err &operator<<(Error Val) { |
63 | handleAllErrors(E: std::move(Val), |
64 | Handlers: [=](ErrorInfoBase &Info) { Stream << Info.message(); }); |
65 | return *this; |
66 | } |
67 | |
68 | operator Error() const { |
69 | return make_error<StringError>(Args: Buffer, Args: errc::invalid_argument); |
70 | } |
71 | }; |
72 | } // anonymous namespace |
73 | |
74 | // ParseContext wraps information that is only necessary while parsing |
75 | // ObjectFile and can be discarded once parsing is done. |
76 | // Used by BTFParser::parse* auxiliary functions. |
77 | struct BTFParser::ParseContext { |
78 | const ObjectFile &Obj; |
79 | const ParseOptions &Opts; |
80 | // Map from ELF section name to SectionRef |
81 | DenseMap<StringRef, SectionRef> Sections; |
82 | |
83 | public: |
84 | ParseContext(const ObjectFile &Obj, const ParseOptions &Opts) |
85 | : Obj(Obj), Opts(Opts) {} |
86 | |
87 | Expected<DataExtractor> (SectionRef Sec) { |
88 | Expected<StringRef> Contents = Sec.getContents(); |
89 | if (!Contents) |
90 | return Contents.takeError(); |
91 | return DataExtractor(Contents.get(), Obj.isLittleEndian(), |
92 | Obj.getBytesInAddress()); |
93 | } |
94 | |
95 | std::optional<SectionRef> findSection(StringRef Name) const { |
96 | auto It = Sections.find(Val: Name); |
97 | if (It != Sections.end()) |
98 | return It->second; |
99 | return std::nullopt; |
100 | } |
101 | }; |
102 | |
103 | Error BTFParser::parseBTF(ParseContext &Ctx, SectionRef BTF) { |
104 | Expected<DataExtractor> = Ctx.makeExtractor(Sec: BTF); |
105 | if (!MaybeExtractor) |
106 | return MaybeExtractor.takeError(); |
107 | |
108 | DataExtractor & = MaybeExtractor.get(); |
109 | DataExtractor::Cursor C = DataExtractor::Cursor(0); |
110 | uint16_t Magic = Extractor.getU16(C); |
111 | if (!C) |
112 | return Err(".BTF" , C); |
113 | if (Magic != BTF::MAGIC) |
114 | return Err("invalid .BTF magic: " ).write_hex(Val: Magic); |
115 | uint8_t Version = Extractor.getU8(C); |
116 | if (!C) |
117 | return Err(".BTF" , C); |
118 | if (Version != 1) |
119 | return Err("unsupported .BTF version: " ) << (unsigned)Version; |
120 | (void)Extractor.getU8(C); // flags |
121 | uint32_t HdrLen = Extractor.getU32(C); |
122 | if (!C) |
123 | return Err(".BTF" , C); |
124 | if (HdrLen < 8) |
125 | return Err("unexpected .BTF header length: " ) << HdrLen; |
126 | uint32_t TypeOff = Extractor.getU32(C); |
127 | uint32_t TypeLen = Extractor.getU32(C); |
128 | uint32_t StrOff = Extractor.getU32(C); |
129 | uint32_t StrLen = Extractor.getU32(C); |
130 | uint32_t StrStart = HdrLen + StrOff; |
131 | uint32_t StrEnd = StrStart + StrLen; |
132 | uint32_t TypesInfoStart = HdrLen + TypeOff; |
133 | uint32_t TypesInfoEnd = TypesInfoStart + TypeLen; |
134 | uint32_t BytesExpected = std::max(a: StrEnd, b: TypesInfoEnd); |
135 | if (!C) |
136 | return Err(".BTF" , C); |
137 | if (Extractor.getData().size() < BytesExpected) |
138 | return Err("invalid .BTF section size, expecting at-least " ) |
139 | << BytesExpected << " bytes" ; |
140 | |
141 | StringsTable = Extractor.getData().slice(Start: StrStart, End: StrEnd); |
142 | |
143 | if (TypeLen > 0 && Ctx.Opts.LoadTypes) { |
144 | StringRef RawData = Extractor.getData().slice(Start: TypesInfoStart, End: TypesInfoEnd); |
145 | if (Error E = parseTypesInfo(Ctx, TypesInfoStart, RawData)) |
146 | return E; |
147 | } |
148 | |
149 | return Error::success(); |
150 | } |
151 | |
152 | // Compute record size for each BTF::CommonType sub-type |
153 | // (including entries in the tail position). |
154 | static size_t byteSize(BTF::CommonType *Type) { |
155 | size_t Size = sizeof(BTF::CommonType); |
156 | switch (Type->getKind()) { |
157 | case BTF::BTF_KIND_INT: |
158 | Size += sizeof(uint32_t); |
159 | break; |
160 | case BTF::BTF_KIND_ARRAY: |
161 | Size += sizeof(BTF::BTFArray); |
162 | break; |
163 | case BTF::BTF_KIND_VAR: |
164 | Size += sizeof(uint32_t); |
165 | break; |
166 | case BTF::BTF_KIND_DECL_TAG: |
167 | Size += sizeof(uint32_t); |
168 | break; |
169 | case BTF::BTF_KIND_STRUCT: |
170 | case BTF::BTF_KIND_UNION: |
171 | Size += sizeof(BTF::BTFMember) * Type->getVlen(); |
172 | break; |
173 | case BTF::BTF_KIND_ENUM: |
174 | Size += sizeof(BTF::BTFEnum) * Type->getVlen(); |
175 | break; |
176 | case BTF::BTF_KIND_ENUM64: |
177 | Size += sizeof(BTF::BTFEnum64) * Type->getVlen(); |
178 | break; |
179 | case BTF::BTF_KIND_FUNC_PROTO: |
180 | Size += sizeof(BTF::BTFParam) * Type->getVlen(); |
181 | break; |
182 | case BTF::BTF_KIND_DATASEC: |
183 | Size += sizeof(BTF::BTFDataSec) * Type->getVlen(); |
184 | break; |
185 | } |
186 | return Size; |
187 | } |
188 | |
189 | // Guard value for voids, simplifies code a bit, but NameOff is not |
190 | // actually valid. |
191 | const BTF::CommonType VoidTypeInst = {.NameOff: 0, .Info: BTF::BTF_KIND_UNKN << 24, {.Size: 0}}; |
192 | |
193 | // Type information "parsing" is very primitive: |
194 | // - The `RawData` is copied to a buffer owned by `BTFParser` instance. |
195 | // - The buffer is treated as an array of `uint32_t` values, each value |
196 | // is swapped to use native endianness. This is possible, because |
197 | // according to BTF spec all buffer elements are structures comprised |
198 | // of `uint32_t` fields. |
199 | // - `BTFParser::Types` vector is filled with pointers to buffer |
200 | // elements, using `byteSize()` function to slice the buffer at type |
201 | // record boundaries. |
202 | // - If at some point a type definition with incorrect size (logical size |
203 | // exceeding buffer boundaries) is reached it is not added to the |
204 | // `BTFParser::Types` vector and the process stops. |
205 | Error BTFParser::parseTypesInfo(ParseContext &Ctx, uint64_t TypesInfoStart, |
206 | StringRef RawData) { |
207 | using support::endian::byte_swap; |
208 | |
209 | TypesBuffer = OwningArrayRef<uint8_t>(arrayRefFromStringRef(Input: RawData)); |
210 | // Switch endianness if necessary. |
211 | endianness Endianness = Ctx.Obj.isLittleEndian() ? llvm::endianness::little |
212 | : llvm::endianness::big; |
213 | uint32_t *TypesBuffer32 = (uint32_t *)TypesBuffer.data(); |
214 | for (uint64_t I = 0; I < TypesBuffer.size() / 4; ++I) |
215 | TypesBuffer32[I] = byte_swap(value: TypesBuffer32[I], endian: Endianness); |
216 | |
217 | // The type id 0 is reserved for void type. |
218 | Types.push_back(x: &VoidTypeInst); |
219 | |
220 | uint64_t Pos = 0; |
221 | while (Pos < RawData.size()) { |
222 | uint64_t BytesLeft = RawData.size() - Pos; |
223 | uint64_t Offset = TypesInfoStart + Pos; |
224 | BTF::CommonType *Type = (BTF::CommonType *)&TypesBuffer[Pos]; |
225 | if (BytesLeft < sizeof(*Type)) |
226 | return Err("incomplete type definition in .BTF section:" ) |
227 | << " offset " << Offset << ", index " << Types.size(); |
228 | |
229 | uint64_t Size = byteSize(Type); |
230 | if (BytesLeft < Size) |
231 | return Err("incomplete type definition in .BTF section:" ) |
232 | << " offset=" << Offset << ", index=" << Types.size() |
233 | << ", vlen=" << Type->getVlen(); |
234 | |
235 | LLVM_DEBUG({ |
236 | llvm::dbgs() << "Adding BTF type:\n" |
237 | << " Id = " << Types.size() << "\n" |
238 | << " Kind = " << Type->getKind() << "\n" |
239 | << " Name = " << findString(Type->NameOff) << "\n" |
240 | << " Record Size = " << Size << "\n" ; |
241 | }); |
242 | Types.push_back(x: Type); |
243 | Pos += Size; |
244 | } |
245 | |
246 | return Error::success(); |
247 | } |
248 | |
249 | Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) { |
250 | Expected<DataExtractor> = Ctx.makeExtractor(Sec: BTFExt); |
251 | if (!MaybeExtractor) |
252 | return MaybeExtractor.takeError(); |
253 | |
254 | DataExtractor & = MaybeExtractor.get(); |
255 | DataExtractor::Cursor C = DataExtractor::Cursor(0); |
256 | uint16_t Magic = Extractor.getU16(C); |
257 | if (!C) |
258 | return Err(".BTF.ext" , C); |
259 | if (Magic != BTF::MAGIC) |
260 | return Err("invalid .BTF.ext magic: " ).write_hex(Val: Magic); |
261 | uint8_t Version = Extractor.getU8(C); |
262 | if (!C) |
263 | return Err(".BTF" , C); |
264 | if (Version != 1) |
265 | return Err("unsupported .BTF.ext version: " ) << (unsigned)Version; |
266 | (void)Extractor.getU8(C); // flags |
267 | uint32_t HdrLen = Extractor.getU32(C); |
268 | if (!C) |
269 | return Err(".BTF.ext" , C); |
270 | if (HdrLen < 8) |
271 | return Err("unexpected .BTF.ext header length: " ) << HdrLen; |
272 | (void)Extractor.getU32(C); // func_info_off |
273 | (void)Extractor.getU32(C); // func_info_len |
274 | uint32_t LineInfoOff = Extractor.getU32(C); |
275 | uint32_t LineInfoLen = Extractor.getU32(C); |
276 | uint32_t RelocInfoOff = Extractor.getU32(C); |
277 | uint32_t RelocInfoLen = Extractor.getU32(C); |
278 | if (!C) |
279 | return Err(".BTF.ext" , C); |
280 | |
281 | if (LineInfoLen > 0 && Ctx.Opts.LoadLines) { |
282 | uint32_t LineInfoStart = HdrLen + LineInfoOff; |
283 | uint32_t LineInfoEnd = LineInfoStart + LineInfoLen; |
284 | if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd)) |
285 | return E; |
286 | } |
287 | |
288 | if (RelocInfoLen > 0 && Ctx.Opts.LoadRelocs) { |
289 | uint32_t RelocInfoStart = HdrLen + RelocInfoOff; |
290 | uint32_t RelocInfoEnd = RelocInfoStart + RelocInfoLen; |
291 | if (Error E = parseRelocInfo(Ctx, Extractor, RelocInfoStart, RelocInfoEnd)) |
292 | return E; |
293 | } |
294 | |
295 | return Error::success(); |
296 | } |
297 | |
298 | Error BTFParser::(ParseContext &Ctx, DataExtractor &, |
299 | uint64_t LineInfoStart, uint64_t LineInfoEnd) { |
300 | DataExtractor::Cursor C = DataExtractor::Cursor(LineInfoStart); |
301 | uint32_t RecSize = Extractor.getU32(C); |
302 | if (!C) |
303 | return Err(".BTF.ext" , C); |
304 | if (RecSize < 16) |
305 | return Err("unexpected .BTF.ext line info record length: " ) << RecSize; |
306 | |
307 | while (C && C.tell() < LineInfoEnd) { |
308 | uint32_t SecNameOff = Extractor.getU32(C); |
309 | uint32_t NumInfo = Extractor.getU32(C); |
310 | StringRef SecName = findString(Offset: SecNameOff); |
311 | std::optional<SectionRef> Sec = Ctx.findSection(Name: SecName); |
312 | if (!C) |
313 | return Err(".BTF.ext" , C); |
314 | if (!Sec) |
315 | return Err("" ) << "can't find section '" << SecName |
316 | << "' while parsing .BTF.ext line info" ; |
317 | BTFLinesVector &Lines = SectionLines[Sec->getIndex()]; |
318 | for (uint32_t I = 0; C && I < NumInfo; ++I) { |
319 | uint64_t RecStart = C.tell(); |
320 | uint32_t InsnOff = Extractor.getU32(C); |
321 | uint32_t FileNameOff = Extractor.getU32(C); |
322 | uint32_t LineOff = Extractor.getU32(C); |
323 | uint32_t LineCol = Extractor.getU32(C); |
324 | if (!C) |
325 | return Err(".BTF.ext" , C); |
326 | Lines.push_back(Elt: {.InsnOffset: InsnOff, .FileNameOff: FileNameOff, .LineOff: LineOff, .LineCol: LineCol}); |
327 | C.seek(NewOffSet: RecStart + RecSize); |
328 | } |
329 | llvm::stable_sort(Range&: Lines, |
330 | C: [](const BTF::BPFLineInfo &L, const BTF::BPFLineInfo &R) { |
331 | return L.InsnOffset < R.InsnOffset; |
332 | }); |
333 | } |
334 | if (!C) |
335 | return Err(".BTF.ext" , C); |
336 | |
337 | return Error::success(); |
338 | } |
339 | |
340 | Error BTFParser::(ParseContext &Ctx, DataExtractor &, |
341 | uint64_t RelocInfoStart, |
342 | uint64_t RelocInfoEnd) { |
343 | DataExtractor::Cursor C = DataExtractor::Cursor(RelocInfoStart); |
344 | uint32_t RecSize = Extractor.getU32(C); |
345 | if (!C) |
346 | return Err(".BTF.ext" , C); |
347 | if (RecSize < 16) |
348 | return Err("unexpected .BTF.ext field reloc info record length: " ) |
349 | << RecSize; |
350 | while (C && C.tell() < RelocInfoEnd) { |
351 | uint32_t SecNameOff = Extractor.getU32(C); |
352 | uint32_t NumInfo = Extractor.getU32(C); |
353 | StringRef SecName = findString(Offset: SecNameOff); |
354 | std::optional<SectionRef> Sec = Ctx.findSection(Name: SecName); |
355 | BTFRelocVector &Relocs = SectionRelocs[Sec->getIndex()]; |
356 | for (uint32_t I = 0; C && I < NumInfo; ++I) { |
357 | uint64_t RecStart = C.tell(); |
358 | uint32_t InsnOff = Extractor.getU32(C); |
359 | uint32_t TypeID = Extractor.getU32(C); |
360 | uint32_t OffsetNameOff = Extractor.getU32(C); |
361 | uint32_t RelocKind = Extractor.getU32(C); |
362 | if (!C) |
363 | return Err(".BTF.ext" , C); |
364 | Relocs.push_back(Elt: {.InsnOffset: InsnOff, .TypeID: TypeID, .OffsetNameOff: OffsetNameOff, .RelocKind: RelocKind}); |
365 | C.seek(NewOffSet: RecStart + RecSize); |
366 | } |
367 | llvm::stable_sort( |
368 | Range&: Relocs, C: [](const BTF::BPFFieldReloc &L, const BTF::BPFFieldReloc &R) { |
369 | return L.InsnOffset < R.InsnOffset; |
370 | }); |
371 | } |
372 | if (!C) |
373 | return Err(".BTF.ext" , C); |
374 | |
375 | return Error::success(); |
376 | } |
377 | |
378 | Error BTFParser::parse(const ObjectFile &Obj, const ParseOptions &Opts) { |
379 | StringsTable = StringRef(); |
380 | SectionLines.clear(); |
381 | SectionRelocs.clear(); |
382 | Types.clear(); |
383 | TypesBuffer = OwningArrayRef<uint8_t>(); |
384 | |
385 | ParseContext Ctx(Obj, Opts); |
386 | std::optional<SectionRef> BTF; |
387 | std::optional<SectionRef> BTFExt; |
388 | for (SectionRef Sec : Obj.sections()) { |
389 | Expected<StringRef> MaybeName = Sec.getName(); |
390 | if (!MaybeName) |
391 | return Err("error while reading section name: " ) << MaybeName.takeError(); |
392 | Ctx.Sections[*MaybeName] = Sec; |
393 | if (*MaybeName == BTFSectionName) |
394 | BTF = Sec; |
395 | if (*MaybeName == BTFExtSectionName) |
396 | BTFExt = Sec; |
397 | } |
398 | if (!BTF) |
399 | return Err("can't find .BTF section" ); |
400 | if (!BTFExt) |
401 | return Err("can't find .BTF.ext section" ); |
402 | if (Error E = parseBTF(Ctx, BTF: *BTF)) |
403 | return E; |
404 | if (Error E = parseBTFExt(Ctx, BTFExt: *BTFExt)) |
405 | return E; |
406 | |
407 | return Error::success(); |
408 | } |
409 | |
410 | bool BTFParser::hasBTFSections(const ObjectFile &Obj) { |
411 | bool HasBTF = false; |
412 | bool HasBTFExt = false; |
413 | for (SectionRef Sec : Obj.sections()) { |
414 | Expected<StringRef> Name = Sec.getName(); |
415 | if (Error E = Name.takeError()) { |
416 | logAllUnhandledErrors(E: std::move(E), OS&: errs()); |
417 | continue; |
418 | } |
419 | HasBTF |= *Name == BTFSectionName; |
420 | HasBTFExt |= *Name == BTFExtSectionName; |
421 | if (HasBTF && HasBTFExt) |
422 | return true; |
423 | } |
424 | return false; |
425 | } |
426 | |
427 | StringRef BTFParser::findString(uint32_t Offset) const { |
428 | return StringsTable.slice(Start: Offset, End: StringsTable.find(C: 0, From: Offset)); |
429 | } |
430 | |
431 | template <typename T> |
432 | static const T *findInfo(const DenseMap<uint64_t, SmallVector<T, 0>> &SecMap, |
433 | SectionedAddress Address) { |
434 | auto MaybeSecInfo = SecMap.find(Address.SectionIndex); |
435 | if (MaybeSecInfo == SecMap.end()) |
436 | return nullptr; |
437 | |
438 | const SmallVector<T, 0> &SecInfo = MaybeSecInfo->second; |
439 | const uint64_t TargetOffset = Address.Address; |
440 | typename SmallVector<T, 0>::const_iterator MaybeInfo = llvm::partition_point( |
441 | SecInfo, [=](const T &Entry) { return Entry.InsnOffset < TargetOffset; }); |
442 | if (MaybeInfo == SecInfo.end() || MaybeInfo->InsnOffset != Address.Address) |
443 | return nullptr; |
444 | |
445 | return &*MaybeInfo; |
446 | } |
447 | |
448 | const BTF::BPFLineInfo * |
449 | BTFParser::findLineInfo(SectionedAddress Address) const { |
450 | return findInfo(SecMap: SectionLines, Address); |
451 | } |
452 | |
453 | const BTF::BPFFieldReloc * |
454 | BTFParser::findFieldReloc(SectionedAddress Address) const { |
455 | return findInfo(SecMap: SectionRelocs, Address); |
456 | } |
457 | |
458 | const BTF::CommonType *BTFParser::findType(uint32_t Id) const { |
459 | if (Id < Types.size()) |
460 | return Types[Id]; |
461 | return nullptr; |
462 | } |
463 | |
464 | enum RelocKindGroup { |
465 | RKG_FIELD, |
466 | RKG_TYPE, |
467 | RKG_ENUMVAL, |
468 | RKG_UNKNOWN, |
469 | }; |
470 | |
471 | static RelocKindGroup relocKindGroup(const BTF::BPFFieldReloc *Reloc) { |
472 | switch (Reloc->RelocKind) { |
473 | case BTF::FIELD_BYTE_OFFSET: |
474 | case BTF::FIELD_BYTE_SIZE: |
475 | case BTF::FIELD_EXISTENCE: |
476 | case BTF::FIELD_SIGNEDNESS: |
477 | case BTF::FIELD_LSHIFT_U64: |
478 | case BTF::FIELD_RSHIFT_U64: |
479 | return RKG_FIELD; |
480 | case BTF::BTF_TYPE_ID_LOCAL: |
481 | case BTF::BTF_TYPE_ID_REMOTE: |
482 | case BTF::TYPE_EXISTENCE: |
483 | case BTF::TYPE_MATCH: |
484 | case BTF::TYPE_SIZE: |
485 | return RKG_TYPE; |
486 | case BTF::ENUM_VALUE_EXISTENCE: |
487 | case BTF::ENUM_VALUE: |
488 | return RKG_ENUMVAL; |
489 | default: |
490 | return RKG_UNKNOWN; |
491 | } |
492 | } |
493 | |
494 | static bool isMod(const BTF::CommonType *Type) { |
495 | switch (Type->getKind()) { |
496 | case BTF::BTF_KIND_VOLATILE: |
497 | case BTF::BTF_KIND_CONST: |
498 | case BTF::BTF_KIND_RESTRICT: |
499 | case BTF::BTF_KIND_TYPE_TAG: |
500 | return true; |
501 | default: |
502 | return false; |
503 | } |
504 | } |
505 | |
506 | static bool printMod(const BTFParser &BTF, const BTF::CommonType *Type, |
507 | raw_ostream &Stream) { |
508 | switch (Type->getKind()) { |
509 | case BTF::BTF_KIND_CONST: |
510 | Stream << " const" ; |
511 | break; |
512 | case BTF::BTF_KIND_VOLATILE: |
513 | Stream << " volatile" ; |
514 | break; |
515 | case BTF::BTF_KIND_RESTRICT: |
516 | Stream << " restrict" ; |
517 | break; |
518 | case BTF::BTF_KIND_TYPE_TAG: |
519 | Stream << " type_tag(\"" << BTF.findString(Offset: Type->NameOff) << "\")" ; |
520 | break; |
521 | default: |
522 | return false; |
523 | } |
524 | return true; |
525 | } |
526 | |
527 | static const BTF::CommonType *skipModsAndTypedefs(const BTFParser &BTF, |
528 | const BTF::CommonType *Type) { |
529 | while (isMod(Type) || Type->getKind() == BTF::BTF_KIND_TYPEDEF) { |
530 | auto *Base = BTF.findType(Id: Type->Type); |
531 | if (!Base) |
532 | break; |
533 | Type = Base; |
534 | } |
535 | return Type; |
536 | } |
537 | |
538 | namespace { |
539 | struct StrOrAnon { |
540 | const BTFParser &BTF; |
541 | uint32_t Offset; |
542 | uint32_t Idx; |
543 | }; |
544 | |
545 | static raw_ostream &operator<<(raw_ostream &Stream, const StrOrAnon &S) { |
546 | StringRef Str = S.BTF.findString(Offset: S.Offset); |
547 | if (Str.empty()) |
548 | Stream << "<anon " << S.Idx << ">" ; |
549 | else |
550 | Stream << Str; |
551 | return Stream; |
552 | } |
553 | } // anonymous namespace |
554 | |
555 | static void relocKindName(uint32_t X, raw_ostream &Out) { |
556 | Out << "<" ; |
557 | switch (X) { |
558 | default: |
559 | Out << "reloc kind #" << X; |
560 | break; |
561 | case BTF::FIELD_BYTE_OFFSET: |
562 | Out << "byte_off" ; |
563 | break; |
564 | case BTF::FIELD_BYTE_SIZE: |
565 | Out << "byte_sz" ; |
566 | break; |
567 | case BTF::FIELD_EXISTENCE: |
568 | Out << "field_exists" ; |
569 | break; |
570 | case BTF::FIELD_SIGNEDNESS: |
571 | Out << "signed" ; |
572 | break; |
573 | case BTF::FIELD_LSHIFT_U64: |
574 | Out << "lshift_u64" ; |
575 | break; |
576 | case BTF::FIELD_RSHIFT_U64: |
577 | Out << "rshift_u64" ; |
578 | break; |
579 | case BTF::BTF_TYPE_ID_LOCAL: |
580 | Out << "local_type_id" ; |
581 | break; |
582 | case BTF::BTF_TYPE_ID_REMOTE: |
583 | Out << "target_type_id" ; |
584 | break; |
585 | case BTF::TYPE_EXISTENCE: |
586 | Out << "type_exists" ; |
587 | break; |
588 | case BTF::TYPE_MATCH: |
589 | Out << "type_matches" ; |
590 | break; |
591 | case BTF::TYPE_SIZE: |
592 | Out << "type_size" ; |
593 | break; |
594 | case BTF::ENUM_VALUE_EXISTENCE: |
595 | Out << "enumval_exists" ; |
596 | break; |
597 | case BTF::ENUM_VALUE: |
598 | Out << "enumval_value" ; |
599 | break; |
600 | } |
601 | Out << ">" ; |
602 | } |
603 | |
604 | // Produces a human readable description of a CO-RE relocation. |
605 | // Such relocations are generated by BPF backend, and processed |
606 | // by libbpf's BPF program loader [1]. |
607 | // |
608 | // Each relocation record has the following information: |
609 | // - Relocation kind; |
610 | // - BTF type ID; |
611 | // - Access string offset in string table. |
612 | // |
613 | // There are different kinds of relocations, these kinds could be split |
614 | // in three groups: |
615 | // - load-time information about types (size, existence), |
616 | // `BTFParser::symbolize()` output for such relocations uses the template: |
617 | // |
618 | // <relocation-kind> [<id>] <type-name> |
619 | // |
620 | // For example: |
621 | // - "<type_exists> [7] struct foo" |
622 | // - "<type_size> [7] struct foo" |
623 | // |
624 | // - load-time information about enums (literal existence, literal value), |
625 | // `BTFParser::symbolize()` output for such relocations uses the template: |
626 | // |
627 | // <relocation-kind> [<id>] <type-name>::<literal-name> = <original-value> |
628 | // |
629 | // For example: |
630 | // - "<enumval_exists> [5] enum foo::U = 1" |
631 | // - "<enumval_value> [5] enum foo::V = 2" |
632 | // |
633 | // - load-time information about fields (e.g. field offset), |
634 | // `BTFParser::symbolize()` output for such relocations uses the template: |
635 | // |
636 | // <relocation-kind> [<id>] \ |
637 | // <type-name>::[N].<field-1-name>...<field-M-name> \ |
638 | // (<access string>) |
639 | // |
640 | // For example: |
641 | // - "<byte_off> [8] struct bar::[7].v (7:1)" |
642 | // - "<field_exists> [8] struct bar::v (0:1)" |
643 | // |
644 | // If relocation description is not valid output follows the following pattern: |
645 | // |
646 | // <relocation-kind> <type-id>::<unprocessedaccess-string> <<error-msg>> |
647 | // |
648 | // For example: |
649 | // |
650 | // - "<type_sz> [42] '' <unknown type id: 42>" |
651 | // - "<byte_off> [4] '0:' <field spec too short>" |
652 | // |
653 | // Additional examples could be found in unit tests, see |
654 | // llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp. |
655 | // |
656 | // [1] https://www.kernel.org/doc/html/latest/bpf/libbpf/index.html |
657 | void BTFParser::symbolize(const BTF::BPFFieldReloc *Reloc, |
658 | SmallVectorImpl<char> &Result) const { |
659 | raw_svector_ostream Stream(Result); |
660 | StringRef FullSpecStr = findString(Offset: Reloc->OffsetNameOff); |
661 | SmallVector<uint32_t, 8> RawSpec; |
662 | |
663 | auto Fail = [&](auto Msg) { |
664 | Result.resize(N: 0); |
665 | relocKindName(X: Reloc->RelocKind, Out&: Stream); |
666 | Stream << " [" << Reloc->TypeID << "] '" << FullSpecStr << "'" |
667 | << " <" << Msg << ">" ; |
668 | }; |
669 | |
670 | // Relocation access string follows pattern [0-9]+(:[0-9]+)*, |
671 | // e.g.: 12:22:3. Code below splits `SpecStr` by ':', parses |
672 | // numbers, and pushes them to `RawSpec`. |
673 | StringRef SpecStr = FullSpecStr; |
674 | while (SpecStr.size()) { |
675 | unsigned long long Val; |
676 | if (consumeUnsignedInteger(Str&: SpecStr, Radix: 10, Result&: Val)) |
677 | return Fail("spec string is not a number" ); |
678 | RawSpec.push_back(Elt: Val); |
679 | if (SpecStr.empty()) |
680 | break; |
681 | if (SpecStr[0] != ':') |
682 | return Fail(format(Fmt: "unexpected spec string delimiter: '%c'" , Vals: SpecStr[0])); |
683 | SpecStr = SpecStr.substr(Start: 1); |
684 | } |
685 | |
686 | // Print relocation kind to `Stream`. |
687 | relocKindName(X: Reloc->RelocKind, Out&: Stream); |
688 | |
689 | uint32_t CurId = Reloc->TypeID; |
690 | const BTF::CommonType *Type = findType(Id: CurId); |
691 | if (!Type) |
692 | return Fail(format(Fmt: "unknown type id: %d" , Vals: CurId)); |
693 | |
694 | Stream << " [" << CurId << "]" ; |
695 | |
696 | // `Type` might have modifiers, e.g. for type 'const int' the `Type` |
697 | // would refer to BTF type of kind BTF_KIND_CONST. |
698 | // Print all these modifiers to `Stream`. |
699 | for (uint32_t ChainLen = 0; printMod(BTF: *this, Type, Stream); ++ChainLen) { |
700 | if (ChainLen >= 32) |
701 | return Fail("modifiers chain is too long" ); |
702 | |
703 | CurId = Type->Type; |
704 | const BTF::CommonType *NextType = findType(Id: CurId); |
705 | if (!NextType) |
706 | return Fail(format(Fmt: "unknown type id: %d in modifiers chain" , Vals: CurId)); |
707 | Type = NextType; |
708 | } |
709 | // Print the type name to `Stream`. |
710 | if (CurId == 0) { |
711 | Stream << " void" ; |
712 | } else { |
713 | switch (Type->getKind()) { |
714 | case BTF::BTF_KIND_TYPEDEF: |
715 | Stream << " typedef" ; |
716 | break; |
717 | case BTF::BTF_KIND_STRUCT: |
718 | Stream << " struct" ; |
719 | break; |
720 | case BTF::BTF_KIND_UNION: |
721 | Stream << " union" ; |
722 | break; |
723 | case BTF::BTF_KIND_ENUM: |
724 | Stream << " enum" ; |
725 | break; |
726 | case BTF::BTF_KIND_ENUM64: |
727 | Stream << " enum" ; |
728 | break; |
729 | case BTF::BTF_KIND_FWD: |
730 | if (Type->Info & BTF::FWD_UNION_FLAG) |
731 | Stream << " fwd union" ; |
732 | else |
733 | Stream << " fwd struct" ; |
734 | break; |
735 | default: |
736 | break; |
737 | } |
738 | Stream << " " << StrOrAnon({.BTF: *this, .Offset: Type->NameOff, .Idx: CurId}); |
739 | } |
740 | |
741 | RelocKindGroup Group = relocKindGroup(Reloc); |
742 | // Type-based relocations don't use access string but clang backend |
743 | // generates '0' and libbpf checks it's value, do the same here. |
744 | if (Group == RKG_TYPE) { |
745 | if (RawSpec.size() != 1 || RawSpec[0] != 0) |
746 | return Fail("unexpected type-based relocation spec: should be '0'" ); |
747 | return; |
748 | } |
749 | |
750 | Stream << "::" ; |
751 | |
752 | // For enum-based relocations access string is a single number, |
753 | // corresponding to the enum literal sequential number. |
754 | // E.g. for `enum E { U, V }`, relocation requesting value of `V` |
755 | // would look as follows: |
756 | // - kind: BTF::ENUM_VALUE |
757 | // - BTF id: id for `E` |
758 | // - access string: "1" |
759 | if (Group == RKG_ENUMVAL) { |
760 | Type = skipModsAndTypedefs(BTF: *this, Type); |
761 | |
762 | if (RawSpec.size() != 1) |
763 | return Fail("unexpected enumval relocation spec size" ); |
764 | |
765 | uint32_t NameOff; |
766 | uint64_t Val; |
767 | uint32_t Idx = RawSpec[0]; |
768 | if (auto *T = dyn_cast<BTF::EnumType>(Val: Type)) { |
769 | if (T->values().size() <= Idx) |
770 | return Fail(format(Fmt: "bad value index: %d" , Vals: Idx)); |
771 | const BTF::BTFEnum &E = T->values()[Idx]; |
772 | NameOff = E.NameOff; |
773 | Val = E.Val; |
774 | } else if (auto *T = dyn_cast<BTF::Enum64Type>(Val: Type)) { |
775 | if (T->values().size() <= Idx) |
776 | return Fail(format(Fmt: "bad value index: %d" , Vals: Idx)); |
777 | const BTF::BTFEnum64 &E = T->values()[Idx]; |
778 | NameOff = E.NameOff; |
779 | Val = (uint64_t)E.Val_Hi32 << 32u | E.Val_Lo32; |
780 | } else { |
781 | return Fail(format(Fmt: "unexpected type kind for enum relocation: %d" , |
782 | Vals: Type->getKind())); |
783 | } |
784 | |
785 | Stream << StrOrAnon({.BTF: *this, .Offset: NameOff, .Idx: Idx}); |
786 | if (Type->Info & BTF::ENUM_SIGNED_FLAG) |
787 | Stream << " = " << (int64_t)Val; |
788 | else |
789 | Stream << " = " << (uint64_t)Val; |
790 | return; |
791 | } |
792 | |
793 | // For type-based relocations access string is an array of numbers, |
794 | // which resemble index parameters for `getelementptr` LLVM IR instruction. |
795 | // E.g. for the following types: |
796 | // |
797 | // struct foo { |
798 | // int a; |
799 | // int b; |
800 | // }; |
801 | // struct bar { |
802 | // int u; |
803 | // struct foo v[7]; |
804 | // }; |
805 | // |
806 | // Relocation requesting `offsetof(struct bar, v[2].b)` will have |
807 | // the following access string: 0:1:2:1 |
808 | // ^ ^ ^ ^ |
809 | // | | | | |
810 | // initial index | | field 'b' is a field #1 |
811 | // | | (counting from 0) |
812 | // | array index #2 |
813 | // field 'v' is a field #1 |
814 | // (counting from 0) |
815 | if (Group == RKG_FIELD) { |
816 | if (RawSpec.size() < 1) |
817 | return Fail("field spec too short" ); |
818 | |
819 | if (RawSpec[0] != 0) |
820 | Stream << "[" << RawSpec[0] << "]" ; |
821 | for (uint32_t I = 1; I < RawSpec.size(); ++I) { |
822 | Type = skipModsAndTypedefs(BTF: *this, Type); |
823 | uint32_t Idx = RawSpec[I]; |
824 | |
825 | if (auto *T = dyn_cast<BTF::StructType>(Val: Type)) { |
826 | if (T->getVlen() <= Idx) |
827 | return Fail( |
828 | format(Fmt: "member index %d for spec sub-string %d is out of range" , |
829 | Vals: Idx, Vals: I)); |
830 | |
831 | const BTF::BTFMember &Member = T->members()[Idx]; |
832 | if (I != 1 || RawSpec[0] != 0) |
833 | Stream << "." ; |
834 | Stream << StrOrAnon({.BTF: *this, .Offset: Member.NameOff, .Idx: Idx}); |
835 | Type = findType(Id: Member.Type); |
836 | if (!Type) |
837 | return Fail(format(Fmt: "unknown member type id %d for spec sub-string %d" , |
838 | Vals: Member.Type, Vals: I)); |
839 | } else if (auto *T = dyn_cast<BTF::ArrayType>(Val: Type)) { |
840 | Stream << "[" << Idx << "]" ; |
841 | Type = findType(Id: T->getArray().ElemType); |
842 | if (!Type) |
843 | return Fail( |
844 | format(Fmt: "unknown element type id %d for spec sub-string %d" , |
845 | Vals: T->getArray().ElemType, Vals: I)); |
846 | } else { |
847 | return Fail(format(Fmt: "unexpected type kind %d for spec sub-string %d" , |
848 | Vals: Type->getKind(), Vals: I)); |
849 | } |
850 | } |
851 | |
852 | Stream << " (" << FullSpecStr << ")" ; |
853 | return; |
854 | } |
855 | |
856 | return Fail(format(Fmt: "unknown relocation kind: %d" , Vals: Reloc->RelocKind)); |
857 | } |
858 | |