Skip to content

Commit 6d07802

Browse files
committed
[BPF] handle typedef of struct/union for CO-RE relocations
Linux commit torvalds/linux@1cf5b23#diff-289313b9fec99c6f0acfea19d9cfd949 uses "#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)" to apply CO-RE relocations to all records including the following pattern: #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record) typedef struct { int a; } __t; #pragma clang attribute pop int test(__t *arg) { return arg->a; } The current approach to use struct/union type in the relocation record will result in an anonymous struct, which make later type matching difficult in bpf loader. In fact, current BPF backend will fail the above program with assertion: clang: ../lib/Target/BPF/BPFAbstractMemberAccess.cpp:796: ... Assertion `TypeName.size()' failed. clang will change to use the type of the base of the member access which will preserve the typedef modifier for the preserve_{struct,union}_access_index intrinsics in the above example. Here we adjust BPF backend to accept that the debuginfo type metadata may be 'typedef' and handle them properly. Differential Revision: https://reviews.llvm.org/D73902
1 parent 386fd2c commit 6d07802

File tree

3 files changed

+192
-10
lines changed

3 files changed

+192
-10
lines changed

llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,20 @@ bool BPFAbstractMemberAccess::runOnModule(Module &M) {
189189
return doTransformation(M);
190190
}
191191

192-
static bool SkipDIDerivedTag(unsigned Tag) {
192+
static bool SkipDIDerivedTag(unsigned Tag, bool skipTypedef) {
193193
if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type &&
194194
Tag != dwarf::DW_TAG_volatile_type &&
195195
Tag != dwarf::DW_TAG_restrict_type &&
196196
Tag != dwarf::DW_TAG_member)
197-
return false;
197+
return false;
198+
if (Tag == dwarf::DW_TAG_typedef && !skipTypedef)
199+
return false;
198200
return true;
199201
}
200202

