Skip to content

Commit

Permalink
[asan] Put ctor/dtor in comdat.
Browse files Browse the repository at this point in the history
When possible, put ASan ctor/dtor in comdat.

The only reason not to is global registration, which can be
TU-specific. This is not the case when there are no instrumented
globals. This is also limited to ELF targets, because MachO does
not have comdat, and COFF linkers may GC comdat constructors.

The benefit of this is a lot less __asan_init() calls: one per DSO
instead of one per TU. It's also necessary for the upcoming
gc-sections-for-globals change on Linux, where multiple references to
section start symbols trigger quadratic behaviour in gold linker.

This is a second re-land of r298756. This time with a flag to disable
the whole thing to avoid a bug in the gold linker:
  https://sourceware.org/bugzilla/show_bug.cgi?id=19002

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301586 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
eugenis committed Apr 27, 2017
1 parent b0c82b8 commit cd2999e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 10 deletions.
57 changes: 48 additions & 9 deletions lib/Transforms/Instrumentation/AddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,13 @@ static cl::opt<bool>
"code stripping of globals"),
cl::Hidden, cl::init(true));

// This is on by default even though there is a bug in gold:
// https://sourceware.org/bugzilla/show_bug.cgi?id=19002
static cl::opt<bool>
ClWithComdat("asan-with-comdat",
cl::desc("Place ASan constructors in comdat sections"),
cl::Hidden, cl::init(true));

// Debug flags.
static cl::opt<int> ClDebug("asan-debug", cl::desc("debug"), cl::Hidden,
cl::init(0));
Expand Down Expand Up @@ -607,7 +614,7 @@ class AddressSanitizerModule : public ModulePass {
private:
void initializeCallbacks(Module &M);

bool InstrumentGlobals(IRBuilder<> &IRB, Module &M);
bool InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat);
void InstrumentGlobalsCOFF(IRBuilder<> &IRB, Module &M,
ArrayRef<GlobalVariable *> ExtendedGlobals,
ArrayRef<Constant *> MetadataInitializers);
Expand Down Expand Up @@ -647,6 +654,9 @@ class AddressSanitizerModule : public ModulePass {
Function *AsanUnregisterGlobals;
Function *AsanRegisterImageGlobals;
Function *AsanUnregisterImageGlobals;

Function *AsanCtorFunction = nullptr;
Function *AsanDtorFunction = nullptr;
};

// Stack poisoning does not play well with exception handling.
Expand Down Expand Up @@ -1431,8 +1441,13 @@ void AddressSanitizerModule::poisonOneInitializer(Function &GlobalInit,
void AddressSanitizerModule::createInitializerPoisonCalls(
Module &M, GlobalValue *ModuleName) {
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
if (!GV)
return;

ConstantArray *CA = dyn_cast<ConstantArray>(GV->getInitializer());
if (!CA)
return;

ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
for (Use &OP : CA->operands()) {
if (isa<ConstantAggregateZero>(OP)) continue;
ConstantStruct *CS = cast<ConstantStruct>(OP);
Expand Down Expand Up @@ -1636,11 +1651,10 @@ AddressSanitizerModule::CreateMetadataGlobal(Module &M, Constant *Initializer,
}

IRBuilder<> AddressSanitizerModule::CreateAsanModuleDtor(Module &M) {
Function *AsanDtorFunction =
AsanDtorFunction =
Function::Create(FunctionType::get(Type::getVoidTy(*C), false),
GlobalValue::InternalLinkage, kAsanModuleDtorName, &M);
BasicBlock *AsanDtorBB = BasicBlock::Create(*C, "", AsanDtorFunction);
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority);

return IRBuilder<>(ReturnInst::Create(*C, AsanDtorBB));
}
Expand Down Expand Up @@ -1756,7 +1770,10 @@ void AddressSanitizerModule::InstrumentGlobalsWithMetadataArray(
// This function replaces all global variables with new variables that have
// trailing redzones. It also creates a function that poisons
// redzones and inserts this function into llvm.global_ctors.
bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
// Sets *CtorComdat to true if the global registration code emitted into the
// asan constructor is comdat-compatible.
bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat) {
*CtorComdat = false;
GlobalsMD.init(M);

SmallVector<GlobalVariable *, 16> GlobalsToChange;
Expand All @@ -1766,7 +1783,10 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
}

size_t n = GlobalsToChange.size();
if (n == 0) return false;
if (n == 0) {
*CtorComdat = true;
return false;
}

auto &DL = M.getDataLayout();

Expand Down Expand Up @@ -1938,17 +1958,36 @@ bool AddressSanitizerModule::runOnModule(Module &M) {
if (CompileKernel)
return false;

Function *AsanCtorFunction;
// Create a module constructor. A destructor is created lazily because not all
// platforms, and not all modules need it.
std::tie(AsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
M, kAsanModuleCtorName, kAsanInitName, /*InitArgTypes=*/{},
/*InitArgs=*/{}, kAsanVersionCheckName);
appendToGlobalCtors(M, AsanCtorFunction, kAsanCtorAndDtorPriority);

bool CtorComdat = true;
bool Changed = false;
// TODO(glider): temporarily disabled globals instrumentation for KASan.
if (ClGlobals) {
IRBuilder<> IRB(AsanCtorFunction->getEntryBlock().getTerminator());
Changed |= InstrumentGlobals(IRB, M);
Changed |= InstrumentGlobals(IRB, M, &CtorComdat);
}

// Put the constructor and destructor in comdat if both
// (1) global instrumentation is not TU-specific
// (2) target is ELF.
if (ClWithComdat && TargetTriple.isOSBinFormatELF() && CtorComdat) {
AsanCtorFunction->setComdat(M.getOrInsertComdat(kAsanModuleCtorName));
appendToGlobalCtors(M, AsanCtorFunction, kAsanCtorAndDtorPriority,
AsanCtorFunction);
if (AsanDtorFunction) {
AsanDtorFunction->setComdat(M.getOrInsertComdat(kAsanModuleDtorName));
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority,
AsanDtorFunction);
}
} else {
appendToGlobalCtors(M, AsanCtorFunction, kAsanCtorAndDtorPriority);
if (AsanDtorFunction)
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority);
}

return Changed;
Expand Down
2 changes: 1 addition & 1 deletion test/Instrumentation/AddressSanitizer/instrument_global.ll
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ target triple = "x86_64-unknown-linux-gnu"
; If a global is present, __asan_[un]register_globals should be called from
; module ctor/dtor

; CHECK: llvm.global_ctors
; CHECK: @__asan_gen_ = private constant [8 x i8] c"<stdin>\00", align 1
; CHECK: llvm.global_ctors
; CHECK: llvm.global_dtors

; Test that we don't instrument global arrays with static initializer
Expand Down
12 changes: 12 additions & 0 deletions test/Instrumentation/AddressSanitizer/no-globals.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
; A module with no asan-instrumented globals has no asan destructor, and has an asan constructor in a comdat.
; RUN: opt -mtriple=x86_64-unknown-linux-gnu < %s -asan -asan-module -asan-with-comdat=1 -S | FileCheck %s

define void @f() {
ret void
}

; CHECK-NOT: @llvm.global_dtors
; CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 1, void ()* @asan.module_ctor, i8* bitcast (void ()* @asan.module_ctor to i8*) }]
; CHECK-NOT: @llvm.global_dtors
; CHECK: define internal void @asan.module_ctor() comdat
; CHECK-NOT: @llvm.global_dtors

0 comments on commit cd2999e

Please sign in to comment.