diff --git a/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 22851b46e8a..67a8325b0cc 100644 --- a/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -44,6 +44,7 @@ #include "llvm/Support/system_error.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BlackList.h" +#include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include @@ -131,6 +132,19 @@ static cl::opt ClBlacklistFile("asan-blacklist", cl::desc("File containing the list of objects to ignore " "during instrumentation"), cl::Hidden); +// This is an experimental feature that will allow to choose between +// instrumented and non-instrumented code at link-time. +// If this option is on, just before instrumenting a function we create its +// clone; if the function is not changed by asan the clone is deleted. +// If we end up with a clone, we put the instrumented function into a section +// called "ASAN" and the uninstrumented function into a section called "NOASAN". +// +// This is still a prototype, we need to figure out a way to keep two copies of +// a function so that the linker can easily choose one of them. +static cl::opt ClKeepUninstrumented("asan-keep-uninstrumented-functions", + cl::desc("Keep uninstrumented copies of functions"), + cl::Hidden, cl::init(false)); + // These flags allow to change the shadow mapping. // The shadow mapping looks like // Shadow = (Mem >> scale) + (1 << offset_log) @@ -1106,8 +1120,7 @@ bool AddressSanitizer::runOnFunction(Function &F) { // If needed, insert __asan_init before checking for SanitizeAddress attr. maybeInsertAsanInitAtFunctionEntry(F); - if (!F.getAttributes().hasAttribute(AttributeSet::FunctionIndex, - Attribute::SanitizeAddress)) + if (!F.hasFnAttribute(Attribute::SanitizeAddress)) return false; if (!ClDebugFunc.empty() && ClDebugFunc != F.getName()) @@ -1118,6 +1131,7 @@ bool AddressSanitizer::runOnFunction(Function &F) { SmallSet TempsToInstrument; SmallVector ToInstrument; SmallVector NoReturnCalls; + int NumAllocas = 0; bool IsWrite; // Fill the set of memory operations to instrument. @@ -1136,6 +1150,8 @@ bool AddressSanitizer::runOnFunction(Function &F) { } else if (isa(BI) && ClMemIntrin) { // ok, take it. } else { + if (isa(BI)) + NumAllocas++; CallSite CS(BI); if (CS) { // A call inside BB. @@ -1152,6 +1168,17 @@ bool AddressSanitizer::runOnFunction(Function &F) { } } + Function *UninstrumentedDuplicate = 0; + bool LikelyToInstrument = + !NoReturnCalls.empty() || !ToInstrument.empty() || (NumAllocas > 0); + if (ClKeepUninstrumented && LikelyToInstrument) { + ValueToValueMapTy VMap; + UninstrumentedDuplicate = CloneFunction(&F, VMap, false); + UninstrumentedDuplicate->removeFnAttr(Attribute::SanitizeAddress); + UninstrumentedDuplicate->setName("NOASAN_" + F.getName()); + F.getParent()->getFunctionList().push_back(UninstrumentedDuplicate); + } + // Instrument. int NumInstrumented = 0; for (size_t i = 0, n = ToInstrument.size(); i != n; i++) { @@ -1176,9 +1203,25 @@ bool AddressSanitizer::runOnFunction(Function &F) { IRBuilder<> IRB(CI); IRB.CreateCall(AsanHandleNoReturnFunc); } - DEBUG(dbgs() << "ASAN done instrumenting:\n" << F << "\n"); - return NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty(); + bool res = NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty(); + DEBUG(dbgs() << "ASAN done instrumenting: " << res << " " << F << "\n"); + + if (ClKeepUninstrumented) { + if (!res) { + // No instrumentation is done, no need for the duplicate. + if (UninstrumentedDuplicate) + UninstrumentedDuplicate->eraseFromParent(); + } else { + // The function was instrumented. We must have the duplicate. + assert(UninstrumentedDuplicate); + UninstrumentedDuplicate->setSection("NOASAN"); + assert(!F.hasSection()); + F.setSection("ASAN"); + } + } + + return res; } static uint64_t ValueForPoison(uint64_t PoisonByte, size_t ShadowRedzoneSize) { diff --git a/test/Instrumentation/AddressSanitizer/keep-instrumented_functions.ll b/test/Instrumentation/AddressSanitizer/keep-instrumented_functions.ll new file mode 100644 index 00000000000..ff3bbb047ff --- /dev/null +++ b/test/Instrumentation/AddressSanitizer/keep-instrumented_functions.ll @@ -0,0 +1,23 @@ +; Test the -asan-keep-uninstrumented-functions flag: FOO should get cloned +; RUN: opt < %s -asan -asan-keep-uninstrumented-functions -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@a = global i32 0, align 4 + +define i32 @main() sanitize_address { +entry: + tail call void @FOO(i32* @a) + ret i32 0 +} + +define void @FOO(i32* nocapture %x) sanitize_address { +entry: + store i32 1, i32* %x, align 4 + ret void +} + +; main should not be cloned since it is not being instrumented by asan. +; CHECK-NOT: NOASAN_main +; CHECK: define void @FOO{{.*}} section "ASAN" +; CHECK: define void @NOASAN_FOO{{.*}} section "NOASAN"