Skip to content

Commit

Permalink
Subscribe to AssemblyLoadContext.Default.Resolving (#6148)
Browse files Browse the repository at this point in the history
## Summary of changes

In the managed loader, subscribe to
`AssemblyLoadContext.Default.Resolving` in addition to
`AppDomain.CurrentDomain.AssemblyResolve`.

## Reason for change

When an assembly is loaded implicitly (because it's referenced in some
code), the JIT compiler calls
`AssemblyLoadContext.ResolveUsingResolvingEvent`. This method asks the
current ALC to resolve the assembly, and it will in turn raise its
`Resolving` event if it fails. If the active ALC `Resolving` event
fails, then the global `AssemblyLoadContext.AssemblyResolve` event is
finally raised.

In the managed loader, we subscribe to
`AppDomain.Current.AssemblyResolve`, which is a wrapper around
`AssemblyLoadContext.AssemblyResolve`. It means that the ALC `Resolving`
event has a chance to resolve assemblies before we do.

`System.Management.Automation`, a powershell library, subscribes to
`AssemblyLoadContext.Default.Resolving`. In its resolution logic, it
loads assemblies from the GAC if they're not found, thus loading the
.NET Framework version of Datadog.Trace even if the process is running
.NET Core. By subscribing to `AssemblyLoadContext.Default.Resolving`, we
prevent that from happining.

## Test coverage

Added a smoke test.

## Other details

Fixes #6135
  • Loading branch information
kevingosse authored Oct 14, 2024
1 parent 5eb0180 commit 34444ea
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Datadog.Trace.sln
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generated", "Generated", "{
tracer\build\PackageVersionsLatestSpecific.g.props = tracer\build\PackageVersionsLatestSpecific.g.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssemblyLoadContextResolve", "tracer\test\test-applications\regression\AssemblyLoadContextResolve\AssemblyLoadContextResolve.csproj", "{8B1AF6A7-DD41-4347-B637-90C23D69B50E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1409,6 +1411,10 @@ Global
{2CA0D70C-DFC1-458A-871B-328AB6E87E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CA0D70C-DFC1-458A-871B-328AB6E87E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CA0D70C-DFC1-458A-871B-328AB6E87E3A}.Release|Any CPU.Build.0 = Release|Any CPU
{8B1AF6A7-DD41-4347-B637-90C23D69B50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B1AF6A7-DD41-4347-B637-90C23D69B50E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B1AF6A7-DD41-4347-B637-90C23D69B50E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B1AF6A7-DD41-4347-B637-90C23D69B50E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1638,6 +1644,7 @@ Global
{7131FE5A-6B27-4BBC-B0CF-09780F6D2DFE} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{2CA0D70C-DFC1-458A-871B-328AB6E87E3A} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{E1B0F72C-991A-409D-9266-DE5ED1BD940E} = {A0C5FBBB-CFB2-4FB9-B8F0-55676E9DCF06}
{8B1AF6A7-DD41-4347-B637-90C23D69B50E} = {498A300E-D036-49B7-A43D-821D1CAF11A5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}
Expand Down Expand Up @@ -1717,6 +1724,7 @@ Global
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{83290961-40bf-48cb-b925-fbbe48e629f3}*SharedItemsImports = 5
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{87d57940-9a6e-473c-a4d6-777e3bafd5f9}*SharedItemsImports = 5
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{887ac8ba-35a6-4646-bf9a-59357155805e}*SharedItemsImports = 5
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{8b1af6a7-dd41-4347-b637-90c23d69b50e}*SharedItemsImports = 5
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{8b457e8f-8716-4f29-bbe2-dd6c7bc4ac37}*SharedItemsImports = 5
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{8bdf1de0-e6de-48ad-aaa3-ce09cb544e2c}*SharedItemsImports = 5
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{8dfe1168-b1cc-43d1-b256-b5708badd47b}*SharedItemsImports = 5
Expand Down
11 changes: 11 additions & 0 deletions tracer/src/Datadog.Trace.ClrProfiler.Managed.Loader/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ static Startup()
StartupLogger.Log(ex, "Unable to register a callback to the CurrentDomain.AssemblyResolve event.");
}

#if NETCOREAPP
try
{
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (_, assemblyName) => ResolveAssembly(assemblyName.Name);
}
catch (Exception ex)
{
StartupLogger.Log(ex, "Unable to register a callback to the AssemblyLoadContext.Default.Resolving event.");
}
#endif

var runInAas = ReadBooleanEnvironmentVariable(AzureAppServicesKey, false);
if (runInAas)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace AssemblyLoadContextResolve;

internal class Program
{
private static ConcurrentStack<string> _assemblyResolveCalls = new();

private const string TestAssemblyName = "datadog_test_assembly";

static void Main(string[] args)
{
AssemblyLoadContext.Default.Resolving += AssemblyResolving;

var traceAssembly = Assembly.Load("Datadog.Trace");
var alc = AssemblyLoadContext.GetLoadContext(traceAssembly);

if (alc.GetType().FullName != "Datadog.Trace.ClrProfiler.Managed.Loader.ManagedProfilerAssemblyLoadContext")
{
throw new InvalidOperationException($"Datadog.Trace was loaded in the wrong ALC: {alc.GetType()}");
}

try
{
var testAssembly = Assembly.Load(TestAssemblyName);
throw new InvalidOperationException($"Test assembly was found, this shouldn't happen: {testAssembly}");
}
catch (FileNotFoundException)
{
// Expected
}

var resolvedAssemblies = _assemblyResolveCalls.ToList();

if (!resolvedAssemblies.Contains(TestAssemblyName))
{
throw new InvalidOperationException($"AssemblyResolving should have been called for {TestAssemblyName}: {string.Join(", ", resolvedAssemblies)}");
}

if (resolvedAssemblies.Contains("Datadog.Trace"))
{
throw new InvalidOperationException($"AssemblyResolving shouldn't have been called for Datadog.Trace: {string.Join(", ", resolvedAssemblies)}");
}
}

private static Assembly AssemblyResolving(AssemblyLoadContext alc, AssemblyName assemblyname)
{
_assemblyResolveCalls.Push(assemblyname?.Name);
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"profiles": {
"AssemblyLoadContextResolve": {
"commandName": "Project",
"environmentVariables": {
"CORECLR_ENABLE_PROFILING": "1",
"CORECLR_PROFILER": "{846F5F1C-F9AE-4B07-969E-05C26BC060D8}",
"CORECLR_PROFILER_PATH": "$(SolutionDir)shared\\bin\\monitoring-home\\tracer\\win-$(Platform)\\Datadog.Trace.ClrProfiler.Native.dll",

"DD_DOTNET_TRACER_HOME": "$(SolutionDir)shared\\bin\\monitoring-home\\tracer",
"DD_VERSION": "1.0.0"
},
"nativeDebugging": true
}
}
}

0 comments on commit 34444ea

Please sign in to comment.