201-
static DIType * stripQualifiers(DIType *Ty) {
203+
static DIType * stripQualifiers(DIType *Ty, bool skipTypedef = true) {
202204
while (auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
203-
if (!SkipDIDerivedTag(DTy->getTag()))
205+
if (!SkipDIDerivedTag(DTy->getTag(), skipTypedef))
204206
break;
205207
Ty = DTy->getBaseType();
206208
}
@@ -209,7 +211,7 @@ static DIType * stripQualifiers(DIType *Ty) {
209211

210212
static const DIType * stripQualifiers(const DIType *Ty) {
211213
while (auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
212-
if (!SkipDIDerivedTag(DTy->getTag()))
214+
if (!SkipDIDerivedTag(DTy->getTag(), true))
213215
break;
214216
Ty = DTy->getBaseType();
215217
}
@@ -710,7 +712,7 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
710712
// calculated here as all debuginfo types are available.
711713

712714
// Get type name and calculate the first index.
713-
// We only want to get type name from structure or union.
715+
// We only want to get type name from typedef, structure or union.
714716
// If user wants a relocation like
715717
// int *p; ... __builtin_preserve_access_index(&p[4]) ...
716718
// or
@@ -727,12 +729,15 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
727729
if (!Base)
728730
Base = CInfo.Base;
729731

730-
DIType *Ty = stripQualifiers(cast<DIType>(CInfo.Metadata));
732+
DIType *PossibleTypeDef = stripQualifiers(cast<DIType>(CInfo.Metadata),
733+
false);
734+
DIType *Ty = stripQualifiers(PossibleTypeDef);
731735
if (CInfo.Kind == BPFPreserveUnionAI ||
732736
CInfo.Kind == BPFPreserveStructAI) {
733-
// struct or union type
734-
TypeName = std::string(Ty->getName());
735-
TypeMeta = Ty;
737+
// struct or union type. If the typedef is in the metadata, always
738+
// use the typedef.
739+
TypeName = std::string(PossibleTypeDef->getName());
740+
TypeMeta = PossibleTypeDef;
736741
PatchImm += FirstIndex * (Ty->getSizeInBits() >> 3);
737742
break;
738743
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck %s
2+
; RUN: llc -march=bpfel -mattr=+alu32 -filetype=asm -o - %s | FileCheck %s
3+
;
4+
; Source code:
5+
; #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
6+
; typedef struct {
7+
; int a;
8+
; } __t;
9+
; #pragma clang attribute pop
10+
;
11+
; int test(__t *arg) { return arg->a; }
12+
; Compiler flag to generate IR:
13+
; clang -target bpf -S -O2 -g -emit-llvm test.c
14+
15+
%struct.__t = type { i32 }
16+
17+
; Function Attrs: nounwind readonly
18+
define dso_local i32 @test(%struct.__t* readonly %arg) local_unnamed_addr #0 !dbg !13 {
19+
entry:
20+
call void @llvm.dbg.value(metadata %struct.__t* %arg, metadata !18, metadata !DIExpression()), !dbg !19
21+
%0 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.__ts(%struct.__t* %arg, i32 0, i32 0), !dbg !20, !llvm.preserve.access.index !4
22+
%1 = load i32, i32* %0, align 4, !dbg !20, !tbaa !21
23+
ret i32 %1, !dbg !26
24+
}
25+
26+
; CHECK: .long 1 # BTF_KIND_TYPEDEF(id = 2)
27+
; CHECK-NEXT: .long 134217728 # 0x8000000
28+
; CHECK-NEXT: .long 3
29+
; CHECK-NEXT: .long 0 # BTF_KIND_STRUCT(id = 3)
30+
; CHECK-NEXT: .long 67108865 # 0x4000001
31+
; CHECK-NEXT: .long 4
32+
; CHECK-NEXT: .long 5
33+
; CHECK-NEXT: .long 4
34+
; CHECK-NEXT: .long 0 # 0x0
35+
;
36+
; CHECK: .ascii "__t" # string offset=1
37+
; CHECK: .byte 97 # string offset=5
38+
; CHECK: .ascii ".text" # string offset=20
39+
; CHECK: .ascii "0:0" # string offset=26
40+
;
41+
; CHECK: .long 16 # FieldReloc
42+
; CHECK-NEXT: .long 20 # Field reloc section string offset=20
43+
; CHECK-NEXT: .long 1
44+
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
45+
; CHECK-NEXT: .long 2
46+
; CHECK-NEXT: .long 26
47+
; CHECK-NEXT: .long 0
48+
49+
; Function Attrs: nounwind readnone
50+
declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.__ts(%struct.__t*, i32, i32) #1
51+
52+
; Function Attrs: nounwind readnone speculatable
53+
declare void @llvm.dbg.value(metadata, metadata, metadata) #2
54+
55+
attributes #0 = { nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
56+
attributes #1 = { nounwind readnone }
57+
attributes #2 = { nounwind readnone speculatable}
58+
59+
!llvm.dbg.cu = !{!0}
60+
!llvm.module.flags = !{!9, !10, !11}
61+
!llvm.ident = !{!12}
62+
63+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 5125d1c934efa69ffc1902ce3b8f2f288653a92f)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None)
64+
!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core_bug")
65+
!2 = !{}
66+
!3 = !{!4}
67+
!4 = !DIDerivedType(tag: DW_TAG_typedef, name: "__t", file: !1, line: 4, baseType: !5)
68+
!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !1, line: 2, size: 32, elements: !6)
69+
!6 = !{!7}
70+
!7 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !5, file: !1, line: 3, baseType: !8, size: 32)
71+
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
72+
!9 = !{i32 7, !"Dwarf Version", i32 4}
73+
!10 = !{i32 2, !"Debug Info Version", i32 3}
74+
!11 = !{i32 1, !"wchar_size", i32 4}
75+
!12 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 5125d1c934efa69ffc1902ce3b8f2f288653a92f)"}
76+
!13 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 7, type: !14, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !17)
77+
!14 = !DISubroutineType(types: !15)
78+
!15 = !{!8, !16}
79+
!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !4, size: 64)
80+
!17 = !{!18}
81+
!18 = !DILocalVariable(name: "arg", arg: 1, scope: !13, file: !1, line: 7, type: !16)
82+
!19 = !DILocation(line: 0, scope: !13)
83+
!20 = !DILocation(line: 7, column: 34, scope: !13)
84+
!21 = !{!22, !23, i64 0}
85+
!22 = !{!"", !23, i64 0}
86+
!23 = !{!"int", !24, i64 0}
87+
!24 = !{!"omnipotent char", !25, i64 0}
88+
!25 = !{!"Simple C/C++ TBAA"}
89+
!26 = !DILocation(line: 7, column: 22, scope: !13)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck %s
2+
; RUN: llc -march=bpfel -mattr=+alu32 -filetype=asm -o - %s | FileCheck %s
3+
;
4+
; Source code:
5+
; #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
6+
; typedef union {
7+
; int a;
8+
; } __t;
9+
; #pragma clang attribute pop
10+
;
11+
; int test(__t *arg) { return arg->a; }
12+
; Compiler flag to generate IR:
13+
; clang -target bpf -S -O2 -g -emit-llvm test.c
14+
15+
%union.__t = type { i32 }
16+
17+
; Function Attrs: nounwind readonly
18+
define dso_local i32 @test(%union.__t* readonly %arg) local_unnamed_addr #0 !dbg !13 {
19+
entry:
20+
call void @llvm.dbg.value(metadata %union.__t* %arg, metadata !18, metadata !DIExpression()), !dbg !19
21+
%0 = tail call %union.__t* @llvm.preserve.union.access.index.p0s_union.__ts.p0s_union.__ts(%union.__t* %arg, i32 0), !dbg !20, !llvm.preserve.access.index !4
22+
%a = getelementptr %union.__t, %union.__t* %0, i64 0, i32 0, !dbg !20
23+
%1 = load i32, i32* %a, align 4, !dbg !20, !tbaa !21
24+
ret i32 %1, !dbg !24
25+
}
26+
27+
; CHECK: .long 1 # BTF_KIND_TYPEDEF(id = 2)
28+
; CHECK-NEXT: .long 134217728 # 0x8000000
29+
; CHECK-NEXT: .long 3
30+
; CHECK-NEXT: .long 0 # BTF_KIND_UNION(id = 3)
31+
; CHECK-NEXT: .long 83886081 # 0x5000001
32+
; CHECK-NEXT: .long 4
33+
; CHECK-NEXT: .long 5
34+
; CHECK-NEXT: .long 4
35+
; CHECK-NEXT: .long 0 # 0x0
36+
;
37+
; CHECK: .ascii "__t" # string offset=1
38+
; CHECK: .byte 97 # string offset=5
39+
; CHECK: .ascii ".text" # string offset=20
40+
; CHECK: .ascii "0:0" # string offset=26
41+
;
42+
; CHECK: .long 16 # FieldReloc
43+
; CHECK-NEXT: .long 20 # Field reloc section string offset=20
44+
; CHECK-NEXT: .long 1
45+
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
46+
; CHECK-NEXT: .long 2
47+
; CHECK-NEXT: .long 26
48+
; CHECK-NEXT: .long 0
49+
50+
; Function Attrs: nounwind readnone
51+
declare %union.__t* @llvm.preserve.union.access.index.p0s_union.__ts.p0s_union.__ts(%union.__t*, i32) #1
52+
53+
; Function Attrs: nounwind readnone speculatable
54+
declare void @llvm.dbg.value(metadata, metadata, metadata) #2
55+
56+
attributes #0 = { nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
57+
attributes #1 = { nounwind readnone }
58+
attributes #2 = { nounwind readnone speculatable}
59+
60+
!llvm.dbg.cu = !{!0}
61+
!llvm.module.flags = !{!9, !10, !11}
62+
!llvm.ident = !{!12}
63+
64+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 5125d1c934efa69ffc1902ce3b8f2f288653a92f)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None)
65+
!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core_bug")
66+
!2 = !{}
67+
!3 = !{!4}
68+
!4 = !DIDerivedType(tag: DW_TAG_typedef, name: "__t", file: !1, line: 4, baseType: !5)
69+
!5 = distinct !DICompositeType(tag: DW_TAG_union_type, file: !1, line: 2, size: 32, elements: !6)
70+
!6 = !{!7}
71+
!7 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !5, file: !1, line: 3, baseType: !8, size: 32)
72+
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
73+
!9 = !{i32 7, !"Dwarf Version", i32 4}
74+
!10 = !{i32 2, !"Debug Info Version", i32 3}
75+
!11 = !{i32 1, !"wchar_size", i32 4}
76+
!12 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 5125d1c934efa69ffc1902ce3b8f2f288653a92f)"}
77+
!13 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 7, type: !14, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !17)
78+
!14 = !DISubroutineType(types: !15)
79+
!15 = !{!8, !16}
80+
!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !4, size: 64)
81+
!17 = !{!18}
82+
!18 = !DILocalVariable(name: "arg", arg: 1, scope: !13, file: !1, line: 7, type: !16)
83+
!19 = !DILocation(line: 0, scope: !13)
84+
!20 = !DILocation(line: 7, column: 34, scope: !13)
85+
!21 = !{!22, !22, i64 0}
86+
!22 = !{!"omnipotent char", !23, i64 0}
87+
!23 = !{!"Simple C/C++ TBAA"}
88+
!24 = !DILocation(line: 7, column: 22, scope: !13)

0 commit comments

Comments
 (0)