Skip to content

Commit e3b043b

Browse files
author
Mohamed Koubaa
committed
load the assembly and get function address inside the domain
When using domain.Load for an assembly, the assembly resolution rules are awkward Even if the full path is given to the AssemblyName, when the domain tries to load the assembly, it does not use that context and tries to resolve the assembly using normal domain resolution rules, which would require an assembly resolver to be installed. However, the assembly resolver that is actually used at runtime is the one installed to the main appdomain. This prevents a library like Python.Runtime.dll (used by pythonnet) which is not installed to the application base directory to be loaded by clr_loader. To fix this issue, the assembly resolver of the main appdomain is lazily extending to include paths needed for libraries passed into GetFunction, and GetFunction internally uses AppDomain.DoCallBack() to marshal the function pointer inside the target app domain, using global domain data to access the function pointer and return it to the user of clr_loader.
1 parent f2058b4 commit e3b043b

File tree

2 files changed

+73
-20
lines changed

2 files changed

+73
-20
lines changed

netfx_loader/ClrLoader.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Reflection;
45
using System.Runtime.InteropServices;
56
using NXPorts.Attributes;
67

@@ -21,23 +22,35 @@ public static void Initialize()
2122
}
2223
}
2324

25+
private static string AssemblyDirectory
26+
{
27+
get
28+
{
29+
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
30+
UriBuilder uri = new UriBuilder(codeBase);
31+
string path = Uri.UnescapeDataString(uri.Path);
32+
return Path.GetDirectoryName(path);
33+
}
34+
}
35+
2436
[DllExport("pyclr_create_appdomain", CallingConvention.Cdecl)]
2537
public static IntPtr CreateAppDomain(
2638
[MarshalAs(UnmanagedType.LPUTF8Str)] string name,
2739
[MarshalAs(UnmanagedType.LPUTF8Str)] string configFile
2840
)
2941
{
3042
Print($"Creating AppDomain {name} with {configFile}");
43+
44+
var clrLoaderDir = AssemblyDirectory;
3145
if (!string.IsNullOrEmpty(name))
3246
{
3347
var setup = new AppDomainSetup
3448
{
35-
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
49+
ApplicationBase = clrLoaderDir,
3650
ConfigurationFile = configFile
3751
};
38-
Print($"Base: {AppDomain.CurrentDomain.BaseDirectory}");
52+
Print($"Base: {clrLoaderDir}");
3953
var domain = AppDomain.CreateDomain(name, null, setup);
40-
4154
Print($"Located domain {domain}");
4255

4356
var domainData = new DomainData(domain);
@@ -61,8 +74,8 @@ public static IntPtr GetFunction(
6174
try
6275
{
6376
var domainData = _domains[(int)domain];
64-
var deleg = domainData.GetEntryPoint(assemblyPath, typeName, function);
65-
return Marshal.GetFunctionPointerForDelegate(deleg);
77+
Print($"Getting functor for function {function} of type {typeName} in assembly {assemblyPath}");
78+
return domainData.GetFunctor(assemblyPath, typeName, function);
6679
}
6780
catch (Exception exc)
6881
{

netfx_loader/DomainData.cs

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,94 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Reflection;
4+
using System.Runtime.InteropServices;
45

56
namespace ClrLoader
67
{
78
using static ClrLoader;
89

9-
class DomainData : IDisposable
10+
public static class DomainSetup
1011
{
1112
public delegate int EntryPoint(IntPtr buffer, int size);
13+
public static void StoreFunctorFromDomainData()
14+
{
15+
var domain = AppDomain.CurrentDomain;
16+
var assemblyPath = (string)domain.GetData("_assemblyPath");
17+
var typeName = (string)domain.GetData("_typeName");
18+
var function = (string)domain.GetData("_function");
19+
var functor = GetFunctor(domain, assemblyPath, typeName, function);
20+
domain.SetData("_thisFunctor", functor);
21+
}
22+
23+
private static IntPtr GetFunctor(AppDomain domain, string assemblyPath, string typeName, string function)
24+
{
25+
var assemblyName = AssemblyName.GetAssemblyName(assemblyPath).Name;
26+
var assembly = domain.Load(AssemblyName.GetAssemblyName(assemblyPath));
27+
var type = assembly.GetType(typeName, throwOnError: true);
28+
var deleg = Delegate.CreateDelegate(typeof(EntryPoint), type, function);
29+
IntPtr result = Marshal.GetFunctionPointerForDelegate(deleg);
30+
return result;
31+
}
32+
}
1233

34+
class DomainData : IDisposable
35+
{
1336
bool _disposed = false;
1437

1538
public AppDomain Domain { get; }
16-
public Dictionary<(string, string, string), EntryPoint> _delegates;
39+
public Dictionary<(string, string, string), IntPtr> _functors;
40+
public HashSet<string> _resolvedAssemblies;
1741

1842
public DomainData(AppDomain domain)
1943
{
2044
Domain = domain;
21-
_delegates = new Dictionary<(string, string, string), EntryPoint>();
45+
_functors = new Dictionary<(string, string, string), IntPtr>();
46+
_resolvedAssemblies = new HashSet<string>();
2247
}
2348

24-
public EntryPoint GetEntryPoint(string assemblyPath, string typeName, string function)
49+
private void installResolver(string assemblyPath)
50+
{
51+
var assemblyName = AssemblyName.GetAssemblyName(assemblyPath).Name;
52+
if (_resolvedAssemblies.Contains(assemblyName))
53+
return;
54+
_resolvedAssemblies.Add(assemblyName);
55+
56+
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
57+
{
58+
if (args.Name.Contains(assemblyName))
59+
return Assembly.LoadFrom(assemblyPath);
60+
return null;
61+
};
62+
}
63+
64+
public IntPtr GetFunctor(string assemblyPath, string typeName, string function)
2565
{
2666
if (_disposed)
2767
throw new InvalidOperationException("Domain is already disposed");
2868

29-
var key = (assemblyPath, typeName, function);
69+
installResolver(assemblyPath);
3070

31-
EntryPoint result;
71+
var key = (assemblyPath, typeName, function);
3272

33-
if (!_delegates.TryGetValue(key, out result))
73+
IntPtr result;
74+
if (!_functors.TryGetValue(key, out result))
3475
{
35-
var assembly = Domain.Load(AssemblyName.GetAssemblyName(assemblyPath));
36-
var type = assembly.GetType(typeName, throwOnError: true);
76+
Domain.SetData("_assemblyPath", assemblyPath);
77+
Domain.SetData("_typeName", typeName);
78+
Domain.SetData("_function", function);
3779

38-
Print($"Loaded type {type}");
39-
result = (EntryPoint)Delegate.CreateDelegate(typeof(EntryPoint), type, function);
40-
41-
_delegates[key] = result;
80+
Domain.DoCallBack(new CrossAppDomainDelegate(DomainSetup.StoreFunctorFromDomainData));
81+
result = (IntPtr)Domain.GetData("_thisFunctor");
82+
_functors[key] = result;
4283
}
43-
4484
return result;
4585
}
4686

4787
public void Dispose()
4888
{
4989
if (!_disposed)
5090
{
51-
_delegates.Clear();
91+
_functors.Clear();
5292

5393
if (Domain != AppDomain.CurrentDomain)
5494
AppDomain.Unload(Domain);

0 commit comments

Comments
 (0)