Index: cmake/modules/HandleLLVMOptions.cmake =================================================================== --- cmake/modules/HandleLLVMOptions.cmake +++ cmake/modules/HandleLLVMOptions.cmake @@ -700,6 +700,9 @@ if (LLVM_USE_SANITIZER STREQUAL "Address") append_common_sanitizer_flags() append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + elseif (LLVM_USE_SANITIZER STREQUAL "HWAddress") + append_common_sanitizer_flags() + append("-fsanitize=hwaddress" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) elseif (LLVM_USE_SANITIZER MATCHES "Memory(WithOrigins)?") append_common_sanitizer_flags() append("-fsanitize=memory" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -164,6 +164,7 @@ void initializeGuardWideningLegacyPassPass(PassRegistry&); void initializeHotColdSplittingLegacyPassPass(PassRegistry&); void initializeHWAddressSanitizerPass(PassRegistry&); +void initializeHWAddressSanitizerModulePass(PassRegistry &); void initializeIPCPPass(PassRegistry&); void initializeIPSCCPLegacyPassPass(PassRegistry&); void initializeIRCELegacyPassPass(PassRegistry&); Index: include/llvm/Transforms/Instrumentation.h =================================================================== --- include/llvm/Transforms/Instrumentation.h +++ include/llvm/Transforms/Instrumentation.h @@ -154,6 +154,7 @@ FunctionPass *createHWAddressSanitizerPass(bool CompileKernel = false, bool Recover = false); +ModulePass *createHWAddressSanitizerModulePass(bool CompileKernel = false); // Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation ModulePass *createDataFlowSanitizerPass( Index: lib/Target/TargetMachine.cpp =================================================================== --- lib/Target/TargetMachine.cpp +++ lib/Target/TargetMachine.cpp @@ -103,6 +103,22 @@ llvm_unreachable("invalid TLS model"); } +static bool isHWAsanInstrumentedGlobal(const GlobalValue *GV) { + // With -hwasan-instrument-globals interposable global variables + // should always be referenced via GOT, otherwise we may overflow + // relocations. + if (const GlobalVariable *GVar = dyn_cast(GV)) + return GVar->hasAttribute("hwasan-interposable-global"); + + // Aliases can't have attributes so we check aliasee name to + // figure out if aliasee points to instrumented global. If + // that's true we should reference alias via GOT. + if (const GlobalAlias *GA = dyn_cast(GV)) + if (const GlobalAlias *Aliasee = dyn_cast(GA->getAliasee())) + return Aliasee->getName().endswith(".hwasan"); + return false; +} + bool TargetMachine::shouldAssumeDSOLocal(const Module &M, const GlobalValue *GV) const { // If the IR producer requested that this GV be treated as dso local, obey. @@ -175,7 +191,7 @@ if (IsExecutable) { // If the symbol is defined, it cannot be preempted. if (GV && !GV->isDeclarationForLinker()) - return true; + return !isHWAsanInstrumentedGlobal(GV); // A symbol marked nonlazybind should not be accessed with a plt. If the // symbol turns out to be external, the linker will convert a direct Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -2364,7 +2364,6 @@ std::string ELFUniqueModuleId = (UseGlobalsGC && TargetTriple.isOSBinFormatELF()) ? getUniqueModuleId(&M) : ""; - if (!ELFUniqueModuleId.empty()) { InstrumentGlobalsELF(IRB, M, NewGlobals, Initializers, ELFUniqueModuleId); *CtorComdat = true; Index: lib/Transforms/Instrumentation/HWAddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -160,7 +160,21 @@ cl::desc("inline all checks"), cl::Hidden, cl::init(false)); +static cl::opt + ClInstrumentGlobals("hwasan-instrument-globals", + cl::desc("instrument global variables"), cl::Hidden, + cl::init(false)); + namespace { +// Get the section name for frame descriptions. Currently ELF-only. +inline const char *getFrameSection() { return "__hwasan_frames"; } +inline const char *getFrameSectionBeg() { return "__start___hwasan_frames"; } +inline const char *getFrameSectionEnd() { return "__stop___hwasan_frames"; } + +// Get the section name for globals descriptions. Currently ELF-only. +inline const char *getGlobalsSection() { return "__hwasan_globals"; } +inline const char *getGlobalsSectionBeg() { return "__start___hwasan_globals"; } +inline const char *getGlobalsSectionEnd() { return "__stop___hwasan_globals"; } /// An instrumentation pass implementing detection of addressability bugs /// using tagged pointers. @@ -225,17 +239,6 @@ // the section beg/end to __hwasan_init_frames() at module init time. std::string createFrameString(ArrayRef Allocas); void createFrameGlobal(Function &F, const std::string &FrameString); - // Get the section name for frame descriptions. Currently ELF-only. - const char *getFrameSection() { return "__hwasan_frames"; } - const char *getFrameSectionBeg() { return "__start___hwasan_frames"; } - const char *getFrameSectionEnd() { return "__stop___hwasan_frames"; } - GlobalVariable *createFrameSectionBound(Module &M, Type *Ty, - const char *Name) { - auto GV = new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage, - nullptr, Name); - GV->setVisibility(GlobalValue::HiddenVisibility); - return GV; - } /// This struct defines the shadow mapping using the rule: /// shadow = (mem >> Scale) + Offset. @@ -264,8 +267,6 @@ bool CompileKernel; bool Recover; - Function *HwasanCtorFunction; - FunctionCallee HwasanMemoryAccessCallback[2][kNumberOfAccessSizes]; FunctionCallee HwasanMemoryAccessCallbackSized[2]; @@ -279,9 +280,37 @@ GlobalValue *ThreadPtrGlobal = nullptr; }; +class HWAddressSanitizerModule : public ModulePass { + bool CompileKernel; + Function *HwasanCtorFunction; + Type *Int8Ty, *Int8PtrTy, *Int64Ty, *Int32Ty, *VoidTy; + bool globalWasGeneratedByCompiler(GlobalVariable *G); + bool shouldInstrumentGlobal(GlobalVariable *G); + GlobalAlias *createTaggedAlias(GlobalVariable *New, StringRef Name); + GlobalVariable *createInstrumentedGlobal(GlobalVariable *Orig); + Constant *replaceGlobal(GlobalVariable *New, GlobalVariable *Orig); + GlobalVariable *createSectionBound(Module &M, Type *Ty, const char *Name, + bool Weak); + +public: + // Pass identification, replacement for typeid + static char ID; + + explicit HWAddressSanitizerModule(bool CompileKernel = false) + : ModulePass(ID) { + this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0 + ? ClEnableKhwasan + : CompileKernel; + } + + bool runOnModule(Module &M) override; + bool doInitialization(Module &M) override; + StringRef getPassName() const override { return "HWAddressSanitizerModule"; } +}; } // end anonymous namespace char HWAddressSanitizer::ID = 0; +char HWAddressSanitizerModule::ID = 0; INITIALIZE_PASS_BEGIN( HWAddressSanitizer, "hwasan", @@ -291,6 +320,11 @@ HWAddressSanitizer, "hwasan", "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) +INITIALIZE_PASS( + HWAddressSanitizerModule, "hwasan-module", + "HWAddressSanitizer: detect memory bugs using tagged addressing." + "ModulePass", + false, false) FunctionPass *llvm::createHWAddressSanitizerPass(bool CompileKernel, bool Recover) { @@ -298,6 +332,10 @@ return new HWAddressSanitizer(CompileKernel, Recover); } +ModulePass *llvm::createHWAddressSanitizerModulePass(bool CompileKernel) { + return new HWAddressSanitizerModule(CompileKernel); +} + /// Module-level initialization. /// /// inserts a call to __hwasan_init to the module's constructor list. @@ -317,45 +355,11 @@ Int8Ty = IRB.getInt8Ty(); Int32Ty = IRB.getInt32Ty(); - HwasanCtorFunction = nullptr; - if (!CompileKernel) { - std::tie(HwasanCtorFunction, std::ignore) = - createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName, - kHwasanInitName, - /*InitArgTypes=*/{}, - /*InitArgs=*/{}); - Comdat *CtorComdat = M.getOrInsertComdat(kHwasanModuleCtorName); - HwasanCtorFunction->setComdat(CtorComdat); - appendToGlobalCtors(M, HwasanCtorFunction, 0, HwasanCtorFunction); - - // Create a zero-length global in __hwasan_frame so that the linker will - // always create start and stop symbols. - // - // N.B. If we ever start creating associated metadata in this pass this - // global will need to be associated with the ctor. - Type *Int8Arr0Ty = ArrayType::get(Int8Ty, 0); - auto GV = - new GlobalVariable(M, Int8Arr0Ty, /*isConstantGlobal*/ true, - GlobalVariable::PrivateLinkage, - Constant::getNullValue(Int8Arr0Ty), "__hwasan"); - GV->setSection(getFrameSection()); - GV->setComdat(CtorComdat); - appendToCompilerUsed(M, GV); - - IRBuilder<> IRBCtor(HwasanCtorFunction->getEntryBlock().getTerminator()); - IRBCtor.CreateCall( - declareSanitizerInitFunction(M, "__hwasan_init_frames", - {Int8PtrTy, Int8PtrTy}), - {createFrameSectionBound(M, Int8Ty, getFrameSectionBeg()), - createFrameSectionBound(M, Int8Ty, getFrameSectionEnd())}); - } - if (!TargetTriple.isAndroid()) appendToCompilerUsed( M, ThreadPtrGlobal = new GlobalVariable( M, IntptrTy, false, GlobalVariable::ExternalLinkage, nullptr, "__hwasan_tls", nullptr, GlobalVariable::InitialExecTLSModel)); - return true; } @@ -971,7 +975,7 @@ } bool HWAddressSanitizer::runOnFunction(Function &F) { - if (&F == HwasanCtorFunction) + if (F.getName().startswith("__hwasan")) return false; if (!F.hasFnAttribute(Attribute::SanitizeHWAddress)) @@ -1081,3 +1085,224 @@ Offset = kDynamicShadowSentinel; } } + +bool HWAddressSanitizerModule::globalWasGeneratedByCompiler(GlobalVariable *G) { + StringRef Name = G->getName(); + return Name.startswith("__hwasan") || Name.startswith("llvm.") || + Name == getFrameSectionBeg() || Name == getFrameSectionEnd(); +} + +bool HWAddressSanitizerModule::shouldInstrumentGlobal(GlobalVariable *G) { + Type *Ty = G->getValueType(); + LLVM_DEBUG(dbgs() << "GLOBAL: " << *G << "\n"); + + if (!Ty->isSized() || G->isDeclaration()) + return false; + if (globalWasGeneratedByCompiler(G)) + return false; // Our own globals. + // We don't support thread local variables for now. + if (G->isThreadLocal()) + return false; + // Values with interposable linkage can be replaced by the linker. + // With appending linkage we don't know size until the final link. + // Hidden globals are not instrumented to allow non-instrumented code + // with external hidden declaration to link against instrumented code + // where that hidden variable is defined. + return !(G->hasAppendingLinkage() || + G->hasAvailableExternallyLinkage() || G->hasHiddenVisibility()); +} + +GlobalVariable *HWAddressSanitizerModule::createSectionBound(Module &M, + Type *Ty, + const char *Name, + bool Weak) { + auto Linkage = Weak ? GlobalVariable::ExternalWeakLinkage + : GlobalVariable::ExternalLinkage; + auto GV = new GlobalVariable(M, Ty, false, Linkage, nullptr, Name); + GV->setVisibility(GlobalValue::HiddenVisibility); + return GV; +} + +bool HWAddressSanitizerModule::doInitialization(Module &M) { + if (CompileKernel) + return false; + + std::tie(HwasanCtorFunction, std::ignore) = + createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName, + kHwasanInitName, + /*InitArgTypes=*/{}, + /*InitArgs=*/{}); + Comdat *CtorComdat = M.getOrInsertComdat(kHwasanModuleCtorName); + HwasanCtorFunction->setComdat(CtorComdat); + appendToGlobalCtors(M, HwasanCtorFunction, 0, HwasanCtorFunction); + + IRBuilder<> IRBCtor(HwasanCtorFunction->getEntryBlock().getTerminator()); + Int8Ty = IRBCtor.getInt8Ty(); + Int8PtrTy = IRBCtor.getInt8PtrTy(); + Int64Ty = IRBCtor.getInt64Ty(); + Int32Ty = IRBCtor.getInt32Ty(); + VoidTy = IRBCtor.getVoidTy(); + // Create a zero-length global in __hwasan_frame so that the linker will + // always create start and stop symbols. + // + // N.B. If we ever start creating associated metadata in this pass this + // global will need to be associated with the ctor. + Type *Int8Arr0Ty = ArrayType::get(Int8Ty, 0); + auto GV = new GlobalVariable(M, Int8Arr0Ty, /*isConstantGlobal*/ true, + GlobalVariable::PrivateLinkage, + Constant::getNullValue(Int8Arr0Ty), "__hwasan"); + GV->setSection(getFrameSection()); + GV->setComdat(CtorComdat); + appendToCompilerUsed(M, GV); + + IRBCtor.CreateCall( + declareSanitizerInitFunction(M, "__hwasan_init_frames", + {Int8PtrTy, Int8PtrTy}), + {createSectionBound(M, Int8Ty, getFrameSectionBeg(), false), + createSectionBound(M, Int8Ty, getFrameSectionEnd(), false)}); + IRBCtor.CreateCall( + declareSanitizerInitFunction(M, "__hwasan_init_globals", + {Int8PtrTy, Int8PtrTy}), + {createSectionBound(M, Int8Ty, getGlobalsSectionBeg(), true), + createSectionBound(M, Int8Ty, getGlobalsSectionEnd(), true)}); + + return true; +} + +static uint64_t getTagForName(StringRef Name) { + // Use Knuth multiplicative hash to get tag value + return (hash_value(Name) * 2654435761) % 255 + 1; +} + +GlobalAlias *HWAddressSanitizerModule::createTaggedAlias(GlobalVariable *New, + StringRef Name) { + uint64_t Tag = getTagForName(Name) << 56; + Constant *Aliasee = ConstantExpr::getIntToPtr( + ConstantExpr::getAdd(ConstantExpr::getPtrToInt(New, Int64Ty), + ConstantInt::get(Int64Ty, Tag)), + New->getType()); + GlobalAlias *Alias = GlobalAlias::create( + New->getValueType(), New->getAddressSpace(), GlobalValue::PrivateLinkage, + Name + ".hwasan", Aliasee, New->getParent()); + GlobalAlias *Result = + GlobalAlias::create(Alias->getValueType(), Alias->getAddressSpace(), + New->getLinkage(), "", Alias, Alias->getParent()); + return Result; +} + +GlobalVariable * +HWAddressSanitizerModule::createInstrumentedGlobal(GlobalVariable *G) { + auto &DL = G->getParent()->getDataLayout(); + Type *VTy = G->getValueType(); + uint64_t SizeInBytes = DL.getTypeAllocSize(VTy); + + // Align the size to shadow granularity. If already aligned then add + // extra 1 << kDefaultShadowScale bytes for padding + uint64_t Mask = (1 << kDefaultShadowScale) - 1; + uint64_t NewSize = (SizeInBytes & ~Mask) + (1 << kDefaultShadowScale); + + // Create padding + Type *PaddingTy = ArrayType::get(Int8Ty, NewSize - SizeInBytes); + StructType *NewTy = StructType::get(VTy, PaddingTy); + Constant *NewInitializer = ConstantStruct::get( + NewTy, G->getInitializer(), Constant::getNullValue(PaddingTy)); + + // Create new global variable + Module &M = *G->getParent(); + auto *NewGlobal = + new GlobalVariable(M, NewTy, G->isConstant(), G->getLinkage(), + NewInitializer, "", G, G->getThreadLocalMode()); + NewGlobal->copyAttributesFrom(G); + NewGlobal->setDSOLocal(G->isDSOLocal()); + NewGlobal->setAlignment( + std::max(G->getAlignment(), 1u << kDefaultShadowScale)); + // Don't fold globals with memory tagging + NewGlobal->setUnnamedAddr(GlobalValue::UnnamedAddr::None); + return NewGlobal; +} + +Constant *HWAddressSanitizerModule::replaceGlobal(GlobalVariable *New, + GlobalVariable *Orig) { + auto ReplaceGV = [](Constant *Expr, GlobalValue *New, GlobalVariable *Old) { + Old->replaceAllUsesWith(Expr); + New->takeName(Old); + Old->eraseFromParent(); + }; + + size_t Tag = getTagForName(Orig->getName()) << 56; + if (New->hasLocalLinkage()) { + Value *Indices[] = {ConstantInt::get(Int64Ty, Tag)}; + auto *TaggedPointer = ConstantExpr::getGetElementPtr( + nullptr, ConstantExpr::getPointerCast(New, Int8PtrTy), Indices); + auto *Expr = ConstantExpr::getPointerCast(TaggedPointer, Orig->getType()); + ReplaceGV(Expr, New, Orig); + return TaggedPointer; + } + + assert(New->getLinkage() == Orig->getLinkage()); + GlobalAlias *TaggedAlias = createTaggedAlias(New, Orig->getName()); + New->setName(Orig->getName() + ".hwasan.data"); + New->setLinkage(GlobalValue::PrivateLinkage); + TaggedAlias->setVisibility(Orig->getVisibility()); + + auto *Zero = ConstantInt::get(Int32Ty, 0); + Value *Indices[] = {Zero, Zero}; + ReplaceGV(ConstantExpr::getGetElementPtr(TaggedAlias->getValueType(), + TaggedAlias, Indices, true), + TaggedAlias, Orig); + return ConstantExpr::getPointerCast(TaggedAlias, Int8PtrTy); +} + +bool HWAddressSanitizerModule::runOnModule(Module &M) { + if (CompileKernel || !ClInstrumentGlobals) + return false; + + SmallVector GlobalsToChange; + + for (auto &G : M.globals()) + if (shouldInstrumentGlobal(&G)) { + if (G.isInterposable()) + // Force interposable vars to be accessed via GOT. + // Otherwise we can overflow relocations. + G.addAttribute("hwasan-interposable-global"); + else + GlobalsToChange.push_back(&G); + } + + size_t N = GlobalsToChange.size(); + if (N == 0) + return false; + + IRBuilder<> IRB(M.getContext()); + // Global description structure type: + // - tagged global pointer: i8* + // - size: i64 + // size is aligned to (1 << kDefaultShadowScale) bytes + StructType *GlobalStructTy = StructType::get(Int8PtrTy, Int64Ty); + for (size_t i = 0; i < N; i++) { + GlobalVariable *G = GlobalsToChange[i]; + if (!G->hasName()) + G->setName("__hwasan_anon_global"); + + std::string Name = G->getName(); + size_t SizeInBytes = M.getDataLayout().getTypeAllocSize(G->getValueType()); + Comdat *C = G->getComdat(); + GlobalVariable *NewGlobal = createInstrumentedGlobal(G); + NewGlobal->setComdat(C); + MDNode *Assoc = + MDNode::get(M.getContext(), ValueAsMetadata::get(NewGlobal)); + Constant *GVPtr = replaceGlobal(NewGlobal, G); + + auto *GVRefInitializer = ConstantStruct::get( + GlobalStructTy, GVPtr, + ConstantInt::get(Int64Ty, + alignTo(SizeInBytes, 1 << kDefaultShadowScale))); + auto *GVRef = new GlobalVariable(M, GlobalStructTy, false, + GlobalVariable::PrivateLinkage, + GVRefInitializer, Name + ".hwasan.ref"); + GVRef->setComdat(C); + GVRef->setSection(getGlobalsSection()); + GVRef->setMetadata(LLVMContext::MD_associated, Assoc); + } + return true; +} Index: lib/Transforms/Instrumentation/Instrumentation.cpp =================================================================== --- lib/Transforms/Instrumentation/Instrumentation.cpp +++ lib/Transforms/Instrumentation/Instrumentation.cpp @@ -113,6 +113,7 @@ initializeInstrProfilingLegacyPassPass(Registry); initializeMemorySanitizerLegacyPassPass(Registry); initializeHWAddressSanitizerPass(Registry); + initializeHWAddressSanitizerModulePass(Registry); initializeThreadSanitizerLegacyPassPass(Registry); initializeSanitizerCoverageModulePass(Registry); initializeDataFlowSanitizerPass(Registry); Index: test/Instrumentation/HWAddressSanitizer/globals-aarch64.ll =================================================================== --- test/Instrumentation/HWAddressSanitizer/globals-aarch64.ll +++ test/Instrumentation/HWAddressSanitizer/globals-aarch64.ll @@ -0,0 +1,27 @@ +; REQUIRES: aarch64-registered-target +; RUN: opt -hwasan-module -hwasan-instrument-globals %s -S -o %t.ll +; RUN: llc -filetype=obj %t.ll -o %t.o +; RUN: llvm-readobj -relocations %t.o | FileCheck %s --check-prefix=RELOCS + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-none-linux-gnu" + +@q = dso_local local_unnamed_addr global i64 2, align 8 +@c = common global i64 0, align 8 + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1) + +; Function Attrs: nounwind uwtable +define dso_local i32 @main() local_unnamed_addr { + tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast (i64* @q to i8*), i8 0, i64 32, i1 false) + tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast (i64* @c to i8*), i8 0, i64 32, i1 false) + ret i32 0 +} + +; RELOCS: Section (3) .rela.text { +; RELOCS-NEXT: R_AARCH64_ADR_GOT_PAGE q 0x0 +; RELOCS-NEXT: R_AARCH64_ADR_GOT_PAGE c 0x0 +; RELOCS-NEXT: R_AARCH64_LD64_GOT_LO12_NC q 0x0 +; RELOCS-NEXT: R_AARCH64_LD64_GOT_LO12_NC c 0x0 +; RELOCS-NEXT: } + Index: test/Instrumentation/HWAddressSanitizer/globals.ll =================================================================== --- test/Instrumentation/HWAddressSanitizer/globals.ll +++ test/Instrumentation/HWAddressSanitizer/globals.ll @@ -0,0 +1,59 @@ +; RUN: opt -hwasan-module -hwasan-instrument-globals %s -S -o %t.ll +; RUN: cat %t.ll | FileCheck %s +; RUN: llc -filetype=obj %t.ll -o %t.o +; RUN: llvm-readobj -relocations %t.o | FileCheck %s --check-prefix=RELOCS + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; For each externally visible global we create an alias with tagged adddress +@q = dso_local local_unnamed_addr global i64 2, align 8 + +; We don't create alias for static variables, because +; those can't be referenced outside the TU +@f = internal global i64 2, align 8 + +; We don't instrument interposable globals, because those can +; be preempted at link time and we may get ODR violation +@c = common global i64 0, align 8 + +; CHECK: @q.hwasan.data = private global { i64, [8 x i8] } { i64 2, [8 x i8] zeroinitializer }, align 16 +; CHECK: @__start___hwasan_globals = extern_weak hidden global i8 +; CHECK-NEXT: @__stop___hwasan_globals = extern_weak hidden global i8 +; CHECK-NEXT: @q.hwasan.ref = private global { i8*, i64 } { i8* bitcast ({ i64, [8 x i8] }* @q to i8*), i64 16 }, section "__hwasan_globals", !associated !0 +; CHECK-NEXT: @f.hwasan.ref = private global { i8*, i64 } { i8* getelementptr (i8, i8* bitcast ({ i64, [8 x i8] }* @f to i8*), i64 8718968878589280256), i64 16 }, section "__hwasan_globals", !associated !1 + +; We create alias to alias to recognize instrumented variables when emitting relocs. +; CHECK: @q.hwasan = private alias { i64, [8 x i8] }, inttoptr (i64 add (i64 ptrtoint ({ i64, [8 x i8] }* @q.hwasan.data to i64), i64 8718968878589280256) to { i64, [8 x i8] }*) +; CHECK-NEXT: @q = alias { i64, [8 x i8] }, { i64, [8 x i8] }* @q.hwasan + +; Function Attrs: nounwind uwtable +define dso_local i32 @main() local_unnamed_addr { + tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast (i64* @q to i8*), i8 0, i64 32, i1 false) +; CHECK: tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast ({ i64, [8 x i8] }* @q to i8*), i8 0, i64 32, i1 false) + tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast (i64* @f to i8*), i8 0, i64 32, i1 false) +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* align 8 getelementptr (i8, i8* bitcast ({ i64, [8 x i8] }* @f to i8*), i64 8718968878589280256), i8 0, i64 32, i1 false) + tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast (i64* @c to i8*), i8 0, i64 32, i1 false) +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* align 8 bitcast (i64* @c to i8*), i8 0, i64 32, i1 false) + ret i32 0 +} + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1) + +; CHECK: define internal void @hwasan.module_ctor() comdat { +; CHECK-NEXT: call void @__hwasan_init() +; CHECK-NEXT: call void @__hwasan_init_frames(i8* @__start___hwasan_frames, i8* @__stop___hwasan_frames) +; CHECK-NEXT: call void @__hwasan_init_globals(i8* @__start___hwasan_globals, i8* @__stop___hwasan_globals) +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +; We mark interposable globals to emit GOT relocations to them +; CHECK: attributes #0 = { "hwasan-interposable-global" } + +; CHECK: !0 = !{{\{\{}} i64, [8 x i8] }* @q.hwasan.data} +; CHECK-NEXT: !1 = !{{\{\{}} i64, [8 x i8] }* @f} + + +; RELOCS: R_X86_64_GOTPCREL q 0xFFFFFFFFFFFFFFFC +; RELOCS: R_X86_64_GOTPCREL c 0xFFFFFFFFFFFFFFFC +