[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:
Rolf Bjarne Kvinge 2021-03-25 15:56:56 +01:00 коммит произвёл GitHub
Родитель 1d9c9baf25 a82575189b
Коммит cfe0a309dc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 283 добавлений и 2 удалений

Просмотреть файл

@ -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 \