Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 111 additions & 20 deletions llvm/lib/Target/BPF/BTFDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "BPF.h"
#include "BPFCORE.h"
#include "MCTargetDesc/BPFMCTargetDesc.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
Expand All @@ -23,6 +24,7 @@
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
Expand Down Expand Up @@ -93,7 +95,24 @@ void BTFTypeDerived::completeType(BTFDebug &BDebug) {
return;
IsCompleted = true;

BTFType.NameOff = BDebug.addString(Name);
switch (Kind) {
case BTF::BTF_KIND_PTR:
case BTF::BTF_KIND_CONST:
case BTF::BTF_KIND_VOLATILE:
case BTF::BTF_KIND_RESTRICT:
// Debug info might contain names for these types, but given that we want
// to keep BTF minimal and naming reference types doesn't bring any value
// (what matters is the completeness of the base type), we don't emit them.
//
// Furthermore, the Linux kernel refuses to load BPF programs that contain
// BTF with these types named:
// https://elixir.bootlin.com/linux/v6.17.1/source/kernel/bpf/btf.c#L2586
BTFType.NameOff = 0;
break;
default:
BTFType.NameOff = BDebug.addString(Name);
break;
}

if (NeedsFixup || !DTy)
return;
Expand Down Expand Up @@ -301,21 +320,59 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) {

BTFType.NameOff = BDebug.addString(STy->getName());

if (STy->getTag() == dwarf::DW_TAG_variant_part) {
// Variant parts might have a discriminator, which has its own memory
// location, and variants, which share the memory location afterwards. LLVM
// DI doesn't consider discriminator as an element and instead keeps
// it as a separate reference.
// To keep BTF simple, let's represent the structure as an union with
// discriminator as the first element.
// The offsets inside variant types are already handled correctly in the
// DI.
const auto *DTy = STy->getDiscriminator();
if (DTy) {
struct BTF::BTFMember Discriminator;

Discriminator.NameOff = BDebug.addString(DTy->getName());
Discriminator.Offset = DTy->getOffsetInBits();
const auto *BaseTy = DTy->getBaseType();
Discriminator.Type = BDebug.getTypeId(BaseTy);

Members.push_back(Discriminator);
}
}

// Add struct/union members.
const DINodeArray Elements = STy->getElements();
for (const auto *Element : Elements) {
struct BTF::BTFMember BTFMember;
const auto *DDTy = cast<DIDerivedType>(Element);

BTFMember.NameOff = BDebug.addString(DDTy->getName());
if (HasBitField) {
uint8_t BitFieldSize = DDTy->isBitField() ? DDTy->getSizeInBits() : 0;
BTFMember.Offset = BitFieldSize << 24 | DDTy->getOffsetInBits();
} else {
BTFMember.Offset = DDTy->getOffsetInBits();
switch (Element->getTag()) {
case dwarf::DW_TAG_member: {
const auto *DDTy = cast<DIDerivedType>(Element);

BTFMember.NameOff = BDebug.addString(DDTy->getName());
if (HasBitField) {
uint8_t BitFieldSize = DDTy->isBitField() ? DDTy->getSizeInBits() : 0;
BTFMember.Offset = BitFieldSize << 24 | DDTy->getOffsetInBits();
} else {
BTFMember.Offset = DDTy->getOffsetInBits();
}
const auto *BaseTy = tryRemoveAtomicType(DDTy->getBaseType());
BTFMember.Type = BDebug.getTypeId(BaseTy);
break;
}
case dwarf::DW_TAG_variant_part: {
const auto *DCTy = dyn_cast<DICompositeType>(Element);

BTFMember.NameOff = BDebug.addString(DCTy->getName());
BTFMember.Offset = DCTy->getOffsetInBits();
BTFMember.Type = BDebug.getTypeId(DCTy);
break;
}
default:
llvm_unreachable("Unexpected DI tag of a struct/union element");
}
const auto *BaseTy = tryRemoveAtomicType(DDTy->getBaseType());
BTFMember.Type = BDebug.getTypeId(BaseTy);
Members.push_back(BTFMember);
}
}
Expand Down Expand Up @@ -672,16 +729,28 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct,
uint32_t &TypeId) {
const DINodeArray Elements = CTy->getElements();
uint32_t VLen = Elements.size();
// Variant parts might have a discriminator. LLVM DI doesn't consider it as
// an element and instead keeps it as a separate reference. But we represent
// it as an element in BTF.
if (CTy->getTag() == dwarf::DW_TAG_variant_part) {
const auto *DTy = CTy->getDiscriminator();
if (DTy) {
visitTypeEntry(DTy);
VLen++;
}
}
if (VLen > BTF::MAX_VLEN)
return;

// Check whether we have any bitfield members or not
bool HasBitField = false;
for (const auto *Element : Elements) {
auto E = cast<DIDerivedType>(Element);
if (E->isBitField()) {
HasBitField = true;
break;
if (Element->getTag() == dwarf::DW_TAG_member) {
auto E = cast<DIDerivedType>(Element);
if (E->isBitField()) {
HasBitField = true;
break;
}
}
}

Expand All @@ -696,9 +765,22 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct,
// Visit all struct members.
int FieldNo = 0;
for (const auto *Element : Elements) {
const auto Elem = cast<DIDerivedType>(Element);
visitTypeEntry(Elem);
processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo);
switch (Element->getTag()) {
case dwarf::DW_TAG_member: {
const auto Elem = cast<DIDerivedType>(Element);
visitTypeEntry(Elem);
processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo);
break;
}
case dwarf::DW_TAG_variant_part: {
const auto Elem = cast<DICompositeType>(Element);
visitTypeEntry(Elem);
processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo);
break;
}
default:
llvm_unreachable("Unexpected DI tag of a struct/union element");
}
FieldNo++;
}
}
Expand Down Expand Up @@ -781,16 +863,25 @@ void BTFDebug::visitFwdDeclType(const DICompositeType *CTy, bool IsUnion,
void BTFDebug::visitCompositeType(const DICompositeType *CTy,
uint32_t &TypeId) {
auto Tag = CTy->getTag();
if (Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type) {
switch (Tag) {
case dwarf::DW_TAG_structure_type:
case dwarf::DW_TAG_union_type:
case dwarf::DW_TAG_variant_part:
// Handle forward declaration differently as it does not have members.
if (CTy->isForwardDecl())
visitFwdDeclType(CTy, Tag == dwarf::DW_TAG_union_type, TypeId);
else
visitStructType(CTy, Tag == dwarf::DW_TAG_structure_type, TypeId);
} else if (Tag == dwarf::DW_TAG_array_type)
break;
case dwarf::DW_TAG_array_type:
visitArrayType(CTy, TypeId);
else if (Tag == dwarf::DW_TAG_enumeration_type)
break;
case dwarf::DW_TAG_enumeration_type:
visitEnumType(CTy, TypeId);
break;
default:
llvm_unreachable("Unexpected DI tag of a composite type");
}
}

bool BTFDebug::IsForwardDeclCandidate(const DIType *Base) {
Expand Down
59 changes: 59 additions & 0 deletions llvm/test/CodeGen/BPF/BTF/ptr-named-2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
; RUN: llc -mtriple=bpfel -filetype=obj -o %t1 %s
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
; RUN: llc -mtriple=bpfeb -filetype=obj -o %t1 %s
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
;
; This IR is hand-written.

; ModuleID = 'ptr-named-2.ll'
source_filename = "ptr-named-2.ll"
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "bpfel-unknown-none"

%struct.TypeExamples = type { i32*, i32, i32, i32* }

@type_examples = internal global %struct.TypeExamples zeroinitializer, align 8, !dbg !0

!llvm.dbg.cu = !{!1}
!llvm.module.flags = !{!2, !3, !4}
!llvm.ident = !{!21}

; CHECK-BTF: [1] STRUCT 'TypeExamples' size=32 vlen=4
; CHECK-BTF-NEXT: 'ptr' type_id=2 bits_offset=0
; CHECK-BTF-NEXT: 'volatile' type_id=4 bits_offset=64
; CHECK-BTF-NEXT: 'const' type_id=5 bits_offset=128
; CHECK-BTF-NEXT: 'restrict_ptr' type_id=6 bits_offset=192
; CHECK-BTF-NEXT: [2] PTR '(anon)' type_id=3
; CHECK-BTF-NEXT: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
; CHECK-BTF-NEXT: [4] VOLATILE '(anon)' type_id=3
; CHECK-BTF-NEXT: [5] CONST '(anon)' type_id=3
; CHECK-BTF-NEXT: [6] RESTRICT '(anon)' type_id=7
; CHECK-BTF-NEXT: [7] PTR '(anon)' type_id=3
; CHECK-BTF-NEXT: [8] VAR 'type_examples' type_id=1, linkage=static
; CHECK-BTF-NEXT: [9] DATASEC '.bss' size=0 vlen=1
; CHECK-BTF-NEXT: type_id=8 offset=0 size=24

!0 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !7, globals: !8, splitDebugInlining: false, nameTableKind: None)
!2 = !{i32 2, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = distinct !DIGlobalVariable(name: "type_examples", scope: !1, file: !6, line: 12, type: !9, isLocal: true, isDefinition: true)
!6 = !DIFile(filename: "ptr-named-2.ll", directory: "/tmp")
!7 = !{}
!8 = !{!0}
!9 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TypeExamples", file: !6, line: 5, size: 256, elements: !10)
!10 = !{!11, !12, !13, !14}
!11 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !9, file: !6, line: 6, baseType: !15, size: 64)
!12 = !DIDerivedType(tag: DW_TAG_member, name: "volatile", scope: !9, file: !6, line: 7, baseType: !17, size: 64, offset: 64)
!13 = !DIDerivedType(tag: DW_TAG_member, name: "const", scope: !9, file: !6, line: 8, baseType: !18, size: 64, offset: 128)
!14 = !DIDerivedType(tag: DW_TAG_member, name: "restrict_ptr", scope: !9, file: !6, line: 9, baseType: !19, size: 64, offset: 192)
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*int", baseType: !16, size: 64)
!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!17 = !DIDerivedType(tag: DW_TAG_volatile_type, name: "volatile int", baseType: !16)
!18 = !DIDerivedType(tag: DW_TAG_const_type, name: "const int", baseType: !16)
!19 = !DIDerivedType(tag: DW_TAG_restrict_type, name: "*int restrict", baseType: !20)
!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
!21 = !{!"my hand-written IR"}
75 changes: 75 additions & 0 deletions llvm/test/CodeGen/BPF/BTF/ptr-named.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
; RUN: llc -mtriple=bpfel -filetype=obj -o %t1 %s
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
; RUN: llc -mtriple=bpfeb -filetype=obj -o %t1 %s
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
;
; Source:
; #![no_std]
; #![no_main]
;
; pub struct MyType {
; ptr: *const u32,
; }
;
; impl MyType {
; pub const fn new() -> Self {
; let ptr = core::ptr::null();
; Self { ptr }
; }
; }
;
; unsafe impl Sync for MyType {}
;
; #[unsafe(no_mangle)]
; pub static X: MyType = MyType::new();
;
; #[cfg(not(test))]
; #[panic_handler]
; fn panic(_info: &core::panic::PanicInfo) -> ! {
; loop {}
; }
; Compilation flag:
; cargo +nightly rustc -Zbuild-std=core --target=bpfel-unknown-none -- --emit=llvm-bc
; llvm-extract --glob=X $(find target/ -name "*.bc" | head -n 1) -o ptr-named.bc
; llvm-dis ptr-named.bc -o ptr-named.ll

; ModuleID = 'ptr-named.bc'
source_filename = "1m2uqe50qkwxmo53ydydvou91"
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "bpfel"

@X = constant [8 x i8] zeroinitializer, align 8, !dbg !0

!llvm.module.flags = !{!11, !12, !13, !14}
!llvm.ident = !{!15}
!llvm.dbg.cu = !{!16}

; CHECK-BTF: [1] STRUCT 'MyType' size=8 vlen=1
; CHECK-BTF-NEXT: 'ptr' type_id=2 bits_offset=0
; CHECK-BTF-NEXT: [2] PTR '(anon)' type_id=3
; CHECK-BTF-NEXT: [3] INT 'u32' size=4 bits_offset=0 nr_bits=32 encoding=(none)
; CHECK-BTF-NEXT: [4] VAR 'X' type_id=1, linkage=global
; CHECK-BTF-NEXT: [5] DATASEC '.rodata' size=0 vlen=1
; CHECK-BTF-NEXT: type_id=4 offset=0 size=8

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "X", scope: !2, file: !3, line: 19, type: !4, isLocal: false, isDefinition: true, align: 64)
!2 = !DINamespace(name: "ptr_named", scope: null)
!3 = !DIFile(filename: "ptr-named/src/main.rs", directory: "/tmp/ptr-named", checksumkind: CSK_MD5, checksum: "e37168304600b30cbb5ba168f0384932")
!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "MyType", scope: !2, file: !5, size: 64, align: 64, flags: DIFlagPublic, elements: !6, templateParams: !10, identifier: "7609fa40332dd486922f074276a171c3")
!5 = !DIFile(filename: "<unknown>", directory: "")
!6 = !{!7}
!7 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !4, file: !5, baseType: !8, size: 64, align: 64, flags: DIFlagPrivate)
!8 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const u32", baseType: !9, size: 64, align: 64, dwarfAddressSpace: 0)
!9 = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned)
!10 = !{}
!11 = !{i32 8, !"PIC Level", i32 2}
!12 = !{i32 7, !"PIE Level", i32 2}
!13 = !{i32 7, !"Dwarf Version", i32 4}
!14 = !{i32 2, !"Debug Info Version", i32 3}
!15 = !{!"rustc version 1.92.0-nightly (c8905eaa6 2025-09-28)"}
!16 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !17, producer: "clang LLVM (rustc version 1.92.0-nightly (c8905eaa6 2025-09-28))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !18, splitDebugInlining: false, nameTableKind: None)
!17 = !DIFile(filename: "ptr-named/src/main.rs/@/1m2uqe50qkwxmo53ydydvou91", directory: "/tmp/ptr-named")
!18 = !{!0}
Loading
Loading