summaryrefslogtreecommitdiff
path: root/llvm/lib/DebugInfo/BTF/BTFParser.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-12-09 13:28:42 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-12-09 13:28:42 +0000
commitb1c73532ee8997fe5dfbeb7d223027bdf99758a0 (patch)
tree7d6e51c294ab6719475d660217aa0c0ad0526292 /llvm/lib/DebugInfo/BTF/BTFParser.cpp
parent7fa27ce4a07f19b07799a767fc29416f3b625afb (diff)
Diffstat (limited to 'llvm/lib/DebugInfo/BTF/BTFParser.cpp')
-rw-r--r--llvm/lib/DebugInfo/BTF/BTFParser.cpp620
1 files changed, 597 insertions, 23 deletions
diff --git a/llvm/lib/DebugInfo/BTF/BTFParser.cpp b/llvm/lib/DebugInfo/BTF/BTFParser.cpp
index 6151e1b15cbb..4fc31a445603 100644
--- a/llvm/lib/DebugInfo/BTF/BTFParser.cpp
+++ b/llvm/lib/DebugInfo/BTF/BTFParser.cpp
@@ -12,6 +12,8 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/BTF/BTFParser.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Endian.h"
#include "llvm/Support/Errc.h"
#define DEBUG_TYPE "debug-info-btf-parser"
@@ -74,11 +76,13 @@ public:
// Used by BTFParser::parse* auxiliary functions.
struct BTFParser::ParseContext {
const ObjectFile &Obj;
+ const ParseOptions &Opts;
// Map from ELF section name to SectionRef
DenseMap<StringRef, SectionRef> Sections;
public:
- ParseContext(const ObjectFile &Obj) : Obj(Obj) {}
+ ParseContext(const ObjectFile &Obj, const ParseOptions &Opts)
+ : Obj(Obj), Opts(Opts) {}
Expected<DataExtractor> makeExtractor(SectionRef Sec) {
Expected<StringRef> Contents = Sec.getContents();
@@ -119,19 +123,126 @@ Error BTFParser::parseBTF(ParseContext &Ctx, SectionRef BTF) {
return Err(".BTF", C);
if (HdrLen < 8)
return Err("unexpected .BTF header length: ") << HdrLen;
- (void)Extractor.getU32(C); // type_off
- (void)Extractor.getU32(C); // type_len
+ uint32_t TypeOff = Extractor.getU32(C);
+ uint32_t TypeLen = Extractor.getU32(C);
uint32_t StrOff = Extractor.getU32(C);
uint32_t StrLen = Extractor.getU32(C);
uint32_t StrStart = HdrLen + StrOff;
uint32_t StrEnd = StrStart + StrLen;
+ uint32_t TypesInfoStart = HdrLen + TypeOff;
+ uint32_t TypesInfoEnd = TypesInfoStart + TypeLen;
+ uint32_t BytesExpected = std::max(StrEnd, TypesInfoEnd);
if (!C)
return Err(".BTF", C);
- if (Extractor.getData().size() < StrEnd)
+ if (Extractor.getData().size() < BytesExpected)
return Err("invalid .BTF section size, expecting at-least ")
- << StrEnd << " bytes";
+ << BytesExpected << " bytes";
+
+ StringsTable = Extractor.getData().slice(StrStart, StrEnd);
+
+ if (TypeLen > 0 && Ctx.Opts.LoadTypes) {
+ StringRef RawData = Extractor.getData().slice(TypesInfoStart, TypesInfoEnd);
+ if (Error E = parseTypesInfo(Ctx, TypesInfoStart, RawData))
+ return E;
+ }
+
+ return Error::success();
+}
+
+// Compute record size for each BTF::CommonType sub-type
+// (including entries in the tail position).
+static size_t byteSize(BTF::CommonType *Type) {
+ size_t Size = sizeof(BTF::CommonType);
+ switch (Type->getKind()) {
+ case BTF::BTF_KIND_INT:
+ Size += sizeof(uint32_t);
+ break;
+ case BTF::BTF_KIND_ARRAY:
+ Size += sizeof(BTF::BTFArray);
+ break;
+ case BTF::BTF_KIND_VAR:
+ Size += sizeof(uint32_t);
+ break;
+ case BTF::BTF_KIND_DECL_TAG:
+ Size += sizeof(uint32_t);
+ break;
+ case BTF::BTF_KIND_STRUCT:
+ case BTF::BTF_KIND_UNION:
+ Size += sizeof(BTF::BTFMember) * Type->getVlen();
+ break;
+ case BTF::BTF_KIND_ENUM:
+ Size += sizeof(BTF::BTFEnum) * Type->getVlen();
+ break;
+ case BTF::BTF_KIND_ENUM64:
+ Size += sizeof(BTF::BTFEnum64) * Type->getVlen();
+ break;
+ case BTF::BTF_KIND_FUNC_PROTO:
+ Size += sizeof(BTF::BTFParam) * Type->getVlen();
+ break;
+ case BTF::BTF_KIND_DATASEC:
+ Size += sizeof(BTF::BTFDataSec) * Type->getVlen();
+ break;
+ }
+ return Size;
+}
+
+// Guard value for voids, simplifies code a bit, but NameOff is not
+// actually valid.
+const BTF::CommonType VoidTypeInst = {0, BTF::BTF_KIND_UNKN << 24, {0}};
+
+// Type information "parsing" is very primitive:
+// - The `RawData` is copied to a buffer owned by `BTFParser` instance.
+// - The buffer is treated as an array of `uint32_t` values, each value
+// is swapped to use native endianness. This is possible, because
+// according to BTF spec all buffer elements are structures comprised
+// of `uint32_t` fields.
+// - `BTFParser::Types` vector is filled with pointers to buffer
+// elements, using `byteSize()` function to slice the buffer at type
+// record boundaries.
+// - If at some point a type definition with incorrect size (logical size
+// exceeding buffer boundaries) is reached it is not added to the
+// `BTFParser::Types` vector and the process stops.
+Error BTFParser::parseTypesInfo(ParseContext &Ctx, uint64_t TypesInfoStart,
+ StringRef RawData) {
+ using support::endian::byte_swap;
+
+ TypesBuffer = OwningArrayRef<uint8_t>(arrayRefFromStringRef(RawData));
+ // Switch endianness if necessary.
+ endianness Endianness = Ctx.Obj.isLittleEndian() ? llvm::endianness::little
+ : llvm::endianness::big;
+ uint32_t *TypesBuffer32 = (uint32_t *)TypesBuffer.data();
+ for (uint64_t I = 0; I < TypesBuffer.size() / 4; ++I)
+ TypesBuffer32[I] = byte_swap(TypesBuffer32[I], Endianness);
+
+ // The type id 0 is reserved for void type.
+ Types.push_back(&VoidTypeInst);
+
+ uint64_t Pos = 0;
+ while (Pos < RawData.size()) {
+ uint64_t BytesLeft = RawData.size() - Pos;
+ uint64_t Offset = TypesInfoStart + Pos;
+ BTF::CommonType *Type = (BTF::CommonType *)&TypesBuffer[Pos];
+ if (BytesLeft < sizeof(*Type))
+ return Err("incomplete type definition in .BTF section:")
+ << " offset " << Offset << ", index " << Types.size();
+
+ uint64_t Size = byteSize(Type);
+ if (BytesLeft < Size)
+ return Err("incomplete type definition in .BTF section:")
+ << " offset=" << Offset << ", index=" << Types.size()
+ << ", vlen=" << Type->getVlen();
+
+ LLVM_DEBUG({
+ llvm::dbgs() << "Adding BTF type:\n"
+ << " Id = " << Types.size() << "\n"
+ << " Kind = " << Type->getKind() << "\n"
+ << " Name = " << findString(Type->NameOff) << "\n"
+ << " Record Size = " << Size << "\n";
+ });
+ Types.push_back(Type);
+ Pos += Size;
+ }
- StringsTable = Extractor.getData().substr(StrStart, StrLen);
return Error::success();
}
@@ -162,12 +273,24 @@ Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) {
(void)Extractor.getU32(C); // func_info_len
uint32_t LineInfoOff = Extractor.getU32(C);
uint32_t LineInfoLen = Extractor.getU32(C);
+ uint32_t RelocInfoOff = Extractor.getU32(C);
+ uint32_t RelocInfoLen = Extractor.getU32(C);
if (!C)
return Err(".BTF.ext", C);
- uint32_t LineInfoStart = HdrLen + LineInfoOff;
- uint32_t LineInfoEnd = LineInfoStart + LineInfoLen;
- if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd))
- return E;
+
+ if (LineInfoLen > 0 && Ctx.Opts.LoadLines) {
+ uint32_t LineInfoStart = HdrLen + LineInfoOff;
+ uint32_t LineInfoEnd = LineInfoStart + LineInfoLen;
+ if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd))
+ return E;
+ }
+
+ if (RelocInfoLen > 0 && Ctx.Opts.LoadRelocs) {
+ uint32_t RelocInfoStart = HdrLen + RelocInfoOff;
+ uint32_t RelocInfoEnd = RelocInfoStart + RelocInfoLen;
+ if (Error E = parseRelocInfo(Ctx, Extractor, RelocInfoStart, RelocInfoEnd))
+ return E;
+ }
return Error::success();
}
@@ -214,11 +337,52 @@ Error BTFParser::parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor,
return Error::success();
}
-Error BTFParser::parse(const ObjectFile &Obj) {
+Error BTFParser::parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor,
+ uint64_t RelocInfoStart,
+ uint64_t RelocInfoEnd) {
+ DataExtractor::Cursor C = DataExtractor::Cursor(RelocInfoStart);
+ uint32_t RecSize = Extractor.getU32(C);
+ if (!C)
+ return Err(".BTF.ext", C);
+ if (RecSize < 16)
+ return Err("unexpected .BTF.ext field reloc info record length: ")
+ << RecSize;
+ while (C && C.tell() < RelocInfoEnd) {
+ uint32_t SecNameOff = Extractor.getU32(C);
+ uint32_t NumInfo = Extractor.getU32(C);
+ StringRef SecName = findString(SecNameOff);
+ std::optional<SectionRef> Sec = Ctx.findSection(SecName);
+ BTFRelocVector &Relocs = SectionRelocs[Sec->getIndex()];
+ for (uint32_t I = 0; C && I < NumInfo; ++I) {
+ uint64_t RecStart = C.tell();
+ uint32_t InsnOff = Extractor.getU32(C);
+ uint32_t TypeID = Extractor.getU32(C);
+ uint32_t OffsetNameOff = Extractor.getU32(C);
+ uint32_t RelocKind = Extractor.getU32(C);
+ if (!C)
+ return Err(".BTF.ext", C);
+ Relocs.push_back({InsnOff, TypeID, OffsetNameOff, RelocKind});
+ C.seek(RecStart + RecSize);
+ }
+ llvm::stable_sort(
+ Relocs, [](const BTF::BPFFieldReloc &L, const BTF::BPFFieldReloc &R) {
+ return L.InsnOffset < R.InsnOffset;
+ });
+ }
+ if (!C)
+ return Err(".BTF.ext", C);
+
+ return Error::success();
+}
+
+Error BTFParser::parse(const ObjectFile &Obj, const ParseOptions &Opts) {
StringsTable = StringRef();
SectionLines.clear();
+ SectionRelocs.clear();
+ Types.clear();
+ TypesBuffer = OwningArrayRef<uint8_t>();
- ParseContext Ctx(Obj);
+ ParseContext Ctx(Obj, Opts);
std::optional<SectionRef> BTF;
std::optional<SectionRef> BTFExt;
for (SectionRef Sec : Obj.sections()) {
@@ -264,20 +428,430 @@ StringRef BTFParser::findString(uint32_t Offset) const {
return StringsTable.slice(Offset, StringsTable.find(0, Offset));
}
-const BTF::BPFLineInfo *
-BTFParser::findLineInfo(SectionedAddress Address) const {
- auto MaybeSecInfo = SectionLines.find(Address.SectionIndex);
- if (MaybeSecInfo == SectionLines.end())
+template <typename T>
+static const T *findInfo(const DenseMap<uint64_t, SmallVector<T, 0>> &SecMap,
+ SectionedAddress Address) {
+ auto MaybeSecInfo = SecMap.find(Address.SectionIndex);
+ if (MaybeSecInfo == SecMap.end())
return nullptr;
- const BTFLinesVector &SecInfo = MaybeSecInfo->second;
+ const SmallVector<T, 0> &SecInfo = MaybeSecInfo->second;
const uint64_t TargetOffset = Address.Address;
- BTFLinesVector::const_iterator LineInfo =
- llvm::partition_point(SecInfo, [=](const BTF::BPFLineInfo &Line) {
- return Line.InsnOffset < TargetOffset;
- });
- if (LineInfo == SecInfo.end() || LineInfo->InsnOffset != Address.Address)
+ typename SmallVector<T, 0>::const_iterator MaybeInfo = llvm::partition_point(
+ SecInfo, [=](const T &Entry) { return Entry.InsnOffset < TargetOffset; });
+ if (MaybeInfo == SecInfo.end() || MaybeInfo->InsnOffset != Address.Address)
return nullptr;
- return LineInfo;
+ return &*MaybeInfo;
+}
+
+const BTF::BPFLineInfo *
+BTFParser::findLineInfo(SectionedAddress Address) const {
+ return findInfo(SectionLines, Address);
+}
+
+const BTF::BPFFieldReloc *
+BTFParser::findFieldReloc(SectionedAddress Address) const {
+ return findInfo(SectionRelocs, Address);
+}
+
+const BTF::CommonType *BTFParser::findType(uint32_t Id) const {
+ if (Id < Types.size())
+ return Types[Id];
+ return nullptr;
+}
+
+enum RelocKindGroup {
+ RKG_FIELD,
+ RKG_TYPE,
+ RKG_ENUMVAL,
+ RKG_UNKNOWN,
+};
+
+static RelocKindGroup relocKindGroup(const BTF::BPFFieldReloc *Reloc) {
+ switch (Reloc->RelocKind) {
+ case BTF::FIELD_BYTE_OFFSET:
+ case BTF::FIELD_BYTE_SIZE:
+ case BTF::FIELD_EXISTENCE:
+ case BTF::FIELD_SIGNEDNESS:
+ case BTF::FIELD_LSHIFT_U64:
+ case BTF::FIELD_RSHIFT_U64:
+ return RKG_FIELD;
+ case BTF::BTF_TYPE_ID_LOCAL:
+ case BTF::BTF_TYPE_ID_REMOTE:
+ case BTF::TYPE_EXISTENCE:
+ case BTF::TYPE_MATCH:
+ case BTF::TYPE_SIZE:
+ return RKG_TYPE;
+ case BTF::ENUM_VALUE_EXISTENCE:
+ case BTF::ENUM_VALUE:
+ return RKG_ENUMVAL;
+ default:
+ return RKG_UNKNOWN;
+ }
+}
+
+static bool isMod(const BTF::CommonType *Type) {
+ switch (Type->getKind()) {
+ case BTF::BTF_KIND_VOLATILE:
+ case BTF::BTF_KIND_CONST:
+ case BTF::BTF_KIND_RESTRICT:
+ case BTF::BTF_KIND_TYPE_TAG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool printMod(const BTFParser &BTF, const BTF::CommonType *Type,
+ raw_ostream &Stream) {
+ switch (Type->getKind()) {
+ case BTF::BTF_KIND_CONST:
+ Stream << " const";
+ break;
+ case BTF::BTF_KIND_VOLATILE:
+ Stream << " volatile";
+ break;
+ case BTF::BTF_KIND_RESTRICT:
+ Stream << " restrict";
+ break;
+ case BTF::BTF_KIND_TYPE_TAG:
+ Stream << " type_tag(\"" << BTF.findString(Type->NameOff) << "\")";
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static const BTF::CommonType *skipModsAndTypedefs(const BTFParser &BTF,
+ const BTF::CommonType *Type) {
+ while (isMod(Type) || Type->getKind() == BTF::BTF_KIND_TYPEDEF) {
+ auto *Base = BTF.findType(Type->Type);
+ if (!Base)
+ break;
+ Type = Base;
+ }
+ return Type;
+}
+
+namespace {
+struct StrOrAnon {
+ const BTFParser &BTF;
+ uint32_t Offset;
+ uint32_t Idx;
+};
+
+static raw_ostream &operator<<(raw_ostream &Stream, const StrOrAnon &S) {
+ StringRef Str = S.BTF.findString(S.Offset);
+ if (Str.empty())
+ Stream << "<anon " << S.Idx << ">";
+ else
+ Stream << Str;
+ return Stream;
+}
+} // anonymous namespace
+
+static void relocKindName(uint32_t X, raw_ostream &Out) {
+ Out << "<";
+ switch (X) {
+ default:
+ Out << "reloc kind #" << X;
+ break;
+ case BTF::FIELD_BYTE_OFFSET:
+ Out << "byte_off";
+ break;
+ case BTF::FIELD_BYTE_SIZE:
+ Out << "byte_sz";
+ break;
+ case BTF::FIELD_EXISTENCE:
+ Out << "field_exists";
+ break;
+ case BTF::FIELD_SIGNEDNESS:
+ Out << "signed";
+ break;
+ case BTF::FIELD_LSHIFT_U64:
+ Out << "lshift_u64";
+ break;
+ case BTF::FIELD_RSHIFT_U64:
+ Out << "rshift_u64";
+ break;
+ case BTF::BTF_TYPE_ID_LOCAL:
+ Out << "local_type_id";
+ break;
+ case BTF::BTF_TYPE_ID_REMOTE:
+ Out << "target_type_id";
+ break;
+ case BTF::TYPE_EXISTENCE:
+ Out << "type_exists";
+ break;
+ case BTF::TYPE_MATCH:
+ Out << "type_matches";
+ break;
+ case BTF::TYPE_SIZE:
+ Out << "type_size";
+ break;
+ case BTF::ENUM_VALUE_EXISTENCE:
+ Out << "enumval_exists";
+ break;
+ case BTF::ENUM_VALUE:
+ Out << "enumval_value";
+ break;
+ }
+ Out << ">";
+}
+
+// Produces a human readable description of a CO-RE relocation.
+// Such relocations are generated by BPF backend, and processed
+// by libbpf's BPF program loader [1].
+//
+// Each relocation record has the following information:
+// - Relocation kind;
+// - BTF type ID;
+// - Access string offset in string table.
+//
+// There are different kinds of relocations, these kinds could be split
+// in three groups:
+// - load-time information about types (size, existence),
+// `BTFParser::symbolize()` output for such relocations uses the template:
+//
+// <relocation-kind> [<id>] <type-name>
+//
+// For example:
+// - "<type_exists> [7] struct foo"
+// - "<type_size> [7] struct foo"
+//
+// - load-time information about enums (literal existence, literal value),
+// `BTFParser::symbolize()` output for such relocations uses the template:
+//
+// <relocation-kind> [<id>] <type-name>::<literal-name> = <original-value>
+//
+// For example:
+// - "<enumval_exists> [5] enum foo::U = 1"
+// - "<enumval_value> [5] enum foo::V = 2"
+//
+// - load-time information about fields (e.g. field offset),
+// `BTFParser::symbolize()` output for such relocations uses the template:
+//
+// <relocation-kind> [<id>] \
+// <type-name>::[N].<field-1-name>...<field-M-name> \
+// (<access string>)
+//
+// For example:
+// - "<byte_off> [8] struct bar::[7].v (7:1)"
+// - "<field_exists> [8] struct bar::v (0:1)"
+//
+// If relocation description is not valid output follows the following pattern:
+//
+// <relocation-kind> <type-id>::<unprocessedaccess-string> <<error-msg>>
+//
+// For example:
+//
+// - "<type_sz> [42] '' <unknown type id: 42>"
+// - "<byte_off> [4] '0:' <field spec too short>"
+//
+// Additional examples could be found in unit tests, see
+// llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp.
+//
+// [1] https://www.kernel.org/doc/html/latest/bpf/libbpf/index.html
+void BTFParser::symbolize(const BTF::BPFFieldReloc *Reloc,
+ SmallVectorImpl<char> &Result) const {
+ raw_svector_ostream Stream(Result);
+ StringRef FullSpecStr = findString(Reloc->OffsetNameOff);
+ SmallVector<uint32_t, 8> RawSpec;
+
+ auto Fail = [&](auto Msg) {
+ Result.resize(0);
+ relocKindName(Reloc->RelocKind, Stream);
+ Stream << " [" << Reloc->TypeID << "] '" << FullSpecStr << "'"
+ << " <" << Msg << ">";
+ };
+
+ // Relocation access string follows pattern [0-9]+(:[0-9]+)*,
+ // e.g.: 12:22:3. Code below splits `SpecStr` by ':', parses
+ // numbers, and pushes them to `RawSpec`.
+ StringRef SpecStr = FullSpecStr;
+ while (SpecStr.size()) {
+ unsigned long long Val;
+ if (consumeUnsignedInteger(SpecStr, 10, Val))
+ return Fail("spec string is not a number");
+ RawSpec.push_back(Val);
+ if (SpecStr.empty())
+ break;
+ if (SpecStr[0] != ':')
+ return Fail(format("unexpected spec string delimiter: '%c'", SpecStr[0]));
+ SpecStr = SpecStr.substr(1);
+ }
+
+ // Print relocation kind to `Stream`.
+ relocKindName(Reloc->RelocKind, Stream);
+
+ uint32_t CurId = Reloc->TypeID;
+ const BTF::CommonType *Type = findType(CurId);
+ if (!Type)
+ return Fail(format("unknown type id: %d", CurId));
+
+ Stream << " [" << CurId << "]";
+
+ // `Type` might have modifiers, e.g. for type 'const int' the `Type`
+ // would refer to BTF type of kind BTF_KIND_CONST.
+ // Print all these modifiers to `Stream`.
+ for (uint32_t ChainLen = 0; printMod(*this, Type, Stream); ++ChainLen) {
+ if (ChainLen >= 32)
+ return Fail("modifiers chain is too long");
+
+ CurId = Type->Type;
+ const BTF::CommonType *NextType = findType(CurId);
+ if (!NextType)
+ return Fail(format("unknown type id: %d in modifiers chain", CurId));
+ Type = NextType;
+ }
+ // Print the type name to `Stream`.
+ if (CurId == 0) {
+ Stream << " void";
+ } else {
+ switch (Type->getKind()) {
+ case BTF::BTF_KIND_TYPEDEF:
+ Stream << " typedef";
+ break;
+ case BTF::BTF_KIND_STRUCT:
+ Stream << " struct";
+ break;
+ case BTF::BTF_KIND_UNION:
+ Stream << " union";
+ break;
+ case BTF::BTF_KIND_ENUM:
+ Stream << " enum";
+ break;
+ case BTF::BTF_KIND_ENUM64:
+ Stream << " enum";
+ break;
+ case BTF::BTF_KIND_FWD:
+ if (Type->Info & BTF::FWD_UNION_FLAG)
+ Stream << " fwd union";
+ else
+ Stream << " fwd struct";
+ break;
+ default:
+ break;
+ }
+ Stream << " " << StrOrAnon({*this, Type->NameOff, CurId});
+ }
+
+ RelocKindGroup Group = relocKindGroup(Reloc);
+ // Type-based relocations don't use access string but clang backend
+ // generates '0' and libbpf checks it's value, do the same here.
+ if (Group == RKG_TYPE) {
+ if (RawSpec.size() != 1 || RawSpec[0] != 0)
+ return Fail("unexpected type-based relocation spec: should be '0'");
+ return;
+ }
+
+ Stream << "::";
+
+ // For enum-based relocations access string is a single number,
+ // corresponding to the enum literal sequential number.
+ // E.g. for `enum E { U, V }`, relocation requesting value of `V`
+ // would look as follows:
+ // - kind: BTF::ENUM_VALUE
+ // - BTF id: id for `E`
+ // - access string: "1"
+ if (Group == RKG_ENUMVAL) {
+ Type = skipModsAndTypedefs(*this, Type);
+
+ if (RawSpec.size() != 1)
+ return Fail("unexpected enumval relocation spec size");
+
+ uint32_t NameOff;
+ uint64_t Val;
+ uint32_t Idx = RawSpec[0];
+ if (auto *T = dyn_cast<BTF::EnumType>(Type)) {
+ if (T->values().size() <= Idx)
+ return Fail(format("bad value index: %d", Idx));
+ const BTF::BTFEnum &E = T->values()[Idx];
+ NameOff = E.NameOff;
+ Val = E.Val;
+ } else if (auto *T = dyn_cast<BTF::Enum64Type>(Type)) {
+ if (T->values().size() <= Idx)
+ return Fail(format("bad value index: %d", Idx));
+ const BTF::BTFEnum64 &E = T->values()[Idx];
+ NameOff = E.NameOff;
+ Val = (uint64_t)E.Val_Hi32 << 32u | E.Val_Lo32;
+ } else {
+ return Fail(format("unexpected type kind for enum relocation: %d",
+ Type->getKind()));
+ }
+
+ Stream << StrOrAnon({*this, NameOff, Idx});
+ if (Type->Info & BTF::ENUM_SIGNED_FLAG)
+ Stream << " = " << (int64_t)Val;
+ else
+ Stream << " = " << (uint64_t)Val;
+ return;
+ }
+
+ // For type-based relocations access string is an array of numbers,
+ // which resemble index parameters for `getelementptr` LLVM IR instruction.
+ // E.g. for the following types:
+ //
+ // struct foo {
+ // int a;
+ // int b;
+ // };
+ // struct bar {
+ // int u;
+ // struct foo v[7];
+ // };
+ //
+ // Relocation requesting `offsetof(struct bar, v[2].b)` will have
+ // the following access string: 0:1:2:1
+ // ^ ^ ^ ^
+ // | | | |
+ // initial index | | field 'b' is a field #1
+ // | | (counting from 0)
+ // | array index #2
+ // field 'v' is a field #1
+ // (counting from 0)
+ if (Group == RKG_FIELD) {
+ if (RawSpec.size() < 1)
+ return Fail("field spec too short");
+
+ if (RawSpec[0] != 0)
+ Stream << "[" << RawSpec[0] << "]";
+ for (uint32_t I = 1; I < RawSpec.size(); ++I) {
+ Type = skipModsAndTypedefs(*this, Type);
+ uint32_t Idx = RawSpec[I];
+
+ if (auto *T = dyn_cast<BTF::StructType>(Type)) {
+ if (T->getVlen() <= Idx)
+ return Fail(
+ format("member index %d for spec sub-string %d is out of range",
+ Idx, I));
+
+ const BTF::BTFMember &Member = T->members()[Idx];
+ if (I != 1 || RawSpec[0] != 0)
+ Stream << ".";
+ Stream << StrOrAnon({*this, Member.NameOff, Idx});
+ Type = findType(Member.Type);
+ if (!Type)
+ return Fail(format("unknown member type id %d for spec sub-string %d",
+ Member.Type, I));
+ } else if (auto *T = dyn_cast<BTF::ArrayType>(Type)) {
+ Stream << "[" << Idx << "]";
+ Type = findType(T->getArray().ElemType);
+ if (!Type)
+ return Fail(
+ format("unknown element type id %d for spec sub-string %d",
+ T->getArray().ElemType, I));
+ } else {
+ return Fail(format("unexpected type kind %d for spec sub-string %d",
+ Type->getKind(), I));
+ }
+ }
+
+ Stream << " (" << FullSpecStr << ")";
+ return;
+ }
+
+ return Fail(format("unknown relocation kind: %d", Reloc->RelocKind));
}