From b451f3588da0bc397b3174342633192aa64ded25 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 21 Jul 2021 14:03:26 -0700 Subject: [PATCH] [sanitizer] Place module_ctor/module_dtor in llvm.used This removes an abuse of ELF linker behaviors while keeping Mach-O/COFF linker behaviors unchanged. ELF: when module_ctor is in a comdat, this patch removes reliance on a linker abuse (an SHT_INIT_ARRAY in a section group retains the whole group) by using SHF_GNU_RETAIN. No linker behavior difference when module_ctor is not in a comdat. Mach-O: module_ctor gets `N_NO_DEAD_STRIP`. No linker behavior difference because module_ctor is already referenced by a `S_MOD_INIT_FUNC_POINTERS` section (GC root). PE/COFF: no-op. SanitizerCoverage already appends module_ctor to `llvm.used`. Other sanitizers: llvm.used for local linkage is not implemented in `TargetLoweringObjectFileCOFF::emitLinkerDirectives` (once implemented or switched to a non-local linkage, COFF can use module_ctor in comdat (i.e. generalize ELF-specific rL301586)). There is no object file size difference. Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D106246 --- lib/Transforms/Instrumentation/AddressSanitizer.cpp | 2 ++ lib/Transforms/Instrumentation/SanitizerCoverage.cpp | 1 - lib/Transforms/Utils/ModuleUtils.cpp | 2 ++ test/Instrumentation/AddressSanitizer/basic.ll | 3 ++- test/Instrumentation/AddressSanitizer/instrument_global.ll | 5 +++-- test/Instrumentation/HWAddressSanitizer/basic.ll | 1 + test/Instrumentation/HeapProfiler/basic.ll | 4 +++- test/Instrumentation/MemorySanitizer/msan_basic.ll | 1 + test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll | 2 +- test/Instrumentation/SanitizerCoverage/trace-pc-guard.ll | 4 ++-- test/Instrumentation/ThreadSanitizer/tsan_basic.ll | 1 + 11 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/lib/Transforms/Instrumentation/AddressSanitizer.cpp index c2ddba469de..0f797ec13ca 100644 --- a/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -2138,6 +2138,8 @@ Instruction *ModuleAddressSanitizer::CreateAsanModuleDtor(Module &M) { GlobalValue::InternalLinkage, 0, kAsanModuleDtorName, &M); AsanDtorFunction->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind); + // Ensure Dtor cannot be discarded, even if in a comdat. + appendToUsed(M, {AsanDtorFunction}); BasicBlock *AsanDtorBB = BasicBlock::Create(*C, "", AsanDtorFunction); return ReturnInst::Create(*C, AsanDtorBB); diff --git a/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/lib/Transforms/Instrumentation/SanitizerCoverage.cpp index d8720c37305..7607464cc0b 100644 --- a/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ b/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -384,7 +384,6 @@ Function *ModuleSanitizerCoverage::CreateInitCallsForSections( // to include the sancov constructor. This way the linker can deduplicate // the constructors but always leave one copy. CtorFunc->setLinkage(GlobalValue::WeakODRLinkage); - appendToUsed(M, CtorFunc); } return CtorFunc; } diff --git a/lib/Transforms/Utils/ModuleUtils.cpp b/lib/Transforms/Utils/ModuleUtils.cpp index 8c6435b5238..7c0316223cd 100644 --- a/lib/Transforms/Utils/ModuleUtils.cpp +++ b/lib/Transforms/Utils/ModuleUtils.cpp @@ -128,6 +128,8 @@ Function *llvm::createSanitizerCtor(Module &M, StringRef CtorName) { Ctor->addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind); BasicBlock *CtorBB = BasicBlock::Create(M.getContext(), "", Ctor); ReturnInst::Create(M.getContext(), CtorBB); + // Ensure Ctor cannot be discarded, even if in a comdat. + appendToUsed(M, {Ctor}); return Ctor; } diff --git a/test/Instrumentation/AddressSanitizer/basic.ll b/test/Instrumentation/AddressSanitizer/basic.ll index 41ab493a0d1..171eed3e2d0 100644 --- a/test/Instrumentation/AddressSanitizer/basic.ll +++ b/test/Instrumentation/AddressSanitizer/basic.ll @@ -8,7 +8,8 @@ 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" target triple = "x86_64-unknown-linux-gnu" -; CHECK: @llvm.global_ctors = {{.*}}@asan.module_ctor +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @asan.module_ctor to i8*)] +; CHECK: @llvm.global_ctors = {{.*}}{ i32 1, void ()* @asan.module_ctor, i8* bitcast (void ()* @asan.module_ctor to i8*) } define i32 @test_load(i32* %a) sanitize_address { ; CHECK-LABEL: @test_load diff --git a/test/Instrumentation/AddressSanitizer/instrument_global.ll b/test/Instrumentation/AddressSanitizer/instrument_global.ll index 18c40a503b5..03261c54222 100644 --- a/test/Instrumentation/AddressSanitizer/instrument_global.ll +++ b/test/Instrumentation/AddressSanitizer/instrument_global.ll @@ -10,8 +10,9 @@ target triple = "x86_64-unknown-linux-gnu" ; module ctor/dtor ; CHECK: @___asan_gen_ = private constant [8 x i8] c"\00", align 1 -; CHECK: llvm.global_ctors -; CHECK: llvm.global_dtors +; CHECK: @llvm.used = appending global [2 x i8*] [i8* bitcast (void ()* @asan.module_ctor to i8*), i8* bitcast (void ()* @asan.module_dtor to i8*)], section "llvm.metadata" +; 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: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 1, void ()* @asan.module_dtor, i8* bitcast (void ()* @asan.module_dtor to i8*) }] ; Test that we don't instrument global arrays with static initializer ; indexed with constants in-bounds. But instrument all other cases. diff --git a/test/Instrumentation/HWAddressSanitizer/basic.ll b/test/Instrumentation/HWAddressSanitizer/basic.ll index 7307ca200e2..04f0a381c3a 100644 --- a/test/Instrumentation/HWAddressSanitizer/basic.ll +++ b/test/Instrumentation/HWAddressSanitizer/basic.ll @@ -11,6 +11,7 @@ ; RUN: opt < %s -passes=hwasan -hwasan-recover=0 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,ABORT ; RUN: opt < %s -passes=hwasan -hwasan-recover=1 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,RECOVER,RECOVER-ZERO-BASED-SHADOW +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @hwasan.module_ctor to i8*)] ; CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @hwasan.module_ctor, i8* bitcast (void ()* @hwasan.module_ctor to i8*) }] target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" diff --git a/test/Instrumentation/HeapProfiler/basic.ll b/test/Instrumentation/HeapProfiler/basic.ll index cf6320414bd..45124d4eb3a 100644 --- a/test/Instrumentation/HeapProfiler/basic.ll +++ b/test/Instrumentation/HeapProfiler/basic.ll @@ -9,7 +9,9 @@ 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" target triple = "x86_64-unknown-linux-gnu" -; CHECK: @llvm.global_ctors = {{.*}}@memprof.module_ctor + +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @memprof.module_ctor to i8*)] +; CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 1, void ()* @memprof.module_ctor, i8* null }] define i32 @test_load(i32* %a) { entry: diff --git a/test/Instrumentation/MemorySanitizer/msan_basic.ll b/test/Instrumentation/MemorySanitizer/msan_basic.ll index e9afb0febe3..82135644c6b 100644 --- a/test/Instrumentation/MemorySanitizer/msan_basic.ll +++ b/test/Instrumentation/MemorySanitizer/msan_basic.ll @@ -10,6 +10,7 @@ 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" +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @msan.module_ctor to i8*)] ; CHECK: @llvm.global_ctors {{.*}} { i32 0, void ()* @msan.module_ctor, i8* null } ; Check the presence and the linkage type of __msan_track_origins and diff --git a/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll b/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll index 42951f1f09c..3acfe43ac74 100644 --- a/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll +++ b/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll @@ -6,7 +6,7 @@ ; CHECK: @__sancov_gen_ = private global [1 x i1] zeroinitializer, section "__sancov_bools", comdat($foo), align 1{{$}} ; CHECK: @__start___sancov_bools = extern_weak hidden global i1 ; CHECK-NEXT: @__stop___sancov_bools = extern_weak hidden global i1 -; CHECK-NOT: @llvm.used = +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @sancov.module_ctor_bool_flag to i8*)], section "llvm.metadata" ; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* bitcast ([1 x i1]* @__sancov_gen_ to i8*)], section "llvm.metadata" 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" diff --git a/test/Instrumentation/SanitizerCoverage/trace-pc-guard.ll b/test/Instrumentation/SanitizerCoverage/trace-pc-guard.ll index 5df7f31c91f..0b4a0ef821b 100644 --- a/test/Instrumentation/SanitizerCoverage/trace-pc-guard.ll +++ b/test/Instrumentation/SanitizerCoverage/trace-pc-guard.ll @@ -18,9 +18,9 @@ ; WIN: @__sancov_gen_ = private global [3 x i32] zeroinitializer, section ".SCOV$GM", comdat($foo), align 4{{$}} ; WIN-NEXT: @__sancov_gen_.1 = private global [1 x i32] zeroinitializer, section ".SCOV$GM", comdat($CallViaVptr), align 4{{$}} -; ELF-NOT: @llvm.used = +; ELF: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @sancov.module_ctor_trace_pc_guard to i8*)] ; ELF: @llvm.compiler.used = appending global [2 x i8*] [i8* bitcast ([3 x i32]* @__sancov_gen_ to i8*), i8* bitcast ([1 x i32]* @__sancov_gen_.1 to i8*)], section "llvm.metadata" -; MACHO: @llvm.used = appending global [2 x i8*] [i8* bitcast ([3 x i32]* @__sancov_gen_ to i8*), i8* bitcast ([1 x i32]* @__sancov_gen_.1 to i8*)], section "llvm.metadata" +; MACHO: @llvm.used = appending global [3 x i8*] [i8* bitcast (void ()* @sancov.module_ctor_trace_pc_guard to i8*), i8* bitcast ([3 x i32]* @__sancov_gen_ to i8*), i8* bitcast ([1 x i32]* @__sancov_gen_.1 to i8*)] ; MACHO-NOT: @llvm.compiler.used = ; WIN: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @sancov.module_ctor_trace_pc_guard to i8*)], section "llvm.metadata" ; WIN-NEXT: @llvm.compiler.used = appending global [2 x i8*] [i8* bitcast ([3 x i32]* @__sancov_gen_ to i8*), i8* bitcast ([1 x i32]* @__sancov_gen_.1 to i8*)], section "llvm.metadata" diff --git a/test/Instrumentation/ThreadSanitizer/tsan_basic.ll b/test/Instrumentation/ThreadSanitizer/tsan_basic.ll index 20406c7f147..db559f6987e 100644 --- a/test/Instrumentation/ThreadSanitizer/tsan_basic.ll +++ b/test/Instrumentation/ThreadSanitizer/tsan_basic.ll @@ -10,6 +10,7 @@ entry: ret i32 %tmp1 } +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @tsan.module_ctor to i8*)] ; CHECK: @llvm.global_ctors = {{.*}}@tsan.module_ctor ; CHECK: define i32 @read_4_bytes(i32* %a)