[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern bool TraceAssemblyResolveHandlerInvoked(string assemblyName, string handlerName, string? resultAssemblyName, string? resultAssemblyPath);
+ [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern bool TraceAssemblyLoadFromResolveHandlerInvoked(string assemblyName, bool isTrackedAssembly, string requestingAssemblyPath, string? requestedAssemblyPath);
+
private Assembly InternalLoadFromPath(string? assemblyPath, string? nativeImagePath)
{
RuntimeAssembly? loadedAssembly = null;
<opcodes>
<opcode name="AssemblyLoadContextResolvingHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedOpcodeMessage)" symbol="CLR_ALC_RESOLVING_HANDLER_INVOKED_OPCODE" value="12"/>
<opcode name="AppDomainAssemblyResolveHandlerInvoked" message="$(string.RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedOpcodeMessage)" symbol="CLR_APPDOMAIN_ASSEMBLY_RESOLVE_HANDLER_INVOKED_OPCODE" value="13"/>
+ <opcode name="AssemblyLoadFromResolveHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedOpcodeMessage)" symbol="CLR_ASSEMBLY_LOAD_FROM_RESOLVE_HANDLER_INVOKED_OPCODE" value="14"/>
</opcodes>
</task>
<!--Next available ID is 33-->
</UserData>
</template>
+ <template tid="AssemblyLoadFromResolveHandlerInvoked">
+ <data name="ClrInstanceID" inType="win:UInt16" />
+ <data name="AssemblyName" inType="win:UnicodeString" />
+ <data name="IsTrackedLoad" inType="win:Boolean" />
+ <data name="RequestingAssemblyPath" inType="win:UnicodeString" />
+ <data name="ComputedRequestedAssemblyPath" inType="win:UnicodeString" />
+ <UserData>
+ <AssemblyLoadFromResolveHandlerInvoked xmlns="myNs">
+ <ClrInstanceID> %1 </ClrInstanceID>
+ <AssemblyName> %2 </AssemblyName>
+ <IsTrackedAssembly> %3 </IsTrackedAssembly>
+ <RequestingAssemblyName> %4 </RequestingAssemblyName>
+ <ComputedRequestedAssemblyPath> %5 </ComputedRequestedAssemblyPath>
+ </AssemblyLoadFromResolveHandlerInvoked>
+ </UserData>
+ </template>
+
<template tid="MethodLoadUnload">
<data name="MethodID" inType="win:UInt64" outType="win:HexInt64" />
<data name="ModuleID" inType="win:UInt64" outType="win:HexInt64" />
task="AssemblyLoader"
symbol="AppDomainAssemblyResolveHandlerInvoked" message="$(string.RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedEventMessage)"/>
+ <event value="295" version="0" level="win:Informational" template="AssemblyLoadFromResolveHandlerInvoked"
+ keywords ="AssemblyLoaderKeyword" opcode="AssemblyLoadFromResolveHandlerInvoked"
+ task="AssemblyLoader"
+ symbol="AssemblyLoadFromResolveHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedEventMessage)"/>
+
</events>
</provider>
<string id="RuntimePublisher.AssemblyLoadStopEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nAssemblyPath=%3;%nRequestingAssembly=%4;%nAssemblyLoadContext=%5;%nRequestingAssemblyLoadContext=%6;%nSuccess=%7;%nResultAssemblyName=%8;%nResultAssemblyPath=%9;%nCached=%10" />
<string id="RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nHandlerName=%3;%nAssemblyLoadContext=%4;%nResultAssemblyName=%5;%nResultAssemblyPath=%6" />
<string id="RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nHandlerName=%3;%nResultAssemblyName=%4;%nResultAssemblyPath=%5" />
+ <string id="RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nIsTrackedLoad=%3;%nRequestingAssemblyPath=%4;%nComputedRequestedAssemblyPath=%5" />
<string id="RuntimePublisher.StackEventMessage" value="ClrInstanceID=%1;%nReserved1=%2;%nReserved2=%3;%nFrameCount=%4;%nStack=%5" />
<string id="RuntimePublisher.AppDomainMemAllocatedEventMessage" value="AppDomainID=%1;%nAllocated=%2;%nClrInstanceID=%3" />
<string id="RuntimePublisher.AppDomainMemSurvivedEventMessage" value="AppDomainID=%1;%nSurvived=%2;%nProcessSurvived=%3;%nClrInstanceID=%4" />
<string id="RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedOpcodeMessage" value="AssemblyLoadContextResolvingHandlerInvoked" />
<string id="RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedOpcodeMessage" value="AppDomainAssemblyResolveHandlerInvoked" />
+ <string id="RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedOpcodeMessage" value="AssemblyLoadFromResolveHandlerInvoked" />
<string id="RundownPublisher.MethodDCStartOpcodeMessage" value="DCStart" />
<string id="RundownPublisher.MethodDCEndOpcodeMessage" value="DCStop" />
FireEtwAppDomainAssemblyResolveHandlerInvoked(GetClrInstanceId(), assemblyName, handlerName, resultAssemblyName, resultAssemblyPath);
END_QCALL;
+}
+
+// static
+void QCALLTYPE AssemblyNative::TraceAssemblyLoadFromResolveHandlerInvoked(LPCWSTR assemblyName, bool isTrackedAssembly, LPCWSTR requestingAssemblyPath, LPCWSTR requestedAssemblyPath)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ FireEtwAssemblyLoadFromResolveHandlerInvoked(GetClrInstanceId(), assemblyName, isTrackedAssembly, requestingAssemblyPath, requestedAssemblyPath);
+
+ END_QCALL;
}
\ No newline at end of file
static BOOL QCALLTYPE InternalTryGetRawMetadata(QCall::AssemblyHandle assembly, UINT8 **blobRef, INT32 *lengthRef);
static void QCALLTYPE TraceResolvingHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR alcName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath);
static void QCALLTYPE TraceAssemblyResolveHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath);
+ static void QCALLTYPE TraceAssemblyLoadFromResolveHandlerInvoked(LPCWSTR assemblyName, bool isTrackedAssembly, LPCWSTR requestingAssemblyPath, LPCWSTR requestedAssemblyPath);
};
#endif
FCFuncElement("IsTracingEnabled", AssemblyNative::IsTracingEnabled)
QCFuncElement("TraceResolvingHandlerInvoked", AssemblyNative::TraceResolvingHandlerInvoked)
QCFuncElement("TraceAssemblyResolveHandlerInvoked", AssemblyNative::TraceAssemblyResolveHandlerInvoked)
+ QCFuncElement("TraceAssemblyLoadFromResolveHandlerInvoked", AssemblyNative::TraceAssemblyLoadFromResolveHandlerInvoked)
FCFuncEnd()
FCFuncStart(gAssemblyNameFuncs)
namespace AssemblyToLoad
{
public class Program
- { }
+ {
+ public static System.Reflection.Assembly UseDependentAssembly()
+ {
+ var p = new AssemblyToLoadDependency.Program();
+ return System.Reflection.Assembly.GetAssembly(p.GetType());
+ }
+ }
}
<ItemGroup>
<Compile Include="AssemblyToLoad.cs" />
</ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="AssemblyToLoadDependency.csproj" />
+ </ItemGroup>
</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace AssemblyToLoadDependency
+{
+ public class Program
+ { }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ <CLRTestKind>BuildOnly</CLRTestKind>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="AssemblyToLoadDependency.cs" />
+ </ItemGroup>
+</Project>
public List<HandlerInvocation> AssemblyLoadContextResolvingHandlers { get; internal set; }
public List<HandlerInvocation> AppDomainAssemblyResolveHandlers { get; internal set; }
+ public LoadFromHandlerInvocation AssemblyLoadFromHandler { get; internal set; }
public List<BindOperation> NestedBinds { get; internal set; }
}
}
+ internal class LoadFromHandlerInvocation
+ {
+ public AssemblyName AssemblyName { get; internal set; }
+ public bool IsTrackedLoad { get; internal set; }
+ public string RequestingAssemblyPath { get; internal set; }
+ public string ComputedRequestedAssemblyPath { get; internal set; }
+ }
+
internal sealed class BinderEventListener : EventListener
{
private const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80;
{
lock (eventsLock)
{
- Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadStop should have a matching AssemblyBindStart");
+ Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
BindOperation bind = bindOperations[data.ActivityId];
bind.Success = (bool)GetData("Success");
string resultName = GetDataString("ResultAssemblyName");
HandlerInvocation handlerInvocation = ParseHandlerInvokedEvent(GetDataString);
lock (eventsLock)
{
- Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadContextResolvingHandlerInvoked should have a matching AssemblyBindStart");
+ Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
BindOperation bind = bindOperations[data.ActivityId];
bind.AssemblyLoadContextResolvingHandlers.Add(handlerInvocation);
}
HandlerInvocation handlerInvocation = ParseHandlerInvokedEvent(GetDataString);
lock (eventsLock)
{
- Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AppDomainAssemblyResolveHandlerInvoked should have a matching AssemblyBindStart");
+ Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
BindOperation bind = bindOperations[data.ActivityId];
bind.AppDomainAssemblyResolveHandlers.Add(handlerInvocation);
}
break;
}
+ case "AssemblyLoadFromResolveHandlerInvoked":
+ {
+ LoadFromHandlerInvocation loadFrom = ParseLoadFromHandlerInvokedEvent(GetData, GetDataString);
+ lock (eventsLock)
+ {
+ Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
+ BindOperation bind = bindOperations[data.ActivityId];
+ bind.AssemblyLoadFromHandler = loadFrom;
+ }
+ break;
+ }
}
}
return handlerInvocation;
}
+
+ private LoadFromHandlerInvocation ParseLoadFromHandlerInvokedEvent(Func<string, object> getData, Func<string, string> getDataString)
+ {
+ var loadFrom = new LoadFromHandlerInvocation()
+ {
+ AssemblyName = new AssemblyName(getDataString("AssemblyName")),
+ IsTrackedLoad = (bool)getData("IsTrackedLoad"),
+ RequestingAssemblyPath = getDataString("RequestingAssemblyPath"),
+ ComputedRequestedAssemblyPath = getDataString("ComputedRequestedAssemblyPath"),
+ };
+ return loadFrom;
+ }
}
}
{
partial class BinderTracingTest
{
+ private const string AssemblyLoadFromHandlerName = "LoadFromResolveHandler";
+
[BinderTest]
public static BindOperation AssemblyLoadContextResolving_ReturnNull()
{
}
}
+ [BinderTest(isolate: true)]
+ public static BindOperation AssemblyLoadFromResolveHandler_LoadDependency()
+ {
+ string assemblyPath = GetAssemblyInSubdirectoryPath(SubdirectoryAssemblyName);
+ Assembly asm = Assembly.LoadFrom(assemblyPath);
+ Type t = asm.GetType(DependentAssemblyTypeName);
+ MethodInfo method = t.GetMethod("UseDependentAssembly", BindingFlags.Public | BindingFlags.Static);
+ Assembly asmDependency = (Assembly)method.Invoke(null, new object[0]);
+
+ return new BindOperation()
+ {
+ AssemblyName = asmDependency.GetName(),
+ AssemblyLoadContext = DefaultALC,
+ RequestingAssembly = asm.GetName(),
+ RequestingAssemblyLoadContext = DefaultALC,
+ Success = true,
+ ResultAssemblyName = asmDependency.GetName(),
+ ResultAssemblyPath = asmDependency.Location,
+ Cached = false,
+ AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>()
+ {
+ new HandlerInvocation()
+ {
+ AssemblyName = asmDependency.GetName(),
+ HandlerName = AssemblyLoadFromHandlerName,
+ ResultAssemblyName = asmDependency.GetName(),
+ ResultAssemblyPath = asmDependency.Location
+ }
+ },
+ AssemblyLoadFromHandler = new LoadFromHandlerInvocation()
+ {
+ AssemblyName = asmDependency.GetName(),
+ IsTrackedLoad = true,
+ RequestingAssemblyPath = asm.Location,
+ ComputedRequestedAssemblyPath = asmDependency.Location
+ }
+ };
+ }
+
+ [BinderTest(isolate: true)]
+ public static BindOperation AssemblyLoadFromResolveHandler_MissingDependency()
+ {
+ string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string assemblyPath = Path.Combine(appPath, $"{DependentAssemblyName}.dll");
+ Assembly asm = Assembly.LoadFrom(assemblyPath);
+ Type t = asm.GetType(DependentAssemblyTypeName);
+ MethodInfo method = t.GetMethod("UseDependentAssembly", BindingFlags.Public | BindingFlags.Static);
+ Assert.Throws<TargetInvocationException, FileNotFoundException>(() => method.Invoke(null, new object[0]));
+
+ var assemblyName = new AssemblyName(asm.FullName);
+ assemblyName.Name = "AssemblyToLoadDependency";
+ var expectedPath = Path.Combine(appPath, $"{assemblyName.Name}.dll");
+ return new BindOperation()
+ {
+ AssemblyName = assemblyName,
+ AssemblyLoadContext = DefaultALC,
+ RequestingAssembly = asm.GetName(),
+ RequestingAssemblyLoadContext = DefaultALC,
+ Success = false,
+ Cached = false,
+ AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>()
+ {
+ new HandlerInvocation()
+ {
+ AssemblyName = assemblyName,
+ HandlerName = AssemblyLoadFromHandlerName,
+ }
+ },
+ AssemblyLoadFromHandler = new LoadFromHandlerInvocation()
+ {
+ AssemblyName = assemblyName,
+ IsTrackedLoad = true,
+ RequestingAssemblyPath = asm.Location,
+ ComputedRequestedAssemblyPath = expectedPath
+ }
+ };
+ }
+
+ [BinderTest(isolate: true)]
+ public static BindOperation AssemblyLoadFromResolveHandler_NotTracked()
+ {
+ string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string assemblyPath = Path.Combine(appPath, $"{DependentAssemblyName}.dll");
+ Assembly.LoadFrom(assemblyPath);
+
+ var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+ Assert.Throws<FileNotFoundException>(() => Assembly.Load(SubdirectoryAssemblyName));
+
+ Assembly executingAssembly = Assembly.GetExecutingAssembly();
+ return new BindOperation()
+ {
+ AssemblyName = assemblyName,
+ AssemblyLoadContext = DefaultALC,
+ RequestingAssembly = executingAssembly.GetName(),
+ RequestingAssemblyLoadContext = DefaultALC,
+ Success = false,
+ Cached = false,
+ AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>()
+ {
+ new HandlerInvocation()
+ {
+ AssemblyName = assemblyName,
+ HandlerName = AssemblyLoadFromHandlerName,
+ }
+ },
+ AssemblyLoadFromHandler = new LoadFromHandlerInvocation()
+ {
+ AssemblyName = assemblyName,
+ IsTrackedLoad = false,
+ RequestingAssemblyPath = executingAssembly.Location
+ }
+ };
+ }
+
+ private static string GetAssemblyInSubdirectoryPath(string assemblyName)
+ {
+ string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ return Path.Combine(appPath, "DependentAssemblies", $"{assemblyName}.dll");
+ }
+
private enum HandlerReturn
{
Null,
if (handlerReturn == HandlerReturn.Null)
return null;
- string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- string fileName = handlerReturn == HandlerReturn.RequestedAssembly ? $"{assemblyName.Name}.dll" : $"{assemblyName.Name}Mismatch.dll";
- string assemblyPath = Path.Combine(appPath, "DependentAssemblies", fileName);
+ string name = handlerReturn == HandlerReturn.RequestedAssembly ? assemblyName.Name : $"{assemblyName.Name}Mismatch";
+ string assemblyPath = GetAssemblyInSubdirectoryPath(name);
if (!File.Exists(assemblyPath))
return null;
private const string DefaultALC = "Default";
private const string DependentAssemblyName = "AssemblyToLoad";
+ private const string DependentAssemblyTypeName = "AssemblyToLoad.Program";
private const string SubdirectoryAssemblyName = "AssemblyToLoad_Subdirectory";
private static readonly AssemblyName CoreLibName = typeof(object).Assembly.GetName();
private static Type GetDependentAssemblyType()
{
- return Type.GetType($"AssemblyToLoad.Program, {DependentAssemblyName}");
+ return Type.GetType($"{DependentAssemblyTypeName}, {DependentAssemblyName}");
}
private static Type LoadTestClassInALC(AssemblyLoadContext alc)
ValidateHandlerInvocations(expected.AssemblyLoadContextResolvingHandlers, actual.AssemblyLoadContextResolvingHandlers, "AssemblyLoadContextResolving");
ValidateHandlerInvocations(expected.AppDomainAssemblyResolveHandlers, actual.AppDomainAssemblyResolveHandlers, "AppDomainAssemblyResolve");
+ ValidateLoadFromHandlerInvocation(expected.AssemblyLoadFromHandler, actual.AssemblyLoadFromHandler);
ValidateNestedBinds(expected.NestedBinds, actual.NestedBinds);
}
}
}
+ private static void ValidateLoadFromHandlerInvocation(LoadFromHandlerInvocation expected, LoadFromHandlerInvocation actual)
+ {
+ if (expected == null || actual == null)
+ {
+ Assert.IsNull(expected);
+ Assert.IsNull(actual);
+ return;
+ }
+
+ ValidateAssemblyName(expected.AssemblyName, actual.AssemblyName, nameof(LoadFromHandlerInvocation.AssemblyName));
+ Assert.AreEqual(expected.IsTrackedLoad, actual.IsTrackedLoad, $"Unexpected value for {nameof(LoadFromHandlerInvocation.IsTrackedLoad)} on event");
+ Assert.AreEqual(expected.RequestingAssemblyPath, actual.RequestingAssemblyPath, $"Unexpected value for {nameof(LoadFromHandlerInvocation.RequestingAssemblyPath)} on event");
+ Assert.AreEqual(expected.ComputedRequestedAssemblyPath, actual.ComputedRequestedAssemblyPath, $"Unexpected value for {nameof(LoadFromHandlerInvocation.ComputedRequestedAssemblyPath)} on event");
+ }
+
private static bool BindOperationsMatch(BindOperation bind1, BindOperation bind2)
{
try
<Target Name="MoveDependentAssembliesToSubdirectory" AfterTargets="CopyFilesToOutputDirectory">
<ItemGroup>
<AssembliesToMove Include="$(OutDir)/AssemblyToLoad_*.*" />
+ <AssembliesToMove Include="$(OutDir)/AssemblyToLoadDependency.*" />
</ItemGroup>
<Move SourceFiles="@(AssembliesToMove)" DestinationFolder="$(OutDir)/DependentAssemblies" />
</Target>
{
// If the requestor assembly was not loaded using LoadFrom, exit.
if (!s_loadFromAssemblyList.Contains(requestorPath))
+ {
+#if CORECLR
+ if (AssemblyLoadContext.IsTracingEnabled())
+ {
+ AssemblyLoadContext.TraceAssemblyLoadFromResolveHandlerInvoked(args.Name, false, requestorPath, null);
+ }
+#endif // CORECLR
return null;
+ }
}
// Requestor assembly was loaded using loadFrom, so look for its dependencies
// Form the name of the assembly using the path of the assembly that requested its load.
AssemblyName requestedAssemblyName = new AssemblyName(args.Name!);
string requestedAssemblyPath = Path.Combine(Path.GetDirectoryName(requestorPath)!, requestedAssemblyName.Name + ".dll");
-
+#if CORECLR
+ if (AssemblyLoadContext.IsTracingEnabled())
+ {
+ AssemblyLoadContext.TraceAssemblyLoadFromResolveHandlerInvoked(args.Name, true, requestorPath, requestedAssemblyPath);
+ }
+#endif // CORECLR
try
{
// Load the dependency via LoadFrom so that it goes through the same path of being in the LoadFrom list.