Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/BTF/BTFParser.cpp
35269 views
//===- BTFParser.cpp ------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// BTFParser reads/interprets .BTF and .BTF.ext ELF sections.9// Refer to BTFParser.h for API description.10//11//===----------------------------------------------------------------------===//1213#include "llvm/DebugInfo/BTF/BTFParser.h"14#include "llvm/ADT/StringExtras.h"15#include "llvm/Support/Endian.h"16#include "llvm/Support/Errc.h"1718#define DEBUG_TYPE "debug-info-btf-parser"1920using namespace llvm;21using object::ObjectFile;22using object::SectionedAddress;23using object::SectionRef;2425const char BTFSectionName[] = ".BTF";26const char BTFExtSectionName[] = ".BTF.ext";2728// Utility class with API similar to raw_ostream but can be cast29// to Error, e.g.:30//31// Error foo(...) {32// ...33// if (Error E = bar(...))34// return Err("error while foo(): ") << E;35// ...36// }37//38namespace {39class Err {40std::string Buffer;41raw_string_ostream Stream;4243public:44Err(const char *InitialMsg) : Buffer(InitialMsg), Stream(Buffer) {}45Err(const char *SectionName, DataExtractor::Cursor &C)46: Buffer(), Stream(Buffer) {47*this << "error while reading " << SectionName48<< " section: " << C.takeError();49};5051template <typename T> Err &operator<<(T Val) {52Stream << Val;53return *this;54}5556Err &write_hex(unsigned long long Val) {57Stream.write_hex(Val);58return *this;59}6061Err &operator<<(Error Val) {62handleAllErrors(std::move(Val),63[=](ErrorInfoBase &Info) { Stream << Info.message(); });64return *this;65}6667operator Error() const {68return make_error<StringError>(Buffer, errc::invalid_argument);69}70};71} // anonymous namespace7273// ParseContext wraps information that is only necessary while parsing74// ObjectFile and can be discarded once parsing is done.75// Used by BTFParser::parse* auxiliary functions.76struct BTFParser::ParseContext {77const ObjectFile &Obj;78const ParseOptions &Opts;79// Map from ELF section name to SectionRef80DenseMap<StringRef, SectionRef> Sections;8182public:83ParseContext(const ObjectFile &Obj, const ParseOptions &Opts)84: Obj(Obj), Opts(Opts) {}8586Expected<DataExtractor> makeExtractor(SectionRef Sec) {87Expected<StringRef> Contents = Sec.getContents();88if (!Contents)89return Contents.takeError();90return DataExtractor(Contents.get(), Obj.isLittleEndian(),91Obj.getBytesInAddress());92}9394std::optional<SectionRef> findSection(StringRef Name) const {95auto It = Sections.find(Name);96if (It != Sections.end())97return It->second;98return std::nullopt;99}100};101102Error BTFParser::parseBTF(ParseContext &Ctx, SectionRef BTF) {103Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTF);104if (!MaybeExtractor)105return MaybeExtractor.takeError();106107DataExtractor &Extractor = MaybeExtractor.get();108DataExtractor::Cursor C = DataExtractor::Cursor(0);109uint16_t Magic = Extractor.getU16(C);110if (!C)111return Err(".BTF", C);112if (Magic != BTF::MAGIC)113return Err("invalid .BTF magic: ").write_hex(Magic);114uint8_t Version = Extractor.getU8(C);115if (!C)116return Err(".BTF", C);117if (Version != 1)118return Err("unsupported .BTF version: ") << (unsigned)Version;119(void)Extractor.getU8(C); // flags120uint32_t HdrLen = Extractor.getU32(C);121if (!C)122return Err(".BTF", C);123if (HdrLen < 8)124return Err("unexpected .BTF header length: ") << HdrLen;125uint32_t TypeOff = Extractor.getU32(C);126uint32_t TypeLen = Extractor.getU32(C);127uint32_t StrOff = Extractor.getU32(C);128uint32_t StrLen = Extractor.getU32(C);129uint32_t StrStart = HdrLen + StrOff;130uint32_t StrEnd = StrStart + StrLen;131uint32_t TypesInfoStart = HdrLen + TypeOff;132uint32_t TypesInfoEnd = TypesInfoStart + TypeLen;133uint32_t BytesExpected = std::max(StrEnd, TypesInfoEnd);134if (!C)135return Err(".BTF", C);136if (Extractor.getData().size() < BytesExpected)137return Err("invalid .BTF section size, expecting at-least ")138<< BytesExpected << " bytes";139140StringsTable = Extractor.getData().slice(StrStart, StrEnd);141142if (TypeLen > 0 && Ctx.Opts.LoadTypes) {143StringRef RawData = Extractor.getData().slice(TypesInfoStart, TypesInfoEnd);144if (Error E = parseTypesInfo(Ctx, TypesInfoStart, RawData))145return E;146}147148return Error::success();149}150151// Compute record size for each BTF::CommonType sub-type152// (including entries in the tail position).153static size_t byteSize(BTF::CommonType *Type) {154size_t Size = sizeof(BTF::CommonType);155switch (Type->getKind()) {156case BTF::BTF_KIND_INT:157Size += sizeof(uint32_t);158break;159case BTF::BTF_KIND_ARRAY:160Size += sizeof(BTF::BTFArray);161break;162case BTF::BTF_KIND_VAR:163Size += sizeof(uint32_t);164break;165case BTF::BTF_KIND_DECL_TAG:166Size += sizeof(uint32_t);167break;168case BTF::BTF_KIND_STRUCT:169case BTF::BTF_KIND_UNION:170Size += sizeof(BTF::BTFMember) * Type->getVlen();171break;172case BTF::BTF_KIND_ENUM:173Size += sizeof(BTF::BTFEnum) * Type->getVlen();174break;175case BTF::BTF_KIND_ENUM64:176Size += sizeof(BTF::BTFEnum64) * Type->getVlen();177break;178case BTF::BTF_KIND_FUNC_PROTO:179Size += sizeof(BTF::BTFParam) * Type->getVlen();180break;181case BTF::BTF_KIND_DATASEC:182Size += sizeof(BTF::BTFDataSec) * Type->getVlen();183break;184}185return Size;186}187188// Guard value for voids, simplifies code a bit, but NameOff is not189// actually valid.190const BTF::CommonType VoidTypeInst = {0, BTF::BTF_KIND_UNKN << 24, {0}};191192// Type information "parsing" is very primitive:193// - The `RawData` is copied to a buffer owned by `BTFParser` instance.194// - The buffer is treated as an array of `uint32_t` values, each value195// is swapped to use native endianness. This is possible, because196// according to BTF spec all buffer elements are structures comprised197// of `uint32_t` fields.198// - `BTFParser::Types` vector is filled with pointers to buffer199// elements, using `byteSize()` function to slice the buffer at type200// record boundaries.201// - If at some point a type definition with incorrect size (logical size202// exceeding buffer boundaries) is reached it is not added to the203// `BTFParser::Types` vector and the process stops.204Error BTFParser::parseTypesInfo(ParseContext &Ctx, uint64_t TypesInfoStart,205StringRef RawData) {206using support::endian::byte_swap;207208TypesBuffer = OwningArrayRef<uint8_t>(arrayRefFromStringRef(RawData));209// Switch endianness if necessary.210endianness Endianness = Ctx.Obj.isLittleEndian() ? llvm::endianness::little211: llvm::endianness::big;212uint32_t *TypesBuffer32 = (uint32_t *)TypesBuffer.data();213for (uint64_t I = 0; I < TypesBuffer.size() / 4; ++I)214TypesBuffer32[I] = byte_swap(TypesBuffer32[I], Endianness);215216// The type id 0 is reserved for void type.217Types.push_back(&VoidTypeInst);218219uint64_t Pos = 0;220while (Pos < RawData.size()) {221uint64_t BytesLeft = RawData.size() - Pos;222uint64_t Offset = TypesInfoStart + Pos;223BTF::CommonType *Type = (BTF::CommonType *)&TypesBuffer[Pos];224if (BytesLeft < sizeof(*Type))225return Err("incomplete type definition in .BTF section:")226<< " offset " << Offset << ", index " << Types.size();227228uint64_t Size = byteSize(Type);229if (BytesLeft < Size)230return Err("incomplete type definition in .BTF section:")231<< " offset=" << Offset << ", index=" << Types.size()232<< ", vlen=" << Type->getVlen();233234LLVM_DEBUG({235llvm::dbgs() << "Adding BTF type:\n"236<< " Id = " << Types.size() << "\n"237<< " Kind = " << Type->getKind() << "\n"238<< " Name = " << findString(Type->NameOff) << "\n"239<< " Record Size = " << Size << "\n";240});241Types.push_back(Type);242Pos += Size;243}244245return Error::success();246}247248Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) {249Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTFExt);250if (!MaybeExtractor)251return MaybeExtractor.takeError();252253DataExtractor &Extractor = MaybeExtractor.get();254DataExtractor::Cursor C = DataExtractor::Cursor(0);255uint16_t Magic = Extractor.getU16(C);256if (!C)257return Err(".BTF.ext", C);258if (Magic != BTF::MAGIC)259return Err("invalid .BTF.ext magic: ").write_hex(Magic);260uint8_t Version = Extractor.getU8(C);261if (!C)262return Err(".BTF", C);263if (Version != 1)264return Err("unsupported .BTF.ext version: ") << (unsigned)Version;265(void)Extractor.getU8(C); // flags266uint32_t HdrLen = Extractor.getU32(C);267if (!C)268return Err(".BTF.ext", C);269if (HdrLen < 8)270return Err("unexpected .BTF.ext header length: ") << HdrLen;271(void)Extractor.getU32(C); // func_info_off272(void)Extractor.getU32(C); // func_info_len273uint32_t LineInfoOff = Extractor.getU32(C);274uint32_t LineInfoLen = Extractor.getU32(C);275uint32_t RelocInfoOff = Extractor.getU32(C);276uint32_t RelocInfoLen = Extractor.getU32(C);277if (!C)278return Err(".BTF.ext", C);279280if (LineInfoLen > 0 && Ctx.Opts.LoadLines) {281uint32_t LineInfoStart = HdrLen + LineInfoOff;282uint32_t LineInfoEnd = LineInfoStart + LineInfoLen;283if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd))284return E;285}286287if (RelocInfoLen > 0 && Ctx.Opts.LoadRelocs) {288uint32_t RelocInfoStart = HdrLen + RelocInfoOff;289uint32_t RelocInfoEnd = RelocInfoStart + RelocInfoLen;290if (Error E = parseRelocInfo(Ctx, Extractor, RelocInfoStart, RelocInfoEnd))291return E;292}293294return Error::success();295}296297Error BTFParser::parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor,298uint64_t LineInfoStart, uint64_t LineInfoEnd) {299DataExtractor::Cursor C = DataExtractor::Cursor(LineInfoStart);300uint32_t RecSize = Extractor.getU32(C);301if (!C)302return Err(".BTF.ext", C);303if (RecSize < 16)304return Err("unexpected .BTF.ext line info record length: ") << RecSize;305306while (C && C.tell() < LineInfoEnd) {307uint32_t SecNameOff = Extractor.getU32(C);308uint32_t NumInfo = Extractor.getU32(C);309StringRef SecName = findString(SecNameOff);310std::optional<SectionRef> Sec = Ctx.findSection(SecName);311if (!C)312return Err(".BTF.ext", C);313if (!Sec)314return Err("") << "can't find section '" << SecName315<< "' while parsing .BTF.ext line info";316BTFLinesVector &Lines = SectionLines[Sec->getIndex()];317for (uint32_t I = 0; C && I < NumInfo; ++I) {318uint64_t RecStart = C.tell();319uint32_t InsnOff = Extractor.getU32(C);320uint32_t FileNameOff = Extractor.getU32(C);321uint32_t LineOff = Extractor.getU32(C);322uint32_t LineCol = Extractor.getU32(C);323if (!C)324return Err(".BTF.ext", C);325Lines.push_back({InsnOff, FileNameOff, LineOff, LineCol});326C.seek(RecStart + RecSize);327}328llvm::stable_sort(Lines,329[](const BTF::BPFLineInfo &L, const BTF::BPFLineInfo &R) {330return L.InsnOffset < R.InsnOffset;331});332}333if (!C)334return Err(".BTF.ext", C);335336return Error::success();337}338339Error BTFParser::parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor,340uint64_t RelocInfoStart,341uint64_t RelocInfoEnd) {342DataExtractor::Cursor C = DataExtractor::Cursor(RelocInfoStart);343uint32_t RecSize = Extractor.getU32(C);344if (!C)345return Err(".BTF.ext", C);346if (RecSize < 16)347return Err("unexpected .BTF.ext field reloc info record length: ")348<< RecSize;349while (C && C.tell() < RelocInfoEnd) {350uint32_t SecNameOff = Extractor.getU32(C);351uint32_t NumInfo = Extractor.getU32(C);352StringRef SecName = findString(SecNameOff);353std::optional<SectionRef> Sec = Ctx.findSection(SecName);354BTFRelocVector &Relocs = SectionRelocs[Sec->getIndex()];355for (uint32_t I = 0; C && I < NumInfo; ++I) {356uint64_t RecStart = C.tell();357uint32_t InsnOff = Extractor.getU32(C);358uint32_t TypeID = Extractor.getU32(C);359uint32_t OffsetNameOff = Extractor.getU32(C);360uint32_t RelocKind = Extractor.getU32(C);361if (!C)362return Err(".BTF.ext", C);363Relocs.push_back({InsnOff, TypeID, OffsetNameOff, RelocKind});364C.seek(RecStart + RecSize);365}366llvm::stable_sort(367Relocs, [](const BTF::BPFFieldReloc &L, const BTF::BPFFieldReloc &R) {368return L.InsnOffset < R.InsnOffset;369});370}371if (!C)372return Err(".BTF.ext", C);373374return Error::success();375}376377Error BTFParser::parse(const ObjectFile &Obj, const ParseOptions &Opts) {378StringsTable = StringRef();379SectionLines.clear();380SectionRelocs.clear();381Types.clear();382TypesBuffer = OwningArrayRef<uint8_t>();383384ParseContext Ctx(Obj, Opts);385std::optional<SectionRef> BTF;386std::optional<SectionRef> BTFExt;387for (SectionRef Sec : Obj.sections()) {388Expected<StringRef> MaybeName = Sec.getName();389if (!MaybeName)390return Err("error while reading section name: ") << MaybeName.takeError();391Ctx.Sections[*MaybeName] = Sec;392if (*MaybeName == BTFSectionName)393BTF = Sec;394if (*MaybeName == BTFExtSectionName)395BTFExt = Sec;396}397if (!BTF)398return Err("can't find .BTF section");399if (!BTFExt)400return Err("can't find .BTF.ext section");401if (Error E = parseBTF(Ctx, *BTF))402return E;403if (Error E = parseBTFExt(Ctx, *BTFExt))404return E;405406return Error::success();407}408409bool BTFParser::hasBTFSections(const ObjectFile &Obj) {410bool HasBTF = false;411bool HasBTFExt = false;412for (SectionRef Sec : Obj.sections()) {413Expected<StringRef> Name = Sec.getName();414if (Error E = Name.takeError()) {415logAllUnhandledErrors(std::move(E), errs());416continue;417}418HasBTF |= *Name == BTFSectionName;419HasBTFExt |= *Name == BTFExtSectionName;420if (HasBTF && HasBTFExt)421return true;422}423return false;424}425426StringRef BTFParser::findString(uint32_t Offset) const {427return StringsTable.slice(Offset, StringsTable.find(0, Offset));428}429430template <typename T>431static const T *findInfo(const DenseMap<uint64_t, SmallVector<T, 0>> &SecMap,432SectionedAddress Address) {433auto MaybeSecInfo = SecMap.find(Address.SectionIndex);434if (MaybeSecInfo == SecMap.end())435return nullptr;436437const SmallVector<T, 0> &SecInfo = MaybeSecInfo->second;438const uint64_t TargetOffset = Address.Address;439typename SmallVector<T, 0>::const_iterator MaybeInfo = llvm::partition_point(440SecInfo, [=](const T &Entry) { return Entry.InsnOffset < TargetOffset; });441if (MaybeInfo == SecInfo.end() || MaybeInfo->InsnOffset != Address.Address)442return nullptr;443444return &*MaybeInfo;445}446447const BTF::BPFLineInfo *448BTFParser::findLineInfo(SectionedAddress Address) const {449return findInfo(SectionLines, Address);450}451452const BTF::BPFFieldReloc *453BTFParser::findFieldReloc(SectionedAddress Address) const {454return findInfo(SectionRelocs, Address);455}456457const BTF::CommonType *BTFParser::findType(uint32_t Id) const {458if (Id < Types.size())459return Types[Id];460return nullptr;461}462463enum RelocKindGroup {464RKG_FIELD,465RKG_TYPE,466RKG_ENUMVAL,467RKG_UNKNOWN,468};469470static RelocKindGroup relocKindGroup(const BTF::BPFFieldReloc *Reloc) {471switch (Reloc->RelocKind) {472case BTF::FIELD_BYTE_OFFSET:473case BTF::FIELD_BYTE_SIZE:474case BTF::FIELD_EXISTENCE:475case BTF::FIELD_SIGNEDNESS:476case BTF::FIELD_LSHIFT_U64:477case BTF::FIELD_RSHIFT_U64:478return RKG_FIELD;479case BTF::BTF_TYPE_ID_LOCAL:480case BTF::BTF_TYPE_ID_REMOTE:481case BTF::TYPE_EXISTENCE:482case BTF::TYPE_MATCH:483case BTF::TYPE_SIZE:484return RKG_TYPE;485case BTF::ENUM_VALUE_EXISTENCE:486case BTF::ENUM_VALUE:487return RKG_ENUMVAL;488default:489return RKG_UNKNOWN;490}491}492493static bool isMod(const BTF::CommonType *Type) {494switch (Type->getKind()) {495case BTF::BTF_KIND_VOLATILE:496case BTF::BTF_KIND_CONST:497case BTF::BTF_KIND_RESTRICT:498case BTF::BTF_KIND_TYPE_TAG:499return true;500default:501return false;502}503}504505static bool printMod(const BTFParser &BTF, const BTF::CommonType *Type,506raw_ostream &Stream) {507switch (Type->getKind()) {508case BTF::BTF_KIND_CONST:509Stream << " const";510break;511case BTF::BTF_KIND_VOLATILE:512Stream << " volatile";513break;514case BTF::BTF_KIND_RESTRICT:515Stream << " restrict";516break;517case BTF::BTF_KIND_TYPE_TAG:518Stream << " type_tag(\"" << BTF.findString(Type->NameOff) << "\")";519break;520default:521return false;522}523return true;524}525526static const BTF::CommonType *skipModsAndTypedefs(const BTFParser &BTF,527const BTF::CommonType *Type) {528while (isMod(Type) || Type->getKind() == BTF::BTF_KIND_TYPEDEF) {529auto *Base = BTF.findType(Type->Type);530if (!Base)531break;532Type = Base;533}534return Type;535}536537namespace {538struct StrOrAnon {539const BTFParser &BTF;540uint32_t Offset;541uint32_t Idx;542};543544static raw_ostream &operator<<(raw_ostream &Stream, const StrOrAnon &S) {545StringRef Str = S.BTF.findString(S.Offset);546if (Str.empty())547Stream << "<anon " << S.Idx << ">";548else549Stream << Str;550return Stream;551}552} // anonymous namespace553554static void relocKindName(uint32_t X, raw_ostream &Out) {555Out << "<";556switch (X) {557default:558Out << "reloc kind #" << X;559break;560case BTF::FIELD_BYTE_OFFSET:561Out << "byte_off";562break;563case BTF::FIELD_BYTE_SIZE:564Out << "byte_sz";565break;566case BTF::FIELD_EXISTENCE:567Out << "field_exists";568break;569case BTF::FIELD_SIGNEDNESS:570Out << "signed";571break;572case BTF::FIELD_LSHIFT_U64:573Out << "lshift_u64";574break;575case BTF::FIELD_RSHIFT_U64:576Out << "rshift_u64";577break;578case BTF::BTF_TYPE_ID_LOCAL:579Out << "local_type_id";580break;581case BTF::BTF_TYPE_ID_REMOTE:582Out << "target_type_id";583break;584case BTF::TYPE_EXISTENCE:585Out << "type_exists";586break;587case BTF::TYPE_MATCH:588Out << "type_matches";589break;590case BTF::TYPE_SIZE:591Out << "type_size";592break;593case BTF::ENUM_VALUE_EXISTENCE:594Out << "enumval_exists";595break;596case BTF::ENUM_VALUE:597Out << "enumval_value";598break;599}600Out << ">";601}602603// Produces a human readable description of a CO-RE relocation.604// Such relocations are generated by BPF backend, and processed605// by libbpf's BPF program loader [1].606//607// Each relocation record has the following information:608// - Relocation kind;609// - BTF type ID;610// - Access string offset in string table.611//612// There are different kinds of relocations, these kinds could be split613// in three groups:614// - load-time information about types (size, existence),615// `BTFParser::symbolize()` output for such relocations uses the template:616//617// <relocation-kind> [<id>] <type-name>618//619// For example:620// - "<type_exists> [7] struct foo"621// - "<type_size> [7] struct foo"622//623// - load-time information about enums (literal existence, literal value),624// `BTFParser::symbolize()` output for such relocations uses the template:625//626// <relocation-kind> [<id>] <type-name>::<literal-name> = <original-value>627//628// For example:629// - "<enumval_exists> [5] enum foo::U = 1"630// - "<enumval_value> [5] enum foo::V = 2"631//632// - load-time information about fields (e.g. field offset),633// `BTFParser::symbolize()` output for such relocations uses the template:634//635// <relocation-kind> [<id>] \636// <type-name>::[N].<field-1-name>...<field-M-name> \637// (<access string>)638//639// For example:640// - "<byte_off> [8] struct bar::[7].v (7:1)"641// - "<field_exists> [8] struct bar::v (0:1)"642//643// If relocation description is not valid output follows the following pattern:644//645// <relocation-kind> <type-id>::<unprocessedaccess-string> <<error-msg>>646//647// For example:648//649// - "<type_sz> [42] '' <unknown type id: 42>"650// - "<byte_off> [4] '0:' <field spec too short>"651//652// Additional examples could be found in unit tests, see653// llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp.654//655// [1] https://www.kernel.org/doc/html/latest/bpf/libbpf/index.html656void BTFParser::symbolize(const BTF::BPFFieldReloc *Reloc,657SmallVectorImpl<char> &Result) const {658raw_svector_ostream Stream(Result);659StringRef FullSpecStr = findString(Reloc->OffsetNameOff);660SmallVector<uint32_t, 8> RawSpec;661662auto Fail = [&](auto Msg) {663Result.resize(0);664relocKindName(Reloc->RelocKind, Stream);665Stream << " [" << Reloc->TypeID << "] '" << FullSpecStr << "'"666<< " <" << Msg << ">";667};668669// Relocation access string follows pattern [0-9]+(:[0-9]+)*,670// e.g.: 12:22:3. Code below splits `SpecStr` by ':', parses671// numbers, and pushes them to `RawSpec`.672StringRef SpecStr = FullSpecStr;673while (SpecStr.size()) {674unsigned long long Val;675if (consumeUnsignedInteger(SpecStr, 10, Val))676return Fail("spec string is not a number");677RawSpec.push_back(Val);678if (SpecStr.empty())679break;680if (SpecStr[0] != ':')681return Fail(format("unexpected spec string delimiter: '%c'", SpecStr[0]));682SpecStr = SpecStr.substr(1);683}684685// Print relocation kind to `Stream`.686relocKindName(Reloc->RelocKind, Stream);687688uint32_t CurId = Reloc->TypeID;689const BTF::CommonType *Type = findType(CurId);690if (!Type)691return Fail(format("unknown type id: %d", CurId));692693Stream << " [" << CurId << "]";694695// `Type` might have modifiers, e.g. for type 'const int' the `Type`696// would refer to BTF type of kind BTF_KIND_CONST.697// Print all these modifiers to `Stream`.698for (uint32_t ChainLen = 0; printMod(*this, Type, Stream); ++ChainLen) {699if (ChainLen >= 32)700return Fail("modifiers chain is too long");701702CurId = Type->Type;703const BTF::CommonType *NextType = findType(CurId);704if (!NextType)705return Fail(format("unknown type id: %d in modifiers chain", CurId));706Type = NextType;707}708// Print the type name to `Stream`.709if (CurId == 0) {710Stream << " void";711} else {712switch (Type->getKind()) {713case BTF::BTF_KIND_TYPEDEF:714Stream << " typedef";715break;716case BTF::BTF_KIND_STRUCT:717Stream << " struct";718break;719case BTF::BTF_KIND_UNION:720Stream << " union";721break;722case BTF::BTF_KIND_ENUM:723Stream << " enum";724break;725case BTF::BTF_KIND_ENUM64:726Stream << " enum";727break;728case BTF::BTF_KIND_FWD:729if (Type->Info & BTF::FWD_UNION_FLAG)730Stream << " fwd union";731else732Stream << " fwd struct";733break;734default:735break;736}737Stream << " " << StrOrAnon({*this, Type->NameOff, CurId});738}739740RelocKindGroup Group = relocKindGroup(Reloc);741// Type-based relocations don't use access string but clang backend742// generates '0' and libbpf checks it's value, do the same here.743if (Group == RKG_TYPE) {744if (RawSpec.size() != 1 || RawSpec[0] != 0)745return Fail("unexpected type-based relocation spec: should be '0'");746return;747}748749Stream << "::";750751// For enum-based relocations access string is a single number,752// corresponding to the enum literal sequential number.753// E.g. for `enum E { U, V }`, relocation requesting value of `V`754// would look as follows:755// - kind: BTF::ENUM_VALUE756// - BTF id: id for `E`757// - access string: "1"758if (Group == RKG_ENUMVAL) {759Type = skipModsAndTypedefs(*this, Type);760761if (RawSpec.size() != 1)762return Fail("unexpected enumval relocation spec size");763764uint32_t NameOff;765uint64_t Val;766uint32_t Idx = RawSpec[0];767if (auto *T = dyn_cast<BTF::EnumType>(Type)) {768if (T->values().size() <= Idx)769return Fail(format("bad value index: %d", Idx));770const BTF::BTFEnum &E = T->values()[Idx];771NameOff = E.NameOff;772Val = E.Val;773} else if (auto *T = dyn_cast<BTF::Enum64Type>(Type)) {774if (T->values().size() <= Idx)775return Fail(format("bad value index: %d", Idx));776const BTF::BTFEnum64 &E = T->values()[Idx];777NameOff = E.NameOff;778Val = (uint64_t)E.Val_Hi32 << 32u | E.Val_Lo32;779} else {780return Fail(format("unexpected type kind for enum relocation: %d",781Type->getKind()));782}783784Stream << StrOrAnon({*this, NameOff, Idx});785if (Type->Info & BTF::ENUM_SIGNED_FLAG)786Stream << " = " << (int64_t)Val;787else788Stream << " = " << (uint64_t)Val;789return;790}791792// For type-based relocations access string is an array of numbers,793// which resemble index parameters for `getelementptr` LLVM IR instruction.794// E.g. for the following types:795//796// struct foo {797// int a;798// int b;799// };800// struct bar {801// int u;802// struct foo v[7];803// };804//805// Relocation requesting `offsetof(struct bar, v[2].b)` will have806// the following access string: 0:1:2:1807// ^ ^ ^ ^808// | | | |809// initial index | | field 'b' is a field #1810// | | (counting from 0)811// | array index #2812// field 'v' is a field #1813// (counting from 0)814if (Group == RKG_FIELD) {815if (RawSpec.size() < 1)816return Fail("field spec too short");817818if (RawSpec[0] != 0)819Stream << "[" << RawSpec[0] << "]";820for (uint32_t I = 1; I < RawSpec.size(); ++I) {821Type = skipModsAndTypedefs(*this, Type);822uint32_t Idx = RawSpec[I];823824if (auto *T = dyn_cast<BTF::StructType>(Type)) {825if (T->getVlen() <= Idx)826return Fail(827format("member index %d for spec sub-string %d is out of range",828Idx, I));829830const BTF::BTFMember &Member = T->members()[Idx];831if (I != 1 || RawSpec[0] != 0)832Stream << ".";833Stream << StrOrAnon({*this, Member.NameOff, Idx});834Type = findType(Member.Type);835if (!Type)836return Fail(format("unknown member type id %d for spec sub-string %d",837Member.Type, I));838} else if (auto *T = dyn_cast<BTF::ArrayType>(Type)) {839Stream << "[" << Idx << "]";840Type = findType(T->getArray().ElemType);841if (!Type)842return Fail(843format("unknown element type id %d for spec sub-string %d",844T->getArray().ElemType, I));845} else {846return Fail(format("unexpected type kind %d for spec sub-string %d",847Type->getKind(), I));848}849}850851Stream << " (" << FullSpecStr << ")";852return;853}854855return Fail(format("unknown relocation kind: %d", Reloc->RelocKind));856}857858859