[llvm] d15f96f - [BPF][DebugInfo] Show CO-RE relocations in llvm-objdump
Eduard Zingerman via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 21 12:00:14 PDT 2023
Author: Eduard Zingerman
Date: 2023-09-21T21:59:10+03:00
New Revision: d15f96fe4b64e24d262fcc727dc0eede327804ba
URL: https://github.com/llvm/llvm-project/commit/d15f96fe4b64e24d262fcc727dc0eede327804ba
DIFF: https://github.com/llvm/llvm-project/commit/d15f96fe4b64e24d262fcc727dc0eede327804ba.diff
LOG: [BPF][DebugInfo] Show CO-RE relocations in llvm-objdump
Extend llvm-objdump to show CO-RE relocations when `-r` option is
passed and object file has .BTF and .BTF.ext sections.
For example, the following C program:
#define __pai __attribute__((preserve_access_index))
struct foo { int i; int j;} __pai;
struct bar { struct foo f[7]; } __pai;
extern void sink(void *);
void root(struct bar *bar) {
sink(&bar[2].f[3].j);
}
Should lead to the following objdump output:
$ clang --target=bpf -O2 -g t.c -c -o - | \
llvm-objdump --no-addresses --no-show-raw-insn -dr -
...
r2 = 0x94
CO-RE <byte_off> [2] struct bar::[2].f[3].j (2:0:3:1)
r1 += r2
call -0x1
R_BPF_64_32 sink
exit
...
More examples could be found in unit tests, see BTFParserTest.cpp.
To achieve this:
- Move CO-RE relocation kinds definitions from BPFCORE.h to BTF.h.
- Extend BTF.h with types derived from BTF::CommonType, e.g.
BTF::IntType and BTF::StrutType, to allow dyn_cast() and access to
type additional data.
- Extend BTFParser to load BTF type and relocation data.
- Modify llvm-objdump.cpp to create instance of BTFParser when
disassembly of object file with BTF sections is processed and `-r`
flag is supplied.
Additional information about CO-RE is available at [1].
[1] https://docs.kernel.org/bpf/llvm_reloc.html
Depends on D149058
Differential Revision: https://reviews.llvm.org/D150079
Added:
llvm/test/tools/llvm-objdump/BPF/core-relo-byte-offset.ll
llvm/test/tools/llvm-objdump/BPF/core-relo-enum-value.ll
llvm/test/tools/llvm-objdump/BPF/core-relo-field-info.ll
llvm/test/tools/llvm-objdump/BPF/core-relo-formatting.s
llvm/test/tools/llvm-objdump/BPF/core-relo-type-id.ll
llvm/test/tools/llvm-objdump/BPF/core-relo-type-info.ll
Modified:
llvm/include/llvm/DebugInfo/BTF/BTF.h
llvm/include/llvm/DebugInfo/BTF/BTFParser.h
llvm/lib/DebugInfo/BTF/BTFContext.cpp
llvm/lib/DebugInfo/BTF/BTFParser.cpp
llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
llvm/lib/Target/BPF/BPFCORE.h
llvm/lib/Target/BPF/BPFPreserveDIType.cpp
llvm/lib/Target/BPF/BTFDebug.cpp
llvm/tools/llvm-objdump/CMakeLists.txt
llvm/tools/llvm-objdump/llvm-objdump.cpp
llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/DebugInfo/BTF/BTF.h b/llvm/include/llvm/DebugInfo/BTF/BTF.h
index c1c77cd447d6f61..d88af2ff30bdfdb 100644
--- a/llvm/include/llvm/DebugInfo/BTF/BTF.h
+++ b/llvm/include/llvm/DebugInfo/BTF/BTF.h
@@ -48,7 +48,8 @@
#ifndef LLVM_LIB_TARGET_BPF_BTF_H
#define LLVM_LIB_TARGET_BPF_BTF_H
-#include <cstdint>
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/TrailingObjects.h"
namespace llvm {
namespace BTF {
@@ -97,6 +98,10 @@ enum TypeKinds : uint8_t {
#include "BTF.def"
};
+// Constants for CommonType::Info field.
+constexpr uint32_t FWD_UNION_FLAG = 1u << 31;
+constexpr uint32_t ENUM_SIGNED_FLAG = 1u << 31;
+
/// The BTF common type definition. Different kinds may have
/// additional information after this structure data.
struct CommonType {
@@ -106,8 +111,8 @@ struct CommonType {
/// "Info" bits arrangement:
/// Bits 0-15: vlen (e.g. # of struct's members)
/// Bits 16-23: unused
- /// Bits 24-27: kind (e.g. int, ptr, array...etc)
- /// Bits 28-30: unused
+ /// Bits 24-28: kind (e.g. int, ptr, array...etc)
+ /// Bits 29-30: unused
/// Bit 31: kind_flag, currently used by
/// struct, union and fwd
uint32_t Info;
@@ -122,6 +127,9 @@ struct CommonType {
uint32_t Size;
uint32_t Type;
};
+
+ uint32_t getKind() const { return Info >> 24 & 0x1f; }
+ uint32_t getVlen() const { return Info & 0xffff; }
};
// For some specific BTF_KIND, "struct CommonType" is immediately
@@ -269,6 +277,84 @@ struct SecFieldReloc {
uint32_t NumFieldReloc; ///< Number of offset reloc's in this section
};
+/// CO-RE relocation kind codes used in .BTF.ext section.
+enum PatchableRelocKind : uint32_t {
+ FIELD_BYTE_OFFSET = 0,
+ FIELD_BYTE_SIZE,
+ FIELD_EXISTENCE,
+ FIELD_SIGNEDNESS,
+ FIELD_LSHIFT_U64,
+ FIELD_RSHIFT_U64,
+ BTF_TYPE_ID_LOCAL,
+ BTF_TYPE_ID_REMOTE,
+ TYPE_EXISTENCE,
+ TYPE_SIZE,
+ ENUM_VALUE_EXISTENCE,
+ ENUM_VALUE,
+ TYPE_MATCH,
+ MAX_FIELD_RELOC_KIND,
+};
+
+// Define a number of sub-types for CommonType, each with:
+// - An accessor for a relevant "tail" information (data fields that
+// follow the CommonType record in binary format).
+// - A classof() definition based on CommonType::getKind() value to
+// allow use with dyn_cast<>() function.
+
+// For CommonType sub-types that are followed by a single entry of
+// some type in the binary format.
+#define BTF_DEFINE_TAIL(Type, Accessor) \
+ const Type &Accessor() const { return *getTrailingObjects<Type>(); }
+
+// For CommonType sub-types that are followed by CommonType::getVlen()
+// number of entries of some type in the binary format.
+#define BTF_DEFINE_TAIL_ARR(Type, Accessor) \
+ ArrayRef<Type> Accessor() const { \
+ return ArrayRef<Type>(getTrailingObjects<Type>(), getVlen()); \
+ }
+
+struct ArrayType final : CommonType,
+ private TrailingObjects<ArrayType, BTFArray> {
+ friend TrailingObjects;
+ BTF_DEFINE_TAIL(BTFArray, getArray)
+
+ static bool classof(const CommonType *V) {
+ return V->getKind() == BTF_KIND_ARRAY;
+ }
+};
+
+struct StructType final : CommonType,
+ private TrailingObjects<StructType, BTFMember> {
+ friend TrailingObjects;
+ BTF_DEFINE_TAIL_ARR(BTFMember, members)
+
+ static bool classof(const CommonType *V) {
+ return V->getKind() == BTF_KIND_STRUCT || V->getKind() == BTF_KIND_UNION;
+ }
+};
+
+struct EnumType final : CommonType, private TrailingObjects<EnumType, BTFEnum> {
+ friend TrailingObjects;
+ BTF_DEFINE_TAIL_ARR(BTFEnum, values)
+
+ static bool classof(const CommonType *V) {
+ return V->getKind() == BTF_KIND_ENUM;
+ }
+};
+
+struct Enum64Type final : CommonType,
+ private TrailingObjects<Enum64Type, BTFEnum64> {
+ friend TrailingObjects;
+ BTF_DEFINE_TAIL_ARR(BTFEnum64, values)
+
+ static bool classof(const CommonType *V) {
+ return V->getKind() == BTF_KIND_ENUM64;
+ }
+};
+
+#undef BTF_DEFINE_TAIL
+#undef BTF_DEFINE_TAIL_ARR
+
} // End namespace BTF.
} // End namespace llvm.
diff --git a/llvm/include/llvm/DebugInfo/BTF/BTFParser.h b/llvm/include/llvm/DebugInfo/BTF/BTFParser.h
index 33d0f32c7c55f81..53f39919950e521 100644
--- a/llvm/include/llvm/DebugInfo/BTF/BTFParser.h
+++ b/llvm/include/llvm/DebugInfo/BTF/BTFParser.h
@@ -10,7 +10,9 @@
// BPF backend and provides introspection for the stored information.
// Currently the following information is accessible:
// - string table;
-// - instruction offset to line information mapping.
+// - instruction offset to line information mapping;
+// - types table;
+// - CO-RE relocations table.
//
// See llvm/DebugInfo/BTF/BTF.h for some details about binary format
// and links to Linux Kernel documentation.
@@ -32,6 +34,7 @@ using object::SectionRef;
class BTFParser {
using BTFLinesVector = SmallVector<BTF::BPFLineInfo, 0>;
+ using BTFRelocVector = SmallVector<BTF::BPFFieldReloc, 0>;
// In BTF strings are stored as a continuous memory region with
// individual strings separated by 0 bytes. Strings are identified
@@ -39,15 +42,34 @@ class BTFParser {
// The `StringsTable` points to this region in the parsed ObjectFile.
StringRef StringsTable;
+ // A copy of types table from the object file but using native byte
+ // order. Should not be too big in practice, e.g. for ~250MiB vmlinux
+ // image it is ~4MiB.
+ OwningArrayRef<uint8_t> TypesBuffer;
+
// Maps ELF section number to instruction line number information.
// Each BTFLinesVector is sorted by `InsnOffset` to allow fast lookups.
DenseMap<uint64_t, BTFLinesVector> SectionLines;
+ // Maps ELF section number to CO-RE relocation information.
+ // Each BTFRelocVector is sorted by `InsnOffset` to allow fast lookups.
+ DenseMap<uint64_t, BTFRelocVector> SectionRelocs;
+
+ // Vector of pointers to all known types, index in this vector
+ // equals to logical type BTF id.
+ // Pointers point to memory owned by `TypesBuffer`
+ // (except pointer at index 0, which is statically allocated).
+ std::vector<const BTF::CommonType *> Types;
+
struct ParseContext;
Error parseBTF(ParseContext &Ctx, SectionRef BTF);
Error parseBTFExt(ParseContext &Ctx, SectionRef BTFExt);
Error parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor,
uint64_t LineInfoStart, uint64_t LineInfoEnd);
+ Error parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor,
+ uint64_t RelocInfoStart, uint64_t RelocInfoEnd);
+ Error parseTypesInfo(ParseContext &Ctx, uint64_t TypesInfoStart,
+ StringRef RawData);
public:
// Looks-up a string in the .BTF section's string table.
@@ -61,6 +83,34 @@ class BTFParser {
// owned by this class.
const BTF::BPFLineInfo *findLineInfo(SectionedAddress Address) const;
+ // Search for CO-RE relocation information for a specific address.
+ // Return nullptr if no information found.
+ // If information is present, return a pointer to object
+ // owned by this class.
+ const BTF::BPFFieldReloc *findFieldReloc(SectionedAddress Address) const;
+
+ // Return a human readable representation of the CO-RE relocation
+ // record, this is for display purpose only.
+ // See implementation for details.
+ void symbolize(const BTF::BPFFieldReloc *Reloc,
+ SmallVectorImpl<char> &Result) const;
+
+ // Lookup BTF type definition with a specific index.
+ // Return nullptr if no information found.
+ // If information is present, return a pointer to object
+ // owned by this class.
+ const BTF::CommonType *findType(uint32_t Id) const;
+
+ // Return total number of known BTF types.
+ size_t typesCount() const { return Types.size(); }
+
+ // Allow to selectively load BTF information.
+ struct ParseOptions {
+ bool LoadLines = false;
+ bool LoadTypes = false;
+ bool LoadRelocs = false;
+ };
+
// Fills instance of BTFParser with information stored in .BTF and
// .BTF.ext sections of the `Obj`. If this instance was already
// filled, old data is discarded.
@@ -70,7 +120,8 @@ class BTFParser {
// - state of the BTFParser might be incomplete but is not invalid,
// queries might be run against it, but some (or all) information
// might be unavailable;
- Error parse(const ObjectFile &Obj);
+ Error parse(const ObjectFile &Obj, const ParseOptions &Opts);
+ Error parse(const ObjectFile &Obj) { return parse(Obj, {true, true, true}); }
// Return true if `Obj` has .BTF and .BTF.ext sections.
static bool hasBTFSections(const ObjectFile &Obj);
diff --git a/llvm/lib/DebugInfo/BTF/BTFContext.cpp b/llvm/lib/DebugInfo/BTF/BTFContext.cpp
index 24898739b8241af..2e651cb378dbf0d 100644
--- a/llvm/lib/DebugInfo/BTF/BTFContext.cpp
+++ b/llvm/lib/DebugInfo/BTF/BTFContext.cpp
@@ -63,7 +63,9 @@ std::unique_ptr<BTFContext>
BTFContext::create(const ObjectFile &Obj,
std::function<void(Error)> ErrorHandler) {
auto Ctx = std::make_unique<BTFContext>();
- if (Error E = Ctx->BTF.parse(Obj))
+ BTFParser::ParseOptions Opts;
+ Opts.LoadLines = true;
+ if (Error E = Ctx->BTF.parse(Obj, Opts))
ErrorHandler(std::move(E));
return Ctx;
}
diff --git a/llvm/lib/DebugInfo/BTF/BTFParser.cpp b/llvm/lib/DebugInfo/BTF/BTFParser.cpp
index 6151e1b15cbbf58..a1b7dd396ff1dca 100644
--- a/llvm/lib/DebugInfo/BTF/BTFParser.cpp
+++ b/llvm/lib/DebugInfo/BTF/BTFParser.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/BTF/BTFParser.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Errc.h"
#define DEBUG_TYPE "debug-info-btf-parser"
@@ -74,11 +75,13 @@ class Err {
// 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 +122,128 @@ 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::big;
+ using support::endianness;
+ using support::little;
+ using support::endian::byte_swap;
+
+ TypesBuffer = OwningArrayRef<uint8_t>(arrayRefFromStringRef(RawData));
+ // Switch endianness if necessary.
+ endianness Endianness = Ctx.Obj.isLittleEndian() ? little : 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 +274,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 +338,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 +429,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
diff erent 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));
}
diff --git a/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp b/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
index 9c99765b60c080e..a878eaabe00ff5e 100644
--- a/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
+++ b/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
@@ -78,6 +78,7 @@
#include "BPFCORE.h"
#include "BPFTargetMachine.h"
#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/BTF/BTF.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instruction.h"
@@ -369,7 +370,7 @@ bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
CInfo.Metadata = nullptr;
// Check validity of info_kind as clang did not check this.
uint64_t InfoKind = getConstant(Call->getArgOperand(1));
- if (InfoKind >= BPFCoreSharedInfo::MAX_FIELD_RELOC_KIND)
+ if (InfoKind >= BTF::MAX_FIELD_RELOC_KIND)
report_fatal_error("Incorrect info_kind for llvm.bpf.preserve.field.info intrinsic");
CInfo.AccessIndex = InfoKind;
return true;
@@ -383,11 +384,11 @@ bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
if (Flag >= BPFCoreSharedInfo::MAX_PRESERVE_TYPE_INFO_FLAG)
report_fatal_error("Incorrect flag for llvm.bpf.preserve.type.info intrinsic");
if (Flag == BPFCoreSharedInfo::PRESERVE_TYPE_INFO_EXISTENCE)
- CInfo.AccessIndex = BPFCoreSharedInfo::TYPE_EXISTENCE;
+ CInfo.AccessIndex = BTF::TYPE_EXISTENCE;
else if (Flag == BPFCoreSharedInfo::PRESERVE_TYPE_INFO_MATCH)
- CInfo.AccessIndex = BPFCoreSharedInfo::TYPE_MATCH;
+ CInfo.AccessIndex = BTF::TYPE_MATCH;
else
- CInfo.AccessIndex = BPFCoreSharedInfo::TYPE_SIZE;
+ CInfo.AccessIndex = BTF::TYPE_SIZE;
return true;
}
if (GV->getName().startswith("llvm.bpf.preserve.enum.value")) {
@@ -399,9 +400,9 @@ bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
if (Flag >= BPFCoreSharedInfo::MAX_PRESERVE_ENUM_VALUE_FLAG)
report_fatal_error("Incorrect flag for llvm.bpf.preserve.enum.value intrinsic");
if (Flag == BPFCoreSharedInfo::PRESERVE_ENUM_VALUE_EXISTENCE)
- CInfo.AccessIndex = BPFCoreSharedInfo::ENUM_VALUE_EXISTENCE;
+ CInfo.AccessIndex = BTF::ENUM_VALUE_EXISTENCE;
else
- CInfo.AccessIndex = BPFCoreSharedInfo::ENUM_VALUE;
+ CInfo.AccessIndex = BTF::ENUM_VALUE;
return true;
}
@@ -672,11 +673,11 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
uint32_t AccessIndex,
uint32_t PatchImm,
MaybeAlign RecordAlignment) {
- if (InfoKind == BPFCoreSharedInfo::FIELD_EXISTENCE)
- return 1;
+ if (InfoKind == BTF::FIELD_EXISTENCE)
+ return 1;
uint32_t Tag = CTy->getTag();
- if (InfoKind == BPFCoreSharedInfo::FIELD_BYTE_OFFSET) {
+ if (InfoKind == BTF::FIELD_BYTE_OFFSET) {
if (Tag == dwarf::DW_TAG_array_type) {
auto *EltTy = stripQualifiers(CTy->getBaseType());
PatchImm += AccessIndex * calcArraySize(CTy, 1) *
@@ -695,7 +696,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
return PatchImm;
}
- if (InfoKind == BPFCoreSharedInfo::FIELD_BYTE_SIZE) {
+ if (InfoKind == BTF::FIELD_BYTE_SIZE) {
if (Tag == dwarf::DW_TAG_array_type) {
auto *EltTy = stripQualifiers(CTy->getBaseType());
return calcArraySize(CTy, 1) * (EltTy->getSizeInBits() >> 3);
@@ -715,7 +716,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
}
}
- if (InfoKind == BPFCoreSharedInfo::FIELD_SIGNEDNESS) {
+ if (InfoKind == BTF::FIELD_SIGNEDNESS) {
const DIType *BaseTy;
if (Tag == dwarf::DW_TAG_array_type) {
// Signedness only checked when final array elements are accessed.
@@ -741,7 +742,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
return (Encoding == dwarf::DW_ATE_signed || Encoding == dwarf::DW_ATE_signed_char);
}
- if (InfoKind == BPFCoreSharedInfo::FIELD_LSHIFT_U64) {
+ if (InfoKind == BTF::FIELD_LSHIFT_U64) {
// The value is loaded into a value with FIELD_BYTE_SIZE size,
// and then zero or sign extended to U64.
// FIELD_LSHIFT_U64 and FIELD_RSHIFT_U64 are operations
@@ -778,7 +779,7 @@ uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
return OffsetInBits + 64 - NextSBitOffset;
}
- if (InfoKind == BPFCoreSharedInfo::FIELD_RSHIFT_U64) {
+ if (InfoKind == BTF::FIELD_RSHIFT_U64) {
DIDerivedType *MemberTy = nullptr;
bool IsBitField = false;
uint32_t SizeInBits;
@@ -849,7 +850,7 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
// we will skip them.
uint32_t FirstIndex = 0;
uint32_t PatchImm = 0; // AccessOffset or the requested field info
- uint32_t InfoKind = BPFCoreSharedInfo::FIELD_BYTE_OFFSET;
+ uint32_t InfoKind = BTF::FIELD_BYTE_OFFSET;
while (CallStack.size()) {
auto StackElem = CallStack.top();
Call = StackElem.first;
@@ -939,7 +940,7 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
if (CInfo.Kind == BPFPreserveFieldInfoAI) {
InfoKind = CInfo.AccessIndex;
- if (InfoKind == BPFCoreSharedInfo::FIELD_EXISTENCE)
+ if (InfoKind == BTF::FIELD_EXISTENCE)
PatchImm = 1;
break;
}
@@ -987,10 +988,10 @@ MDNode *BPFAbstractMemberAccess::computeAccessKey(CallInst *Call,
int64_t PatchImm;
std::string AccessStr("0");
- if (CInfo.AccessIndex == BPFCoreSharedInfo::TYPE_EXISTENCE ||
- CInfo.AccessIndex == BPFCoreSharedInfo::TYPE_MATCH) {
+ if (CInfo.AccessIndex == BTF::TYPE_EXISTENCE ||
+ CInfo.AccessIndex == BTF::TYPE_MATCH) {
PatchImm = 1;
- } else if (CInfo.AccessIndex == BPFCoreSharedInfo::TYPE_SIZE) {
+ } else if (CInfo.AccessIndex == BTF::TYPE_SIZE) {
// typedef debuginfo type has size 0, get the eventual base type.
DIType *BaseTy = stripQualifiers(Ty, true);
PatchImm = BaseTy->getSizeInBits() / 8;
@@ -1026,7 +1027,7 @@ MDNode *BPFAbstractMemberAccess::computeAccessKey(CallInst *Call,
EnumIndex++;
}
- if (CInfo.AccessIndex == BPFCoreSharedInfo::ENUM_VALUE) {
+ if (CInfo.AccessIndex == BTF::ENUM_VALUE) {
StringRef EValueStr = ValueStr.substr(Separator + 1);
PatchImm = std::stoll(std::string(EValueStr));
} else {
diff --git a/llvm/lib/Target/BPF/BPFCORE.h b/llvm/lib/Target/BPF/BPFCORE.h
index c9aa135232c18b1..9a547a775c96024 100644
--- a/llvm/lib/Target/BPF/BPFCORE.h
+++ b/llvm/lib/Target/BPF/BPFCORE.h
@@ -19,24 +19,6 @@ class Module;
class BPFCoreSharedInfo {
public:
- enum PatchableRelocKind : uint32_t {
- FIELD_BYTE_OFFSET = 0,
- FIELD_BYTE_SIZE,
- FIELD_EXISTENCE,
- FIELD_SIGNEDNESS,
- FIELD_LSHIFT_U64,
- FIELD_RSHIFT_U64,
- BTF_TYPE_ID_LOCAL,
- BTF_TYPE_ID_REMOTE,
- TYPE_EXISTENCE,
- TYPE_SIZE,
- ENUM_VALUE_EXISTENCE,
- ENUM_VALUE,
- TYPE_MATCH,
-
- MAX_FIELD_RELOC_KIND,
- };
-
enum BTFTypeIdFlag : uint32_t {
BTF_TYPE_ID_LOCAL_RELOC = 0,
BTF_TYPE_ID_REMOTE_RELOC,
diff --git a/llvm/lib/Target/BPF/BPFPreserveDIType.cpp b/llvm/lib/Target/BPF/BPFPreserveDIType.cpp
index ec770eecb2e50f5..78e1bf90f1bd524 100644
--- a/llvm/lib/Target/BPF/BPFPreserveDIType.cpp
+++ b/llvm/lib/Target/BPF/BPFPreserveDIType.cpp
@@ -13,6 +13,7 @@
#include "BPF.h"
#include "BPFCORE.h"
#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/BTF/BTF.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instruction.h"
@@ -82,9 +83,9 @@ static bool BPFPreserveDITypeImpl(Function &F) {
uint32_t Reloc;
if (FlagValue == BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL_RELOC) {
- Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL;
+ Reloc = BTF::BTF_TYPE_ID_LOCAL;
} else {
- Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_REMOTE;
+ Reloc = BTF::BTF_TYPE_ID_REMOTE;
DIType *Ty = cast<DIType>(MD);
while (auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
unsigned Tag = DTy->getTag();
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index 2fe4206b194c1c6..f91ce7f250351bb 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -1518,10 +1518,8 @@ bool BTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) {
return false;
}
- if (Reloc == BPFCoreSharedInfo::ENUM_VALUE_EXISTENCE ||
- Reloc == BPFCoreSharedInfo::ENUM_VALUE ||
- Reloc == BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL ||
- Reloc == BPFCoreSharedInfo::BTF_TYPE_ID_REMOTE)
+ if (Reloc == BTF::ENUM_VALUE_EXISTENCE || Reloc == BTF::ENUM_VALUE ||
+ Reloc == BTF::BTF_TYPE_ID_LOCAL || Reloc == BTF::BTF_TYPE_ID_REMOTE)
OutMI.setOpcode(BPF::LD_imm64);
else
OutMI.setOpcode(BPF::MOV_ri);
diff --git a/llvm/test/tools/llvm-objdump/BPF/core-relo-byte-offset.ll b/llvm/test/tools/llvm-objdump/BPF/core-relo-byte-offset.ll
new file mode 100644
index 000000000000000..320152d21e5751a
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/BPF/core-relo-byte-offset.ll
@@ -0,0 +1,154 @@
+; REQUIRES: bpf-registered-target
+
+;; Verify that llvm-objdump can use .BTF.ext to show CO-RE relocation data.
+
+; RUN: llc --mtriple bpfel %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+; RUN: llc --mtriple bpfeb %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+;; Input generated from the following C code:
+;;
+;; #define __pai __attribute__((preserve_access_index))
+;;
+;; struct buz {
+;; int a;
+;; int b;
+;; } __pai;
+;;
+;; struct foo {
+;; int :4;
+;; int i;
+;; struct buz k[10];
+;; } __pai;
+;;
+;; struct bar {
+;; struct foo f;
+;; } __pai;
+;;
+;; void * volatile g;
+;;
+;; void root(void) {
+;; struct bar *bar = (void *)0;
+;; g = &bar->f;
+;; g = &bar->f.i;
+;; g = &bar->f.k;
+;; g = &bar->f.k[7].a;
+;; g = &bar->f.k[7].b;
+;; g = &bar[1].f.k[7].b;
+;; }
+;;
+;; Using the following command:
+;;
+;; clang --target=bpf -g -O2 -emit-llvm -S t.c
+
+; CHECK: CO-RE <byte_off> [[[#bar:]]] struct bar::f (0:0)
+; CHECK: CO-RE <byte_off> [[[#bar]]] struct bar::f.i (0:0:0)
+; CHECK: CO-RE <byte_off> [[[#bar]]] struct bar::f.k (0:0:1)
+; CHECK: CO-RE <byte_off> [[[#bar]]] struct bar::f.k[7].a (0:0:1:7:0)
+; CHECK: CO-RE <byte_off> [[[#bar]]] struct bar::f.k[7].b (0:0:1:7:1)
+; CHECK: CO-RE <byte_off> [[[#bar]]] struct bar::[1].f.k[7].b (1:0:1:7:1)
+
+ at g = dso_local global ptr null, align 8, !dbg !0
+@"llvm.bar:0:0$0:0" = external global i64, !llvm.preserve.access.index !14 #0
+@"llvm.bar:0:8$0:0:1" = external global i64, !llvm.preserve.access.index !14 #0
+@"llvm.bar:0:4$0:0:0" = external global i64, !llvm.preserve.access.index !14 #0
+@"llvm.bar:0:64$0:0:1:7:0" = external global i64, !llvm.preserve.access.index !14 #0
+@"llvm.bar:0:68$0:0:1:7:1" = external global i64, !llvm.preserve.access.index !14 #0
+@"llvm.bar:0:156$1:0:1:7:1" = external global i64, !llvm.preserve.access.index !14 #0
+
+; Function Attrs: nofree nounwind memory(readwrite, argmem: none)
+define dso_local void @root() local_unnamed_addr #1 !dbg !29 {
+entry:
+ call void @llvm.dbg.value(metadata ptr null, metadata !33, metadata !DIExpression()), !dbg !34
+ %0 = load i64, ptr @"llvm.bar:0:0$0:0", align 8
+ %1 = getelementptr i8, ptr null, i64 %0
+ %2 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 0, ptr %1)
+ store volatile ptr %2, ptr @g, align 8, !dbg !35, !tbaa !36
+ %3 = load i64, ptr @"llvm.bar:0:4$0:0:0", align 8
+ %4 = getelementptr i8, ptr null, i64 %3
+ %5 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 2, ptr %4)
+ store volatile ptr %5, ptr @g, align 8, !dbg !40, !tbaa !36
+ %6 = load i64, ptr @"llvm.bar:0:8$0:0:1", align 8
+ %7 = getelementptr i8, ptr null, i64 %6
+ %8 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 1, ptr %7)
+ store volatile ptr %8, ptr @g, align 8, !dbg !41, !tbaa !36
+ %9 = load i64, ptr @"llvm.bar:0:64$0:0:1:7:0", align 8
+ %10 = getelementptr i8, ptr null, i64 %9
+ %11 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 3, ptr %10)
+ store volatile ptr %11, ptr @g, align 8, !dbg !42, !tbaa !36
+ %12 = load i64, ptr @"llvm.bar:0:68$0:0:1:7:1", align 8
+ %13 = getelementptr i8, ptr null, i64 %12
+ %14 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 4, ptr %13)
+ store volatile ptr %14, ptr @g, align 8, !dbg !43, !tbaa !36
+ %15 = load i64, ptr @"llvm.bar:0:156$1:0:1:7:1", align 8
+ %16 = getelementptr i8, ptr null, i64 %15
+ %17 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 5, ptr %16)
+ store volatile ptr %17, ptr @g, align 8, !dbg !44, !tbaa !36
+ ret void, !dbg !45
+}
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare ptr @llvm.bpf.passthrough.p0.p0(i32, ptr) #2
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { "btf_ama" }
+attributes #1 = { nofree nounwind memory(readwrite, argmem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nofree nosync nounwind memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!24, !25, !26, !27}
+!llvm.ident = !{!28}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 18, type: !22, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !4, globals: !21, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "f7c638151153f385e69bef98e88c80ef")
+!4 = !{!5, !13}
+!5 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 640, elements: !11)
+!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "buz", file: !3, line: 3, size: 64, elements: !7)
+!7 = !{!8, !10}
+!8 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !6, file: !3, line: 4, baseType: !9, size: 32)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !6, file: !3, line: 5, baseType: !9, size: 32, offset: 32)
+!11 = !{!12}
+!12 = !DISubrange(count: 10)
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !3, line: 14, size: 704, elements: !15)
+!15 = !{!16}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "f", scope: !14, file: !3, line: 15, baseType: !17, size: 704)
+!17 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !3, line: 8, size: 704, elements: !18)
+!18 = !{!19, !20}
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !17, file: !3, line: 10, baseType: !9, size: 32, offset: 32)
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "k", scope: !17, file: !3, line: 11, baseType: !5, size: 640, offset: 64)
+!21 = !{!0}
+!22 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !23)
+!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!24 = !{i32 7, !"Dwarf Version", i32 5}
+!25 = !{i32 2, !"Debug Info Version", i32 3}
+!26 = !{i32 1, !"wchar_size", i32 4}
+!27 = !{i32 7, !"frame-pointer", i32 2}
+!28 = !{!"clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)"}
+!29 = distinct !DISubprogram(name: "root", scope: !3, file: !3, line: 20, type: !30, scopeLine: 20, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !32)
+!30 = !DISubroutineType(types: !31)
+!31 = !{null}
+!32 = !{!33}
+!33 = !DILocalVariable(name: "bar", scope: !29, file: !3, line: 21, type: !13)
+!34 = !DILocation(line: 0, scope: !29)
+!35 = !DILocation(line: 22, column: 5, scope: !29)
+!36 = !{!37, !37, i64 0}
+!37 = !{!"any pointer", !38, i64 0}
+!38 = !{!"omnipotent char", !39, i64 0}
+!39 = !{!"Simple C/C++ TBAA"}
+!40 = !DILocation(line: 23, column: 5, scope: !29)
+!41 = !DILocation(line: 24, column: 5, scope: !29)
+!42 = !DILocation(line: 25, column: 5, scope: !29)
+!43 = !DILocation(line: 26, column: 5, scope: !29)
+!44 = !DILocation(line: 27, column: 5, scope: !29)
+!45 = !DILocation(line: 28, column: 1, scope: !29)
diff --git a/llvm/test/tools/llvm-objdump/BPF/core-relo-enum-value.ll b/llvm/test/tools/llvm-objdump/BPF/core-relo-enum-value.ll
new file mode 100644
index 000000000000000..2a3f3b19641a807
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/BPF/core-relo-enum-value.ll
@@ -0,0 +1,86 @@
+; REQUIRES: bpf-registered-target
+
+;; Verify that llvm-objdump can use .BTF.ext to show CO-RE relocation data.
+
+; RUN: llc --mtriple bpfel %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+; RUN: llc --mtriple bpfeb %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+;; Input generated from the following C code:
+;;
+;; #define __pai __attribute__((preserve_access_index))
+;;
+;; enum bar { U, V };
+;; volatile unsigned long g;
+;; void root(void) {
+;; g = __builtin_preserve_enum_value(*(enum bar *)U, 0);
+;; g = __builtin_preserve_enum_value(*(enum bar *)V, 1);
+;; }
+;;
+;; Using the following command:
+;;
+;; clang --target=bpf -g -O2 -emit-llvm -S t.c
+
+; CHECK: CO-RE <enumval_exists> [[[#]]] enum bar::U = 0
+; CHECK: CO-RE <enumval_value> [[[#]]] enum bar::V = 1
+
+ at g = dso_local global i64 0, align 8, !dbg !0
+@"llvm.bar:11:1$1" = external global i64, !llvm.preserve.access.index !5 #0
+@"llvm.bar:10:1$0" = external global i64, !llvm.preserve.access.index !5 #0
+
+; Function Attrs: nofree nounwind memory(readwrite, argmem: none)
+define dso_local void @root() local_unnamed_addr #1 !dbg !18 {
+entry:
+ %0 = load i64, ptr @"llvm.bar:10:1$0", align 8
+ %1 = tail call i64 @llvm.bpf.passthrough.i64.i64(i32 1, i64 %0)
+ store volatile i64 %1, ptr @g, align 8, !dbg !22, !tbaa !23
+ %2 = load i64, ptr @"llvm.bar:11:1$1", align 8
+ %3 = tail call i64 @llvm.bpf.passthrough.i64.i64(i32 0, i64 %2)
+ store volatile i64 %3, ptr @g, align 8, !dbg !27, !tbaa !23
+ ret void, !dbg !28
+}
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare i64 @llvm.bpf.passthrough.i64.i64(i32, i64) #2
+
+attributes #0 = { "btf_ama" }
+attributes #1 = { nofree nounwind memory(readwrite, argmem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nofree nosync nounwind memory(none) }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!13, !14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !11, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !10, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "5423aa9ef48cb61e948b5c2bd75fd1df")
+!4 = !{!5}
+!5 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "bar", file: !3, line: 3, baseType: !6, size: 32, elements: !7)
+!6 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!7 = !{!8, !9}
+!8 = !DIEnumerator(name: "U", value: 0)
+!9 = !DIEnumerator(name: "V", value: 1)
+!10 = !{!0}
+!11 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !12)
+!12 = !DIBasicType(name: "unsigned long", size: 64, encoding: DW_ATE_unsigned)
+!13 = !{i32 7, !"Dwarf Version", i32 5}
+!14 = !{i32 2, !"Debug Info Version", i32 3}
+!15 = !{i32 1, !"wchar_size", i32 4}
+!16 = !{i32 7, !"frame-pointer", i32 2}
+!17 = !{!"clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)"}
+!18 = distinct !DISubprogram(name: "root", scope: !3, file: !3, line: 5, type: !19, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !21)
+!19 = !DISubroutineType(types: !20)
+!20 = !{null}
+!21 = !{}
+!22 = !DILocation(line: 6, column: 5, scope: !18)
+!23 = !{!24, !24, i64 0}
+!24 = !{!"long", !25, i64 0}
+!25 = !{!"omnipotent char", !26, i64 0}
+!26 = !{!"Simple C/C++ TBAA"}
+!27 = !DILocation(line: 7, column: 5, scope: !18)
+!28 = !DILocation(line: 8, column: 1, scope: !18)
diff --git a/llvm/test/tools/llvm-objdump/BPF/core-relo-field-info.ll b/llvm/test/tools/llvm-objdump/BPF/core-relo-field-info.ll
new file mode 100644
index 000000000000000..7878e04c4b68465
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/BPF/core-relo-field-info.ll
@@ -0,0 +1,124 @@
+; REQUIRES: bpf-registered-target
+
+;; Verify that llvm-objdump can use .BTF.ext to show CO-RE relocation data.
+
+; RUN: llc --mtriple bpfel %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+; RUN: llc --mtriple bpfeb %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+;; Input generated from the following C code:
+;;
+;; #define __pai __attribute__((preserve_access_index))
+;;
+;; struct bar { int a; } __pai;
+;; volatile unsigned long g;
+;; void root(void) {
+;; struct bar *bar = (void *)0;
+;; g = __builtin_preserve_field_info(bar->a, 1);
+;; g = __builtin_preserve_field_info(bar->a, 2);
+;; g = __builtin_preserve_field_info(bar->a, 3);
+;; g = __builtin_preserve_field_info(bar->a, 4);
+;; g = __builtin_preserve_field_info(bar->a, 5);
+;; }
+;;
+;; Using the following command:
+;;
+;; clang --target=bpf -g -O2 -emit-llvm -S t.c
+
+; CHECK: CO-RE <byte_sz> [[[#]]] struct bar::a
+; CHECK: CO-RE <field_exists> [[[#]]] struct bar::a
+; CHECK: CO-RE <signed> [[[#]]] struct bar::a
+; CHECK: CO-RE <lshift_u64> [[[#]]] struct bar::a
+; CHECK: CO-RE <rshift_u64> [[[#]]] struct bar::a
+
+ at g = dso_local global i64 0, align 8, !dbg !0
+@"llvm.bar:1:4$0:0" = external global i32, !llvm.preserve.access.index !7 #0
+@"llvm.bar:2:1$0:0" = external global i32, !llvm.preserve.access.index !7 #0
+@"llvm.bar:3:1$0:0" = external global i32, !llvm.preserve.access.index !7 #0
+@"llvm.bar:4:32$0:0" = external global i32, !llvm.preserve.access.index !7 #0
+@"llvm.bar:5:32$0:0" = external global i32, !llvm.preserve.access.index !7 #0
+
+; Function Attrs: nofree nounwind memory(readwrite, argmem: none)
+define dso_local void @root() local_unnamed_addr #1 !dbg !16 {
+entry:
+ call void @llvm.dbg.value(metadata ptr null, metadata !20, metadata !DIExpression()), !dbg !22
+ %0 = load i32, ptr @"llvm.bar:1:4$0:0", align 4
+ %1 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 0, i32 %0)
+ %conv = zext i32 %1 to i64, !dbg !23
+ store volatile i64 %conv, ptr @g, align 8, !dbg !24, !tbaa !25
+ %2 = load i32, ptr @"llvm.bar:2:1$0:0", align 4
+ %3 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 1, i32 %2)
+ %conv1 = zext i32 %3 to i64, !dbg !29
+ store volatile i64 %conv1, ptr @g, align 8, !dbg !30, !tbaa !25
+ %4 = load i32, ptr @"llvm.bar:3:1$0:0", align 4
+ %5 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 2, i32 %4)
+ %conv2 = zext i32 %5 to i64, !dbg !31
+ store volatile i64 %conv2, ptr @g, align 8, !dbg !32, !tbaa !25
+ %6 = load i32, ptr @"llvm.bar:4:32$0:0", align 4
+ %7 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 3, i32 %6)
+ %conv3 = zext i32 %7 to i64, !dbg !33
+ store volatile i64 %conv3, ptr @g, align 8, !dbg !34, !tbaa !25
+ %8 = load i32, ptr @"llvm.bar:5:32$0:0", align 4
+ %9 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 4, i32 %8)
+ %conv4 = zext i32 %9 to i64, !dbg !35
+ store volatile i64 %conv4, ptr @g, align 8, !dbg !36, !tbaa !25
+ ret void, !dbg !37
+}
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare i32 @llvm.bpf.passthrough.i32.i32(i32, i32) #2
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { "btf_ama" }
+attributes #1 = { nofree nounwind memory(readwrite, argmem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nofree nosync nounwind memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "ff78616039301f51cd56ee6ea1377b86")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !6)
+!6 = !DIBasicType(name: "unsigned long", size: 64, encoding: DW_ATE_unsigned)
+!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !3, line: 3, size: 32, elements: !8)
+!8 = !{!9}
+!9 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !7, file: !3, line: 3, baseType: !10, size: 32)
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !{i32 7, !"Dwarf Version", i32 5}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{i32 7, !"frame-pointer", i32 2}
+!15 = !{!"clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)"}
+!16 = distinct !DISubprogram(name: "root", scope: !3, file: !3, line: 5, type: !17, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !19)
+!17 = !DISubroutineType(types: !18)
+!18 = !{null}
+!19 = !{!20}
+!20 = !DILocalVariable(name: "bar", scope: !16, file: !3, line: 6, type: !21)
+!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!22 = !DILocation(line: 0, scope: !16)
+!23 = !DILocation(line: 7, column: 7, scope: !16)
+!24 = !DILocation(line: 7, column: 5, scope: !16)
+!25 = !{!26, !26, i64 0}
+!26 = !{!"long", !27, i64 0}
+!27 = !{!"omnipotent char", !28, i64 0}
+!28 = !{!"Simple C/C++ TBAA"}
+!29 = !DILocation(line: 8, column: 7, scope: !16)
+!30 = !DILocation(line: 8, column: 5, scope: !16)
+!31 = !DILocation(line: 9, column: 7, scope: !16)
+!32 = !DILocation(line: 9, column: 5, scope: !16)
+!33 = !DILocation(line: 10, column: 7, scope: !16)
+!34 = !DILocation(line: 10, column: 5, scope: !16)
+!35 = !DILocation(line: 11, column: 7, scope: !16)
+!36 = !DILocation(line: 11, column: 5, scope: !16)
+!37 = !DILocation(line: 12, column: 1, scope: !16)
diff --git a/llvm/test/tools/llvm-objdump/BPF/core-relo-formatting.s b/llvm/test/tools/llvm-objdump/BPF/core-relo-formatting.s
new file mode 100644
index 000000000000000..98cd31f6406a4f8
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/BPF/core-relo-formatting.s
@@ -0,0 +1,614 @@
+# REQUIRES: bpf-registered-target
+
+## Verify that when llvm-objdump uses .BTF.ext to show CO-RE
+## relocations formatting options operate as expected.
+
+# RUN: llvm-mc --triple bpfel %s --filetype=obj | \
+# RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+# RUN: FileCheck --strict-whitespace --check-prefix=NOADDR %s
+
+# RUN: llvm-mc --triple bpfel %s --filetype=obj | \
+# RUN: llvm-objdump --no-addresses --no-show-raw-insn -d - | \
+# RUN: FileCheck --strict-whitespace --check-prefix=NORELO %s
+
+# RUN: llvm-mc --triple bpfel %s --filetype=obj | \
+# RUN: llvm-objdump --no-show-raw-insn -dr - | \
+# RUN: FileCheck --strict-whitespace --check-prefix=ADDR %s
+
+# RUN: llvm-mc --triple bpfel %s --filetype=obj | \
+# RUN: llvm-objdump --adjust-vma=0x10 --no-show-raw-insn -dr - | \
+# RUN: FileCheck --strict-whitespace --check-prefix=VMA %s
+
+## Input generated from the following C code:
+##
+## #define __pai __attribute__((preserve_access_index))
+## struct foo {
+## int a;
+## } __pai;
+## enum bar { U, V };
+## extern void consume(unsigned long);
+## void root() {
+## asm volatile("r0 = 42;":::);
+## struct foo *foo = 0;
+## consume(__builtin_preserve_type_info(*foo, 0));
+## consume((unsigned long) &foo->a);
+## consume(__builtin_preserve_enum_value(*(enum bar *)U, 0));
+## }
+##
+## Using the following command:
+##
+## clang -target bpf -g -O2 -S t.c
+
+# NOADDR: r1 = 0x1
+# NOADDR-NEXT: CO-RE <type_exists> [3] struct foo
+# NOADDR-NEXT: call -0x1
+# NOADDR-NEXT: R_BPF_64_32 consume
+# NOADDR-NEXT: r1 = 0x0
+# NOADDR-NEXT: CO-RE <byte_off> [3] struct foo::a (0:0)
+# NOADDR-NEXT: call -0x1
+# NOADDR-NEXT: R_BPF_64_32 consume
+# NOADDR-NEXT: r1 = 0x1 ll
+# NOADDR-NEXT: CO-RE <enumval_exists> [8] enum bar::U = 0
+# NOADDR-NEXT: call -0x1
+# NOADDR-NEXT: R_BPF_64_32 consume
+# NOADDR-NEXT: exit
+
+# NORELO: r1 = 0x1
+# NORELO-NEXT: call -0x1
+# NORELO-NEXT: r1 = 0x0
+# NORELO-NEXT: call -0x1
+# NORELO-NEXT: r1 = 0x1 ll
+# NORELO-NEXT: call -0x1
+# NORELO-NEXT: exit
+
+# ADDR: 1: r1 = 0x1
+# ADDR-NEXT: 0000000000000008: CO-RE <type_exists> [3] struct foo
+# ADDR-NEXT: 2: call -0x1
+# ADDR-NEXT: 0000000000000010: R_BPF_64_32 consume
+# ADDR-NEXT: 3: r1 = 0x0
+# ADDR-NEXT: 0000000000000018: CO-RE <byte_off> [3] struct foo::a (0:0)
+# ADDR-NEXT: 4: call -0x1
+# ADDR-NEXT: 0000000000000020: R_BPF_64_32 consume
+# ADDR-NEXT: 5: r1 = 0x1 ll
+# ADDR-NEXT: 0000000000000028: CO-RE <enumval_exists> [8] enum bar::U = 0
+# ADDR-NEXT: 7: call -0x1
+# ADDR-NEXT: 0000000000000038: R_BPF_64_32 consume
+# ADDR-NEXT: 8: exit
+
+# VMA: 3: r1 = 0x1
+# VMA-NEXT: 0000000000000018: CO-RE <type_exists> [3] struct foo
+# VMA-NEXT: 4: call -0x1
+# VMA-NEXT: 0000000000000010: R_BPF_64_32 consume
+# VMA-NEXT: 5: r1 = 0x0
+# VMA-NEXT: 0000000000000028: CO-RE <byte_off> [3] struct foo::a (0:0)
+# VMA-NEXT: 6: call -0x1
+# VMA-NEXT: 0000000000000020: R_BPF_64_32 consume
+# VMA-NEXT: 7: r1 = 0x1 ll
+# VMA-NEXT: 0000000000000038: CO-RE <enumval_exists> [8] enum bar::U = 0
+# VMA-NEXT: 9: call -0x1
+# VMA-NEXT: 0000000000000038: R_BPF_64_32 consume
+# VMA-NEXT: 10: exit
+
+ .text
+ .file "t.c"
+ .file 0 "/home/eddy/work/tmp" "t.c" md5 0x7675be79a30f35c69b89cf826ff55a5f
+ .globl root # -- Begin function root
+ .p2align 3
+ .type root, at function
+root: # @root
+.Lfunc_begin0:
+ .cfi_sections .debug_frame
+ .cfi_startproc
+# %bb.0: # %entry
+ .loc 0 8 3 prologue_end # t.c:8:3
+.Ltmp0:
+ #APP
+ r0 = 42
+
+ #NO_APP
+.Ltmp1:
+.Ltmp2:
+ #DEBUG_VALUE: root:foo <- 0
+ .loc 0 10 3 # t.c:10:3
+.Ltmp3:
+.Ltmp4:
+ r1 = 1
+ call consume
+.Ltmp5:
+ .loc 0 11 3 # t.c:11:3
+.Ltmp6:
+.Ltmp7:
+ r1 = 0
+ call consume
+.Ltmp8:
+ .loc 0 12 3 # t.c:12:3
+.Ltmp9:
+.Ltmp10:
+ r1 = 1 ll
+ call consume
+.Ltmp11:
+ .loc 0 13 1 # t.c:13:1
+.Ltmp12:
+ exit
+.Ltmp13:
+.Ltmp14:
+.Lfunc_end0:
+ .size root, .Lfunc_end0-root
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 2 # Loc expr size
+ .byte 48 # DW_OP_lit0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 4 # DW_TAG_enumeration_type
+ .byte 1 # DW_CHILDREN_yes
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 40 # DW_TAG_enumerator
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 28 # DW_AT_const_value
+ .byte 15 # DW_FORM_udata
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 72 # DW_TAG_call_site
+ .byte 0 # DW_CHILDREN_no
+ .byte 127 # DW_AT_call_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 125 # DW_AT_call_return_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 60 # DW_AT_declaration
+ .byte 25 # DW_FORM_flag_present
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 11 # Abbreviation Code
+ .byte 19 # DW_TAG_structure_type
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 12 # Abbreviation Code
+ .byte 13 # DW_TAG_member
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 56 # DW_AT_data_member_location
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x7d DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x10 DW_TAG_enumeration_type
+ .long 55 # DW_AT_type
+ .byte 6 # DW_AT_name
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .byte 3 # Abbrev [3] 0x30:0x3 DW_TAG_enumerator
+ .byte 4 # DW_AT_name
+ .byte 0 # DW_AT_const_value
+ .byte 3 # Abbrev [3] 0x33:0x3 DW_TAG_enumerator
+ .byte 5 # DW_AT_name
+ .byte 1 # DW_AT_const_value
+ .byte 0 # End Of Children Mark
+ .byte 4 # Abbrev [4] 0x37:0x4 DW_TAG_base_type
+ .byte 3 # DW_AT_name
+ .byte 7 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 4 # Abbrev [4] 0x3b:0x4 DW_TAG_base_type
+ .byte 7 # DW_AT_name
+ .byte 7 # DW_AT_encoding
+ .byte 8 # DW_AT_byte_size
+ .byte 5 # Abbrev [5] 0x3f:0x27 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 90
+ # DW_AT_call_all_calls
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 7 # DW_AT_decl_line
+ # DW_AT_external
+ .byte 6 # Abbrev [6] 0x4a:0x9 DW_TAG_variable
+ .byte 0 # DW_AT_location
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 9 # DW_AT_decl_line
+ .long 112 # DW_AT_type
+ .byte 7 # Abbrev [7] 0x53:0x6 DW_TAG_call_site
+ .long 102 # DW_AT_call_origin
+ .byte 1 # DW_AT_call_return_pc
+ .byte 7 # Abbrev [7] 0x59:0x6 DW_TAG_call_site
+ .long 102 # DW_AT_call_origin
+ .byte 2 # DW_AT_call_return_pc
+ .byte 7 # Abbrev [7] 0x5f:0x6 DW_TAG_call_site
+ .long 102 # DW_AT_call_origin
+ .byte 3 # DW_AT_call_return_pc
+ .byte 0 # End Of Children Mark
+ .byte 8 # Abbrev [8] 0x66:0xa DW_TAG_subprogram
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ # DW_AT_prototyped
+ # DW_AT_declaration
+ # DW_AT_external
+ .byte 9 # Abbrev [9] 0x6a:0x5 DW_TAG_formal_parameter
+ .long 59 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 10 # Abbrev [10] 0x70:0x5 DW_TAG_pointer_type
+ .long 117 # DW_AT_type
+ .byte 11 # Abbrev [11] 0x75:0xf DW_TAG_structure_type
+ .byte 10 # DW_AT_name
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .byte 12 # Abbrev [12] 0x7a:0x9 DW_TAG_member
+ .byte 11 # DW_AT_name
+ .long 132 # DW_AT_type
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .byte 0 # DW_AT_data_member_location
+ .byte 0 # End Of Children Mark
+ .byte 4 # Abbrev [4] 0x84:0x4 DW_TAG_base_type
+ .byte 12 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 56 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 17.0.0 (/home/eddy/work/llvm-project/clang 76d673bb89f8ec8cf65a4294a98a83c9d6646b11)" # string offset=0
+.Linfo_string1:
+ .asciz "t.c" # string offset=99
+.Linfo_string2:
+ .asciz "/home/eddy/work/tmp" # string offset=103
+.Linfo_string3:
+ .asciz "unsigned int" # string offset=123
+.Linfo_string4:
+ .asciz "U" # string offset=136
+.Linfo_string5:
+ .asciz "V" # string offset=138
+.Linfo_string6:
+ .asciz "bar" # string offset=140
+.Linfo_string7:
+ .asciz "unsigned long" # string offset=144
+.Linfo_string8:
+ .asciz "consume" # string offset=158
+.Linfo_string9:
+ .asciz "root" # string offset=166
+.Linfo_string10:
+ .asciz "foo" # string offset=171
+.Linfo_string11:
+ .asciz "a" # string offset=175
+.Linfo_string12:
+ .asciz "int" # string offset=177
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .long .Linfo_string11
+ .long .Linfo_string12
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp5
+ .quad .Ltmp8
+ .quad .Ltmp11
+.Ldebug_addr_end0:
+ .section .BTF,"", at progbits
+ .short 60319 # 0xeb9f
+ .byte 1
+ .byte 0
+ .long 24
+ .long 0
+ .long 140
+ .long 140
+ .long 262
+ .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
+ .long 218103808 # 0xd000000
+ .long 0
+ .long 1 # BTF_KIND_FUNC(id = 2)
+ .long 201326593 # 0xc000001
+ .long 1
+ .long 67 # BTF_KIND_STRUCT(id = 3)
+ .long 67108865 # 0x4000001
+ .long 4
+ .long 71
+ .long 4
+ .long 0 # 0x0
+ .long 73 # BTF_KIND_INT(id = 4)
+ .long 16777216 # 0x1000000
+ .long 4
+ .long 16777248 # 0x1000020
+ .long 0 # BTF_KIND_FUNC_PROTO(id = 5)
+ .long 218103809 # 0xd000001
+ .long 0
+ .long 0
+ .long 6
+ .long 129 # BTF_KIND_INT(id = 6)
+ .long 16777216 # 0x1000000
+ .long 8
+ .long 64 # 0x40
+ .long 143 # BTF_KIND_FUNC(id = 7)
+ .long 201326594 # 0xc000002
+ .long 5
+ .long 191 # BTF_KIND_ENUM(id = 8)
+ .long 100663298 # 0x6000002
+ .long 4
+ .long 195
+ .long 0
+ .long 197
+ .long 1
+ .byte 0 # string offset=0
+ .ascii "root" # string offset=1
+ .byte 0
+ .ascii ".text" # string offset=6
+ .byte 0
+ .ascii "/home/eddy/work/tmp/t.c" # string offset=12
+ .byte 0
+ .ascii " asm volatile(\"r0 = 42;\":::);" # string offset=36
+ .byte 0
+ .ascii "foo" # string offset=67
+ .byte 0
+ .byte 97 # string offset=71
+ .byte 0
+ .ascii "int" # string offset=73
+ .byte 0
+ .byte 48 # string offset=77
+ .byte 0
+ .ascii " consume(__builtin_preserve_type_info(*foo, 0));" # string offset=79
+ .byte 0
+ .ascii "unsigned long" # string offset=129
+ .byte 0
+ .ascii "consume" # string offset=143
+ .byte 0
+ .ascii "0:0" # string offset=151
+ .byte 0
+ .ascii " consume((unsigned long) &foo->a);" # string offset=155
+ .byte 0
+ .ascii "bar" # string offset=191
+ .byte 0
+ .byte 85 # string offset=195
+ .byte 0
+ .byte 86 # string offset=197
+ .byte 0
+ .ascii " consume(__builtin_preserve_enum_value(*(enum bar *)U, 0));" # string offset=199
+ .byte 0
+ .byte 125 # string offset=260
+ .byte 0
+ .section .BTF.ext,"", at progbits
+ .short 60319 # 0xeb9f
+ .byte 1
+ .byte 0
+ .long 32
+ .long 0
+ .long 20
+ .long 20
+ .long 92
+ .long 112
+ .long 60
+ .long 8 # FuncInfo
+ .long 6 # FuncInfo section string offset=6
+ .long 1
+ .long .Lfunc_begin0
+ .long 2
+ .long 16 # LineInfo
+ .long 6 # LineInfo section string offset=6
+ .long 5
+ .long .Ltmp0
+ .long 12
+ .long 36
+ .long 8195 # Line 8 Col 3
+ .long .Ltmp4
+ .long 12
+ .long 79
+ .long 10243 # Line 10 Col 3
+ .long .Ltmp7
+ .long 12
+ .long 155
+ .long 11267 # Line 11 Col 3
+ .long .Ltmp10
+ .long 12
+ .long 199
+ .long 12291 # Line 12 Col 3
+ .long .Ltmp12
+ .long 12
+ .long 260
+ .long 13313 # Line 13 Col 1
+ .long 16 # FieldReloc
+ .long 6 # Field reloc section string offset=6
+ .long 3
+ .long .Ltmp3
+ .long 3
+ .long 77
+ .long 8
+ .long .Ltmp6
+ .long 3
+ .long 151
+ .long 0
+ .long .Ltmp9
+ .long 8
+ .long 77
+ .long 10
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-objdump/BPF/core-relo-type-id.ll b/llvm/test/tools/llvm-objdump/BPF/core-relo-type-id.ll
new file mode 100644
index 000000000000000..51b6d214582bb22
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/BPF/core-relo-type-id.ll
@@ -0,0 +1,91 @@
+; REQUIRES: bpf-registered-target
+
+;; Verify that llvm-objdump can use .BTF.ext to show CO-RE relocation data.
+
+; RUN: llc --mtriple bpfel %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+; RUN: llc --mtriple bpfeb %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+;; Input generated from the following C code:
+;;
+;; #define __pai __attribute__((preserve_access_index))
+;;
+;; struct bar { } __pai;
+;; volatile unsigned long g;
+;; void root(void) {
+;; struct bar *bar = (void *)0;
+;; g = __builtin_btf_type_id(*bar, 0);
+;; g = __builtin_btf_type_id(*bar, 1);
+;; }
+;;
+;; Using the following command:
+;;
+;; clang --target=bpf -g -O2 -emit-llvm -S t.c
+
+; CHECK: CO-RE <local_type_id> [[[#]]] struct bar
+; CHECK: CO-RE <target_type_id> [[[#]]] struct bar
+
+ at g = dso_local global i64 0, align 8, !dbg !0
+@"llvm.btf_type_id.0$6" = external global i64, !llvm.preserve.access.index !7 #0
+@"llvm.btf_type_id.1$7" = external global i64, !llvm.preserve.access.index !7 #0
+
+; Function Attrs: nofree nounwind memory(readwrite, argmem: none)
+define dso_local void @root() local_unnamed_addr #1 !dbg !14 {
+entry:
+ call void @llvm.dbg.value(metadata ptr null, metadata !18, metadata !DIExpression()), !dbg !20
+ %0 = load i64, ptr @"llvm.btf_type_id.0$6", align 8
+ %1 = tail call i64 @llvm.bpf.passthrough.i64.i64(i32 0, i64 %0)
+ store volatile i64 %1, ptr @g, align 8, !dbg !21, !tbaa !22
+ %2 = load i64, ptr @"llvm.btf_type_id.1$7", align 8
+ %3 = tail call i64 @llvm.bpf.passthrough.i64.i64(i32 1, i64 %2)
+ store volatile i64 %3, ptr @g, align 8, !dbg !26, !tbaa !22
+ ret void, !dbg !27
+}
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare i64 @llvm.bpf.passthrough.i64.i64(i32, i64) #2
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { "btf_type_id" }
+attributes #1 = { nofree nounwind memory(readwrite, argmem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nofree nosync nounwind memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!9, !10, !11, !12}
+!llvm.ident = !{!13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "29efc9dba44aaba9e4b0c389bb8694ea")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !6)
+!6 = !DIBasicType(name: "unsigned long", size: 64, encoding: DW_ATE_unsigned)
+!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !3, line: 3, elements: !8)
+!8 = !{}
+!9 = !{i32 7, !"Dwarf Version", i32 5}
+!10 = !{i32 2, !"Debug Info Version", i32 3}
+!11 = !{i32 1, !"wchar_size", i32 4}
+!12 = !{i32 7, !"frame-pointer", i32 2}
+!13 = !{!"clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)"}
+!14 = distinct !DISubprogram(name: "root", scope: !3, file: !3, line: 5, type: !15, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
+!15 = !DISubroutineType(types: !16)
+!16 = !{null}
+!17 = !{!18}
+!18 = !DILocalVariable(name: "bar", scope: !14, file: !3, line: 6, type: !19)
+!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!20 = !DILocation(line: 0, scope: !14)
+!21 = !DILocation(line: 7, column: 5, scope: !14)
+!22 = !{!23, !23, i64 0}
+!23 = !{!"long", !24, i64 0}
+!24 = !{!"omnipotent char", !25, i64 0}
+!25 = !{!"Simple C/C++ TBAA"}
+!26 = !DILocation(line: 8, column: 5, scope: !14)
+!27 = !DILocation(line: 9, column: 1, scope: !14)
diff --git a/llvm/test/tools/llvm-objdump/BPF/core-relo-type-info.ll b/llvm/test/tools/llvm-objdump/BPF/core-relo-type-info.ll
new file mode 100644
index 000000000000000..53775edd9d993f4
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/BPF/core-relo-type-info.ll
@@ -0,0 +1,104 @@
+; REQUIRES: bpf-registered-target
+
+;; Verify that llvm-objdump can use .BTF.ext to show CO-RE relocation data.
+
+; RUN: llc --mtriple bpfel %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+; RUN: llc --mtriple bpfeb %s --filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn -dr - | \
+; RUN: FileCheck %s
+
+;; Input generated from the following C code:
+;;
+;; #define __pai __attribute__((preserve_access_index))
+;;
+;; struct bar { } __pai;
+;; volatile unsigned long g;
+;; void root(void) {
+;; struct bar *bar = (void *)0;
+;; g = __builtin_preserve_type_info(*bar, 0);
+;; g = __builtin_preserve_type_info(*bar, 1);
+;; g = __builtin_preserve_type_info(*bar, 2);
+;; }
+;;
+;; Using the following command:
+;;
+;; clang --target=bpf -g -O2 -emit-llvm -S t.c
+
+; CHECK: CO-RE <type_exists> [[[#]]] struct bar
+; CHECK: CO-RE <type_size> [[[#]]] struct bar
+; CHECK: CO-RE <type_matches> [[[#]]] struct bar
+
+ at g = dso_local global i64 0, align 8, !dbg !0
+@"llvm.bar:8:1$0" = external global i32, !llvm.preserve.access.index !7 #0
+@"llvm.bar:9:0$0" = external global i32, !llvm.preserve.access.index !7 #0
+@"llvm.bar:12:1$0" = external global i32, !llvm.preserve.access.index !7 #0
+
+; Function Attrs: nofree nounwind memory(readwrite, argmem: none)
+define dso_local void @root() local_unnamed_addr #1 !dbg !14 {
+entry:
+ call void @llvm.dbg.value(metadata ptr null, metadata !18, metadata !DIExpression()), !dbg !20
+ %0 = load i32, ptr @"llvm.bar:8:1$0", align 4
+ %1 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 0, i32 %0)
+ %conv = zext i32 %1 to i64, !dbg !21
+ store volatile i64 %conv, ptr @g, align 8, !dbg !22, !tbaa !23
+ %2 = load i32, ptr @"llvm.bar:9:0$0", align 4
+ %3 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 1, i32 %2)
+ %conv1 = zext i32 %3 to i64, !dbg !27
+ store volatile i64 %conv1, ptr @g, align 8, !dbg !28, !tbaa !23
+ %4 = load i32, ptr @"llvm.bar:12:1$0", align 4
+ %5 = tail call i32 @llvm.bpf.passthrough.i32.i32(i32 2, i32 %4)
+ %conv2 = zext i32 %5 to i64, !dbg !29
+ store volatile i64 %conv2, ptr @g, align 8, !dbg !30, !tbaa !23
+ ret void, !dbg !31
+}
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare i32 @llvm.bpf.passthrough.i32.i32(i32, i32) #2
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { "btf_ama" }
+attributes #1 = { nofree nounwind memory(readwrite, argmem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nofree nosync nounwind memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!9, !10, !11, !12}
+!llvm.ident = !{!13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "5bf218e82301e866fa302fd927913bcf")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !6)
+!6 = !DIBasicType(name: "unsigned long", size: 64, encoding: DW_ATE_unsigned)
+!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !3, line: 3, elements: !8)
+!8 = !{}
+!9 = !{i32 7, !"Dwarf Version", i32 5}
+!10 = !{i32 2, !"Debug Info Version", i32 3}
+!11 = !{i32 1, !"wchar_size", i32 4}
+!12 = !{i32 7, !"frame-pointer", i32 2}
+!13 = !{!"clang version 17.0.0 (/home/eddy/work/llvm-project/clang 2f8c5c0afd1d79a771dd74c8fb1e5bbae6d04eb7)"}
+!14 = distinct !DISubprogram(name: "root", scope: !3, file: !3, line: 5, type: !15, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
+!15 = !DISubroutineType(types: !16)
+!16 = !{null}
+!17 = !{!18}
+!18 = !DILocalVariable(name: "bar", scope: !14, file: !3, line: 6, type: !19)
+!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!20 = !DILocation(line: 0, scope: !14)
+!21 = !DILocation(line: 7, column: 7, scope: !14)
+!22 = !DILocation(line: 7, column: 5, scope: !14)
+!23 = !{!24, !24, i64 0}
+!24 = !{!"long", !25, i64 0}
+!25 = !{!"omnipotent char", !26, i64 0}
+!26 = !{!"Simple C/C++ TBAA"}
+!27 = !DILocation(line: 8, column: 7, scope: !14)
+!28 = !DILocation(line: 8, column: 5, scope: !14)
+!29 = !DILocation(line: 9, column: 7, scope: !14)
+!30 = !DILocation(line: 9, column: 5, scope: !14)
+!31 = !DILocation(line: 10, column: 1, scope: !14)
diff --git a/llvm/tools/llvm-objdump/CMakeLists.txt b/llvm/tools/llvm-objdump/CMakeLists.txt
index 73c857085e65bf8..ae173957b226073 100644
--- a/llvm/tools/llvm-objdump/CMakeLists.txt
+++ b/llvm/tools/llvm-objdump/CMakeLists.txt
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
AllTargetsDisassemblers
AllTargetsInfos
BinaryFormat
+ DebugInfoBTF
DebugInfoDWARF
Demangle
MC
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index e4e1ceb9b3f97f6..fcfce7764e012d0 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -31,6 +31,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/BTF/BTFParser.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
@@ -535,6 +536,22 @@ static void printRelocation(formatted_raw_ostream &OS, StringRef FileName,
OS << Name << "\t" << Val;
}
+static void printBTFRelocation(formatted_raw_ostream &FOS, llvm::BTFParser &BTF,
+ object::SectionedAddress Address,
+ LiveVariablePrinter &LVP) {
+ const llvm::BTF::BPFFieldReloc *Reloc = BTF.findFieldReloc(Address);
+ if (!Reloc)
+ return;
+
+ SmallString<64> Val;
+ BTF.symbolize(Reloc, Val);
+ FOS << "\t\t";
+ if (LeadingAddr)
+ FOS << format("%016" PRIx64 ": ", Address.Address + AdjustVMA);
+ FOS << "CO-RE " << Val;
+ LVP.printAfterOtherLine(FOS, true);
+}
+
class PrettyPrinter {
public:
virtual ~PrettyPrinter() = default;
@@ -1626,6 +1643,16 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
if (SymbolizeOperands && !Obj.isRelocatableObject())
ReadBBAddrMap();
+ std::optional<llvm::BTFParser> BTF;
+ if (InlineRelocs && BTFParser::hasBTFSections(Obj)) {
+ BTF.emplace();
+ BTFParser::ParseOptions Opts = {};
+ Opts.LoadTypes = true;
+ Opts.LoadRelocs = true;
+ if (Error E = BTF->parse(Obj, Opts))
+ WithColor::defaultErrorHandler(std::move(E));
+ }
+
for (const SectionRef &Section : ToolSectionFilter(Obj)) {
if (FilterSections.empty() && !DisassembleAll &&
(!Section.isText() || Section.isVirtual()))
@@ -2163,6 +2190,9 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
*DT->SubtargetInfo, CommentStream.str(), LVP);
Comments.clear();
+ if (BTF)
+ printBTFRelocation(FOS, *BTF, {Index, Section.getIndex()}, LVP);
+
// Hexagon does this in pretty printer
if (Obj.getArch() != Triple::hexagon) {
// Print relocation for instruction and data.
diff --git a/llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp b/llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp
index 64ba8222a267c40..7b4f7939e678304 100644
--- a/llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp
+++ b/llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp
@@ -16,6 +16,7 @@ using namespace llvm;
using namespace llvm::object;
#define LC(Line, Col) ((Line << 10u) | Col)
+#define ASSERT_SUCCEEDED(E) ASSERT_THAT_ERROR((E), Succeeded())
const char BTFEndOfData[] =
"error while reading .BTF section: unexpected end of data";
@@ -52,7 +53,7 @@ struct MockData1 {
#pragma pack(push, 1)
struct B {
BTF::Header Header = {};
- // no types
+ // No types.
struct S {
char Foo[4] = "foo";
char Bar[4] = "bar";
@@ -74,7 +75,7 @@ struct MockData1 {
struct E {
BTF::ExtHeader Header = {};
- // no func info
+ // No func info.
struct {
uint32_t LineRecSize = sizeof(BTF::BPFLineInfo);
struct {
@@ -163,7 +164,7 @@ TEST(BTFParserTest, simpleCorrectInput) {
EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File1)), "a.c");
EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File2)), "b.c");
- // Invalid offset
+ // Invalid offset.
EXPECT_EQ(BTF.findString(sizeof(MockData1::B::S)), StringRef());
const BTF::BPFLineInfo *I1 = BTF.findLineInfo({16, 1});
@@ -187,10 +188,10 @@ TEST(BTFParserTest, simpleCorrectInput) {
EXPECT_EQ(BTF.findString(I3->FileNameOff), "b.c");
EXPECT_EQ(BTF.findString(I3->LineOff), "first line");
- // No info for insn address
+ // No info for insn address.
EXPECT_FALSE(BTF.findLineInfo({24, 1}));
EXPECT_FALSE(BTF.findLineInfo({8, 2}));
- // No info for section number
+ // No info for section number.
EXPECT_FALSE(BTF.findLineInfo({16, 3}));
}
@@ -202,13 +203,13 @@ TEST(BTFParserTest, badSectionNameOffset) {
Mock.Ext.Lines.Foo.Sec.SecNameOff = 100500;
Error Err = BTF.parse(Mock.makeObj());
EXPECT_FALSE(Err);
- // "foo" line info should be corrupted
+ // "foo" line info should be corrupted.
EXPECT_FALSE(BTF.findLineInfo({16, 1}));
- // "bar" line info should be ok
+ // "bar" line info should be ok.
EXPECT_TRUE(BTF.findLineInfo({0, 2}));
}
-// Keep this as macro to preserve line number info
+// Keep this as macro to preserve line number info.
#define EXPECT_PARSE_ERROR(Mock, Message) \
do { \
BTFParser BTF; \
@@ -237,12 +238,12 @@ TEST(BTFParserTest, badBTFHdrLen) {
TEST(BTFParserTest, badBTFSectionLen) {
MockData1 Mock1, Mock2;
- // Cut-off string section by one byte
+ // Cut-off string section by one byte.
Mock1.BTFSectionLen =
offsetof(MockData1::B, Strings) + sizeof(MockData1::B::S) - 1;
EXPECT_PARSE_ERROR(Mock1, "invalid .BTF section size");
- // Cut-off header
+ // Cut-off header.
Mock2.BTFSectionLen = offsetof(BTF::Header, StrOff);
EXPECT_PARSE_ERROR(Mock2, BTFEndOfData);
}
@@ -272,15 +273,15 @@ TEST(BTFParserTest, badBTFExtHdrLen) {
TEST(BTFParserTest, badBTFExtSectionLen) {
MockData1 Mock1, Mock2, Mock3;
- // Cut-off header before HdrLen
+ // Cut-off header before HdrLen.
Mock1.ExtSectionLen = offsetof(BTF::ExtHeader, HdrLen);
EXPECT_PARSE_ERROR(Mock1, BTFExtEndOfData);
- // Cut-off header before LineInfoLen
+ // Cut-off header before LineInfoLen.
Mock2.ExtSectionLen = offsetof(BTF::ExtHeader, LineInfoLen);
EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);
- // Cut-off line-info section somewhere in the middle
+ // Cut-off line-info section somewhere in the middle.
Mock3.ExtSectionLen = offsetof(MockData1::E, Lines) + 4;
EXPECT_PARSE_ERROR(Mock3, BTFExtEndOfData);
}
@@ -355,4 +356,732 @@ TEST(BTFParserTest, btfContext) {
EXPECT_EQ(I2.LineSource, std::nullopt);
}
+static uint32_t mkInfo(uint32_t Kind) { return Kind << 24; }
+
+template <typename T> static void append(std::string &S, const T &What) {
+ S.append((const char *)&What, sizeof(What));
+}
+
+class MockData2 {
+ SmallString<0> ObjStorage;
+ std::unique_ptr<ObjectFile> Obj;
+ std::string Types;
+ std::string Strings;
+ std::string Relocs;
+ std::string Lines;
+ unsigned TotalTypes;
+ int LastRelocSecIdx;
+ unsigned NumRelocs;
+ int LastLineSecIdx;
+ unsigned NumLines;
+
+public:
+ MockData2() { reset(); }
+
+ unsigned totalTypes() const { return TotalTypes; }
+
+ uint32_t addString(StringRef S) {
+ uint32_t Off = Strings.size();
+ Strings.append(S.data(), S.size());
+ Strings.append("\0", 1);
+ return Off;
+ };
+
+ uint32_t addType(const BTF::CommonType &Tp) {
+ append(Types, Tp);
+ return ++TotalTypes;
+ }
+
+ template <typename T> void addTail(const T &Tp) { append(Types, Tp); }
+
+ void resetTypes() {
+ Types.resize(0);
+ TotalTypes = 0;
+ }
+
+ void reset() {
+ ObjStorage.clear();
+ Types.resize(0);
+ Strings.resize(0);
+ Relocs.resize(0);
+ Lines.resize(0);
+ TotalTypes = 0;
+ LastRelocSecIdx = -1;
+ NumRelocs = 0;
+ LastLineSecIdx = -1;
+ NumLines = 0;
+ }
+
+ void finishRelocSec() {
+ if (LastRelocSecIdx == -1)
+ return;
+
+ BTF::SecFieldReloc *SecInfo =
+ (BTF::SecFieldReloc *)&Relocs[LastRelocSecIdx];
+ SecInfo->NumFieldReloc = NumRelocs;
+ LastRelocSecIdx = -1;
+ NumRelocs = 0;
+ }
+
+ void finishLineSec() {
+ if (LastLineSecIdx == -1)
+ return;
+
+ BTF::SecLineInfo *SecInfo = (BTF::SecLineInfo *)&Lines[LastLineSecIdx];
+ SecInfo->NumLineInfo = NumLines;
+ NumLines = 0;
+ LastLineSecIdx = -1;
+ }
+
+ void addRelocSec(const BTF::SecFieldReloc &R) {
+ finishRelocSec();
+ LastRelocSecIdx = Relocs.size();
+ append(Relocs, R);
+ }
+
+ void addReloc(const BTF::BPFFieldReloc &R) {
+ append(Relocs, R);
+ ++NumRelocs;
+ }
+
+ void addLinesSec(const BTF::SecLineInfo &R) {
+ finishLineSec();
+ LastLineSecIdx = Lines.size();
+ append(Lines, R);
+ }
+
+ void addLine(const BTF::BPFLineInfo &R) {
+ append(Lines, R);
+ ++NumLines;
+ }
+
+ ObjectFile &makeObj() {
+ finishRelocSec();
+ finishLineSec();
+
+ BTF::Header BTFHeader = {};
+ BTFHeader.Magic = BTF::MAGIC;
+ BTFHeader.Version = 1;
+ BTFHeader.HdrLen = sizeof(BTFHeader);
+ BTFHeader.StrOff = 0;
+ BTFHeader.StrLen = Strings.size();
+ BTFHeader.TypeOff = Strings.size();
+ BTFHeader.TypeLen = Types.size();
+
+ std::string BTFSec;
+ append(BTFSec, BTFHeader);
+ BTFSec.append(Strings);
+ BTFSec.append(Types);
+
+ BTF::ExtHeader ExtHeader = {};
+ ExtHeader.Magic = BTF::MAGIC;
+ ExtHeader.Version = 1;
+ ExtHeader.HdrLen = sizeof(ExtHeader);
+ ExtHeader.FieldRelocOff = 0;
+ ExtHeader.FieldRelocLen = Relocs.size() + sizeof(uint32_t);
+ ExtHeader.LineInfoOff = ExtHeader.FieldRelocLen;
+ ExtHeader.LineInfoLen = Lines.size() + sizeof(uint32_t);
+
+ std::string ExtSec;
+ append(ExtSec, ExtHeader);
+ append(ExtSec, (uint32_t)sizeof(BTF::BPFFieldReloc));
+ ExtSec.append(Relocs);
+ append(ExtSec, (uint32_t)sizeof(BTF::BPFLineInfo));
+ ExtSec.append(Lines);
+
+ std::string YamlBuffer;
+ raw_string_ostream Yaml(YamlBuffer);
+ Yaml << R"(
+!ELF
+FileHeader:
+ Class: ELFCLASS64)";
+ if (sys::IsBigEndianHost)
+ Yaml << "\n Data: ELFDATA2MSB";
+ else
+ Yaml << "\n Data: ELFDATA2LSB";
+ Yaml << R"(
+ Type: ET_REL
+ Machine: EM_BPF
+Sections:
+ - Name: foo
+ Type: SHT_PROGBITS
+ Size: 0x80
+ - Name: bar
+ Type: SHT_PROGBITS
+ Size: 0x80
+ - Name: .BTF
+ Type: SHT_PROGBITS
+ Content: )"
+ << makeBinRef(BTFSec.data(), BTFSec.size());
+ Yaml << R"(
+ - Name: .BTF.ext
+ Type: SHT_PROGBITS
+ Content: )"
+ << makeBinRef(ExtSec.data(), ExtSec.size());
+
+ Obj = yaml::yaml2ObjectFile(ObjStorage, YamlBuffer,
+ [](const Twine &Err) { errs() << Err; });
+ return *Obj.get();
+ }
+};
+
+TEST(BTFParserTest, allTypeKinds) {
+ MockData2 D;
+ D.addType({D.addString("1"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+ D.addType({D.addString("2"), mkInfo(BTF::BTF_KIND_PTR), {1}});
+ D.addType({D.addString("3"), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
+ D.addTail(BTF::BTFArray({1, 1, 2}));
+ D.addType({D.addString("4"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
+ D.addTail(BTF::BTFMember({D.addString("a"), 1, 0}));
+ D.addTail(BTF::BTFMember({D.addString("b"), 1, 0}));
+ D.addType({D.addString("5"), mkInfo(BTF::BTF_KIND_UNION) | 3, {8}});
+ D.addTail(BTF::BTFMember({D.addString("a"), 1, 0}));
+ D.addTail(BTF::BTFMember({D.addString("b"), 1, 0}));
+ D.addTail(BTF::BTFMember({D.addString("c"), 1, 0}));
+ D.addType({D.addString("6"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
+ D.addTail(BTF::BTFEnum({D.addString("U"), 1}));
+ D.addTail(BTF::BTFEnum({D.addString("V"), 2}));
+ D.addType({D.addString("7"), mkInfo(BTF::BTF_KIND_ENUM64) | 1, {4}});
+ D.addTail(BTF::BTFEnum64({D.addString("W"), 0, 1}));
+ D.addType(
+ {D.addString("8"), BTF::FWD_UNION_FLAG | mkInfo(BTF::BTF_KIND_FWD), {0}});
+ D.addType({D.addString("9"), mkInfo(BTF::BTF_KIND_TYPEDEF), {1}});
+ D.addType({D.addString("10"), mkInfo(BTF::BTF_KIND_VOLATILE), {1}});
+ D.addType({D.addString("11"), mkInfo(BTF::BTF_KIND_CONST), {1}});
+ D.addType({D.addString("12"), mkInfo(BTF::BTF_KIND_RESTRICT), {1}});
+ D.addType({D.addString("13"), mkInfo(BTF::BTF_KIND_FUNC_PROTO) | 1, {1}});
+ D.addTail(BTF::BTFParam({D.addString("P"), 2}));
+ D.addType({D.addString("14"), mkInfo(BTF::BTF_KIND_FUNC), {13}});
+ D.addType({D.addString("15"), mkInfo(BTF::BTF_KIND_VAR), {2}});
+ D.addTail((uint32_t)0);
+ D.addType({D.addString("16"), mkInfo(BTF::BTF_KIND_DATASEC) | 3, {0}});
+ D.addTail(BTF::BTFDataSec({1, 0, 4}));
+ D.addTail(BTF::BTFDataSec({1, 4, 4}));
+ D.addTail(BTF::BTFDataSec({1, 8, 4}));
+ D.addType({D.addString("17"), mkInfo(BTF::BTF_KIND_FLOAT), {4}});
+ D.addType({D.addString("18"), mkInfo(BTF::BTF_KIND_DECL_TAG), {0}});
+ D.addTail((uint32_t)-1);
+ D.addType({D.addString("19"), mkInfo(BTF::BTF_KIND_TYPE_TAG), {0}});
+
+ BTFParser BTF;
+ Error Err = BTF.parse(D.makeObj());
+ EXPECT_FALSE(Err);
+
+ EXPECT_EQ(D.totalTypes() + 1 /* +1 for void */, BTF.typesCount());
+ for (unsigned Id = 1; Id < D.totalTypes() + 1; ++Id) {
+ const BTF::CommonType *Tp = BTF.findType(Id);
+ ASSERT_TRUE(Tp);
+ std::string IdBuf;
+ raw_string_ostream IdBufStream(IdBuf);
+ IdBufStream << Id;
+ EXPECT_EQ(BTF.findString(Tp->NameOff), IdBuf);
+ }
+}
+
+TEST(BTFParserTest, bigStruct) {
+ const uint32_t N = 1000u;
+ MockData2 D;
+ uint32_t FStr = D.addString("f");
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+ D.addType({D.addString("big"), mkInfo(BTF::BTF_KIND_STRUCT) | N, {8}});
+ for (unsigned I = 0; I < N; ++I)
+ D.addTail(BTF::BTFMember({FStr, 1, 0}));
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+
+ BTFParser BTF;
+ ASSERT_SUCCEEDED(BTF.parse(D.makeObj()));
+ ASSERT_EQ(BTF.typesCount(), 4u /* +1 for void */);
+ const BTF::CommonType *Foo = BTF.findType(1);
+ const BTF::CommonType *Big = BTF.findType(2);
+ const BTF::CommonType *Bar = BTF.findType(3);
+ ASSERT_TRUE(Foo);
+ ASSERT_TRUE(Big);
+ ASSERT_TRUE(Bar);
+ EXPECT_EQ(BTF.findString(Foo->NameOff), "foo");
+ EXPECT_EQ(BTF.findString(Big->NameOff), "big");
+ EXPECT_EQ(BTF.findString(Bar->NameOff), "bar");
+ EXPECT_EQ(Big->getVlen(), N);
+}
+
+TEST(BTFParserTest, incompleteTypes) {
+ MockData2 D;
+ auto IncompleteType = [&](const BTF::CommonType &Tp) {
+ D.resetTypes();
+ D.addType(Tp);
+ EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section");
+ };
+
+ // All kinds that need tail.
+ IncompleteType({D.addString("a"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ IncompleteType({D.addString("b"), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
+ IncompleteType({D.addString("c"), mkInfo(BTF::BTF_KIND_VAR), {0}});
+ IncompleteType({D.addString("d"), mkInfo(BTF::BTF_KIND_DECL_TAG), {0}});
+
+ // All kinds with vlen.
+ IncompleteType({D.addString("a"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
+ IncompleteType({D.addString("b"), mkInfo(BTF::BTF_KIND_UNION) | 3, {8}});
+ IncompleteType({D.addString("c"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
+ IncompleteType({D.addString("d"), mkInfo(BTF::BTF_KIND_ENUM64) | 1, {4}});
+ IncompleteType({D.addString("e"), mkInfo(BTF::BTF_KIND_FUNC_PROTO) | 1, {1}});
+ IncompleteType({D.addString("f"), mkInfo(BTF::BTF_KIND_DATASEC) | 3, {0}});
+
+ // An unexpected tail.
+ D.resetTypes();
+ D.addTail((uint32_t)0);
+ EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section");
+}
+
+// Use macro to preserve line number in error message.
+#define SYMBOLIZE(SecAddr, Expected) \
+ do { \
+ const BTF::BPFFieldReloc *R = BTF.findFieldReloc((SecAddr)); \
+ ASSERT_TRUE(R); \
+ SmallString<64> Symbolized; \
+ BTF.symbolize(R, Symbolized); \
+ EXPECT_EQ(Symbolized, (Expected)); \
+ } while (false)
+
+// Shorter name for initializers below.
+using SA = SectionedAddress;
+
+TEST(BTFParserTest, typeRelocs) {
+ MockData2 D;
+ uint32_t Zero = D.addString("0");
+ // id 1: struct foo {}
+ // id 2: union bar;
+ // id 3: struct buz;
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+ D.addType({D.addString("bar"),
+ mkInfo(BTF::BTF_KIND_FWD) | BTF::FWD_UNION_FLAG,
+ {0}});
+ D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_FWD), {0}});
+ D.addRelocSec({D.addString("foo"), 7});
+ // List of all possible correct type relocations for type id #1.
+ D.addReloc({0, 1, Zero, BTF::BTF_TYPE_ID_LOCAL});
+ D.addReloc({8, 1, Zero, BTF::BTF_TYPE_ID_REMOTE});
+ D.addReloc({16, 1, Zero, BTF::TYPE_EXISTENCE});
+ D.addReloc({24, 1, Zero, BTF::TYPE_MATCH});
+ D.addReloc({32, 1, Zero, BTF::TYPE_SIZE});
+ // Forward declarations.
+ D.addReloc({40, 2, Zero, BTF::TYPE_SIZE});
+ D.addReloc({48, 3, Zero, BTF::TYPE_SIZE});
+ // Incorrect type relocation: bad type id.
+ D.addReloc({56, 42, Zero, BTF::TYPE_SIZE});
+ // Incorrect type relocation: spec should be '0'.
+ D.addReloc({64, 1, D.addString("10"), BTF::TYPE_SIZE});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<local_type_id> [1] struct foo");
+ SYMBOLIZE(SA({8, 1}), "<target_type_id> [1] struct foo");
+ SYMBOLIZE(SA({16, 1}), "<type_exists> [1] struct foo");
+ SYMBOLIZE(SA({24, 1}), "<type_matches> [1] struct foo");
+ SYMBOLIZE(SA({32, 1}), "<type_size> [1] struct foo");
+ SYMBOLIZE(SA({40, 1}), "<type_size> [2] fwd union bar");
+ SYMBOLIZE(SA({48, 1}), "<type_size> [3] fwd struct buz");
+ SYMBOLIZE(SA({56, 1}), "<type_size> [42] '0' <unknown type id: 42>");
+ SYMBOLIZE(SA({64, 1}),
+ "<type_size> [1] '10' "
+ "<unexpected type-based relocation spec: should be '0'>");
+}
+
+TEST(BTFParserTest, enumRelocs) {
+ MockData2 D;
+ // id 1: enum { U, V }
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
+ D.addTail(BTF::BTFEnum({D.addString("U"), 1}));
+ D.addTail(BTF::BTFEnum({D.addString("V"), 2}));
+ // id 2: int
+ D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+ // id 3: enum: uint64_t { A, B }
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_ENUM64) | 2, {8}});
+ D.addTail(BTF::BTFEnum64({D.addString("A"), 1, 0}));
+ D.addTail(BTF::BTFEnum64({D.addString("B"), 2, 0}));
+
+ D.addRelocSec({D.addString("foo"), 5});
+ // An ok relocation accessing value #1: U.
+ D.addReloc({0, 1, D.addString("0"), BTF::ENUM_VALUE_EXISTENCE});
+ // An ok relocation accessing value #2: V.
+ D.addReloc({8, 1, D.addString("1"), BTF::ENUM_VALUE});
+ // Incorrect relocation: too many elements in string "1:0".
+ D.addReloc({16, 1, D.addString("1:0"), BTF::ENUM_VALUE});
+ // Incorrect relocation: type id "2" is not an enum.
+ D.addReloc({24, 2, D.addString("1"), BTF::ENUM_VALUE});
+ // Incorrect relocation: value #42 does not exist for enum "foo".
+ D.addReloc({32, 1, D.addString("42"), BTF::ENUM_VALUE});
+ // An ok relocation accessing value #1: A.
+ D.addReloc({40, 3, D.addString("0"), BTF::ENUM_VALUE_EXISTENCE});
+ // An ok relocation accessing value #2: B.
+ D.addReloc({48, 3, D.addString("1"), BTF::ENUM_VALUE});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<enumval_exists> [1] enum foo::U = 1");
+ SYMBOLIZE(SA({8, 1}), "<enumval_value> [1] enum foo::V = 2");
+ SYMBOLIZE(
+ SA({16, 1}),
+ "<enumval_value> [1] '1:0' <unexpected enumval relocation spec size>");
+ SYMBOLIZE(
+ SA({24, 1}),
+ "<enumval_value> [2] '1' <unexpected type kind for enum relocation: 1>");
+ SYMBOLIZE(SA({32, 1}), "<enumval_value> [1] '42' <bad value index: 42>");
+ SYMBOLIZE(SA({40, 1}), "<enumval_exists> [3] enum bar::A = 1");
+ SYMBOLIZE(SA({48, 1}), "<enumval_value> [3] enum bar::B = 2");
+}
+
+TEST(BTFParserTest, enumRelocsMods) {
+ MockData2 D;
+ // id 1: enum { U, V }
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
+ D.addTail(BTF::BTFEnum({D.addString("U"), 1}));
+ D.addTail(BTF::BTFEnum({D.addString("V"), 2}));
+ // id 2: typedef enum foo a;
+ D.addType({D.addString("a"), mkInfo(BTF::BTF_KIND_TYPEDEF), {1}});
+ // id 3: const enum foo;
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {1}});
+
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, 2, D.addString("0"), BTF::ENUM_VALUE});
+ D.addReloc({8, 3, D.addString("1"), BTF::ENUM_VALUE});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<enumval_value> [2] typedef a::U = 1");
+ SYMBOLIZE(SA({8, 1}), "<enumval_value> [3] const enum foo::V = 2");
+}
+
+TEST(BTFParserTest, fieldRelocs) {
+ MockData2 D;
+ // id 1: int
+ D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+ // id 2: struct foo { int a; int b; }
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
+ D.addTail(BTF::BTFMember({D.addString("a"), 1, 0}));
+ D.addTail(BTF::BTFMember({D.addString("b"), 1, 0}));
+ // id 3: array of struct foo.
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
+ D.addTail(BTF::BTFArray({2, 1, 2}));
+ // id 4: struct bar { struct foo u[2]; int v; }
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
+ D.addTail(BTF::BTFMember({D.addString("u"), 3, 0}));
+ D.addTail(BTF::BTFMember({D.addString("v"), 1, 0}));
+ // id 5: array with bad element type id.
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
+ D.addTail(BTF::BTFArray({42, 1, 2}));
+ // id 6: struct buz { <bad type> u[2]; <bad type> v; }
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
+ D.addTail(BTF::BTFMember({D.addString("u"), 5, 0}));
+ D.addTail(BTF::BTFMember({D.addString("v"), 42, 0}));
+
+ D.addRelocSec({D.addString("foo"), 0 /* patched automatically */});
+ // All field relocations kinds for struct bar::v.
+ D.addReloc({0, 4, D.addString("0:1"), BTF::FIELD_BYTE_OFFSET});
+ D.addReloc({8, 4, D.addString("0:1"), BTF::FIELD_BYTE_SIZE});
+ D.addReloc({16, 4, D.addString("0:1"), BTF::FIELD_EXISTENCE});
+ D.addReloc({24, 4, D.addString("0:1"), BTF::FIELD_SIGNEDNESS});
+ D.addReloc({32, 4, D.addString("0:1"), BTF::FIELD_LSHIFT_U64});
+ D.addReloc({40, 4, D.addString("0:1"), BTF::FIELD_RSHIFT_U64});
+ // Non-zero first idx.
+ D.addReloc({48, 4, D.addString("7:1"), BTF::FIELD_BYTE_OFFSET});
+ // Access through array and struct: struct bar::u[1].a.
+ D.addReloc({56, 4, D.addString("0:0:1:0"), BTF::FIELD_BYTE_OFFSET});
+ // Access through array and struct: struct bar::u[1].b.
+ D.addReloc({64, 4, D.addString("0:0:1:1"), BTF::FIELD_BYTE_OFFSET});
+ // Incorrect relocation: empty access string.
+ D.addReloc({72, 4, D.addString(""), BTF::FIELD_BYTE_OFFSET});
+ // Incorrect relocation: member index out of range (only two members in bar).
+ D.addReloc({80, 4, D.addString("0:2"), BTF::FIELD_BYTE_OFFSET});
+ // Incorrect relocation: unknown element type id (buz::u[0] access).
+ D.addReloc({88, 6, D.addString("0:0:0"), BTF::FIELD_BYTE_OFFSET});
+ // Incorrect relocation: unknown member type id (buz::v access).
+ D.addReloc({96, 6, D.addString("0:1:0"), BTF::FIELD_BYTE_OFFSET});
+ // Incorrect relocation: non structural type in the middle of access string
+ // struct bar::v.<something>.
+ D.addReloc({104, 4, D.addString("0:1:0"), BTF::FIELD_BYTE_OFFSET});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<byte_off> [4] struct bar::v (0:1)");
+ SYMBOLIZE(SA({8, 1}), "<byte_sz> [4] struct bar::v (0:1)");
+ SYMBOLIZE(SA({16, 1}), "<field_exists> [4] struct bar::v (0:1)");
+ SYMBOLIZE(SA({24, 1}), "<signed> [4] struct bar::v (0:1)");
+ SYMBOLIZE(SA({32, 1}), "<lshift_u64> [4] struct bar::v (0:1)");
+ SYMBOLIZE(SA({40, 1}), "<rshift_u64> [4] struct bar::v (0:1)");
+ SYMBOLIZE(SA({48, 1}), "<byte_off> [4] struct bar::[7].v (7:1)");
+ SYMBOLIZE(SA({56, 1}), "<byte_off> [4] struct bar::u[1].a (0:0:1:0)");
+ SYMBOLIZE(SA({64, 1}), "<byte_off> [4] struct bar::u[1].b (0:0:1:1)");
+ SYMBOLIZE(SA({72, 1}), "<byte_off> [4] '' <field spec too short>");
+ SYMBOLIZE(SA({80, 1}),
+ "<byte_off> [4] '0:2' "
+ "<member index 2 for spec sub-string 1 is out of range>");
+ SYMBOLIZE(SA({88, 1}), "<byte_off> [6] '0:0:0' "
+ "<unknown element type id 42 for spec sub-string 2>");
+ SYMBOLIZE(SA({96, 1}), "<byte_off> [6] '0:1:0' "
+ "<unknown member type id 42 for spec sub-string 1>");
+ SYMBOLIZE(SA({104, 1}), "<byte_off> [4] '0:1:0' "
+ "<unexpected type kind 1 for spec sub-string 2>");
+}
+
+TEST(BTFParserTest, fieldRelocsMods) {
+ MockData2 D;
+ // struct foo {
+ // int u;
+ // }
+ // typedef struct foo bar;
+ // struct buz {
+ // const bar v;
+ // }
+ // typedef buz quux;
+ // const volatile restrict quux <some-var>;
+ uint32_t Int =
+ D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+ uint32_t Foo =
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}});
+ D.addTail(BTF::BTFMember({D.addString("u"), Int, 0}));
+ uint32_t Bar =
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_TYPEDEF), {Foo}});
+ uint32_t CBar =
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_CONST), {Bar}});
+ uint32_t Buz =
+ D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}});
+ D.addTail(BTF::BTFMember({D.addString("v"), CBar, 0}));
+ uint32_t Quux =
+ D.addType({D.addString("quux"), mkInfo(BTF::BTF_KIND_TYPEDEF), {Buz}});
+ uint32_t RQuux =
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_RESTRICT), {Quux}});
+ uint32_t VRQuux =
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_VOLATILE), {RQuux}});
+ uint32_t CVRQuux =
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {VRQuux}});
+ uint32_t CUnknown =
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {77}});
+ uint32_t CVUnknown =
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_VOLATILE), {CUnknown}});
+
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, Bar, D.addString("0:0"), BTF::FIELD_BYTE_OFFSET});
+ D.addReloc({8, CVRQuux, D.addString("0:0:0"), BTF::FIELD_BYTE_OFFSET});
+ D.addReloc({16, CVUnknown, D.addString("0:1:2"), BTF::FIELD_BYTE_OFFSET});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ // Should show modifiers / name of typedef.
+ SYMBOLIZE(SA({0, 1}), "<byte_off> [3] typedef bar::u (0:0)");
+ SYMBOLIZE(SA({8, 1}),
+ "<byte_off> [9] const volatile restrict typedef quux::v.u (0:0:0)");
+ SYMBOLIZE(SA({16, 1}),
+ "<byte_off> [11] '0:1:2' <unknown type id: 77 in modifiers chain>");
+}
+
+TEST(BTFParserTest, relocTypeTagAndVoid) {
+ MockData2 D;
+ // __attribute__((type_tag("tag"))) void
+ uint32_t Tag =
+ D.addType({D.addString("tag"), mkInfo(BTF::BTF_KIND_TYPE_TAG), {0}});
+
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, Tag, D.addString("0"), BTF::TYPE_EXISTENCE});
+ D.addReloc({8, 0 /* void */, D.addString("0"), BTF::TYPE_EXISTENCE});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<type_exists> [1] type_tag(\"tag\") void");
+ SYMBOLIZE(SA({8, 1}), "<type_exists> [0] void");
+}
+
+TEST(BTFParserTest, longRelocModifiersCycle) {
+ MockData2 D;
+
+ D.addType(
+ {D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {1 /* ourselves */}});
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<type_exists> [1] '' <modifiers chain is too long>");
+}
+
+TEST(BTFParserTest, relocAnonFieldsAndTypes) {
+ MockData2 D;
+
+ // struct {
+ // int :32;
+ // } v;
+ uint32_t Int =
+ D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
+ D.addTail((uint32_t)0);
+ uint32_t Anon =
+ D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}});
+ D.addTail(BTF::BTFMember({D.addString(""), Int, 0}));
+
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, Anon, D.addString("0"), BTF::TYPE_EXISTENCE});
+ D.addReloc({8, Anon, D.addString("0:0"), BTF::FIELD_BYTE_OFFSET});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}), "<type_exists> [2] struct <anon 2>");
+ SYMBOLIZE(SA({8, 1}), "<byte_off> [2] struct <anon 2>::<anon 0> (0:0)");
+}
+
+TEST(BTFParserTest, miscBadRelos) {
+ MockData2 D;
+
+ uint32_t S = D.addType({D.addString("S"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, 0, D.addString(""), 777});
+ D.addReloc({8, S, D.addString("abc"), BTF::FIELD_BYTE_OFFSET});
+ D.addReloc({16, S, D.addString("0#"), BTF::FIELD_BYTE_OFFSET});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ SYMBOLIZE(SA({0, 1}),
+ "<reloc kind #777> [0] '' <unknown relocation kind: 777>");
+ SYMBOLIZE(SA({8, 1}), "<byte_off> [1] 'abc' <spec string is not a number>");
+ SYMBOLIZE(SA({16, 1}),
+ "<byte_off> [1] '0#' <unexpected spec string delimiter: '#'>");
+}
+
+TEST(BTFParserTest, relocsMultipleSections) {
+ MockData2 D;
+
+ uint32_t S = D.addType({D.addString("S"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+ uint32_t T = D.addType({D.addString("T"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, S, D.addString(""), BTF::TYPE_EXISTENCE});
+ D.addReloc({8, S, D.addString(""), BTF::TYPE_EXISTENCE});
+
+ D.addRelocSec({D.addString("bar"), 0});
+ D.addReloc({8, T, D.addString(""), BTF::TYPE_EXISTENCE});
+ D.addReloc({16, T, D.addString(""), BTF::TYPE_EXISTENCE});
+
+ BTFParser BTF;
+ Error E = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E);
+
+ EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
+ EXPECT_TRUE(BTF.findFieldReloc({8, 1}));
+ EXPECT_FALSE(BTF.findFieldReloc({16, 1}));
+
+ EXPECT_FALSE(BTF.findFieldReloc({0, 2}));
+ EXPECT_TRUE(BTF.findFieldReloc({8, 2}));
+ EXPECT_TRUE(BTF.findFieldReloc({16, 2}));
+
+ EXPECT_FALSE(BTF.findFieldReloc({0, 3}));
+ EXPECT_FALSE(BTF.findFieldReloc({8, 3}));
+ EXPECT_FALSE(BTF.findFieldReloc({16, 3}));
+
+ auto AssertReloType = [&](const SectionedAddress &A, const char *Name) {
+ const BTF::BPFFieldReloc *Relo = BTF.findFieldReloc(A);
+ ASSERT_TRUE(Relo);
+ const BTF::CommonType *Type = BTF.findType(Relo->TypeID);
+ ASSERT_TRUE(Type);
+ EXPECT_EQ(BTF.findString(Type->NameOff), Name);
+ };
+
+ AssertReloType({8, 1}, "S");
+ AssertReloType({8, 2}, "T");
+}
+
+TEST(BTFParserTest, parserResetReloAndTypes) {
+ BTFParser BTF;
+ MockData2 D;
+
+ // First time: two types, two relocations.
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+ D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});
+ D.addReloc({8, 2, D.addString(""), BTF::TYPE_EXISTENCE});
+
+ Error E1 = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E1);
+
+ ASSERT_TRUE(BTF.findType(1));
+ EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "foo");
+ EXPECT_TRUE(BTF.findType(2));
+ EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
+ EXPECT_TRUE(BTF.findFieldReloc({8, 1}));
+
+ // Second time: one type, one relocation.
+ D.reset();
+ D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});
+
+ Error E2 = BTF.parse(D.makeObj());
+ EXPECT_FALSE(E2);
+
+ ASSERT_TRUE(BTF.findType(1));
+ EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "buz");
+ EXPECT_FALSE(BTF.findType(2));
+ EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
+ EXPECT_FALSE(BTF.findFieldReloc({8, 1}));
+}
+
+TEST(BTFParserTest, selectiveLoad) {
+ BTFParser BTF1, BTF2, BTF3;
+ MockData2 D;
+
+ D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
+ D.addRelocSec({D.addString("foo"), 0});
+ D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});
+ D.addLinesSec({D.addString("foo"), 0});
+ D.addLine({0, D.addString("file.c"), D.addString("some line"), LC(2, 3)});
+
+ BTFParser::ParseOptions Opts;
+
+ ObjectFile &Obj1 = D.makeObj();
+ Opts = {};
+ Opts.LoadLines = true;
+ ASSERT_SUCCEEDED(BTF1.parse(Obj1, Opts));
+
+ Opts = {};
+ Opts.LoadTypes = true;
+ ASSERT_SUCCEEDED(BTF2.parse(Obj1, Opts));
+
+ Opts = {};
+ Opts.LoadRelocs = true;
+ ASSERT_SUCCEEDED(BTF3.parse(Obj1, Opts));
+
+ EXPECT_TRUE(BTF1.findLineInfo({0, 1}));
+ EXPECT_FALSE(BTF2.findLineInfo({0, 1}));
+ EXPECT_FALSE(BTF3.findLineInfo({0, 1}));
+
+ EXPECT_FALSE(BTF1.findType(1));
+ EXPECT_TRUE(BTF2.findType(1));
+ EXPECT_FALSE(BTF3.findType(1));
+
+ EXPECT_FALSE(BTF1.findFieldReloc({0, 1}));
+ EXPECT_FALSE(BTF2.findFieldReloc({0, 1}));
+ EXPECT_TRUE(BTF3.findFieldReloc({0, 1}));
+}
+
} // namespace
More information about the llvm-commits
mailing list