From: Yonghong Song Date: Thu, 16 Jul 2020 21:26:09 +0000 (-0700) Subject: BPF: generate .rodata BTF datasec for certain initialized local var's X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=0e347c0ff0a88a8412299d024c2f32201fe342d1;p=android-x86%2Fexternal-llvm-project.git BPF: generate .rodata BTF datasec for certain initialized local var's Currently, BTF datasec type for .rodata is generated only if there are user-defined readonly global variables which have debuginfo generated. Certain readonly global variables may be generated from initialized local variables. For example, void foo(const void *); int test() { const struct { unsigned a[4]; char b; } val = { .a = {2, 3, 4, 5}, .b = 6 }; foo(&val); return 0; } The clang will create a private linkage const global to store the initialized value: @__const.test.val = private unnamed_addr constant %struct.anon { [4 x i32] [i32 2, i32 3, i32 4, i32 5], i8 6 }, align 4 This global variable eventually is put in .rodata ELF section. If there is .rodata ELF section, libbpf expects a BTF .rodata datasec as well even though it may be empty meaning there are no global readonly variables with proper debuginfo. Martin reported a bug where without this empty BTF .rodata datasec, the bpftool gen will exit with an error. This patch fixed the issue by generating .rodata BTF datasec if there exists local var intial data which will result in .rodata ELF section. Differential Revision: https://reviews.llvm.org/D84002 --- diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp index 4510e935748..13999d800a8 100644 --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -22,6 +22,7 @@ #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/Support/LineIterator.h" +#include "llvm/Target/TargetLoweringObjectFile.h" using namespace llvm; @@ -1125,6 +1126,20 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) { if (ProcessingMapDef != SecName.startswith(".maps")) continue; + // Create a .rodata datasec if the global variable is an initialized + // constant with private linkage and if it won't be in .rodata.str<#> + // and .rodata.cst<#> sections. + if (SecName == ".rodata" && Global.hasPrivateLinkage() && + DataSecEntries.find(std::string(SecName)) == DataSecEntries.end()) { + SectionKind GVKind = + TargetLoweringObjectFile::getKindForGlobal(&Global, Asm->TM); + // skip .rodata.str<#> and .rodata.cst<#> sections + if (!GVKind.isMergeableCString() && !GVKind.isMergeableConst()) { + DataSecEntries[std::string(SecName)] = + std::make_unique(Asm, std::string(SecName)); + } + } + SmallVector GVs; Global.getDebugInfo(GVs); diff --git a/llvm/test/CodeGen/BPF/BTF/local-var-readonly-1.ll b/llvm/test/CodeGen/BPF/BTF/local-var-readonly-1.ll new file mode 100644 index 00000000000..3da7e64c220 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/local-var-readonly-1.ll @@ -0,0 +1,105 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; +; Source: +; void foo(const void *); +; int test() { +; const char *str = "abcd"; +; const struct { +; unsigned a[4]; +; } val = { .a = {2, 3, 4, 5} }; +; foo(str); +; foo(&val); +; return 0; +; } +; Compilation flag: +; clang -target bpf -O2 -g -S -emit-llvm t.c + +%struct.anon = type { [4 x i32] } + +@.str = private unnamed_addr constant [5 x i8] c"abcd\00", align 1 +@__const.test.val = private unnamed_addr constant %struct.anon { [4 x i32] [i32 2, i32 3, i32 4, i32 5] }, align 4 + +; Function Attrs: nounwind +define dso_local i32 @test() local_unnamed_addr #0 !dbg !7 { +entry: + %val = alloca %struct.anon, align 4 + call void @llvm.dbg.value(metadata i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), metadata !12, metadata !DIExpression()), !dbg !25 + %0 = bitcast %struct.anon* %val to i8*, !dbg !26 + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %0) #4, !dbg !26 + call void @llvm.dbg.declare(metadata %struct.anon* %val, metadata !16, metadata !DIExpression()), !dbg !27 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(16) %0, i8* nonnull align 4 dereferenceable(16) bitcast (%struct.anon* @__const.test.val to i8*), i64 16, i1 false), !dbg !27 + tail call void @foo(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0)) #4, !dbg !28 + call void @foo(i8* nonnull %0) #4, !dbg !29 + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %0) #4, !dbg !30 + ret i32 0, !dbg !31 +} + +; the initial value of "str" is stored in section .rodata.str1.1 +; the initial value of "val" is stored in section .rodata.cst16 +; CHECK-NOT: BTF_KIND_DATASEC + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +declare !dbg !32 dso_local void @foo(i8*) local_unnamed_addr #3 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #2 + +attributes #0 = { nounwind "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"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { nounwind readnone speculatable willreturn } +attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 1e92cffe18a07c12042b57504dfa7fb709b833c8)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "t.c", directory: "/tmp/home/yhs/tmp") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 1e92cffe18a07c12042b57504dfa7fb709b833c8)"} +!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12, !16} +!12 = !DILocalVariable(name: "str", scope: !7, file: !1, line: 3, type: !13) +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !15) +!15 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!16 = !DILocalVariable(name: "val", scope: !7, file: !1, line: 6, type: !17) +!17 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !18) +!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !7, file: !1, line: 4, size: 128, elements: !19) +!19 = !{!20} +!20 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !18, file: !1, line: 5, baseType: !21, size: 128) +!21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 128, elements: !23) +!22 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!23 = !{!24} +!24 = !DISubrange(count: 4) +!25 = !DILocation(line: 0, scope: !7) +!26 = !DILocation(line: 4, column: 3, scope: !7) +!27 = !DILocation(line: 6, column: 5, scope: !7) +!28 = !DILocation(line: 7, column: 3, scope: !7) +!29 = !DILocation(line: 8, column: 3, scope: !7) +!30 = !DILocation(line: 10, column: 1, scope: !7) +!31 = !DILocation(line: 9, column: 3, scope: !7) +!32 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !33, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!33 = !DISubroutineType(types: !34) +!34 = !{null, !35} +!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) +!36 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) diff --git a/llvm/test/CodeGen/BPF/BTF/local-var-readonly-2.ll b/llvm/test/CodeGen/BPF/BTF/local-var-readonly-2.ll new file mode 100644 index 00000000000..772b566698f --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/local-var-readonly-2.ll @@ -0,0 +1,97 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; +; Source: +; void foo(const void *); +; int test() { +; const struct { +; unsigned a[4]; +; char b; +; } val = { .a = {2, 3, 4, 5}, .b = 4 }; +; foo(&val); +; return 0; +; } +; Compilation flag: +; clang -target bpf -O2 -g -S -emit-llvm t.c + +%struct.anon = type { [4 x i32], i8 } + +@__const.test.val = private unnamed_addr constant %struct.anon { [4 x i32] [i32 2, i32 3, i32 4, i32 5], i8 4 }, align 4 + +; Function Attrs: nounwind +define dso_local i32 @test() local_unnamed_addr #0 !dbg !7 { +entry: + %val = alloca %struct.anon, align 4 + %0 = bitcast %struct.anon* %val to i8*, !dbg !23 + call void @llvm.lifetime.start.p0i8(i64 20, i8* nonnull %0) #4, !dbg !23 + call void @llvm.dbg.declare(metadata %struct.anon* %val, metadata !12, metadata !DIExpression()), !dbg !24 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(20) %0, i8* nonnull align 4 dereferenceable(20) bitcast (%struct.anon* @__const.test.val to i8*), i64 20, i1 false), !dbg !24 + call void @foo(i8* nonnull %0) #4, !dbg !25 + call void @llvm.lifetime.end.p0i8(i64 20, i8* nonnull %0) #4, !dbg !26 + ret i32 0, !dbg !27 +} + +; the init value of local variable "val" is stored in .rodata section +; CHECK: .long 42 # BTF_KIND_DATASEC +; CHECK-NEXT: .long 251658240 # 0xf000000 +; CHECK-NEXT: .long 0 + +; CHECK: .ascii ".rodata" # string offset=42 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +declare !dbg !28 dso_local void @foo(i8*) local_unnamed_addr #3 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "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"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { nounwind readnone speculatable willreturn } +attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 1e92cffe18a07c12042b57504dfa7fb709b833c8)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "t.c", directory: "/tmp/home/yhs/tmp") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 1e92cffe18a07c12042b57504dfa7fb709b833c8)"} +!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "val", scope: !7, file: !1, line: 6, type: !13) +!13 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !14) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !7, file: !1, line: 3, size: 160, elements: !15) +!15 = !{!16, !21} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !14, file: !1, line: 4, baseType: !17, size: 128) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) +!18 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!19 = !{!20} +!20 = !DISubrange(count: 4) +!21 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !14, file: !1, line: 5, baseType: !22, size: 8, offset: 128) +!22 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!23 = !DILocation(line: 3, column: 3, scope: !7) +!24 = !DILocation(line: 6, column: 5, scope: !7) +!25 = !DILocation(line: 7, column: 3, scope: !7) +!26 = !DILocation(line: 9, column: 1, scope: !7) +!27 = !DILocation(line: 8, column: 3, scope: !7) +!28 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !29, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!29 = !DISubroutineType(types: !30) +!30 = !{null, !31} +!31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) +!32 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null)