ASAN: Provide reliable debug info for local variables at -O0.
authorAdrian Prantl <aprantl@apple.com>
Mon, 11 Dec 2017 20:43:21 +0000 (20:43 +0000)
committerAdrian Prantl <aprantl@apple.com>
Mon, 11 Dec 2017 20:43:21 +0000 (20:43 +0000)
The function stack poisioner conditionally stores local variables
either in an alloca or in malloc'ated memory, which has the
unfortunate side-effect, that the actual address of the variable is
only materialized when the variable is accessed, which means that
those variables are mostly invisible to the debugger even when
compiling without optimizations.

This patch stores the address of the local stack base into an alloca,
which can be referred to by the debug info and is available throughout
the function. This adds one extra pointer-sized alloca to each stack
frame (but mem2reg can optimize it away again when optimizations are
enabled, yielding roughly the same debug info quality as before in
optimized code).

rdar://problem/30433661

Differential Revision: https://reviews.llvm.org/D41034

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@320415 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Transforms/Instrumentation/AddressSanitizer.cpp
test/DebugInfo/Generic/block-asan.ll
test/Instrumentation/AddressSanitizer/basic.ll
test/Instrumentation/AddressSanitizer/debug_info.ll
test/Instrumentation/AddressSanitizer/local_stack_base.ll [new file with mode: 0644]
test/Instrumentation/AddressSanitizer/stack_layout.ll

index f626c2c0b168cc75ba6b6d1953cbfad5057f199e..8328d40319411accdf43bd1d8a5c8dd81b129a54 100644 (file)
@@ -2869,8 +2869,12 @@ void FunctionStackPoisoner::processStaticAllocas() {
 
   Value *FakeStack;
   Value *LocalStackBase;
+  Value *LocalStackBaseAlloca;
+  bool Deref;
 
   if (DoStackMalloc) {
+    LocalStackBaseAlloca =
+        IRB.CreateAlloca(IntptrTy, nullptr, "asan_local_stack_base");
     // void *FakeStack = __asan_option_detect_stack_use_after_return
     //     ? __asan_stack_malloc_N(LocalStackSize)
     //     : nullptr;
@@ -2901,25 +2905,31 @@ void FunctionStackPoisoner::processStaticAllocas() {
     IRBIf.SetCurrentDebugLocation(EntryDebugLocation);
     Value *AllocaValue =
         DoDynamicAlloca ? createAllocaForLayout(IRBIf, L, true) : StaticAlloca;
+
     IRB.SetInsertPoint(InsBefore);
     IRB.SetCurrentDebugLocation(EntryDebugLocation);
     LocalStackBase = createPHI(IRB, NoFakeStack, AllocaValue, Term, FakeStack);
+    IRB.SetCurrentDebugLocation(EntryDebugLocation);
+    IRB.CreateStore(LocalStackBase, LocalStackBaseAlloca);
+    Deref = true;
   } else {
     // void *FakeStack = nullptr;
     // void *LocalStackBase = alloca(LocalStackSize);
     FakeStack = ConstantInt::get(IntptrTy, 0);
     LocalStackBase =
         DoDynamicAlloca ? createAllocaForLayout(IRB, L, true) : StaticAlloca;
+    LocalStackBaseAlloca = LocalStackBase;
+    Deref = false;
   }
 
   // Replace Alloca instructions with base+offset.
   for (const auto &Desc : SVD) {
     AllocaInst *AI = Desc.AI;
+    replaceDbgDeclareForAlloca(AI, LocalStackBaseAlloca, DIB, Deref,
+                               Desc.Offset, DIExpression::NoDeref);
     Value *NewAllocaPtr = IRB.CreateIntToPtr(
         IRB.CreateAdd(LocalStackBase, ConstantInt::get(IntptrTy, Desc.Offset)),
         AI->getType());
-    replaceDbgDeclareForAlloca(AI, NewAllocaPtr, DIB, DIExpression::NoDeref,
-                               0, DIExpression::NoDeref);
     AI->replaceAllUsesWith(NewAllocaPtr);
   }
 
index 73df59bf3d5dfdd84063f20c4bea0593382ddffa..c3f71e742b2c4c476bec9ddb6503297076be9d95 100644 (file)
@@ -13,7 +13,7 @@
 
 ; Check that the location of the ASAN instrumented __block variable is
 ; correct.
-; CHECK: !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_plus_uconst, 24)
+; CHECK: !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 32, DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_plus_uconst, 24)
 
 target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
 
index 2385341387d2726986edc7402f2e5d762b59f97e..f684f9927faeecac261dd43dbd64431b8c77d721 100644 (file)
@@ -91,6 +91,7 @@ entry:
 }
 
 ; CHECK-LABEL: define void @alloca_test()
