diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc4da879..8f1cbc04d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,16 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h - Support for [`Npgsql`](https://www.nuget.org/packages/Npgsql/) metrics instrumentation for versions `6.0.0`+. +- In install script, Install-OpenTelemetryCore accepts optional argument RegisterAssembliesInGAC, + which is true by default. When set to false, assemblies would not be installed in GAC. +- In install script, new function added: Register-AssembliesInGAC. It installs OpenTelemetry assemblies + and dependencies in GAC. ### Changed - `otel-dotnet-auto-install.sh` now optionally uses `wget` instead of `curl`, improving compatibility with `mcr.microsoft.com/dotnet/runtime` Alpine images. +- Non-default application domains will be forced to load with LoaderOptimization.SingleDomain #### Dependency updates diff --git a/build/Build.Steps.Windows.cs b/build/Build.Steps.Windows.cs index 41c8500a6..6743580a7 100644 --- a/build/Build.Steps.Windows.cs +++ b/build/Build.Steps.Windows.cs @@ -135,7 +135,7 @@ partial class Build .Executes(() => { var aspNetProject = Solution.GetProjectByName(Projects.Tests.Applications.AspNet); - BuildDockerImage(aspNetProject, "integrated", "classic"); + BuildDockerImage(aspNetProject, "integrated-nogac", "classic-nogac", "integrated", "classic"); var wcfProject = Solution.GetProjectByName(Projects.Tests.Applications.WcfIis); BuildDockerImage(wcfProject); diff --git a/docs/iis-instrumentation.md b/docs/iis-instrumentation.md index b62b7d108..0d7cec66c 100644 --- a/docs/iis-instrumentation.md +++ b/docs/iis-instrumentation.md @@ -99,7 +99,3 @@ application pool. 1. Close all external windows and press 'Apply' in the main 'Configuration Editor' view. 1. Restart your application. - -## Troubleshooting - -See [`troubleshooting.md`](troubleshooting.md#iis---loading-this-assembly-would-produce-a-different-grant-set-from-other-instances). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 41ad6f0b2..9993314f2 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -232,23 +232,3 @@ Sample Diagnostic Output: For resolving runtime store assembly version conflicts, follow the same solution as outlined for [Assembly version conflicts](#assembly-version-conflicts) in this document. - -### IIS - Loading this assembly would produce a different grant set from other instances - -#### Symptoms - -.NET Framework, IIS hosted application crashes and you get an event similar to -the following: - -```log -Exception: System.IO.FileLoadException - -Message: Loading this assembly would produce a different grant set from other instances. -``` - -#### Solution - -Create a new `DWORD` value called `LoaderOptimization` and give it the value `1` -under the `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework`. -It will allows to load different versions of the same application to different domains. -It might increase CPU and memory usage. diff --git a/script-templates/OpenTelemetry.DotNet.Auto.psm1.template b/script-templates/OpenTelemetry.DotNet.Auto.psm1.template index e6b240a47..482ceaa31 100644 --- a/script-templates/OpenTelemetry.DotNet.Auto.psm1.template +++ b/script-templates/OpenTelemetry.DotNet.Auto.psm1.template @@ -295,6 +295,36 @@ function Is-Greater-Version($v1, $v2) { return $false; } +<# + .SYNOPSIS + Installs OpenTelemetry .NET Assemblies in GAC. + .PARAMETER InstallDir + Default: - the default path is Program Files dir. + Install path of the OpenTelemetry .NET Automatic Instrumentation + Possible values: , (Custom path) +#> +function Register-AssembliesInGAC() { + $installDir = Get-Current-InstallDir + + # Register .NET Framework dlls in GAC + [System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null + $publish = New-Object System.EnterpriseServices.Internal.Publish + $dlls = Get-ChildItem -Path $installDir\netfx\ -Filter *.dll -File + for ($i = 0; $i -lt $dlls.Count; $i++) { + $percentageComplete = $i / $dlls.Count * 100 + Write-Progress -Activity "Registering .NET Framework dlls in GAC" ` + -Status "Module $($i+1) out of $($dlls.Count). Installing $($dlls[$i].Name):" ` + -PercentComplete $percentageComplete + + if (Test-AssemblyNotForGAC $dlls[$i].Name) { + continue + } + + $publish.GacInstall($dlls[$i].FullName) + } + Write-Progress -Activity "Registering .NET Framework dlls in GAC" -Status "Ready" -Completed +} + <# .SYNOPSIS Installs OpenTelemetry .NET Automatic Instrumentation. @@ -308,7 +338,9 @@ function Install-OpenTelemetryCore() { [Parameter(Mandatory = $false)] [string]$InstallDir = "", [Parameter(Mandatory = $false)] - [string]$LocalPath + [string]$LocalPath, + [Parameter(Mandatory = $false)] + [bool]$RegisterAssembliesInGAC = $true ) $version = "v{{VERSION}}" @@ -332,23 +364,9 @@ function Install-OpenTelemetryCore() { # OpenTelemetry service locator [System.Environment]::SetEnvironmentVariable($ServiceLocatorVariable, $installDir, [System.EnvironmentVariableTarget]::Machine) - # Register .NET Framework dlls in GAC - [System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null - $publish = New-Object System.EnterpriseServices.Internal.Publish - $dlls = Get-ChildItem -Path $installDir\netfx\ -Filter *.dll -File - for ($i = 0; $i -lt $dlls.Count; $i++) { - $percentageComplete = $i / $dlls.Count * 100 - Write-Progress -Activity "Registering .NET Framework dlls in GAC" ` - -Status "Module $($i+1) out of $($dlls.Count). Installing $($dlls[$i].Name):" ` - -PercentComplete $percentageComplete - - if (Test-AssemblyNotForGAC $dlls[$i].Name) { - continue - } - - $publish.GacInstall($dlls[$i].FullName) + if ($RegisterAssembliesInGAC){ + Register-AssembliesInGAC $installDir } - Write-Progress -Activity "Registering .NET Framework dlls in GAC" -Status "Ready" -Completed } catch { $message = $_ @@ -406,7 +424,9 @@ function Uninstall-OpenTelemetryCore() { function Update-OpenTelemetryCore() { param( [Parameter(Mandatory = $false)] - [bool]$RegisterIIS = $false + [bool]$RegisterIIS = $false, + [Parameter(Mandatory = $false)] + [bool]$RegisterAssembliesInGAC = $true ) $currentInstallVersion = Get-OpenTelemetryInstallVersion @@ -436,7 +456,7 @@ function Update-OpenTelemetryCore() { Remove-Module OpenTelemetry.Dotnet.Auto Import-Module $modulePath - Install-OpenTelemetryCore -InstallDir $installDir + Install-OpenTelemetryCore -InstallDir $installDir -RegisterAssembliesInGAC $$RegisterAssembliesInGAC if ($RegisterIIS) { Register-OpenTelemetryForIIS } @@ -660,3 +680,4 @@ Export-ModuleMember -Function Get-OpenTelemetryInstallDirectory Export-ModuleMember -Function Get-OpenTelemetryInstallVersion Export-ModuleMember -Function Enable-OpenTelemetryForIISAppPool Export-ModuleMember -Function Disable-OpenTelemetryForIISAppPool +Export-ModuleMember -Function Register-AssembliesInGAC diff --git a/src/OpenTelemetry.AutoInstrumentation.Loader/AppConfigUpdater.cs b/src/OpenTelemetry.AutoInstrumentation.Loader/AppConfigUpdater.cs new file mode 100644 index 000000000..937f510aa --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation.Loader/AppConfigUpdater.cs @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#if NETFRAMEWORK +namespace OpenTelemetry.AutoInstrumentation.Loader; + +/// +/// Handles update of config files for non-default AppDomain +/// +internal static class AppConfigUpdater +{ + /// + /// Modify assembly bindings in appDomainSetup. + /// Will be called through reflection when new created. + /// Call done using bytecode modifications for + /// and . + /// + /// appDomainSetup to be updated + public static void ModifyConfig(AppDomainSetup appDomainSetup) + { + appDomainSetup.LoaderOptimization = LoaderOptimization.SingleDomain; + } +} +#endif diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/CMakeLists.txt b/src/OpenTelemetry.AutoInstrumentation.Native/CMakeLists.txt index 41909e402..9184df6de 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/CMakeLists.txt +++ b/src/OpenTelemetry.AutoInstrumentation.Native/CMakeLists.txt @@ -177,6 +177,7 @@ add_library("OpenTelemetry.AutoInstrumentation.Native.static" STATIC il_rewriter_wrapper.cpp il_rewriter.cpp integration.cpp + member_resolver.cpp metadata_builder.cpp miniutf.cpp string.cpp diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/OpenTelemetry.AutoInstrumentation.Native.vcxproj b/src/OpenTelemetry.AutoInstrumentation.Native/OpenTelemetry.AutoInstrumentation.Native.vcxproj index 6953fc939..7b9da91c7 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/OpenTelemetry.AutoInstrumentation.Native.vcxproj +++ b/src/OpenTelemetry.AutoInstrumentation.Native/OpenTelemetry.AutoInstrumentation.Native.vcxproj @@ -200,6 +200,7 @@ + @@ -211,6 +212,7 @@ + @@ -230,6 +232,7 @@ + diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp index c7272162f..a2961fd94 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp +++ b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp @@ -25,11 +25,13 @@ #include "otel_profiler_constants.h" #include "pal.h" #include "resource.h" +#include "signature_builder.h" #include "startup_hook.h" #include "stats.h" #include "util.h" #include "version.h" #include "continuous_profiler.h" +#include "member_resolver.h" #ifdef MACOS #include @@ -665,6 +667,30 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID module_id, HR rejit_handler->SetCorAssemblyProfiler(&corAssemblyProperty); } +#ifdef _WIN32 + if (runtime_information_.is_desktop()) + { + mdTypeDef loader_type = mdTokenNil; + mdMethodDef init_method = mdTokenNil; + mdMethodDef patch_app_domain_setup_method = mdTokenNil; + hr = GenerateLoaderType(module_id, &loader_type, &init_method, &patch_app_domain_setup_method); + + if (FAILED(hr)) + { + Logger::Error("Failed to inject loader type in mscorlib, module id ", module_id, ", result ", hr); + } + else + { + hr = ModifyAppDomainCreate(module_id, patch_app_domain_setup_method); + + if (FAILED(hr)) + { + Logger::Warn("Failed to patch AppDomain creation, module id ", module_id, ", result ", hr); + } + } + } +#endif + return S_OK; } @@ -1291,7 +1317,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::GetAssemblyReferences(const WCHAR* } Logger::Debug("GetAssemblyReferences extending assembly closure for ", assembly_name, " to include ", - asmRefInfo.szName, ". Path=", wszAssemblyPath); + WSTRING(asmRefInfo.szName), ". Path=", WSTRING(wszAssemblyPath)); return S_OK; } @@ -1770,6 +1796,1373 @@ HRESULT CorProfiler::RunAutoInstrumentationLoader(const ComPtr& return S_OK; } +// Add at the start of System.AppDomain::CreateDomain(string,System.Security.Policy.Evidence,System.AppDomainSetup) +// and System.AppDomainManager::CreateDomainHelper(string,System.Security.Policy.Evidence,System.AppDomainSetup) +// call to __DDVoidMethodType__::__DDPatchAppDomainSetup__ passing AppDomainSetup argument by ref there. +// If AppDomainSetup is null, it will be created. Resulting AppDomainSetup will be passed to +// OpenTelemetry.AutoInstrumentation.Loader.AppConfigUpdater::ModifyConfig(System.AppDomainSetup appDomainSetup) +HRESULT CorProfiler::ModifyAppDomainCreate(const ModuleID module_id, mdMethodDef patch_app_domain_setup_method) +{ + // Expects to be called on mscorlib only + // patch_app_domain_setup_method should be pre-injected in mscorlib + ComPtr metadata_interfaces; + auto hr = this->info_->GetModuleMetaData(module_id, ofRead | ofWrite, IID_IMetaDataImport2, + metadata_interfaces.GetAddressOf()); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: failed to get metadata interface for ", module_id); + return hr; + } + + const auto& metadata_import = metadata_interfaces.As(IID_IMetaDataImport); + + mdTypeDef system_app_domain_token; + { + hr = metadata_import->FindTypeDefByName(WStr("System.AppDomain"), mdTokenNil, &system_app_domain_token); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: FindTypeDefByName System.AppDomain failed"); + return hr; + } + } + + mdTypeDef system_app_domain_manager_token; + { + hr = metadata_import->FindTypeDefByName(WStr("System.AppDomainManager"), mdTokenNil, + &system_app_domain_manager_token); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: FindTypeDefByName System.AppDomainManager failed"); + return hr; + } + } + + mdTypeDef system_security_policy_evidence_token; + { + hr = metadata_import->FindTypeDefByName(WStr("System.Security.Policy.Evidence"), mdTokenNil, + &system_security_policy_evidence_token); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: FindTypeDefByName System.Security.Policy.Evidence failed"); + return hr; + } + } + + mdTypeDef system_app_domain_setup_token; + { + hr = metadata_import->FindTypeDefByName(WStr("System.AppDomainSetup"), mdTokenNil, + &system_app_domain_setup_token); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: FindTypeDefByName System.AppDomainSetup failed"); + return hr; + } + } + + mdMethodDef system_app_domain_create_domain_token; + { + SignatureBuilder::StaticMethod + system_app_domain_create_domain_signature{SignatureBuilder::Class(system_app_domain_token), + {SignatureBuilder::BuiltIn::String, + SignatureBuilder::Class(system_security_policy_evidence_token), + SignatureBuilder::Class(system_app_domain_setup_token)}}; + + hr = metadata_import->FindMethod(system_app_domain_token, WStr("CreateDomain"), + system_app_domain_create_domain_signature.Head(), + system_app_domain_create_domain_signature.Size(), + &system_app_domain_create_domain_token); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: FindMethod System.AppDomain::CreateDomain failed"); + return hr; + } + + ILRewriter rewriter(this->info_, nullptr, module_id, system_app_domain_create_domain_token); + hr = rewriter.Import(); + + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: ILRewriter.Import System.AppDomain::CreateDomain failed"); + return hr; + } + + ILInstr* pFirstInstr = rewriter.GetILList()->m_pNext; + ILInstr* pNewInstr = NULL; + + pNewInstr = rewriter.NewILInstr(); + pNewInstr->m_opcode = CEE_LDARGA_S; + pNewInstr->m_Arg8 = 2; + rewriter.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = patch_app_domain_setup_method; + rewriter.InsertBefore(pFirstInstr, pNewInstr); + + hr = rewriter.Export(); + + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: ILRewriter.Export System.AppDomain::CreateDomain failed"); + return hr; + } + + if (IsDumpILRewriteEnabled()) + { + mdToken token = 0; + TypeInfo typeInfo{}; + WSTRING methodName = WStr("CreateDomain"); + FunctionInfo caller(token, methodName, typeInfo, MethodSignature(), FunctionMethodSignature()); + Logger::Info(GetILCodes("*** ModifyAppDomainCreate: Modified Code: ", &rewriter, caller, metadata_import)); + } + } + + mdMethodDef system_app_domain_manager_create_domain_helper_token; + { + SignatureBuilder::StaticMethod system_app_domain_manager_create_domain_helper_signature{ + SignatureBuilder::Class(system_app_domain_token), + {SignatureBuilder::BuiltIn::String, SignatureBuilder::Class(system_security_policy_evidence_token), + SignatureBuilder::Class(system_app_domain_setup_token)}}; + + hr = metadata_import->FindMethod(system_app_domain_manager_token, WStr("CreateDomainHelper"), + system_app_domain_manager_create_domain_helper_signature.Head(), + system_app_domain_manager_create_domain_helper_signature.Size(), + &system_app_domain_manager_create_domain_helper_token); + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: FindMethod System.AppDomainManager::CreateDomainHelper failed"); + return hr; + } + + ILRewriter rewriter(this->info_, nullptr, module_id, system_app_domain_manager_create_domain_helper_token); + hr = rewriter.Import(); + + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: ILRewriter.Import System.AppDomainManager::CreateDomainHelper failed"); + return hr; + } + + ILInstr* pFirstInstr = rewriter.GetILList()->m_pNext; + ILInstr* pNewInstr = NULL; + + pNewInstr = rewriter.NewILInstr(); + pNewInstr->m_opcode = CEE_LDARGA_S; + pNewInstr->m_Arg8 = 2; + rewriter.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = patch_app_domain_setup_method; + rewriter.InsertBefore(pFirstInstr, pNewInstr); + + hr = rewriter.Export(); + + if (FAILED(hr)) + { + Logger::Warn("ModifyAppDomainCreate: ILRewriter.Export System.AppDomainManager::CreateDomainHelper failed"); + return hr; + } + + if (IsDumpILRewriteEnabled()) + { + mdToken token = 0; + TypeInfo typeInfo{}; + WSTRING methodName = WStr("CreateDomainHelper"); + FunctionInfo caller(token, methodName, typeInfo, MethodSignature(), FunctionMethodSignature()); + Logger::Info(GetILCodes("*** ModifyAppDomainCreate: Modified Code: ", &rewriter, caller, metadata_import)); + } + } + + return S_OK; +} + +// clang-format off +// This method will generate new type __DDVoidMethodType__ in target module. +// C# code for created class: +// public static class __DDVoidMethodType__ +// { +// private static System.Reflection.Assembly _assembly; +// private static System.Action _assemblyFixer; +// private static int _isAssemblyLoaded; +// +// static __DDVoidMethodType__() +// { +// GetAssemblyAndSymbolsBytes(out nint assemblyPtr, out int assemblySize, out nint symbolsPtr, +// out int symbolsSize); +// System.Byte[] assemblyBytes = new byte[assemblySize]; +// System.Runtime.InteropServices.Marshal.Copy(assemblyPtr, assemblyBytes, 0, assemblySize); +// System.Byte[] symbolsBytes = new byte[symbolsSize]; +// System.Runtime.InteropServices.Marshal.Copy(symbolsPtr, symbolsBytes, 0, symbolsSize); +// _assembly = System.Reflection.Assembly.Load(assemblyBytes, symbolsBytes); +// _assemblyFixer = (System.Action)_assembly +// .GetType("OpenTelemetry.AutoInstrumentation.Loader.AppConfigUpdater") +// .GetMethod("ModifyConfig") +// .CreateDelegate(typeof(System.Action)); +// } +// +// public static void __DDVoidMethodCall__() +// { +// if (IsAlreadyLoaded()) +// { +// return; +// } +// +// _assembly.CreateInstance("OpenTelemetry.AutoInstrumentation.Loader.Loader"); +// } +// +// public static void __DDPatchAppDomainSetup__(ref System.AppDomainSetup setup) +// { +// if (setup is null) +// { +// setup = new AppDomainSetup(); +// } +// +// _assemblyFixer(setup); +// } +// +// private static bool IsAlreadyLoaded() +// { +// return System.Threading.Interlocked.CompareExchange(ref _isAssemblyLoaded, 1, 0) == 1; +// } +// +// [System.Runtime.InteropServices.DllImport("OpenTelemetry.AutoInstrumentation.Native.dll", PreserveSig = true)] +// private static extern void GetAssemblyAndSymbolsBytes(out nint assemblyPtr, out int assemblySize, out nint symbolsPtr, out int symbolsSize); +// } +// clang-format on +HRESULT CorProfiler::GenerateLoaderType(const ModuleID module_id, + mdTypeDef* loader_type, + mdMethodDef* init_method, + mdMethodDef* patch_app_domain_setup_method) +{ + + const auto& module_info = GetModuleInfo(this->info_, module_id); + if (!module_info.IsValid()) + { + Logger::Warn("GenerateLoaderType: failed to get module info ", module_id); + + return E_FAIL; + } + + ComPtr metadata_interfaces; + auto hr = this->info_->GetModuleMetaData(module_id, ofRead | ofWrite, IID_IMetaDataImport2, + metadata_interfaces.GetAddressOf()); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: failed to get metadata interface for ", module_id); + return hr; + } + + const auto& metadata_import = metadata_interfaces.As(IID_IMetaDataImport); + const auto& metadata_emit = metadata_interfaces.As(IID_IMetaDataEmit); + const auto& assembly_emit = metadata_interfaces.As(IID_IMetaDataAssemblyEmit); + + MemberResolver resolver(metadata_import, metadata_emit); + + mdAssemblyRef corlib_ref = mdTokenNil; + // We need assemblyRef only when we generate type outside of mscorlib + // resolver will handle if we need to use typeRef/methodRef for other mscorlib-defined types + // (if corlib_ref != mdTokenNil), or typeDef/methodDef can be used otherwise + if (module_info.assembly.name != mscorlib_assemblyName) + { + mdAssemblyRef corlib_ref; + hr = GetCorLibAssemblyRef(assembly_emit, corAssemblyProperty, &corlib_ref); + + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: failed to define AssemblyRef to mscorlib"); + return hr; + } + } + + // TypeDef/TypeRef for System.Object + mdToken system_object_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Object"), &system_object_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Object failed"); + return hr; + } + } + + // .class public abstract auto ansi sealed __DDVoidMethodType__ + // extends[mscorlib] System.Object + { + hr = metadata_emit->DefineTypeDef(WStr("__DDVoidMethodType__"), tdAbstract | tdSealed | tdPublic, + system_object_token, NULL, loader_type); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineTypeDef __DDVoidMethodType__ failed"); + return hr; + } + } + + // TypeDef/TypeRef for System.Reflection.Assembly + mdToken system_reflection_assembly_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Reflection.Assembly"), + &system_reflection_assembly_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Reflection.Assembly failed"); + return hr; + } + } + + // .field private static class [mscorlib]System.Reflection.Assembly _assembly + mdFieldDef assembly_field_token = mdFieldDefNil; + { + SignatureBuilder::Field assembly_field_signature{SignatureBuilder::Class{system_reflection_assembly_token}}; + hr = metadata_emit->DefineField(*loader_type, WStr("_assembly"), fdStatic | fdPrivate, + assembly_field_signature.Head(), assembly_field_signature.Size(), 0, nullptr, 0, + &assembly_field_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Failed to define _assembly field"); + return hr; + } + } + + // TypeDef/TypeRef for System.Action`1 + mdToken system_action_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Action`1"), &system_action_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Action`1 failed"); + return hr; + } + } + + // TypeDef/TypeRef for System.AppDomainSetup + mdToken system_app_domain_setup_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.AppDomainSetup"), &system_app_domain_setup_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.AppDomainSetup failed"); + return hr; + } + } + + // .field private static class [mscorlib]System.Action`1 _assemblyFixer + mdFieldDef app_domain_setup_fixer_field_token = mdFieldDefNil; + { + SignatureBuilder::Field app_domain_setup_fixer_signature{ + SignatureBuilder::GenericInstance{SignatureBuilder::Class{system_action_token}, + {SignatureBuilder::Class{system_app_domain_setup_token}}}}; + + hr = + metadata_emit->DefineField(*loader_type, WStr("_assemblyFixer"), fdStatic | fdPrivate, + app_domain_setup_fixer_signature.Head(), app_domain_setup_fixer_signature.Size(), + 0, nullptr, 0, &app_domain_setup_fixer_field_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Failed to define _assemblyFixer field"); + return hr; + } + } + + // .field private static int32 _isAssemblyLoaded + mdFieldDef isAssemblyLoadedFieldToken = mdFieldDefNil; + { + BYTE field_signature[] = {IMAGE_CEE_CS_CALLCONV_FIELD, ELEMENT_TYPE_I4}; + hr = metadata_emit->DefineField(*loader_type, WStr("_isAssemblyLoaded"), fdStatic | fdPrivate, field_signature, + sizeof(field_signature), 0, nullptr, 0, &isAssemblyLoadedFieldToken); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineField _isAssemblyLoaded failed"); + return hr; + } + } + + // .method private hidebysig specialname rtspecialname static + // void .cctor() cil managed + mdMethodDef cctor_token; + { + BYTE cctor_signature[] = { + IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention + 0, // Number of parameters + ELEMENT_TYPE_VOID, // Return type + }; + hr = metadata_emit->DefineMethod(*loader_type, WStr(".cctor"), + mdPrivate | mdHideBySig | mdSpecialName | mdRTSpecialName | mdStatic, + cctor_signature, sizeof(cctor_signature), 0, 0, &cctor_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineMethod .cctor failed"); + return hr; + } + } + + // .method private hidebysig static bool IsAlreadyLoaded() cil managed + mdMethodDef already_loaded_method_token; + { + BYTE already_loaded_signature[] = { + IMAGE_CEE_CS_CALLCONV_DEFAULT, + 0, + ELEMENT_TYPE_BOOLEAN, + }; + hr = metadata_emit->DefineMethod(*loader_type, WStr("IsAlreadyLoaded"), mdPrivate | mdHideBySig | mdStatic, + already_loaded_signature, sizeof(already_loaded_signature), 0, 0, + &already_loaded_method_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineMethod IsAlreadyLoaded failed"); + return hr; + } + } + + // .method public hidebysig static void __DDVoidMethodCall__() cil managed + { + BYTE initialize_signature[] = { + IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention + 0, // Number of parameters + ELEMENT_TYPE_VOID, // Return type + }; + hr = metadata_emit->DefineMethod(*loader_type, WStr("__DDVoidMethodCall__"), mdPublic | mdHideBySig | mdStatic, + initialize_signature, sizeof(initialize_signature), 0, 0, init_method); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineMethod __DDVoidMethodCall__ failed"); + return hr; + } + } + + // .method public hidebysig static void __DDPatchAppDomainSetup__(class [mscorlib]System.AppDomainSetup& setup) cil + // managed + { + SignatureBuilder::StaticMethod + app_domain_setup_fixer_method_signature{SignatureBuilder::BuiltIn::Void, + {SignatureBuilder::ByRef{ + SignatureBuilder::Class{system_app_domain_setup_token}}}}; + + hr = metadata_emit->DefineMethod(*loader_type, WStr("__DDPatchAppDomainSetup__"), + mdPublic | mdHideBySig | mdStatic, + app_domain_setup_fixer_method_signature.Head(), + app_domain_setup_fixer_method_signature.Size(), 0, 0, + patch_app_domain_setup_method); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineMethod __DDPatchAppDomainSetup__ failed"); + return hr; + } + } + + // TypeSpec for System.Action + mdTypeSpec system_action_of_system_app_domain_setup_token; + { + SignatureBuilder::GenericInstance + system_action_of_system_app_domain_setup_signature{SignatureBuilder::Class{system_action_token}, + {SignatureBuilder::Class{ + system_app_domain_setup_token}}}; + hr = metadata_emit->GetTokenFromTypeSpec(system_action_of_system_app_domain_setup_signature.Head(), + system_action_of_system_app_domain_setup_signature.Size(), + &system_action_of_system_app_domain_setup_token); + + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTokenFromTypeSpec System.Action`1 failed"); + return hr; + } + } + + // Define a method on the managed side that will PInvoke into the profiler method: + // C++: void GetAssemblyAndSymbolsBytes(BYTE** pAssemblyArray, int* assemblySize, BYTE** pSymbolsArray, int* + // symbolsSize) + // C#: static extern void GetAssemblyAndSymbolsBytes(out IntPtr assemblyPtr, out int assemblySize, out + // IntPtr symbolsPtr, out int symbolsSize) + // + // .method private hidebysig static pinvokeimpl("OpenTelemetry.AutoInstrumentation.Native.dll" winapi) + // void GetAssemblyAndSymbolsBytes([out] native int& assemblyPtr, + // [out] int32& assemblySize, + // [out] native int& symbolsPtr, + // [out] int32& symbolsSize) + // cil managed preservesig + mdMethodDef pinvoke_method_def; + { + COR_SIGNATURE get_assembly_bytes_signature[] = { + IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention + 4, // Number of parameters + ELEMENT_TYPE_VOID, // Return type + ELEMENT_TYPE_BYREF, // List of parameter types + ELEMENT_TYPE_I, + ELEMENT_TYPE_BYREF, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_BYREF, + ELEMENT_TYPE_I, + ELEMENT_TYPE_BYREF, + ELEMENT_TYPE_I4, + }; + hr = metadata_emit->DefineMethod(*loader_type, WStr("GetAssemblyAndSymbolsBytes"), + mdPrivate | mdHideBySig | mdStatic | mdPinvokeImpl, + get_assembly_bytes_signature, sizeof(get_assembly_bytes_signature), 0, 0, + &pinvoke_method_def); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineMethod GetAssemblyAndSymbolsBytes failed"); + return hr; + } + + metadata_emit->SetMethodImplFlags(pinvoke_method_def, miPreserveSig); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: SetMethodImplFlags failed"); + return hr; + } + + WSTRING native_profiler_file = GetCurrentModuleFileName(); + Logger::Debug("GenerateLoaderType: Setting the PInvoke native profiler library path to ", native_profiler_file); + + mdModuleRef profiler_ref; + hr = metadata_emit->DefineModuleRef(native_profiler_file.c_str(), &profiler_ref); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineModuleRef failed"); + return hr; + } + + hr = metadata_emit->DefinePinvokeMap(pinvoke_method_def, 0, WStr("GetAssemblyAndSymbolsBytes"), profiler_ref); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefinePinvokeMap failed"); + return hr; + } + } + + // Add IL instructions into .cctor + { + // TypeRef/TypeDef for System.Byte + mdToken byte_type_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Byte"), &byte_type_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Byte failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // void [mscorlib]System.Runtime.InteropServices.Marshal::Copy(native int, uint8[], int32, int32) + mdToken marshal_copy_token; + { + // TypeRef/TypeDef for [mscorlib]System.Runtime.InteropServices.Marshal + mdTypeRef marshal_type_token; + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Runtime.InteropServices.Marshal"), + &marshal_type_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineTypeRefByName failed"); + return hr; + } + + COR_SIGNATURE marshal_copy_signature[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention + 4, // Number of parameters + ELEMENT_TYPE_VOID, // Return type + ELEMENT_TYPE_I, // List of parameter types + ELEMENT_TYPE_SZARRAY, + ELEMENT_TYPE_U1, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_I4}; + hr = resolver.GetMemberRefOrDef(marshal_type_token, WStr("Copy"), marshal_copy_signature, + sizeof(marshal_copy_signature), &marshal_copy_token); + if (FAILED(hr)) + { + Logger::Warn( + "GenerateLoaderType: GetMemberRefOrDef System.Runtime.InteropServices.Marshal::Copy failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(uint8[], uint8[]) + mdToken system_reflection_assembly_load_token; + { + SignatureBuilder::StaticMethod + system_reflection_assembly_load_signature{SignatureBuilder::Class{system_reflection_assembly_token}, + {SignatureBuilder::Array{SignatureBuilder::BuiltIn::Byte}, + SignatureBuilder::Array{SignatureBuilder::BuiltIn::Byte}}}; + + hr = resolver.GetMemberRefOrDef(system_reflection_assembly_token, WStr("Load"), + system_reflection_assembly_load_signature.Head(), + system_reflection_assembly_load_signature.Size(), + &system_reflection_assembly_load_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetMemberRefOrDef System.Reflection.Assembly::Load failed"); + return hr; + } + } + + // TypeRef/TypeDef for System.Type + mdToken system_type_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Type"), &system_type_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Type failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) + mdToken system_reflection_assembly_get_type_token; + { + SignatureBuilder::InstanceMethod + system_reflection_assembly_get_type_signature{SignatureBuilder::Class{system_type_token}, + {SignatureBuilder::BuiltIn::String}}; + + hr = resolver.GetMemberRefOrDef(system_reflection_assembly_token, WStr("GetType"), + system_reflection_assembly_get_type_signature.Head(), + system_reflection_assembly_get_type_signature.Size(), + &system_reflection_assembly_get_type_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetMemberRefOrDef System.Reflection.Assembly::GetType failed"); + return hr; + } + } + + // TypeRef/TypeDef for System.Reflection.MethodInfo + mdToken system_reflection_method_info_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Reflection.MethodInfo"), + &system_reflection_method_info_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Reflection.MethodInfo failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // instance class [mscorlib]System.Reflection.MethodInfo [mscorlib]System.Type::GetMethod(string) + mdToken system_type_get_method_token; + { + SignatureBuilder::InstanceMethod system_type_get_method_signature = + {SignatureBuilder::Class{system_reflection_method_info_token}, {SignatureBuilder::BuiltIn::String}}; + + hr = resolver.GetMemberRefOrDef(system_type_token, WStr("GetMethod"), + system_type_get_method_signature.Head(), + system_type_get_method_signature.Size(), &system_type_get_method_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetMemberRefOrDef System.Type::GetMethod(string) failed"); + return hr; + } + } + + // TypeRef/TypeDef for System.RuntimeTypeHandle + mdToken system_runtime_type_handle_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.RuntimeTypeHandle"), + &system_runtime_type_handle_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.RuntimeTypeHandle failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle( + // valuetype [mscorlib]System.RuntimeTypeHandle) + mdMemberRef system_type_get_type_from_handle_token; + { + SignatureBuilder::StaticMethod + system_type_get_type_from_handle_signature{SignatureBuilder::Class{system_type_token}, + {SignatureBuilder::ValueType{ + system_runtime_type_handle_token}}}; + + hr = resolver.GetMemberRefOrDef(system_type_token, WStr("GetTypeFromHandle"), + system_type_get_type_from_handle_signature.Head(), + system_type_get_type_from_handle_signature.Size(), + &system_type_get_type_from_handle_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetMemberRefOrDef System.Type::GetTypeFromHandle failed"); + return hr; + } + } + + // TypeRef/TypeDef for System.Delegate + mdTypeRef system_delegate_token; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Delegate"), &system_delegate_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName System.Delegate failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // instance class [mscorlib]System.Delegate [mscorlib]System.Reflection.MethodInfo::CreateDelegate( + // class [mscorlib]System.Type) + mdMemberRef system_reflection_method_info_create_delegate_token; + { + SignatureBuilder::InstanceMethod + system_reflection_method_info_create_delegate_signature{SignatureBuilder::Class{system_delegate_token}, + {SignatureBuilder::Class{system_type_token}}}; + + hr = resolver.GetMemberRefOrDef(system_reflection_method_info_token, WStr("CreateDelegate"), + system_reflection_method_info_create_delegate_signature.Head(), + system_reflection_method_info_create_delegate_signature.Size(), + &system_reflection_method_info_create_delegate_token); + if (FAILED(hr)) + { + Logger::Warn( + "GenerateLoaderType: GetMemberRefOrDef System.Reflection.MethodInfo::CreateDelegate failed"); + return hr; + } + } + + // Create a string representing "OpenTelemetry.AutoInstrumentation.Loader.AppConfigUpdater" + mdString config_updater_class_name_token; + { + LPCWSTR config_updater_class_name_str = L"OpenTelemetry.AutoInstrumentation.Loader.AppConfigUpdater"; + auto config_updater_class_name_str_size = wcslen(config_updater_class_name_str); + + hr = metadata_emit->DefineUserString(config_updater_class_name_str, + (ULONG)config_updater_class_name_str_size, + &config_updater_class_name_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineUserString " + "OpenTelemetry.AutoInstrumentation.Loader.AppConfigUpdater failed"); + return hr; + } + } + + // Create a string representing "ModifyConfig" + mdString config_updater_method_name_token; + { + LPCWSTR config_updater_method_name_str = L"ModifyConfig"; + auto config_updater_method_name_str_size = wcslen(config_updater_method_name_str); + + hr = metadata_emit->DefineUserString(config_updater_method_name_str, + (ULONG)config_updater_method_name_str_size, + &config_updater_method_name_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineUserString ModifyConfig failed"); + return hr; + } + } + + // Add IL instructions into .cctor + // clang-format off + // .locals init ( + // [0] native int assemblyPtr, + // [1] int32 assemblySize, + // [2] native int symbolsPtr, + // [3] int32 symbolsSize, + // [4] unsigned int8[] assemblyBytes, + // [5] unsigned int8[] symbolsBytes + // ) + // + // IL_0000: ldloca.s assemblyPtr + // IL_0002: ldloca.s assemblySize + // IL_0004: ldloca.s symbolsPtr + // IL_0006: ldloca.s symbolsSize + // IL_0008: call void __DDVoidMethodType__::GetAssemblyAndSymbolsBytes(native int&, int32&, native int&, int32&) + // + // IL_000d: ldloc.1 // assemblySize + // IL_000e: newarr [mscorlib]System.Byte + // IL_0013: stloc.s assemblyBytes + // + // IL_0015: ldloc.0 // assemblyPtr + // IL_0016: ldloc.s assemblyBytes + // IL_0018: ldc.i4.0 + // IL_0019: ldloc.1 // assemblySize + // IL_001a: call void [mscorlib]System.Runtime.InteropServices.Marshal::Copy(native int, unsigned int8[], int32, int32) + // + // IL_001f: ldloc.3 // symbolsSize + // IL_0020: newarr [mscorlib]System.Byte + // IL_0025: stloc.s symbolsBytes + // + // IL_0027: ldloc.2 // symbolsPtr + // IL_0028: ldloc.s symbolsBytes + // IL_002a: ldc.i4.0 + // IL_002b: ldloc.3 // symbolsSize + // IL_002c: call void [mscorlib]System.Runtime.InteropServices.Marshal::Copy(native int, unsigned int8[], int32, int32) + // + // IL_0031: ldloc.s assemblyBytes + // IL_0033: ldloc.s symbolsBytes + // IL_0035: call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(unsigned int8[], unsigned int8[]) + // IL_003a: stsfld class [mscorlib]System.Reflection.Assembly __DDVoidMethodType__::_assembly + // + // IL_003f: ldsfld class [mscorlib]System.Reflection.Assembly __DDVoidMethodType__::_assembly + // IL_0044: ldstr "OpenTelemetry.AutoInstrumentation.Loader.AppConfigUpdater" + // IL_0049: callvirt instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) + // IL_004e: ldstr "ModifyConfig" + // IL_0053: callvirt instance class [mscorlib]System.Reflection.MethodInfo [mscorlib]System.Type::GetMethod(string) + // IL_0058: ldtoken class [mscorlib]System.Action`1 + // IL_005d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + // IL_0062: callvirt instance class [mscorlib]System.Delegate [mscorlib]System.Reflection.MethodInfo::CreateDelegate(class [mscorlib]System.Type) + // IL_0067: castclass class [mscorlib]System.Action`1 + // IL_006c: stsfld class [mscorlib]System.Action`1 __DDVoidMethodType__::_assemblyFixer + // + // IL_0071: ret + // clang-format on + ILRewriter rewriter_void(this->info_, nullptr, module_id, cctor_token); + rewriter_void.InitializeTiny(); + mdSignature locals_signature_token; + { + COR_SIGNATURE locals_signature[] = {IMAGE_CEE_CS_CALLCONV_LOCAL_SIG, // Calling convention + 6, // Number of variables + ELEMENT_TYPE_I, // List of variable types + ELEMENT_TYPE_I4, + ELEMENT_TYPE_I, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_SZARRAY, + ELEMENT_TYPE_U1, + ELEMENT_TYPE_SZARRAY, + ELEMENT_TYPE_U1}; + hr = metadata_emit->GetTokenFromSig(locals_signature, sizeof(locals_signature), &locals_signature_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Unable to generate locals signature. ModuleID=", module_id); + return hr; + } + } + + rewriter_void.SetTkLocalVarSig(locals_signature_token); + + ILInstr* pFirstInstr = rewriter_void.GetILList()->m_pNext; + ILInstr* pNewInstr = NULL; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOCA_S; + pNewInstr->m_Arg32 = 0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOCA_S; + pNewInstr->m_Arg32 = 1; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOCA_S; + pNewInstr->m_Arg32 = 2; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOCA_S; + pNewInstr->m_Arg32 = 3; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = pinvoke_method_def; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_1; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_NEWARR; + pNewInstr->m_Arg32 = byte_type_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_STLOC_S; + pNewInstr->m_Arg8 = 4; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_S; + pNewInstr->m_Arg8 = 4; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDC_I4_0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_1; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = marshal_copy_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_3; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_NEWARR; + pNewInstr->m_Arg32 = byte_type_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_STLOC_S; + pNewInstr->m_Arg8 = 5; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_2; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_S; + pNewInstr->m_Arg8 = 5; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDC_I4_0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_3; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = marshal_copy_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_S; + pNewInstr->m_Arg8 = 4; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDLOC_S; + pNewInstr->m_Arg8 = 5; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = system_reflection_assembly_load_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_STSFLD; + pNewInstr->m_Arg32 = assembly_field_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDSFLD; + pNewInstr->m_Arg32 = assembly_field_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDSTR; + pNewInstr->m_Arg32 = config_updater_class_name_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALLVIRT; + pNewInstr->m_Arg32 = system_reflection_assembly_get_type_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDSTR; + pNewInstr->m_Arg32 = config_updater_method_name_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALLVIRT; + pNewInstr->m_Arg32 = system_type_get_method_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDTOKEN; + pNewInstr->m_Arg32 = system_action_of_system_app_domain_setup_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = system_type_get_type_from_handle_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALLVIRT; + pNewInstr->m_Arg32 = system_reflection_method_info_create_delegate_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CASTCLASS; + pNewInstr->m_Arg32 = system_action_of_system_app_domain_setup_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_STSFLD; + pNewInstr->m_Arg32 = app_domain_setup_fixer_field_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_RET; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + if (IsDumpILRewriteEnabled()) + { + mdToken token = 0; + TypeInfo typeInfo{}; + WSTRING methodName = WStr(".cctor"); + FunctionInfo caller(token, methodName, typeInfo, MethodSignature(), FunctionMethodSignature()); + Logger::Info( + GetILCodes("*** GenerateLoaderType: Modified Code: ", &rewriter_void, caller, metadata_import)); + } + + hr = rewriter_void.Export(); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Call to ILRewriter.Export() failed for ModuleID=", module_id); + return hr; + } + } + + // Add IL instructions into the IsAlreadyLoaded method + { + // MethodRef/MethodDef for + // int32 [mscorlib]System.Threading.Interlocked::CompareExchange(int32&, int32, int32) + mdMemberRef interlocked_compare_member_ref; + { + // TypeRef/TypeDef for System.Threading.Interlocked + mdTypeRef interlocked_type_ref; + { + hr = resolver.GetTypeRefOrDefByName(corlib_ref, WStr("System.Threading.Interlocked"), + &interlocked_type_ref); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetTypeRefOrDefByName iSystem.Threading.Interlocked failed"); + return hr; + } + } + + COR_SIGNATURE interlocked_compare_exchange_signature[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT, + 3, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_BYREF, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_I4}; + + hr = resolver.GetMemberRefOrDef(interlocked_type_ref, WStr("CompareExchange"), + interlocked_compare_exchange_signature, + sizeof(interlocked_compare_exchange_signature), + &interlocked_compare_member_ref); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineMemberRef CompareExchange failed"); + return hr; + } + } + + // clang-format off + // IL_0000: ldsflda int32 __DDVoidMethodType__::_isAssemblyLoaded + // IL_0005: ldc.i4.1 + // IL_0006: ldc.i4.0 + // IL_0007: call int32 [mscorlib]System.Threading.Interlocked::CompareExchange(int32&, int32, int32) + // IL_000c: ldc.i4.1 + // IL_000d: ceq + // IL_000f: ret + // clang-format on + ILRewriter rewriter_already_loaded(this->info_, nullptr, module_id, already_loaded_method_token); + rewriter_already_loaded.InitializeTiny(); + + ILInstr* pALFirstInstr = rewriter_already_loaded.GetILList()->m_pNext; + ILInstr* pALNewInstr = NULL; + + // ldsflda _isAssemblyLoaded : Load the address of the "_isAssemblyLoaded" static var + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_LDSFLDA; + pALNewInstr->m_Arg32 = isAssemblyLoadedFieldToken; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + // ldc.i4.1 : Load the constant 1 (int) to the stack + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_LDC_I4_1; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + // ldc.i4.0 : Load the constant 0 (int) to the stack + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_LDC_I4_0; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + // call int Interlocked.CompareExchange(ref int, int, int) method + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_CALL; + pALNewInstr->m_Arg32 = interlocked_compare_member_ref; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + // ldc.i4.1 : Load the constant 1 (int) to the stack + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_LDC_I4_1; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + // ceq : Compare equality from two values from the stack + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_CEQ; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + // ret : Return the value of the comparison + pALNewInstr = rewriter_already_loaded.NewILInstr(); + pALNewInstr->m_opcode = CEE_RET; + rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); + + hr = rewriter_already_loaded.Export(); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Call to ILRewriter.Export() failed for ModuleID=", module_id); + return hr; + } + } + + // Add IL instructions into __DDVoidMethodCall__ + { + // MethodRef/MethodDef for + // instance object [mscorlib]System.Reflection.Assembly::CreateInstance(string) + mdToken assembly_create_instance_member_ref; + { + COR_SIGNATURE assembly_create_instance_signature[] = {IMAGE_CEE_CS_CALLCONV_HASTHIS, 1, ELEMENT_TYPE_OBJECT, + ELEMENT_TYPE_STRING}; + + hr = resolver.GetMemberRefOrDef(system_reflection_assembly_token, WStr("CreateInstance"), + assembly_create_instance_signature, + sizeof(assembly_create_instance_signature), + &assembly_create_instance_member_ref); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetMemberRefOrDef System.Reflection.Assembly::CreateInstance failed"); + return hr; + } + } + + // Create a string representing "OpenTelemetry.AutoInstrumentation.Loader.Loader" + mdString load_helper_token; + { + LPCWSTR load_helper_str = L"OpenTelemetry.AutoInstrumentation.Loader.Loader"; + auto load_helper_str_size = wcslen(load_helper_str); + + hr = metadata_emit->DefineUserString(load_helper_str, (ULONG)load_helper_str_size, &load_helper_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: DefineUserString failed"); + return hr; + } + } + + // clang-format off + // IL_0000: call bool __DDVoidMethodType__::IsAlreadyLoaded() + // IL_0005: brfalse.s IL_0008 + // + // IL_0007: ret + // + // IL_0008: ldsfld class [mscorlib]System.Reflection.Assembly __DDVoidMethodType__::_assembly + // IL_000d: ldstr "OpenTelemetry.AutoInstrumentation.Loader.Loader" + // IL_0012: callvirt instance object [mscorlib]System.Reflection.Assembly::CreateInstance(string) + // IL_0017: pop + // + // IL_0018: ret + // clang-format on + ILRewriter rewriter_void(this->info_, nullptr, module_id, *init_method); + rewriter_void.InitializeTiny(); + + ILInstr* pFirstInstr = rewriter_void.GetILList()->m_pNext; + ILInstr* pNewInstr = NULL; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALL; + pNewInstr->m_Arg32 = already_loaded_method_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_BRFALSE_S; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + ILInstr* pBranchFalseInstr = pNewInstr; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_RET; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDSFLD; + pNewInstr->m_Arg32 = assembly_field_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + pBranchFalseInstr->m_pTarget = pNewInstr; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDSTR; + pNewInstr->m_Arg32 = load_helper_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALLVIRT; + pNewInstr->m_Arg32 = assembly_create_instance_member_ref; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_POP; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_RET; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + if (IsDumpILRewriteEnabled()) + { + mdToken token = 0; + TypeInfo typeInfo{}; + WSTRING methodName = WStr("__DDVoidMethodCall__"); + FunctionInfo caller(token, methodName, typeInfo, MethodSignature(), FunctionMethodSignature()); + Logger::Info( + GetILCodes("*** GenerateLoaderType: Modified Code: ", &rewriter_void, caller, metadata_import)); + } + + hr = rewriter_void.Export(); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Call to ILRewriter.Export() failed for ModuleID=", module_id); + return hr; + } + } + + // Add IL instructions into __DDPatchAppDomainSetup__ + { + // MethodRef/MethodDef for + // instance void class [mscorlib]System.Action`1::Invoke(!0) + mdToken system_action_of_system_app_domain_setup_invoke_token; + { + COR_SIGNATURE system_action_of_system_app_domain_setup_signature[] = {IMAGE_CEE_CS_CALLCONV_HASTHIS, 1, + ELEMENT_TYPE_VOID, ELEMENT_TYPE_VAR, + 0}; + + hr = resolver.GetMemberRefOrDef(system_action_of_system_app_domain_setup_token, WStr("Invoke"), + system_action_of_system_app_domain_setup_signature, + sizeof(system_action_of_system_app_domain_setup_signature), + &system_action_of_system_app_domain_setup_invoke_token); + if (FAILED(hr)) + { + Logger::Warn( + "GenerateLoaderType: GetMemberRefOrDef System.Action::Invoke failed"); + return hr; + } + } + + // MethodRef/MethodDef for + // instance void [mscorlib]System.AppDomainSetup::.ctor() + mdToken system_app_domain_setup_ctor_token; + { + COR_SIGNATURE system_app_domain_setup_ctor_signature[] = {IMAGE_CEE_CS_CALLCONV_HASTHIS, 0, + ELEMENT_TYPE_VOID}; + + hr = resolver.GetMemberRefOrDef(system_app_domain_setup_token, WStr(".ctor"), + system_app_domain_setup_ctor_signature, + sizeof(system_app_domain_setup_ctor_signature), + &system_app_domain_setup_ctor_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: GetMemberRefOrDef System.AppDomainSetup::.ctor failed"); + return hr; + } + } + + // clang-format off + // IL_0000: ldarg.0 // setup + // IL_0001: ldind.ref + // IL_0002: brtrue.s IL_000b + // + // IL_0004: ldarg.0 // setup + // IL_0005: newobj instance void [mscorlib]System.AppDomainSetup::.ctor() + // IL_000a: stind.ref + // + // IL_000b: ldsfld class [mscorlib]System.Action`1 __DDVoidMethodType__::_assemblyFixer + // IL_0010: ldarg.0 // setup + // IL_0011: ldind.ref + // IL_0012: callvirt instance void class [mscorlib]System.Action`1::Invoke(!0/*class [mscorlib]System.AppDomainSetup*/) + // + // IL_0017: ret + // clang-format on + ILRewriter rewriter_void(this->info_, nullptr, module_id, *patch_app_domain_setup_method); + rewriter_void.InitializeTiny(); + + ILInstr* pFirstInstr = rewriter_void.GetILList()->m_pNext; + ILInstr* pNewInstr = NULL; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDARG_0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDIND_REF; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_BRTRUE_S; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + ILInstr* branch_source = pNewInstr; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDARG_0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_NEWOBJ; + pNewInstr->m_Arg32 = system_app_domain_setup_ctor_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_STIND_REF; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDSFLD; + pNewInstr->m_Arg32 = app_domain_setup_fixer_field_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + branch_source->m_pTarget = pNewInstr; + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDARG_0; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_LDIND_REF; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_CALLVIRT; + pNewInstr->m_Arg32 = system_action_of_system_app_domain_setup_invoke_token; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + pNewInstr = rewriter_void.NewILInstr(); + pNewInstr->m_opcode = CEE_RET; + rewriter_void.InsertBefore(pFirstInstr, pNewInstr); + + if (IsDumpILRewriteEnabled()) + { + mdToken token = 0; + TypeInfo typeInfo{}; + WSTRING methodName = WStr("__DDPatchAppDomainSetup__"); + FunctionInfo caller(token, methodName, typeInfo, MethodSignature(), FunctionMethodSignature()); + Logger::Info( + GetILCodes("*** GenerateLoaderType: Modified Code: ", &rewriter_void, caller, metadata_import)); + } + + hr = rewriter_void.Export(); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderType: Call to ILRewriter.Export() failed for ModuleID=", module_id); + return hr; + } + } + + return S_OK; +} + HRESULT CorProfiler::GenerateLoaderMethod(const ModuleID module_id, mdMethodDef* ret_method_token) { ComPtr metadata_interfaces; @@ -1795,590 +3188,30 @@ HRESULT CorProfiler::GenerateLoaderMethod(const ModuleID module_id, mdMethodDef* return hr; } - // Define a TypeRef for System.Object - mdTypeRef object_type_ref; - hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("System.Object"), &object_type_ref); + // Define a TypeRef for Init Type + mdTypeRef init_type_ref; + hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("__DDVoidMethodType__"), &init_type_ref); if (FAILED(hr)) { - Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName failed"); + Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName __DDVoidMethodType__ failed"); return hr; } - // Define a new TypeDef __DDVoidMethodType__ that extends System.Object - mdTypeDef new_type_def; - hr = metadata_emit->DefineTypeDef(WStr("__DDVoidMethodType__"), tdAbstract | tdSealed, object_type_ref, NULL, - &new_type_def); - if (FAILED(hr)) { - Logger::Warn("GenerateLoaderMethod: DefineTypeDef failed"); - return hr; - } - - // Define a new static method __DDVoidMethodCall__ on the new type that has a void return type and takes no - // arguments - BYTE initialize_signature[] = { - IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention - 0, // Number of parameters - ELEMENT_TYPE_VOID, // Return type - }; - hr = metadata_emit->DefineMethod(new_type_def, WStr("__DDVoidMethodCall__"), mdStatic, initialize_signature, - sizeof(initialize_signature), 0, 0, ret_method_token); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMethod failed"); - return hr; - } - - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Define IsAlreadyLoaded() method - // - - // - // Define a new static method IsAlreadyLoaded on the new type that has a bool return type and takes no arguments; - // - mdMethodDef alreadyLoadedMethodToken; - BYTE already_loaded_signature[] = { - IMAGE_CEE_CS_CALLCONV_DEFAULT, - 0, - ELEMENT_TYPE_BOOLEAN, - }; - hr = metadata_emit->DefineMethod(new_type_def, WStr("IsAlreadyLoaded"), mdStatic | mdPrivate, - already_loaded_signature, sizeof(already_loaded_signature), 0, 0, - &alreadyLoadedMethodToken); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMethod IsAlreadyLoaded failed"); - return hr; - } - - // Define a new static int field _isAssemblyLoaded on the new type. - mdFieldDef isAssemblyLoadedFieldToken = mdFieldDefNil; - BYTE field_signature[] = {IMAGE_CEE_CS_CALLCONV_FIELD, ELEMENT_TYPE_I4}; - hr = metadata_emit->DefineField(new_type_def, WStr("_isAssemblyLoaded"), fdStatic | fdPrivate, field_signature, - sizeof(field_signature), 0, nullptr, 0, &isAssemblyLoadedFieldToken); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineField _isAssemblyLoaded failed"); - return hr; - } - - // Get a TypeRef for System.Threading.Interlocked - mdTypeRef interlocked_type_ref; - hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("System.Threading.Interlocked"), &interlocked_type_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName interlocked_type_ref failed"); - return hr; - } - - // Create method signature for System.Threading.Interlocked::CompareExchange(int32&, int32, int32) - COR_SIGNATURE interlocked_compare_exchange_signature[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT, - 3, - ELEMENT_TYPE_I4, - ELEMENT_TYPE_BYREF, - ELEMENT_TYPE_I4, - ELEMENT_TYPE_I4, - ELEMENT_TYPE_I4}; - - mdMemberRef interlocked_compare_member_ref; - hr = - metadata_emit->DefineMemberRef(interlocked_type_ref, WStr("CompareExchange"), - interlocked_compare_exchange_signature, - sizeof(interlocked_compare_exchange_signature), &interlocked_compare_member_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMemberRef CompareExchange failed"); - return hr; - } - - ///////////////////////////////////////////// - // Add IL instructions into the IsAlreadyLoaded method - // - // static int _isAssemblyLoaded = 0; - // - // public static bool IsAlreadyLoaded() { - // return Interlocked.CompareExchange(ref _isAssemblyLoaded, 1, 0) == 1; - // } - // - ILRewriter rewriter_already_loaded(this->info_, nullptr, module_id, alreadyLoadedMethodToken); - rewriter_already_loaded.InitializeTiny(); - - ILInstr* pALFirstInstr = rewriter_already_loaded.GetILList()->m_pNext; - ILInstr* pALNewInstr = NULL; - - // ldsflda _isAssemblyLoaded : Load the address of the "_isAssemblyLoaded" static var - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_LDSFLDA; - pALNewInstr->m_Arg32 = isAssemblyLoadedFieldToken; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - // ldc.i4.1 : Load the constant 1 (int) to the stack - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_LDC_I4_1; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - // ldc.i4.0 : Load the constant 0 (int) to the stack - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_LDC_I4_0; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - // call int Interlocked.CompareExchange(ref int, int, int) method - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_CALL; - pALNewInstr->m_Arg32 = interlocked_compare_member_ref; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - // ldc.i4.1 : Load the constant 1 (int) to the stack - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_LDC_I4_1; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - // ceq : Compare equality from two values from the stack - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_CEQ; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - // ret : Return the value of the comparison - pALNewInstr = rewriter_already_loaded.NewILInstr(); - pALNewInstr->m_opcode = CEE_RET; - rewriter_already_loaded.InsertBefore(pALFirstInstr, pALNewInstr); - - hr = rewriter_already_loaded.Export(); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: Call to ILRewriter.Export() failed for ModuleID=", module_id); - return hr; - } - - // Define a method on the managed side that will PInvoke into the profiler method: - // C++: void GetAssemblyAndSymbolsBytes(BYTE** pAssemblyArray, int* assemblySize, BYTE** pSymbolsArray, int* - // symbolsSize) C#: static extern void GetAssemblyAndSymbolsBytes(out IntPtr assemblyPtr, out int assemblySize, out - // IntPtr symbolsPtr, out int symbolsSize) - mdMethodDef pinvoke_method_def; - COR_SIGNATURE get_assembly_bytes_signature[] = { - IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention - 4, // Number of parameters - ELEMENT_TYPE_VOID, // Return type - ELEMENT_TYPE_BYREF, // List of parameter types - ELEMENT_TYPE_I, - ELEMENT_TYPE_BYREF, - ELEMENT_TYPE_I4, - ELEMENT_TYPE_BYREF, - ELEMENT_TYPE_I, - ELEMENT_TYPE_BYREF, - ELEMENT_TYPE_I4, - }; - hr = metadata_emit->DefineMethod(new_type_def, WStr("GetAssemblyAndSymbolsBytes"), - mdStatic | mdPinvokeImpl | mdHideBySig, get_assembly_bytes_signature, - sizeof(get_assembly_bytes_signature), 0, 0, &pinvoke_method_def); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMethod failed"); - return hr; - } - - metadata_emit->SetMethodImplFlags(pinvoke_method_def, miPreserveSig); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: SetMethodImplFlags failed"); - return hr; - } - - WSTRING native_profiler_file = GetCurrentModuleFileName(); - Logger::Debug("GenerateVoidILStartupMethod: Setting the PInvoke native profiler library path to ", - native_profiler_file); - - mdModuleRef profiler_ref; - hr = metadata_emit->DefineModuleRef(native_profiler_file.c_str(), &profiler_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineModuleRef failed"); - return hr; - } - - hr = metadata_emit->DefinePinvokeMap(pinvoke_method_def, 0, WStr("GetAssemblyAndSymbolsBytes"), profiler_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefinePinvokeMap failed"); - return hr; - } - - // Get a TypeRef for System.Byte - mdTypeRef byte_type_ref; - hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("System.Byte"), &byte_type_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName failed"); - return hr; - } - - // Get a TypeRef for System.Runtime.InteropServices.Marshal - mdTypeRef marshal_type_ref; - hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("System.Runtime.InteropServices.Marshal"), - &marshal_type_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName failed"); - return hr; - } - - // Get a MemberRef for System.Runtime.InteropServices.Marshal.Copy(IntPtr, Byte[], int, int) - mdMemberRef marshal_copy_member_ref; - COR_SIGNATURE marshal_copy_signature[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention - 4, // Number of parameters - ELEMENT_TYPE_VOID, // Return type - ELEMENT_TYPE_I, // List of parameter types - ELEMENT_TYPE_SZARRAY, - ELEMENT_TYPE_U1, - ELEMENT_TYPE_I4, - ELEMENT_TYPE_I4}; - hr = metadata_emit->DefineMemberRef(marshal_type_ref, WStr("Copy"), marshal_copy_signature, - sizeof(marshal_copy_signature), &marshal_copy_member_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMemberRef failed"); - return hr; - } - - // Get a TypeRef for System.Reflection.Assembly - mdTypeRef system_reflection_assembly_type_ref; - hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("System.Reflection.Assembly"), - &system_reflection_assembly_type_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName failed"); - return hr; - } - - // Get a MemberRef for System.Object.ToString() - mdTypeRef system_object_type_ref; - hr = metadata_emit->DefineTypeRefByName(corlib_ref, WStr("System.Object"), &system_object_type_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineTypeRefByName failed"); - return hr; - } - - // Create method signature for System.Reflection.Assembly.Load(byte[], byte[]) - COR_SIGNATURE appdomain_load_signature_start[] = { - IMAGE_CEE_CS_CALLCONV_DEFAULT, 2, - ELEMENT_TYPE_CLASS // ret = System.Reflection.Assembly - // insert compressed token for System.Reflection.Assembly TypeRef here - }; - COR_SIGNATURE appdomain_load_signature_end[] = {ELEMENT_TYPE_SZARRAY, ELEMENT_TYPE_U1, ELEMENT_TYPE_SZARRAY, - ELEMENT_TYPE_U1}; - ULONG start_length = sizeof(appdomain_load_signature_start); - ULONG end_length = sizeof(appdomain_load_signature_end); - - BYTE system_reflection_assembly_type_ref_compressed_token[4]; - ULONG token_length = - CorSigCompressToken(system_reflection_assembly_type_ref, system_reflection_assembly_type_ref_compressed_token); - - const auto appdomain_load_signature_length = start_length + token_length + end_length; - COR_SIGNATURE appdomain_load_signature[250]; - memcpy(appdomain_load_signature, appdomain_load_signature_start, start_length); - memcpy(&appdomain_load_signature[start_length], system_reflection_assembly_type_ref_compressed_token, token_length); - memcpy(&appdomain_load_signature[start_length + token_length], appdomain_load_signature_end, end_length); - - mdMemberRef appdomain_load_member_ref; - hr = metadata_emit->DefineMemberRef(system_reflection_assembly_type_ref, WStr("Load"), appdomain_load_signature, - appdomain_load_signature_length, &appdomain_load_member_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMemberRef failed"); - return hr; - } - - // Create method signature for Assembly.CreateInstance(string) - COR_SIGNATURE assembly_create_instance_signature[] = {IMAGE_CEE_CS_CALLCONV_HASTHIS, 1, - ELEMENT_TYPE_OBJECT, // ret = System.Object - ELEMENT_TYPE_STRING}; - - mdMemberRef assembly_create_instance_member_ref; - hr = metadata_emit->DefineMemberRef(system_reflection_assembly_type_ref, WStr("CreateInstance"), - assembly_create_instance_signature, sizeof(assembly_create_instance_signature), - &assembly_create_instance_member_ref); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineMemberRef failed"); - return hr; - } - - // Create a string representing "OpenTelemetry.AutoInstrumentation.Loader.Loader" - LPCWSTR load_helper_str = L"OpenTelemetry.AutoInstrumentation.Loader.Loader"; - auto load_helper_str_size = wcslen(load_helper_str); - - mdString load_helper_token; - hr = metadata_emit->DefineUserString(load_helper_str, (ULONG)load_helper_str_size, &load_helper_token); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: DefineUserString failed"); - return hr; - } - - // Generate a locals signature defined in the following way: - // [0] System.IntPtr ("assemblyPtr" - address of assembly bytes) - // [1] System.Int32 ("assemblySize" - size of assembly bytes) - // [2] System.IntPtr ("symbolsPtr" - address of symbols bytes) - // [3] System.Int32 ("symbolsSize" - size of symbols bytes) - // [4] System.Byte[] ("assemblyBytes" - managed byte array for assembly) - // [5] System.Byte[] ("symbolsBytes" - managed byte array for symbols) - // [6] class System.Reflection.Assembly ("loadedAssembly" - assembly instance to save loaded assembly) - mdSignature locals_signature_token; - COR_SIGNATURE locals_signature[15] = { - IMAGE_CEE_CS_CALLCONV_LOCAL_SIG, // Calling convention - 7, // Number of variables - ELEMENT_TYPE_I, // List of variable types - ELEMENT_TYPE_I4, - ELEMENT_TYPE_I, - ELEMENT_TYPE_I4, - ELEMENT_TYPE_SZARRAY, - ELEMENT_TYPE_U1, - ELEMENT_TYPE_SZARRAY, - ELEMENT_TYPE_U1, - ELEMENT_TYPE_CLASS - // insert compressed token for System.Reflection.Assembly TypeRef here - }; - CorSigCompressToken(system_reflection_assembly_type_ref, &locals_signature[11]); - hr = metadata_emit->GetTokenFromSig(locals_signature, sizeof(locals_signature), &locals_signature_token); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: Unable to generate locals signature. ModuleID=", module_id); - return hr; - } - - ///////////////////////////////////////////// - // Add IL instructions into the void method - ILRewriter rewriter_void(this->info_, nullptr, module_id, *ret_method_token); - rewriter_void.InitializeTiny(); - rewriter_void.SetTkLocalVarSig(locals_signature_token); - - ILInstr* pFirstInstr = rewriter_void.GetILList()->m_pNext; - ILInstr* pNewInstr = NULL; - - // Step 0) Check if the assembly was already loaded - - // call bool IsAlreadyLoaded() - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_CALL; - pNewInstr->m_Arg32 = alreadyLoadedMethodToken; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // check if the return of the method call is true or false - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_BRFALSE_S; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - ILInstr* pBranchFalseInstr = pNewInstr; - - // return if IsAlreadyLoaded is true - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_RET; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // Step 1) Call void GetAssemblyAndSymbolsBytes(out IntPtr assemblyPtr, out int assemblySize, out IntPtr symbolsPtr, - // out int symbolsSize) - - // ldloca.s 0 : Load the address of the "assemblyPtr" variable (locals index 0) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOCA_S; - pNewInstr->m_Arg32 = 0; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // Set the false branch target - pBranchFalseInstr->m_pTarget = pNewInstr; - - // ldloca.s 1 : Load the address of the "assemblySize" variable (locals index 1) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOCA_S; - pNewInstr->m_Arg32 = 1; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloca.s 2 : Load the address of the "symbolsPtr" variable (locals index 2) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOCA_S; - pNewInstr->m_Arg32 = 2; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloca.s 3 : Load the address of the "symbolsSize" variable (locals index 3) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOCA_S; - pNewInstr->m_Arg32 = 3; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // call void GetAssemblyAndSymbolsBytes(out IntPtr assemblyPtr, out int assemblySize, out IntPtr symbolsPtr, out int - // symbolsSize) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_CALL; - pNewInstr->m_Arg32 = pinvoke_method_def; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // Step 2) Call void Marshal.Copy(IntPtr source, byte[] destination, int startIndex, int length) to populate the - // managed assembly bytes - - // ldloc.1 : Load the "assemblySize" variable (locals index 1) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_1; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // newarr System.Byte : Create a new Byte[] to hold a managed copy of the assembly data - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_NEWARR; - pNewInstr->m_Arg32 = byte_type_ref; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // stloc.s 4 : Assign the Byte[] to the "assemblyBytes" variable (locals index 4) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_STLOC_S; - pNewInstr->m_Arg8 = 4; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.0 : Load the "assemblyPtr" variable (locals index 0) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_0; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.s 4 : Load the "assemblyBytes" variable (locals index 4) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_S; - pNewInstr->m_Arg8 = 4; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldc.i4.0 : Load the integer 0 for the Marshal.Copy startIndex parameter - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDC_I4_0; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.1 : Load the "assemblySize" variable (locals index 1) for the Marshal.Copy length parameter - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_1; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // call Marshal.Copy(IntPtr source, byte[] destination, int startIndex, int length) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_CALL; - pNewInstr->m_Arg32 = marshal_copy_member_ref; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // Step 3) Call void Marshal.Copy(IntPtr source, byte[] destination, int startIndex, int length) to populate the - // symbols bytes - - // ldloc.3 : Load the "symbolsSize" variable (locals index 3) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_3; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // newarr System.Byte : Create a new Byte[] to hold a managed copy of the symbols data - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_NEWARR; - pNewInstr->m_Arg32 = byte_type_ref; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // stloc.s 5 : Assign the Byte[] to the "symbolsBytes" variable (locals index 5) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_STLOC_S; - pNewInstr->m_Arg8 = 5; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.2 : Load the "symbolsPtr" variables (locals index 2) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_2; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.s 5 : Load the "symbolsBytes" variable (locals index 5) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_S; - pNewInstr->m_Arg8 = 5; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldc.i4.0 : Load the integer 0 for the Marshal.Copy startIndex parameter - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDC_I4_0; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.3 : Load the "symbolsSize" variable (locals index 3) for the Marshal.Copy length parameter - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_3; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // call void Marshal.Copy(IntPtr source, byte[] destination, int startIndex, int length) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_CALL; - pNewInstr->m_Arg32 = marshal_copy_member_ref; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // Step 4) Call System.Reflection.Assembly System.Reflection.Assembly.Load(byte[], byte[])) - - // ldloc.s 4 : Load the "assemblyBytes" variable (locals index 4) for the first byte[] parameter of - // AppDomain.Load(byte[], byte[]) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_S; - pNewInstr->m_Arg8 = 4; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldloc.s 5 : Load the "symbolsBytes" variable (locals index 5) for the second byte[] parameter of - // AppDomain.Load(byte[], byte[]) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_S; - pNewInstr->m_Arg8 = 5; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // call System.Reflection.Assembly System.Reflection.Assembly.Load(uint8[], uint8[]) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_CALL; - pNewInstr->m_Arg32 = appdomain_load_member_ref; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // stloc.s 6 : Assign the System.Reflection.Assembly object to the "loadedAssembly" variable (locals index 6) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_STLOC_S; - pNewInstr->m_Arg8 = 6; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // Step 4) Call instance method Assembly.CreateInstance("OpenTelemetry.AutoInstrumentation.Loader.Loader") - - // ldloc.s 6 : Load the "loadedAssembly" variable (locals index 6) to call Assembly.CreateInstance - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDLOC_S; - pNewInstr->m_Arg8 = 6; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // ldstr "OpenTelemetry.AutoInstrumentation.Loader.Loader" - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_LDSTR; - pNewInstr->m_Arg32 = load_helper_token; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // callvirt System.Object System.Reflection.Assembly.CreateInstance(string) - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_CALLVIRT; - pNewInstr->m_Arg32 = assembly_create_instance_member_ref; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // pop the returned object - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_POP; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - // return - pNewInstr = rewriter_void.NewILInstr(); - pNewInstr->m_opcode = CEE_RET; - rewriter_void.InsertBefore(pFirstInstr, pNewInstr); - - if (IsDumpILRewriteEnabled()) - { - mdToken token = 0; - TypeInfo typeInfo{}; - WSTRING methodName = WStr("__DDVoidMethodCall__"); - FunctionInfo caller(token, methodName, typeInfo, MethodSignature(), FunctionMethodSignature()); - Logger::Info( - GetILCodes("*** GenerateLoaderMethod(): Modified Code: ", &rewriter_void, caller, metadata_import)); - } - - hr = rewriter_void.Export(); - if (FAILED(hr)) - { - Logger::Warn("GenerateLoaderMethod: Call to ILRewriter.Export() failed for ModuleID=", module_id); - return hr; + // Define a new static method __DDVoidMethodCall__ on the new type that has a void return type and takes no + // arguments + BYTE initialize_signature[] = { + IMAGE_CEE_CS_CALLCONV_DEFAULT, // Calling convention + 0, // Number of parameters + ELEMENT_TYPE_VOID, // Return type + }; + hr = metadata_emit->DefineMemberRef(init_type_ref, WStr("__DDVoidMethodCall__"), initialize_signature, + sizeof(initialize_signature), ret_method_token); + if (FAILED(hr)) + { + Logger::Warn("GenerateLoaderMethod: DefineMemberRef __DDVoidMethodCall__ failed"); + return hr; + } } return S_OK; diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h index bd49305b6..179f809db 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h +++ b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h @@ -106,6 +106,11 @@ private: // HRESULT RunAutoInstrumentationLoader(const ComPtr&, const ModuleID module_id, const mdToken function_token, const FunctionInfo& caller, const ModuleMetadata& module_metadata); HRESULT GenerateLoaderMethod(const ModuleID module_id, mdMethodDef* ret_method_token); + HRESULT GenerateLoaderType(const ModuleID module_id, + mdTypeDef* loader_type, + mdMethodDef* init_method, + mdMethodDef* patch_app_domain_setup_method); + HRESULT ModifyAppDomainCreate(const ModuleID module_id, mdMethodDef patch_app_domain_setup_method); HRESULT AddIISPreStartInitFlags(const ModuleID module_id, const mdToken function_token); #endif diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/member_resolver.cpp b/src/OpenTelemetry.AutoInstrumentation.Native/member_resolver.cpp new file mode 100644 index 000000000..9333fa6d6 --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation.Native/member_resolver.cpp @@ -0,0 +1,40 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "member_resolver.h" + +namespace trace +{ + +MemberResolver::MemberResolver(const ComPtr& import, const ComPtr& emit) + : metadaImport_(import), metadaEmit_(emit) +{ +} + +HRESULT MemberResolver::GetTypeRefOrDefByName(mdToken tkResolutionScope, LPCWSTR szName, mdToken* token) +{ + if (TypeFromToken(tkResolutionScope) == mdtAssembly) + { + tkResolutionScope = mdTokenNil; + } + + if (tkResolutionScope == mdTokenNil || TypeFromToken(tkResolutionScope) == mdtTypeDef) + { + return metadaImport_->FindTypeDefByName(szName, tkResolutionScope, token); + } + + return metadaEmit_->DefineTypeRefByName(tkResolutionScope, szName, token); +} + +HRESULT MemberResolver::GetMemberRefOrDef( + mdToken tkScope, LPCWSTR szName, PCCOR_SIGNATURE pvSigBlob, ULONG cbSigBlob, mdToken* token) + +{ + if (TypeFromToken(tkScope) == mdtTypeDef) + { + return metadaImport_->FindMember(tkScope, szName, pvSigBlob, cbSigBlob, token); + } + + return metadaEmit_->DefineMemberRef(tkScope, szName, pvSigBlob, cbSigBlob, token); +} +} // namespace trace diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/member_resolver.h b/src/OpenTelemetry.AutoInstrumentation.Native/member_resolver.h new file mode 100644 index 000000000..71653e477 --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation.Native/member_resolver.h @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OTEL_CLR_PROFILER_MEMBER_RESOLVER_H_ +#define OTEL_CLR_PROFILER_MEMBER_RESOLVER_H_ + +#include +#include + +#include "com_ptr.h" +#include "module_metadata.h" + +namespace trace +{ + +class MemberResolver +{ +private: + ComPtr metadaImport_; + ComPtr metadaEmit_; + +public: + MemberResolver(const ComPtr& import, const ComPtr& emit); + + + /// + /// Uses IMetaDataImport::FindTypeDefByName if tkResolutionScope is mdTokenNill, TypeDef or Assembly + /// Uses IMetaDataEmit::DefineTypeRefByName otherwise (if tkResolutionScope is AssemblyRef or TypeRef) + /// + HRESULT GetTypeRefOrDefByName(mdToken tkResolutionScope, LPCWSTR szName, mdToken* token); + + /// + /// Uses IMetaDataImport::FindMember if tkScope is TypeDef + /// Uses IMetaDataEmit::DefineMemberRef otherwise (if tkScope is TypeRef or TypeSpec) + /// + HRESULT GetMemberRefOrDef(mdToken tkScope, LPCWSTR szName, PCCOR_SIGNATURE pvSigBlob, ULONG cbSigBlob, mdToken* token); +}; + +} // namespace trace + +#endif // OTEL_CLR_PROFILER_MEMBER_RESOLVER_H_ diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/signature_builder.h b/src/OpenTelemetry.AutoInstrumentation.Native/signature_builder.h new file mode 100644 index 000000000..6b7e6f69a --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation.Native/signature_builder.h @@ -0,0 +1,288 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OTEL_CLR_PROFILER_SIGNATURE_BUILDER_H_ +#define OTEL_CLR_PROFILER_SIGNATURE_BUILDER_H_ + +#include +#include + +namespace trace +{ + +class SignatureData +{ +protected: + std::vector blob_; + +public: + SignatureData() = default; + SignatureData(std::initializer_list bytes) : blob_(bytes) {} + SignatureData(std::initializer_list elements) + { + for (auto& inner : elements) + { + blob_.insert(blob_.end(), inner.blob_.begin(), inner.blob_.end()); + } + } + + PCCOR_SIGNATURE Head() const + { + return blob_.data(); + } + + ULONG Size() const + { + return static_cast(blob_.size()); + } +}; + +class SignatureBuilder : public virtual SignatureData +{ +public: + SignatureBuilder() = default; + SignatureBuilder(std::initializer_list bytes) : SignatureData(bytes) {} + SignatureBuilder(std::initializer_list elements) : SignatureData(elements) {} + + SignatureBuilder& PushRawByte(COR_SIGNATURE byte) + { + blob_.push_back(byte); + return *this; + } + + template + SignatureBuilder& PushRawBytes(InputIt first, InputIt last) + { + blob_.insert(blob_.end(), first, last); + return *this; + } + + SignatureBuilder& PushRawBytes(std::initializer_list bytes) + { + blob_.insert(blob_.end(), bytes); + return *this; + } + + SignatureBuilder& PushCompressedData(ULONG data) + { + COR_SIGNATURE compressed[sizeof(ULONG)]; + ULONG compressedSize = CorSigCompressData(data, compressed); + for (ULONG i = 0; i < compressedSize; i++) + { + PushRawByte(compressed[i]); + } + return *this; + } + + SignatureBuilder& PushToken(mdToken token) + { + COR_SIGNATURE compressed[sizeof(mdToken)]; + ULONG compressedSize = CorSigCompressToken(token, compressed); + for (ULONG i = 0; i < compressedSize; i++) + { + PushRawByte(compressed[i]); + } + return *this; + } + + SignatureBuilder& Push(const SignatureData& inner) + { + blob_.insert(blob_.end(), inner.Head(), inner.Head()+inner.Size()); + return *this; + } + + enum class MethodCallingConvection : COR_SIGNATURE + { + Static = IMAGE_CEE_CS_CALLCONV_DEFAULT, + Instance = IMAGE_CEE_CS_CALLCONV_HASTHIS + }; + + enum class TokenTypeMode : COR_SIGNATURE + { + ValueType = ELEMENT_TYPE_VALUETYPE, + Class = ELEMENT_TYPE_CLASS + }; + + enum class GenericArgMode : COR_SIGNATURE + { + Type = ELEMENT_TYPE_VAR, + Method = ELEMENT_TYPE_MVAR + }; + + enum class BuiltIn : COR_SIGNATURE + { + Void = ELEMENT_TYPE_VOID, + Boolean = ELEMENT_TYPE_BOOLEAN, + Char = ELEMENT_TYPE_CHAR, + SByte = ELEMENT_TYPE_I1, + Byte = ELEMENT_TYPE_U1, + Int16 = ELEMENT_TYPE_I2, + UInt16 = ELEMENT_TYPE_U2, + Int32 = ELEMENT_TYPE_I4, + UInt32 = ELEMENT_TYPE_U4, + Int64 = ELEMENT_TYPE_I8, + UInt64 = ELEMENT_TYPE_U8, + Float = ELEMENT_TYPE_R4, + Double = ELEMENT_TYPE_R8, + String = ELEMENT_TYPE_STRING, + IntPtr = ELEMENT_TYPE_I, + UintPtr = ELEMENT_TYPE_U, + Object = ELEMENT_TYPE_OBJECT + }; + + class Type; + class TokenType; + class ValueType; + class Class; + class Array; + class ByRef; + class GenericArgument; + class GenericArgumentOfMethod; + class GenericArgumentOfType; + class GenericInstance; + class Field; + class Method; + class InstanceMethod; + class StaticMethod; +}; + +class SignatureBuilder::Type : public virtual SignatureData, protected SignatureBuilder +{ +public: + Type(BuiltIn builtin) + : SignatureData{static_cast(builtin)} + { + } + +protected: + Type() = default; +}; + +class SignatureBuilder::TokenType : public Type +{ +public: + TokenType(TokenTypeMode type, mdToken token) + { + this->PushRawByte(static_cast(type)); + this->PushToken(token); + } +}; + +class SignatureBuilder::ValueType : public TokenType +{ +public: + explicit ValueType(mdToken token) : TokenType(TokenTypeMode::ValueType, token) {} +}; + +class SignatureBuilder::Class : public TokenType +{ +public: + explicit Class(mdToken token) : TokenType(TokenTypeMode::Class, token) {} +}; + +class SignatureBuilder::Array : public Type +{ +public: + explicit Array(const Type& type) + { + this->PushRawByte(ELEMENT_TYPE_SZARRAY); + this->Push(type); + } +}; + +class SignatureBuilder::ByRef : public Type +{ +public: + explicit ByRef(const Type& type) + { + this->PushRawByte(ELEMENT_TYPE_BYREF); + this->Push(type); + } +}; + +class SignatureBuilder::GenericArgument : public Type +{ +public: + GenericArgument(GenericArgMode type, UINT num) + { + this->PushRawByte(static_cast(type)); + this->PushCompressedData(num); + } +}; + +class SignatureBuilder::GenericArgumentOfMethod : public GenericArgument +{ +public: + explicit GenericArgumentOfMethod(UINT num) : GenericArgument(GenericArgMode::Method, num) {} +}; + +class SignatureBuilder::GenericArgumentOfType : public GenericArgument +{ +public: + explicit GenericArgumentOfType(UINT num) : GenericArgument(GenericArgMode::Type, num) {} +}; + +class SignatureBuilder::GenericInstance : public Type +{ +public: + GenericInstance(const TokenType& open_generic, const std::vector& generic_args) + { + this->PushRawByte(ELEMENT_TYPE_GENERICINST); + this->Push(open_generic); + this->PushCompressedData(static_cast(generic_args.size())); + for (const auto& arg : generic_args) + { + this->Push(arg); + } + } +}; + +class SignatureBuilder::Field : public virtual SignatureData, protected SignatureBuilder +{ +public: + explicit Field(const Type& field_type) + { + this->PushRawByte(IMAGE_CEE_CS_CALLCONV_FIELD); + this->Push(field_type); + } +}; + +class SignatureBuilder::Method : public virtual SignatureData, protected SignatureBuilder +{ +public: + Method(MethodCallingConvection calling_convection, const Type& return_type, const std::vector& args) + { + this->PushRawByte(static_cast(calling_convection)); + this->PushCompressedData(static_cast(args.size())); + this->Push(return_type); + for (const auto& arg : args) + { + this->Push(arg); + } + } +}; + +class SignatureBuilder::InstanceMethod : public Method +{ +public: + InstanceMethod(const Type& return_type, const std::vector& args) + : Method(MethodCallingConvection::Instance, return_type, args) + { + } +}; + +class SignatureBuilder::StaticMethod : public Method +{ +public: + StaticMethod(const Type& return_type, const std::vector& args) + : Method(MethodCallingConvection::Static, return_type, args) + { + } +}; + + +} // namespace trace + +#endif // OTEL_CLR_PROFILER_SIGNATURE_BUILDER_H_ diff --git a/test/IntegrationTests/AspNetTests.cs b/test/IntegrationTests/AspNetTests.cs index f76464cc3..a815c84cf 100644 --- a/test/IntegrationTests/AspNetTests.cs +++ b/test/IntegrationTests/AspNetTests.cs @@ -17,14 +17,42 @@ public class AspNetTests Output = output; } + public enum Gac + { + /// + /// Use image with OTEL assemblies registered in GAC + /// + UseGac, + + /// + /// Use image with OTEL assemblies not registered in GAC + /// + UseLocal + } + + public enum AppPoolMode + { + /// + /// Use image with Classic AppPoolMode + /// + Classic, + + /// + /// Use image with Integrated AppPoolMode + /// + Integrated + } + private ITestOutputHelper Output { get; } [Theory] [Trait("Category", "EndToEnd")] [Trait("Containers", "Windows")] - [InlineData("Classic")] - [InlineData("Integrated")] - public async Task SubmitsTraces(string appPoolMode) + [InlineData(AppPoolMode.Classic, Gac.UseGac)] + [InlineData(AppPoolMode.Classic, Gac.UseLocal)] + [InlineData(AppPoolMode.Integrated, Gac.UseGac)] + [InlineData(AppPoolMode.Integrated, Gac.UseLocal)] + public async Task SubmitsTraces(AppPoolMode appPoolMode, Gac useGac) { Assert.True(EnvironmentTools.IsWindowsAdministrator(), "This test requires Windows Administrator privileges."); @@ -43,7 +71,7 @@ public class AspNetTests ["OTEL_EXPORTER_OTLP_ENDPOINT"] = collectorUrl }; var webPort = TcpPortProvider.GetOpenPort(); - var imageName = GetTestImageName(appPoolMode); + var imageName = GetTestImageName(appPoolMode, useGac); await using var container = await IISContainerTestHelper.StartContainerAsync(imageName, webPort, environmentVariables, Output); await CallTestApplicationEndpoint(webPort); @@ -53,9 +81,11 @@ public class AspNetTests [Theory] [Trait("Category", "EndToEnd")] [Trait("Containers", "Windows")] - [InlineData("Classic")] - [InlineData("Integrated")] - public async Task SubmitTracesCapturesHttpHeaders(string appPoolMode) + [InlineData(AppPoolMode.Classic, Gac.UseGac)] + [InlineData(AppPoolMode.Classic, Gac.UseLocal)] + [InlineData(AppPoolMode.Integrated, Gac.UseGac)] + [InlineData(AppPoolMode.Integrated, Gac.UseLocal)] + public async Task SubmitTracesCapturesHttpHeaders(AppPoolMode appPoolMode, Gac useGac) { Assert.True(EnvironmentTools.IsWindowsAdministrator(), "This test requires Windows Administrator privileges."); @@ -67,7 +97,7 @@ public class AspNetTests using var fwPort = FirewallHelper.OpenWinPort(collector.Port, Output); collector.Expect("OpenTelemetry.Instrumentation.AspNet.Telemetry", span => // Expect Mvc span { - if (appPoolMode == "Classic") + if (appPoolMode == AppPoolMode.Classic) { return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2") && span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1") @@ -87,7 +117,7 @@ public class AspNetTests collector.Expect("OpenTelemetry.Instrumentation.AspNet.Telemetry", span => // Expect WebApi span { - if (appPoolMode == "Classic") + if (appPoolMode == AppPoolMode.Classic) { return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2") && span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1") @@ -113,7 +143,7 @@ public class AspNetTests ["OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS"] = "Custom-Response-Test-Header1,Custom-Response-Test-Header3" }; var webPort = TcpPortProvider.GetOpenPort(); - var imageName = GetTestImageName(appPoolMode); + var imageName = GetTestImageName(appPoolMode, useGac); await using var container = await IISContainerTestHelper.StartContainerAsync(imageName, webPort, environmentVariables, Output); await CallTestApplicationEndpoint(webPort); @@ -181,9 +211,16 @@ public class AspNetTests collector.AssertExpectations(); } - private static string GetTestImageName(string appPoolMode) + private static string GetTestImageName(AppPoolMode appPoolMode, Gac useGac) { - return appPoolMode == "Classic" ? "testapplication-aspnet-netframework-classic" : "testapplication-aspnet-netframework-integrated"; + return (appPoolMode, useGac) switch + { + (AppPoolMode.Classic, Gac.UseGac) => "testapplication-aspnet-netframework-classic", + (AppPoolMode.Classic, Gac.UseLocal) => "testapplication-aspnet-netframework-classic-nogac", + (AppPoolMode.Integrated, Gac.UseGac) => "testapplication-aspnet-netframework-integrated", + (AppPoolMode.Integrated, Gac.UseLocal) => "testapplication-aspnet-netframework-integrated-nogac", + _ => throw new ArgumentOutOfRangeException() + }; } private async Task CallTestApplicationEndpoint(int webPort) diff --git a/test/OpenTelemetry.AutoInstrumentation.Native.Tests/signature_builder_test.cpp b/test/OpenTelemetry.AutoInstrumentation.Native.Tests/signature_builder_test.cpp new file mode 100644 index 000000000..133cf6b2a --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Native.Tests/signature_builder_test.cpp @@ -0,0 +1,338 @@ +#include "pch.h" + +#include "../../src/OpenTelemetry.AutoInstrumentation.Native/signature_builder.h" +#include + +using namespace trace; + +class SignatureBuilderTest : public ::testing::Test +{ +}; + +TEST_F(SignatureBuilderTest, SignatureBuilderDefaultCtor) +{ + SignatureBuilder empty; + EXPECT_EQ(empty.Size(), 0); +} + +TEST_F(SignatureBuilderTest, CorSignatureCtor) +{ + SignatureBuilder for_test{1, 2, 3, 99}; + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({1, 2, 3, 99})); +} + +TEST_F(SignatureBuilderTest, PushRawByte) +{ + SignatureBuilder for_test = SignatureBuilder{}.PushRawByte(64).PushRawByte(50); + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({64, 50})); +} + +TEST_F(SignatureBuilderTest, PushRawBytes) +{ + SignatureBuilder for_test = SignatureBuilder{}.PushRawBytes({64, 98}).PushRawBytes({70, 54}); + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({64, 98, 70, 54})); +} + +TEST_F(SignatureBuilderTest, PushRawBytesBeginEnd) +{ + std::vector source1{23, 98, 37}; + std::vector source2{35, 42}; + + SignatureBuilder for_test = + SignatureBuilder{}.PushRawBytes(source1.begin(), source1.end()).PushRawBytes(source2.begin(), source2.end()); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({23, 98, 37, 35, 42})); +} + +TEST_F(SignatureBuilderTest, PushCompressedData) +{ + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(1); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({1})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0x7F); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0x7F})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0x80); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0x80, 0x80})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0x2E57); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0xAE, 0x57})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0x3FFF); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0xBF, 0xFF})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0x4000); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0xC0, 0x00, 0x40, 0x00})); + } + { + SignatureBuilder for_test = SignatureBuilder{}.PushCompressedData(0x1FFFFFFF); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0xDF, 0xFF, 0xFF, 0xFF})); + } +} + +TEST_F(SignatureBuilderTest, PushToken) +{ + SignatureBuilder for_test = SignatureBuilder{}.PushToken(TokenFromRid(0x12, mdtTypeRef)); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({0x49})); +} + +TEST_F(SignatureBuilderTest, Push) +{ + SignatureBuilder source1{23, 98, 37}; + SignatureBuilder source2{35, 42}; + + SignatureBuilder for_test = SignatureBuilder{}.Push(source1).Push(source2); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({23, 98, 37, 35, 42})); +} + +TEST_F(SignatureBuilderTest, SignatureBuilderCtor) +{ + SignatureBuilder source1{23, 98, 37}; + SignatureBuilder source2{35, 42}; + + SignatureBuilder for_test{source1, source2}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({23, 98, 37, 35, 42})); +} + +TEST_F(SignatureBuilderTest, CombinationOfApi) +{ + SignatureBuilder source1{23, 98, 37}; + SignatureBuilder source2{35, 42}; + + SignatureBuilder for_test = SignatureBuilder{source1, source2} + .PushRawByte(33) + .PushRawBytes({34, 51}) + .PushToken(TokenFromRid(0x12, mdtTypeRef)) + .PushCompressedData(0x4000); + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({23, 98, 37, 35, 42, 33, 34, 51, 0x49, 0xC0, 0x00, 0x40, 0x00})); +} + +TEST_F(SignatureBuilderTest, Type) +{ + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Void}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_VOID})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Boolean}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_BOOLEAN})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Char}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_CHAR})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::SByte}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_I1})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Byte}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_U1})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Int16}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_I2})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::UInt16}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_U2})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Int32}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_I4})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::UInt32}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_U4})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Int64}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_I8})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::UInt64}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_U8})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Float}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_R4})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Double}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_R8})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::String}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_STRING})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::IntPtr}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_I})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::UintPtr}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_U})); + } + { + SignatureBuilder::Type for_test{SignatureBuilder::BuiltIn::Object}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_OBJECT})); + } +} + +TEST_F(SignatureBuilderTest, ValueType) +{ + SignatureBuilder::ValueType for_test{TokenFromRid(0x12, mdtTypeRef)}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_VALUETYPE, 0x49})); +} + +TEST_F(SignatureBuilderTest, Class) +{ + SignatureBuilder::Class for_test{TokenFromRid(0x12, mdtTypeRef)}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_CLASS, 0x49})); +} + +TEST_F(SignatureBuilderTest, Array) +{ + SignatureBuilder::Array for_test{SignatureBuilder::BuiltIn::String}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_SZARRAY, ELEMENT_TYPE_STRING})); +} + +TEST_F(SignatureBuilderTest, ByRef) +{ + SignatureBuilder::ByRef for_test{SignatureBuilder::BuiltIn::String}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_BYREF, ELEMENT_TYPE_STRING})); +} + +TEST_F(SignatureBuilderTest, GenericArgumentOfMethod) +{ + SignatureBuilder::GenericArgumentOfMethod for_test{0x80}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_MVAR, 0x80, 0x80})); +} + +TEST_F(SignatureBuilderTest, GenericArgumentOfType) +{ + SignatureBuilder::GenericArgumentOfType for_test{0x0}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({ELEMENT_TYPE_VAR, 0x00})); +} + +TEST_F(SignatureBuilderTest, GenericInstance) +{ + SignatureBuilder::GenericInstance for_test{SignatureBuilder::Class{TokenFromRid(0x12, mdtTypeRef)}, + {SignatureBuilder::BuiltIn::String, SignatureBuilder::BuiltIn::Double}}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector( + {ELEMENT_TYPE_GENERICINST, ELEMENT_TYPE_CLASS, 0x49, 2, ELEMENT_TYPE_STRING, ELEMENT_TYPE_R8})); +} + +TEST_F(SignatureBuilderTest, Field) +{ + SignatureBuilder::Field for_test{SignatureBuilder::Class{TokenFromRid(0x12, mdtTypeRef)}}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector({IMAGE_CEE_CS_CALLCONV_FIELD, ELEMENT_TYPE_CLASS, 0x49})); +} + +TEST_F(SignatureBuilderTest, InstanceMethod) +{ + SignatureBuilder::InstanceMethod for_test{SignatureBuilder::Class{TokenFromRid(0x12, mdtTypeRef)}, + {SignatureBuilder::BuiltIn::String, SignatureBuilder::BuiltIn::Double}}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector( + {IMAGE_CEE_CS_CALLCONV_HASTHIS, 2, ELEMENT_TYPE_CLASS, 0x49, ELEMENT_TYPE_STRING, ELEMENT_TYPE_R8})); +} + +TEST_F(SignatureBuilderTest, StaticMethod) +{ + SignatureBuilder::StaticMethod for_test{SignatureBuilder::Class{TokenFromRid(0x12, mdtTypeRef)}, + {SignatureBuilder::BuiltIn::String, SignatureBuilder::BuiltIn::Double}}; + + EXPECT_EQ(std::vector(for_test.Head(), for_test.Head() + for_test.Size()), + std::vector( + {IMAGE_CEE_CS_CALLCONV_DEFAULT, 2, ELEMENT_TYPE_CLASS, 0x49, ELEMENT_TYPE_STRING, ELEMENT_TYPE_R8})); +} \ No newline at end of file diff --git a/test/test-applications/integrations/TestApplication.AspNet.NetFramework/Dockerfile b/test/test-applications/integrations/TestApplication.AspNet.NetFramework/Dockerfile index 8d7992102..1022444f8 100644 --- a/test/test-applications/integrations/TestApplication.AspNet.NetFramework/Dockerfile +++ b/test/test-applications/integrations/TestApplication.AspNet.NetFramework/Dockerfile @@ -1,6 +1,6 @@ # escape=` -FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022@sha256:84079c734ab5aec702409ef7967ec47af9468c56ff4046882239cabacda78097 AS integrated +FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022@sha256:84079c734ab5aec702409ef7967ec47af9468c56ff4046882239cabacda78097 AS integrated-base ARG configuration=Debug ARG platform=x64 WORKDIR /opentelemetry @@ -8,15 +8,27 @@ COPY bin/tracer.zip . COPY bin/OpenTelemetry.DotNet.Auto.psm1 . ENV OTEL_DOTNET_AUTO_INSTALL_DIR=C:\opentelemetry SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] -RUN Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\.NETFramework" -Name "LoaderOptimization" -Value 1 RUN Import-Module .\OpenTelemetry.DotNet.Auto.psm1 -Verbose; ` - Install-OpenTelemetryCore -LocalPath .\tracer.zip; ` + Install-OpenTelemetryCore -LocalPath .\tracer.zip -RegisterAssembliesInGAC $false; ` Register-OpenTelemetryForIIS; ENV OTEL_DOTNET_AUTO_LOG_DIRECTORY=C:\inetpub\wwwroot\logs ` OTEL_LOG_LEVEL=debug WORKDIR /inetpub/wwwroot COPY bin/${configuration}/app.publish . +FROM integrated-base AS integrated-nogac + +FROM integrated-nogac AS classic-nogac +RUN Start-IISCommitDelay;` + (Get-IISAppPool -Name DefaultAppPool).ManagedPipelineMode = 'Classic';` + Stop-IISCommitDelay -Commit $True + +FROM integrated-base AS integrated +WORKDIR /opentelemetry +RUN Import-Module .\OpenTelemetry.DotNet.Auto.psm1 -Verbose; ` + Register-AssembliesInGAC -InstallDir C:\inetpub\wwwroot\otel; +WORKDIR /inetpub/wwwroot + FROM integrated AS classic RUN Start-IISCommitDelay;` (Get-IISAppPool -Name DefaultAppPool).ManagedPipelineMode = 'Classic';` diff --git a/test/test-applications/integrations/TestApplication.Owin.IIS.NetFramework/Dockerfile b/test/test-applications/integrations/TestApplication.Owin.IIS.NetFramework/Dockerfile index e63b830a6..cd7186caf 100644 --- a/test/test-applications/integrations/TestApplication.Owin.IIS.NetFramework/Dockerfile +++ b/test/test-applications/integrations/TestApplication.Owin.IIS.NetFramework/Dockerfile @@ -8,7 +8,6 @@ COPY bin/tracer.zip . COPY bin/OpenTelemetry.DotNet.Auto.psm1 . ENV OTEL_DOTNET_AUTO_INSTALL_DIR=C:\opentelemetry SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] -RUN Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\.NETFramework" -Name "LoaderOptimization" -Value 1 RUN Import-Module .\OpenTelemetry.DotNet.Auto.psm1 -Verbose; ` Install-OpenTelemetryCore -LocalPath .\tracer.zip; ` Register-OpenTelemetryForIIS; diff --git a/test/test-applications/integrations/TestApplication.Wcf.Server.IIS.NetFramework/Dockerfile b/test/test-applications/integrations/TestApplication.Wcf.Server.IIS.NetFramework/Dockerfile index 2ac6388f9..1f8933c15 100644 --- a/test/test-applications/integrations/TestApplication.Wcf.Server.IIS.NetFramework/Dockerfile +++ b/test/test-applications/integrations/TestApplication.Wcf.Server.IIS.NetFramework/Dockerfile @@ -8,7 +8,6 @@ COPY bin/tracer.zip . COPY bin/OpenTelemetry.DotNet.Auto.psm1 . ENV OTEL_DOTNET_AUTO_INSTALL_DIR=C:\opentelemetry SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] -RUN Set-ItemProperty -Path "HKLM:\\SOFTWARE\\Microsoft\\.NETFramework" -Name "LoaderOptimization" -Value 1 RUN Import-Module .\OpenTelemetry.DotNet.Auto.psm1 -Verbose; ` Install-OpenTelemetryCore -LocalPath .\tracer.zip; ` Register-OpenTelemetryForIIS;