[CoreCLR] Add support for mono_assembly_open. (#10962)
This requires a few things: * [runtime] Add support for generating managed delegates only for CoreCLR. However, since our managed code is shared between CoreCLR and MonoVM, the best we can do is to make these CoreCLR-only delegates .NET-only. * [runtime] Make it possible to implement Mono Embedding API in the CoreCLR bridge By making it possible to skip the automatically generated Mono Embedding * [runtime] Add a MonoObject implementation for CoreCLR. We need a way to represent a managed object in native code, and since most our existing runtime code uses MonoObjects, we use the same for the CoreCLR bridge, just our own version of it. In Mono, the MonoObjects are tracked by the GC (which scans the stack), but we can't make CoreCLR scan the stack, so we use a reference counted version of MonoObject instead - we just put the GCHandle into a reference counted MonoObject, and when the MonoObject is freed, then we free the GCHandle as well. * Go through all uses of mono_assembly_open, and make sure they release the returned assembly.
This commit is contained in:
Коммит
cfe0a309dc
|
@ -26,16 +26,36 @@ namespace ObjCRuntime {
|
|||
public unsafe partial class Runtime {
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if NET\n");
|
||||
#>
|
||||
internal delegate <#= d.ReturnType.MType #> <#= d.SimpleEntryPoint #>_delegate (<#= d.MArgumentSignature #>);
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // NET\n");
|
||||
#>
|
||||
<# } #>
|
||||
|
||||
internal struct Delegates {
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if NET\n");
|
||||
#>
|
||||
public IntPtr <#= d.SimpleEntryPoint #>;
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // NET\n");
|
||||
#>
|
||||
<# } #>
|
||||
}
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if NET\n");
|
||||
#>
|
||||
[MonoPInvokeCallback (typeof (<#= d.SimpleEntryPoint #>_delegate))]
|
||||
static <#= d.ReturnType.MType #> <#= d.SimpleEntryPoint #> (<#= d.MArgumentSignature #>)
|
||||
<# if (d.ExceptionHandling) { #>
|
||||
|
@ -65,6 +85,10 @@ namespace ObjCRuntime {
|
|||
<# } #>
|
||||
}
|
||||
<# } #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // NET\n");
|
||||
#>
|
||||
|
||||
<# } #>
|
||||
|
||||
|
@ -73,7 +97,15 @@ namespace ObjCRuntime {
|
|||
{
|
||||
<# foreach (var d in delegates) {
|
||||
if (d.OnlyDynamicUsage) continue; #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if NET\n");
|
||||
#>
|
||||
options->Delegates-><#= d.SimpleEntryPoint #> = GetFunctionPointer (new <#= d.SimpleEntryPoint #>_delegate (<#= d.SimpleEntryPoint #>));
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // NET\n");
|
||||
#>
|
||||
<# } #>
|
||||
|
||||
// The linker will remove this condition (and the subsequent method call) if possible
|
||||
|
@ -85,7 +117,15 @@ namespace ObjCRuntime {
|
|||
{
|
||||
<# foreach (var d in delegates) {
|
||||
if (!d.OnlyDynamicUsage) continue; #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if NET\n");
|
||||
#>
|
||||
options->Delegates-><#= d.SimpleEntryPoint #> = GetFunctionPointer (new <#= d.SimpleEntryPoint #>_delegate (<#= d.SimpleEntryPoint #>));
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // NET\n");
|
||||
#>
|
||||
<# } #>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,9 @@ xamarin_bridge_call_runtime_initialize (struct InitializationOptions* options, G
|
|||
void
|
||||
xamarin_bridge_register_product_assembly (GCHandle* exception_gchandle)
|
||||
{
|
||||
xamarin_open_and_register (PRODUCT_DUAL_ASSEMBLY, exception_gchandle);
|
||||
MonoAssembly *assembly;
|
||||
assembly = xamarin_open_and_register (PRODUCT_DUAL_ASSEMBLY, exception_gchandle);
|
||||
xamarin_mono_object_release (&assembly);
|
||||
}
|
||||
|
||||
MonoClass *
|
||||
|
@ -116,4 +118,46 @@ xamarin_get_runtime_class ()
|
|||
xamarin_assertion_message ("The method %s it not implemented yet for CoreCLR", __func__);
|
||||
}
|
||||
|
||||
void
|
||||
xamarin_mono_object_retain (MonoObject *mobj)
|
||||
{
|
||||
atomic_fetch_add (&mobj->reference_count, 1);
|
||||
}
|
||||
|
||||
void
|
||||
xamarin_mono_object_release (MonoObject **mobj_ref)
|
||||
{
|
||||
MonoObject *mobj = *mobj_ref;
|
||||
|
||||
if (mobj == NULL)
|
||||
return;
|
||||
|
||||
int rc = atomic_fetch_sub (&mobj->reference_count, 1) - 1;
|
||||
if (rc == 0) {
|
||||
if (mobj->gchandle != INVALID_GCHANDLE) {
|
||||
xamarin_gchandle_free (mobj->gchandle);
|
||||
mobj->gchandle = INVALID_GCHANDLE;
|
||||
}
|
||||
|
||||
xamarin_free (mobj); // allocated using Marshal.AllocHGlobal.
|
||||
}
|
||||
|
||||
*mobj_ref = NULL;
|
||||
}
|
||||
|
||||
/* Implementation of the Mono Embedding API */
|
||||
|
||||
// returns a retained MonoAssembly *
|
||||
MonoAssembly *
|
||||
mono_assembly_open (const char * filename, MonoImageOpenStatus * status)
|
||||
{
|
||||
assert (status == NULL);
|
||||
|
||||
MonoAssembly *rv = xamarin_find_assembly (filename);
|
||||
|
||||
LOG_CORECLR (stderr, "mono_assembly_open (%s, %p) => MonoObject=%p GCHandle=%p\n", filename, status, rv, rv->gchandle);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif // CORECLR_RUNTIME
|
||||
|
|
|
@ -16,12 +16,28 @@
|
|||
#include "delegates.h"
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if DOTNET\n");
|
||||
#>
|
||||
typedef <#= d.ReturnType.InterfaceCType #><#= d.AlignCReturnType #> (*func_<#= d.EntryPoint #>)<#= d.AlignEntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // DOTNET\n");
|
||||
#>
|
||||
<# } #>
|
||||
|
||||
struct Delegates {
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if DOTNET\n");
|
||||
#>
|
||||
func_<#= d.EntryPoint #><#= d.AlignEntryPoint #> <#= d.EntryPoint.Substring ("xamarin_".Length) #>;
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // DOTNET\n");
|
||||
#>
|
||||
<# } #>
|
||||
};
|
||||
|
||||
|
@ -37,5 +53,13 @@ create_linked_away_exception (const char *func)
|
|||
}
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if defined (CORECLR_RUNTIME)\n");
|
||||
#>
|
||||
<#= d.Function #>
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // CORECLR_RUNTIME\n");
|
||||
#>
|
||||
<# } #>
|
||||
|
|
|
@ -286,6 +286,15 @@
|
|||
WrappedManagedFunction = "CreateNSObject",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoAssembly *", "IntPtr", "xamarin_find_assembly",
|
||||
"const char *","IntPtr", "assembly_name"
|
||||
) {
|
||||
WrappedManagedFunction = "FindAssembly",
|
||||
OnlyDynamicUsage = false,
|
||||
OnlyCoreCLR = true,
|
||||
},
|
||||
|
||||
};
|
||||
delegates.CalculateLengths ();
|
||||
#><#+
|
||||
|
@ -335,6 +344,7 @@
|
|||
public bool ExceptionHandling = true;
|
||||
// Detemines whether the function is only used by the dynamic registrar (in which case we might be able to link the function away if the static registrar is being used)
|
||||
public bool OnlyDynamicUsage;
|
||||
public bool OnlyCoreCLR;
|
||||
|
||||
public string DelegateName {
|
||||
get {
|
||||
|
|
|
@ -251,7 +251,9 @@
|
|||
new Export ("MonoAssembly *", "mono_assembly_open",
|
||||
"const char *", "filename",
|
||||
"MonoImageOpenStatus *", "status"
|
||||
),
|
||||
) {
|
||||
HasCoreCLRBridgeFunction = true,
|
||||
},
|
||||
|
||||
new Export ("MonoImage *", "mono_assembly_get_image",
|
||||
"MonoAssembly *", "assembly"
|
||||
|
@ -706,6 +708,7 @@
|
|||
public bool Optional;
|
||||
public DotNetMode Mode;
|
||||
public RuntimeMode XamarinRuntime;
|
||||
public bool HasCoreCLRBridgeFunction;
|
||||
|
||||
public Export (string returnType, string entryPoint, params string [] arguments)
|
||||
: this (false, returnType, entryPoint, arguments)
|
||||
|
|
|
@ -550,6 +550,8 @@ run_application_init (xamarin_initialize_data *data)
|
|||
|
||||
MonoImage *image = mono_assembly_get_image (assembly);
|
||||
|
||||
xamarin_mono_object_release (&assembly);
|
||||
|
||||
MonoClass *app_class = mono_class_from_name (image, "AppKit", "NSApplication");
|
||||
if (!app_class)
|
||||
xamarin_assertion_message ("Fatal error: failed to load the NSApplication class");
|
||||
|
|
|
@ -65,7 +65,12 @@ char *xamarin_get_mono_runtime_build_info (); // returns NULL if libmono couldn'
|
|||
typedef int32_t mono_bool;
|
||||
|
||||
/* metadata/image.h */
|
||||
#if defined (CORECLR_RUNTIME)
|
||||
// In Mono, MonoAssembly is not related to MonoObject, but for the CoreCLR bridge we use the same memory representation for both types.
|
||||
typedef struct _MonoObject MonoAssembly;
|
||||
#else
|
||||
typedef struct _MonoAssembly MonoAssembly;
|
||||
#endif
|
||||
typedef struct _MonoAssemblyName MonoAssemblyName;
|
||||
typedef struct _MonoImage MonoImage;
|
||||
|
||||
|
|
|
@ -126,6 +126,8 @@ int xamarin_fix_ranlib_warning_about_no_symbols;
|
|||
<# foreach (var export in exports) {
|
||||
if (export.XamarinRuntime == RuntimeMode.MonoVM)
|
||||
continue;
|
||||
if (export.HasCoreCLRBridgeFunction)
|
||||
continue;
|
||||
#>
|
||||
MONO_API <#= export.ReturnType #>
|
||||
<#= export.EntryPoint #> (<#= export.ArgumentSignature #>)
|
||||
|
|
|
@ -471,6 +471,8 @@ xamarin_main (int argc, char *argv[], enum XamarinLaunchMode launch_mode)
|
|||
xamarin_assertion_message ("Invalid launch mode: %i.", launch_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
xamarin_mono_object_release (&assembly);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -143,6 +143,8 @@ void
|
|||
xamarin_bridge_register_product_assembly (GCHandle* exception_gchandle)
|
||||
{
|
||||
xamarin_register_monoassembly (entry_assembly, exception_gchandle);
|
||||
// We don't need the entry_assembly around anymore, so release it.
|
||||
xamarin_mono_object_release (&entry_assembly);
|
||||
}
|
||||
|
||||
MonoClass *
|
||||
|
|
|
@ -24,9 +24,15 @@ extern "C" {
|
|||
<# foreach (var d in delegates) {
|
||||
if (d.OnlyDynamicUsage)
|
||||
continue;
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#if defined (CORECLR_RUNTIME)\n");
|
||||
#>
|
||||
<#= d.ReturnType.ExposedCType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignatureFunctionDeclaration #>);
|
||||
<#
|
||||
if (d.OnlyCoreCLR)
|
||||
Write ("#endif // CORECLR_RUNTIME\n");
|
||||
#>
|
||||
|
||||
<# } #>
|
||||
|
||||
|
|
|
@ -941,6 +941,7 @@ xamarin_file_exists (const char *path)
|
|||
return stat (path, &buffer) == 0;
|
||||
}
|
||||
|
||||
// Returns a retained MonoObject. Caller must release.
|
||||
MonoAssembly *
|
||||
xamarin_open_assembly (const char *name)
|
||||
{
|
||||
|
@ -996,6 +997,7 @@ xamarin_register_monoassembly (MonoAssembly *assembly, GCHandle *exception_gchan
|
|||
return *exception_gchandle == INVALID_GCHANDLE;
|
||||
}
|
||||
|
||||
// Returns a retained MonoObject. Caller must release.
|
||||
MonoAssembly *
|
||||
xamarin_open_and_register (const char *aname, GCHandle *exception_gchandle)
|
||||
{
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef __CORECLR_BRIDGE__
|
||||
#define __CORECLR_BRIDGE__
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
//#define LOG_CORECLR(...)
|
||||
#define LOG_CORECLR(...) fprintf (__VA_ARGS__)
|
||||
|
||||
|
@ -13,6 +15,20 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
// We need a way to represent a managed object in native code, and since most
|
||||
// our existing runtime code uses MonoObjects, we use the same for the CoreCLR
|
||||
// bridge, just our own version of it. In Mono, the MonoObjects are tracked by
|
||||
// the GC (which scans the stack), but we can't make CoreCLR scan the stack,
|
||||
// so we use a reference counted version of MonoObject instead - we just put
|
||||
// the GCHandle into a reference counted MonoObject, and when the MonoObject
|
||||
// is freed, then we free the GCHandle as well.
|
||||
//
|
||||
// This struct must be kept in sync with the MonoObject struct in Runtime.CoreCLR.cs
|
||||
struct _MonoObject {
|
||||
int _Atomic reference_count;
|
||||
GCHandle gchandle;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
|
|
@ -280,6 +280,24 @@ MonoObject * xamarin_gchandle_get_target (GCHandle handle);
|
|||
void xamarin_gchandle_free (GCHandle handle);
|
||||
MonoObject * xamarin_gchandle_unwrap (GCHandle handle); // Will get the target and free the GCHandle
|
||||
|
||||
/*
|
||||
* In MonoVM MonoObjects are tracked in memory/the stack directly by the GC, but that doesn't
|
||||
* work for CoreCLR, so we make it ref-counted. All code must use the functions below to retain/release
|
||||
* MonoObjects, although these functions do nothing when using MonoVM.
|
||||
*
|
||||
* The release function take a pointer to the variable that contains the MonoObject, and clears out the value,
|
||||
* to avoid running into use-after-free problems.
|
||||
*/
|
||||
#if defined(CORECLR_RUNTIME)
|
||||
void xamarin_mono_object_retain (MonoObject *mobj);
|
||||
void xamarin_mono_object_release (MonoObject **mobj);
|
||||
#else
|
||||
// Nothing to do here.
|
||||
#define xamarin_mono_object_retain(x)
|
||||
#define xamarin_mono_object_release(x) do { *x = NULL; } while (0);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Look for an assembly in the app and open it.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// Runtime.CoreCLR.cs: Supporting managed code for the CoreCLR bridge
|
||||
//
|
||||
// Authors:
|
||||
// Rolf Bjarne Kvinge
|
||||
//
|
||||
// Copyright 2021 Microsoft Corp.
|
||||
|
||||
#if NET && !COREBUILD
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ObjCRuntime {
|
||||
|
||||
public partial class Runtime {
|
||||
// This struct must be kept in sync with the _MonoObject struct in coreclr-bridge.h
|
||||
[StructLayout (LayoutKind.Sequential)]
|
||||
struct MonoObject {
|
||||
public int ReferenceCount;
|
||||
public IntPtr GCHandle;
|
||||
}
|
||||
|
||||
// Comment out the attribute to get all printfs
|
||||
[System.Diagnostics.Conditional ("UNDEFINED")]
|
||||
static void log_coreclr (string message)
|
||||
{
|
||||
xamarin_log (message);
|
||||
}
|
||||
|
||||
// Returns a retained MonoObject. Caller must release.
|
||||
static IntPtr FindAssembly (IntPtr assembly_name)
|
||||
{
|
||||
var path = Marshal.PtrToStringAuto (assembly_name);
|
||||
var name = Path.GetFileNameWithoutExtension (path);
|
||||
|
||||
log_coreclr ($"Runtime.FindAssembly (0x{assembly_name.ToString ("x")} = {name})");
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies ()) {
|
||||
log_coreclr ($" Assembly from app domain: {asm.GetName ().Name}");
|
||||
if (asm.GetName ().Name == name) {
|
||||
log_coreclr ($" Match!");
|
||||
return GetMonoObject (asm);
|
||||
}
|
||||
}
|
||||
|
||||
log_coreclr ($" Did not find the assembly in the app domain's loaded assemblies. Will try to load it.");
|
||||
|
||||
var loadedAssembly = Assembly.LoadFrom (path);
|
||||
if (loadedAssembly != null) {
|
||||
log_coreclr ($" Loaded {loadedAssembly.GetName ().Name}");
|
||||
return GetMonoObject (loadedAssembly);
|
||||
}
|
||||
|
||||
log_coreclr ($" Found no assembly named {name}");
|
||||
|
||||
throw new InvalidOperationException ($"Could not find any assemblies named {name}");
|
||||
}
|
||||
|
||||
// Returns a retained MonoObject. Caller must release.
|
||||
static IntPtr GetMonoObject (object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
return GetMonoObjectImpl (obj);
|
||||
}
|
||||
|
||||
// Returns a retained MonoObject. Caller must release.
|
||||
static IntPtr GetMonoObjectImpl (object obj)
|
||||
{
|
||||
var handle = AllocGCHandle (obj);
|
||||
|
||||
var mobj = new MonoObject ();
|
||||
mobj.GCHandle = handle;
|
||||
mobj.ReferenceCount = 1;
|
||||
|
||||
IntPtr rv = MarshalStructure (mobj);
|
||||
|
||||
log_coreclr ($"GetMonoObjectImpl ({obj.GetType ()}) => 0x{rv.ToString ("x")} => GCHandle=0x{handle.ToString ("x")}");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static IntPtr MarshalStructure<T> (T value) where T: struct
|
||||
{
|
||||
var rv = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (T)));
|
||||
StructureToPtr (value, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void StructureToPtr (object obj, IntPtr ptr)
|
||||
{
|
||||
if (obj == null)
|
||||
return;
|
||||
|
||||
Marshal.StructureToPtr (obj, ptr, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NET && !COREBUILD
|
|
@ -1853,6 +1853,7 @@ SHARED_SOURCES = \
|
|||
ObjCRuntime/ReleaseAttribute.cs \
|
||||
ObjCRuntime/RequiredFrameworkAttribute.cs \
|
||||
ObjCRuntime/Runtime.cs \
|
||||
ObjCRuntime/Runtime.CoreCLR.cs \
|
||||
ObjCRuntime/Runtime.iOS.cs \
|
||||
ObjCRuntime/Runtime.mac.cs \
|
||||
ObjCRuntime/RuntimeException.cs \
|
||||
|
|
Загрузка…
Ссылка в новой задаче