Skip to content

Commit 716f0ff

Browse files
committed
[asan] Put ctor/dtor in comdat.
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 llvm-svn: 301586
1 parent cdc5493 commit 716f0ff

File tree

3 files changed

+61
-10
lines changed

3 files changed

+61
-10
lines changed

llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,13 @@ static cl::opt<bool>
270270
"code stripping of globals"),
271271
cl::Hidden, cl::init(true));
272272

273+
// This is on by default even though there is a bug in gold:
274+
// https://sourceware.org/bugzilla/show_bug.cgi?id=19002
275+
static cl::opt<bool>
276+
ClWithComdat("asan-with-comdat",
277+
cl::desc("Place ASan constructors in comdat sections"),
278+
cl::Hidden, cl::init(true));
279+
273280
// Debug flags.
274281
static cl::opt<int> ClDebug("asan-debug", cl::desc("debug"), cl::Hidden,
275282
cl::init(0));
@@ -607,7 +614,7 @@ class AddressSanitizerModule : public ModulePass {
607614
private:
608615
void initializeCallbacks(Module &M);
609616

610-
bool InstrumentGlobals(IRBuilder<> &IRB, Module &M);
617+
bool InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat);
611618
void InstrumentGlobalsCOFF(IRBuilder<> &IRB, Module &M,
612619
ArrayRef<GlobalVariable *> ExtendedGlobals,
613620
ArrayRef<Constant *> MetadataInitializers);
@@ -647,6 +654,9 @@ class AddressSanitizerModule : public ModulePass {
647654
Function *AsanUnregisterGlobals;
648655
Function *AsanRegisterImageGlobals;
649656
Function *AsanUnregisterImageGlobals;
657+
658+
Function *AsanCtorFunction = nullptr;
659+
Function *AsanDtorFunction = nullptr;
650660
};
651661

652662
// Stack poisoning does not play well with exception handling.
@@ -1431,8 +1441,13 @@ void AddressSanitizerModule::poisonOneInitializer(Function &GlobalInit,
14311441
void AddressSanitizerModule::createInitializerPoisonCalls(
14321442
Module &M, GlobalValue *ModuleName) {
14331443
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
1444+
if (!GV)
1445+
return;
1446+
1447+
ConstantArray *CA = dyn_cast<ConstantArray>(GV->getInitializer());
1448+
if (!CA)
1449+
return;
14341450

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

16381653
IRBuilder<> AddressSanitizerModule::CreateAsanModuleDtor(Module &M) {
1639-
Function *AsanDtorFunction =
1654+
AsanDtorFunction =
16401655
Function::Create(FunctionType::get(Type::getVoidTy(*C), false),
16411656
GlobalValue::InternalLinkage, kAsanModuleDtorName, &M);
16421657
BasicBlock *AsanDtorBB = BasicBlock::Create(*C, "", AsanDtorFunction);
1643-
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority);
16441658

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

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

17681785
size_t n = GlobalsToChange.size();
1769-
if (n == 0) return false;
1786+
if (n == 0) {
1787+
*CtorComdat = true;
1788+
return false;
1789+
}
17701790

17711791
auto &DL = M.getDataLayout();
17721792

@@ -1938,17 +1958,36 @@ bool AddressSanitizerModule::runOnModule(Module &M) {
19381958
if (CompileKernel)
19391959
return false;
19401960

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

1967+
bool CtorComdat = true;
19471968
bool Changed = false;
19481969
// TODO(glider): temporarily disabled globals instrumentation for KASan.
19491970
if (ClGlobals) {
19501971
IRBuilder<> IRB(AsanCtorFunction->getEntryBlock().getTerminator());
1951-
Changed |= InstrumentGlobals(IRB, M);
1972+
Changed |= InstrumentGlobals(IRB, M, &CtorComdat);
1973+
}
1974+
1975+
// Put the constructor and destructor in comdat if both
1976+
// (1) global instrumentation is not TU-specific
1977+
// (2) target is ELF.
1978+
if (ClWithComdat && TargetTriple.isOSBinFormatELF() && CtorComdat) {
1979+
AsanCtorFunction->setComdat(M.getOrInsertComdat(kAsanModuleCtorName));
1980+
appendToGlobalCtors(M, AsanCtorFunction, kAsanCtorAndDtorPriority,
1981+
AsanCtorFunction);
1982+
if (AsanDtorFunction) {
1983+
AsanDtorFunction->setComdat(M.getOrInsertComdat(kAsanModuleDtorName));
1984+
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority,
1985+
AsanDtorFunction);
1986+
}
1987+
} else {
1988+
appendToGlobalCtors(M, AsanCtorFunction, kAsanCtorAndDtorPriority);
1989+
if (AsanDtorFunction)
1990+
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority);
19521991
}
19531992

19541993
return Changed;

llvm/test/Instrumentation/AddressSanitizer/instrument_global.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ target triple = "x86_64-unknown-linux-gnu"
66
; If a global is present, __asan_[un]register_globals should be called from
77
; module ctor/dtor
88

9-
; CHECK: llvm.global_ctors
109
; CHECK: @__asan_gen_ = private constant [8 x i8] c"<stdin>\00", align 1
10+
; CHECK: llvm.global_ctors
1111
; CHECK: llvm.global_dtors
1212

1313
; Test that we don't instrument global arrays with static initializer
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
; A module with no asan-instrumented globals has no asan destructor, and has an asan constructor in a comdat.
2+
; RUN: opt -mtriple=x86_64-unknown-linux-gnu < %s -asan -asan-module -asan-with-comdat=1 -S | FileCheck %s
3+
4+
define void @f() {
5+
ret void
6+
}
7+
8+
; CHECK-NOT: @llvm.global_dtors
9+
; 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*) }]
10+
; CHECK-NOT: @llvm.global_dtors
11+
; CHECK: define internal void @asan.module_ctor() comdat
12+
; CHECK-NOT: @llvm.global_dtors

0 commit comments

Comments
 (0)