858 lines · cpp
1//===- BTFParser.cpp ------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// BTFParser reads/interprets .BTF and .BTF.ext ELF sections.10// Refer to BTFParser.h for API description.11//12//===----------------------------------------------------------------------===//13 14#include "llvm/DebugInfo/BTF/BTFParser.h"15#include "llvm/ADT/StringExtras.h"16#include "llvm/Support/Endian.h"17#include "llvm/Support/Errc.h"18 19#define DEBUG_TYPE "debug-info-btf-parser"20 21using namespace llvm;22using object::ObjectFile;23using object::SectionedAddress;24using object::SectionRef;25 26const char BTFSectionName[] = ".BTF";27const char BTFExtSectionName[] = ".BTF.ext";28 29// Utility class with API similar to raw_ostream but can be cast30// to Error, e.g.:31//32// Error foo(...) {33// ...34// if (Error E = bar(...))35// return Err("error while foo(): ") << E;36// ...37// }38//39namespace {40class Err {41 std::string Buffer;42 raw_string_ostream Stream;43 44public:45 Err(const char *InitialMsg) : Buffer(InitialMsg), Stream(Buffer) {}46 Err(const char *SectionName, DataExtractor::Cursor &C)47 : Buffer(), Stream(Buffer) {48 *this << "error while reading " << SectionName49 << " section: " << C.takeError();50 };51 52 template <typename T> Err &operator<<(T Val) {53 Stream << Val;54 return *this;55 }56 57 Err &write_hex(unsigned long long Val) {58 Stream.write_hex(Val);59 return *this;60 }61 62 Err &operator<<(Error Val) {63 handleAllErrors(std::move(Val),64 [=](ErrorInfoBase &Info) { Stream << Info.message(); });65 return *this;66 }67 68 operator Error() const {69 return make_error<StringError>(Buffer, errc::invalid_argument);70 }71};72} // anonymous namespace73 74// ParseContext wraps information that is only necessary while parsing75// ObjectFile and can be discarded once parsing is done.76// Used by BTFParser::parse* auxiliary functions.77struct BTFParser::ParseContext {78 const ObjectFile &Obj;79 const ParseOptions &Opts;80 // Map from ELF section name to SectionRef81 DenseMap<StringRef, SectionRef> Sections;82 83public:84 ParseContext(const ObjectFile &Obj, const ParseOptions &Opts)85 : Obj(Obj), Opts(Opts) {}86 87 Expected<DataExtractor> makeExtractor(SectionRef Sec) {88 Expected<StringRef> Contents = Sec.getContents();89 if (!Contents)90 return Contents.takeError();91 return DataExtractor(Contents.get(), Obj.isLittleEndian(),92 Obj.getBytesInAddress());93 }94 95 std::optional<SectionRef> findSection(StringRef Name) const {96 auto It = Sections.find(Name);97 if (It != Sections.end())98 return It->second;99 return std::nullopt;100 }101};102 103Error BTFParser::parseBTF(ParseContext &Ctx, SectionRef BTF) {104 Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTF);105 if (!MaybeExtractor)106 return MaybeExtractor.takeError();107 108 DataExtractor &Extractor = MaybeExtractor.get();109 DataExtractor::Cursor C = DataExtractor::Cursor(0);110 uint16_t Magic = Extractor.getU16(C);111 if (!C)112 return Err(".BTF", C);113 if (Magic != BTF::MAGIC)114 return Err("invalid .BTF magic: ").write_hex(Magic);115 uint8_t Version = Extractor.getU8(C);116 if (!C)117 return Err(".BTF", C);118 if (Version != 1)119 return Err("unsupported .BTF version: ") << (unsigned)Version;120 (void)Extractor.getU8(C); // flags121 uint32_t HdrLen = Extractor.getU32(C);122 if (!C)123 return Err(".BTF", C);124 if (HdrLen < 8)125 return Err("unexpected .BTF header length: ") << HdrLen;126 uint32_t TypeOff = Extractor.getU32(C);127 uint32_t TypeLen = Extractor.getU32(C);128 uint32_t StrOff = Extractor.getU32(C);129 uint32_t StrLen = Extractor.getU32(C);130 uint32_t StrStart = HdrLen + StrOff;131 uint32_t StrEnd = StrStart + StrLen;132 uint32_t TypesInfoStart = HdrLen + TypeOff;133 uint32_t TypesInfoEnd = TypesInfoStart + TypeLen;134 uint32_t BytesExpected = std::max(StrEnd, TypesInfoEnd);135 if (!C)136 return Err(".BTF", C);137 if (Extractor.getData().size() < BytesExpected)138 return Err("invalid .BTF section size, expecting at-least ")139 << BytesExpected << " bytes";140 141 StringsTable = Extractor.getData().slice(StrStart, StrEnd);142 143 if (TypeLen > 0 && Ctx.Opts.LoadTypes) {144 StringRef RawData = Extractor.getData().slice(TypesInfoStart, TypesInfoEnd);145 if (Error E = parseTypesInfo(Ctx, TypesInfoStart, RawData))146 return E;147 }148 149 return Error::success();150}151 152// Compute record size for each BTF::CommonType sub-type153// (including entries in the tail position).154static size_t byteSize(BTF::CommonType *Type) {155 size_t Size = sizeof(BTF::CommonType);156 switch (Type->getKind()) {157 case BTF::BTF_KIND_INT:158 Size += sizeof(uint32_t);159 break;160 case BTF::BTF_KIND_ARRAY:161 Size += sizeof(BTF::BTFArray);162 break;163 case BTF::BTF_KIND_VAR:164 Size += sizeof(uint32_t);165 break;166 case BTF::BTF_KIND_DECL_TAG:167 Size += sizeof(uint32_t);168 break;169 case BTF::BTF_KIND_STRUCT:170 case BTF::BTF_KIND_UNION:171 Size += sizeof(BTF::BTFMember) * Type->getVlen();172 break;173 case BTF::BTF_KIND_ENUM:174 Size += sizeof(BTF::BTFEnum) * Type->getVlen();175 break;176 case BTF::BTF_KIND_ENUM64:177 Size += sizeof(BTF::BTFEnum64) * Type->getVlen();178 break;179 case BTF::BTF_KIND_FUNC_PROTO:180 Size += sizeof(BTF::BTFParam) * Type->getVlen();181 break;182 case BTF::BTF_KIND_DATASEC:183 Size += sizeof(BTF::BTFDataSec) * Type->getVlen();184 break;185 }186 return Size;187}188 189// Guard value for voids, simplifies code a bit, but NameOff is not190// actually valid.191const BTF::CommonType VoidTypeInst = {0, BTF::BTF_KIND_UNKN << 24, {0}};192 193// Type information "parsing" is very primitive:194// - The `RawData` is copied to a buffer owned by `BTFParser` instance.195// - The buffer is treated as an array of `uint32_t` values, each value196// is swapped to use native endianness. This is possible, because197// according to BTF spec all buffer elements are structures comprised198// of `uint32_t` fields.199// - `BTFParser::Types` vector is filled with pointers to buffer200// elements, using `byteSize()` function to slice the buffer at type201// record boundaries.202// - If at some point a type definition with incorrect size (logical size203// exceeding buffer boundaries) is reached it is not added to the204// `BTFParser::Types` vector and the process stops.205Error BTFParser::parseTypesInfo(ParseContext &Ctx, uint64_t TypesInfoStart,206 StringRef RawData) {207 using support::endian::byte_swap;208 209 TypesBuffer = OwningArrayRef<uint8_t>(arrayRefFromStringRef(RawData));210 // Switch endianness if necessary.211 endianness Endianness = Ctx.Obj.isLittleEndian() ? llvm::endianness::little212 : llvm::endianness::big;213 uint32_t *TypesBuffer32 = (uint32_t *)TypesBuffer.data();214 for (uint64_t I = 0; I < TypesBuffer.size() / 4; ++I)215 TypesBuffer32[I] = byte_swap(TypesBuffer32[I], Endianness);216 217 // The type id 0 is reserved for void type.218 Types.push_back(&VoidTypeInst);219 220 uint64_t Pos = 0;221 while (Pos < RawData.size()) {222 uint64_t BytesLeft = RawData.size() - Pos;223 uint64_t Offset = TypesInfoStart + Pos;224 BTF::CommonType *Type = (BTF::CommonType *)&TypesBuffer[Pos];225 if (BytesLeft < sizeof(*Type))226 return Err("incomplete type definition in .BTF section:")227 << " offset " << Offset << ", index " << Types.size();228 229 uint64_t Size = byteSize(Type);230 if (BytesLeft < Size)231 return Err("incomplete type definition in .BTF section:")232 << " offset=" << Offset << ", index=" << Types.size()233 << ", vlen=" << Type->getVlen();234 235 LLVM_DEBUG({236 llvm::dbgs() << "Adding BTF type:\n"237 << " Id = " << Types.size() << "\n"238 << " Kind = " << Type->getKind() << "\n"239 << " Name = " << findString(Type->NameOff) << "\n"240 << " Record Size = " << Size << "\n";241 });242 Types.push_back(Type);243 Pos += Size;244 }245 246 return Error::success();247}248 249Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) {250 Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTFExt);251 if (!MaybeExtractor)252 return MaybeExtractor.takeError();253 254 DataExtractor &Extractor = MaybeExtractor.get();255 DataExtractor::Cursor C = DataExtractor::Cursor(0);256 uint16_t Magic = Extractor.getU16(C);257 if (!C)258 return Err(".BTF.ext", C);259 if (Magic != BTF::MAGIC)260 return Err("invalid .BTF.ext magic: ").write_hex(Magic);261 uint8_t Version = Extractor.getU8(C);262 if (!C)263 return Err(".BTF", C);264 if (Version != 1)265 return Err("unsupported .BTF.ext version: ") << (unsigned)Version;266 (void)Extractor.getU8(C); // flags267 uint32_t HdrLen = Extractor.getU32(C);268 if (!C)269 return Err(".BTF.ext", C);270 if (HdrLen < 8)271 return Err("unexpected .BTF.ext header length: ") << HdrLen;272 (void)Extractor.getU32(C); // func_info_off273 (void)Extractor.getU32(C); // func_info_len274 uint32_t LineInfoOff = Extractor.getU32(C);275 uint32_t LineInfoLen = Extractor.getU32(C);276 uint32_t RelocInfoOff = Extractor.getU32(C);277 uint32_t RelocInfoLen = Extractor.getU32(C);278 if (!C)279 return Err(".BTF.ext", C);280 281 if (LineInfoLen > 0 && Ctx.Opts.LoadLines) {282 uint32_t LineInfoStart = HdrLen + LineInfoOff;283 uint32_t LineInfoEnd = LineInfoStart + LineInfoLen;284 if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd))285 return E;286 }287 288 if (RelocInfoLen > 0 && Ctx.Opts.LoadRelocs) {289 uint32_t RelocInfoStart = HdrLen + RelocInfoOff;290 uint32_t RelocInfoEnd = RelocInfoStart + RelocInfoLen;291 if (Error E = parseRelocInfo(Ctx, Extractor, RelocInfoStart, RelocInfoEnd))292 return E;293 }294 295 return Error::success();296}297 298Error BTFParser::parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor,299 uint64_t LineInfoStart, uint64_t LineInfoEnd) {300 DataExtractor::Cursor C = DataExtractor::Cursor(LineInfoStart);301 uint32_t RecSize = Extractor.getU32(C);302 if (!C)303 return Err(".BTF.ext", C);304 if (RecSize < 16)305 return Err("unexpected .BTF.ext line info record length: ") << RecSize;306 307 while (C && C.tell() < LineInfoEnd) {308 uint32_t SecNameOff = Extractor.getU32(C);309 uint32_t NumInfo = Extractor.getU32(C);310 StringRef SecName = findString(SecNameOff);311 std::optional<SectionRef> Sec = Ctx.findSection(SecName);312 if (!C)313 return Err(".BTF.ext", C);314 if (!Sec)315 return Err("") << "can't find section '" << SecName316 << "' while parsing .BTF.ext line info";317 BTFLinesVector &Lines = SectionLines[Sec->getIndex()];318 for (uint32_t I = 0; C && I < NumInfo; ++I) {319 uint64_t RecStart = C.tell();320 uint32_t InsnOff = Extractor.getU32(C);321 uint32_t FileNameOff = Extractor.getU32(C);322 uint32_t LineOff = Extractor.getU32(C);323 uint32_t LineCol = Extractor.getU32(C);324 if (!C)325 return Err(".BTF.ext", C);326 Lines.push_back({InsnOff, FileNameOff, LineOff, LineCol});327 C.seek(RecStart + RecSize);328 }329 llvm::stable_sort(Lines,330 [](const BTF::BPFLineInfo &L, const BTF::BPFLineInfo &R) {331 return L.InsnOffset < R.InsnOffset;332 });333 }334 if (!C)335 return Err(".BTF.ext", C);336 337 return Error::success();338}339 340Error BTFParser::parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor,341 uint64_t RelocInfoStart,342 uint64_t RelocInfoEnd) {343 DataExtractor::Cursor C = DataExtractor::Cursor(RelocInfoStart);344 uint32_t RecSize = Extractor.getU32(C);345 if (!C)346 return Err(".BTF.ext", C);347 if (RecSize < 16)348 return Err("unexpected .BTF.ext field reloc info record length: ")349 << RecSize;350 while (C && C.tell() < RelocInfoEnd) {351 uint32_t SecNameOff = Extractor.getU32(C);352 uint32_t NumInfo = Extractor.getU32(C);353 StringRef SecName = findString(SecNameOff);354 std::optional<SectionRef> Sec = Ctx.findSection(SecName);355 BTFRelocVector &Relocs = SectionRelocs[Sec->getIndex()];356 for (uint32_t I = 0; C && I < NumInfo; ++I) {357 uint64_t RecStart = C.tell();358 uint32_t InsnOff = Extractor.getU32(C);359 uint32_t TypeID = Extractor.getU32(C);360 uint32_t OffsetNameOff = Extractor.getU32(C);361 uint32_t RelocKind = Extractor.getU32(C);362 if (!C)363 return Err(".BTF.ext", C);364 Relocs.push_back({InsnOff, TypeID, OffsetNameOff, RelocKind});365 C.seek(RecStart + RecSize);366 }367 llvm::stable_sort(368 Relocs, [](const BTF::BPFFieldReloc &L, const BTF::BPFFieldReloc &R) {369 return L.InsnOffset < R.InsnOffset;370 });371 }372 if (!C)373 return Err(".BTF.ext", C);374 375 return Error::success();376}377 378Error BTFParser::parse(const ObjectFile &Obj, const ParseOptions &Opts) {379 StringsTable = StringRef();380 SectionLines.clear();381 SectionRelocs.clear();382 Types.clear();383 TypesBuffer = OwningArrayRef<uint8_t>();384 385 ParseContext Ctx(Obj, Opts);386 std::optional<SectionRef> BTF;387 std::optional<SectionRef> BTFExt;388 for (SectionRef Sec : Obj.sections()) {389 Expected<StringRef> MaybeName = Sec.getName();390 if (!MaybeName)391 return Err("error while reading section name: ") << MaybeName.takeError();392 Ctx.Sections[*MaybeName] = Sec;393 if (*MaybeName == BTFSectionName)394 BTF = Sec;395 if (*MaybeName == BTFExtSectionName)396 BTFExt = Sec;397 }398 if (!BTF)399 return Err("can't find .BTF section");400 if (!BTFExt)401 return Err("can't find .BTF.ext section");402 if (Error E = parseBTF(Ctx, *BTF))403 return E;404 if (Error E = parseBTFExt(Ctx, *BTFExt))405 return E;406 407 return Error::success();408}409 410bool BTFParser::hasBTFSections(const ObjectFile &Obj) {411 bool HasBTF = false;412 bool HasBTFExt = false;413 for (SectionRef Sec : Obj.sections()) {414 Expected<StringRef> Name = Sec.getName();415 if (Error E = Name.takeError()) {416 logAllUnhandledErrors(std::move(E), errs());417 continue;418 }419 HasBTF |= *Name == BTFSectionName;420 HasBTFExt |= *Name == BTFExtSectionName;421 if (HasBTF && HasBTFExt)422 return true;423 }424 return false;425}426 427StringRef BTFParser::findString(uint32_t Offset) const {428 return StringsTable.slice(Offset, StringsTable.find(0, Offset));429}430 431template <typename T>432static const T *findInfo(const DenseMap<uint64_t, SmallVector<T, 0>> &SecMap,433 SectionedAddress Address) {434 auto MaybeSecInfo = SecMap.find(Address.SectionIndex);435 if (MaybeSecInfo == SecMap.end())436 return nullptr;437 438 const SmallVector<T, 0> &SecInfo = MaybeSecInfo->second;439 const uint64_t TargetOffset = Address.Address;440 typename SmallVector<T, 0>::const_iterator MaybeInfo = llvm::partition_point(441 SecInfo, [=](const T &Entry) { return Entry.InsnOffset < TargetOffset; });442 if (MaybeInfo == SecInfo.end() || MaybeInfo->InsnOffset != Address.Address)443 return nullptr;444 445 return &*MaybeInfo;446}447 448const BTF::BPFLineInfo *449BTFParser::findLineInfo(SectionedAddress Address) const {450 return findInfo(SectionLines, Address);451}452 453const BTF::BPFFieldReloc *454BTFParser::findFieldReloc(SectionedAddress Address) const {455 return findInfo(SectionRelocs, Address);456}457 458const BTF::CommonType *BTFParser::findType(uint32_t Id) const {459 if (Id < Types.size())460 return Types[Id];461 return nullptr;462}463 464enum RelocKindGroup {465 RKG_FIELD,466 RKG_TYPE,467 RKG_ENUMVAL,468 RKG_UNKNOWN,469};470 471static RelocKindGroup relocKindGroup(const BTF::BPFFieldReloc *Reloc) {472 switch (Reloc->RelocKind) {473 case BTF::FIELD_BYTE_OFFSET:474 case BTF::FIELD_BYTE_SIZE:475 case BTF::FIELD_EXISTENCE:476 case BTF::FIELD_SIGNEDNESS:477 case BTF::FIELD_LSHIFT_U64:478 case BTF::FIELD_RSHIFT_U64:479 return RKG_FIELD;480 case BTF::BTF_TYPE_ID_LOCAL:481 case BTF::BTF_TYPE_ID_REMOTE:482 case BTF::TYPE_EXISTENCE:483 case BTF::TYPE_MATCH:484 case BTF::TYPE_SIZE:485 return RKG_TYPE;486 case BTF::ENUM_VALUE_EXISTENCE:487 case BTF::ENUM_VALUE:488 return RKG_ENUMVAL;489 default:490 return RKG_UNKNOWN;491 }492}493 494static bool isMod(const BTF::CommonType *Type) {495 switch (Type->getKind()) {496 case BTF::BTF_KIND_VOLATILE:497 case BTF::BTF_KIND_CONST:498 case BTF::BTF_KIND_RESTRICT:499 case BTF::BTF_KIND_TYPE_TAG:500 return true;501 default:502 return false;503 }504}505 506static bool printMod(const BTFParser &BTF, const BTF::CommonType *Type,507 raw_ostream &Stream) {508 switch (Type->getKind()) {509 case BTF::BTF_KIND_CONST:510 Stream << " const";511 break;512 case BTF::BTF_KIND_VOLATILE:513 Stream << " volatile";514 break;515 case BTF::BTF_KIND_RESTRICT:516 Stream << " restrict";517 break;518 case BTF::BTF_KIND_TYPE_TAG:519 Stream << " type_tag(\"" << BTF.findString(Type->NameOff) << "\")";520 break;521 default:522 return false;523 }524 return true;525}526 527static const BTF::CommonType *skipModsAndTypedefs(const BTFParser &BTF,528 const BTF::CommonType *Type) {529 while (isMod(Type) || Type->getKind() == BTF::BTF_KIND_TYPEDEF) {530 auto *Base = BTF.findType(Type->Type);531 if (!Base)532 break;533 Type = Base;534 }535 return Type;536}537 538namespace {539struct StrOrAnon {540 const BTFParser &BTF;541 uint32_t Offset;542 uint32_t Idx;543};544 545static raw_ostream &operator<<(raw_ostream &Stream, const StrOrAnon &S) {546 StringRef Str = S.BTF.findString(S.Offset);547 if (Str.empty())548 Stream << "<anon " << S.Idx << ">";549 else550 Stream << Str;551 return Stream;552}553} // anonymous namespace554 555static void relocKindName(uint32_t X, raw_ostream &Out) {556 Out << "<";557 switch (X) {558 default:559 Out << "reloc kind #" << X;560 break;561 case BTF::FIELD_BYTE_OFFSET:562 Out << "byte_off";563 break;564 case BTF::FIELD_BYTE_SIZE:565 Out << "byte_sz";566 break;567 case BTF::FIELD_EXISTENCE:568 Out << "field_exists";569 break;570 case BTF::FIELD_SIGNEDNESS:571 Out << "signed";572 break;573 case BTF::FIELD_LSHIFT_U64:574 Out << "lshift_u64";575 break;576 case BTF::FIELD_RSHIFT_U64:577 Out << "rshift_u64";578 break;579 case BTF::BTF_TYPE_ID_LOCAL:580 Out << "local_type_id";581 break;582 case BTF::BTF_TYPE_ID_REMOTE:583 Out << "target_type_id";584 break;585 case BTF::TYPE_EXISTENCE:586 Out << "type_exists";587 break;588 case BTF::TYPE_MATCH:589 Out << "type_matches";590 break;591 case BTF::TYPE_SIZE:592 Out << "type_size";593 break;594 case BTF::ENUM_VALUE_EXISTENCE:595 Out << "enumval_exists";596 break;597 case BTF::ENUM_VALUE:598 Out << "enumval_value";599 break;600 }601 Out << ">";602}603 604// Produces a human readable description of a CO-RE relocation.605// Such relocations are generated by BPF backend, and processed606// by libbpf's BPF program loader [1].607//608// Each relocation record has the following information:609// - Relocation kind;610// - BTF type ID;611// - Access string offset in string table.612//613// There are different kinds of relocations, these kinds could be split614// in three groups:615// - load-time information about types (size, existence),616// `BTFParser::symbolize()` output for such relocations uses the template:617//618// <relocation-kind> [<id>] <type-name>619//620// For example:621// - "<type_exists> [7] struct foo"622// - "<type_size> [7] struct foo"623//624// - load-time information about enums (literal existence, literal value),625// `BTFParser::symbolize()` output for such relocations uses the template:626//627// <relocation-kind> [<id>] <type-name>::<literal-name> = <original-value>628//629// For example:630// - "<enumval_exists> [5] enum foo::U = 1"631// - "<enumval_value> [5] enum foo::V = 2"632//633// - load-time information about fields (e.g. field offset),634// `BTFParser::symbolize()` output for such relocations uses the template:635//636// <relocation-kind> [<id>] \637// <type-name>::[N].<field-1-name>...<field-M-name> \638// (<access string>)639//640// For example:641// - "<byte_off> [8] struct bar::[7].v (7:1)"642// - "<field_exists> [8] struct bar::v (0:1)"643//644// If relocation description is not valid output follows the following pattern:645//646// <relocation-kind> <type-id>::<unprocessedaccess-string> <<error-msg>>647//648// For example:649//650// - "<type_sz> [42] '' <unknown type id: 42>"651// - "<byte_off> [4] '0:' <field spec too short>"652//653// Additional examples could be found in unit tests, see654// llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp.655//656// [1] https://www.kernel.org/doc/html/latest/bpf/libbpf/index.html657void BTFParser::symbolize(const BTF::BPFFieldReloc *Reloc,658 SmallVectorImpl<char> &Result) const {659 raw_svector_ostream Stream(Result);660 StringRef FullSpecStr = findString(Reloc->OffsetNameOff);661 SmallVector<uint32_t, 8> RawSpec;662 663 auto Fail = [&](auto Msg) {664 Result.resize(0);665 relocKindName(Reloc->RelocKind, Stream);666 Stream << " [" << Reloc->TypeID << "] '" << FullSpecStr << "'"667 << " <" << Msg << ">";668 };669 670 // Relocation access string follows pattern [0-9]+(:[0-9]+)*,671 // e.g.: 12:22:3. Code below splits `SpecStr` by ':', parses672 // numbers, and pushes them to `RawSpec`.673 StringRef SpecStr = FullSpecStr;674 while (SpecStr.size()) {675 unsigned long long Val;676 if (consumeUnsignedInteger(SpecStr, 10, Val))677 return Fail("spec string is not a number");678 RawSpec.push_back(Val);679 if (SpecStr.empty())680 break;681 if (SpecStr[0] != ':')682 return Fail(format("unexpected spec string delimiter: '%c'", SpecStr[0]));683 SpecStr = SpecStr.substr(1);684 }685 686 // Print relocation kind to `Stream`.687 relocKindName(Reloc->RelocKind, Stream);688 689 uint32_t CurId = Reloc->TypeID;690 const BTF::CommonType *Type = findType(CurId);691 if (!Type)692 return Fail(format("unknown type id: %d", CurId));693 694 Stream << " [" << CurId << "]";695 696 // `Type` might have modifiers, e.g. for type 'const int' the `Type`697 // would refer to BTF type of kind BTF_KIND_CONST.698 // Print all these modifiers to `Stream`.699 for (uint32_t ChainLen = 0; printMod(*this, Type, Stream); ++ChainLen) {700 if (ChainLen >= 32)701 return Fail("modifiers chain is too long");702 703 CurId = Type->Type;704 const BTF::CommonType *NextType = findType(CurId);705 if (!NextType)706 return Fail(format("unknown type id: %d in modifiers chain", CurId));707 Type = NextType;708 }709 // Print the type name to `Stream`.710 if (CurId == 0) {711 Stream << " void";712 } else {713 switch (Type->getKind()) {714 case BTF::BTF_KIND_TYPEDEF:715 Stream << " typedef";716 break;717 case BTF::BTF_KIND_STRUCT:718 Stream << " struct";719 break;720 case BTF::BTF_KIND_UNION:721 Stream << " union";722 break;723 case BTF::BTF_KIND_ENUM:724 Stream << " enum";725 break;726 case BTF::BTF_KIND_ENUM64:727 Stream << " enum";728 break;729 case BTF::BTF_KIND_FWD:730 if (Type->Info & BTF::FWD_UNION_FLAG)731 Stream << " fwd union";732 else733 Stream << " fwd struct";734 break;735 default:736 break;737 }738 Stream << " " << StrOrAnon({*this, Type->NameOff, CurId});739 }740 741 RelocKindGroup Group = relocKindGroup(Reloc);742 // Type-based relocations don't use access string but clang backend743 // generates '0' and libbpf checks it's value, do the same here.744 if (Group == RKG_TYPE) {745 if (RawSpec.size() != 1 || RawSpec[0] != 0)746 return Fail("unexpected type-based relocation spec: should be '0'");747 return;748 }749 750 Stream << "::";751 752 // For enum-based relocations access string is a single number,753 // corresponding to the enum literal sequential number.754 // E.g. for `enum E { U, V }`, relocation requesting value of `V`755 // would look as follows:756 // - kind: BTF::ENUM_VALUE757 // - BTF id: id for `E`758 // - access string: "1"759 if (Group == RKG_ENUMVAL) {760 Type = skipModsAndTypedefs(*this, Type);761 762 if (RawSpec.size() != 1)763 return Fail("unexpected enumval relocation spec size");764 765 uint32_t NameOff;766 uint64_t Val;767 uint32_t Idx = RawSpec[0];768 if (auto *T = dyn_cast<BTF::EnumType>(Type)) {769 if (T->values().size() <= Idx)770 return Fail(format("bad value index: %d", Idx));771 const BTF::BTFEnum &E = T->values()[Idx];772 NameOff = E.NameOff;773 Val = E.Val;774 } else if (auto *T = dyn_cast<BTF::Enum64Type>(Type)) {775 if (T->values().size() <= Idx)776 return Fail(format("bad value index: %d", Idx));777 const BTF::BTFEnum64 &E = T->values()[Idx];778 NameOff = E.NameOff;779 Val = (uint64_t)E.Val_Hi32 << 32u | E.Val_Lo32;780 } else {781 return Fail(format("unexpected type kind for enum relocation: %d",782 Type->getKind()));783 }784 785 Stream << StrOrAnon({*this, NameOff, Idx});786 if (Type->Info & BTF::ENUM_SIGNED_FLAG)787 Stream << " = " << (int64_t)Val;788 else789 Stream << " = " << (uint64_t)Val;790 return;791 }792 793 // For type-based relocations access string is an array of numbers,794 // which resemble index parameters for `getelementptr` LLVM IR instruction.795 // E.g. for the following types:796 //797 // struct foo {798 // int a;799 // int b;800 // };801 // struct bar {802 // int u;803 // struct foo v[7];804 // };805 //806 // Relocation requesting `offsetof(struct bar, v[2].b)` will have807 // the following access string: 0:1:2:1808 // ^ ^ ^ ^809 // | | | |810 // initial index | | field 'b' is a field #1811 // | | (counting from 0)812 // | array index #2813 // field 'v' is a field #1814 // (counting from 0)815 if (Group == RKG_FIELD) {816 if (RawSpec.size() < 1)817 return Fail("field spec too short");818 819 if (RawSpec[0] != 0)820 Stream << "[" << RawSpec[0] << "]";821 for (uint32_t I = 1; I < RawSpec.size(); ++I) {822 Type = skipModsAndTypedefs(*this, Type);823 uint32_t Idx = RawSpec[I];824 825 if (auto *T = dyn_cast<BTF::StructType>(Type)) {826 if (T->getVlen() <= Idx)827 return Fail(828 format("member index %d for spec sub-string %d is out of range",829 Idx, I));830 831 const BTF::BTFMember &Member = T->members()[Idx];832 if (I != 1 || RawSpec[0] != 0)833 Stream << ".";834 Stream << StrOrAnon({*this, Member.NameOff, Idx});835 Type = findType(Member.Type);836 if (!Type)837 return Fail(format("unknown member type id %d for spec sub-string %d",838 Member.Type, I));839 } else if (auto *T = dyn_cast<BTF::ArrayType>(Type)) {840 Stream << "[" << Idx << "]";841 Type = findType(T->getArray().ElemType);842 if (!Type)843 return Fail(844 format("unknown element type id %d for spec sub-string %d",845 T->getArray().ElemType, I));846 } else {847 return Fail(format("unexpected type kind %d for spec sub-string %d",848 Type->getKind(), I));849 }850 }851 852 Stream << " (" << FullSpecStr << ")";853 return;854 }855 856 return Fail(format("unknown relocation kind: %d", Reloc->RelocKind));857}858