+; CHECK: %asan_local_stack_base = alloca
 ; CHECK: = alloca
 ; CHECK-NOT: = alloca
 ; CHECK: ret void
index 37829b0053fb84e60dcadb1bff252408994a5306..544082d0da38d1f583c6dde59333a19633d45ca4 100644 (file)
@@ -24,9 +24,9 @@ entry:
 ;   CHECK: entry:
 ; Verify that llvm.dbg.declare calls are in the entry basic block.
 ;   CHECK-NOT: %entry
-;   CHECK: call void @llvm.dbg.declare(metadata {{.*}}, metadata ![[ARG_ID:[0-9]+]], metadata !DIExpression())
+;   CHECK: call void @llvm.dbg.declare(metadata {{.*}}, metadata ![[ARG_ID:[0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 32))
 ;   CHECK-NOT: %entry
-;   CHECK: call void @llvm.dbg.declare(metadata {{.*}}, metadata ![[VAR_ID:[0-9]+]], metadata !DIExpression())
+;   CHECK: call void @llvm.dbg.declare(metadata {{.*}}, metadata ![[VAR_ID:[0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 48))
 
 declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone
 
diff --git a/test/Instrumentation/AddressSanitizer/local_stack_base.ll b/test/Instrumentation/AddressSanitizer/local_stack_base.ll
new file mode 100644 (file)
index 0000000..f440410
--- /dev/null
@@ -0,0 +1,54 @@
+; RUN: opt -S -asan -asan-skip-promotable-allocas=0 %s -o - | FileCheck %s
+; Generated from:
+; int bar(int y) {
+;   return y + 2;
+; }
+
+source_filename = "/tmp/t.c"
+target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-apple-macosx10.13.0"
+
+; Function Attrs: noinline nounwind optnone sanitize_address ssp uwtable
+define i32 @foo(i32 %i) #0 !dbg !8 {
+entry:
+  %i.addr = alloca i32, align 4
+  store i32 %i, i32* %i.addr, align 4
+  call void @llvm.dbg.declare(metadata i32* %i.addr, metadata !12, metadata !DIExpression()), !dbg !13
+
+  ; CHECK: %asan_local_stack_base = alloca i64
+  ; CHECK: %[[ALLOCA:.*]] = ptrtoint i8* %MyAlloca to i64
+  ; CHECK: %[[PHI:.*]] = phi i64 {{.*}} %[[ALLOCA]],
+  ; CHECK: store i64 %[[PHI]], i64* %asan_local_stack_base, !dbg
+  ; CHECK: call void @llvm.dbg.declare(metadata i64* %asan_local_stack_base, metadata !13, metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 32)), !dbg !14
+  %0 = load i32, i32* %i.addr, align 4, !dbg !14
+  %add = add nsw i32 %0, 2, !dbg !15
+  ret i32 %add, !dbg !16
+}
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind optnone sanitize_address ssp uwtable }
+attributes #1 = { nounwind readnone speculatable }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 (trunk 320115) (llvm/trunk 320116)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "/tmp/t.c", directory: "/Data/llvm")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{!"clang version 6.0.0 (trunk 320115) (llvm/trunk 320116)"}
+!8 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2)
+!9 = !DISubroutineType(types: !10)
+!10 = !{!11, !11}
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !DILocalVariable(name: "i", arg: 1, scope: !8, file: !1, line: 1, type: !11)
+!13 = !DILocation(line: 1, column: 13, scope: !8)
+!14 = !DILocation(line: 2, column: 10, scope: !8)
+!15 = !DILocation(line: 2, column: 12, scope: !8)
+!16 = !DILocation(line: 2, column: 3, scope: !8)
index 4e756f9ab2f251ad4dcb85ad3d9ed0d2e643cfd4..85169d523b6850f00dece88287bfad09657b1613 100644 (file)
@@ -22,6 +22,7 @@ entry:
 ; CHECK-LABEL: Func1
 
 ; CHECK-STATIC: alloca [192 x i8]
+; CHECK-STATIC: %asan_local_stack_base = alloca i64
 ; CHECK-DYNAMIC: alloca i8, i64 192
 
 ; CHECK-NOT: alloca
@@ -43,6 +44,7 @@ entry:
 ; CHECK-LABEL: Func2
 
 ; CHECK-STATIC: alloca [864 x i8]
+; CHECK-STATIC: %asan_local_stack_base = alloca i64
 ; CHECK-DYNAMIC: alloca i8, i64 864
 
 ; CHECK-NOT: alloca
@@ -65,6 +67,7 @@ entry:
 ; CHECK-LABEL: Func3
 
 ; CHECK-STATIC: alloca [768 x i8]
+; CHECK-STATIC: %asan_local_stack_base = alloca i64
 ; CHECK-DYNAMIC: alloca i8, i64 768
 
 ; CHECK-NOT: alloca