1087 lines
36 KiB
C++
1087 lines
36 KiB
C++
//===-- 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
|