Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -92,11 +92,15 @@ static const char *const kAsanRegisterGlobalsName = "__asan_register_globals"; static const char *const kAsanUnregisterGlobalsName = "__asan_unregister_globals"; +static const char *const kAsanRegisterModuleGlobalsName = + "__asan_register_module_globals"; +static const char *const kAsanUnregisterModuleGlobalsName = + "__asan_unregister_module_globals"; static const char *const kAsanPoisonGlobalsName = "__asan_before_dynamic_init"; static const char *const kAsanUnpoisonGlobalsName = "__asan_after_dynamic_init"; static const char *const kAsanInitName = "__asan_init"; static const char *const kAsanVersionCheckName = - "__asan_version_mismatch_check_v7"; + "__asan_version_mismatch_check_v8"; static const char *const kAsanPtrCmp = "__sanitizer_ptr_cmp"; static const char *const kAsanPtrSub = "__sanitizer_ptr_sub"; static const char *const kAsanHandleNoReturnName = "__asan_handle_no_return"; @@ -110,6 +114,8 @@ "__asan_poison_stack_memory"; static const char *const kAsanUnpoisonStackMemoryName = "__asan_unpoison_stack_memory"; +static const char *const kAsanGlobalsRegisteredFlagName = + "__asan_globals_registered"; static const char *const kAsanOptionDetectUAR = "__asan_option_detect_stack_use_after_return"; @@ -534,6 +540,7 @@ bool InstrumentGlobals(IRBuilder<> &IRB, Module &M); bool ShouldInstrumentGlobal(GlobalVariable *G); + bool ShouldUseMachOGlobalsSection() const; void poisonOneInitializer(Function &GlobalInit, GlobalValue *ModuleName); void createInitializerPoisonCalls(Module &M, GlobalValue *ModuleName); size_t MinRedzoneSizeForGlobal() const { @@ -551,6 +558,8 @@ Function *AsanUnpoisonGlobals; Function *AsanRegisterGlobals; Function *AsanUnregisterGlobals; + Function *AsanRegisterModuleGlobals; + Function *AsanUnregisterModuleGlobals; }; // Stack poisoning does not play well with exception handling. @@ -1287,8 +1296,26 @@ return true; } +// On Mach-O platforms, we emit global metadata in a separate section of the +// binary in order to allow the linker to properly dead strip. This is only +// supported on recent versions of ld64. +bool AddressSanitizerModule::ShouldUseMachOGlobalsSection() const { + if (!TargetTriple.isOSBinFormatMachO()) + return false; + + if (TargetTriple.isMacOSX() && !TargetTriple.isMacOSXVersionLT(10, 11)) + return true; + if (TargetTriple.isiOS() /* or tvOS */ && !TargetTriple.isOSVersionLT(9)) + return true; + if (TargetTriple.isWatchOS() && !TargetTriple.isOSVersionLT(2)) + return true; + + return false; +} + void AddressSanitizerModule::initializeCallbacks(Module &M) { IRBuilder<> IRB(*C); + // Declare our poisoning and unpoisoning functions. AsanPoisonGlobals = checkSanitizerInterfaceFunction(M.getOrInsertFunction( kAsanPoisonGlobalsName, IRB.getVoidTy(), IntptrTy, nullptr)); @@ -1296,6 +1323,7 @@ AsanUnpoisonGlobals = checkSanitizerInterfaceFunction(M.getOrInsertFunction( kAsanUnpoisonGlobalsName, IRB.getVoidTy(), nullptr)); AsanUnpoisonGlobals->setLinkage(Function::ExternalLinkage); + // Declare functions that register/unregister globals. AsanRegisterGlobals = checkSanitizerInterfaceFunction(M.getOrInsertFunction( kAsanRegisterGlobalsName, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); @@ -1304,6 +1332,18 @@ M.getOrInsertFunction(kAsanUnregisterGlobalsName, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); AsanUnregisterGlobals->setLinkage(Function::ExternalLinkage); + + // Declare the functions that find globals in a shared object and then invoke + // the (un)register function on them. + AsanRegisterModuleGlobals = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanRegisterModuleGlobalsName, + IRB.getVoidTy(), IntptrTy, nullptr)); + AsanRegisterModuleGlobals->setLinkage(Function::ExternalLinkage); + + AsanUnregisterModuleGlobals = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanUnregisterModuleGlobalsName, + IRB.getVoidTy(), IntptrTy, nullptr)); + AsanUnregisterModuleGlobals->setLinkage(Function::ExternalLinkage); } // This function replaces all global variables with new variables that have @@ -1442,28 +1482,84 @@ DEBUG(dbgs() << "NEW GLOBAL: " << *NewGlobal << "\n"); } - ArrayType *ArrayOfGlobalStructTy = ArrayType::get(GlobalStructTy, n); - GlobalVariable *AllGlobals = new GlobalVariable( - M, ArrayOfGlobalStructTy, false, GlobalVariable::InternalLinkage, - ConstantArray::get(ArrayOfGlobalStructTy, Initializers), ""); + + GlobalVariable *AllGlobals = nullptr; + GlobalVariable *RegisteredFlag = nullptr; + + // On recent Mach-O platforms, we emit the global metadata in a way that + // allows the linker to properly strip dead globals. + if (ShouldUseMachOGlobalsSection()) { + // RegisteredFlag serves two purposes. First, we can pass it to dladdr() + // to look up the loaded image that contains it. Second, we can store in it + // whether registration has already occurred, to prevent duplicate + // registration. + // + // Common linkage allows us to coalesce needles defined in each object + // file so that there's only one per shared library. + RegisteredFlag = new GlobalVariable( + M, IntptrTy, false, GlobalVariable::CommonLinkage, + ConstantInt::get(IntptrTy, 0), kAsanGlobalsRegisteredFlagName); + + // We also emit a structure which binds the liveness of the global + // variable to the metadata struct. + StructType *LivenessTy = StructType::get(IntptrTy, IntptrTy, nullptr); + + for (size_t i = 0; i < n; i++) { + GlobalVariable *Metadata = new GlobalVariable( + M, GlobalStructTy, false, GlobalVariable::InternalLinkage, + Initializers[i], ""); + Metadata->setSection("__DATA,__asan_globals,regular"); + Metadata->setAlignment(1); // don't leave padding in between + + auto LivenessBinder = ConstantStruct::get(LivenessTy, + Initializers[i]->getAggregateElement(0u), + ConstantExpr::getPointerCast(Metadata, IntptrTy), + nullptr); + GlobalVariable *Liveness = new GlobalVariable( + M, LivenessTy, false, GlobalVariable::InternalLinkage, + LivenessBinder, ""); + Liveness->setSection("__DATA,__asan_liveness,regular,live_support"); + } + } else { + // On all other platfoms, we just emit an array of global metadata + // structures. + ArrayType *ArrayOfGlobalStructTy = ArrayType::get(GlobalStructTy, n); + AllGlobals = new GlobalVariable( + M, ArrayOfGlobalStructTy, false, GlobalVariable::InternalLinkage, + ConstantArray::get(ArrayOfGlobalStructTy, Initializers), ""); + } // Create calls for poisoning before initializers run and unpoisoning after. if (HasDynamicallyInitializedGlobals) createInitializerPoisonCalls(M, ModuleName); - IRB.CreateCall(AsanRegisterGlobals, - {IRB.CreatePointerCast(AllGlobals, IntptrTy), - ConstantInt::get(IntptrTy, n)}); - // We also need to unregister globals at the end, e.g. when a shared library + // Create a call to register the globals with the runtime. + if (ShouldUseMachOGlobalsSection()) { + IRB.CreateCall(AsanRegisterModuleGlobals, + {IRB.CreatePointerCast(RegisteredFlag, IntptrTy)}); + } else { + IRB.CreateCall(AsanRegisterGlobals, + {IRB.CreatePointerCast(AllGlobals, IntptrTy), + ConstantInt::get(IntptrTy, n)}); + } + + // We also need to unregister globals at the end, e.g., when a shared library // gets closed. Function *AsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*C), false), GlobalValue::InternalLinkage, kAsanModuleDtorName, &M); BasicBlock *AsanDtorBB = BasicBlock::Create(*C, "", AsanDtorFunction); IRBuilder<> IRB_Dtor(ReturnInst::Create(*C, AsanDtorBB)); - IRB_Dtor.CreateCall(AsanUnregisterGlobals, - {IRB.CreatePointerCast(AllGlobals, IntptrTy), - ConstantInt::get(IntptrTy, n)}); + + if (ShouldUseMachOGlobalsSection()) { + IRB_Dtor.CreateCall(AsanUnregisterModuleGlobals, + {IRB.CreatePointerCast(RegisteredFlag, IntptrTy)}); + } else { + IRB_Dtor.CreateCall(AsanUnregisterGlobals, + {IRB.CreatePointerCast(AllGlobals, IntptrTy), + ConstantInt::get(IntptrTy, n)}); + } + appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority); DEBUG(dbgs() << M); Index: test/Instrumentation/AddressSanitizer/global_metadata.ll =================================================================== --- test/Instrumentation/AddressSanitizer/global_metadata.ll +++ test/Instrumentation/AddressSanitizer/global_metadata.ll @@ -20,7 +20,7 @@ ; CHECK: [[FILENAME:@__asan_gen_.[0-9]+]] = private unnamed_addr constant [22 x i8] c"/tmp/asan-globals.cpp\00", align 1 ; CHECK: [[LOCDESCR:@__asan_gen_.[0-9]+]] = private unnamed_addr constant { [22 x i8]*, i32, i32 } { [22 x i8]* [[FILENAME]], i32 5, i32 5 } -; Check that location decriptors and global names were passed into __asan_register_globals: +; Check that location descriptors and global names were passed into __asan_register_globals: ; CHECK: i64 ptrtoint ([7 x i8]* [[VARNAME]] to i64) ; CHECK: i64 ptrtoint ({ [22 x i8]*, i32, i32 }* [[LOCDESCR]] to i64) Index: test/Instrumentation/AddressSanitizer/global_metadata_darwin.ll =================================================================== --- test/Instrumentation/AddressSanitizer/global_metadata_darwin.ll +++ test/Instrumentation/AddressSanitizer/global_metadata_darwin.ll @@ -0,0 +1,37 @@ +; Test that global metadata is placed in a separate section on Mach-O platforms, +; allowing dead stripping to be performed, and that the appropriate runtime +; routines are invoked. + +; RUN: opt < %s -asan -asan-module -S | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +@global = global [1 x i32] zeroinitializer, align 4 + +!llvm.asan.globals = !{!0} + +!0 = !{[1 x i32]* @global, !1, !"global", i1 false, i1 false} +!1 = !{!"test-globals.c", i32 1, i32 5} + + +; Test that there is a Needle global variable: +; CHECK: @__asan_needle = internal global i64 0 + +; Find the metadata for @global: +; CHECK: [[METADATA:@[0-9]+]] = internal global {{.*}} @global {{.*}} section "__DATA,__asan_globals,regular", align 1 + +; Find the liveness binder for @global and its metadata: +; CHECK: @{{[0-9]+}} = internal global {{.*}} @global {{.*}} [[METADATA]] {{.*}} section "__DATA,__asan_liveness,regular,live_support" + +; Test that __asan_apply_to_globals is invoked from the constructor: +; CHECK-LABEL: define internal void @asan.module_ctor +; CHECK-NOT: ret +; CHECK: call void @__asan_apply_to_globals(i64 ptrtoint (void (i64, i64)* @__asan_register_globals to i64), i64 ptrtoint (i64* @__asan_needle to i64)) +; CHECK: ret + +; Test that __asan_apply_to_globals is invoked from the destructor: +; CHECK-LABEL: define internal void @asan.module_dtor +; CHECK-NOT: ret +; CHECK: call void @__asan_apply_to_globals(i64 ptrtoint (void (i64, i64)* @__asan_unregister_globals to i64), i64 ptrtoint (i64* @__asan_needle to i64)) +; CHECK: ret