Skip to content

Commit 3819d8f

Browse files
mtardyeddyz87
andcommitted
[BPF] Visit nested map array during BTF generation
Fixes missing inner map struct type definitions [^1]. We should visit the type of nested array of maps like we do for global maps. We visit the derived type of the array directly in visitMapDefType. Because of the small changes in visitMapDefType, ordering of the BTF is changed, for example, the PTR for the nested maps might appear later. This explain why this patch had to update the BTF checks of the tests 'map-def-nested-array.ll', 'map-def-2.ll' and 'map-def-3.ll'. We took the opportunity to migrate the last two to use 'print_btf.py'. It ressembles commit 0d21c95 ("[BPF] Handle nested wrapper structs in BPF map definition traversal (#144097)") which focused on directly nested wrapper structs. Before that patch, this ARRAY_OF_MAPS definition would lead to the BTF information include the 'missing_type' as "FWD 'missing_type' fwd_kind=struct": struct missing_type { uint64_t foo; }; struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); [...] __array( values, struct { [...] __type(value, struct missing_type); }); } map SEC(".maps"); Which lead to errors while trying to load the map: libbpf: map 'outer_map.inner': can't determine value size for type [N]: -22. To solve this issue, users had to use the struct in a dummy variable or in a dummy function for the BTF to be generated correctly [^2]. [^1]: https://lore.kernel.org/netdev/[email protected]/T/#u [^2]: cilium/ebpf#1658 (reply in thread) Co-authored-by: Eduard Zingerman <[email protected]> Signed-off-by: Mahe Tardy <[email protected]>
1 parent e38f98f commit 3819d8f

File tree

4 files changed

+127
-99
lines changed

4 files changed

+127
-99
lines changed

llvm/lib/Target/BPF/BTFDebug.cpp

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -957,13 +957,13 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
957957
return;
958958
}
959959

960-
// MapDef type may be a struct type or a non-pointer derived type
960+
// MapDef type may be a struct type or a derived type
961961
const DIType *OrigTy = Ty;
962962
while (auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
963963
auto Tag = DTy->getTag();
964964
if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type &&
965965
Tag != dwarf::DW_TAG_volatile_type &&
966-
Tag != dwarf::DW_TAG_restrict_type)
966+
Tag != dwarf::DW_TAG_restrict_type && Tag != dwarf::DW_TAG_pointer_type)
967967
break;
968968
Ty = DTy->getBaseType();
969969
}
@@ -973,26 +973,34 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
973973
return;
974974

975975
auto Tag = CTy->getTag();
976-
if (Tag != dwarf::DW_TAG_structure_type || CTy->isForwardDecl())
976+
if ((Tag != dwarf::DW_TAG_structure_type &&
977+
Tag != dwarf::DW_TAG_array_type) ||
978+
CTy->isForwardDecl())
977979
return;
978980

