brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.4 KiB · 4fc31a4 Raw
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