[llvm] [BPF] Strip map struct names (PR #164851)
Michal R via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 23 10:07:25 PDT 2025
https://github.com/vadorovsky created https://github.com/llvm/llvm-project/pull/164851
Linux kernel rejects programs which use named structs as map definitions. BPF programs written in C usually follow that restriction by using anonymous structs. But some LLVM frontend (e.g. Rust) don't support defining anonymous structs. To make sure they work, strip the map struct name unconditionally.
>From 44d9e0411038c11a8c9e6c7d0781d0ca88f1f4d1 Mon Sep 17 00:00:00 2001
From: Michal R <vad.sol at proton.me>
Date: Mon, 13 Oct 2025 13:49:33 +0200
Subject: [PATCH] [BPF] Strip map struct names
Linux kernel rejects programs which use named structs as map
definitions. BPF programs written in C usually follow that restriction
by using anonymous structs. But some LLVM frontend (e.g. Rust) don't
support defining anonymous structs. To make sure they work, strip the
map struct name unconditionally.
---
llvm/lib/Target/BPF/BTFDebug.cpp | 46 ++++++++------
llvm/lib/Target/BPF/BTFDebug.h | 10 +--
llvm/test/CodeGen/BPF/BTF/map-def-2.ll | 2 +-
llvm/test/CodeGen/BPF/BTF/map-def-3.ll | 2 +-
llvm/test/CodeGen/BPF/BTF/map-def-4.ll | 84 ++++++++++++++++++++++++++
llvm/test/CodeGen/BPF/BTF/map-def.ll | 22 +++----
6 files changed, 130 insertions(+), 36 deletions(-)
create mode 100644 llvm/test/CodeGen/BPF/BTF/map-def-4.ll
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index a652b7e9c537f..7718e36f1dc18 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -306,8 +306,8 @@ void BTFTypeArray::emitType(MCStreamer &OS) {
/// Represent either a struct or a union.
BTFTypeStruct::BTFTypeStruct(const DICompositeType *STy, bool IsStruct,
- bool HasBitField, uint32_t Vlen)
- : STy(STy), HasBitField(HasBitField) {
+ bool HasBitField, uint32_t Vlen, bool StripName)
+ : STy(STy), HasBitField(HasBitField), StripName(StripName) {
Kind = IsStruct ? BTF::BTF_KIND_STRUCT : BTF::BTF_KIND_UNION;
BTFType.Size = roundupToBytes(STy->getSizeInBits());
BTFType.Info = (HasBitField << 31) | (Kind << 24) | Vlen;
@@ -318,7 +318,10 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) {
return;
IsCompleted = true;
- BTFType.NameOff = BDebug.addString(STy->getName());
+ if (StripName)
+ BTFType.NameOff = 0;
+ else
+ 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
@@ -726,7 +729,7 @@ int BTFDebug::genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId) {
/// Handle structure/union types.
void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct,
- uint32_t &TypeId) {
+ uint32_t &TypeId, bool StripName) {
const DINodeArray Elements = CTy->getElements();
uint32_t VLen = Elements.size();
// Variant parts might have a discriminator. LLVM DI doesn't consider it as
@@ -755,7 +758,8 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct,
}
auto TypeEntry =
- std::make_unique<BTFTypeStruct>(CTy, IsStruct, HasBitField, VLen);
+ std::make_unique<BTFTypeStruct>(CTy, IsStruct, HasBitField, VLen,
+ StripName);
StructTypes.push_back(TypeEntry.get());
TypeId = addType(std::move(TypeEntry), CTy);
@@ -789,7 +793,7 @@ void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) {
// Visit array element type.
uint32_t ElemTypeId;
const DIType *ElemType = CTy->getBaseType();
- visitTypeEntry(ElemType, ElemTypeId, false, false);
+ visitTypeEntry(ElemType, ElemTypeId, false, false, false);
// Visit array dimensions.
DINodeArray Elements = CTy->getElements();
@@ -861,7 +865,7 @@ void BTFDebug::visitFwdDeclType(const DICompositeType *CTy, bool IsUnion,
/// Handle structure, union, array and enumeration types.
void BTFDebug::visitCompositeType(const DICompositeType *CTy,
- uint32_t &TypeId) {
+ uint32_t &TypeId, bool StripName) {
auto Tag = CTy->getTag();
switch (Tag) {
case dwarf::DW_TAG_structure_type:
@@ -871,7 +875,7 @@ void BTFDebug::visitCompositeType(const DICompositeType *CTy,
if (CTy->isForwardDecl())
visitFwdDeclType(CTy, Tag == dwarf::DW_TAG_union_type, TypeId);
else
- visitStructType(CTy, Tag == dwarf::DW_TAG_structure_type, TypeId);
+ visitStructType(CTy, Tag == dwarf::DW_TAG_structure_type, TypeId, StripName);
break;
case dwarf::DW_TAG_array_type:
visitArrayType(CTy, TypeId);
@@ -902,7 +906,7 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
if (Tag == dwarf::DW_TAG_atomic_type)
return visitTypeEntry(DTy->getBaseType(), TypeId, CheckPointer,
- SeenPointer);
+ SeenPointer, false);
/// Try to avoid chasing pointees, esp. structure pointees which may
/// unnecessary bring in a lot of types.
@@ -951,9 +955,10 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
// struct/union member.
uint32_t TempTypeId = 0;
if (Tag == dwarf::DW_TAG_member)
- visitTypeEntry(DTy->getBaseType(), TempTypeId, true, false);
+ visitTypeEntry(DTy->getBaseType(), TempTypeId, true, false, false);
else
- visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer);
+ visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer,
+ false);
}
/// Visit a type entry. CheckPointer is true if the type has
@@ -964,7 +969,8 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
/// will not be emitted in BTF and rather forward declarations
/// will be generated.
void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
- bool CheckPointer, bool SeenPointer) {
+ bool CheckPointer, bool SeenPointer,
+ bool StripName) {
if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) {
TypeId = DIToIdMap[Ty];
@@ -1014,7 +1020,8 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
break;
}
uint32_t TmpTypeId;
- visitTypeEntry(BaseTy, TmpTypeId, CheckPointer, SeenPointer);
+ visitTypeEntry(BaseTy, TmpTypeId, CheckPointer, SeenPointer,
+ StripName);
break;
}
}
@@ -1030,7 +1037,7 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
visitSubroutineType(STy, false, std::unordered_map<uint32_t, StringRef>(),
TypeId);
else if (const auto *CTy = dyn_cast<DICompositeType>(Ty))
- visitCompositeType(CTy, TypeId);
+ visitCompositeType(CTy, TypeId, StripName);
else if (const auto *DTy = dyn_cast<DIDerivedType>(Ty))
visitDerivedType(DTy, TypeId, CheckPointer, SeenPointer);
else
@@ -1039,7 +1046,7 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
void BTFDebug::visitTypeEntry(const DIType *Ty) {
uint32_t TypeId;
- visitTypeEntry(Ty, TypeId, false, false);
+ visitTypeEntry(Ty, TypeId, false, false, false);
}
void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
@@ -1048,6 +1055,7 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
return;
}
+ bool StripName = true;
uint32_t TmpId;
switch (Ty->getTag()) {
case dwarf::DW_TAG_typedef:
@@ -1077,6 +1085,8 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
const auto *MemberCTy = dyn_cast<DICompositeType>(MemberBaseType);
if (MemberCTy) {
visitMapDefType(MemberBaseType, TmpId);
+ // Don't strip the name of the wrapper.
+ StripName = false;
} else {
visitTypeEntry(MemberBaseType);
}
@@ -1088,7 +1098,7 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
}
// Visit this type, struct or a const/typedef/volatile/restrict type
- visitTypeEntry(Ty, TypeId, false, false);
+ visitTypeEntry(Ty, TypeId, false, false, StripName);
}
/// Read file contents from the actual file or from the source
@@ -1364,7 +1374,7 @@ void BTFDebug::endFunctionImpl(const MachineFunction *MF) {
/// accessing or preserve debuginfo type.
unsigned BTFDebug::populateType(const DIType *Ty) {
unsigned Id;
- visitTypeEntry(Ty, Id, false, false);
+ visitTypeEntry(Ty, Id, false, false, false);
for (const auto &TypeEntry : TypeEntries)
TypeEntry->completeType(*this);
return Id;
@@ -1563,7 +1573,7 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
visitMapDefType(DIGlobal->getType(), GVTypeId);
else {
const DIType *Ty = tryRemoveAtomicType(DIGlobal->getType());
- visitTypeEntry(Ty, GVTypeId, false, false);
+ visitTypeEntry(Ty, GVTypeId, false, false, false);
}
break;
}
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 75858fcc8bfde..7e8250e5cb1f3 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -126,11 +126,12 @@ class BTFTypeArray : public BTFTypeBase {
class BTFTypeStruct : public BTFTypeBase {
const DICompositeType *STy;
bool HasBitField;
+ bool StripName;
std::vector<struct BTF::BTFMember> Members;
public:
BTFTypeStruct(const DICompositeType *STy, bool IsStruct, bool HasBitField,
- uint32_t NumMembers);
+ uint32_t NumMembers, bool StripName);
uint32_t getSize() override {
return BTFTypeBase::getSize() + Members.size() * BTF::BTFMemberSize;
}
@@ -321,7 +322,7 @@ class BTFDebug : public DebugHandlerBase {
/// @{
void visitTypeEntry(const DIType *Ty);
void visitTypeEntry(const DIType *Ty, uint32_t &TypeId, bool CheckPointer,
- bool SeenPointer);
+ bool SeenPointer, bool StripName);
void visitBasicType(const DIBasicType *BTy, uint32_t &TypeId);
void visitSubroutineType(
const DISubroutineType *STy, bool ForSubprog,
@@ -329,9 +330,10 @@ class BTFDebug : public DebugHandlerBase {
uint32_t &TypeId);
void visitFwdDeclType(const DICompositeType *CTy, bool IsUnion,
uint32_t &TypeId);
- void visitCompositeType(const DICompositeType *CTy, uint32_t &TypeId);
+ void visitCompositeType(const DICompositeType *CTy, uint32_t &TypeId,
+ bool StripName);
void visitStructType(const DICompositeType *STy, bool IsStruct,
- uint32_t &TypeId);
+ uint32_t &TypeId, bool StripName);
void visitArrayType(const DICompositeType *ATy, uint32_t &TypeId);
void visitEnumType(const DICompositeType *ETy, uint32_t &TypeId);
void visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
diff --git a/llvm/test/CodeGen/BPF/BTF/map-def-2.ll b/llvm/test/CodeGen/BPF/BTF/map-def-2.ll
index d4c836f7c479c..12770c5efdcf2 100644
--- a/llvm/test/CodeGen/BPF/BTF/map-def-2.ll
+++ b/llvm/test/CodeGen/BPF/BTF/map-def-2.ll
@@ -23,7 +23,7 @@
; CHECK-BTF-NEXT: [2] STRUCT 'key_type' size=4 vlen=1
; CHECK-BTF-NEXT: 'a1' type_id=3 bits_offset=0
; CHECK-BTF-NEXT: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
-; CHECK-BTF-NEXT: [4] STRUCT 'map_type' size=8 vlen=1
+; CHECK-BTF-NEXT: [4] STRUCT '(anon)' size=8 vlen=1
; CHECK-BTF-NEXT: 'key' type_id=1 bits_offset=0
; CHECK-BTF-NEXT: [5] TYPEDEF '_map_type' type_id=4
; CHECK-BTF-NEXT: [6] TYPEDEF '__map_type' type_id=5
diff --git a/llvm/test/CodeGen/BPF/BTF/map-def-3.ll b/llvm/test/CodeGen/BPF/BTF/map-def-3.ll
index 1d95f03b0d5e8..7013d4e344fba 100644
--- a/llvm/test/CodeGen/BPF/BTF/map-def-3.ll
+++ b/llvm/test/CodeGen/BPF/BTF/map-def-3.ll
@@ -15,7 +15,7 @@
@hash_map = dso_local local_unnamed_addr constant %struct.key_type zeroinitializer, section ".maps", align 4, !dbg !0
; CHECK-BTF: [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
-; CHECK-BTF-NEXT: [2] STRUCT 'key_type' size=4 vlen=1
+; CHECK-BTF-NEXT: [2] STRUCT '(anon)' size=4 vlen=1
; CHECK-BTF-NEXT: 'a1' type_id=1 bits_offset=0
; CHECK-BTF-NEXT: [3] CONST '(anon)' type_id=2
; CHECK-BTF-NEXT: [4] VAR 'hash_map' type_id=3, linkage=global
diff --git a/llvm/test/CodeGen/BPF/BTF/map-def-4.ll b/llvm/test/CodeGen/BPF/BTF/map-def-4.ll
new file mode 100644
index 0000000000000..807af6dafaf73
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/map-def-4.ll
@@ -0,0 +1,84 @@
+; 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]
+;
+; use core::ptr;
+;
+; #[allow(dead_code)]
+; pub struct HashMap<K, V> {
+; key: *const K,
+; value: *const V,
+; }
+;
+; impl<K, V> HashMap<K, V> {
+; pub const fn new() -> Self {
+; Self {
+; key: ptr::null(),
+; value: ptr::null(),
+; }
+; }
+; }
+;
+; #[unsafe(link_section = ".maps")]
+; #[unsafe(no_mangle)]
+; pub static mut MY_MAP: HashMap<u32, u32> = HashMap::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=MY_MAP $(find target/ -name "*.bc" | head -n 1) -o map-def-named-2.bc
+; llvm-dis map-def-named-2.bc -o map-def-named-2.ll
+
+; ModuleID = 'map-def-named-2.bc'
+source_filename = "9xlybfhcys3n1sozp3bnamiuu"
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "bpfel"
+
+ at MY_MAP = global [16 x i8] zeroinitializer, section ".maps", align 8, !dbg !0
+
+!llvm.module.flags = !{!14, !15, !16, !17}
+!llvm.ident = !{!18}
+!llvm.dbg.cu = !{!19}
+
+; CHECK-BTF: [1] PTR '(anon)' type_id=2
+; CHECK-BTF-NEXT: [2] INT 'u32' size=4 bits_offset=0 nr_bits=32 encoding=(none)
+; CHECK-BTF-NEXT: [3] STRUCT '(anon)' size=16 vlen=2
+; CHECK-BTF-NEXT: 'key' type_id=1 bits_offset=0
+; CHECK-BTF-NEXT: 'value' type_id=1 bits_offset=64
+; CHECK-BTF-NEXT: [4] VAR 'MY_MAP' type_id=3, linkage=global
+; CHECK-BTF-NEXT: [5] DATASEC '.maps' size=0 vlen=1
+; CHECK-BTF-NEXT: type_id=4 offset=0 size=16
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "MY_MAP", scope: !2, file: !3, line: 23, type: !4, isLocal: false, isDefinition: true, align: 64)
+!2 = !DINamespace(name: "btf_map", scope: null)
+!3 = !DIFile(filename: "src/main.rs", directory: "/home/vad/playground/btf-map", checksumkind: CSK_MD5, checksum: "954834fe667cc8cedd8b47ffcd2b489f")
+!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "HashMap<u32, u32>", scope: !2, file: !5, size: 128, align: 64, flags: DIFlagPublic, elements: !6, templateParams: !11, identifier: "53d93ef1bb5578628ce008544cf10207")
+!5 = !DIFile(filename: "<unknown>", directory: "")
+!6 = !{!7, !10}
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "key", 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 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !4, file: !5, baseType: !8, size: 64, align: 64, offset: 64, flags: DIFlagPrivate)
+!11 = !{!12, !13}
+!12 = !DITemplateTypeParameter(name: "K", type: !9)
+!13 = !DITemplateTypeParameter(name: "V", type: !9)
+!14 = !{i32 8, !"PIC Level", i32 2}
+!15 = !{i32 7, !"PIE Level", i32 2}
+!16 = !{i32 7, !"Dwarf Version", i32 4}
+!17 = !{i32 2, !"Debug Info Version", i32 3}
+!18 = !{!"rustc version 1.91.0-nightly (160e7623e 2025-08-26)"}
+!19 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !20, producer: "clang LLVM (rustc version 1.91.0-nightly (160e7623e 2025-08-26))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !21, splitDebugInlining: false, nameTableKind: None)
+!20 = !DIFile(filename: "src/main.rs/@/9xlybfhcys3n1sozp3bnamiuu", directory: "/home/vad/playground/btf-map")
+!21 = !{!0}
diff --git a/llvm/test/CodeGen/BPF/BTF/map-def.ll b/llvm/test/CodeGen/BPF/BTF/map-def.ll
index a3835f1dae4b5..d97b75868e516 100644
--- a/llvm/test/CodeGen/BPF/BTF/map-def.ll
+++ b/llvm/test/CodeGen/BPF/BTF/map-def.ll
@@ -27,7 +27,7 @@
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 168
; CHECK-NEXT: .long 168
-; CHECK-NEXT: .long 65
+; CHECK-NEXT: .long 56
; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 1)
; CHECK-NEXT: .long 33554432 # 0x2000000
; CHECK-NEXT: .long 2
@@ -51,20 +51,20 @@
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 32 # 0x20
-; CHECK-NEXT: .long 31 # BTF_KIND_STRUCT(id = 6)
+; CHECK-NEXT: .long 0 # BTF_KIND_STRUCT(id = 6)
; CHECK-NEXT: .long 67108866 # 0x4000002
; CHECK-NEXT: .long 16
-; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 31
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # 0x0
-; CHECK-NEXT: .long 44
+; CHECK-NEXT: .long 35
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 64 # 0x40
-; CHECK-NEXT: .long 50 # BTF_KIND_VAR(id = 7)
+; CHECK-NEXT: .long 41 # BTF_KIND_VAR(id = 7)
; CHECK-NEXT: .long 234881024 # 0xe000000
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 59 # BTF_KIND_DATASEC(id = 8)
+; CHECK-NEXT: .long 50 # BTF_KIND_DATASEC(id = 8)
; CHECK-NEXT: .long 251658241 # 0xf000001
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 7
@@ -81,15 +81,13 @@
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "unsigned int" # string offset=18
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "map_type" # string offset=31
+; CHECK-NEXT: .ascii "key" # string offset=31
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "key" # string offset=40
+; CHECK-NEXT: .ascii "value" # string offset=35
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "value" # string offset=44
+; CHECK-NEXT: .ascii "hash_map" # string offset=41
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "hash_map" # string offset=50
-; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii ".maps" # string offset=59
+; CHECK-NEXT: .ascii ".maps" # string offset=50
; CHECK-NEXT: .byte 0
!llvm.dbg.cu = !{!2}
More information about the llvm-commits
mailing list