Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/BTF/BTFParser.cpp
35269 views
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
Err(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(Val);
59
return *this;
60
}
61
62
Err &operator<<(Error Val) {
63
handleAllErrors(std::move(Val),
64
[=](ErrorInfoBase &Info) { Stream << Info.message(); });
65
return *this;
66
}
67
68
operator Error() const {
69
return make_error<StringError>(Buffer, 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> makeExtractor(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(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> MaybeExtractor = Ctx.makeExtractor(BTF);
105
if (!MaybeExtractor)
106
return MaybeExtractor.takeError();
107
108
DataExtractor &Extractor = 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(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(StrEnd, 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(StrStart, StrEnd);
142
143
if (TypeLen > 0 && Ctx.Opts.LoadTypes) {
144
StringRef RawData = Extractor.getData().slice(TypesInfoStart, 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 = {0, BTF::BTF_KIND_UNKN << 24, {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(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(TypesBuffer32[I], Endianness);
216
217
// The type id 0 is reserved for void type.
218
Types.push_back(&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(Type);
243
Pos += Size;
244
}
245
246
return Error::success();
247
}
248
249
Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) {
250
Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTFExt);
251
if (!MaybeExtractor)
252
return MaybeExtractor.takeError();
253
254
DataExtractor &Extractor = 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(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::parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor,
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(SecNameOff);
311
std::optional<SectionRef> Sec = Ctx.findSection(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({InsnOff, FileNameOff, LineOff, LineCol});
327
C.seek(RecStart + RecSize);
328
}
329
llvm::stable_sort(Lines,
330
[](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::parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor,
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(SecNameOff);
354
std::optional<SectionRef> Sec = Ctx.findSection(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({InsnOff, TypeID, OffsetNameOff, RelocKind});
365
C.seek(RecStart + RecSize);
366
}
367
llvm::stable_sort(
368
Relocs, [](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))
403
return E;
404
if (Error E = parseBTFExt(Ctx, *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(std::move(E), 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(Offset, StringsTable.find(0, 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(SectionLines, Address);
451
}
452
453
const BTF::BPFFieldReloc *
454
BTFParser::findFieldReloc(SectionedAddress Address) const {
455
return findInfo(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(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(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(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(Reloc->OffsetNameOff);
661
SmallVector<uint32_t, 8> RawSpec;
662
663
auto Fail = [&](auto Msg) {
664
Result.resize(0);
665
relocKindName(Reloc->RelocKind, 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(SpecStr, 10, Val))
677
return Fail("spec string is not a number");
678
RawSpec.push_back(Val);
679
if (SpecStr.empty())
680
break;
681
if (SpecStr[0] != ':')
682
return Fail(format("unexpected spec string delimiter: '%c'", SpecStr[0]));
683
SpecStr = SpecStr.substr(1);
684
}
685
686
// Print relocation kind to `Stream`.
687
relocKindName(Reloc->RelocKind, Stream);
688
689
uint32_t CurId = Reloc->TypeID;
690
const BTF::CommonType *Type = findType(CurId);
691
if (!Type)
692
return Fail(format("unknown type id: %d", 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(*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(CurId);
705
if (!NextType)
706
return Fail(format("unknown type id: %d in modifiers chain", 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({*this, Type->NameOff, 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(*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>(Type)) {
769
if (T->values().size() <= Idx)
770
return Fail(format("bad value index: %d", 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>(Type)) {
775
if (T->values().size() <= Idx)
776
return Fail(format("bad value index: %d", 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("unexpected type kind for enum relocation: %d",
782
Type->getKind()));
783
}
784
785
Stream << StrOrAnon({*this, NameOff, 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(*this, Type);
823
uint32_t Idx = RawSpec[I];
824
825
if (auto *T = dyn_cast<BTF::StructType>(Type)) {
826
if (T->getVlen() <= Idx)
827
return Fail(
828
format("member index %d for spec sub-string %d is out of range",
829
Idx, I));
830
831
const BTF::BTFMember &Member = T->members()[Idx];
832
if (I != 1 || RawSpec[0] != 0)
833
Stream << ".";
834
Stream << StrOrAnon({*this, Member.NameOff, Idx});
835
Type = findType(Member.Type);
836
if (!Type)
837
return Fail(format("unknown member type id %d for spec sub-string %d",
838
Member.Type, I));
839
} else if (auto *T = dyn_cast<BTF::ArrayType>(Type)) {
840
Stream << "[" << Idx << "]";
841
Type = findType(T->getArray().ElemType);
842
if (!Type)
843
return Fail(
844
format("unknown element type id %d for spec sub-string %d",
845
T->getArray().ElemType, I));
846
} else {
847
return Fail(format("unexpected type kind %d for spec sub-string %d",
848
Type->getKind(), I));
849
}
850
}
851
852
Stream << " (" << FullSpecStr << ")";
853
return;
854
}
855
856
return Fail(format("unknown relocation kind: %d", Reloc->RelocKind));
857
}
858
859