diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -635,6 +635,7 @@ "CUDA offload target is supported only along with --emit-llvm">; def warn_drv_jmc_requires_debuginfo : Warning< - "/JMC requires debug info. Use '/Zi', '/Z7' or other debug options; option ignored">, + "%0 requires debug info. Use %1 or debug options that enable debugger's " + "stepping function; option ignored">, InGroup; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5368,6 +5368,15 @@ types::isLLVMIR(InputType), CmdArgs, DebugInfoKind, DwarfFission); + // This controls whether or not we perform JustMyCode instrumentation. + if (TC.getTriple().isOSBinFormatELF() && Args.hasArg(options::OPT_fjmc)) { + if (DebugInfoKind >= codegenoptions::DebugInfoConstructor) + CmdArgs.push_back("-fjmc"); + else + D.Diag(clang::diag::warn_drv_jmc_requires_debuginfo) << "-fjmc" + << "-g"; + } + // Add the split debug info name to the command lines here so we // can propagate it to the backend. bool SplitDWARF = (DwarfFission != DwarfFissionKind::None) && @@ -7496,7 +7505,8 @@ if (*EmitCodeView && *DebugInfoKind >= codegenoptions::DebugInfoConstructor) CmdArgs.push_back("-fjmc"); else - D.Diag(clang::diag::warn_drv_jmc_requires_debuginfo); + D.Diag(clang::diag::warn_drv_jmc_requires_debuginfo) << "/JMC" + << "'/Zi', '/Z7'"; } EHFlags EH = parseClangCLEHFlags(D, Args); diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -779,7 +779,7 @@ // TARGET: "-triple" "i686-pc-windows-msvc19.14.0" // RUN: %clang_cl /JMC /c -### -- %s 2>&1 | FileCheck %s --check-prefix JMCWARN -// JMCWARN: /JMC requires debug info. Use '/Zi', '/Z7' or other debug options; option ignored +// JMCWARN: /JMC requires debug info. Use '/Zi', '/Z7' or debug options that enable debugger's stepping function; option ignored // RUN: %clang_cl /JMC /c -### -- %s 2>&1 | FileCheck %s --check-prefix NOJMC // RUN: %clang_cl /JMC /Z7 /JMC- /c -### -- %s 2>&1 | FileCheck %s --check-prefix NOJMC diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -597,3 +597,9 @@ // RUN: %clang -### -xobjective-c %s 2>&1 | FileCheck -check-prefix=CHECK_NO_DISABLE_DIRECT %s // CHECK_DISABLE_DIRECT: -fobjc-disable-direct-methods-for-testing // CHECK_NO_DISABLE_DIRECT-NOT: -fobjc-disable-direct-methods-for-testing + +// RUN: %clang -### -S -fjmc %s 2>&1 | FileCheck -check-prefix=CHECK_NOJMC %s +// RUN: %clang -### -S -fjmc -g %s 2>&1 | FileCheck -check-prefix=CHECK_JMC %s +// CHECK_NOJMC: -fjmc requires debug info. Use -g or debug options that enable debugger's stepping function; option ignored +// CHECK_NOJMC-NOT: -fjmc +// CHECK_JMC: -fjmc diff --git a/llvm/lib/CodeGen/JMCInstrumenter.cpp b/llvm/lib/CodeGen/JMCInstrumenter.cpp --- a/llvm/lib/CodeGen/JMCInstrumenter.cpp +++ b/llvm/lib/CodeGen/JMCInstrumenter.cpp @@ -7,14 +7,17 @@ //===----------------------------------------------------------------------===// // // JMCInstrumenter pass: -// - add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to -// "llvm.linker.options" -// - create the dummy COMDAT function __JustMyCode_Default // - instrument each function with a call to __CheckForDebuggerJustMyCode. The // sole argument should be defined in .msvcjmc. Each flag is 1 byte initilized // to 1. -// - (TODO) currently targeting MSVC, adds ELF debuggers support -// +// - create the dummy COMDAT function __JustMyCode_Default to prevent linking +// error if __CheckForDebuggerJustMyCode is not available. +// - For MSVC: +// add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to +// "llvm.linker.options" +// For ELF: +// Rename __JustMyCode_Default to __CheckForDebuggerJustMyCode and mark it as +// weak symbol. //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallString.h" @@ -110,7 +113,7 @@ return FunctionType::get(VoidTy, VoidPtrTy, false); } -void createDefaultCheckFunction(Module &M, bool UseX86FastCall) { +Function *createDefaultCheckFunction(Module &M, bool UseX86FastCall) { LLVMContext &Ctx = M.getContext(); const char *DefaultCheckFunctionName = UseX86FastCall ? "_JustMyCode_Default" : "__JustMyCode_Default"; @@ -122,21 +125,10 @@ DefaultCheckFunc->addParamAttr(0, Attribute::NoUndef); if (UseX86FastCall) DefaultCheckFunc->addParamAttr(0, Attribute::InReg); - appendToUsed(M, {DefaultCheckFunc}); - Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName); - C->setSelectionKind(Comdat::Any); - DefaultCheckFunc->setComdat(C); + BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc); ReturnInst::Create(Ctx, EntryBB); - - // Add a linker option /alternatename to set the default implementation for - // the check function. - // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024 - std::string AltOption = std::string("/alternatename:") + CheckFunctionName + - "=" + DefaultCheckFunctionName; - llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)}; - MDTuple *N = MDNode::get(Ctx, Ops); - M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N); + return DefaultCheckFunc; } } // namespace @@ -144,10 +136,13 @@ bool Changed = false; LLVMContext &Ctx = M.getContext(); Triple ModuleTriple(M.getTargetTriple()); - bool UseX86FastCall = - ModuleTriple.isOSWindows() && ModuleTriple.getArch() == Triple::x86; + bool IsMSVC = ModuleTriple.isKnownWindowsMSVCEnvironment(); + bool IsELF = ModuleTriple.isOSBinFormatELF(); + assert((IsELF || IsMSVC) && "Unsupported triple for JMC"); + bool UseX86FastCall = IsMSVC && ModuleTriple.getArch() == Triple::x86; + const char *const FlagSymbolSection = IsELF ? ".just.my.code" : ".msvcjmc"; - Function *CheckFunction = nullptr; + GlobalValue *CheckFunction = nullptr; DenseMap SavedFlags(8); for (auto &F : M) { if (F.isDeclaration()) @@ -166,7 +161,7 @@ GlobalVariable *GV = new GlobalVariable( M, FlagTy, /*isConstant=*/false, GlobalValue::InternalLinkage, ConstantInt::get(FlagTy, 1), FlagName); - GV->setSection(".msvcjmc"); + GV->setSection(FlagSymbolSection); GV->setAlignment(Align(1)); GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); attachDebugInfo(*GV, *SP); @@ -175,22 +170,46 @@ } if (!CheckFunction) { - assert(!M.getFunction(CheckFunctionName) && - "JMC instrument more than once?"); - CheckFunction = cast( - M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx)) - .getCallee()); - CheckFunction->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); - CheckFunction->addParamAttr(0, Attribute::NoUndef); - if (UseX86FastCall) { - CheckFunction->setCallingConv(CallingConv::X86_FastCall); - CheckFunction->addParamAttr(0, Attribute::InReg); + Function *DefaultCheckFunc = + createDefaultCheckFunction(M, UseX86FastCall); + if (IsELF) { + DefaultCheckFunc->setName(CheckFunctionName); + DefaultCheckFunc->setLinkage(GlobalValue::WeakODRLinkage); + CheckFunction = DefaultCheckFunc; + } else { + assert(!M.getFunction(CheckFunctionName) && + "JMC instrument more than once?"); + auto *CheckFunc = cast( + M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx)) + .getCallee()); + CheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + CheckFunc->addParamAttr(0, Attribute::NoUndef); + if (UseX86FastCall) { + CheckFunc->setCallingConv(CallingConv::X86_FastCall); + CheckFunc->addParamAttr(0, Attribute::InReg); + } + CheckFunction = CheckFunc; + + StringRef DefaultCheckFunctionName = DefaultCheckFunc->getName(); + appendToUsed(M, {DefaultCheckFunc}); + Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName); + C->setSelectionKind(Comdat::Any); + DefaultCheckFunc->setComdat(C); + // Add a linker option /alternatename to set the default implementation + // for the check function. + // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024 + std::string AltOption = std::string("/alternatename:") + + CheckFunctionName + "=" + + DefaultCheckFunctionName.str(); + llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)}; + MDTuple *N = MDNode::get(Ctx, Ops); + M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N); } } // FIXME: it would be nice to make CI scheduling boundary, although in // practice it does not matter much. - auto *CI = CallInst::Create(CheckFunction, {Flag}, "", - &*F.begin()->getFirstInsertionPt()); + auto *CI = CallInst::Create(getCheckFunctionType(Ctx), CheckFunction, + {Flag}, "", &*F.begin()->getFirstInsertionPt()); CI->addParamAttr(0, Attribute::NoUndef); if (UseX86FastCall) { CI->setCallingConv(CallingConv::X86_FastCall); @@ -199,9 +218,5 @@ Changed = true; } - if (!Changed) - return false; - - createDefaultCheckFunction(M, UseX86FastCall); - return true; + return Changed; } diff --git a/llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll b/llvm/test/Instrumentation/JustMyCode/jmc-instrument-elf.ll copy from llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll copy to llvm/test/Instrumentation/JustMyCode/jmc-instrument-elf.ll --- a/llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll +++ b/llvm/test/Instrumentation/JustMyCode/jmc-instrument-elf.ll @@ -1,78 +1,72 @@ -; REQUIRES: system-windows -; RUN: opt -jmc-instrument -mtriple=x86_64-pc-windows-msvc -S < %s | FileCheck %s -; RUN: opt -jmc-instrument -mtriple=aarch64-pc-windows-msvc -S < %s | FileCheck %s -; RUN: opt -jmc-instrument -mtriple=arm-pc-windows-msvc -S < %s | FileCheck %s +; REQUIRES: system-linux +; RUN: opt -jmc-instrument -mtriple=x86_64-unknown-linux-gnu -S < %s | FileCheck %s -; CHECK: $__JustMyCode_Default = comdat any +; CHECK: @"__7DF23CF5_x@c" = internal unnamed_addr global i8 1, section ".just.my.code", align 1, !dbg !0 +; CHECK: @"__A85D9D03_x@c" = internal unnamed_addr global i8 1, section ".just.my.code", align 1, !dbg !5 -; CHECK: @"__E6EA670F_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 -; CHECK: @"__A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !5 -; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @__JustMyCode_Default to i8*)], section "llvm.metadata" - -; CHECK: define void @l1() !dbg !13 { -; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__E6EA670F_x@c") +; CHECK: define void @l1() !dbg !12 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__7DF23CF5_x@c") ; CHECK: ret void ; CHECK: } -; CHECK: define void @l2() !dbg !17 { -; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__E6EA670F_x@c") +; CHECK: define void @l2() !dbg !16 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__7DF23CF5_x@c") ; CHECK: ret void ; CHECK: } -; CHECK: define void @w1() !dbg !19 { -; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A8764FDD_x@c") +; CHECK: define void @w1() !dbg !18 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") ; CHECK: ret void ; CHECK: } -; CHECK: define void @w2() !dbg !20 { -; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A8764FDD_x@c") +; CHECK: define void @w2() !dbg !19 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") ; CHECK: ret void ; CHECK: } -; CHECK: define void @w3() !dbg !22 { -; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A8764FDD_x@c") +; CHECK: define void @w3() !dbg !21 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") ; CHECK: ret void ; CHECK: } -; CHECK: define void @w4() !dbg !24 { -; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A8764FDD_x@c") +; CHECK: define void @w4() !dbg !23 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") ; CHECK: ret void ; CHECK: } -; CHECK: declare void @__CheckForDebuggerJustMyCode(i8* noundef) unnamed_addr - -; CHECK: define void @__JustMyCode_Default(i8* noundef %0) unnamed_addr comdat { +; CHECK: define weak_odr void @__CheckForDebuggerJustMyCode(i8* noundef %0) unnamed_addr { ; CHECK: ret void ; CHECK: } -; CHECK: !llvm.linker.options = !{!12} +; CHECK: !llvm.dbg.cu = !{!2} +; CHECK: !llvm.module.flags = !{!9, !10} +; CHECK: !llvm.ident = !{!11} ; CHECK: !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) -; CHECK: !1 = distinct !DIGlobalVariable(name: "__E6EA670F_x@c", scope: !2, file: !3, type: !8, isLocal: true, isDefinition: true) +; CHECK: !1 = distinct !DIGlobalVariable(name: "__7DF23CF5_x@c", scope: !2, file: !3, type: !8, isLocal: true, isDefinition: true) ; CHECK: !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) ; CHECK: !3 = !DIFile(filename: "a/x.c", directory: "/tmp") ; CHECK: !4 = !{!0, !5} ; CHECK: !5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) -; CHECK: !6 = distinct !DIGlobalVariable(name: "__A8764FDD_x@c", scope: !2, file: !7, type: !8, isLocal: true, isDefinition: true) +; CHECK: !6 = distinct !DIGlobalVariable(name: "__A85D9D03_x@c", scope: !2, file: !7, type: !8, isLocal: true, isDefinition: true) ; CHECK: !7 = !DIFile(filename: "./x.c", directory: "C:\\\\a\\\\b\\\\") ; CHECK: !8 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char, flags: DIFlagArtificial) ; CHECK: !9 = !{i32 2, !"CodeView", i32 1} ; CHECK: !10 = !{i32 2, !"Debug Info Version", i32 3} ; CHECK: !11 = !{!"clang"} -; CHECK: !12 = !{!"/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default"} -; CHECK: !13 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -; CHECK: !14 = !DISubroutineType(types: !15) -; CHECK: !15 = !{null} -; CHECK: !16 = !{} -; CHECK: !17 = distinct !DISubprogram(name: "f", scope: !18, file: !18, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -; CHECK: !18 = !DIFile(filename: "x.c", directory: "/tmp/a") -; CHECK: !19 = distinct !DISubprogram(name: "f", scope: !7, file: !7, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -; CHECK: !20 = distinct !DISubprogram(name: "f", scope: !21, file: !21, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -; CHECK: !21 = !DIFile(filename: "./b\\x.c", directory: "C:\\\\a\\\\") -; CHECK: !22 = distinct !DISubprogram(name: "f", scope: !23, file: !23, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -; CHECK: !23 = !DIFile(filename: "./b/x.c", directory: "C:\\\\a\\\\") -; CHECK: !24 = distinct !DISubprogram(name: "f", scope: !25, file: !25, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -; CHECK: !25 = !DIFile(filename: "./b/./../b/x.c", directory: "C:\\\\a") +; CHECK: !12 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +; CHECK: !13 = !DISubroutineType(types: !14) +; CHECK: !14 = !{null} +; CHECK: !15 = !{} +; CHECK: !16 = distinct !DISubprogram(name: "f", scope: !17, file: !17, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +; CHECK: !17 = !DIFile(filename: "x.c", directory: "/tmp/a") +; CHECK: !18 = distinct !DISubprogram(name: "f", scope: !7, file: !7, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +; CHECK: !19 = distinct !DISubprogram(name: "f", scope: !20, file: !20, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +; CHECK: !20 = !DIFile(filename: "./b\\x.c", directory: "C:\\\\a\\\\") +; CHECK: !21 = distinct !DISubprogram(name: "f", scope: !22, file: !22, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +; CHECK: !22 = !DIFile(filename: "./b/x.c", directory: "C:\\\\a\\\\") +; CHECK: !23 = distinct !DISubprogram(name: "f", scope: !24, file: !24, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +; CHECK: !24 = !DIFile(filename: "./b/./../b/x.c", directory: "C:\\\\a") ; All use the same flag define void @l1() !dbg !10 { diff --git a/llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll b/llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll --- a/llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll +++ b/llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll @@ -11,12 +11,12 @@ ; CHECK: ret void ; CHECK: } -; CHECK: declare x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef) unnamed_addr - ; CHECK: define void @_JustMyCode_Default(i8* inreg noundef %0) unnamed_addr comdat { ; CHECK: ret void ; CHECK: } +; CHECK: declare x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef) unnamed_addr + ; CHECK: !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) ; CHECK: !1 = distinct !DIGlobalVariable(name: "_A8764FDD_x@c", scope: !2, file: !3, type: !5, isLocal: true, isDefinition: true) ; CHECK: !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) diff --git a/llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll b/llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll --- a/llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll +++ b/llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll @@ -6,8 +6,8 @@ ; CHECK: $__JustMyCode_Default = comdat any ; CHECK: @"__E6EA670F_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 -; CHECK: @"__A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !5 ; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @__JustMyCode_Default to i8*)], section "llvm.metadata" +; CHECK: @"__A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !5 ; CHECK: define void @l1() !dbg !13 { ; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__E6EA670F_x@c") @@ -39,12 +39,12 @@ ; CHECK: ret void ; CHECK: } -; CHECK: declare void @__CheckForDebuggerJustMyCode(i8* noundef) unnamed_addr - ; CHECK: define void @__JustMyCode_Default(i8* noundef %0) unnamed_addr comdat { ; CHECK: ret void ; CHECK: } +; CHECK: declare void @__CheckForDebuggerJustMyCode(i8* noundef) unnamed_addr + ; CHECK: !llvm.linker.options = !{!12} ; CHECK: !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())