Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -622,6 +622,19 @@ let Documentation = [ArtificialDocs]; } +def SyringeInjectionSite : InheritableAttr { + let Spellings = [Clang<"syringe_injection_site">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Documentation = [SyringeDocs]; +} + +def SyringePayload : InheritableAttr { + let Spellings = [Clang<"syringe_payload">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Args = [StringArgument<"SyringeTargetFunction">]; + let Documentation = [SyringeDocs]; +} + def XRayInstrument : InheritableAttr { let Spellings = [Clang<"xray_always_instrument">, Clang<"xray_never_instrument">]; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3394,6 +3394,18 @@ }]; } +def SyringeDocs : Documentation { + let Category = DocCatFunction; + let Heading = "syringe_injection_site (clang::syringe_injection_site), syringe_injection_payload (clang::syringe_injection_payload)"; + let Content = [{ +``__attribute__((syringe_injection_site))`` or ``[[clang::syringe_injection_site]]`` is used to mark member functions (in C++), methods (in Objective C), and free functions (in C, C++, and Objective C) to have new behaviors injected by Syringe. This will allow the Syringe runtime to modify the function's behavior dynamically as the program runs. + +``__attribute__((syringe_injection_payload(TARGET_FUNCTION)))`` or ``[[clang::syringe_injection_payload(TARGET_FUNCTION)]]`` is used to demark the payload function that Syringe will inject into invocations of TARGET_FUNCTION. + +If a function has neither of these attributes, they are ignored by the Syringe passes and runtime. + }]; +} + def XRayDocs : Documentation { let Category = DocCatFunction; let Heading = "xray_always_instrument (clang::xray_always_instrument), xray_never_instrument (clang::xray_never_instrument), xray_log_args (clang::xray_log_args)"; Index: clang/include/clang/Basic/Features.def =================================================================== --- clang/include/clang/Basic/Features.def +++ clang/include/clang/Basic/Features.def @@ -77,6 +77,7 @@ FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow)) FEATURE(efficiency_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Efficiency)) +FEATURE(syringe, LangOpts.SyringeInject) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) // Objective-C features FEATURE(objc_arr, LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -162,6 +162,12 @@ /// attribute(s). std::vector XRayAttrListFiles; + /// Paths to the Syringe attribute list files, specifying which objects + /// (files, functions, variables) should be imbued with the appropriate Syringe + /// attribute(s). + std::vector SyringeConfigFiles; + + clang::ObjCRuntime ObjCRuntime; std::string ObjCConstantStringClass; Index: clang/include/clang/Basic/LangOptions.def =================================================================== --- clang/include/clang/Basic/LangOptions.def +++ clang/include/clang/Basic/LangOptions.def @@ -293,6 +293,8 @@ "controls whether to always emit intrinsic calls to " "__xray_typedevent(...) builtin.") +LANGOPT(SyringeInject, 1, 0, "controls whether to enable Syringe behavior injection") + LANGOPT(ForceEmitVTables, 1, 0, "whether to emit all vtables") BENIGN_LANGOPT(AllowEditorPlaceholders, 1, 0, Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1135,6 +1135,23 @@ Alias, AliasArgs<["full"]>, HelpText<"Enable cf-protection in 'full' mode">; +def fsyringe : Flag<["-"], "fsyringe">, Group, + Flags<[CC1Option]>, + HelpText<"Generate stub functions and indirect calls for Syringe to perform behavior injection">; +def fnosyringe : Flag<["-"], "fno-syringe">, Group, + Flags<[CC1Option]>; + +def fsyringe_link_deps : Flag<["-"], "fsyringe-link-deps">, Group, + Flags<[CC1Option]>, + HelpText<"Tells clang to add the link dependencies for Syringe.">; +def fnosyringe_link_deps : Flag<["-"], "fnosyringe-link-deps">, Group, + Flags<[CC1Option]>; + +def fsyringe_config_file : + JoinedOrSeparate<["-"], "fsyringe-config-file=">, + Group, Flags<[CC1Option]>, + HelpText<"Filename defining Syringe Injeciton sites and payloads in YAML.">; + def fxray_instrument : Flag<["-"], "fxray-instrument">, Group, Flags<[CC1Option]>, HelpText<"Generate XRay instrumentation sleds on function entry and exit">; Index: clang/include/clang/Driver/SyringeArgs.h =================================================================== --- /dev/null +++ clang/include/clang/Driver/SyringeArgs.h @@ -0,0 +1,43 @@ +//===--- SyringeArgs.h - Arguments for Syringe ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_DRIVER_SYRINGEARGS_H +#define LLVM_CLANG_DRIVER_SYRINGEARGS_H + +#include "clang/Driver/Types.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" + +#include +#include + +namespace clang { +namespace driver { + +class ToolChain; + +class SyringeArgs { + bool SyringeInject = false; + bool SyringeRT = true; + std::vector ConfigFiles; + +public: + /// Parses the Syringe arguments from an argument list. + SyringeArgs(const ToolChain &TC, const llvm::opt::ArgList &Args); + void addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs, types::ID InputType) const; + + /// Checks if the Syringe Runtime is required + bool needsSyringeRt() const { return SyringeInject && SyringeRT; } + std::vector &getConfigFiles() { return ConfigFiles; } +}; + +} // namespace driver +} // namespace clang + +#endif // LLVM_CLANG_DRIVER_SYRINGEARGS_H Index: clang/include/clang/Driver/ToolChain.h =================================================================== --- clang/include/clang/Driver/ToolChain.h +++ clang/include/clang/Driver/ToolChain.h @@ -55,6 +55,7 @@ class SanitizerArgs; class Tool; class XRayArgs; +class SyringeArgs; /// Helper structure used to pass information extracted from clang executable /// name such as `i686-linux-android-g++`. @@ -135,6 +136,7 @@ mutable std::unique_ptr SanitizerArguments; mutable std::unique_ptr XRayArguments; + mutable std::unique_ptr SyringeArguments; /// The effective clang triple for the current Job. mutable llvm::Triple EffectiveTriple; @@ -225,6 +227,8 @@ const XRayArgs& getXRayArgs() const; + const SyringeArgs& getSyringeArgs() const; + // Returns the Arg * that explicitly turned on/off rtti, or nullptr. const llvm::opt::Arg *getRTTIArg() const { return CachedRTTIArg; } Index: clang/include/clang/Frontend/CodeGenOptions.def =================================================================== --- clang/include/clang/Frontend/CodeGenOptions.def +++ clang/include/clang/Frontend/CodeGenOptions.def @@ -94,6 +94,8 @@ ///< enabled. CODEGENOPT(StackSizeSection , 1, 0) ///< Set when -fstack-size-section is enabled. +CODEGENOPT(SyringeInjectBehaviors , 1, 0) ///< Set when -fsyringe is enabled. + ///< Set when -fxray-always-emit-customevents is enabled. CODEGENOPT(XRayAlwaysEmitCustomEvents , 1, 0) Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -47,6 +47,7 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/Transforms/Coroutines.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Syringe.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" @@ -313,6 +314,21 @@ PM.add(createEfficiencySanitizerPass(Opts)); } +static void addSyringePass(const PassManagerBuilder &Builder, + legacy::PassManagerBase &PM) { + const PassManagerBuilderWrapper &BuilderWrapper = + static_cast(Builder); + const LangOptions &LangOpts = BuilderWrapper.getLangOpts(); + auto P = static_cast(createSyringe()); + auto FileList = LangOpts.SyringeConfigFiles; + for(auto& FileName : FileList) + { + P->parse(FileName); + } + + PM.add(P); +} + static TargetLibraryInfoImpl *createTLII(llvm::Triple &TargetTriple, const CodeGenOptions &CodeGenOpts) { TargetLibraryInfoImpl *TLII = new TargetLibraryInfoImpl(TargetTriple); @@ -655,6 +671,11 @@ Options.InstrProfileOutput = CodeGenOpts.InstrProfileOutput; MPM.add(createInstrProfilingLegacyPass(Options)); } + + if (LangOpts.SyringeInject) { + addSyringePass(PMBuilder, MPM); + } + if (CodeGenOpts.hasProfileIRInstr()) { PMBuilder.EnablePGOInstrGen = true; if (!CodeGenOpts.InstrProfileOutput.empty()) Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -1845,7 +1845,6 @@ FuncAttrs.addAttribute(llvm::Attribute::NoDuplicate); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::Convergent); - if (const FunctionDecl *Fn = dyn_cast(TargetDecl)) { AddAttributesFromFunctionProtoType( getContext(), FuncAttrs, Fn->getType()->getAs()); Index: clang/lib/CodeGen/CMakeLists.txt =================================================================== --- clang/lib/CodeGen/CMakeLists.txt +++ clang/lib/CodeGen/CMakeLists.txt @@ -18,6 +18,7 @@ Passes ProfileData ScalarOpts + Syringe Support Target TransformUtils Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -1947,6 +1947,10 @@ /// instrumented with XRay nop sleds. bool ShouldXRayInstrumentFunction() const; + /// ShouldXRayInstrument - Return true if the current function should be + /// instrumented with XRay nop sleds. + bool ShouldSyringeInject() const; + /// AlwaysEmitXRayCustomEvents - Return true if we must unconditionally emit /// XRay custom event handling calls. bool AlwaysEmitXRayCustomEvents() const; Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -454,6 +454,12 @@ return CGM.getCodeGenOpts().XRayInstrumentFunctions; } +//// ShouldSyringeInject - Return true if the current function should use +/// Syringe to modify runtime behavior. +bool CodeGenFunction::ShouldSyringeInject() const { + return CGM.getCodeGenOpts().SyringeInjectBehaviors; +} + /// AlwaysEmitXRayCustomEvents - Return true if we should emit IR for calls to /// the __xray_customevent(...) builtin calls, when doing XRay instrumentation. bool CodeGenFunction::AlwaysEmitXRayCustomEvents() const { @@ -902,6 +908,20 @@ SanOpts.Mask &= ~SanitizerKind::CFIUnrelatedCast; } + // Handle Syringe annotations + if (D && ShouldSyringeInject()) { + if (const auto *SyringeAttr = D->getAttr()) { + Fn->addFnAttr("syringe-injection-site"); + } + + if (const auto *SyringeAttr = D->getAttr()) { + Fn->addFnAttr("syringe-payload"); + auto fnName = SyringeAttr->getSyringeTargetFunction(); + Fn->addFnAttr("syringe-target-function", fnName); + } + + } + // Apply xray attributes to the function (as a string, for now) bool InstrumentXray = ShouldXRayInstrumentFunction() && CGM.getCodeGenOpts().XRayInstrumentationBundle.has( Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -1189,6 +1189,11 @@ bool imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, StringRef Category = StringRef()) const; + + /// Imbue Syringe attributes to a function. + /// Returns true if we did imbue attributes this way, false otherwise. + bool imbueSyringeAttrs(llvm::Function *Fn) const; + SanitizerMetadata *getSanitizerMetadata() { return SanitizerMD.get(); } Index: clang/lib/Driver/CMakeLists.txt =================================================================== --- clang/lib/Driver/CMakeLists.txt +++ clang/lib/Driver/CMakeLists.txt @@ -19,6 +19,7 @@ Multilib.cpp Phases.cpp SanitizerArgs.cpp + SyringeArgs.cpp Tool.cpp ToolChain.cpp ToolChains/Arch/AArch64.cpp Index: clang/lib/Driver/SyringeArgs.cpp =================================================================== --- /dev/null +++ clang/lib/Driver/SyringeArgs.cpp @@ -0,0 +1,87 @@ +//===--- SyringeArgs.cpp - Arguments for Syringe --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Driver/SyringeArgs.h" +#include "ToolChains/CommonArgs.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace clang::driver; +using namespace llvm::opt; + +namespace { +constexpr char SyringeInstrumentOption[] = "-fsyringe"; +constexpr char SyringeConfigOption[] = "-fsyringe-config-file="; +} // namespace + +SyringeArgs::SyringeArgs(const ToolChain &TC, const ArgList &Args) { + const Driver &D = TC.getDriver(); + const llvm::Triple &Triple = TC.getTriple(); + if (Args.hasFlag(options::OPT_fsyringe, options::OPT_fnosyringe, false)) { + if (Triple.getOS() == llvm::Triple::Linux || + Triple.getOS() == llvm::Triple::Fuchsia) { + switch (Triple.getArch()) { + case llvm::Triple::x86_64: + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::ppc64le: + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + break; + default: + D.Diag(diag::err_drv_clang_unsupported) + << (std::string(SyringeInstrumentOption) + " on " + Triple.str()); + } + } else if (Triple.getOS() == llvm::Triple::FreeBSD || + Triple.getOS() == llvm::Triple::OpenBSD || + Triple.getOS() == llvm::Triple::Darwin || + Triple.getOS() == llvm::Triple::NetBSD) { + if (Triple.getArch() != llvm::Triple::x86_64) { + D.Diag(diag::err_drv_clang_unsupported) + << (std::string(SyringeInstrumentOption) + " on " + Triple.str()); + } + } else { + D.Diag(diag::err_drv_clang_unsupported) + << (std::string(SyringeInstrumentOption) + " on " + Triple.str()); + } + + SyringeInject = true; + + if (!Args.hasFlag(options::OPT_fsyringe_link_deps, + options::OPT_fnosyringe_link_deps, true)) + SyringeRT = false; + + for (const auto &Filename : + Args.getAllArgValues(options::OPT_fsyringe_config_file)) { + if (llvm::sys::fs::exists(Filename)) { + ConfigFiles.push_back(Filename); + // ExtraDeps.push_back(Filename); + } else + D.Diag(clang::diag::err_drv_no_such_file) << Filename; + } + } +} + +void SyringeArgs::addArgs(const ToolChain &TC, const ArgList &Args, + ArgStringList &CmdArgs, types::ID InputType) const { + if (!SyringeInject) + return; + + CmdArgs.push_back(SyringeInstrumentOption); + for (const auto &Config : ConfigFiles) { + SmallString<64> ConfigFileOpt(SyringeConfigOption); + ConfigFileOpt += Config; + CmdArgs.push_back(Args.MakeArgString(ConfigFileOpt)); + } +} Index: clang/lib/Driver/ToolChain.cpp =================================================================== --- clang/lib/Driver/ToolChain.cpp +++ clang/lib/Driver/ToolChain.cpp @@ -21,6 +21,7 @@ #include "clang/Driver/Job.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" +#include "clang/Driver/SyringeArgs.h" #include "clang/Driver/XRayArgs.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" @@ -116,6 +117,12 @@ return *XRayArguments.get(); } +const SyringeArgs& ToolChain::getSyringeArgs() const { + if (!SyringeArguments.get()) + SyringeArguments.reset(new SyringeArgs(*this, Args)); + return *SyringeArguments.get(); +} + namespace { struct DriverSuffix { Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -29,6 +29,7 @@ #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/XRayArgs.h" +#include "clang/Driver/SyringeArgs.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/llvm-config.h" #include "llvm/Option/ArgList.h" @@ -4052,6 +4053,9 @@ const XRayArgs &XRay = getToolChain().getXRayArgs(); XRay.addArgs(getToolChain(), Args, CmdArgs, InputType); + const SyringeArgs &Syringe = getToolChain().getSyringeArgs(); + Syringe.addArgs(getToolChain(), Args, CmdArgs, InputType); + if (getToolChain().SupportsProfiling()) Args.AddLastArg(CmdArgs, options::OPT_pg); @@ -4324,6 +4328,12 @@ if (Args.hasArg(options::OPT_fno_inline)) CmdArgs.push_back("-fno-inline"); + if (Args.hasArg(options::OPT_fnosyringe)) + CmdArgs.push_back("-fno-syringe"); + + if (Args.hasArg(options::OPT_fsyringe)) + CmdArgs.push_back("-fsyringe"); + if (Arg* InlineArg = Args.getLastArg(options::OPT_finline_functions, options::OPT_finline_hint_functions, options::OPT_fno_inline_functions)) Index: clang/lib/Driver/ToolChains/CommonArgs.h =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.h +++ clang/lib/Driver/ToolChains/CommonArgs.h @@ -41,6 +41,12 @@ void linkXRayRuntimeDeps(const ToolChain &TC, llvm::opt::ArgStringList &CmdArgs); +bool addSyringeRuntime(const ToolChain &TC, const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs); + +void linkSyringeRuntimeDeps(const ToolChain &TC, + llvm::opt::ArgStringList &CmdArgs); + void AddRunTimeLibs(const ToolChain &TC, const Driver &D, llvm::opt::ArgStringList &CmdArgs, const llvm::opt::ArgList &Args); Index: clang/lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.cpp +++ clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -32,6 +32,7 @@ #include "clang/Driver/ToolChain.h" #include "clang/Driver/Util.h" #include "clang/Driver/XRayArgs.h" +#include "clang/Driver/SyringeArgs.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -736,7 +737,36 @@ return !StaticRuntimes.empty() || !NonWholeStaticRuntimes.empty(); } -bool tools::addXRayRuntime(const ToolChain&TC, const ArgList &Args, ArgStringList &CmdArgs) { +bool tools::addSyringeRuntime(const ToolChain &TC, const ArgList &Args, + ArgStringList &CmdArgs) { + if (Args.hasArg(options::OPT_shared)) + return false; + + if (TC.getSyringeArgs().needsSyringeRt()) { + CmdArgs.push_back("-whole-archive"); + CmdArgs.push_back(TC.getCompilerRTArgString(Args, "syringe", false)); + CmdArgs.push_back("-no-whole-archive"); + return true; + } + + return false; +} + +void tools::linkSyringeRuntimeDeps(const ToolChain &TC, + ArgStringList &CmdArgs) { + CmdArgs.push_back("--no-as-needed"); + CmdArgs.push_back("-lpthread"); + if (TC.getTriple().getOS() != llvm::Triple::OpenBSD) + CmdArgs.push_back("-lrt"); + CmdArgs.push_back("-lm"); + + if (TC.getTriple().getOS() != llvm::Triple::FreeBSD && + TC.getTriple().getOS() != llvm::Triple::NetBSD && + TC.getTriple().getOS() != llvm::Triple::OpenBSD) + CmdArgs.push_back("-ldl"); +} + +bool tools::addXRayRuntime(const ToolChain& TC, const ArgList &Args, ArgStringList &CmdArgs) { if (Args.hasArg(options::OPT_shared)) return false; Index: clang/lib/Driver/ToolChains/Darwin.h =================================================================== --- clang/lib/Driver/ToolChains/Darwin.h +++ clang/lib/Driver/ToolChains/Darwin.h @@ -515,6 +515,10 @@ llvm::opt::ArgStringList &CmdArgs, StringRef Sanitizer, bool shared = true) const; + + void addSyringeRTLibArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs, + bool shared = true) const; }; } // end namespace toolchains Index: clang/lib/Driver/ToolChains/Darwin.cpp =================================================================== --- clang/lib/Driver/ToolChains/Darwin.cpp +++ clang/lib/Driver/ToolChains/Darwin.cpp @@ -18,6 +18,7 @@ #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" +#include "clang/Driver/SyringeArgs.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/Path.h" @@ -1050,6 +1051,17 @@ RLO); } +void DarwinClang::addSyringeRTLibArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs, + bool Shared) const { + auto RLO = RuntimeLinkOptions(RLO_AlwaysLink | (Shared ? RLO_AddRPath : 0U)); + AddLinkRuntimeLib(Args, CmdArgs, + (Twine("libclang_rt.syringe_") + getOSLibraryNameSuffix() + + (Shared ? "_dynamic.dylib" : ".a")) + .str(), + RLO); +} + ToolChain::RuntimeLibType DarwinClang::GetRuntimeLibType( const ArgList &Args) const { if (Arg* A = Args.getLastArg(options::OPT_rtlib_EQ)) { @@ -1082,6 +1094,10 @@ return; } + if (getSyringeArgs().needsSyringeRt()) { + addSyringeRTLibArgs(Args, CmdArgs, false); + } + const SanitizerArgs &Sanitize = getSanitizerArgs(); if (Sanitize.needsAsanRt()) AddLinkSanitizerLibArgs(Args, CmdArgs, "asan"); Index: clang/lib/Driver/ToolChains/FreeBSD.cpp =================================================================== --- clang/lib/Driver/ToolChains/FreeBSD.cpp +++ clang/lib/Driver/ToolChains/FreeBSD.cpp @@ -260,6 +260,7 @@ bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs); bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs); + bool NeedsSyringeDeps = addSyringeRuntime(ToolChain, Args, CmdArgs); AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { @@ -276,6 +277,8 @@ linkSanitizerRuntimeDeps(ToolChain, CmdArgs); if (NeedsXRayDeps) linkXRayRuntimeDeps(ToolChain, CmdArgs); + if (NeedsSyringeDeps) + linkSyringeRuntimeDeps(ToolChain, CmdArgs); // FIXME: For some reason GCC passes -lgcc and -lgcc_s before adding // the default system libraries. Just mimic this for now. if (Args.hasArg(options::OPT_pg)) Index: clang/lib/Driver/ToolChains/Gnu.cpp =================================================================== --- clang/lib/Driver/ToolChains/Gnu.cpp +++ clang/lib/Driver/ToolChains/Gnu.cpp @@ -446,6 +446,7 @@ bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs); bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs); + bool NeedsSyringeDeps = addSyringeRuntime(ToolChain, Args, CmdArgs); AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); // The profile runtime also needs access to system libraries. getToolChain().addProfileRTLibs(Args, CmdArgs); @@ -477,6 +478,9 @@ if (NeedsXRayDeps) linkXRayRuntimeDeps(ToolChain, CmdArgs); + if (NeedsSyringeDeps) + linkSyringeRuntimeDeps(ToolChain, CmdArgs); + bool WantPthread = Args.hasArg(options::OPT_pthread) || Args.hasArg(options::OPT_pthreads); Index: clang/lib/Driver/ToolChains/NetBSD.cpp =================================================================== --- clang/lib/Driver/ToolChains/NetBSD.cpp +++ clang/lib/Driver/ToolChains/NetBSD.cpp @@ -254,6 +254,7 @@ bool NeedsSanitizerDeps = addSanitizerRuntimes(getToolChain(), Args, CmdArgs); bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs); + bool NeedsSyringeDeps = addSyringeRuntime(ToolChain, Args, CmdArgs); AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA); unsigned Major, Minor, Micro; @@ -292,6 +293,8 @@ linkSanitizerRuntimeDeps(getToolChain(), CmdArgs); if (NeedsXRayDeps) linkXRayRuntimeDeps(ToolChain, CmdArgs); + if (NeedsSyringeDeps) + linkSyringeRuntimeDeps(ToolChain, CmdArgs); if (Args.hasArg(options::OPT_pthread)) CmdArgs.push_back("-lpthread"); CmdArgs.push_back("-lc"); Index: clang/lib/Driver/ToolChains/OpenBSD.cpp =================================================================== --- clang/lib/Driver/ToolChains/OpenBSD.cpp +++ clang/lib/Driver/ToolChains/OpenBSD.cpp @@ -181,6 +181,7 @@ bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs); bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs); + bool NeedsSyringeDeps = addSyringeRuntime(ToolChain, Args, CmdArgs); AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { @@ -200,6 +201,10 @@ CmdArgs.push_back(ToolChain.getCompilerRTArgString(Args, "builtins", false)); linkXRayRuntimeDeps(ToolChain, CmdArgs); } + if (NeedsSyringeDeps) { + CmdArgs.push_back(ToolChain.getCompilerRTArgString(Args, "builtins", false)); + linkSyringeRuntimeDeps(ToolChain, CmdArgs); + } // FIXME: For some reason GCC passes -lgcc before adding // the default system libraries. Just mimic this for now. CmdArgs.push_back("-lgcc"); Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -856,7 +856,6 @@ Args.hasArg(OPT_finstrument_functions_after_inlining); Opts.InstrumentFunctionEntryBare = Args.hasArg(OPT_finstrument_function_entry_bare); - Opts.XRayInstrumentFunctions = Args.hasArg(OPT_fxray_instrument); Opts.XRayAlwaysEmitCustomEvents = @@ -865,6 +864,7 @@ Args.hasArg(OPT_fxray_always_emit_typedevents); Opts.XRayInstructionThreshold = getLastArgIntValue(Args, OPT_fxray_instruction_threshold_EQ, 200, Diags); + Opts.SyringeInjectBehaviors = Args.hasArg(OPT_fsyringe); auto XRayInstrBundles = Args.getAllArgValues(OPT_fxray_instrumentation_bundle); @@ -2747,6 +2747,10 @@ Args.getAllArgValues(OPT_fxray_never_instrument); Opts.XRayAttrListFiles = Args.getAllArgValues(OPT_fxray_attr_list); + // -fsyringe + Opts.SyringeInject = Args.hasArg(OPT_fsyringe, OPT_fnosyringe, false); + Opts.SyringeConfigFiles = Args.getAllArgValues(OPT_fsyringe_config_file); + // -fforce-emit-vtables Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables); Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -411,6 +411,17 @@ AL); } +/// Applies the Syringe Payload Attribute and sets the payload target string +static void handleSyringePayloadAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Model; + SourceLocation LiteralLoc; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Model, &LiteralLoc)) + return; + + D->addAttr(::new (S.Context) SyringePayloadAttr( + AL.getRange(), S.Context, Model, AL.getAttributeSpellingListIndex())); +} + /// Check if the passed-in expression is of type int or bool. static bool isIntOrBool(Expr *Exp) { QualType QT = Exp->getType(); @@ -6604,6 +6615,12 @@ case ParsedAttr::AT_XRayLogArgs: handleXRayLogArgsAttr(S, D, AL); break; + case ParsedAttr::AT_SyringeInjectionSite: + handleSimpleAttribute(S, D, AL); + break; + case ParsedAttr::AT_SyringePayload: + handleSyringePayloadAttr(S, D, AL); + break; } } Index: clang/test/CodeGen/syringe-imbue.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/syringe-imbue.cpp @@ -0,0 +1,20 @@ +// RUN: echo "- filename: %s" > %t.syringe-imbue.yml +// RUN: echo " targets:" >> %t.syringe-imbue.yml +// RUN: echo " - name: _Z3foov" >> %t.syringe-imbue.yml +// RUN: echo " enable: ALWAYS" >> %t.syringe-imbue.yml +// RUN: echo " disable: NEVER" >> %t.syringe-imbue.yml +// RUN: echo " payloads:" >> %t.syringe-imbue.yml +// RUN: echo " - name: _Z3barv" >> %t.syringe-imbue.yml +// RUN: echo " target: _Z3foov" >> %t.syringe-imbue.yml +// RUN: %clang -fsyringe -fsyringe-config-file=%t.syringe-imbue.yml \ +// RUN: -x c++ -std=c++11 -emit-llvm -S %s -o - | FileCheck %s + +void foo(){} + +void bar(){} + +// CHECK: define dso_local void @_Z3foov() #[[FOO:[0-9]+]] { +// CHECK: define dso_local void @_Z3barv() #[[BAR:[0-9]+]] { + +// CHECK: attributes #[[FOO]] = {{.*}}"syringe-injection-site"{{.*}} +// CHECK: attributes #[[BAR]] = {{.*}} "syringe-payload" {{.*}}"syringe-target-function"="_Z3foov" Index: clang/test/CodeGen/syringe-injection-site.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/syringe-injection-site.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 %s -fsyringe -std=c++11 -x c++ -emit-llvm -o - -triple x86_64-unknown-linux-gnu | FileCheck %s + +// CHECK: @"_Z3fooi$syringe_bool" = global i8 0 +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] + +// CHECK: @"_Z3fooi$detour_impl" = alias void (i32), void (i32)* @_Z3bari + +// Make sure that the LLVM attribute for Syringe-annotated functions do show up. +[[clang::syringe_injection_site]] void foo(int a) { +// CHECK: define void @_Z3fooi(i32 %a) #0 +// CHECK-DAG: %"_Z3fooi$syringe_bool" = load i8, i8* @"_Z3fooi$syringe_bool" +// CHECK-DAG: [[REG2:%[0-9]+]] = trunc i8 %"_Z3fooi$syringe_bool" to i1 +// CHECK: br i1 [[REG2]], label %syringe_inject, label %entry +// CHECK: tail call void @_Z3bari(i32 %a) +// CHECK-NEXT: ret void +}; + +[[clang::syringe_payload("_Z3fooi")]] void bar(int a) { +// CHECK: define void @_Z3bari(i32 %a) #1 { +}; + + +// CHECK: define internal void @"syringe.module_ctor{{.*}}syringe-injection-site.cpp"() +// CHECK-NEXT: call void @__syringe_register(void (i32)* @_Z3fooi, i8* @"_Z3fooi$syringe_bool") +// CHECK-NEXT: ret void + +// CHECK: declare void @__syringe_register(void (i32)*, i8*) + + +// CHECK: #0 = {{.*}}"syringe-injection-site"{{.*}} +// CHECK: #1 = {{.*}}"syringe-payload"{{.*}}"syringe-target-function"="_Z3fooi" Index: clang/test/CodeGen/syringe-inline-function.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/syringe-inline-function.cpp @@ -0,0 +1,48 @@ + +// RUN: %clang_cc1 %s -fsyringe -std=c++11 -x c++ -emit-llvm -o - -triple x86_64-unknown-linux-gnu | FileCheck %s + +[[clang::syringe_injection_site]] int __attribute((always_inline)) foo(int a) { + // CHECK: %"_Z3fooi$syringe_bool" = load i8, i8* @"_Z3fooi$syringe_bool" + // CHECK_NEXT: %0 = trunc i8 %"_Z3fooi$syringe_bool" to i1 + // CHECK_NEXT: br i1 %0, label %syringe_inject, label %entry + return a + 1; +} + +[[clang::syringe_payload("_Z3fooi")]] int bar(int a) { + // CHECK: define i32 @_Z3bari(i32 %a) #1 { + return a - 1; +} + +// CHECK: define i32 @main() +// CHECK: entry: +// CHECK: %retval = alloca i32, align 4 +// CHECK: %j = alloca i32, align 4 +// CHECK: store i32 0, i32* %retval, align 4 +// CHECK: %savedstack = call i8* @llvm.stacksave() +// CHECK: %"_Z3fooi$syringe_bool.i" = load i8, i8* @"_Z3fooi$syringe_bool" +// CHECK: %0 = trunc i8 %"_Z3fooi$syringe_bool.i" to i1 +// CHECK: br i1 %0, label %syringe_inject.i, label %entry.i +// CHECK: entry.i: +// CHECK: %a.addr.i = alloca i32, align 4 +// CHECK: store i32 1, i32* %a.addr.i, align 4 +// CHECK: %1 = load i32, i32* %a.addr.i, align 4 +// CHECK: %add.i = add nsw i32 %1, 1 +// CHECK: call void @llvm.stackrestore(i8* %savedstack) +// CHECK: br label %_Z3fooi.exit +// CHECK: syringe_inject.i: +// CHECK: %2 = call i32 @_Z3bari(i32 1) #3 +// CHECK: call void @llvm.stackrestore(i8* %savedstack) +// CHECK: br label %_Z3fooi.exit +// CHECK: _Z3fooi.exit: +// CHECK: %call1 = phi i32 [ %add.i, %entry.i ], [ %2, %syringe_inject.i ] +// CHECK: store i32 %call1, i32* %j, align 4 +// CHECK: %3 = load i32, i32* %j, align 4 +// CHECK: ret i32 %3 + + + +int main() { + auto j = foo(1); + return j; +} +///usr/local/google/home/paulkirth/workspace/llvm-dev/build/bin/clang -cc1 -internal-isystem /usr/local/google/home/paulkirth/workspace/llvm-dev/build/lib/clang/8.0.0/include -nostdsysteminc /usr/local/google/home/paulkirth/workspace/llvm-dev/clang/test/CodeGen/syringe-inline-function.cpp -fsyringe -std=c++11 -x c++ -emit-llvm -o - -triple x86_64-unknown-linux-gnu Index: clang/test/Sema/syringe-injection-site-attr.c =================================================================== --- /dev/null +++ clang/test/Sema/syringe-injection-site-attr.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only -std=c11 +void foo() __attribute__((syringe_injection_site)) ; + +struct __attribute__((syringe_injection_site)) a { int x; }; // expected-warning {{'syringe_injection_site' attribute only applies to functions and Objective-C methods}} + +void bar() __attribute__((syringe_injection_site("not-supported"))); // expected-error {{'syringe_injection_site' attribute takes no arguments}} Index: clang/test/Sema/syringe-injection-site-attr.cpp =================================================================== --- /dev/null +++ clang/test/Sema/syringe-injection-site-attr.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only -std=c++11 -x c++ +void foo [[clang::syringe_injection_site]] (); + +struct [[clang::syringe_injection_site]] a { int x; }; // expected-warning {{'syringe_injection_site' attribute only applies to functions and Objective-C methods}} + +class b { + void c [[clang::syringe_injection_site]] (); +}; + +void baz [[clang::syringe_injection_site("not-supported")]] (); // expected-error {{'syringe_injection_site' attribute takes no arguments}} Index: clang/test/Sema/syringe-payload-attr.c =================================================================== --- /dev/null +++ clang/test/Sema/syringe-payload-attr.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only -std=c11 +void foo() __attribute__((syringe_payload)) ; // expected-error {{'syringe_payload' attribute takes one argument}} + +struct __attribute__((syringe_payload)) a { int x; }; // expected-warning {{'syringe_payload' attribute only applies to functions and Objective-C methods}} + +void bar() __attribute__((syringe_payload("foo"))); Index: clang/test/Sema/syringe-payload-attr.cpp =================================================================== --- /dev/null +++ clang/test/Sema/syringe-payload-attr.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only -std=c++11 -x c++ +void foo [[clang::syringe_payload]] (); // expected-error {{'syringe_payload' attribute takes one argument}} + +struct [[clang::syringe_payload]] a { int x; }; // expected-warning {{'syringe_payload' attribute only applies to functions and Objective-C methods}} + +class b { + void c [[clang::syringe_payload("_Z3foov")]] (); +}; + +void baz [[clang::syringe_payload("foo")]] (); Index: compiler-rt/CMakeLists.txt =================================================================== --- compiler-rt/CMakeLists.txt +++ compiler-rt/CMakeLists.txt @@ -33,6 +33,8 @@ mark_as_advanced(COMPILER_RT_BUILD_SANITIZERS) option(COMPILER_RT_BUILD_XRAY "Build xray" ON) mark_as_advanced(COMPILER_RT_BUILD_XRAY) +option(COMPILER_RT_BUILD_SYRINGE "Build syringe" ON) +mark_as_advanced(COMPILER_RT_BUILD_SYRINGE) option(COMPILER_RT_BUILD_LIBFUZZER "Build libFuzzer" ON) mark_as_advanced(COMPILER_RT_BUILD_LIBFUZZER) option(COMPILER_RT_BUILD_PROFILE "Build profile runtime" ON) Index: compiler-rt/cmake/builtin-config-ix.cmake =================================================================== --- compiler-rt/cmake/builtin-config-ix.cmake +++ compiler-rt/cmake/builtin-config-ix.cmake @@ -13,6 +13,7 @@ builtin_check_c_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG) builtin_check_c_compiler_flag(-ffreestanding COMPILER_RT_HAS_FREESTANDING_FLAG) builtin_check_c_compiler_flag(-fxray-instrument COMPILER_RT_HAS_XRAY_COMPILER_FLAG) +builtin_check_c_compiler_flag(-fsyringe COMPILER_RT_HAS_SYRINGE_COMPILER_FLAG) builtin_check_c_compiler_source(COMPILER_RT_HAS_ATOMIC_KEYWORD " Index: compiler-rt/cmake/config-ix.cmake =================================================================== --- compiler-rt/cmake/config-ix.cmake +++ compiler-rt/cmake/config-ix.cmake @@ -222,6 +222,11 @@ else() set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} powerpc64le) endif() +if(APPLE) +set(ALL_SYRINGE_SUPPORTED_ARCH ${X86_64}) +else() +set(ALL_SYRINGE_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} powerpc64le) +endif() set(ALL_SHADOWCALLSTACK_SUPPORTED_ARCH ${X86_64} ${ARM64}) if(APPLE) @@ -268,6 +273,7 @@ set(PROFILE_SUPPORTED_OS osx) set(TSAN_SUPPORTED_OS osx) set(XRAY_SUPPORTED_OS osx) + set(SYRINGE_SUPPORTED_OS osx) if(NOT SANITIZER_MIN_OSX_VERSION) string(REGEX MATCH "-mmacosx-version-min=([.0-9]+)" MACOSX_VERSION_MIN_FLAG "${CMAKE_CXX_FLAGS}") @@ -436,6 +442,9 @@ list_intersect(XRAY_SUPPORTED_ARCH ALL_XRAY_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(SYRINGE_SUPPORTED_ARCH + ALL_SYRINGE_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(SHADOWCALLSTACK_SUPPORTED_ARCH ALL_SHADOWCALLSTACK_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) @@ -465,6 +474,7 @@ filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH}) filter_available_targets(SCUDO_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH}) filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH}) + filter_available_targets(SYRINGE_SUPPORTED_ARCH ${ALL_SYRINGE_SUPPORTED_ARCH}) filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH ${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH}) endif() @@ -618,6 +628,13 @@ set(COMPILER_RT_HAS_XRAY FALSE) endif() +if (COMPILER_RT_HAS_SANITIZER_COMMON AND SYRINGE_SUPPORTED_ARCH AND + OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD|OpenBSD") + set(COMPILER_RT_HAS_SYRINGE TRUE) +else() + set(COMPILER_RT_HAS_SYRINGE FALSE) +endif() + if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia") set(COMPILER_RT_HAS_FUZZER TRUE) Index: compiler-rt/include/CMakeLists.txt =================================================================== --- compiler-rt/include/CMakeLists.txt +++ compiler-rt/include/CMakeLists.txt @@ -22,9 +22,20 @@ xray/xray_log_interface.h) endif(COMPILER_RT_BUILD_XRAY) +if (COMPILER_RT_BUILD_SYRINGE) + set(SYRINGE_HEADERS + syringe/injection_data.h + syringe/syringe_rt.h + syringe/syringe_rt_cxx.h + ) +endif(COMPILER_RT_BUILD_SYRINGE) + + set(COMPILER_RT_HEADERS ${SANITIZER_HEADERS} - ${XRAY_HEADERS}) + ${XRAY_HEADERS} + ${SYRINGE_HEADERS} + ) set(output_dir ${COMPILER_RT_OUTPUT_DIR}/include) @@ -54,6 +65,12 @@ COMPONENT compiler-rt-headers PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/xray) +# Install syringe headers. +install(FILES ${SYRINGE_HEADERS} + COMPONENT compiler-rt-headers + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/syringe) + if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDEs. add_custom_target(install-compiler-rt-headers Index: compiler-rt/include/syringe/injection_data.h =================================================================== --- /dev/null +++ compiler-rt/include/syringe/injection_data.h @@ -0,0 +1,58 @@ +//===- injection_data.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Syringe, a dynamic behavior injection system. +// +// Data structures used by the syringe runtime. +//===----------------------------------------------------------------------===// + +#ifndef SYRINGE_INJECTION_DATA_H +#define SYRINGE_INJECTION_DATA_H 1 + +#include + +// Outside of namespace for C compatibility +typedef void (*fptr_t)(void); + +namespace __syringe { + +// metadata structure for holding implementation pointer data +struct InjectionData { + fptr_t OrigFunc; + fptr_t StubImpl; + fptr_t DetourFunc; + fptr_t *ImplPtr; + template + InjectionData(T OrigFunction, T StubImplementation, T DetourFunction, + R ImplPointer) + : OrigFunc((fptr_t)OrigFunction), StubImpl((fptr_t)StubImplementation), + DetourFunc((fptr_t)DetourFunction), ImplPtr((fptr_t *)ImplPointer) {} +}; + +// metadata structure for accessing the syringe boolean flag +struct SimpleInjectionData { + fptr_t OrigFunc; // pointer to syringe site + bool *InjectionEnabled; // pointer to runtime controled boolead (controls injection) + + template + SimpleInjectionData(T OrigFunction, bool *InjectionEnabled) + : OrigFunc((fptr_t)OrigFunction), InjectionEnabled(InjectionEnabled) {} +}; + +// a struct describing class method pointers for itanium ABI +// See Itanium ABI for more details +struct mPtrTy { + ptrdiff_t ptr; // the pointer value + // for non-virtual methods, this is the + // actual pointer, otherwise its an index into the vtable + ptrdiff_t adj; // the adjustment to the pointer +}; + +} // namespace __syringe +#endif // SYRINGE_INJECTION_DATA_H Index: compiler-rt/include/syringe/syringe_rt.h =================================================================== --- /dev/null +++ compiler-rt/include/syringe/syringe_rt.h @@ -0,0 +1,40 @@ +//===- syringe_rt.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Syringe, a dynamic behavior injection system. +// +// APIs for using the Syringe Runtime. +//===----------------------------------------------------------------------===// + +#ifndef SYRINGE_SYRINGE_RT_H +#define SYRINGE_SYRINGE_RT_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif +// typedef for generic function pointer type to use when casting +// or manipulating funciton pointers in the runtime. +typedef void (*fptr_t)(void); + +// toggles the current state of injection +bool toggleImpl(fptr_t OrigFunc); + +// prints the addresses of syringe metadata, and their values +void printSyringeData(); + +// registers metadata for initialization in the syringe runtime +void __syringe_register(void *OrigFunc, bool *InjecitonEnabled); + +#ifdef __cplusplus +} +#endif + +#endif // SYRINGE_SYRINGE_RT_H Index: compiler-rt/include/syringe/syringe_rt_cxx.h =================================================================== --- /dev/null +++ compiler-rt/include/syringe/syringe_rt_cxx.h @@ -0,0 +1,120 @@ +//===- syringe_rt_cxx.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Syringe, a dynamic behavior injection system. +// +// APIs for using the Syringe Runtime. +//===----------------------------------------------------------------------===// + +#ifndef SYRINGE_SYRINGE_RT_CXX_H +#define SYRINGE_SYRINGE_RT_CXX_H 1 + +#include "syringe/injection_data.h" + +#include +#include +#include +#include + +namespace __syringe { + +/// A container for the Syringe runtime data +extern std::vector GlobalSyringeData; + +/// Use a untion to effectivly cast between member function pointers +/// and normal funciton pointers. +/// On Itanium a non-virutal member function pointer is the first 8 bytes +/// of the member function pointer. We can (ab)use that to get its address. +template fptr_t convertMemberPtr(T foo) { + union { + T mfunc; + fptr_t addr; + }; + mfunc = foo; + return addr; +} + +/// Look up the metadata associated with target +/// @target pointer to the target injection site +SimpleInjectionData *findImplPointerImpl(fptr_t target); + +/// look up the memtadata for OrigFunc +/// @OrigFunc pointer to the target injection site +/// We use a dirty hack with a union to enable casting between +/// pointer types (like class method pointers) that are normally not allowed. +template SimpleInjectionData *findImplPointer(T OrigFunc) { + fptr_t target; + target = convertMemberPtr(OrigFunc); + + return findImplPointerImpl(target); +} + +/// register the metadata about OrigFunc in our global metadata +/// Creates a tuple binding the address of OrigFunc to its injeciton flag +/// @OrigFunc pointer to the target function +/// @InjectionEnabled address the OrigFunc's syringe boolean +template +void registerInjection(T OrigFunc, bool *InjectionEnabled) { + assert(findImplPointer(OrigFunc) == nullptr && + "Cannot register two payloads for the same injection site!"); + GlobalSyringeData.emplace_back(OrigFunc, InjectionEnabled); +} + +/// Changes if the injected behavior is active or inactive +/// @OrigFunc pointer to the Syringe Site +/// @return true if state changed, false when pointer not found +template bool toggleImplPtr(T OrigFunc) { + auto Ptr = findImplPointer(OrigFunc); + if (!Ptr) { + return false; + } + + *Ptr->InjectionEnabled = !(*Ptr->InjectionEnabled); + return true; +} + +/// Toggle virutal function implementation +/// Currently only works for Itanium ABI +/// Relies on horrible ABI hacks to get the address of virtual methods +/// @OrigFunc pointer to the member function +/// @Instance address of an instance of the class in question +template +bool toggleVirtualImpl(T OrigFunc, R Instance) { + // use char * to represent vtbl address for easy indexing + typedef char *vtblptr_t; + + // convert the OrigFunc to a pointer to a member function pointer type + mPtrTy *MemberFnPtrPtr = (mPtrTy *)&OrigFunc; + + // get a pointer to the vtbl pointer for the passed in instance + vtblptr_t *PtrToVtblPtr = (vtblptr_t *)Instance; + + // on itanium the vtable pointer is the at the instance's address + auto VtblPtr = *PtrToVtblPtr; + + // TODO: do we need the adjustment? + // calculate the offset of into the vtable for the target function + auto FnOffset = MemberFnPtrPtr->ptr + MemberFnPtrPtr->adj - 1; + auto TargetAddr = + static_cast(static_cast(VtblPtr + FnOffset)); + + return toggleImplPtr(*TargetAddr); +} + +/// Gets the address of a member function +template fptr_t lookupMemberFunctionAddr(T FPtr) { + assert(FPtr != nullptr && + "Syringe Member Function Lookup was passed a nullptr!"); + mPtrTy *j = (mPtrTy *)FPtr; + return (fptr_t)(j->ptr); +} + +} // end namespace __syringe + +#endif // SYRINGE_SYRINGE_RT_H Index: compiler-rt/lib/CMakeLists.txt =================================================================== --- compiler-rt/lib/CMakeLists.txt +++ compiler-rt/lib/CMakeLists.txt @@ -9,7 +9,8 @@ # #TODO: Refactor sanitizer_common into smaller pieces (e.g. flag parsing, utils). if (COMPILER_RT_HAS_SANITIZER_COMMON AND - (COMPILER_RT_BUILD_SANITIZERS OR COMPILER_RT_BUILD_XRAY)) + (COMPILER_RT_BUILD_SANITIZERS OR COMPILER_RT_BUILD_XRAY + OR COMPILER_RT_BUILD_SYRINGE)) add_subdirectory(sanitizer_common) endif() @@ -49,6 +50,10 @@ compiler_rt_build_runtime(xray) endif() +if(COMPILER_RT_BUILD_SYRINGE) + compiler_rt_build_runtime(syringe) +endif() + if(COMPILER_RT_BUILD_LIBFUZZER) compiler_rt_build_runtime(fuzzer) endif() Index: compiler-rt/lib/syringe/CMakeLists.txt =================================================================== --- /dev/null +++ compiler-rt/lib/syringe/CMakeLists.txt @@ -0,0 +1,106 @@ +# Build for all components of the Syinge runtime support library. + +# Syinge runtime library implementation files. +set(SYRINGE_SOURCES + syringe_rt.cpp) + +set(SYRINGE_HEADERS + #syringe_rt.h + #syringe_rt_cxx.h + ) + +# Create list of all source files for +# consumption by tests. +set(SYRINGE_ALL_SOURCE_FILES + ${SYRINGE_SOURCES} + ${SYRINGE_HEADERS} +) +list(REMOVE_DUPLICATES SYRINGE_ALL_SOURCE_FILES) +# Make list that uses absolute paths +set(SYRINGE_ALL_SOURCE_FILES_ABS_PATHS "") +foreach (src_file ${SYRINGE_ALL_SOURCE_FILES}) + list(APPEND + SYRINGE_ALL_SOURCE_FILES_ABS_PATHS + "${CMAKE_CURRENT_SOURCE_DIR}/${src_file}") +endforeach() + + +# Now put it all together... +include_directories(..) +include_directories(../../include) + +set(SYRINGE_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +set(SYRINGE_COMMON_DEFINITIONS SYRINGE_HAS_EXCEPTIONS=1) + +# We don't need RTTI in Syringe, so turn that off. +append_rtti_flag(OFF SYRINGE_CFLAGS) + +append_list_if( + COMPILER_RT_HAS_SYRINGE_COMPILER_FLAG SYRINGE_SUPPORTED=1 SYRINGE_COMMON_DEFINITIONS) + +add_compiler_rt_component(syringe) + +set(SYRINGE_COMMON_RUNTIME_OBJECT_LIBS + RTSanitizerCommon + RTSanitizerCommonLibc) + + +if (TARGET cxx-headers OR HAVE_LIBCXX) + set(SYRINGE_DEPS cxx-headers) +endif() + +if (APPLE) + set(SYRINGE_LINK_LIBS ${SANITIZER_COMMON_LINK_LIBS}) + add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS) + #add_weak_symbols("syringe" WEAK_SYMBOL_LINK_FLAGS) + + add_compiler_rt_object_libraries(RTSyringe + OS ${SYRINGE_SUPPORTED_OS} + ARCHS ${SYRINGE_SUPPORTED_ARCH} + SOURCES ${SYRINGE_SOURCES} ${${arch}_SOURCES} + ADDITIONAL_HEADERS ${SYRINGE_HEADERS} + CFLAGS ${SYRINGE_CFLAGS} + DEFS ${SYRINGE_COMMON_DEFINITIONS} + DEPS ${SYRINGE_DEPS}) + + # We only support running on osx for now. + add_compiler_rt_runtime(clang_rt.syringe + STATIC + OS ${SYRINGE_SUPPORTED_OS} + ARCHS ${SYRINGE_SUPPORTED_ARCH} + OBJECT_LIBS RTSyringe + RTSanitizerCommon + RTSanitizerCommonLibc + CFLAGS ${SYRINGE_CFLAGS} + DEFS ${SYRINGE_COMMON_DEFINITIONS} + LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS} + LINK_LIBS ${SYRINGE_LINK_LIBS} + PARENT_TARGET syringe) + +else() # not Apple + foreach(arch ${SYRINGE_SUPPORTED_ARCH}) + if(NOT CAN_TARGET_${arch}) + continue() + endif() + add_compiler_rt_object_libraries(RTSyringe + ARCHS ${arch} + SOURCES ${SYRINGE_SOURCES} ${${arch}_SOURCES} + ADDITIONAL_HEADERS ${SYRINGE_HEADERS} + CFLAGS ${SYRINGE_CFLAGS} + DEFS ${SYRINGE_COMMON_DEFINITIONS} + DEPS ${SYRINGE_DEPS}) + + # Common Syringe archive for instrumented binaries. + add_compiler_rt_runtime(clang_rt.syringe + STATIC + ARCHS ${arch} + CFLAGS ${SYRINGE_CFLAGS} + DEFS ${SYRINGE_COMMON_DEFINITIONS} + OBJECT_LIBS ${SYRINGE_COMMON_RUNTIME_OBJECT_LIBS} RTSyringe + PARENT_TARGET syringe) + endforeach() +endif() # not Apple + +if(COMPILER_RT_INCLUDE_TESTS) + add_subdirectory(tests) +endif() Index: compiler-rt/lib/syringe/syringe_rt.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/syringe/syringe_rt.cpp @@ -0,0 +1,57 @@ +//===-- syringe_rt.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Syringe, a dynamic behavior injection system. +// +// APIs for using the Syringe Runtime. +//===----------------------------------------------------------------------===// + +#include +#include + +#include "syringe/injection_data.h" +#include "syringe/syringe_rt.h" +#include "syringe/syringe_rt_cxx.h" + +namespace __syringe { + +std::vector GlobalSyringeData; + +SimpleInjectionData *findImplPointerImpl(fptr_t target) { + auto It = std::find_if(GlobalSyringeData.begin(), GlobalSyringeData.end(), + [target](SimpleInjectionData It) -> bool { + return (void *)It.OrigFunc == (void *)target; + }); + + if (It == GlobalSyringeData.end()) { + return nullptr; + } else { + return &*It; + } +} + +} // end namespace __syringe + +void printSyringeData() { + std::cout << "Syringe Global Data" << std::endl; + for (auto &item : __syringe::GlobalSyringeData) { + std::cout << "Orig Func: " << (void *)item.OrigFunc + << ", Injection Enabled Addr: " << (void *)item.InjectionEnabled + << ", Injection Enabled Value: " + << (*item.InjectionEnabled ? "true" : "false") << std::endl; + } + + std::cout << std::endl; +} + +bool toggleImpl(fptr_t OrigFunc) { return __syringe::toggleImplPtr(OrigFunc); } + +void __syringe_register(void *OrigFunc, bool *InjectionEnabled) { + __syringe::registerInjection((fptr_t)OrigFunc, InjectionEnabled); +} Index: compiler-rt/lib/syringe/tests/CMakeLists.txt =================================================================== --- /dev/null +++ compiler-rt/lib/syringe/tests/CMakeLists.txt @@ -0,0 +1,95 @@ +include_directories(..) + +add_custom_target(SyringeUnitTests) +set_target_properties(SyringeUnitTests PROPERTIES FOLDER "Syringe unittests") + +# Sanity check SYRINGE_ALL_SOURCE_FILES_ABS_PATHS +list(LENGTH SYRINGE_ALL_SOURCE_FILES_ABS_PATHS XASFAP_LENGTH) +if (${XASFAP_LENGTH} EQUAL 0) + message(FATAL_ERROR "SYRINGE_ALL_SOURCE_FILES_ABS_PATHS cannot be empty") +endif() +unset(XASFAP_LENGTH) +foreach (src_file ${SYRINGE_ALL_SOURCE_FILES_ABS_PATHS}) + if (NOT EXISTS "${src_file}") + message(FATAL_ERROR "Source file \"${src_file}\" does not exist") + endif() +endforeach() + +set(SYRINGE_UNITTEST_CFLAGS + ${SYRINGE_CFLAGS} + ${COMPILER_RT_UNITTEST_CFLAGS} + ${COMPILER_RT_GTEST_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib/syringe + -I${COMPILER_RT_SOURCE_DIR}/lib) + +function(add_syringe_lib library) + add_library(${library} STATIC ${ARGN}) + set_target_properties(${library} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + FOLDER "Compiler-RT Runtime tests") +endfunction() + +function(get_syringe_lib_for_arch arch lib) + if(APPLE) + set(tgt_name "RTSyringe.test.osx") + else() + set(tgt_name "RTSyringe.test.${arch}") + endif() + set(${lib} "${tgt_name}" PARENT_SCOPE) +endfunction() + +set(SYRINGE_TEST_ARCH ${SYRINGE_SUPPORTED_ARCH}) +set(SYRINGE_UNITTEST_LINK_FLAGS + ${CMAKE_THREAD_LIBS_INIT} + -l${SANITIZER_CXX_ABI_LIBRARY} + -fsyringe + ) +if (NOT APPLE) + append_list_if(COMPILER_RT_HAS_LIBM -lm SYRINGE_UNITTEST_LINK_FLAGS) + append_list_if(COMPILER_RT_HAS_LIBRT -lrt SYRINGE_UNITTEST_LINK_FLAGS) + append_list_if(COMPILER_RT_HAS_LIBDL -ldl SYRINGE_UNITTEST_LINK_FLAGS) + append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SYRINGE_UNITTEST_LINK_FLAGS) +endif() + +macro(add_syringe_unittest testname) + cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN}) + if(UNIX AND NOT APPLE) + set(CMAKE_DL_LIBS_INIT "") + foreach(arch ${SYRINGE_TEST_ARCH}) + set(TEST_OBJECTS) + get_syringe_lib_for_arch(${arch} SYRINGE_RUNTIME_LIBS) + generate_compiler_rt_tests(TEST_OBJECTS + SyringeUnitTests "${testname}-${arch}-Test" "${arch}" + SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE} + # Note that any change in the implementations will cause all the unit + # tests to be re-built. This is by design, but may be cumbersome during + # the build/test cycle. + COMPILE_DEPS ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE} + ${SYRINGE_HEADERS} ${SYRINGE_ALL_SOURCE_FILES_ABS_PATHS} + RUNTIME "${SYRINGE_RUNTIME_LIBS}" + DEPS gtest syringe llvm-syringe + CFLAGS ${SYRINGE_UNITTEST_CFLAGS} + LINK_FLAGS ${TARGET_LINK_FLAGS} ${SYRINGE_UNITTEST_LINK_FLAGS}) + set_target_properties(SyringeUnitTests + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endforeach() + endif() +endmacro() + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + if (APPLE) + add_syringe_lib("RTSyringe.test.osx" + $ + $ + $) + else() + foreach(arch ${SYRINGE_SUPPORTED_ARCH}) + add_syringe_lib("RTSyringe.test.${arch}" + $ + $ + $) + endforeach() + endif() + add_subdirectory(unit) +endif() Index: compiler-rt/lib/syringe/tests/unit/CMakeLists.txt =================================================================== --- /dev/null +++ compiler-rt/lib/syringe/tests/unit/CMakeLists.txt @@ -0,0 +1,2 @@ +add_syringe_unittest(SyringeRegistrationTest SOURCES + syringe_registration_test.cc syringe_unit_test_main.cc) Index: compiler-rt/lib/syringe/tests/unit/syringe_registration_test.cc =================================================================== --- /dev/null +++ compiler-rt/lib/syringe/tests/unit/syringe_registration_test.cc @@ -0,0 +1,38 @@ +//===-- syringe_registration_test.cc +//-----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Syringe, a behavior injection system. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +#include "syringe/syringe_rt.h" +#include "syringe/syringe_rt_cxx.h" +namespace { +[[clang::syringe_injeciton_site]] int foo() { return 1; } +[[clang::syringe_payload("_Z3fooi")]] int bar() { return 0; } +} // namespace + +namespace __syringe { + +TEST(RegistrationTest, Simple) { + __syringe_register(nullptr, nullptr); + auto data_ptr = findImplPtr(nullptr); + ASSERT_NE(nullptr, data_ptr); + ASSERT_EQ(nullptr, data_ptr->ImplPtr); + ASSERT_GT(GlobalSyringeData.size(), 1); +} + +TEST(RegistrationTest, NoDoubleInsert) { + __syringe_register(nullptr, nullptr); + ASSERT_DEATH(__syringe_register(nullptr, nullptr), "Cannot register two payloads for the same injection site!"); +} + +} // namespace __syringe Index: compiler-rt/lib/syringe/tests/unit/syringe_unit_test_main.cc =================================================================== --- /dev/null +++ compiler-rt/lib/syringe/tests/unit/syringe_unit_test_main.cc @@ -0,0 +1,18 @@ +//===-- syringe_unit_test_main.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Syringe, a behavior injection system. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} Index: llvm/include/llvm-c/Initialization.h =================================================================== --- llvm/include/llvm-c/Initialization.h +++ llvm/include/llvm-c/Initialization.h @@ -44,6 +44,7 @@ void LLVMInitializeIPA(LLVMPassRegistryRef R); void LLVMInitializeCodeGen(LLVMPassRegistryRef R); void LLVMInitializeTarget(LLVMPassRegistryRef R); +void LLVMInitializeSyringe(LLVMPassRegistryRef R); /** * @} Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -591,6 +591,8 @@ ATTR_KIND_NOCF_CHECK = 56, ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_SHADOWCALLSTACK = 58, + ATTR_KIND_SYRINGE_SITE = 59, + ATTR_KIND_SYRINGE_PAYLOAD = 60, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -400,6 +400,7 @@ void initializeWriteBitcodePassPass(PassRegistry&); void initializeWriteThinLTOBitcodePass(PassRegistry&); void initializeXRayInstrumentationPass(PassRegistry&); +void initializeSyringeLegacyPassPass(PassRegistry&); } // end namespace llvm Index: llvm/include/llvm/LinkAllPasses.h =================================================================== --- llvm/include/llvm/LinkAllPasses.h +++ llvm/include/llvm/LinkAllPasses.h @@ -42,6 +42,7 @@ #include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/Syringe.h" #include "llvm/Transforms/IPO/FunctionAttrs.h" #include "llvm/Transforms/InstCombine/InstCombine.h" #include "llvm/Transforms/Instrumentation.h" Index: llvm/include/llvm/SyringeRecord.h =================================================================== --- /dev/null +++ llvm/include/llvm/SyringeRecord.h @@ -0,0 +1,47 @@ +//===- SyringeRecord.h - Syringe Trace Record +//-----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file replicates the record definition for Syringe instrumentation. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SYRINGE_SYRINGE_RECORD_H +#define LLVM_SYRINGE_SYRINGE_RECORD_H + +#include +#include +#include + +namespace llvm { +namespace syringe { + +enum class SyringeTrigger { NONE, NEVER, ALWAYS, COUNT, ONCE }; + +struct SyringeTarget { + std::string Name; + SyringeTrigger EnableTrigger; + SyringeTrigger DisableTrigger; + uint32_t Count = 0; +}; + +struct SyringePayload { + std::string Name; + std::string Target; +}; + +struct SyringeRecord { + std::string Filename; + std::vector Targets; + std::vector Payloads; +}; + +} // namespace syringe +} // namespace llvm + +#endif // LLVM_SYRINGE_SYRINGE_RECORD_H Index: llvm/include/llvm/Transforms/Syringe.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/Syringe.h @@ -0,0 +1,63 @@ +//===- Transforms/Syringe.h - Syringe passes --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines constructor functions for Syringe passes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SYRINGE_H +#define LLVM_TRANSFORMS_SYRINGE_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/YAMLSyringeRecord.h" + +namespace llvm { +class ModulePass; + +/// Pass to insert Syringe Injection sites. +class SyringePass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +ModulePass *createSyringe(); + +void initializeSyringeLegacyPass(PassRegistry &Registry); + +class SyringeLegacyPass : public ModulePass { +public: + /// pass identification + static char ID; + + SyringeLegacyPass(); + virtual ~SyringeLegacyPass() = default; + + /// Specify pass name for debug output + StringRef getPassName() const override; + + /// run module pass + bool runOnModule(Module &M) override; + + /// create funciton stub for behavior injection + bool doBehaviorInjectionForModule(Module &M); + + bool parse(const std::string &MapFile); + void modifyFunctionBody(Module& M, Function *Fn, Function* Payload, Value* SyringeBool); + +private: + /* data */ + std::vector Metadata; + bool parse(std::unique_ptr &MapFile); +}; +} // namespace llvm + +#endif // Include Guard Index: llvm/include/llvm/YAMLSyringeRecord.h =================================================================== --- /dev/null +++ llvm/include/llvm/YAMLSyringeRecord.h @@ -0,0 +1,91 @@ +//===- YAMLXRayRecord.h - XRay Record YAML Support Definitions ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Types and traits specialisations for YAML I/O of XRay log entries. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SYRINGE_YAML_SYRINGE_RECORD_H +#define LLVM_SYRINGE_YAML_SYRINGE_RECORD_H + +#include + +#include "llvm/Support/YAMLTraits.h" +#include "llvm/SyringeRecord.h" + +namespace llvm { +namespace syringe { + +struct YAMLSyringeTarget { + std::string Name; + SyringeTrigger EnableTrigger; + SyringeTrigger DisableTrigger; + uint32_t Count = 0; +}; + +struct YAMLSyringePayload { + std::string Name; + std::string Target; +}; + +struct YAMLSyringeRecord { + std::string Filename; + std::vector Targets; + std::vector Payloads; +}; + +} // namespace syringe + +namespace yaml { + +template struct MappingTraits; + +// YAML Traits +// ----------- +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, syringe::SyringeTrigger &Type) { + IO.enumCase(Type, "NONE", syringe::SyringeTrigger::NONE); + IO.enumCase(Type, "NEVER", syringe::SyringeTrigger::NEVER); + IO.enumCase(Type, "ALWAYS", syringe::SyringeTrigger::ALWAYS); + IO.enumCase(Type, "COUNT", syringe::SyringeTrigger::COUNT); + IO.enumCase(Type, "ONCE", syringe::SyringeTrigger::ONCE); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, syringe::YAMLSyringeTarget &Target) { + IO.mapRequired("name", Target.Name); + IO.mapRequired("enable", Target.EnableTrigger); + IO.mapRequired("disable", Target.DisableTrigger); + IO.mapOptional("count", Target.Count); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, syringe::YAMLSyringePayload &Payload) { + IO.mapRequired("name", Payload.Name); + IO.mapRequired("target", Payload.Target); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, syringe::YAMLSyringeRecord &Record) { + IO.mapRequired("filename", Record.Filename); + IO.mapOptional("targets", Record.Targets); + IO.mapOptional("payloads", Record.Payloads); + } +}; + +} // namespace yaml +} // namespace llvm + +LLVM_YAML_IS_SEQUENCE_VECTOR(syringe::YAMLSyringeTarget) +LLVM_YAML_IS_SEQUENCE_VECTOR(syringe::YAMLSyringePayload) +LLVM_YAML_IS_SEQUENCE_VECTOR(syringe::YAMLSyringeRecord) + +#endif // LLVM_syringe_YAML_syringe_RECORD_H Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -628,6 +628,8 @@ KEYWORD(attributes); KEYWORD(alwaysinline); + KEYWORD(syringe_site); + KEYWORD(syringe_payload); KEYWORD(allocsize); KEYWORD(argmemonly); KEYWORD(builtin); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1228,6 +1228,7 @@ B.addAllocSizeAttr(ElemSizeArg, NumElemsArg); continue; } + case lltok::kw_alwaysinline: B.addAttribute(Attribute::AlwaysInline); break; case lltok::kw_argmemonly: B.addAttribute(Attribute::ArgMemOnly); break; case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break; Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -171,6 +171,8 @@ kw_attributes, kw_allocsize, kw_alwaysinline, + kw_syringe_site, + kw_syringe_payload, kw_argmemonly, kw_sanitize_address, kw_sanitize_hwaddress, Index: llvm/lib/Passes/LLVMBuild.txt =================================================================== --- llvm/lib/Passes/LLVMBuild.txt +++ llvm/lib/Passes/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Library name = Passes parent = Libraries -required_libraries = AggressiveInstCombine Analysis CodeGen Core IPO InstCombine Scalar Support Target TransformUtils Vectorize Instrumentation +required_libraries = AggressiveInstCombine Analysis CodeGen Core IPO InstCombine Scalar Support Target TransformUtils Vectorize Instrumentation Syringe Index: llvm/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/lib/Passes/PassBuilder.cpp +++ llvm/lib/Passes/PassBuilder.cpp @@ -62,6 +62,7 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" #include "llvm/Transforms/Instrumentation/CGProfile.h" +#include "llvm/Transforms/Syringe.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/Transforms/IPO/CalledValuePropagation.h" Index: llvm/lib/Passes/PassRegistry.def =================================================================== --- llvm/lib/Passes/PassRegistry.def +++ llvm/lib/Passes/PassRegistry.def @@ -39,6 +39,7 @@ #define MODULE_PASS(NAME, CREATE_PASS) #endif MODULE_PASS("always-inline", AlwaysInlinerPass()) +MODULE_PASS("syringe", SyringePass()) MODULE_PASS("called-value-propagation", CalledValuePropagationPass()) MODULE_PASS("cg-profile", CGProfilePass()) MODULE_PASS("constmerge", ConstantMergePass()) Index: llvm/lib/Transforms/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/CMakeLists.txt +++ llvm/lib/Transforms/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(Hello) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) +add_subdirectory(Syringe) Index: llvm/lib/Transforms/LLVMBuild.txt =================================================================== --- llvm/lib/Transforms/LLVMBuild.txt +++ llvm/lib/Transforms/LLVMBuild.txt @@ -16,7 +16,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC +subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC Syringe [component_0] type = Group Index: llvm/lib/Transforms/Syringe/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/Transforms/Syringe/CMakeLists.txt @@ -0,0 +1,9 @@ +add_llvm_library(LLVMSyringe + Syringe.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Syringe + + DEPENDS + intrinsics_gen + ) Index: llvm/lib/Transforms/Syringe/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/lib/Transforms/Syringe/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/Transforms/Instrumentation/LLVMBuild.txt -----------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = Syringe +parent = Transforms +required_libraries = Analysis Core MC Support TransformUtils ProfileData OrcJIT Instrumentation Index: llvm/lib/Transforms/Syringe/Syringe.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Syringe/Syringe.cpp @@ -0,0 +1,438 @@ +//===-- Syringe.cpp - Syringe Infrastructure ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the common initialization infrastructure for the +// Syringe library. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Syringe.h" +#include "llvm-c/Initialization.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalIFunc.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/SyringeRecord.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h" + +using namespace llvm; + +// Strings for Syringe names and Suffix +static const char *const SyringeModuleCtorName = "syringe.module_ctor"; +static const char *const SyringeInitName = "__syringe_register"; +static const char *const SyringeDetourImplSuffix = "$detour_impl"; +static const char *const SyringeBoolSuffix = "$syringe_bool"; + +namespace { + +// contains data used for registratin function +struct SyringeInitData { + // the address of the original function, serves as key for runtime lookups + Function *Target; + // the address of the new stub impl (the moved body of the origianl function) + Function *Stub; + // the address of the function whose behavior we wish to inject + Function *Detour; + // the global function pointer used in the stub for indirect calls + GlobalValue *SyringePtr; +}; + +struct SimpleSyringeInitData { + // the address of the original function, serves as key for runtime lookups + Function *Target; + // the address of the new stub impl (the moved body of the origianl function) + GlobalVariable *SyringeBool; +}; + +} // namespace + +// create a single ctor function for the module, whose body should consist of a +// series of calls to the registration function. One call per injection site. +// We create the function, add a basic block to it, and then insert calls to the +// registration function for each injection site in the module. +// +// example ctor function: +// +// void ctor_func_name(){ +// register(original_func, orig_func_bool); +// register(original_func2, orig_func_bool2); +// .... +// } +// +static void createCtorInit(Module &M, + SmallVector &InitData) { + + // create a ctor to register all injection sites + Function *Ctor = Function::Create( + FunctionType::get(Type::getVoidTy(M.getContext()), false), + GlobalValue::InternalLinkage, SyringeModuleCtorName + M.getName(), &M); + BasicBlock *CtorBB = BasicBlock::Create(M.getContext(), "", Ctor); + IRBuilder<> IRB(ReturnInst::Create(M.getContext(), CtorBB)); + + for (auto SID : InitData) { + auto Target = SID.Target; + auto Flag = SID.SyringeBool; + + // target function types + auto FuncTy = Target->getType(); + auto BoolTy = llvm::Type::getInt8PtrTy(M.getContext()); + // auto BoolTyPtr = static_cast(BoolTy); + + // the parameter types of the registration function + // Type *ParamTypes[] = {FuncTy, BoolTyPtr}; + Type *ParamTypes[] = {FuncTy, BoolTy}; + auto ParamTypesRef = makeArrayRef(ParamTypes, 2); + + // actual parameters + Value *ParamArgs[] = {Target, Flag}; + auto ParamArgsRef = makeArrayRef(ParamArgs, 2); + + // assert that these are the same size incase we are ever change the + // implementation + assert(ParamArgsRef.size() == ParamTypesRef.size()); + + // void return type + auto RetTy = Type::getVoidTy(M.getContext()); + + // the type of the registration function + auto RegFnTy = FunctionType::get(RetTy, ParamTypesRef, false); + + // create or find the registration function + auto ConstFn = M.getOrInsertFunction(SyringeInitName, RegFnTy); + auto RegFunc = M.getFunction(SyringeInitName); + + // set its linkage + RegFunc->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage); + + // add a call to the registration function w/ our target arguments + IRB.CreateCall(ConstFn, ParamArgsRef); + } + + // FIXME: the magic constant is in parts of clang as well, should we move to a + // const int? follow Clang's example for setting priority for global ctors + appendToGlobalCtors(M, Ctor, 65535); +} + +// Naming APIs + +static std::string createSuffixedName(StringRef BaseName, StringRef Suffix) { + return (BaseName + Suffix).str(); +} + +static std::string createBoolNameFromBase(StringRef BaseName) { + return createSuffixedName(BaseName, SyringeBoolSuffix); +} + +static std::string createAliasNameFromBase(StringRef BaseName) { + return createSuffixedName(BaseName, SyringeDetourImplSuffix); +} + +bool SyringeLegacyPass::parse(std::unique_ptr &MapFile) { + bool status = false; + yaml::Input yin(MapFile->getBuffer()); + + yin >> Metadata; + if (yin.error()) + return false; + + if (!Metadata.empty()) + status = true; + + for (syringe::YAMLSyringeRecord Rec : Metadata) { + Metadata.push_back(Rec); + } + return status; +} + +bool SyringeLegacyPass::parse(const std::string &MapFile) { + ErrorOr> Mapping = + MemoryBuffer::getFile(MapFile); + + if (!Mapping) + report_fatal_error("unable to read rewrite map '" + MapFile + + "': " + Mapping.getError().message()); + + if (!parse(*Mapping)) + report_fatal_error("unable to parse rewrite map '" + MapFile + "'"); + + return true; +} + +// Module APIs + +// check if the module needs the Syringe Pass +static bool doInjectionForModule(Module &M) { + for (Function &F : M) { + if (F.hasFnAttribute("syringe-payload") || + F.hasFnAttribute("syringe-injection-site")) { + return true; + } + } + return false; +} + +// run the syringe pass +PreservedAnalyses SyringePass::run(Module &M, ModuleAnalysisManager &AM) { + if (!doInjectionForModule(M)) + return PreservedAnalyses::all(); + + return PreservedAnalyses::none(); +} + +/// initializeSyringe - Initialize all passes in the Syringe library. +void initializeSyringeLegacyPass(PassRegistry &Registry) { + initializeSyringeLegacyPassPass(Registry); +} + +/// LLVMInitializeSyringe - C binding for initializeSyringe. +void LLVMInitializeSyringe(LLVMPassRegistryRef R) { + initializeSyringeLegacyPassPass(*unwrap(R)); +} + +SyringeLegacyPass::SyringeLegacyPass() : ModulePass(ID) {} + +/// Specify pass name for debug output +StringRef SyringeLegacyPass::getPassName() const { + return "Syringe Instrumentation"; +} + +/// run module pass +bool SyringeLegacyPass::runOnModule(Module &M) { + if (skipModule(M)) { + return false; + } + + // If there is a config file, go through it and annotate Syringe functions + for (auto &Rec : Metadata) { + if (M.getName() == Rec.Filename) { + // Annotate targets + for (auto &Target : Rec.Targets) { + auto F = M.getFunction(Target.Name); + if (F != nullptr) { + F->addFnAttr("syringe-injection-site"); + break; + } + } + + // Annotate payloads + for (auto &Payload : Rec.Payloads) { + auto F = M.getFunction(Payload.Name); + if (F != nullptr) { + F->addFnAttr("syringe-payload"); + F->addFnAttr("syringe-target-function", Payload.Target); + } + } + } + } + return doBehaviorInjectionForModule(M); +} + +// Note: this code is from lib/Transforms/IPO/MergeFunctions.cpp +// -- This may be better to just link in, or refactor into a utility lib +// +// Helper for writeThunk, +// Selects proper bitcast operation, +// but a bit simpler then CastInst::getCastOpcode. +static Value *createCast(IRBuilder<> &Builder, Value *V, Type *DestTy) { + Type *SrcTy = V->getType(); + if (SrcTy->isStructTy()) { + assert(DestTy->isStructTy()); + assert(SrcTy->getStructNumElements() == DestTy->getStructNumElements()); + Value *Result = UndefValue::get(DestTy); + for (unsigned int I = 0, E = SrcTy->getStructNumElements(); I < E; ++I) { + Value *Element = + createCast(Builder, Builder.CreateExtractValue(V, makeArrayRef(I)), + DestTy->getStructElementType(I)); + + Result = Builder.CreateInsertValue(Result, Element, makeArrayRef(I)); + } + return Result; + } + assert(!DestTy->isStructTy()); + if (SrcTy->isIntegerTy() && DestTy->isPointerTy()) + return Builder.CreateIntToPtr(V, DestTy); + else if (SrcTy->isPointerTy() && DestTy->isIntegerTy()) + return Builder.CreatePtrToInt(V, DestTy); + else + return Builder.CreateBitCast(V, DestTy); +} + +/// Adds instrumentation to change target of direct dispatch +// Transforms the function so that it has this general form +// +// if(_syringe_boolean == true) +// tail call __syringe__detourFunction +// else +// original funciton body +// +void SyringeLegacyPass::modifyFunctionBody(Module &M, Function *Fn, + Function *Payload, + Value *SyringeBool) { + // cache ref to coontext + auto &Context = Fn->getContext(); + auto EntryBB = &Fn->getEntryBlock(); + + BasicBlock *CmpBB = + BasicBlock::Create(Context, "syringe_branch", Fn, EntryBB); + BasicBlock *InjectBB = BasicBlock::Create(Context, "syringe_inject", Fn); + + // Insert call to the injected payload in the then block + IRBuilder<> ThenIRB(InjectBB); + + // get the args in a form we can pass to createcall + SmallVector ArgsVec; + for (auto &item : Fn->args()) { + // for class members, make sure that we bitcast the this pointer + // this is ensured for us by construction when annotating things in the + // frontend + if (&item == Fn->arg_begin()) { + auto bitcast = ThenIRB.CreateBitCast((Value *)&item, + Payload->arg_begin()->getType()); + ArgsVec.push_back(bitcast); + } else { + ArgsVec.push_back((Value *)&item); + } + } + + auto Args = makeArrayRef(ArgsVec); + auto SyringeCallInst = ThenIRB.CreateCall(Payload, Args); + + // make the injected call a tail call, and give it the appropriate return + SyringeCallInst->setTailCall(); + ReturnInst *RI = nullptr; + if (Fn->getReturnType()->isVoidTy()) { + RI = ThenIRB.CreateRetVoid(); + } else { + RI = ThenIRB.CreateRet( + createCast(ThenIRB, SyringeCallInst, Fn->getReturnType())); + } + + // the Syringe bool controls the injected behavior + // The logic is + // if true + // do tail call to injected function + // else + // normal funciton body + IRBuilder<> IRB(CmpBB); + auto loadInst = + IRB.CreateLoad(SyringeBool, createBoolNameFromBase(Fn->getName())); + auto trunc = IRB.CreateTrunc(loadInst, llvm::Type::getInt1Ty(Context)); + IRB.CreateCondBr(trunc, InjectBB, EntryBB); +} + +/// create funciton stub for behavior injection +// Algorithm: +// examine the functions in the module. +// First process any payloads +// -- makes processing injection sites in the same translation unit easier +// -- also create any aliases required +// Next process the injection sites +// -- create any declarations needed for the payload/alias +// -- copy the function body of the injeciton site into a new function +// -- replace the target functions's body with a stub that makes an indirect +// tail call through an implementation pointer +// -- record the data required for registering the initialization function +// -- if any changes to the injection site were made, register the init data +// in a ctor +bool SyringeLegacyPass::doBehaviorInjectionForModule(Module &M) { + auto cleanupPass = static_cast( + llvm::createUnifyFunctionExitNodesPass()); + bool ChangedPayload = false; + bool ChangedInjection = false; + SmallVector InitData; + + for (Function &F : M) { + ChangedPayload = true; + if (F.hasFnAttribute("syringe-payload")) { + + if (F.hasFnAttribute("syringe-target-function")) { + auto TargetName = + F.getFnAttribute("syringe-target-function").getValueAsString(); + + // create alias + auto NewAlias = + GlobalAlias::create(createAliasNameFromBase(TargetName), &F); + NewAlias->setVisibility( + GlobalValue::VisibilityTypes::DefaultVisibility); + } + } + } + + for (Function &F : M) { + if (F.hasFnAttribute("syringe-injection-site")) { + ChangedInjection = true; + ValueToValueMapTy VMap; + + auto AliasName = createAliasNameFromBase(F.getName()); + + Function *DetourFunction; + + // lookup the alias from the payload if it is in the same TU + auto *InternalAlias = M.getNamedAlias(AliasName); + + if (InternalAlias == nullptr) { + // create allias if our lookup failed + auto AliasDecl = orc::cloneFunctionDecl(M, F, nullptr); + AliasDecl->setName(AliasName); + AliasDecl->removeFnAttr("syringe-injection-site"); + AliasDecl->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage); + DetourFunction = AliasDecl; + } else { + DetourFunction = dyn_cast(InternalAlias->getAliasee()); + } + + // Initialize the Syringe Boolean to 0 (false) + auto BoolIntializer = ConstantInt::get(M.getContext(), APInt(8, 0)); + auto SyringeBool = new GlobalVariable( + M, BoolIntializer->getType(), false, GlobalValue::ExternalLinkage, + BoolIntializer, createBoolNameFromBase(F.getName()), nullptr, + GlobalValue::NotThreadLocal, 0, false); + // TODO: Default visibility can be removed after we have a builtin or intrinsic + SyringeBool->setVisibility(GlobalValue::DefaultVisibility); + + auto Target = &F; + + // modify function body to make direct call to injected alias + modifyFunctionBody(M, &F, DetourFunction, SyringeBool); + InitData.push_back({Target, SyringeBool}); + + // We've added new control flow and returns, make sure they are patched + cleanupPass->runOnFunction(F); + } + } + + // if we've made a modification to the injection site, add a global ctor + // entry for the function + if (ChangedInjection) { + createCtorInit(M, InitData); + } + + return ChangedPayload || ChangedInjection; +} + +ModulePass *llvm::createSyringe() { return new SyringeLegacyPass(); } +char SyringeLegacyPass::ID; + +INITIALIZE_PASS(SyringeLegacyPass, "syringe", + "Syringe: dynamic behavior injection.", false, false) Index: llvm/tools/opt/CMakeLists.txt =================================================================== --- llvm/tools/opt/CMakeLists.txt +++ llvm/tools/opt/CMakeLists.txt @@ -16,6 +16,7 @@ Support Target TransformUtils + Syringe Vectorize Passes ) Index: llvm/tools/opt/LLVMBuild.txt =================================================================== --- llvm/tools/opt/LLVMBuild.txt +++ llvm/tools/opt/LLVMBuild.txt @@ -30,4 +30,5 @@ Scalar ObjCARC Passes + Syringe all-targets Index: llvm/tools/opt/opt.cpp =================================================================== --- llvm/tools/opt/opt.cpp +++ llvm/tools/opt/opt.cpp @@ -53,6 +53,7 @@ #include "llvm/Support/YAMLTraits.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/Coroutines.h" +#include "llvm/Transforms/Syringe.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -448,6 +449,7 @@ initializeInstCombine(Registry); initializeAggressiveInstCombine(Registry); initializeInstrumentation(Registry); + initializeSyringeLegacyPassPass(Registry); initializeTarget(Registry); // For codegen passes, only passes that do IR to IR transformation are // supported.