1//===-- SourcePrinter.cpp - source interleaving utilities ----------------===//
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#include "llvm/DebugInfo/BTF/BTFContext.h"
10#include "llvm/ObjectYAML/YAML.h"
11#include "llvm/ObjectYAML/yaml2obj.h"
12#include "llvm/Support/SwapByteOrder.h"
13#include "llvm/Testing/Support/Error.h"
14
15using namespace llvm;
16using namespace llvm::object;
17
18#define LC(Line, Col) ((Line << 10u) | Col)
19#define ASSERT_SUCCEEDED(E) ASSERT_THAT_ERROR((E), Succeeded())
20
21const char BTFEndOfData[] =
22 "error while reading .BTF section: unexpected end of data";
23const char BTFExtEndOfData[] =
24 "error while reading .BTF.ext section: unexpected end of data";
25
26static raw_ostream &operator<<(raw_ostream &OS, const yaml::BinaryRef &Ref) {
27 Ref.writeAsHex(OS);
28 return OS;
29}
30
31template <typename T>
32static yaml::BinaryRef makeBinRef(const T *Ptr, size_t Size = sizeof(T)) {
33 return yaml::BinaryRef(ArrayRef<uint8_t>((const uint8_t *)Ptr, Size));
34}
35
36namespace {
37// This is a mockup for an ELF file containing .BTF and .BTF.ext sections.
38// Binary content of these sections corresponds to the value of
39// MockData1::BTF and MockData1::Ext fields.
40//
41// The yaml::yaml2ObjectFile() is used to generate actual ELF,
42// see MockData1::makeObj().
43//
44// The `BTF` and `Ext` fields are initialized with correct values
45// valid for a small example with a few sections, fields could be
46// modified before a call to `makeObj()` to test parser with invalid
47// input, etc.
48
49struct MockData1 {
50// Use "pragma pack" to model .BTF & .BTF.ext sections content using
51// 'struct' objects. This pragma is supported by CLANG, GCC & MSVC,
52// which matters for LLVM CI.
53#pragma pack(push, 1)
54 struct B {
55 BTF::Header Header = {};
56 // No types.
57 struct S {
58 char Foo[4] = "foo";
59 char Bar[4] = "bar";
60 char Buz[4] = "buz";
61 char Line1[11] = "first line";
62 char Line2[12] = "second line";
63 char File1[4] = "a.c";
64 char File2[4] = "b.c";
65 } Strings;
66
67 B() {
68 Header.Magic = BTF::MAGIC;
69 Header.Version = 1;
70 Header.HdrLen = sizeof(Header);
71 Header.StrOff = offsetof(B, Strings) - sizeof(Header);
72 Header.StrLen = sizeof(Strings);
73 }
74 } BTF;
75
76 struct E {
77 BTF::ExtHeader Header = {};
78 // No func info.
79 struct {
80 uint32_t LineRecSize = sizeof(BTF::BPFLineInfo);
81 struct {
82 BTF::SecLineInfo Sec = {offsetof(B::S, Foo), .NumLineInfo: 2};
83 BTF::BPFLineInfo Lines[2] = {
84 {.InsnOffset: 16, offsetof(B::S, File1), offsetof(B::S, Line1), LC(7, 1)},
85 {.InsnOffset: 32, offsetof(B::S, File1), offsetof(B::S, Line2), LC(14, 5)},
86 };
87 } Foo;
88 struct {
89 BTF::SecLineInfo Sec = {offsetof(B::S, Bar), .NumLineInfo: 1};
90 BTF::BPFLineInfo Lines[1] = {
91 {.InsnOffset: 0, offsetof(B::S, File2), offsetof(B::S, Line1), LC(42, 4)},
92 };
93 } Bar;
94 } Lines;
95
96 E() {
97 Header.Magic = BTF::MAGIC;
98 Header.Version = 1;
99 Header.HdrLen = sizeof(Header);
100 Header.LineInfoOff = offsetof(E, Lines) - sizeof(Header);
101 Header.LineInfoLen = sizeof(Lines);
102 }
103 } Ext;
104#pragma pack(pop)
105
106 int BTFSectionLen = sizeof(BTF);
107 int ExtSectionLen = sizeof(Ext);
108
109 SmallString<0> Storage;
110 std::unique_ptr<ObjectFile> Obj;
111
112 ObjectFile &makeObj() {
113 std::string Buffer;
114 raw_string_ostream Yaml(Buffer);
115 Yaml << R"(
116!ELF
117FileHeader:
118 Class: ELFCLASS64)";
119 if (sys::IsBigEndianHost)
120 Yaml << "\n Data: ELFDATA2MSB";
121 else
122 Yaml << "\n Data: ELFDATA2LSB";
123 Yaml << R"(
124 Type: ET_REL
125 Machine: EM_BPF
126Sections:
127 - Name: foo
128 Type: SHT_PROGBITS
129 Size: 0x0
130 - Name: bar
131 Type: SHT_PROGBITS
132 Size: 0x0)";
133
134 if (BTFSectionLen >= 0)
135 Yaml << R"(
136 - Name: .BTF
137 Type: SHT_PROGBITS
138 Content: )"
139 << makeBinRef(Ptr: &BTF, Size: BTFSectionLen);
140
141 if (ExtSectionLen >= 0)
142 Yaml << R"(
143 - Name: .BTF.ext
144 Type: SHT_PROGBITS
145 Content: )"
146 << makeBinRef(Ptr: &Ext, Size: ExtSectionLen);
147
148 Obj = yaml::yaml2ObjectFile(Storage, Yaml: Buffer,
149 ErrHandler: [](const Twine &Err) { errs() << Err; });
150 return *Obj.get();
151 }
152};
153
154TEST(BTFParserTest, simpleCorrectInput) {
155 BTFParser BTF;
156 MockData1 Mock;
157 Error Err = BTF.parse(Obj: Mock.makeObj());
158 EXPECT_FALSE(Err);
159
160 EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Foo)), "foo");
161 EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Bar)), "bar");
162 EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Line1)), "first line");
163 EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Line2)), "second line");
164 EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File1)), "a.c");
165 EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File2)), "b.c");
166
167 // Invalid offset.
168 EXPECT_EQ(BTF.findString(sizeof(MockData1::B::S)), StringRef());
169
170 const BTF::BPFLineInfo *I1 = BTF.findLineInfo(Address: {.Address: 16, .SectionIndex: 1});
171 ASSERT_TRUE(I1);
172 EXPECT_EQ(I1->getLine(), 7u);
173 EXPECT_EQ(I1->getCol(), 1u);
174 EXPECT_EQ(BTF.findString(I1->FileNameOff), "a.c");
175 EXPECT_EQ(BTF.findString(I1->LineOff), "first line");
176
177 const BTF::BPFLineInfo *I2 = BTF.findLineInfo(Address: {.Address: 32, .SectionIndex: 1});
178 ASSERT_TRUE(I2);
179 EXPECT_EQ(I2->getLine(), 14u);
180 EXPECT_EQ(I2->getCol(), 5u);
181 EXPECT_EQ(BTF.findString(I2->FileNameOff), "a.c");
182 EXPECT_EQ(BTF.findString(I2->LineOff), "second line");
183
184 const BTF::BPFLineInfo *I3 = BTF.findLineInfo(Address: {.Address: 0, .SectionIndex: 2});
185 ASSERT_TRUE(I3);
186 EXPECT_EQ(I3->getLine(), 42u);
187 EXPECT_EQ(I3->getCol(), 4u);
188 EXPECT_EQ(BTF.findString(I3->FileNameOff), "b.c");
189 EXPECT_EQ(BTF.findString(I3->LineOff), "first line");
190
191 // No info for insn address.
192 EXPECT_FALSE(BTF.findLineInfo({24, 1}));
193 EXPECT_FALSE(BTF.findLineInfo({8, 2}));
194 // No info for section number.
195 EXPECT_FALSE(BTF.findLineInfo({16, 3}));
196}
197
198TEST(BTFParserTest, badSectionNameOffset) {
199 BTFParser BTF;
200 MockData1 Mock;
201 // "foo" is section #1, corrupting it's name offset will make impossible
202 // to match section name with section index when BTF is parsed.
203 Mock.Ext.Lines.Foo.Sec.SecNameOff = 100500;
204 Error Err = BTF.parse(Obj: Mock.makeObj());
205 EXPECT_FALSE(Err);
206 // "foo" line info should be corrupted.
207 EXPECT_FALSE(BTF.findLineInfo({16, 1}));
208 // "bar" line info should be ok.
209 EXPECT_TRUE(BTF.findLineInfo({0, 2}));
210}
211
212// Keep this as macro to preserve line number info.
213#define EXPECT_PARSE_ERROR(Mock, Message) \
214 do { \
215 BTFParser BTF; \
216 EXPECT_THAT_ERROR(BTF.parse((Mock).makeObj()), \
217 FailedWithMessage(testing::HasSubstr(Message))); \
218 } while (false)
219
220TEST(BTFParserTest, badBTFMagic) {
221 MockData1 Mock;
222 Mock.BTF.Header.Magic = 42;
223 EXPECT_PARSE_ERROR(Mock, "invalid .BTF magic: 2a");
224}
225
226TEST(BTFParserTest, badBTFVersion) {
227 MockData1 Mock;
228 Mock.BTF.Header.Version = 42;
229 EXPECT_PARSE_ERROR(Mock, "unsupported .BTF version: 42");
230}
231
232TEST(BTFParserTest, badBTFHdrLen) {
233 MockData1 Mock;
234 Mock.BTF.Header.HdrLen = 5;
235 EXPECT_PARSE_ERROR(Mock, "unexpected .BTF header length: 5");
236}
237
238TEST(BTFParserTest, badBTFSectionLen) {
239 MockData1 Mock1, Mock2;
240
241 // Cut-off string section by one byte.
242 Mock1.BTFSectionLen =
243 offsetof(MockData1::B, Strings) + sizeof(MockData1::B::S) - 1;
244 EXPECT_PARSE_ERROR(Mock1, "invalid .BTF section size");
245
246 // Cut-off header.
247 Mock2.BTFSectionLen = offsetof(BTF::Header, StrOff);
248 EXPECT_PARSE_ERROR(Mock2, BTFEndOfData);
249}
250
251TEST(BTFParserTest, badBTFExtMagic) {
252 MockData1 Mock;
253 Mock.Ext.Header.Magic = 42;
254 EXPECT_PARSE_ERROR(Mock, "invalid .BTF.ext magic: 2a");
255}
256
257TEST(BTFParserTest, badBTFExtVersion) {
258 MockData1 Mock;
259 Mock.Ext.Header.Version = 42;
260 EXPECT_PARSE_ERROR(Mock, "unsupported .BTF.ext version: 42");
261}
262
263TEST(BTFParserTest, badBTFExtHdrLen) {
264 MockData1 Mock1, Mock2;
265
266 Mock1.Ext.Header.HdrLen = 5;
267 EXPECT_PARSE_ERROR(Mock1, "unexpected .BTF.ext header length: 5");
268
269 Mock2.Ext.Header.HdrLen = sizeof(Mock2.Ext);
270 EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);
271}
272
273TEST(BTFParserTest, badBTFExtSectionLen) {
274 MockData1 Mock1, Mock2, Mock3;
275
276 // Cut-off header before HdrLen.
277 Mock1.ExtSectionLen = offsetof(BTF::ExtHeader, HdrLen);
278 EXPECT_PARSE_ERROR(Mock1, BTFExtEndOfData);
279
280 // Cut-off header before LineInfoLen.
281 Mock2.ExtSectionLen = offsetof(BTF::ExtHeader, LineInfoLen);
282 EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);
283
284 // Cut-off line-info section somewhere in the middle.
285 Mock3.ExtSectionLen = offsetof(MockData1::E, Lines) + 4;
286 EXPECT_PARSE_ERROR(Mock3, BTFExtEndOfData);
287}
288
289TEST(BTFParserTest, badBTFExtLineInfoRecSize) {
290 MockData1 Mock1, Mock2;
291
292 Mock1.Ext.Lines.LineRecSize = 2;
293 EXPECT_PARSE_ERROR(Mock1, "unexpected .BTF.ext line info record length: 2");
294
295 Mock2.Ext.Lines.LineRecSize = sizeof(Mock2.Ext.Lines.Foo.Lines[0]) + 1;
296 EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);
297}
298
299TEST(BTFParserTest, badBTFExtLineSectionName) {
300 MockData1 Mock1;
301
302 Mock1.Ext.Lines.Foo.Sec.SecNameOff = offsetof(MockData1::B::S, Buz);
303 EXPECT_PARSE_ERROR(
304 Mock1, "can't find section 'buz' while parsing .BTF.ext line info");
305}
306
307TEST(BTFParserTest, missingSections) {
308 MockData1 Mock1, Mock2, Mock3;
309
310 Mock1.BTFSectionLen = -1;
311 EXPECT_PARSE_ERROR(Mock1, "can't find .BTF section");
312 EXPECT_FALSE(BTFParser::hasBTFSections(Mock1.makeObj()));
313
314 Mock2.ExtSectionLen = -1;
315 EXPECT_PARSE_ERROR(Mock2, "can't find .BTF.ext section");
316 EXPECT_FALSE(BTFParser::hasBTFSections(Mock2.makeObj()));
317
318 EXPECT_TRUE(BTFParser::hasBTFSections(Mock3.makeObj()));
319}
320
321// Check that BTFParser instance is reset when BTFParser::parse() is
322// called several times.
323TEST(BTFParserTest, parserReset) {
324 BTFParser BTF;
325 MockData1 Mock1, Mock2;
326
327 EXPECT_FALSE(BTF.parse(Mock1.makeObj()));
328 EXPECT_TRUE(BTF.findLineInfo({16, 1}));
329 EXPECT_TRUE(BTF.findLineInfo({0, 2}));
330
331 // Break the reference to "bar" section name, thus making
332 // information about "bar" line numbers unavailable.
333 Mock2.Ext.Lines.Bar.Sec.SecNameOff = 100500;
334
335 EXPECT_FALSE(BTF.parse(Mock2.makeObj()));
336 EXPECT_TRUE(BTF.findLineInfo({16, 1}));
337 // Make sure that "bar" no longer available (its index is 2).
338 EXPECT_FALSE(BTF.findLineInfo({0, 2}));
339}
340
341TEST(BTFParserTest, btfContext) {
342 MockData1 Mock;
343 BTFParser BTF;
344 std::unique_ptr<BTFContext> Ctx = BTFContext::create(Obj: Mock.makeObj());
345
346 DILineInfo I1 = Ctx->getLineInfoForAddress(Address: {.Address: 16, .SectionIndex: 1});
347 EXPECT_EQ(I1.Line, 7u);
348 EXPECT_EQ(I1.Column, 1u);
349 EXPECT_EQ(I1.FileName, "a.c");
350 EXPECT_EQ(I1.LineSource, "first line");
351
352 DILineInfo I2 = Ctx->getLineInfoForAddress(Address: {.Address: 24, .SectionIndex: 1});
353 EXPECT_EQ(I2.Line, 0u);
354 EXPECT_EQ(I2.Column, 0u);
355 EXPECT_EQ(I2.FileName, DILineInfo::BadString);
356 EXPECT_EQ(I2.LineSource, std::nullopt);
357}
358
359static uint32_t mkInfo(uint32_t Kind) { return Kind << 24; }
360
361template <typename T> static void append(std::string &S, const T &What) {
362 S.append(s: (const char *)&What, n: sizeof(What));
363}
364
365class MockData2 {
366 SmallString<0> ObjStorage;
367 std::unique_ptr<ObjectFile> Obj;
368 std::string Types;
369 std::string Strings;
370 std::string Relocs;
371 std::string Lines;
372 unsigned TotalTypes;
373 int LastRelocSecIdx;
374 unsigned NumRelocs;
375 int LastLineSecIdx;
376 unsigned NumLines;
377
378public:
379 MockData2() { reset(); }
380
381 unsigned totalTypes() const { return TotalTypes; }
382
383 uint32_t addString(StringRef S) {
384 uint32_t Off = Strings.size();
385 Strings.append(s: S.data(), n: S.size());
386 Strings.append(s: "\0", n: 1);
387 return Off;
388 };
389
390 uint32_t addType(const BTF::CommonType &Tp) {
391 append(S&: Types, What: Tp);
392 return ++TotalTypes;
393 }
394
395 template <typename T> void addTail(const T &Tp) { append(Types, Tp); }
396
397 void resetTypes() {
398 Types.resize(n: 0);
399 TotalTypes = 0;
400 }
401
402 void reset() {
403 ObjStorage.clear();
404 Types.resize(n: 0);
405 Strings.resize(n: 0);
406 Relocs.resize(n: 0);
407 Lines.resize(n: 0);
408 TotalTypes = 0;
409 LastRelocSecIdx = -1;
410 NumRelocs = 0;
411 LastLineSecIdx = -1;
412 NumLines = 0;
413 }
414
415 void finishRelocSec() {
416 if (LastRelocSecIdx == -1)
417 return;
418
419 BTF::SecFieldReloc *SecInfo =
420 (BTF::SecFieldReloc *)&Relocs[LastRelocSecIdx];
421 SecInfo->NumFieldReloc = NumRelocs;
422 LastRelocSecIdx = -1;
423 NumRelocs = 0;
424 }
425
426 void finishLineSec() {
427 if (LastLineSecIdx == -1)
428 return;
429
430 BTF::SecLineInfo *SecInfo = (BTF::SecLineInfo *)&Lines[LastLineSecIdx];
431 SecInfo->NumLineInfo = NumLines;
432 NumLines = 0;
433 LastLineSecIdx = -1;
434 }
435
436 void addRelocSec(const BTF::SecFieldReloc &R) {
437 finishRelocSec();
438 LastRelocSecIdx = Relocs.size();
439 append(S&: Relocs, What: R);
440 }
441
442 void addReloc(const BTF::BPFFieldReloc &R) {
443 append(S&: Relocs, What: R);
444 ++NumRelocs;
445 }
446
447 void addLinesSec(const BTF::SecLineInfo &R) {
448 finishLineSec();
449 LastLineSecIdx = Lines.size();
450 append(S&: Lines, What: R);
451 }
452
453 void addLine(const BTF::BPFLineInfo &R) {
454 append(S&: Lines, What: R);
455 ++NumLines;
456 }
457
458 ObjectFile &makeObj() {
459 finishRelocSec();
460 finishLineSec();
461
462 BTF::Header BTFHeader = {};
463 BTFHeader.Magic = BTF::MAGIC;
464 BTFHeader.Version = 1;
465 BTFHeader.HdrLen = sizeof(BTFHeader);
466 BTFHeader.StrOff = 0;
467 BTFHeader.StrLen = Strings.size();
468 BTFHeader.TypeOff = Strings.size();
469 BTFHeader.TypeLen = Types.size();
470
471 std::string BTFSec;
472 append(S&: BTFSec, What: BTFHeader);
473 BTFSec.append(str: Strings);
474 BTFSec.append(str: Types);
475
476 BTF::ExtHeader ExtHeader = {};
477 ExtHeader.Magic = BTF::MAGIC;
478 ExtHeader.Version = 1;
479 ExtHeader.HdrLen = sizeof(ExtHeader);
480 ExtHeader.FieldRelocOff = 0;
481 ExtHeader.FieldRelocLen = Relocs.size() + sizeof(uint32_t);
482 ExtHeader.LineInfoOff = ExtHeader.FieldRelocLen;
483 ExtHeader.LineInfoLen = Lines.size() + sizeof(uint32_t);
484
485 std::string ExtSec;
486 append(S&: ExtSec, What: ExtHeader);
487 append(S&: ExtSec, What: (uint32_t)sizeof(BTF::BPFFieldReloc));
488 ExtSec.append(str: Relocs);
489 append(S&: ExtSec, What: (uint32_t)sizeof(BTF::BPFLineInfo));
490 ExtSec.append(str: Lines);
491
492 std::string YamlBuffer;
493 raw_string_ostream Yaml(YamlBuffer);
494 Yaml << R"(
495!ELF
496FileHeader:
497 Class: ELFCLASS64)";
498 if (sys::IsBigEndianHost)
499 Yaml << "\n Data: ELFDATA2MSB";
500 else
501 Yaml << "\n Data: ELFDATA2LSB";
502 Yaml << R"(
503 Type: ET_REL
504 Machine: EM_BPF
505Sections:
506 - Name: foo
507 Type: SHT_PROGBITS
508 Size: 0x80
509 - Name: bar
510 Type: SHT_PROGBITS
511 Size: 0x80
512 - Name: .BTF
513 Type: SHT_PROGBITS
514 Content: )"
515 << makeBinRef(Ptr: BTFSec.data(), Size: BTFSec.size());
516 Yaml << R"(
517 - Name: .BTF.ext
518 Type: SHT_PROGBITS
519 Content: )"
520 << makeBinRef(Ptr: ExtSec.data(), Size: ExtSec.size());
521
522 Obj = yaml::yaml2ObjectFile(Storage&: ObjStorage, Yaml: YamlBuffer,
523 ErrHandler: [](const Twine &Err) { errs() << Err; });
524 return *Obj.get();
525 }
526};
527
528TEST(BTFParserTest, allTypeKinds) {
529 MockData2 D;
530 D.addType(Tp: {.NameOff: D.addString(S: "1"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
531 D.addTail(Tp: (uint32_t)0);
532 D.addType(Tp: {.NameOff: D.addString(S: "2"), .Info: mkInfo(Kind: BTF::BTF_KIND_PTR), {.Size: 1}});
533 D.addType(Tp: {.NameOff: D.addString(S: "3"), .Info: mkInfo(Kind: BTF::BTF_KIND_ARRAY), {.Size: 0}});
534 D.addTail(Tp: BTF::BTFArray({.ElemType: 1, .IndexType: 1, .Nelems: 2}));
535 D.addType(Tp: {.NameOff: D.addString(S: "4"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 2, {.Size: 8}});
536 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "a"), .Type: 1, .Offset: 0}));
537 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "b"), .Type: 1, .Offset: 0}));
538 D.addType(Tp: {.NameOff: D.addString(S: "5"), .Info: mkInfo(Kind: BTF::BTF_KIND_UNION) | 3, {.Size: 8}});
539 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "a"), .Type: 1, .Offset: 0}));
540 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "b"), .Type: 1, .Offset: 0}));
541 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "c"), .Type: 1, .Offset: 0}));
542 D.addType(Tp: {.NameOff: D.addString(S: "6"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM) | 2, {.Size: 4}});
543 D.addTail(Tp: BTF::BTFEnum({.NameOff: D.addString(S: "U"), .Val: 1}));
544 D.addTail(Tp: BTF::BTFEnum({.NameOff: D.addString(S: "V"), .Val: 2}));
545 D.addType(Tp: {.NameOff: D.addString(S: "7"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM64) | 1, {.Size: 4}});
546 D.addTail(Tp: BTF::BTFEnum64({.NameOff: D.addString(S: "W"), .Val_Lo32: 0, .Val_Hi32: 1}));
547 D.addType(
548 Tp: {.NameOff: D.addString(S: "8"), .Info: BTF::FWD_UNION_FLAG | mkInfo(Kind: BTF::BTF_KIND_FWD), {.Size: 0}});
549 D.addType(Tp: {.NameOff: D.addString(S: "9"), .Info: mkInfo(Kind: BTF::BTF_KIND_TYPEDEF), {.Size: 1}});
550 D.addType(Tp: {.NameOff: D.addString(S: "10"), .Info: mkInfo(Kind: BTF::BTF_KIND_VOLATILE), {.Size: 1}});
551 D.addType(Tp: {.NameOff: D.addString(S: "11"), .Info: mkInfo(Kind: BTF::BTF_KIND_CONST), {.Size: 1}});
552 D.addType(Tp: {.NameOff: D.addString(S: "12"), .Info: mkInfo(Kind: BTF::BTF_KIND_RESTRICT), {.Size: 1}});
553 D.addType(Tp: {.NameOff: D.addString(S: "13"), .Info: mkInfo(Kind: BTF::BTF_KIND_FUNC_PROTO) | 1, {.Size: 1}});
554 D.addTail(Tp: BTF::BTFParam({.NameOff: D.addString(S: "P"), .Type: 2}));
555 D.addType(Tp: {.NameOff: D.addString(S: "14"), .Info: mkInfo(Kind: BTF::BTF_KIND_FUNC), {.Size: 13}});
556 D.addType(Tp: {.NameOff: D.addString(S: "15"), .Info: mkInfo(Kind: BTF::BTF_KIND_VAR), {.Size: 2}});
557 D.addTail(Tp: (uint32_t)0);
558 D.addType(Tp: {.NameOff: D.addString(S: "16"), .Info: mkInfo(Kind: BTF::BTF_KIND_DATASEC) | 3, {.Size: 0}});
559 D.addTail(Tp: BTF::BTFDataSec({.Type: 1, .Offset: 0, .Size: 4}));
560 D.addTail(Tp: BTF::BTFDataSec({.Type: 1, .Offset: 4, .Size: 4}));
561 D.addTail(Tp: BTF::BTFDataSec({.Type: 1, .Offset: 8, .Size: 4}));
562 D.addType(Tp: {.NameOff: D.addString(S: "17"), .Info: mkInfo(Kind: BTF::BTF_KIND_FLOAT), {.Size: 4}});
563 D.addType(Tp: {.NameOff: D.addString(S: "18"), .Info: mkInfo(Kind: BTF::BTF_KIND_DECL_TAG), {.Size: 0}});
564 D.addTail(Tp: (uint32_t)-1);
565 D.addType(Tp: {.NameOff: D.addString(S: "19"), .Info: mkInfo(Kind: BTF::BTF_KIND_TYPE_TAG), {.Size: 0}});
566
567 BTFParser BTF;
568 Error Err = BTF.parse(Obj: D.makeObj());
569 EXPECT_FALSE(Err);
570
571 EXPECT_EQ(D.totalTypes() + 1 /* +1 for void */, BTF.typesCount());
572 for (unsigned Id = 1; Id < D.totalTypes() + 1; ++Id) {
573 const BTF::CommonType *Tp = BTF.findType(Id);
574 ASSERT_TRUE(Tp);
575 std::string IdBuf;
576 raw_string_ostream IdBufStream(IdBuf);
577 IdBufStream << Id;
578 EXPECT_EQ(BTF.findString(Tp->NameOff), IdBuf);
579 }
580}
581
582TEST(BTFParserTest, bigStruct) {
583 const uint32_t N = 1000u;
584 MockData2 D;
585 uint32_t FStr = D.addString(S: "f");
586 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
587 D.addTail(Tp: (uint32_t)0);
588 D.addType(Tp: {.NameOff: D.addString(S: "big"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | N, {.Size: 8}});
589 for (unsigned I = 0; I < N; ++I)
590 D.addTail(Tp: BTF::BTFMember({.NameOff: FStr, .Type: 1, .Offset: 0}));
591 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
592 D.addTail(Tp: (uint32_t)0);
593
594 BTFParser BTF;
595 ASSERT_SUCCEEDED(BTF.parse(D.makeObj()));
596 ASSERT_EQ(BTF.typesCount(), 4u /* +1 for void */);
597 const BTF::CommonType *Foo = BTF.findType(Id: 1);
598 const BTF::CommonType *Big = BTF.findType(Id: 2);
599 const BTF::CommonType *Bar = BTF.findType(Id: 3);
600 ASSERT_TRUE(Foo);
601 ASSERT_TRUE(Big);
602 ASSERT_TRUE(Bar);
603 EXPECT_EQ(BTF.findString(Foo->NameOff), "foo");
604 EXPECT_EQ(BTF.findString(Big->NameOff), "big");
605 EXPECT_EQ(BTF.findString(Bar->NameOff), "bar");
606 EXPECT_EQ(Big->getVlen(), N);
607}
608
609TEST(BTFParserTest, incompleteTypes) {
610 MockData2 D;
611 auto IncompleteType = [&](const BTF::CommonType &Tp) {
612 D.resetTypes();
613 D.addType(Tp);
614 EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section");
615 };
616
617 // All kinds that need tail.
618 IncompleteType({.NameOff: D.addString(S: "a"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
619 IncompleteType({.NameOff: D.addString(S: "b"), .Info: mkInfo(Kind: BTF::BTF_KIND_ARRAY), {.Size: 0}});
620 IncompleteType({.NameOff: D.addString(S: "c"), .Info: mkInfo(Kind: BTF::BTF_KIND_VAR), {.Size: 0}});
621 IncompleteType({.NameOff: D.addString(S: "d"), .Info: mkInfo(Kind: BTF::BTF_KIND_DECL_TAG), {.Size: 0}});
622
623 // All kinds with vlen.
624 IncompleteType({.NameOff: D.addString(S: "a"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 2, {.Size: 8}});
625 IncompleteType({.NameOff: D.addString(S: "b"), .Info: mkInfo(Kind: BTF::BTF_KIND_UNION) | 3, {.Size: 8}});
626 IncompleteType({.NameOff: D.addString(S: "c"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM) | 2, {.Size: 4}});
627 IncompleteType({.NameOff: D.addString(S: "d"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM64) | 1, {.Size: 4}});
628 IncompleteType({.NameOff: D.addString(S: "e"), .Info: mkInfo(Kind: BTF::BTF_KIND_FUNC_PROTO) | 1, {.Size: 1}});
629 IncompleteType({.NameOff: D.addString(S: "f"), .Info: mkInfo(Kind: BTF::BTF_KIND_DATASEC) | 3, {.Size: 0}});
630
631 // An unexpected tail.
632 D.resetTypes();
633 D.addTail(Tp: (uint32_t)0);
634 EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section");
635}
636
637// Use macro to preserve line number in error message.
638#define SYMBOLIZE(SecAddr, Expected) \
639 do { \
640 const BTF::BPFFieldReloc *R = BTF.findFieldReloc((SecAddr)); \
641 ASSERT_TRUE(R); \
642 SmallString<64> Symbolized; \
643 BTF.symbolize(R, Symbolized); \
644 EXPECT_EQ(Symbolized, (Expected)); \
645 } while (false)
646
647// Shorter name for initializers below.
648using SA = SectionedAddress;
649
650TEST(BTFParserTest, typeRelocs) {
651 MockData2 D;
652 uint32_t Zero = D.addString(S: "0");
653 // id 1: struct foo {}
654 // id 2: union bar;
655 // id 3: struct buz;
656 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
657 D.addType(Tp: {.NameOff: D.addString(S: "bar"),
658 .Info: mkInfo(Kind: BTF::BTF_KIND_FWD) | BTF::FWD_UNION_FLAG,
659 {.Size: 0}});
660 D.addType(Tp: {.NameOff: D.addString(S: "buz"), .Info: mkInfo(Kind: BTF::BTF_KIND_FWD), {.Size: 0}});
661 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 7});
662 // List of all possible correct type relocations for type id #1.
663 D.addReloc(R: {.InsnOffset: 0, .TypeID: 1, .OffsetNameOff: Zero, .RelocKind: BTF::BTF_TYPE_ID_LOCAL});
664 D.addReloc(R: {.InsnOffset: 8, .TypeID: 1, .OffsetNameOff: Zero, .RelocKind: BTF::BTF_TYPE_ID_REMOTE});
665 D.addReloc(R: {.InsnOffset: 16, .TypeID: 1, .OffsetNameOff: Zero, .RelocKind: BTF::TYPE_EXISTENCE});
666 D.addReloc(R: {.InsnOffset: 24, .TypeID: 1, .OffsetNameOff: Zero, .RelocKind: BTF::TYPE_MATCH});
667 D.addReloc(R: {.InsnOffset: 32, .TypeID: 1, .OffsetNameOff: Zero, .RelocKind: BTF::TYPE_SIZE});
668 // Forward declarations.
669 D.addReloc(R: {.InsnOffset: 40, .TypeID: 2, .OffsetNameOff: Zero, .RelocKind: BTF::TYPE_SIZE});
670 D.addReloc(R: {.InsnOffset: 48, .TypeID: 3, .OffsetNameOff: Zero, .RelocKind: BTF::TYPE_SIZE});
671 // Incorrect type relocation: bad type id.
672 D.addReloc(R: {.InsnOffset: 56, .TypeID: 42, .OffsetNameOff: Zero, .RelocKind: BTF::TYPE_SIZE});
673 // Incorrect type relocation: spec should be '0'.
674 D.addReloc(R: {.InsnOffset: 64, .TypeID: 1, .OffsetNameOff: D.addString(S: "10"), .RelocKind: BTF::TYPE_SIZE});
675
676 BTFParser BTF;
677 Error E = BTF.parse(Obj: D.makeObj());
678 EXPECT_FALSE(E);
679
680 SYMBOLIZE(SA({0, 1}), "<local_type_id> [1] struct foo");
681 SYMBOLIZE(SA({8, 1}), "<target_type_id> [1] struct foo");
682 SYMBOLIZE(SA({16, 1}), "<type_exists> [1] struct foo");
683 SYMBOLIZE(SA({24, 1}), "<type_matches> [1] struct foo");
684 SYMBOLIZE(SA({32, 1}), "<type_size> [1] struct foo");
685 SYMBOLIZE(SA({40, 1}), "<type_size> [2] fwd union bar");
686 SYMBOLIZE(SA({48, 1}), "<type_size> [3] fwd struct buz");
687 SYMBOLIZE(SA({56, 1}), "<type_size> [42] '0' <unknown type id: 42>");
688 SYMBOLIZE(SA({64, 1}),
689 "<type_size> [1] '10' "
690 "<unexpected type-based relocation spec: should be '0'>");
691}
692
693TEST(BTFParserTest, enumRelocs) {
694 MockData2 D;
695 // id 1: enum { U, V }
696 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM) | 2, {.Size: 4}});
697 D.addTail(Tp: BTF::BTFEnum({.NameOff: D.addString(S: "U"), .Val: 1}));
698 D.addTail(Tp: BTF::BTFEnum({.NameOff: D.addString(S: "V"), .Val: 2}));
699 // id 2: int
700 D.addType(Tp: {.NameOff: D.addString(S: "int"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
701 D.addTail(Tp: (uint32_t)0);
702 // id 3: enum: uint64_t { A, B }
703 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM64) | 2, {.Size: 8}});
704 D.addTail(Tp: BTF::BTFEnum64({.NameOff: D.addString(S: "A"), .Val_Lo32: 1, .Val_Hi32: 0}));
705 D.addTail(Tp: BTF::BTFEnum64({.NameOff: D.addString(S: "B"), .Val_Lo32: 2, .Val_Hi32: 0}));
706
707 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 5});
708 // An ok relocation accessing value #1: U.
709 D.addReloc(R: {.InsnOffset: 0, .TypeID: 1, .OffsetNameOff: D.addString(S: "0"), .RelocKind: BTF::ENUM_VALUE_EXISTENCE});
710 // An ok relocation accessing value #2: V.
711 D.addReloc(R: {.InsnOffset: 8, .TypeID: 1, .OffsetNameOff: D.addString(S: "1"), .RelocKind: BTF::ENUM_VALUE});
712 // Incorrect relocation: too many elements in string "1:0".
713 D.addReloc(R: {.InsnOffset: 16, .TypeID: 1, .OffsetNameOff: D.addString(S: "1:0"), .RelocKind: BTF::ENUM_VALUE});
714 // Incorrect relocation: type id "2" is not an enum.
715 D.addReloc(R: {.InsnOffset: 24, .TypeID: 2, .OffsetNameOff: D.addString(S: "1"), .RelocKind: BTF::ENUM_VALUE});
716 // Incorrect relocation: value #42 does not exist for enum "foo".
717 D.addReloc(R: {.InsnOffset: 32, .TypeID: 1, .OffsetNameOff: D.addString(S: "42"), .RelocKind: BTF::ENUM_VALUE});
718 // An ok relocation accessing value #1: A.
719 D.addReloc(R: {.InsnOffset: 40, .TypeID: 3, .OffsetNameOff: D.addString(S: "0"), .RelocKind: BTF::ENUM_VALUE_EXISTENCE});
720 // An ok relocation accessing value #2: B.
721 D.addReloc(R: {.InsnOffset: 48, .TypeID: 3, .OffsetNameOff: D.addString(S: "1"), .RelocKind: BTF::ENUM_VALUE});
722
723 BTFParser BTF;
724 Error E = BTF.parse(Obj: D.makeObj());
725 EXPECT_FALSE(E);
726
727 SYMBOLIZE(SA({0, 1}), "<enumval_exists> [1] enum foo::U = 1");
728 SYMBOLIZE(SA({8, 1}), "<enumval_value> [1] enum foo::V = 2");
729 SYMBOLIZE(
730 SA({16, 1}),
731 "<enumval_value> [1] '1:0' <unexpected enumval relocation spec size>");
732 SYMBOLIZE(
733 SA({24, 1}),
734 "<enumval_value> [2] '1' <unexpected type kind for enum relocation: 1>");
735 SYMBOLIZE(SA({32, 1}), "<enumval_value> [1] '42' <bad value index: 42>");
736 SYMBOLIZE(SA({40, 1}), "<enumval_exists> [3] enum bar::A = 1");
737 SYMBOLIZE(SA({48, 1}), "<enumval_value> [3] enum bar::B = 2");
738}
739
740TEST(BTFParserTest, enumRelocsMods) {
741 MockData2 D;
742 // id 1: enum { U, V }
743 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_ENUM) | 2, {.Size: 4}});
744 D.addTail(Tp: BTF::BTFEnum({.NameOff: D.addString(S: "U"), .Val: 1}));
745 D.addTail(Tp: BTF::BTFEnum({.NameOff: D.addString(S: "V"), .Val: 2}));
746 // id 2: typedef enum foo a;
747 D.addType(Tp: {.NameOff: D.addString(S: "a"), .Info: mkInfo(Kind: BTF::BTF_KIND_TYPEDEF), {.Size: 1}});
748 // id 3: const enum foo;
749 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_CONST), {.Size: 1}});
750
751 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
752 D.addReloc(R: {.InsnOffset: 0, .TypeID: 2, .OffsetNameOff: D.addString(S: "0"), .RelocKind: BTF::ENUM_VALUE});
753 D.addReloc(R: {.InsnOffset: 8, .TypeID: 3, .OffsetNameOff: D.addString(S: "1"), .RelocKind: BTF::ENUM_VALUE});
754
755 BTFParser BTF;
756 Error E = BTF.parse(Obj: D.makeObj());
757 EXPECT_FALSE(E);
758
759 SYMBOLIZE(SA({0, 1}), "<enumval_value> [2] typedef a::U = 1");
760 SYMBOLIZE(SA({8, 1}), "<enumval_value> [3] const enum foo::V = 2");
761}
762
763TEST(BTFParserTest, fieldRelocs) {
764 MockData2 D;
765 // id 1: int
766 D.addType(Tp: {.NameOff: D.addString(S: "int"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
767 D.addTail(Tp: (uint32_t)0);
768 // id 2: struct foo { int a; int b; }
769 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 2, {.Size: 8}});
770 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "a"), .Type: 1, .Offset: 0}));
771 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "b"), .Type: 1, .Offset: 0}));
772 // id 3: array of struct foo.
773 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_ARRAY), {.Size: 0}});
774 D.addTail(Tp: BTF::BTFArray({.ElemType: 2, .IndexType: 1, .Nelems: 2}));
775 // id 4: struct bar { struct foo u[2]; int v; }
776 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 2, {.Size: 8}});
777 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "u"), .Type: 3, .Offset: 0}));
778 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "v"), .Type: 1, .Offset: 0}));
779 // id 5: array with bad element type id.
780 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_ARRAY), {.Size: 0}});
781 D.addTail(Tp: BTF::BTFArray({.ElemType: 42, .IndexType: 1, .Nelems: 2}));
782 // id 6: struct buz { <bad type> u[2]; <bad type> v; }
783 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 2, {.Size: 8}});
784 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "u"), .Type: 5, .Offset: 0}));
785 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "v"), .Type: 42, .Offset: 0}));
786
787 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0 /* patched automatically */});
788 // All field relocations kinds for struct bar::v.
789 D.addReloc(R: {.InsnOffset: 0, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
790 D.addReloc(R: {.InsnOffset: 8, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1"), .RelocKind: BTF::FIELD_BYTE_SIZE});
791 D.addReloc(R: {.InsnOffset: 16, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1"), .RelocKind: BTF::FIELD_EXISTENCE});
792 D.addReloc(R: {.InsnOffset: 24, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1"), .RelocKind: BTF::FIELD_SIGNEDNESS});
793 D.addReloc(R: {.InsnOffset: 32, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1"), .RelocKind: BTF::FIELD_LSHIFT_U64});
794 D.addReloc(R: {.InsnOffset: 40, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1"), .RelocKind: BTF::FIELD_RSHIFT_U64});
795 // Non-zero first idx.
796 D.addReloc(R: {.InsnOffset: 48, .TypeID: 4, .OffsetNameOff: D.addString(S: "7:1"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
797 // Access through array and struct: struct bar::u[1].a.
798 D.addReloc(R: {.InsnOffset: 56, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:0:1:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
799 // Access through array and struct: struct bar::u[1].b.
800 D.addReloc(R: {.InsnOffset: 64, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:0:1:1"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
801 // Incorrect relocation: empty access string.
802 D.addReloc(R: {.InsnOffset: 72, .TypeID: 4, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::FIELD_BYTE_OFFSET});
803 // Incorrect relocation: member index out of range (only two members in bar).
804 D.addReloc(R: {.InsnOffset: 80, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:2"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
805 // Incorrect relocation: unknown element type id (buz::u[0] access).
806 D.addReloc(R: {.InsnOffset: 88, .TypeID: 6, .OffsetNameOff: D.addString(S: "0:0:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
807 // Incorrect relocation: unknown member type id (buz::v access).
808 D.addReloc(R: {.InsnOffset: 96, .TypeID: 6, .OffsetNameOff: D.addString(S: "0:1:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
809 // Incorrect relocation: non structural type in the middle of access string
810 // struct bar::v.<something>.
811 D.addReloc(R: {.InsnOffset: 104, .TypeID: 4, .OffsetNameOff: D.addString(S: "0:1:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
812
813 BTFParser BTF;
814 Error E = BTF.parse(Obj: D.makeObj());
815 EXPECT_FALSE(E);
816
817 SYMBOLIZE(SA({0, 1}), "<byte_off> [4] struct bar::v (0:1)");
818 SYMBOLIZE(SA({8, 1}), "<byte_sz> [4] struct bar::v (0:1)");
819 SYMBOLIZE(SA({16, 1}), "<field_exists> [4] struct bar::v (0:1)");
820 SYMBOLIZE(SA({24, 1}), "<signed> [4] struct bar::v (0:1)");
821 SYMBOLIZE(SA({32, 1}), "<lshift_u64> [4] struct bar::v (0:1)");
822 SYMBOLIZE(SA({40, 1}), "<rshift_u64> [4] struct bar::v (0:1)");
823 SYMBOLIZE(SA({48, 1}), "<byte_off> [4] struct bar::[7].v (7:1)");
824 SYMBOLIZE(SA({56, 1}), "<byte_off> [4] struct bar::u[1].a (0:0:1:0)");
825 SYMBOLIZE(SA({64, 1}), "<byte_off> [4] struct bar::u[1].b (0:0:1:1)");
826 SYMBOLIZE(SA({72, 1}), "<byte_off> [4] '' <field spec too short>");
827 SYMBOLIZE(SA({80, 1}),
828 "<byte_off> [4] '0:2' "
829 "<member index 2 for spec sub-string 1 is out of range>");
830 SYMBOLIZE(SA({88, 1}), "<byte_off> [6] '0:0:0' "
831 "<unknown element type id 42 for spec sub-string 2>");
832 SYMBOLIZE(SA({96, 1}), "<byte_off> [6] '0:1:0' "
833 "<unknown member type id 42 for spec sub-string 1>");
834 SYMBOLIZE(SA({104, 1}), "<byte_off> [4] '0:1:0' "
835 "<unexpected type kind 1 for spec sub-string 2>");
836}
837
838TEST(BTFParserTest, fieldRelocsMods) {
839 MockData2 D;
840 // struct foo {
841 // int u;
842 // }
843 // typedef struct foo bar;
844 // struct buz {
845 // const bar v;
846 // }
847 // typedef buz quux;
848 // const volatile restrict quux <some-var>;
849 uint32_t Int =
850 D.addType(Tp: {.NameOff: D.addString(S: "int"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
851 D.addTail(Tp: (uint32_t)0);
852 uint32_t Foo =
853 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 1, {.Size: 4}});
854 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "u"), .Type: Int, .Offset: 0}));
855 uint32_t Bar =
856 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_TYPEDEF), {.Size: Foo}});
857 uint32_t CBar =
858 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_CONST), {.Size: Bar}});
859 uint32_t Buz =
860 D.addType(Tp: {.NameOff: D.addString(S: "buz"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 1, {.Size: 4}});
861 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: "v"), .Type: CBar, .Offset: 0}));
862 uint32_t Quux =
863 D.addType(Tp: {.NameOff: D.addString(S: "quux"), .Info: mkInfo(Kind: BTF::BTF_KIND_TYPEDEF), {.Size: Buz}});
864 uint32_t RQuux =
865 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_RESTRICT), {.Size: Quux}});
866 uint32_t VRQuux =
867 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_VOLATILE), {.Size: RQuux}});
868 uint32_t CVRQuux =
869 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_CONST), {.Size: VRQuux}});
870 uint32_t CUnknown =
871 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_CONST), {.Size: 77}});
872 uint32_t CVUnknown =
873 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_VOLATILE), {.Size: CUnknown}});
874
875 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
876 D.addReloc(R: {.InsnOffset: 0, .TypeID: Bar, .OffsetNameOff: D.addString(S: "0:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
877 D.addReloc(R: {.InsnOffset: 8, .TypeID: CVRQuux, .OffsetNameOff: D.addString(S: "0:0:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
878 D.addReloc(R: {.InsnOffset: 16, .TypeID: CVUnknown, .OffsetNameOff: D.addString(S: "0:1:2"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
879
880 BTFParser BTF;
881 Error E = BTF.parse(Obj: D.makeObj());
882 EXPECT_FALSE(E);
883
884 // Should show modifiers / name of typedef.
885 SYMBOLIZE(SA({0, 1}), "<byte_off> [3] typedef bar::u (0:0)");
886 SYMBOLIZE(SA({8, 1}),
887 "<byte_off> [9] const volatile restrict typedef quux::v.u (0:0:0)");
888 SYMBOLIZE(SA({16, 1}),
889 "<byte_off> [11] '0:1:2' <unknown type id: 77 in modifiers chain>");
890}
891
892TEST(BTFParserTest, relocTypeTagAndVoid) {
893 MockData2 D;
894 // __attribute__((type_tag("tag"))) void
895 uint32_t Tag =
896 D.addType(Tp: {.NameOff: D.addString(S: "tag"), .Info: mkInfo(Kind: BTF::BTF_KIND_TYPE_TAG), {.Size: 0}});
897
898 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
899 D.addReloc(R: {.InsnOffset: 0, .TypeID: Tag, .OffsetNameOff: D.addString(S: "0"), .RelocKind: BTF::TYPE_EXISTENCE});
900 D.addReloc(R: {.InsnOffset: 8, .TypeID: 0 /* void */, .OffsetNameOff: D.addString(S: "0"), .RelocKind: BTF::TYPE_EXISTENCE});
901
902 BTFParser BTF;
903 Error E = BTF.parse(Obj: D.makeObj());
904 EXPECT_FALSE(E);
905
906 SYMBOLIZE(SA({0, 1}), "<type_exists> [1] type_tag(\"tag\") void");
907 SYMBOLIZE(SA({8, 1}), "<type_exists> [0] void");
908}
909
910TEST(BTFParserTest, longRelocModifiersCycle) {
911 MockData2 D;
912
913 D.addType(
914 Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_CONST), {.Size: 1 /* ourselves */}});
915 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
916 D.addReloc(R: {.InsnOffset: 0, .TypeID: 1, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
917
918 BTFParser BTF;
919 Error E = BTF.parse(Obj: D.makeObj());
920 EXPECT_FALSE(E);
921
922 SYMBOLIZE(SA({0, 1}), "<type_exists> [1] '' <modifiers chain is too long>");
923}
924
925TEST(BTFParserTest, relocAnonFieldsAndTypes) {
926 MockData2 D;
927
928 // struct {
929 // int :32;
930 // } v;
931 uint32_t Int =
932 D.addType(Tp: {.NameOff: D.addString(S: "int"), .Info: mkInfo(Kind: BTF::BTF_KIND_INT), {.Size: 4}});
933 D.addTail(Tp: (uint32_t)0);
934 uint32_t Anon =
935 D.addType(Tp: {.NameOff: D.addString(S: ""), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT) | 1, {.Size: 4}});
936 D.addTail(Tp: BTF::BTFMember({.NameOff: D.addString(S: ""), .Type: Int, .Offset: 0}));
937
938 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
939 D.addReloc(R: {.InsnOffset: 0, .TypeID: Anon, .OffsetNameOff: D.addString(S: "0"), .RelocKind: BTF::TYPE_EXISTENCE});
940 D.addReloc(R: {.InsnOffset: 8, .TypeID: Anon, .OffsetNameOff: D.addString(S: "0:0"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
941
942 BTFParser BTF;
943 Error E = BTF.parse(Obj: D.makeObj());
944 EXPECT_FALSE(E);
945
946 SYMBOLIZE(SA({0, 1}), "<type_exists> [2] struct <anon 2>");
947 SYMBOLIZE(SA({8, 1}), "<byte_off> [2] struct <anon 2>::<anon 0> (0:0)");
948}
949
950TEST(BTFParserTest, miscBadRelos) {
951 MockData2 D;
952
953 uint32_t S = D.addType(Tp: {.NameOff: D.addString(S: "S"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
954
955 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
956 D.addReloc(R: {.InsnOffset: 0, .TypeID: 0, .OffsetNameOff: D.addString(S: ""), .RelocKind: 777});
957 D.addReloc(R: {.InsnOffset: 8, .TypeID: S, .OffsetNameOff: D.addString(S: "abc"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
958 D.addReloc(R: {.InsnOffset: 16, .TypeID: S, .OffsetNameOff: D.addString(S: "0#"), .RelocKind: BTF::FIELD_BYTE_OFFSET});
959
960 BTFParser BTF;
961 Error E = BTF.parse(Obj: D.makeObj());
962 EXPECT_FALSE(E);
963
964 SYMBOLIZE(SA({0, 1}),
965 "<reloc kind #777> [0] '' <unknown relocation kind: 777>");
966 SYMBOLIZE(SA({8, 1}), "<byte_off> [1] 'abc' <spec string is not a number>");
967 SYMBOLIZE(SA({16, 1}),
968 "<byte_off> [1] '0#' <unexpected spec string delimiter: '#'>");
969}
970
971TEST(BTFParserTest, relocsMultipleSections) {
972 MockData2 D;
973
974 uint32_t S = D.addType(Tp: {.NameOff: D.addString(S: "S"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
975 uint32_t T = D.addType(Tp: {.NameOff: D.addString(S: "T"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
976
977 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
978 D.addReloc(R: {.InsnOffset: 0, .TypeID: S, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
979 D.addReloc(R: {.InsnOffset: 8, .TypeID: S, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
980
981 D.addRelocSec(R: {.SecNameOff: D.addString(S: "bar"), .NumFieldReloc: 0});
982 D.addReloc(R: {.InsnOffset: 8, .TypeID: T, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
983 D.addReloc(R: {.InsnOffset: 16, .TypeID: T, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
984
985 BTFParser BTF;
986 Error E = BTF.parse(Obj: D.makeObj());
987 EXPECT_FALSE(E);
988
989 EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
990 EXPECT_TRUE(BTF.findFieldReloc({8, 1}));
991 EXPECT_FALSE(BTF.findFieldReloc({16, 1}));
992
993 EXPECT_FALSE(BTF.findFieldReloc({0, 2}));
994 EXPECT_TRUE(BTF.findFieldReloc({8, 2}));
995 EXPECT_TRUE(BTF.findFieldReloc({16, 2}));
996
997 EXPECT_FALSE(BTF.findFieldReloc({0, 3}));
998 EXPECT_FALSE(BTF.findFieldReloc({8, 3}));
999 EXPECT_FALSE(BTF.findFieldReloc({16, 3}));
1000
1001 auto AssertReloType = [&](const SectionedAddress &A, const char *Name) {
1002 const BTF::BPFFieldReloc *Relo = BTF.findFieldReloc(Address: A);
1003 ASSERT_TRUE(Relo);
1004 const BTF::CommonType *Type = BTF.findType(Id: Relo->TypeID);
1005 ASSERT_TRUE(Type);
1006 EXPECT_EQ(BTF.findString(Type->NameOff), Name);
1007 };
1008
1009 AssertReloType({.Address: 8, .SectionIndex: 1}, "S");
1010 AssertReloType({.Address: 8, .SectionIndex: 2}, "T");
1011}
1012
1013TEST(BTFParserTest, parserResetReloAndTypes) {
1014 BTFParser BTF;
1015 MockData2 D;
1016
1017 // First time: two types, two relocations.
1018 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
1019 D.addType(Tp: {.NameOff: D.addString(S: "bar"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
1020 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
1021 D.addReloc(R: {.InsnOffset: 0, .TypeID: 1, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
1022 D.addReloc(R: {.InsnOffset: 8, .TypeID: 2, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
1023
1024 Error E1 = BTF.parse(Obj: D.makeObj());
1025 EXPECT_FALSE(E1);
1026
1027 ASSERT_TRUE(BTF.findType(1));
1028 EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "foo");
1029 EXPECT_TRUE(BTF.findType(2));
1030 EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
1031 EXPECT_TRUE(BTF.findFieldReloc({8, 1}));
1032
1033 // Second time: one type, one relocation.
1034 D.reset();
1035 D.addType(Tp: {.NameOff: D.addString(S: "buz"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
1036 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
1037 D.addReloc(R: {.InsnOffset: 0, .TypeID: 1, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
1038
1039 Error E2 = BTF.parse(Obj: D.makeObj());
1040 EXPECT_FALSE(E2);
1041
1042 ASSERT_TRUE(BTF.findType(1));
1043 EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "buz");
1044 EXPECT_FALSE(BTF.findType(2));
1045 EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
1046 EXPECT_FALSE(BTF.findFieldReloc({8, 1}));
1047}
1048
1049TEST(BTFParserTest, selectiveLoad) {
1050 BTFParser BTF1, BTF2, BTF3;
1051 MockData2 D;
1052
1053 D.addType(Tp: {.NameOff: D.addString(S: "foo"), .Info: mkInfo(Kind: BTF::BTF_KIND_STRUCT), {.Size: 0}});
1054 D.addRelocSec(R: {.SecNameOff: D.addString(S: "foo"), .NumFieldReloc: 0});
1055 D.addReloc(R: {.InsnOffset: 0, .TypeID: 1, .OffsetNameOff: D.addString(S: ""), .RelocKind: BTF::TYPE_EXISTENCE});
1056 D.addLinesSec(R: {.SecNameOff: D.addString(S: "foo"), .NumLineInfo: 0});
1057 D.addLine(R: {.InsnOffset: 0, .FileNameOff: D.addString(S: "file.c"), .LineOff: D.addString(S: "some line"), LC(2, 3)});
1058
1059 BTFParser::ParseOptions Opts;
1060
1061 ObjectFile &Obj1 = D.makeObj();
1062 Opts = {};
1063 Opts.LoadLines = true;
1064 ASSERT_SUCCEEDED(BTF1.parse(Obj1, Opts));
1065
1066 Opts = {};
1067 Opts.LoadTypes = true;
1068 ASSERT_SUCCEEDED(BTF2.parse(Obj1, Opts));
1069
1070 Opts = {};
1071 Opts.LoadRelocs = true;
1072 ASSERT_SUCCEEDED(BTF3.parse(Obj1, Opts));
1073
1074 EXPECT_TRUE(BTF1.findLineInfo({0, 1}));
1075 EXPECT_FALSE(BTF2.findLineInfo({0, 1}));
1076 EXPECT_FALSE(BTF3.findLineInfo({0, 1}));
1077
1078 EXPECT_FALSE(BTF1.findType(1));
1079 EXPECT_TRUE(BTF2.findType(1));
1080 EXPECT_FALSE(BTF3.findType(1));
1081
1082 EXPECT_FALSE(BTF1.findFieldReloc({0, 1}));
1083 EXPECT_FALSE(BTF2.findFieldReloc({0, 1}));
1084 EXPECT_TRUE(BTF3.findFieldReloc({0, 1}));
1085}
1086
1087} // namespace
1088

source code of llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp