From 59a75c2d873031b1ad8b1d1a711dc0ed5c15e84b Mon Sep 17 00:00:00 2001 From: Chris Ventura <45495992+nrcventura@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:06:01 -0700 Subject: [PATCH] chore: Reduce duplicate methods for handling .net framework and .net/.net core injection (#2017) --- .../Profiler/ModuleInjector/IModule.h | 17 +- .../Profiler/ModuleInjector/ModuleInjector.h | 225 +++--------------- .../Profiler/CorProfilerCallbackImpl.h | 25 +- src/Agent/NewRelic/Profiler/Profiler/Module.h | 99 ++++---- 4 files changed, 93 insertions(+), 273 deletions(-) diff --git a/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h b/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h index 09bca5e29d..2ff8063793 100644 --- a/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h +++ b/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h @@ -23,21 +23,12 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector virtual xstring_t GetModuleName() = 0; virtual void InjectPlatformInvoke(const xstring_t& methodName, const xstring_t& className, const xstring_t& moduleName, const ByteVector& signature) = 0; virtual void InjectStaticSecuritySafeMethod(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) = 0; - virtual void InjectMscorlibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) = 0; - virtual void InjectSystemPrivateCoreLibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) = 0; + virtual void InjectCoreLibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) = 0; virtual void InjectNRHelperType() = 0; + virtual bool InjectReferenceToCoreLib() = 0; - virtual bool GetHasRefMscorlib() = 0; - virtual bool GetHasRefSysRuntime() = 0; - virtual bool GetHasRefNetStandard() = 0; - virtual bool GetHasRefSystemPrivateCoreLib() = 0; - - virtual void SetMscorlibAssemblyRef(mdAssembly assemblyRefToken) = 0; - virtual void SetSystemPrivateCoreLibAssemblyRef(mdAssembly assemblyRefToken) = 0; - - virtual bool GetIsThisTheMscorlibAssembly() = 0; - virtual bool GetIsThisTheNetStandardAssembly() = 0; - virtual bool GetIsThisTheSystemPrivateCoreLibAssembly() = 0; + virtual bool NeedsReferenceToCoreLib() = 0; + virtual bool GetIsThisTheCoreLibAssembly() = 0; virtual CComPtr GetMetaDataAssemblyEmit() = 0; diff --git a/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h b/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h index 696cd7fad2..0305c4bbb8 100644 --- a/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h +++ b/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h @@ -8,6 +8,7 @@ #include "../Common/CorStandIn.h" #include "../Common/Strings.h" #include "../Logging/Logger.h" +#include "../Profiler/Exceptions.h" #include "../Sicily/Sicily.h" #include "IModule.h" @@ -30,14 +31,25 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector }; public: - static void InjectIntoModule(IModule& module) + static void InjectIntoModule(IModule& module, const bool isCoreClr) { - //for background on the methodology and rationale, see: - // https://social.msdn.microsoft.com/Forums/en-US/f8bf431a-7a83-4dfb-bbf7-ef23b1e30904/profiling-silverlight-with-securitysafecriticalattributesecuritycriticalattribute-and-injected-il + // When injecting method REFERENCES into an assembly, theses references should have + // the external assembly identifier to System.Private.CoreLib + constexpr std::array methodReferencesToInjectCoreClr{ + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("LoadAssemblyOrThrow"), _X("class [System.Private.CoreLib]System.Reflection.Assembly"), _X("string")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetTypeViaReflectionOrThrow"), _X("class [System.Private.CoreLib]System.Type"), _X("string,string")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodViaReflectionOrThrow"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string,string,string,class [System.Private.CoreLib]System.Type[]")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodFromAppDomainStorage"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodFromAppDomainStorageOrReflectionOrThrow"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string,string,string,string,class [System.Private.CoreLib]System.Type[]")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("StoreMethodInAppDomainStorageOrThrow"), _X("void"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo,string")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("EnsureInitialized"), _X("void"), _X("string")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodInfoFromAgentCache"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string,string,string,string,class [System.Private.CoreLib]System.Type[]")), + ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodCacheLookupMethod"), _X("object"), _X("")) + }; // When injecting method REFERENCES into an assembly, theses references should have // the external assembly identifier to mscorlib - constexpr std::array methodReferencesToInject{ + constexpr std::array methodReferencesToInjectNetFramework{ ManagedMethodToInject(_X("[mscorlib]System.CannotUnloadAppDomainException"), _X("LoadAssemblyOrThrow"), _X("class [mscorlib]System.Reflection.Assembly"), _X("string")), ManagedMethodToInject(_X("[mscorlib]System.CannotUnloadAppDomainException"), _X("GetTypeViaReflectionOrThrow"), _X("class [mscorlib]System.Type"), _X("string,string")), ManagedMethodToInject(_X("[mscorlib]System.CannotUnloadAppDomainException"), _X("GetMethodViaReflectionOrThrow"), _X("class [mscorlib]System.Reflection.MethodInfo"), _X("string,string,string,class [mscorlib]System.Type[]")), @@ -49,100 +61,6 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector ManagedMethodToInject(_X("[mscorlib]System.CannotUnloadAppDomainException"), _X("GetMethodCacheLookupMethod"), _X("object"), _X("")) }; - // When injecting HELPER METHODS into the mscorlib assembly, theses references should be local. - // They cannot reference [mscorlib] since these methods are being rewritten in mscorlib. - constexpr std::array methodImplsToInject { - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("LoadAssemblyOrThrow"), _X("class System.Reflection.Assembly"), _X("string")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetTypeViaReflectionOrThrow"), _X("class System.Type"), _X("string,string")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetMethodViaReflectionOrThrow"), _X("class System.Reflection.MethodInfo"), _X("string,string,string,class System.Type[]")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetMethodFromAppDomainStorage"), _X("class System.Reflection.MethodInfo"), _X("string")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetMethodFromAppDomainStorageOrReflectionOrThrow"), _X("class System.Reflection.MethodInfo"), _X("string,string,string,string,class System.Type[]")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("StoreMethodInAppDomainStorageOrThrow"), _X("void"), _X("class System.Reflection.MethodInfo,string")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("EnsureInitialized"), _X("void"), _X("string")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetMethodInfoFromAgentCache"), _X("class System.Reflection.MethodInfo"), _X("string,string,string,string,class System.Type[]")), - ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetMethodCacheLookupMethod"), _X("object"), _X("")) - }; - - const auto is_mscorlib = module.GetIsThisTheMscorlibAssembly(); - - // If instrumenting mscorlib, use local (to the assembly) references - // otherwise use external references - auto methods = is_mscorlib - ? methodImplsToInject - : methodReferencesToInject; - - if (!is_mscorlib && !EnsureReferenceToMscorlib(module)) - { - LogInfo(L"Unable to inject reference to mscorlib into ", module.GetModuleName(), L". This module will not be instrumented."); - return; - } - - if (is_mscorlib) - { - LogDebug(L"Injecting New Relic helper type into ", module.GetModuleName()); - try - { - module.InjectNRHelperType(); - } - catch (NewRelic::Profiler::Win32Exception&) - { - LogError(L"Failed to inject New Relic helper type into ", module.GetModuleName()); - } - } - - LogDebug(L"Injecting ", ((is_mscorlib) ? L"" : L"references to "), L"helper methods into ", module.GetModuleName()); - - //inject the methods if mscorlib and inject references into all other assemblies. (pointer to member function to select method to call in loop) - const auto workerFunc{ (is_mscorlib) ? &IModule::InjectStaticSecuritySafeMethod : &IModule::InjectMscorlibSecuritySafeMethodReference }; - xstring_t signatum; - signatum.reserve(200); - for (const auto& managedMethod : methods) - { - try - { - //create standard signature string - signatum.assign(managedMethod.ReturnType) - .append(1, _X(' ')).append(managedMethod.TypeName) - .append(_X("::"), 2).append(managedMethod.MethodName) - .append(1, _X('(')).append(managedMethod.ParameterTypes) - .append(1, _X(')')); - auto signature = ToSignature(signatum, module.GetTokenizer()); - - //inject method or references... - (module.*workerFunc)(managedMethod.MethodName, managedMethod.TypeName, signature); - } - catch (NewRelic::Profiler::Win32Exception&) - { - //exception in an error if we are injecting methods, otherwise, just neat to know. - //if is mscorlib, allow the loop to proceed. if not, break out of the loop - if (is_mscorlib) - { - LogError(L"Failed to tokenize method signature: ", signatum, L". Proceeding to next method."); - } - else - { - LogTrace(L"Failed to tokenize method signature: ", signatum, L". Skipping injection of other method references for this module."); - } - } - } - } - - static void InjectIntoModuleCore(IModule& module) - { - // When injecting method REFERENCES into an assembly, theses references should have - // the external assembly identifier to System.Private.CoreLib - constexpr std::array methodReferencesToInject{ - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("LoadAssemblyOrThrow"), _X("class [System.Private.CoreLib]System.Reflection.Assembly"), _X("string")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetTypeViaReflectionOrThrow"), _X("class [System.Private.CoreLib]System.Type"), _X("string,string")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodViaReflectionOrThrow"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string,string,string,class [System.Private.CoreLib]System.Type[]")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodFromAppDomainStorage"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodFromAppDomainStorageOrReflectionOrThrow"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string,string,string,string,class [System.Private.CoreLib]System.Type[]")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("StoreMethodInAppDomainStorageOrThrow"), _X("void"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo,string")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("EnsureInitialized"), _X("void"), _X("string")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodInfoFromAgentCache"), _X("class [System.Private.CoreLib]System.Reflection.MethodInfo"), _X("string,string,string,string,class [System.Private.CoreLib]System.Type[]")), - ManagedMethodToInject(_X("[System.Private.CoreLib]System.CannotUnloadAppDomainException"), _X("GetMethodCacheLookupMethod"), _X("object"), _X("")) - }; - // When injecting HELPER METHODS into the System.Private.CoreLib assembly, theses references should be local. // They cannot reference [System.Private.CoreLib] since these methods are being rewritten in System.Private.CoreLib. constexpr std::array methodImplsToInject{ @@ -157,21 +75,21 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector ManagedMethodToInject(_X("System.CannotUnloadAppDomainException"), _X("GetMethodCacheLookupMethod"), _X("object"), _X("")) }; - const auto is_systemPrivateCoreLib = module.GetIsThisTheSystemPrivateCoreLibAssembly(); + const auto is_coreLib = module.GetIsThisTheCoreLibAssembly(); // If instrumenting System.Private.CoreLib, use local (to the assembly) references // otherwise use external references - auto methods = is_systemPrivateCoreLib + auto methods = is_coreLib ? methodImplsToInject - : methodReferencesToInject; + : (isCoreClr ? methodReferencesToInjectCoreClr : methodReferencesToInjectNetFramework); - if (!is_systemPrivateCoreLib && !EnsureReferenceToSystemPrivateCoreLib(module)) + if (!is_coreLib && !EnsureReferenceToCoreLib(module)) { - LogInfo(L"Unable to inject reference to System.Private.CoreLib into ", module.GetModuleName(), L". This module will not be instrumented."); + LogInfo(L"Unable to inject reference to Core Library into ", module.GetModuleName(), L". This module will not be instrumented."); return; } - if (is_systemPrivateCoreLib) + if (is_coreLib) { LogDebug(L"Injecting New Relic helper type into ", module.GetModuleName()); try @@ -184,10 +102,10 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector } } - LogDebug(L"Injecting ", ((is_systemPrivateCoreLib) ? L"" : L"references to "), L"helper methods into ", module.GetModuleName()); + LogDebug(L"Injecting ", ((is_coreLib) ? L"" : L"references to "), L"helper methods into ", module.GetModuleName()); - //inject the methods if System.Private.CoreLib and inject references into all other assemblies. (pointer to member function to select method to call in loop) - const auto workerFunc{ (is_systemPrivateCoreLib) ? &IModule::InjectStaticSecuritySafeMethod : &IModule::InjectSystemPrivateCoreLibSecuritySafeMethodReference }; + //inject the methods if this is the Core Lib and inject references into all other assemblies. (pointer to member function to select method to call in loop) + const auto workerFunc{ (is_coreLib) ? &IModule::InjectStaticSecuritySafeMethod : &IModule::InjectCoreLibSecuritySafeMethodReference }; xstring_t signatum; signatum.reserve(200); for (const auto& managedMethod : methods) @@ -209,7 +127,7 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector { //exception in an error if we are injecting methods, otherwise, just neat to know. //if is System.Private.CoreLib, allow the loop to proceed. if not, break out of the loop - if (is_systemPrivateCoreLib) + if (is_coreLib) { LogError(L"Failed to tokenize method signature: ", signatum, L". Proceeding to next method."); } @@ -231,99 +149,14 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector return generator.TypeToBytes(type); } - static bool EnsureReferenceToMscorlib(IModule& module) + static bool EnsureReferenceToCoreLib(IModule& module) { - // If this is the NetStandard assembly, it already has a reference to mscorlib, so no need to do anything - // Otherwise if this already has a reference to mscorlib, nothing to do. - if (module.GetIsThisTheNetStandardAssembly() || module.GetHasRefMscorlib()) + if (!module.NeedsReferenceToCoreLib()) { return true; } - try - { - LogDebug(L"Attempting to Inject reference to mscorlib into netstandard Module ", module.GetModuleName()); - - // if the assembly wasn't in the existing references try to define a new one - ASSEMBLYMETADATA amd; - ZeroMemory(&amd, sizeof(amd)); - amd.usMajorVersion = 4; - amd.usMinorVersion = 0; - amd.usBuildNumber = 0; - amd.usRevisionNumber = 0; - - auto metaDataAssemblyEmit = module.GetMetaDataAssemblyEmit(); - mdAssemblyRef assemblyToken; - const BYTE pubToken[] = { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; - - auto injectResult = metaDataAssemblyEmit->DefineAssemblyRef(pubToken, sizeof(pubToken), _X("mscorlib"), &amd, NULL, 0, 0, &assemblyToken); - if (injectResult == S_OK) - { - module.SetMscorlibAssemblyRef(assemblyToken); - LogDebug(L"Attempting to Inject reference to mscorlib into netstandard Module ", module.GetModuleName(), L" - Success: ", assemblyToken); - - return true; - } - else - { - LogDebug(L"Attempting to Inject reference to mscorlib into netstandard Module ", module.GetModuleName(), L" - FAIL: ", injectResult); - - return false; - } - } - catch (NewRelic::Profiler::Win32Exception& ex) - { - LogError(L"Attempting to Inject reference to mscorlib into netstandard Module ", module.GetModuleName(), L" - ERROR: ", ex._message); - return false; - } - } - - static bool EnsureReferenceToSystemPrivateCoreLib(IModule& module) - { - // If this is the NetStandard or mscorelib assembly, it already has a reference to System.Private.CoreLib, so no need to do anything - // Otherwise if this already has a reference to System.Private.CoreLib, nothing to do. - if (module.GetIsThisTheNetStandardAssembly() || module.GetIsThisTheMscorlibAssembly() || module.GetHasRefSystemPrivateCoreLib()) - { - return true; - } - - try - { - LogDebug(L"Attempting to Inject reference to System.Private.CoreLib into Module ", module.GetModuleName()); - - // if the assembly wasn't in the existing references try to define a new one - ASSEMBLYMETADATA amd; - ZeroMemory(&amd, sizeof(amd)); - amd.usMajorVersion = 6; - amd.usMinorVersion = 0; - amd.usBuildNumber = 0; - amd.usRevisionNumber = 0; - - auto metaDataAssemblyEmit = module.GetMetaDataAssemblyEmit(); - mdAssemblyRef assemblyToken; - const BYTE pubToken[] = { 0x7C, 0xEC, 0x85, 0xD7, 0xBE, 0xA7, 0x79, 0x8E }; - - auto injectResult = metaDataAssemblyEmit->DefineAssemblyRef(pubToken, sizeof(pubToken), _X("System.Private.CoreLib"), &amd, NULL, 0, 0, &assemblyToken); - if (injectResult == S_OK) - { - module.SetSystemPrivateCoreLibAssemblyRef(assemblyToken); - LogDebug(L"Attempting to Inject reference to System.Private.CoreLib into Module ", module.GetModuleName(), L" - Success: ", assemblyToken); - - return true; - } - else - { - LogDebug(L"Attempting to Inject reference to System.Private.CoreLib into Module ", module.GetModuleName(), L" - FAIL: ", injectResult); - - return false; - } - } - catch (NewRelic::Profiler::Win32Exception& ex) - { - LogError(L"Attempting to Inject reference to System.Private.CoreLib into Module ", module.GetModuleName(), L" - ERROR: ", ex._message); - return false; - } + return module.InjectReferenceToCoreLib(); } - }; }}} diff --git a/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h b/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h index fc7ba0ea58..f0bb2797e8 100644 --- a/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h +++ b/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h @@ -69,7 +69,6 @@ namespace NewRelic { namespace Profiler { private: std::atomic _referenceCount; - std::shared_ptr _moduleInjector; public: CorProfilerCallbackImpl() @@ -287,14 +286,7 @@ namespace NewRelic { namespace Profiler { try { - if (_isCoreClr) - { - _moduleInjector->InjectIntoModuleCore(*module); - } - else - { - _moduleInjector->InjectIntoModule(*module); - } + ModuleInjector::ModuleInjector::InjectIntoModule(*module, _isCoreClr); } catch (...) { @@ -306,18 +298,6 @@ namespace NewRelic { namespace Profiler { return S_OK; } - - virtual DWORD OverrideEventMask(DWORD eventMask) - { -#ifndef PAL_STDCPP_COMPAT - if (!_isCoreClr) - { - _moduleInjector.reset(new ModuleInjector::ModuleInjector()); - } -#endif - return eventMask; - } - virtual void ConfigureEventMask(IUnknown* pICorProfilerInfoUnk) { if (_isCoreClr) @@ -798,8 +778,7 @@ namespace NewRelic { namespace Profiler { MethodRewriter::CustomInstrumentationBuilder _customInstrumentationBuilder; MethodRewriter::CustomInstrumentation _customInstrumentation; - DWORD _eventMask = OverrideEventMask( - COR_PRF_MONITOR_JIT_COMPILATION | COR_PRF_MONITOR_MODULE_LOADS | COR_PRF_USE_PROFILE_IMAGES | COR_PRF_MONITOR_THREADS | COR_PRF_ENABLE_STACK_SNAPSHOT | COR_PRF_ENABLE_REJIT | (DWORD)COR_PRF_DISABLE_ALL_NGEN_IMAGES); + DWORD _eventMask = COR_PRF_MONITOR_JIT_COMPILATION | COR_PRF_MONITOR_MODULE_LOADS | COR_PRF_USE_PROFILE_IMAGES | COR_PRF_MONITOR_THREADS | COR_PRF_ENABLE_STACK_SNAPSHOT | COR_PRF_ENABLE_REJIT | (DWORD)COR_PRF_DISABLE_ALL_NGEN_IMAGES; xstring_t _productName = _X(""); xstring_t _agentCoreDllPath = _X(""); diff --git a/src/Agent/NewRelic/Profiler/Profiler/Module.h b/src/Agent/NewRelic/Profiler/Profiler/Module.h index 649206aec4..f03a89d660 100644 --- a/src/Agent/NewRelic/Profiler/Profiler/Module.h +++ b/src/Agent/NewRelic/Profiler/Profiler/Module.h @@ -10,6 +10,9 @@ #include "CorTokenizer.h" #include "Win32Helpers.h" +#define SYSTEM_PRIVATE_CORELIB_ASSEMBLYNAME _X("System.Private.CoreLib") +#define MSCORLIB_ASSEMBLYNAME _X("mscorlib") + namespace NewRelic { namespace Profiler { class Module : public ModuleInjector::IModule @@ -56,17 +59,12 @@ namespace NewRelic { namespace Profiler virtual xstring_t GetModuleName() override { return _moduleName; } - virtual bool GetHasRefMscorlib() override { return _hasRefMscorlib; } - virtual bool GetHasRefSysRuntime() override { return _hasRefSysRuntime; } - virtual bool GetHasRefNetStandard() override { return _hasRefNetStandard; } - virtual bool GetHasRefSystemPrivateCoreLib() override { return _hasRefSystemPrivateCoreLib; } - - virtual void SetMscorlibAssemblyRef(mdAssembly assemblyRefToken) override { _mscorlibAssemblyRefToken = assemblyRefToken; } - virtual void SetSystemPrivateCoreLibAssemblyRef(mdAssembly assemblyRefToken) override { _systemPrivateCoreLibAssemblyRefToken = assemblyRefToken; } + virtual bool NeedsReferenceToCoreLib() override + { + return !(_isMscorlib || _isNetStandard || _isSystemPrivateCoreLib || _hasCoreLibReference); + } - virtual bool GetIsThisTheMscorlibAssembly() override { return _isMscorlib; } - virtual bool GetIsThisTheNetStandardAssembly() override { return _isNetStandard; } - virtual bool GetIsThisTheSystemPrivateCoreLibAssembly() override { return _isSystemPrivateCoreLib; } + virtual bool GetIsThisTheCoreLibAssembly() override { return _isCoreLibAssembly; } virtual CComPtr GetMetaDataAssemblyEmit() override{ return _metaDataAssemblyEmit; } @@ -113,16 +111,51 @@ namespace NewRelic { namespace Profiler ThrowOnError(_metaDataEmit->DefineField, nrHelperType, _X("_methodCache"), dataFieldAttributes, dataFieldSignature.data(), (uint32_t)dataFieldSignature.size(), 0, nullptr, 0, &dataFieldToken); } - virtual void InjectMscorlibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) override + virtual void InjectCoreLibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) override { - auto typeReferenceOrDefinitionToken = GetOrCreateTypeReferenceToken(_mscorlibAssemblyRefToken, className); + auto typeReferenceOrDefinitionToken = GetOrCreateTypeReferenceToken(_coreLibAssemblyRefToken, className); GetOrCreateMemberReferenceToken(typeReferenceOrDefinitionToken, methodName, signature); } - virtual void InjectSystemPrivateCoreLibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) override + virtual bool InjectReferenceToCoreLib() { - auto typeReferenceOrDefinitionToken = GetOrCreateTypeReferenceToken(_systemPrivateCoreLibAssemblyRefToken, className); - GetOrCreateMemberReferenceToken(typeReferenceOrDefinitionToken, methodName, signature); + const auto coreLibName = _isCoreClr ? SYSTEM_PRIVATE_CORELIB_ASSEMBLYNAME : MSCORLIB_ASSEMBLYNAME; + constexpr const BYTE pubTokenCoreClr[] = { 0x7C, 0xEC, 0x85, 0xD7, 0xBE, 0xA7, 0x79, 0x8E }; + constexpr const BYTE pubTokenNetFramework[] = { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; + + try + { + LogDebug(L"Attempting to Inject reference to ", coreLibName, L" into Module ", GetModuleName()); + + // if the assembly wasn't in the existing references try to define a new one + ASSEMBLYMETADATA amd; + ZeroMemory(&amd, sizeof(amd)); + amd.usMajorVersion = _isCoreClr ? 6 : 4; + amd.usMinorVersion = 0; + amd.usBuildNumber = 0; + amd.usRevisionNumber = 0; + + auto pubToken = _isCoreClr ? pubTokenCoreClr : pubTokenNetFramework; + + auto injectResult = _metaDataAssemblyEmit->DefineAssemblyRef(pubToken, sizeof(pubToken), coreLibName, &amd, NULL, 0, 0, &_coreLibAssemblyRefToken); + if (injectResult == S_OK) + { + LogDebug(L"Attempting to Inject reference to ", coreLibName, L" into Module ", GetModuleName(), L" - Success: ", _coreLibAssemblyRefToken); + + return true; + } + else + { + LogDebug(L"Attempting to Inject reference to ", coreLibName, L" into Module ", GetModuleName(), L" - FAIL: ", injectResult); + + return false; + } + } + catch (NewRelic::Profiler::Win32Exception& ex) + { + LogError(L"Attempting to Inject reference to ", coreLibName, L" into Module ", GetModuleName(), L" - ERROR: ", ex._message); + return false; + } } virtual sicily::codegen::ITokenizerPtr GetTokenizer() override @@ -139,17 +172,14 @@ namespace NewRelic { namespace Profiler sicily::codegen::ITokenizerPtr _tokenizer; ModuleID _moduleId; xstring_t _moduleName; - mdAssemblyRef _mscorlibAssemblyRefToken; - mdAssemblyRef _systemPrivateCoreLibAssemblyRefToken; + mdAssemblyRef _coreLibAssemblyRefToken; const bool _isCoreClr; - bool _hasRefMscorlib; - bool _hasRefSysRuntime; - bool _hasRefNetStandard; - bool _hasRefSystemPrivateCoreLib; + bool _hasCoreLibReference; bool _isMscorlib; bool _isNetStandard; bool _isSystemPrivateCoreLib; + bool _isCoreLibAssembly; mdMethodDef GetSecuritySafeCriticalConstructorToken() { @@ -235,10 +265,9 @@ namespace NewRelic { namespace Profiler void IdentifyFrameworkAssemblyReferences() { - _hasRefMscorlib = false; - _hasRefNetStandard = false; - _hasRefSysRuntime = false; - _hasRefSystemPrivateCoreLib = false; + _hasCoreLibReference = false; + + const auto assemblyNameToFind = _isCoreClr ? SYSTEM_PRIVATE_CORELIB_ASSEMBLYNAME : MSCORLIB_ASSEMBLYNAME; HCORENUM enumerator = nullptr; mdAssemblyRef assemblyToken; @@ -248,23 +277,10 @@ namespace NewRelic { namespace Profiler { auto foundAssemblyName = GetAssemblyName(assemblyToken); - if (Strings::EndsWith(foundAssemblyName, _X("mscorlib"))) - { - _hasRefMscorlib = true; - _mscorlibAssemblyRefToken = assemblyToken; - } - else if (Strings::EndsWith(foundAssemblyName, _X("netstandard"))) - { - _hasRefNetStandard = true; - } - else if (Strings::EndsWith(foundAssemblyName, _X("System.Runtime"))) - { - _hasRefSysRuntime = true; - } - else if (Strings::EndsWith(foundAssemblyName, _X("System.Private.CoreLib"))) + if (Strings::EndsWith(foundAssemblyName, assemblyNameToFind)) { - _hasRefSystemPrivateCoreLib = true; - _systemPrivateCoreLibAssemblyRefToken = assemblyToken; + _hasCoreLibReference = true; + _coreLibAssemblyRefToken = assemblyToken; } } } @@ -274,6 +290,7 @@ namespace NewRelic { namespace Profiler _isMscorlib = Strings::EndsWith(GetModuleName(), _X("mscorlib.dll")); _isNetStandard = Strings::EndsWith(GetModuleName(), _X("netstandard.dll")); _isSystemPrivateCoreLib = Strings::EndsWith(GetModuleName(), _X("System.Private.CoreLib.dll")); + _isCoreLibAssembly = _isCoreClr ? _isSystemPrivateCoreLib : _isMscorlib; } mdAssemblyRef GetAssemblyReference(const xstring_t& assemblyName)