mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
[asan] Support dead code stripping on Mach-O platforms
On OS X El Capitan and iOS 9, the linker supports a new section attribute, live_support, which allows dead stripping to remove dead globals along with the ASAN metadata about them. With this change __asan_global structures are emitted in a new __DATA,__asan_globals section on Darwin. Additionally, there is a __DATA,__asan_liveness section with the live_support attribute. Each entry in this section is simply a tuple that binds together the liveness of a global variable and its ASAN metadata structure. Thus the metadata structure will be alive if and only if the global it references is also alive. Review: http://reviews.llvm.org/D16737 llvm-svn: 264645
This commit is contained in:
parent
ef4c4640e2
commit
968dea05c0
@ -92,11 +92,15 @@ static const char *const kAsanReportErrorTemplate = "__asan_report_";
|
||||
static const char *const kAsanRegisterGlobalsName = "__asan_register_globals";
|
||||
static const char *const kAsanUnregisterGlobalsName =
|
||||
"__asan_unregister_globals";
|
||||
static const char *const kAsanRegisterImageGlobalsName =
|
||||
"__asan_register_image_globals";
|
||||
static const char *const kAsanUnregisterImageGlobalsName =
|
||||
"__asan_unregister_image_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 @@ static const char *const kAsanPoisonStackMemoryName =
|
||||
"__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 @@ class AddressSanitizerModule : public ModulePass {
|
||||
|
||||
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 @@ class AddressSanitizerModule : public ModulePass {
|
||||
Function *AsanUnpoisonGlobals;
|
||||
Function *AsanRegisterGlobals;
|
||||
Function *AsanUnregisterGlobals;
|
||||
Function *AsanRegisterImageGlobals;
|
||||
Function *AsanUnregisterImageGlobals;
|
||||
};
|
||||
|
||||
// Stack poisoning does not play well with exception handling.
|
||||
@ -1287,8 +1296,26 @@ bool AddressSanitizerModule::ShouldInstrumentGlobal(GlobalVariable *G) {
|
||||
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 @@ void AddressSanitizerModule::initializeCallbacks(Module &M) {
|
||||
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 @@ void AddressSanitizerModule::initializeCallbacks(Module &M) {
|
||||
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.
|
||||
AsanRegisterImageGlobals = checkSanitizerInterfaceFunction(
|
||||
M.getOrInsertFunction(kAsanRegisterImageGlobalsName,
|
||||
IRB.getVoidTy(), IntptrTy, nullptr));
|
||||
AsanRegisterImageGlobals->setLinkage(Function::ExternalLinkage);
|
||||
|
||||
AsanUnregisterImageGlobals = checkSanitizerInterfaceFunction(
|
||||
M.getOrInsertFunction(kAsanUnregisterImageGlobalsName,
|
||||
IRB.getVoidTy(), IntptrTy, nullptr));
|
||||
AsanUnregisterImageGlobals->setLinkage(Function::ExternalLinkage);
|
||||
}
|
||||
|
||||
// This function replaces all global variables with new variables that have
|
||||
@ -1442,28 +1482,84 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
|
||||
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(AsanRegisterImageGlobals,
|
||||
{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(AsanUnregisterImageGlobals,
|
||||
{IRB.CreatePointerCast(RegisteredFlag, IntptrTy)});
|
||||
} else {
|
||||
IRB_Dtor.CreateCall(AsanUnregisterGlobals,
|
||||
{IRB.CreatePointerCast(AllGlobals, IntptrTy),
|
||||
ConstantInt::get(IntptrTy, n)});
|
||||
}
|
||||
|
||||
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority);
|
||||
|
||||
DEBUG(dbgs() << M);
|
||||
|
@ -20,7 +20,7 @@ target triple = "x86_64-unknown-linux-gnu"
|
||||
; 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)
|
||||
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user