| //===-- SourcePrinter.cpp - source interleaving utilities ----------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/BTF/BTFContext.h" |
| #include "llvm/ObjectYAML/YAML.h" |
| #include "llvm/ObjectYAML/yaml2obj.h" |
| #include "llvm/Support/SwapByteOrder.h" |
| #include "llvm/Testing/Support/Error.h" |
| |
| using namespace llvm; |
| using namespace llvm::object; |
| |
| #define LC(Line, Col) ((Line << 10u) | Col) |
| #define ASSERT_SUCCEEDED(E) ASSERT_THAT_ERROR((E), Succeeded()) |
| |
| const char BTFEndOfData[] = |
| "error while reading .BTF section: unexpected end of data"; |
| const char BTFExtEndOfData[] = |
| "error while reading .BTF.ext section: unexpected end of data"; |
| |
| static raw_ostream &operator<<(raw_ostream &OS, const yaml::BinaryRef &Ref) { |
| Ref.writeAsHex(OS); |
| return OS; |
| } |
| |
| template <typename T> |
| static yaml::BinaryRef makeBinRef(const T *Ptr, size_t Size = sizeof(T)) { |
| return yaml::BinaryRef(ArrayRef<uint8_t>((const uint8_t *)Ptr, Size)); |
| } |
| |
| namespace { |
| // This is a mockup for an ELF file containing .BTF and .BTF.ext sections. |
| // Binary content of these sections corresponds to the value of |
| // MockData1::BTF and MockData1::Ext fields. |
| // |
| // The yaml::yaml2ObjectFile() is used to generate actual ELF, |
| // see MockData1::makeObj(). |
| // |
| // The `BTF` and `Ext` fields are initialized with correct values |
| // valid for a small example with a few sections, fields could be |
| // modified before a call to `makeObj()` to test parser with invalid |
| // input, etc. |
| |
| struct MockData1 { |
| // Use "pragma pack" to model .BTF & .BTF.ext sections content using |
| // 'struct' objects. This pragma is supported by CLANG, GCC & MSVC, |
| // which matters for LLVM CI. |
| #pragma pack(push, 1) |
| struct B { |
| BTF::Header Header = {}; |
| // No types. |
| struct S { |
| char Foo[4] = "foo"; |
| char Bar[4] = "bar"; |
| char Buz[4] = "buz"; |
| char Line1[11] = "first line"; |
| char Line2[12] = "second line"; |
| char File1[4] = "a.c"; |
| char File2[4] = "b.c"; |
| } Strings; |
| |
| B() { |
| Header.Magic = BTF::MAGIC; |
| Header.Version = 1; |
| Header.HdrLen = sizeof(Header); |
| Header.StrOff = offsetof(B, Strings) - sizeof(Header); |
| Header.StrLen = sizeof(Strings); |
| } |
| } BTF; |
| |
| struct E { |
| BTF::ExtHeader Header = {}; |
| // No func info. |
| struct { |
| uint32_t LineRecSize = sizeof(BTF::BPFLineInfo); |
| struct { |
| BTF::SecLineInfo Sec = {offsetof(B::S, Foo), 2}; |
| BTF::BPFLineInfo Lines[2] = { |
| {16, offsetof(B::S, File1), offsetof(B::S, Line1), LC(7, 1)}, |
| {32, offsetof(B::S, File1), offsetof(B::S, Line2), LC(14, 5)}, |
| }; |
| } Foo; |
| struct { |
| BTF::SecLineInfo Sec = {offsetof(B::S, Bar), 1}; |
| BTF::BPFLineInfo Lines[1] = { |
| {0, offsetof(B::S, File2), offsetof(B::S, Line1), LC(42, 4)}, |
| }; |
| } Bar; |
| } Lines; |
| |
| E() { |
| Header.Magic = BTF::MAGIC; |
| Header.Version = 1; |
| Header.HdrLen = sizeof(Header); |
| Header.LineInfoOff = offsetof(E, Lines) - sizeof(Header); |
| Header.LineInfoLen = sizeof(Lines); |
| } |
| } Ext; |
| #pragma pack(pop) |
| |
| int BTFSectionLen = sizeof(BTF); |
| int ExtSectionLen = sizeof(Ext); |
| |
| SmallString<0> Storage; |
| std::unique_ptr<ObjectFile> Obj; |
| |
| ObjectFile &makeObj() { |
| std::string Buffer; |
| raw_string_ostream Yaml(Buffer); |
| Yaml << R"( |
| !ELF |
| FileHeader: |
| Class: ELFCLASS64)"; |
| if (sys::IsBigEndianHost) |
| Yaml << "\n Data: ELFDATA2MSB"; |
| else |
| Yaml << "\n Data: ELFDATA2LSB"; |
| Yaml << R"( |
| Type: ET_REL |
| Machine: EM_BPF |
| Sections: |
| - Name: foo |
| Type: SHT_PROGBITS |
| Size: 0x0 |
| - Name: bar |
| Type: SHT_PROGBITS |
| Size: 0x0)"; |
| |
| if (BTFSectionLen >= 0) |
| Yaml << R"( |
| - Name: .BTF |
| Type: SHT_PROGBITS |
| Content: )" |
| << makeBinRef(&BTF, BTFSectionLen); |
| |
| if (ExtSectionLen >= 0) |
| Yaml << R"( |
| - Name: .BTF.ext |
| Type: SHT_PROGBITS |
| Content: )" |
| << makeBinRef(&Ext, ExtSectionLen); |
| |
| Obj = yaml::yaml2ObjectFile(Storage, Buffer, |
| [](const Twine &Err) { errs() << Err; }); |
| return *Obj.get(); |
| } |
| }; |
| |
| TEST(BTFParserTest, simpleCorrectInput) { |
| BTFParser BTF; |
| MockData1 Mock; |
| Error Err = BTF.parse(Mock.makeObj()); |
| EXPECT_FALSE(Err); |
| |
| EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Foo)), "foo"); |
| EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Bar)), "bar"); |
| EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Line1)), "first line"); |
| EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Line2)), "second line"); |
| EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File1)), "a.c"); |
| EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File2)), "b.c"); |
| |
| // Invalid offset. |
| EXPECT_EQ(BTF.findString(sizeof(MockData1::B::S)), StringRef()); |
| |
| const BTF::BPFLineInfo *I1 = BTF.findLineInfo({16, 1}); |
| ASSERT_TRUE(I1); |
| EXPECT_EQ(I1->getLine(), 7u); |
| EXPECT_EQ(I1->getCol(), 1u); |
| EXPECT_EQ(BTF.findString(I1->FileNameOff), "a.c"); |
| EXPECT_EQ(BTF.findString(I1->LineOff), "first line"); |
| |
| const BTF::BPFLineInfo *I2 = BTF.findLineInfo({32, 1}); |
| ASSERT_TRUE(I2); |
| EXPECT_EQ(I2->getLine(), 14u); |
| EXPECT_EQ(I2->getCol(), 5u); |
| EXPECT_EQ(BTF.findString(I2->FileNameOff), "a.c"); |
| EXPECT_EQ(BTF.findString(I2->LineOff), "second line"); |
| |
| const BTF::BPFLineInfo *I3 = BTF.findLineInfo({0, 2}); |
| ASSERT_TRUE(I3); |
| EXPECT_EQ(I3->getLine(), 42u); |
| EXPECT_EQ(I3->getCol(), 4u); |
| EXPECT_EQ(BTF.findString(I3->FileNameOff), "b.c"); |
| EXPECT_EQ(BTF.findString(I3->LineOff), "first line"); |
| |
| // No info for insn address. |
| EXPECT_FALSE(BTF.findLineInfo({24, 1})); |
| EXPECT_FALSE(BTF.findLineInfo({8, 2})); |
| // No info for section number. |
| EXPECT_FALSE(BTF.findLineInfo({16, 3})); |
| } |
| |
| TEST(BTFParserTest, badSectionNameOffset) { |
| BTFParser BTF; |
| MockData1 Mock; |
| // "foo" is section #1, corrupting it's name offset will make impossible |
| // to match section name with section index when BTF is parsed. |
| Mock.Ext.Lines.Foo.Sec.SecNameOff = 100500; |
| Error Err = BTF.parse(Mock.makeObj()); |
| EXPECT_FALSE(Err); |
| // "foo" line info should be corrupted. |
| EXPECT_FALSE(BTF.findLineInfo({16, 1})); |
| // "bar" line info should be ok. |
| EXPECT_TRUE(BTF.findLineInfo({0, 2})); |
| } |
| |
| // Keep this as macro to preserve line number info. |
| #define EXPECT_PARSE_ERROR(Mock, Message) \ |
| do { \ |
| BTFParser BTF; \ |
| EXPECT_THAT_ERROR(BTF.parse((Mock).makeObj()), \ |
| FailedWithMessage(testing::HasSubstr(Message))); \ |
| } while (false) |
| |
| TEST(BTFParserTest, badBTFMagic) { |
| MockData1 Mock; |
| Mock.BTF.Header.Magic = 42; |
| EXPECT_PARSE_ERROR(Mock, "invalid .BTF magic: 2a"); |
| } |
| |
| TEST(BTFParserTest, badBTFVersion) { |
| MockData1 Mock; |
| Mock.BTF.Header.Version = 42; |
| EXPECT_PARSE_ERROR(Mock, "unsupported .BTF version: 42"); |
| } |
| |
| TEST(BTFParserTest, badBTFHdrLen) { |
| MockData1 Mock; |
| Mock.BTF.Header.HdrLen = 5; |
| EXPECT_PARSE_ERROR(Mock, "unexpected .BTF header length: 5"); |
| } |
| |
| TEST(BTFParserTest, badBTFSectionLen) { |
| MockData1 Mock1, Mock2; |
| |
| // Cut-off string section by one byte. |
| Mock1.BTFSectionLen = |
| offsetof(MockData1::B, Strings) + sizeof(MockData1::B::S) - 1; |
| EXPECT_PARSE_ERROR(Mock1, "invalid .BTF section size"); |
| |
| // Cut-off header. |
| Mock2.BTFSectionLen = offsetof(BTF::Header, StrOff); |
| EXPECT_PARSE_ERROR(Mock2, BTFEndOfData); |
| } |
| |
| TEST(BTFParserTest, badBTFExtMagic) { |
| MockData1 Mock; |
| Mock.Ext.Header.Magic = 42; |
| EXPECT_PARSE_ERROR(Mock, "invalid .BTF.ext magic: 2a"); |
| } |
| |
| TEST(BTFParserTest, badBTFExtVersion) { |
| MockData1 Mock; |
| Mock.Ext.Header.Version = 42; |
| EXPECT_PARSE_ERROR(Mock, "unsupported .BTF.ext version: 42"); |
| } |
| |
| TEST(BTFParserTest, badBTFExtHdrLen) { |
| MockData1 Mock1, Mock2; |
| |
| Mock1.Ext.Header.HdrLen = 5; |
| EXPECT_PARSE_ERROR(Mock1, "unexpected .BTF.ext header length: 5"); |
| |
| Mock2.Ext.Header.HdrLen = sizeof(Mock2.Ext); |
| EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData); |
| } |
| |
| TEST(BTFParserTest, badBTFExtSectionLen) { |
| MockData1 Mock1, Mock2, Mock3; |
| |
| // Cut-off header before HdrLen. |
| Mock1.ExtSectionLen = offsetof(BTF::ExtHeader, HdrLen); |
| EXPECT_PARSE_ERROR(Mock1, BTFExtEndOfData); |
| |
| // Cut-off header before LineInfoLen. |
| Mock2.ExtSectionLen = offsetof(BTF::ExtHeader, LineInfoLen); |
| EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData); |
| |
| // Cut-off line-info section somewhere in the middle. |
| Mock3.ExtSectionLen = offsetof(MockData1::E, Lines) + 4; |
| EXPECT_PARSE_ERROR(Mock3, BTFExtEndOfData); |
| } |
| |
| TEST(BTFParserTest, badBTFExtLineInfoRecSize) { |
| MockData1 Mock1, Mock2; |
| |
| Mock1.Ext.Lines.LineRecSize = 2; |
| EXPECT_PARSE_ERROR(Mock1, "unexpected .BTF.ext line info record length: 2"); |
| |
| Mock2.Ext.Lines.LineRecSize = sizeof(Mock2.Ext.Lines.Foo.Lines[0]) + 1; |
| EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData); |
| } |
| |
| TEST(BTFParserTest, badBTFExtLineSectionName) { |
| MockData1 Mock1; |
| |
| Mock1.Ext.Lines.Foo.Sec.SecNameOff = offsetof(MockData1::B::S, Buz); |
| EXPECT_PARSE_ERROR( |
| Mock1, "can't find section 'buz' while parsing .BTF.ext line info"); |
| } |
| |
| TEST(BTFParserTest, missingSections) { |
| MockData1 Mock1, Mock2, Mock3; |
| |
| Mock1.BTFSectionLen = -1; |
| EXPECT_PARSE_ERROR(Mock1, "can't find .BTF section"); |
| EXPECT_FALSE(BTFParser::hasBTFSections(Mock1.makeObj())); |
| |
| Mock2.ExtSectionLen = -1; |
| EXPECT_PARSE_ERROR(Mock2, "can't find .BTF.ext section"); |
| EXPECT_FALSE(BTFParser::hasBTFSections(Mock2.makeObj())); |
| |
| EXPECT_TRUE(BTFParser::hasBTFSections(Mock3.makeObj())); |
| } |
| |
| // Check that BTFParser instance is reset when BTFParser::parse() is |
| // called several times. |
| TEST(BTFParserTest, parserReset) { |
| BTFParser BTF; |
| MockData1 Mock1, Mock2; |
| |
| EXPECT_FALSE(BTF.parse(Mock1.makeObj())); |
| EXPECT_TRUE(BTF.findLineInfo({16, 1})); |
| EXPECT_TRUE(BTF.findLineInfo({0, 2})); |
| |
| // Break the reference to "bar" section name, thus making |
| // information about "bar" line numbers unavailable. |
| Mock2.Ext.Lines.Bar.Sec.SecNameOff = 100500; |
| |
| EXPECT_FALSE(BTF.parse(Mock2.makeObj())); |
| EXPECT_TRUE(BTF.findLineInfo({16, 1})); |
| // Make sure that "bar" no longer available (its index is 2). |
| EXPECT_FALSE(BTF.findLineInfo({0, 2})); |
| } |
| |
| TEST(BTFParserTest, btfContext) { |
| MockData1 Mock; |
| BTFParser BTF; |
| std::unique_ptr<BTFContext> Ctx = BTFContext::create(Mock.makeObj()); |
| |
| DILineInfo I1 = Ctx->getLineInfoForAddress({16, 1}); |
| EXPECT_EQ(I1.Line, 7u); |
| EXPECT_EQ(I1.Column, 1u); |
| EXPECT_EQ(I1.FileName, "a.c"); |
| EXPECT_EQ(I1.LineSource, "first line"); |
| |
| DILineInfo I2 = Ctx->getLineInfoForAddress({24, 1}); |
| EXPECT_EQ(I2.Line, 0u); |
| EXPECT_EQ(I2.Column, 0u); |
| EXPECT_EQ(I2.FileName, DILineInfo::BadString); |
| EXPECT_EQ(I2.LineSource, std::nullopt); |
| } |
| |
| static uint32_t mkInfo(uint32_t Kind) { return Kind << 24; } |
| |
| template <typename T> static void append(std::string &S, const T &What) { |
| S.append((const char *)&What, sizeof(What)); |
| } |
| |
| class MockData2 { |
| SmallString<0> ObjStorage; |
| std::unique_ptr<ObjectFile> Obj; |
| std::string Types; |
| std::string Strings; |
| std::string Relocs; |
| std::string Lines; |
| unsigned TotalTypes; |
| int LastRelocSecIdx; |
| unsigned NumRelocs; |
| int LastLineSecIdx; |
| unsigned NumLines; |
| |
| public: |
| MockData2() { reset(); } |
| |
| unsigned totalTypes() const { return TotalTypes; } |
| |
| uint32_t addString(StringRef S) { |
| uint32_t Off = Strings.size(); |
| Strings.append(S.data(), S.size()); |
| Strings.append("\0", 1); |
| return Off; |
| }; |
| |
| uint32_t addType(const BTF::CommonType &Tp) { |
| append(Types, Tp); |
| return ++TotalTypes; |
| } |
| |
| template <typename T> void addTail(const T &Tp) { append(Types, Tp); } |
| |
| void resetTypes() { |
| Types.resize(0); |
| TotalTypes = 0; |
| } |
| |
| void reset() { |
| ObjStorage.clear(); |
| Types.resize(0); |
| Strings.resize(0); |
| Relocs.resize(0); |
| Lines.resize(0); |
| TotalTypes = 0; |
| LastRelocSecIdx = -1; |
| NumRelocs = 0; |
| LastLineSecIdx = -1; |
| NumLines = 0; |
| } |
| |
| void finishRelocSec() { |
| if (LastRelocSecIdx == -1) |
| return; |
| |
| BTF::SecFieldReloc *SecInfo = |
| (BTF::SecFieldReloc *)&Relocs[LastRelocSecIdx]; |
| SecInfo->NumFieldReloc = NumRelocs; |
| LastRelocSecIdx = -1; |
| NumRelocs = 0; |
| } |
| |
| void finishLineSec() { |
| if (LastLineSecIdx == -1) |
| return; |
| |
| BTF::SecLineInfo *SecInfo = (BTF::SecLineInfo *)&Lines[LastLineSecIdx]; |
| SecInfo->NumLineInfo = NumLines; |
| NumLines = 0; |
| LastLineSecIdx = -1; |
| } |
| |
| void addRelocSec(const BTF::SecFieldReloc &R) { |
| finishRelocSec(); |
| LastRelocSecIdx = Relocs.size(); |
| append(Relocs, R); |
| } |
| |
| void addReloc(const BTF::BPFFieldReloc &R) { |
| append(Relocs, R); |
| ++NumRelocs; |
| } |
| |
| void addLinesSec(const BTF::SecLineInfo &R) { |
| finishLineSec(); |
| LastLineSecIdx = Lines.size(); |
| append(Lines, R); |
| } |
| |
| void addLine(const BTF::BPFLineInfo &R) { |
| append(Lines, R); |
| ++NumLines; |
| } |
| |
| ObjectFile &makeObj() { |
| finishRelocSec(); |
| finishLineSec(); |
| |
| BTF::Header BTFHeader = {}; |
| BTFHeader.Magic = BTF::MAGIC; |
| BTFHeader.Version = 1; |
| BTFHeader.HdrLen = sizeof(BTFHeader); |
| BTFHeader.StrOff = 0; |
| BTFHeader.StrLen = Strings.size(); |
| BTFHeader.TypeOff = Strings.size(); |
| BTFHeader.TypeLen = Types.size(); |
| |
| std::string BTFSec; |
| append(BTFSec, BTFHeader); |
| BTFSec.append(Strings); |
| BTFSec.append(Types); |
| |
| BTF::ExtHeader ExtHeader = {}; |
| ExtHeader.Magic = BTF::MAGIC; |
| ExtHeader.Version = 1; |
| ExtHeader.HdrLen = sizeof(ExtHeader); |
| ExtHeader.FieldRelocOff = 0; |
| ExtHeader.FieldRelocLen = Relocs.size() + sizeof(uint32_t); |
| ExtHeader.LineInfoOff = ExtHeader.FieldRelocLen; |
| ExtHeader.LineInfoLen = Lines.size() + sizeof(uint32_t); |
| |
| std::string ExtSec; |
| append(ExtSec, ExtHeader); |
| append(ExtSec, (uint32_t)sizeof(BTF::BPFFieldReloc)); |
| ExtSec.append(Relocs); |
| append(ExtSec, (uint32_t)sizeof(BTF::BPFLineInfo)); |
| ExtSec.append(Lines); |
| |
| std::string YamlBuffer; |
| raw_string_ostream Yaml(YamlBuffer); |
| Yaml << R"( |
| !ELF |
| FileHeader: |
| Class: ELFCLASS64)"; |
| if (sys::IsBigEndianHost) |
| Yaml << "\n Data: ELFDATA2MSB"; |
| else |
| Yaml << "\n Data: ELFDATA2LSB"; |
| Yaml << R"( |
| Type: ET_REL |
| Machine: EM_BPF |
| Sections: |
| - Name: foo |
| Type: SHT_PROGBITS |
| Size: 0x80 |
| - Name: bar |
| Type: SHT_PROGBITS |
| Size: 0x80 |
| - Name: .BTF |
| Type: SHT_PROGBITS |
| Content: )" |
| << makeBinRef(BTFSec.data(), BTFSec.size()); |
| Yaml << R"( |
| - Name: .BTF.ext |
| Type: SHT_PROGBITS |
| Content: )" |
| << makeBinRef(ExtSec.data(), ExtSec.size()); |
| |
| Obj = yaml::yaml2ObjectFile(ObjStorage, YamlBuffer, |
| [](const Twine &Err) { errs() << Err; }); |
| return *Obj.get(); |
| } |
| }; |
| |
| TEST(BTFParserTest, allTypeKinds) { |
| MockData2 D; |
| D.addType({D.addString("1"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| D.addType({D.addString("2"), mkInfo(BTF::BTF_KIND_PTR), {1}}); |
| D.addType({D.addString("3"), mkInfo(BTF::BTF_KIND_ARRAY), {0}}); |
| D.addTail(BTF::BTFArray({1, 1, 2})); |
| D.addType({D.addString("4"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}}); |
| D.addTail(BTF::BTFMember({D.addString("a"), 1, 0})); |
| D.addTail(BTF::BTFMember({D.addString("b"), 1, 0})); |
| D.addType({D.addString("5"), mkInfo(BTF::BTF_KIND_UNION) | 3, {8}}); |
| D.addTail(BTF::BTFMember({D.addString("a"), 1, 0})); |
| D.addTail(BTF::BTFMember({D.addString("b"), 1, 0})); |
| D.addTail(BTF::BTFMember({D.addString("c"), 1, 0})); |
| D.addType({D.addString("6"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}}); |
| D.addTail(BTF::BTFEnum({D.addString("U"), 1})); |
| D.addTail(BTF::BTFEnum({D.addString("V"), 2})); |
| D.addType({D.addString("7"), mkInfo(BTF::BTF_KIND_ENUM64) | 1, {4}}); |
| D.addTail(BTF::BTFEnum64({D.addString("W"), 0, 1})); |
| D.addType( |
| {D.addString("8"), BTF::FWD_UNION_FLAG | mkInfo(BTF::BTF_KIND_FWD), {0}}); |
| D.addType({D.addString("9"), mkInfo(BTF::BTF_KIND_TYPEDEF), {1}}); |
| D.addType({D.addString("10"), mkInfo(BTF::BTF_KIND_VOLATILE), {1}}); |
| D.addType({D.addString("11"), mkInfo(BTF::BTF_KIND_CONST), {1}}); |
| D.addType({D.addString("12"), mkInfo(BTF::BTF_KIND_RESTRICT), {1}}); |
| D.addType({D.addString("13"), mkInfo(BTF::BTF_KIND_FUNC_PROTO) | 1, {1}}); |
| D.addTail(BTF::BTFParam({D.addString("P"), 2})); |
| D.addType({D.addString("14"), mkInfo(BTF::BTF_KIND_FUNC), {13}}); |
| D.addType({D.addString("15"), mkInfo(BTF::BTF_KIND_VAR), {2}}); |
| D.addTail((uint32_t)0); |
| D.addType({D.addString("16"), mkInfo(BTF::BTF_KIND_DATASEC) | 3, {0}}); |
| D.addTail(BTF::BTFDataSec({1, 0, 4})); |
| D.addTail(BTF::BTFDataSec({1, 4, 4})); |
| D.addTail(BTF::BTFDataSec({1, 8, 4})); |
| D.addType({D.addString("17"), mkInfo(BTF::BTF_KIND_FLOAT), {4}}); |
| D.addType({D.addString("18"), mkInfo(BTF::BTF_KIND_DECL_TAG), {0}}); |
| D.addTail((uint32_t)-1); |
| D.addType({D.addString("19"), mkInfo(BTF::BTF_KIND_TYPE_TAG), {0}}); |
| |
| BTFParser BTF; |
| Error Err = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(Err); |
| |
| EXPECT_EQ(D.totalTypes() + 1 /* +1 for void */, BTF.typesCount()); |
| for (unsigned Id = 1; Id < D.totalTypes() + 1; ++Id) { |
| const BTF::CommonType *Tp = BTF.findType(Id); |
| ASSERT_TRUE(Tp); |
| std::string IdBuf; |
| raw_string_ostream IdBufStream(IdBuf); |
| IdBufStream << Id; |
| EXPECT_EQ(BTF.findString(Tp->NameOff), IdBuf); |
| } |
| } |
| |
| TEST(BTFParserTest, bigStruct) { |
| const uint32_t N = 1000u; |
| MockData2 D; |
| uint32_t FStr = D.addString("f"); |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| D.addType({D.addString("big"), mkInfo(BTF::BTF_KIND_STRUCT) | N, {8}}); |
| for (unsigned I = 0; I < N; ++I) |
| D.addTail(BTF::BTFMember({FStr, 1, 0})); |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| |
| BTFParser BTF; |
| ASSERT_SUCCEEDED(BTF.parse(D.makeObj())); |
| ASSERT_EQ(BTF.typesCount(), 4u /* +1 for void */); |
| const BTF::CommonType *Foo = BTF.findType(1); |
| const BTF::CommonType *Big = BTF.findType(2); |
| const BTF::CommonType *Bar = BTF.findType(3); |
| ASSERT_TRUE(Foo); |
| ASSERT_TRUE(Big); |
| ASSERT_TRUE(Bar); |
| EXPECT_EQ(BTF.findString(Foo->NameOff), "foo"); |
| EXPECT_EQ(BTF.findString(Big->NameOff), "big"); |
| EXPECT_EQ(BTF.findString(Bar->NameOff), "bar"); |
| EXPECT_EQ(Big->getVlen(), N); |
| } |
| |
| TEST(BTFParserTest, incompleteTypes) { |
| MockData2 D; |
| auto IncompleteType = [&](const BTF::CommonType &Tp) { |
| D.resetTypes(); |
| D.addType(Tp); |
| EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section"); |
| }; |
| |
| // All kinds that need tail. |
| IncompleteType({D.addString("a"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| IncompleteType({D.addString("b"), mkInfo(BTF::BTF_KIND_ARRAY), {0}}); |
| IncompleteType({D.addString("c"), mkInfo(BTF::BTF_KIND_VAR), {0}}); |
| IncompleteType({D.addString("d"), mkInfo(BTF::BTF_KIND_DECL_TAG), {0}}); |
| |
| // All kinds with vlen. |
| IncompleteType({D.addString("a"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}}); |
| IncompleteType({D.addString("b"), mkInfo(BTF::BTF_KIND_UNION) | 3, {8}}); |
| IncompleteType({D.addString("c"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}}); |
| IncompleteType({D.addString("d"), mkInfo(BTF::BTF_KIND_ENUM64) | 1, {4}}); |
| IncompleteType({D.addString("e"), mkInfo(BTF::BTF_KIND_FUNC_PROTO) | 1, {1}}); |
| IncompleteType({D.addString("f"), mkInfo(BTF::BTF_KIND_DATASEC) | 3, {0}}); |
| |
| // An unexpected tail. |
| D.resetTypes(); |
| D.addTail((uint32_t)0); |
| EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section"); |
| } |
| |
| // Use macro to preserve line number in error message. |
| #define SYMBOLIZE(SecAddr, Expected) \ |
| do { \ |
| const BTF::BPFFieldReloc *R = BTF.findFieldReloc((SecAddr)); \ |
| ASSERT_TRUE(R); \ |
| SmallString<64> Symbolized; \ |
| BTF.symbolize(R, Symbolized); \ |
| EXPECT_EQ(Symbolized, (Expected)); \ |
| } while (false) |
| |
| // Shorter name for initializers below. |
| using SA = SectionedAddress; |
| |
| TEST(BTFParserTest, typeRelocs) { |
| MockData2 D; |
| uint32_t Zero = D.addString("0"); |
| // id 1: struct foo {} |
| // id 2: union bar; |
| // id 3: struct buz; |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| D.addType({D.addString("bar"), |
| mkInfo(BTF::BTF_KIND_FWD) | BTF::FWD_UNION_FLAG, |
| {0}}); |
| D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_FWD), {0}}); |
| D.addRelocSec({D.addString("foo"), 7}); |
| // List of all possible correct type relocations for type id #1. |
| D.addReloc({0, 1, Zero, BTF::BTF_TYPE_ID_LOCAL}); |
| D.addReloc({8, 1, Zero, BTF::BTF_TYPE_ID_REMOTE}); |
| D.addReloc({16, 1, Zero, BTF::TYPE_EXISTENCE}); |
| D.addReloc({24, 1, Zero, BTF::TYPE_MATCH}); |
| D.addReloc({32, 1, Zero, BTF::TYPE_SIZE}); |
| // Forward declarations. |
| D.addReloc({40, 2, Zero, BTF::TYPE_SIZE}); |
| D.addReloc({48, 3, Zero, BTF::TYPE_SIZE}); |
| // Incorrect type relocation: bad type id. |
| D.addReloc({56, 42, Zero, BTF::TYPE_SIZE}); |
| // Incorrect type relocation: spec should be '0'. |
| D.addReloc({64, 1, D.addString("10"), BTF::TYPE_SIZE}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<local_type_id> [1] struct foo"); |
| SYMBOLIZE(SA({8, 1}), "<target_type_id> [1] struct foo"); |
| SYMBOLIZE(SA({16, 1}), "<type_exists> [1] struct foo"); |
| SYMBOLIZE(SA({24, 1}), "<type_matches> [1] struct foo"); |
| SYMBOLIZE(SA({32, 1}), "<type_size> [1] struct foo"); |
| SYMBOLIZE(SA({40, 1}), "<type_size> [2] fwd union bar"); |
| SYMBOLIZE(SA({48, 1}), "<type_size> [3] fwd struct buz"); |
| SYMBOLIZE(SA({56, 1}), "<type_size> [42] '0' <unknown type id: 42>"); |
| SYMBOLIZE(SA({64, 1}), |
| "<type_size> [1] '10' " |
| "<unexpected type-based relocation spec: should be '0'>"); |
| } |
| |
| TEST(BTFParserTest, enumRelocs) { |
| MockData2 D; |
| // id 1: enum { U, V } |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}}); |
| D.addTail(BTF::BTFEnum({D.addString("U"), 1})); |
| D.addTail(BTF::BTFEnum({D.addString("V"), 2})); |
| // id 2: int |
| D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| // id 3: enum: uint64_t { A, B } |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_ENUM64) | 2, {8}}); |
| D.addTail(BTF::BTFEnum64({D.addString("A"), 1, 0})); |
| D.addTail(BTF::BTFEnum64({D.addString("B"), 2, 0})); |
| |
| D.addRelocSec({D.addString("foo"), 5}); |
| // An ok relocation accessing value #1: U. |
| D.addReloc({0, 1, D.addString("0"), BTF::ENUM_VALUE_EXISTENCE}); |
| // An ok relocation accessing value #2: V. |
| D.addReloc({8, 1, D.addString("1"), BTF::ENUM_VALUE}); |
| // Incorrect relocation: too many elements in string "1:0". |
| D.addReloc({16, 1, D.addString("1:0"), BTF::ENUM_VALUE}); |
| // Incorrect relocation: type id "2" is not an enum. |
| D.addReloc({24, 2, D.addString("1"), BTF::ENUM_VALUE}); |
| // Incorrect relocation: value #42 does not exist for enum "foo". |
| D.addReloc({32, 1, D.addString("42"), BTF::ENUM_VALUE}); |
| // An ok relocation accessing value #1: A. |
| D.addReloc({40, 3, D.addString("0"), BTF::ENUM_VALUE_EXISTENCE}); |
| // An ok relocation accessing value #2: B. |
| D.addReloc({48, 3, D.addString("1"), BTF::ENUM_VALUE}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<enumval_exists> [1] enum foo::U = 1"); |
| SYMBOLIZE(SA({8, 1}), "<enumval_value> [1] enum foo::V = 2"); |
| SYMBOLIZE( |
| SA({16, 1}), |
| "<enumval_value> [1] '1:0' <unexpected enumval relocation spec size>"); |
| SYMBOLIZE( |
| SA({24, 1}), |
| "<enumval_value> [2] '1' <unexpected type kind for enum relocation: 1>"); |
| SYMBOLIZE(SA({32, 1}), "<enumval_value> [1] '42' <bad value index: 42>"); |
| SYMBOLIZE(SA({40, 1}), "<enumval_exists> [3] enum bar::A = 1"); |
| SYMBOLIZE(SA({48, 1}), "<enumval_value> [3] enum bar::B = 2"); |
| } |
| |
| TEST(BTFParserTest, enumRelocsMods) { |
| MockData2 D; |
| // id 1: enum { U, V } |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}}); |
| D.addTail(BTF::BTFEnum({D.addString("U"), 1})); |
| D.addTail(BTF::BTFEnum({D.addString("V"), 2})); |
| // id 2: typedef enum foo a; |
| D.addType({D.addString("a"), mkInfo(BTF::BTF_KIND_TYPEDEF), {1}}); |
| // id 3: const enum foo; |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {1}}); |
| |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, 2, D.addString("0"), BTF::ENUM_VALUE}); |
| D.addReloc({8, 3, D.addString("1"), BTF::ENUM_VALUE}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<enumval_value> [2] typedef a::U = 1"); |
| SYMBOLIZE(SA({8, 1}), "<enumval_value> [3] const enum foo::V = 2"); |
| } |
| |
| TEST(BTFParserTest, fieldRelocs) { |
| MockData2 D; |
| // id 1: int |
| D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| // id 2: struct foo { int a; int b; } |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}}); |
| D.addTail(BTF::BTFMember({D.addString("a"), 1, 0})); |
| D.addTail(BTF::BTFMember({D.addString("b"), 1, 0})); |
| // id 3: array of struct foo. |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_ARRAY), {0}}); |
| D.addTail(BTF::BTFArray({2, 1, 2})); |
| // id 4: struct bar { struct foo u[2]; int v; } |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}}); |
| D.addTail(BTF::BTFMember({D.addString("u"), 3, 0})); |
| D.addTail(BTF::BTFMember({D.addString("v"), 1, 0})); |
| // id 5: array with bad element type id. |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_ARRAY), {0}}); |
| D.addTail(BTF::BTFArray({42, 1, 2})); |
| // id 6: struct buz { <bad type> u[2]; <bad type> v; } |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}}); |
| D.addTail(BTF::BTFMember({D.addString("u"), 5, 0})); |
| D.addTail(BTF::BTFMember({D.addString("v"), 42, 0})); |
| |
| D.addRelocSec({D.addString("foo"), 0 /* patched automatically */}); |
| // All field relocations kinds for struct bar::v. |
| D.addReloc({0, 4, D.addString("0:1"), BTF::FIELD_BYTE_OFFSET}); |
| D.addReloc({8, 4, D.addString("0:1"), BTF::FIELD_BYTE_SIZE}); |
| D.addReloc({16, 4, D.addString("0:1"), BTF::FIELD_EXISTENCE}); |
| D.addReloc({24, 4, D.addString("0:1"), BTF::FIELD_SIGNEDNESS}); |
| D.addReloc({32, 4, D.addString("0:1"), BTF::FIELD_LSHIFT_U64}); |
| D.addReloc({40, 4, D.addString("0:1"), BTF::FIELD_RSHIFT_U64}); |
| // Non-zero first idx. |
| D.addReloc({48, 4, D.addString("7:1"), BTF::FIELD_BYTE_OFFSET}); |
| // Access through array and struct: struct bar::u[1].a. |
| D.addReloc({56, 4, D.addString("0:0:1:0"), BTF::FIELD_BYTE_OFFSET}); |
| // Access through array and struct: struct bar::u[1].b. |
| D.addReloc({64, 4, D.addString("0:0:1:1"), BTF::FIELD_BYTE_OFFSET}); |
| // Incorrect relocation: empty access string. |
| D.addReloc({72, 4, D.addString(""), BTF::FIELD_BYTE_OFFSET}); |
| // Incorrect relocation: member index out of range (only two members in bar). |
| D.addReloc({80, 4, D.addString("0:2"), BTF::FIELD_BYTE_OFFSET}); |
| // Incorrect relocation: unknown element type id (buz::u[0] access). |
| D.addReloc({88, 6, D.addString("0:0:0"), BTF::FIELD_BYTE_OFFSET}); |
| // Incorrect relocation: unknown member type id (buz::v access). |
| D.addReloc({96, 6, D.addString("0:1:0"), BTF::FIELD_BYTE_OFFSET}); |
| // Incorrect relocation: non structural type in the middle of access string |
| // struct bar::v.<something>. |
| D.addReloc({104, 4, D.addString("0:1:0"), BTF::FIELD_BYTE_OFFSET}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<byte_off> [4] struct bar::v (0:1)"); |
| SYMBOLIZE(SA({8, 1}), "<byte_sz> [4] struct bar::v (0:1)"); |
| SYMBOLIZE(SA({16, 1}), "<field_exists> [4] struct bar::v (0:1)"); |
| SYMBOLIZE(SA({24, 1}), "<signed> [4] struct bar::v (0:1)"); |
| SYMBOLIZE(SA({32, 1}), "<lshift_u64> [4] struct bar::v (0:1)"); |
| SYMBOLIZE(SA({40, 1}), "<rshift_u64> [4] struct bar::v (0:1)"); |
| SYMBOLIZE(SA({48, 1}), "<byte_off> [4] struct bar::[7].v (7:1)"); |
| SYMBOLIZE(SA({56, 1}), "<byte_off> [4] struct bar::u[1].a (0:0:1:0)"); |
| SYMBOLIZE(SA({64, 1}), "<byte_off> [4] struct bar::u[1].b (0:0:1:1)"); |
| SYMBOLIZE(SA({72, 1}), "<byte_off> [4] '' <field spec too short>"); |
| SYMBOLIZE(SA({80, 1}), |
| "<byte_off> [4] '0:2' " |
| "<member index 2 for spec sub-string 1 is out of range>"); |
| SYMBOLIZE(SA({88, 1}), "<byte_off> [6] '0:0:0' " |
| "<unknown element type id 42 for spec sub-string 2>"); |
| SYMBOLIZE(SA({96, 1}), "<byte_off> [6] '0:1:0' " |
| "<unknown member type id 42 for spec sub-string 1>"); |
| SYMBOLIZE(SA({104, 1}), "<byte_off> [4] '0:1:0' " |
| "<unexpected type kind 1 for spec sub-string 2>"); |
| } |
| |
| TEST(BTFParserTest, fieldRelocsMods) { |
| MockData2 D; |
| // struct foo { |
| // int u; |
| // } |
| // typedef struct foo bar; |
| // struct buz { |
| // const bar v; |
| // } |
| // typedef buz quux; |
| // const volatile restrict quux <some-var>; |
| uint32_t Int = |
| D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| uint32_t Foo = |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}}); |
| D.addTail(BTF::BTFMember({D.addString("u"), Int, 0})); |
| uint32_t Bar = |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_TYPEDEF), {Foo}}); |
| uint32_t CBar = |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_CONST), {Bar}}); |
| uint32_t Buz = |
| D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}}); |
| D.addTail(BTF::BTFMember({D.addString("v"), CBar, 0})); |
| uint32_t Quux = |
| D.addType({D.addString("quux"), mkInfo(BTF::BTF_KIND_TYPEDEF), {Buz}}); |
| uint32_t RQuux = |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_RESTRICT), {Quux}}); |
| uint32_t VRQuux = |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_VOLATILE), {RQuux}}); |
| uint32_t CVRQuux = |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {VRQuux}}); |
| uint32_t CUnknown = |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {77}}); |
| uint32_t CVUnknown = |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_VOLATILE), {CUnknown}}); |
| |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, Bar, D.addString("0:0"), BTF::FIELD_BYTE_OFFSET}); |
| D.addReloc({8, CVRQuux, D.addString("0:0:0"), BTF::FIELD_BYTE_OFFSET}); |
| D.addReloc({16, CVUnknown, D.addString("0:1:2"), BTF::FIELD_BYTE_OFFSET}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| // Should show modifiers / name of typedef. |
| SYMBOLIZE(SA({0, 1}), "<byte_off> [3] typedef bar::u (0:0)"); |
| SYMBOLIZE(SA({8, 1}), |
| "<byte_off> [9] const volatile restrict typedef quux::v.u (0:0:0)"); |
| SYMBOLIZE(SA({16, 1}), |
| "<byte_off> [11] '0:1:2' <unknown type id: 77 in modifiers chain>"); |
| } |
| |
| TEST(BTFParserTest, relocTypeTagAndVoid) { |
| MockData2 D; |
| // __attribute__((type_tag("tag"))) void |
| uint32_t Tag = |
| D.addType({D.addString("tag"), mkInfo(BTF::BTF_KIND_TYPE_TAG), {0}}); |
| |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, Tag, D.addString("0"), BTF::TYPE_EXISTENCE}); |
| D.addReloc({8, 0 /* void */, D.addString("0"), BTF::TYPE_EXISTENCE}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<type_exists> [1] type_tag(\"tag\") void"); |
| SYMBOLIZE(SA({8, 1}), "<type_exists> [0] void"); |
| } |
| |
| TEST(BTFParserTest, longRelocModifiersCycle) { |
| MockData2 D; |
| |
| D.addType( |
| {D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {1 /* ourselves */}}); |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<type_exists> [1] '' <modifiers chain is too long>"); |
| } |
| |
| TEST(BTFParserTest, relocAnonFieldsAndTypes) { |
| MockData2 D; |
| |
| // struct { |
| // int :32; |
| // } v; |
| uint32_t Int = |
| D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}}); |
| D.addTail((uint32_t)0); |
| uint32_t Anon = |
| D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}}); |
| D.addTail(BTF::BTFMember({D.addString(""), Int, 0})); |
| |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, Anon, D.addString("0"), BTF::TYPE_EXISTENCE}); |
| D.addReloc({8, Anon, D.addString("0:0"), BTF::FIELD_BYTE_OFFSET}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), "<type_exists> [2] struct <anon 2>"); |
| SYMBOLIZE(SA({8, 1}), "<byte_off> [2] struct <anon 2>::<anon 0> (0:0)"); |
| } |
| |
| TEST(BTFParserTest, miscBadRelos) { |
| MockData2 D; |
| |
| uint32_t S = D.addType({D.addString("S"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, 0, D.addString(""), 777}); |
| D.addReloc({8, S, D.addString("abc"), BTF::FIELD_BYTE_OFFSET}); |
| D.addReloc({16, S, D.addString("0#"), BTF::FIELD_BYTE_OFFSET}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| SYMBOLIZE(SA({0, 1}), |
| "<reloc kind #777> [0] '' <unknown relocation kind: 777>"); |
| SYMBOLIZE(SA({8, 1}), "<byte_off> [1] 'abc' <spec string is not a number>"); |
| SYMBOLIZE(SA({16, 1}), |
| "<byte_off> [1] '0#' <unexpected spec string delimiter: '#'>"); |
| } |
| |
| TEST(BTFParserTest, relocsMultipleSections) { |
| MockData2 D; |
| |
| uint32_t S = D.addType({D.addString("S"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| uint32_t T = D.addType({D.addString("T"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, S, D.addString(""), BTF::TYPE_EXISTENCE}); |
| D.addReloc({8, S, D.addString(""), BTF::TYPE_EXISTENCE}); |
| |
| D.addRelocSec({D.addString("bar"), 0}); |
| D.addReloc({8, T, D.addString(""), BTF::TYPE_EXISTENCE}); |
| D.addReloc({16, T, D.addString(""), BTF::TYPE_EXISTENCE}); |
| |
| BTFParser BTF; |
| Error E = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E); |
| |
| EXPECT_TRUE(BTF.findFieldReloc({0, 1})); |
| EXPECT_TRUE(BTF.findFieldReloc({8, 1})); |
| EXPECT_FALSE(BTF.findFieldReloc({16, 1})); |
| |
| EXPECT_FALSE(BTF.findFieldReloc({0, 2})); |
| EXPECT_TRUE(BTF.findFieldReloc({8, 2})); |
| EXPECT_TRUE(BTF.findFieldReloc({16, 2})); |
| |
| EXPECT_FALSE(BTF.findFieldReloc({0, 3})); |
| EXPECT_FALSE(BTF.findFieldReloc({8, 3})); |
| EXPECT_FALSE(BTF.findFieldReloc({16, 3})); |
| |
| auto AssertReloType = [&](const SectionedAddress &A, const char *Name) { |
| const BTF::BPFFieldReloc *Relo = BTF.findFieldReloc(A); |
| ASSERT_TRUE(Relo); |
| const BTF::CommonType *Type = BTF.findType(Relo->TypeID); |
| ASSERT_TRUE(Type); |
| EXPECT_EQ(BTF.findString(Type->NameOff), Name); |
| }; |
| |
| AssertReloType({8, 1}, "S"); |
| AssertReloType({8, 2}, "T"); |
| } |
| |
| TEST(BTFParserTest, parserResetReloAndTypes) { |
| BTFParser BTF; |
| MockData2 D; |
| |
| // First time: two types, two relocations. |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE}); |
| D.addReloc({8, 2, D.addString(""), BTF::TYPE_EXISTENCE}); |
| |
| Error E1 = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E1); |
| |
| ASSERT_TRUE(BTF.findType(1)); |
| EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "foo"); |
| EXPECT_TRUE(BTF.findType(2)); |
| EXPECT_TRUE(BTF.findFieldReloc({0, 1})); |
| EXPECT_TRUE(BTF.findFieldReloc({8, 1})); |
| |
| // Second time: one type, one relocation. |
| D.reset(); |
| D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE}); |
| |
| Error E2 = BTF.parse(D.makeObj()); |
| EXPECT_FALSE(E2); |
| |
| ASSERT_TRUE(BTF.findType(1)); |
| EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "buz"); |
| EXPECT_FALSE(BTF.findType(2)); |
| EXPECT_TRUE(BTF.findFieldReloc({0, 1})); |
| EXPECT_FALSE(BTF.findFieldReloc({8, 1})); |
| } |
| |
| TEST(BTFParserTest, selectiveLoad) { |
| BTFParser BTF1, BTF2, BTF3; |
| MockData2 D; |
| |
| D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}}); |
| D.addRelocSec({D.addString("foo"), 0}); |
| D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE}); |
| D.addLinesSec({D.addString("foo"), 0}); |
| D.addLine({0, D.addString("file.c"), D.addString("some line"), LC(2, 3)}); |
| |
| BTFParser::ParseOptions Opts; |
| |
| ObjectFile &Obj1 = D.makeObj(); |
| Opts = {}; |
| Opts.LoadLines = true; |
| ASSERT_SUCCEEDED(BTF1.parse(Obj1, Opts)); |
| |
| Opts = {}; |
| Opts.LoadTypes = true; |
| ASSERT_SUCCEEDED(BTF2.parse(Obj1, Opts)); |
| |
| Opts = {}; |
| Opts.LoadRelocs = true; |
| ASSERT_SUCCEEDED(BTF3.parse(Obj1, Opts)); |
| |
| EXPECT_TRUE(BTF1.findLineInfo({0, 1})); |
| EXPECT_FALSE(BTF2.findLineInfo({0, 1})); |
| EXPECT_FALSE(BTF3.findLineInfo({0, 1})); |
| |
| EXPECT_FALSE(BTF1.findType(1)); |
| EXPECT_TRUE(BTF2.findType(1)); |
| EXPECT_FALSE(BTF3.findType(1)); |
| |
| EXPECT_FALSE(BTF1.findFieldReloc({0, 1})); |
| EXPECT_FALSE(BTF2.findFieldReloc({0, 1})); |
| EXPECT_TRUE(BTF3.findFieldReloc({0, 1})); |
| } |
| |
| } // namespace |