Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -4948,6 +4948,12 @@ See :doc:`TypeMetadata`. +'``associated``' Metadata +^^^^^^^^^^^^^^^^^^^ + +The ``associated`` metadata may be attached to a global variable +declaration. It's only effect is adding SHT_ASSOCIATED flag on the +corresponding section in the ELF targets. Module Flags Metadata ===================== Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -78,6 +78,7 @@ MD_type = 19, // "type" MD_section_prefix = 20, // "section_prefix" MD_absolute_symbol = 21, // "absolute_symbol" + MD_associated = 22, // "associated" }; /// Known operand bundle tag IDs, which always have the same value. All Index: include/llvm/Transforms/IPO/GlobalDCE.h =================================================================== --- include/llvm/Transforms/IPO/GlobalDCE.h +++ include/llvm/Transforms/IPO/GlobalDCE.h @@ -34,11 +34,15 @@ SmallPtrSet SeenConstants; std::unordered_multimap ComdatMembers; + // Maps global to a set of !associated globals that depend on it. + std::unordered_multimap AssociatedGlobals; + /// Mark the specific global value as needed, and /// recursively mark anything that it uses as also needed. void GlobalIsNeeded(GlobalValue *GV); void MarkUsedGlobalsAsNeeded(Constant *C); bool RemoveUnusedGlobalValue(GlobalValue &GV); + void AddAssociatedGlobal(GlobalVariable *AG); }; } Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -224,6 +224,10 @@ Group = C->getName(); Flags |= ELF::SHF_GROUP; } + + if (GO->getMetadata(LLVMContext::MD_associated)) + Flags |= ELF::SHF_ASSOCIATED; + return getContext().getELFSection(SectionName, getELFSectionType(SectionName, Kind), Flags, /*EntrySize=*/0, Group); Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -58,6 +58,7 @@ {MD_type, "type"}, {MD_section_prefix, "section_prefix"}, {MD_absolute_symbol, "absolute_symbol"}, + {MD_associated, "associated"}, }; for (auto &MDKind : MDKinds) { Index: lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- lib/Transforms/IPO/GlobalDCE.cpp +++ lib/Transforms/IPO/GlobalDCE.cpp @@ -95,6 +95,10 @@ if (Comdat *C = GA.getComdat()) ComdatMembers.insert(std::make_pair(C, &GA)); + for (GlobalVariable &GV : M.globals()) + if (GV.getMetadata(LLVMContext::MD_associated)) + AddAssociatedGlobal(&GV); + // Loop over the module, adding globals which are obviously necessary. for (GlobalObject &GO : M.global_objects()) { Changed |= RemoveUnusedGlobalValue(GO); @@ -190,6 +194,7 @@ AliveGlobals.clear(); SeenConstants.clear(); ComdatMembers.clear(); + AssociatedGlobals.clear(); if (Changed) return PreservedAnalyses::none(); @@ -208,6 +213,9 @@ GlobalIsNeeded(CM.second); } + for (auto &&AG : make_range(AssociatedGlobals.equal_range(G))) + GlobalIsNeeded(AG.second); + if (GlobalVariable *GV = dyn_cast(G)) { // If this is a global variable, we must make sure to add any global values // referenced by the initializer to the alive set. @@ -236,6 +244,27 @@ } } +void GlobalDCEPass::AddAssociatedGlobal(GlobalVariable *AG) { + SmallPtrSet Seen; + SmallVector WorkList; + WorkList.push_back(AG->getInitializer()); + + while (!WorkList.empty()) { + Constant *C = WorkList.pop_back_val(); + if (GlobalValue *GV = dyn_cast(C)) { + AssociatedGlobals.insert(std::make_pair(GV, AG)); + continue; + } + + for (Use &U : C->operands()) { + // If we've already processed this constant there's no need to do it again. + Constant *Op = dyn_cast(U); + if (Op && Seen.insert(Op).second) + WorkList.push_back(Op); + } + } +} + void GlobalDCEPass::MarkUsedGlobalsAsNeeded(Constant *C) { if (GlobalValue *GV = dyn_cast(C)) return GlobalIsNeeded(GV); Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/None.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" @@ -100,6 +101,10 @@ "__asan_register_image_globals"; static const char *const kAsanUnregisterImageGlobalsName = "__asan_unregister_image_globals"; +static const char *const kAsanRegisterElfGlobalsName = + "__asan_register_elf_globals"; +static const char *const kAsanUnregisterElfGlobalsName = + "__asan_unregister_elf_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"; @@ -119,8 +124,11 @@ "__asan_poison_stack_memory"; static const char *const kAsanUnpoisonStackMemoryName = "__asan_unpoison_stack_memory"; + +// ASan version script has __asan_* wildcard. Triple underscore prevents +// exporting of this _local_ symbol. static const char *const kAsanGlobalsRegisteredFlagName = - "__asan_globals_registered"; + "___asan_globals_registered"; static const char *const kAsanOptionDetectUseAfterReturn = "__asan_option_detect_stack_use_after_return"; @@ -603,6 +611,9 @@ void InstrumentGlobalsCOFF(IRBuilder<> &IRB, Module &M, ArrayRef ExtendedGlobals, ArrayRef MetadataInitializers); + void InstrumentGlobalsELF(IRBuilder<> &IRB, Module &M, + ArrayRef ExtendedGlobals, + ArrayRef MetadataInitializers); void InstrumentGlobalsMachO(IRBuilder<> &IRB, Module &M, ArrayRef ExtendedGlobals, ArrayRef MetadataInitializers); @@ -638,6 +649,8 @@ Function *AsanUnregisterGlobals; Function *AsanRegisterImageGlobals; Function *AsanUnregisterImageGlobals; + Function *AsanRegisterElfGlobals; + Function *AsanUnregisterElfGlobals; }; // Stack poisoning does not play well with exception handling. @@ -1578,6 +1591,16 @@ checkSanitizerInterfaceFunction(M.getOrInsertFunction( kAsanUnregisterImageGlobalsName, IRB.getVoidTy(), IntptrTy, nullptr)); AsanUnregisterImageGlobals->setLinkage(Function::ExternalLinkage); + + AsanRegisterElfGlobals = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanRegisterElfGlobalsName, IRB.getVoidTy(), + IntptrTy, IntptrTy, IntptrTy, nullptr)); + AsanRegisterElfGlobals->setLinkage(Function::ExternalLinkage); + + AsanUnregisterElfGlobals = checkSanitizerInterfaceFunction( + M.getOrInsertFunction(kAsanUnregisterElfGlobalsName, IRB.getVoidTy(), + IntptrTy, IntptrTy, IntptrTy, nullptr)); + AsanUnregisterElfGlobals->setLinkage(Function::ExternalLinkage); } // Put the metadata and the instrumented global in the same group. This ensures @@ -1652,6 +1675,61 @@ } } +void AddressSanitizerModule::InstrumentGlobalsELF( + IRBuilder<> &IRB, Module &M, ArrayRef ExtendedGlobals, + ArrayRef MetadataInitializers) { + assert(ExtendedGlobals.size() == MetadataInitializers.size()); + + SmallVector MetadataGlobals(ExtendedGlobals.size()); + for (size_t i = 0; i < ExtendedGlobals.size(); i++) { + GlobalVariable *Metadata = CreateMetadataGlobal( + M, MetadataInitializers[i], ExtendedGlobals[i]->getName()); + MDNode *MD = MDNode::get(M.getContext(), None); + Metadata->setMetadata(LLVMContext::MD_associated, MD); + MetadataGlobals[i] = Metadata; + } + + // Update llvm.compiler.used, adding the new metadata globals. This is + // needed so that during LTO these variables stay alive. + if (!MetadataGlobals.empty()) + appendToCompilerUsed(M, MetadataGlobals); + + // 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 ensures that there is only one global per shared library. + GlobalVariable *RegisteredFlag = new GlobalVariable( + M, IntptrTy, false, GlobalVariable::CommonLinkage, + ConstantInt::get(IntptrTy, 0), kAsanGlobalsRegisteredFlagName); + RegisteredFlag->setVisibility(GlobalVariable::HiddenVisibility); + + // Create start and stop symbols. + GlobalVariable *StartELFMetadata = new GlobalVariable( + M, IntptrTy, false, GlobalVariable::ExternalWeakLinkage, nullptr, + "__start_" + getGlobalMetadataSection()); + StartELFMetadata->setVisibility(GlobalVariable::HiddenVisibility); + GlobalVariable *StopELFMetadata = new GlobalVariable( + M, IntptrTy, false, GlobalVariable::ExternalWeakLinkage, nullptr, + "__stop_" + getGlobalMetadataSection()); + StopELFMetadata->setVisibility(GlobalVariable::HiddenVisibility); + + // Create a call to register the globals with the runtime. + IRB.CreateCall(AsanRegisterElfGlobals, + {IRB.CreatePointerCast(RegisteredFlag, IntptrTy), + IRB.CreatePointerCast(StartELFMetadata, IntptrTy), + IRB.CreatePointerCast(StopELFMetadata, IntptrTy)}); + + // We also need to unregister globals at the end, e.g., when a shared library + // gets closed. + IRBuilder<> IRB_Dtor = CreateAsanModuleDtor(M); + IRB_Dtor.CreateCall(AsanUnregisterElfGlobals, + {IRB.CreatePointerCast(RegisteredFlag, IntptrTy), + IRB.CreatePointerCast(StartELFMetadata, IntptrTy), + IRB.CreatePointerCast(StopELFMetadata, IntptrTy)}); +} + void AddressSanitizerModule::InstrumentGlobalsMachO( IRBuilder<> &IRB, Module &M, ArrayRef ExtendedGlobals, ArrayRef MetadataInitializers) { @@ -1896,6 +1974,8 @@ if (TargetTriple.isOSBinFormatCOFF()) { InstrumentGlobalsCOFF(IRB, M, NewGlobals, Initializers); + } else if (TargetTriple.isOSBinFormatELF()) { + InstrumentGlobalsELF(IRB, M, NewGlobals, Initializers); } else if (ShouldUseMachOGlobalsSection()) { InstrumentGlobalsMachO(IRB, M, NewGlobals, Initializers); } else { Index: test/Instrumentation/AddressSanitizer/global_metadata.ll =================================================================== --- test/Instrumentation/AddressSanitizer/global_metadata.ll +++ test/Instrumentation/AddressSanitizer/global_metadata.ll @@ -12,6 +12,7 @@ @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_asan_globals.cpp, i8* null }] ; Check that globals were instrumented: + ; CHECK: @global = global { i32, [60 x i8] } zeroinitializer, align 32 ; CHECK: @.str = internal unnamed_addr constant { [14 x i8], [50 x i8] } { [14 x i8] c"Hello, world!\00", [50 x i8] zeroinitializer }, align 32 @@ -19,10 +20,15 @@ ; CHECK: [[VARNAME:@__asan_gen_.[0-9]+]] = private unnamed_addr constant [7 x i8] c"global\00", align 1 ; 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: @__asan_global_global = {{.*}}i64 ptrtoint ({ i32, [60 x i8] }* @global to i64){{.*}} section "asan_globals"{{.*}}, !associated +; CHECK: @__asan_global_.str = {{.*}}i64 ptrtoint ({ [14 x i8], [50 x i8] }* @.str to i64){{.*}} section "asan_globals"{{.*}}, !associated + +; The metadata has to be inserted to llvm.compiler.used to avoid being stripped +; during LTO. +; CHECK: @llvm.compiler.used {{.*}} @__asan_global_global {{.*}} section "llvm.metadata" ; 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) +; CHECK: call void @__asan_register_elf_globals(i64 ptrtoint (i64* @___asan_globals_registered to i64), i64 ptrtoint (i64* @__start_asan_globals to i64), i64 ptrtoint (i64* @__stop_asan_globals to i64)) ; Function Attrs: nounwind sanitize_address define internal void @__cxx_global_var_init() #0 section ".text.startup" { Index: test/Instrumentation/AddressSanitizer/global_metadata_darwin.ll =================================================================== --- test/Instrumentation/AddressSanitizer/global_metadata_darwin.ll +++ test/Instrumentation/AddressSanitizer/global_metadata_darwin.ll @@ -26,16 +26,16 @@ ; CHECK: @llvm.compiler.used {{.*}} @__asan_binder_global {{.*}} section "llvm.metadata" ; Test that there is the flag global variable: -; CHECK: @__asan_globals_registered = common hidden global i64 0 +; CHECK: @___asan_globals_registered = common hidden global i64 0 ; Test that __asan_register_image_globals is invoked from the constructor: ; CHECK-LABEL: define internal void @asan.module_ctor ; CHECK-NOT: ret -; CHECK: call void @__asan_register_image_globals(i64 ptrtoint (i64* @__asan_globals_registered to i64)) +; CHECK: call void @__asan_register_image_globals(i64 ptrtoint (i64* @___asan_globals_registered to i64)) ; CHECK: ret ; Test that __asan_unregister_image_globals is invoked from the destructor: ; CHECK-LABEL: define internal void @asan.module_dtor ; CHECK-NOT: ret -; CHECK: call void @__asan_unregister_image_globals(i64 ptrtoint (i64* @__asan_globals_registered to i64)) +; CHECK: call void @__asan_unregister_image_globals(i64 ptrtoint (i64* @___asan_globals_registered to i64)) ; CHECK: ret Index: test/Instrumentation/AddressSanitizer/instrument_global.ll =================================================================== --- test/Instrumentation/AddressSanitizer/instrument_global.ll +++ test/Instrumentation/AddressSanitizer/instrument_global.ll @@ -73,10 +73,10 @@ ; CHECK-LABEL: define internal void @asan.module_ctor ; CHECK-NOT: ret -; CHECK: call void @__asan_register_globals +; CHECK: call void @__asan_register_elf_globals ; CHECK: ret ; CHECK-LABEL: define internal void @asan.module_dtor ; CHECK-NOT: ret -; CHECK: call void @__asan_unregister_globals +; CHECK: call void @__asan_unregister_elf_globals ; CHECK: ret Index: test/ThinLTO/X86/lazyload_metadata.ll =================================================================== --- test/ThinLTO/X86/lazyload_metadata.ll +++ test/ThinLTO/X86/lazyload_metadata.ll @@ -10,13 +10,13 @@ ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=LAZY -; LAZY: 49 bitcode-reader - Number of Metadata records loaded +; LAZY: 51 bitcode-reader - Number of Metadata records loaded ; LAZY: 2 bitcode-reader - Number of MDStrings loaded ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -disable-ondemand-mds-loading -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=NOTLAZY -; NOTLAZY: 58 bitcode-reader - Number of Metadata records loaded +; NOTLAZY: 60 bitcode-reader - Number of Metadata records loaded ; NOTLAZY: 7 bitcode-reader - Number of MDStrings loaded @@ -55,4 +55,4 @@ !6 = !{!9} !7 = !{!"7"} !8 = !{!"8"} -!9 = !{!6} \ No newline at end of file +!9 = !{!6} Index: test/Transforms/GlobalDCE/associated.ll =================================================================== --- /dev/null +++ test/Transforms/GlobalDCE/associated.ll @@ -0,0 +1,26 @@ +; RUN: opt < %s -globaldce -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.A = type { i32, i32* } + +; CHECK: @x = +@x = global i32 0, align 4 + +; CHECK-NOT: @y0 = +@y0 = internal global i32* @x, align 8 +; CHECK: @y1 = +@y1 = internal global i32* @x, align 8, !associated !0 + +; CHECK-NOT: @z0 = +@z0 = internal global %struct.A { i32 5, i32* @x }, align 8 +; CHECK: @z1 = +@z1 = internal global %struct.A { i32 5, i32* @x }, align 8, !associated !0 + +; CHECK-NOT: @zz0 = +@zz0 = internal global %struct.A* @z1, align 8 +; CHECK: @zz1 = +@zz1 = internal global %struct.A* @z1, align 8, !associated !0 + +!0 = !{}