979-
// Visit all struct members to ensure their types are visited.
980-
const DINodeArray Elements = CTy->getElements();
981-
for (const auto *Element : Elements) {
982-
const auto *MemberType = cast<DIDerivedType>(Element);
983-
const DIType *MemberBaseType = MemberType->getBaseType();
984-
985-
// If the member is a composite type, that may indicate the currently
986-
// visited composite type is a wrapper, and the member represents the
987-
// actual map definition.
988-
// In that case, visit the member with `visitMapDefType` instead of
989-
// `visitTypeEntry`, treating it specifically as a map definition rather
990-
// than as a regular composite type.
991-
const auto *MemberCTy = dyn_cast<DICompositeType>(MemberBaseType);
992-
if (MemberCTy) {
993-
visitMapDefType(MemberBaseType, TypeId);
994-
} else {
995-
visitTypeEntry(MemberBaseType);
981+
// Visit potential nested map array
982+
if (CTy->getTag() == dwarf::DW_TAG_array_type) {
983+
// Jump to the element type of the array
984+
visitMapDefType(CTy->getBaseType(), TypeId);
985+
} else {
986+
// Visit all struct members to ensure their types are visited.
987+
const DINodeArray Elements = CTy->getElements();
988+
for (const auto *Element : Elements) {
989+
const auto *MemberType = cast<DIDerivedType>(Element);
990+
const DIType *MemberBaseType = MemberType->getBaseType();
991+
992+
// If the member is a composite type, that may indicate the currently
993+
// visited composite type is a wrapper, and the member represents the
994+
// actual map definition.
995+
// In that case, visit the member with `visitMapDefType` instead of
996+
// `visitTypeEntry`, treating it specifically as a map definition rather
997+
// than as a regular composite type.
998+
const auto *MemberCTy = dyn_cast<DICompositeType>(MemberBaseType);
999+
if (MemberCTy) {
1000+
visitMapDefType(MemberBaseType, TypeId);
1001+
} else {
1002+
visitTypeEntry(MemberBaseType);
1003+
}
9961004
}
9971005
}
9981006

llvm/test/CodeGen/BPF/BTF/map-def-2.ll

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
; RUN: llc -mtriple=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
2-
; RUN: llc -mtriple=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
1+
; RUN: llc -mtriple=bpfel -mcpu=v3 -filetype=obj -o %t1 %s
2+
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
3+
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
34
;
45
; Source code:
56
; struct key_type {
@@ -18,51 +19,17 @@
1819

1920
@hash_map = dso_local local_unnamed_addr global %struct.map_type zeroinitializer, section ".maps", align 8, !dbg !0
2021

21-
; CHECK: .long 0 # BTF_KIND_PTR(id = 1)
22-
; CHECK-NEXT: .long 33554432 # 0x2000000
23-
; CHECK-NEXT: .long 2
24-
; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 2)
25-
; CHECK-NEXT: .long 67108865 # 0x4000001
26-
; CHECK-NEXT: .long 4
27-
; CHECK-NEXT: .long 10
28-
; CHECK-NEXT: .long 3
29-
; CHECK-NEXT: .long 0 # 0x0
30-
; CHECK-NEXT: .long 13 # BTF_KIND_INT(id = 3)
31-
; CHECK-NEXT: .long 16777216 # 0x1000000
32-
; CHECK-NEXT: .long 4
33-
; CHECK-NEXT: .long 16777248 # 0x1000020
34-
; CHECK-NEXT: .long 17 # BTF_KIND_TYPEDEF(id = 4)
35-
; CHECK-NEXT: .long 134217728 # 0x8000000
36-
; CHECK-NEXT: .long 5
37-
; CHECK-NEXT: .long 28 # BTF_KIND_TYPEDEF(id = 5)
38-
; CHECK-NEXT: .long 134217728 # 0x8000000
39-
; CHECK-NEXT: .long 6
40-
; CHECK-NEXT: .long 38 # BTF_KIND_STRUCT(id = 6)
41-
; CHECK-NEXT: .long 67108865 # 0x4000001
42-
; CHECK-NEXT: .long 8
43-
; CHECK-NEXT: .long 47
44-
; CHECK-NEXT: .long 1
45-
; CHECK-NEXT: .long 0 # 0x0
46-
; CHECK-NEXT: .long 51 # BTF_KIND_VAR(id = 7)
47-
; CHECK-NEXT: .long 234881024 # 0xe000000
48-
; CHECK-NEXT: .long 4
49-
; CHECK-NEXT: .long 1
50-
; CHECK-NEXT: .long 60 # BTF_KIND_DATASEC(id = 8)
51-
; CHECK-NEXT: .long 251658241 # 0xf000001
52-
; CHECK-NEXT: .long 0
53-
; CHECK-NEXT: .long 7
54-
; CHECK-NEXT: .long hash_map
55-
; CHECK-NEXT: .long 8
56-
57-
; CHECK: .ascii "key_type" # string offset=1
58-
; CHECK: .ascii "a1" # string offset=10
59-
; CHECK: .ascii "int" # string offset=13
60-
; CHECK: .ascii "__map_type" # string offset=17
61-
; CHECK: .ascii "_map_type" # string offset=28
62-
; CHECK: .ascii "map_type" # string offset=38
63-
; CHECK: .ascii "key" # string offset=47
64-
; CHECK: .ascii "hash_map" # string offset=51
65-
; CHECK: .ascii ".maps" # string offset=60
22+
; CHECK-BTF: [1] PTR '(anon)' type_id=2
23+
; CHECK-BTF-NEXT: [2] STRUCT 'key_type' size=4 vlen=1
24+
; CHECK-BTF-NEXT: 'a1' type_id=3 bits_offset=0
25+
; CHECK-BTF-NEXT: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
26+
; CHECK-BTF-NEXT: [4] STRUCT 'map_type' size=8 vlen=1
27+
; CHECK-BTF-NEXT: 'key' type_id=1 bits_offset=0
28+
; CHECK-BTF-NEXT: [5] TYPEDEF '_map_type' type_id=4
29+
; CHECK-BTF-NEXT: [6] TYPEDEF '__map_type' type_id=5
30+
; CHECK-BTF-NEXT: [7] VAR 'hash_map' type_id=6, linkage=global
31+
; CHECK-BTF-NEXT: [8] DATASEC '.maps' size=0 vlen=1
32+
; CHECK-BTF-NEXT: type_id=7 offset=0 size=8
6633

6734
!llvm.dbg.cu = !{!2}
6835
!llvm.module.flags = !{!16, !17, !18}

llvm/test/CodeGen/BPF/BTF/map-def-3.ll

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
; RUN: llc -mtriple=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
2-
; RUN: llc -mtriple=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
1+
; RUN: llc -mtriple=bpfel -mcpu=v3 -filetype=obj -o %t1 %s
2+
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
3+
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
34
;
45
; Source code:
56
; struct key_type {
@@ -13,36 +14,13 @@
1314

1415
@hash_map = dso_local local_unnamed_addr constant %struct.key_type zeroinitializer, section ".maps", align 4, !dbg !0
1516

16-
; CHECK: .long 1 # BTF_KIND_INT(id = 1)
17-
; CHECK-NEXT: .long 16777216 # 0x1000000
18-
; CHECK-NEXT: .long 4
19-
; CHECK-NEXT: .long 16777248 # 0x1000020
20-
; CHECK-NEXT: .long 0 # BTF_KIND_CONST(id = 2)
21-
; CHECK-NEXT: .long 167772160 # 0xa000000
22-
; CHECK-NEXT: .long 3
23-
; CHECK-NEXT: .long 5 # BTF_KIND_STRUCT(id = 3)
24-
; CHECK-NEXT: .long 67108865 # 0x4000001
25-
; CHECK-NEXT: .long 4
26-
; CHECK-NEXT: .long 14
27-
; CHECK-NEXT: .long 1
28-
; CHECK-NEXT: .long 0 # 0x0
29-
; CHECK-NEXT: .long 17 # BTF_KIND_VAR(id = 4)
30-
; CHECK-NEXT: .long 234881024 # 0xe000000
31-
; CHECK-NEXT: .long 2
32-
; CHECK-NEXT: .long 1
33-
; CHECK-NEXT: .long 26 # BTF_KIND_DATASEC(id = 5)
34-
; CHECK-NEXT: .long 251658241 # 0xf000001
35-
; CHECK-NEXT: .long 0
36-
; CHECK-NEXT: .long 4
37-
; CHECK-NEXT: .long hash_map
38-
; CHECK-NEXT: .long 4
39-
40-
; CHECK: .ascii "int" # string offset=1
41-
; CHECK: .ascii "key_type" # string offset=5
42-
; CHECK: .ascii "a1" # string offset=14
43-
; CHECK: .ascii "hash_map" # string offset=17
44-
; CHECK: .ascii ".maps" # string offset=26
45-
17+
; CHECK-BTF: [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
18+
; CHECK-BTF-NEXT: [2] STRUCT 'key_type' size=4 vlen=1
19+
; CHECK-BTF-NEXT: 'a1' type_id=1 bits_offset=0
20+
; CHECK-BTF-NEXT: [3] CONST '(anon)' type_id=2
21+
; CHECK-BTF-NEXT: [4] VAR 'hash_map' type_id=3, linkage=global
22+
; CHECK-BTF-NEXT: [5] DATASEC '.maps' size=0 vlen=1
23+
; CHECK-BTF-NEXT: type_id=4 offset=0 size=4
4624

4725
!llvm.dbg.cu = !{!2}
4826
!llvm.module.flags = !{!11, !12, !13}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
; RUN: llc -mtriple=bpfel -mcpu=v3 -filetype=obj -o %t1 %s
2+
; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
3+
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF-SHORT %s
4+
; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
5+
; Source:
6+
; struct nested_value_type {
7+
; int a1;
8+
; };
9+
; struct map_type {
10+
; struct {
11+
; struct nested_value_type *value;
12+
; } *values[];
13+
; };
14+
; Compilation flags:
15+
; clang -target bpf -g -O2 -S -emit-llvm prog.c
16+
17+
; ModuleID = 'prog.c'
18+
source_filename = "prog.c"
19+
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
20+
target triple = "bpf"
21+
22+
%struct.map_type = type { [0 x ptr] }
23+
24+
@array_of_maps = dso_local local_unnamed_addr global %struct.map_type zeroinitializer, section ".maps", align 8, !dbg !0
25+
26+
; We expect no forward declarations.
27+
;
28+
; CHECK-BTF-SHORT-NOT: FWD
29+
30+
; Assert the whole BTF.
31+
;
32+
; CHECK-BTF: [1] PTR '(anon)' type_id=2
33+
; CHECK-BTF-NEXT: [2] STRUCT 'nested_value_type' size=4 vlen=1
34+
; CHECK-BTF-NEXT: 'a1' type_id=3 bits_offset=0
35+
; CHECK-BTF-NEXT: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
36+
; CHECK-BTF-NEXT: [4] STRUCT '(anon)' size=8 vlen=1
37+
; CHECK-BTF-NEXT: 'value' type_id=1 bits_offset=0
38+
; CHECK-BTF-NEXT: [5] PTR '(anon)' type_id=4
39+
; CHECK-BTF-NEXT: [6] ARRAY '(anon)' type_id=5 index_type_id=7 nr_elems=0
40+
; CHECK-BTF-NEXT: [7] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)
41+
; CHECK-BTF-NEXT: [8] STRUCT 'map_type' size=0 vlen=1
42+
; CHECK-BTF-NEXT: 'values' type_id=6 bits_offset=0
43+
; CHECK-BTF-NEXT: [9] VAR 'array_of_maps' type_id=8, linkage=global
44+
; CHECK-BTF-NEXT: [10] DATASEC '.maps' size=0 vlen=1
45+
; CHECK-BTF-NEXT: type_id=9 offset=0 size=0
46+
47+
!llvm.dbg.cu = !{!2}
48+
!llvm.module.flags = !{!20, !21, !22, !23}
49+
!llvm.ident = !{!24}
50+
51+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
52+
!1 = distinct !DIGlobalVariable(name: "array_of_maps", scope: !2, file: !3, line: 9, type: !5, isLocal: false, isDefinition: true)
53+
!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 22.0.0git ([email protected]:llvm/llvm-project.git ed93eaa421b714028b85cc887d80c45991d7207f)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
54+
!3 = !DIFile(filename: "prog.c", directory: "/home/mtardy/llvm-bug-repro", checksumkind: CSK_MD5, checksum: "9381d9e83e9c0b235a14704224815e96")
55+
!4 = !{!0}
56+
!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_type", file: !3, line: 4, elements: !6)
57+
!6 = !{!7}
58+
!7 = !DIDerivedType(tag: DW_TAG_member, name: "values", scope: !5, file: !3, line: 7, baseType: !8)
59+
!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, elements: !18)
60+
!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
61+
!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !5, file: !3, line: 5, size: 64, elements: !11)
62+
!11 = !{!12}
63+
!12 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !10, file: !3, line: 6, baseType: !13, size: 64)
64+
!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
65+
!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "nested_value_type", file: !3, line: 1, size: 32, elements: !15)
66+
!15 = !{!16}
67+
!16 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !14, file: !3, line: 2, baseType: !17, size: 32)
68+
!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
69+
!18 = !{!19}
70+
!19 = !DISubrange(count: -1)
71+
!20 = !{i32 7, !"Dwarf Version", i32 5}
72+
!21 = !{i32 2, !"Debug Info Version", i32 3}
73+
!22 = !{i32 1, !"wchar_size", i32 4}
74+
!23 = !{i32 7, !"frame-pointer", i32 2}
75+
!24 = !{!"clang version 22.0.0git ([email protected]:llvm/llvm-project.git ed93eaa421b714028b85cc887d80c45991d7207f)"}

0 commit comments

Comments
 (0)