Skip to content

Commit e445985

Browse files
committed
[mono] Implement public hot reload API
Also add ApplyUpdateSdb that takes byte[] arguments for use with mono/debugger-libs as its awkward to call a ROS<byte> method from the debugger. Contributes to dotnet#45689
1 parent 8b2b2f0 commit e445985

File tree

7 files changed

+86
-47
lines changed

7 files changed

+86
-47
lines changed

src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml

+6-5
Original file line numberDiff line numberDiff line change
@@ -548,11 +548,6 @@
548548
<method signature="System.Void .ctor(System.Object)" />
549549
</type>
550550

551-
<!-- Used by metadata update -->
552-
<type fullname="System.Runtime.CompilerServices.RuntimeFeature">
553-
<method name="LoadMetadataUpdate" />
554-
</type>
555-
556551
<!-- domain.c: mono_defaults.marshal_class -->
557552
<type fullname="System.Runtime.InteropServices.Marshal" preserve="fields" >
558553
<!-- marshal.c (mono_marshal_get_struct_to_ptr) -->
@@ -637,5 +632,11 @@
637632
<!-- marshal-ilgen.c:emit_invoke_call -->
638633
<method signature="System.Void .ctor()" />
639634
</type>
635+
636+
<!-- Used by metadata update -->
637+
<type fullname="System.Reflection.Metadata.AssemblyExtensions">
638+
<method name="ApplyUpdateSdb" />
639+
</type>
640+
640641
</assembly>
641642
</linker>
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,69 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Runtime.CompilerServices;
5+
46
namespace System.Reflection.Metadata
57
{
68
public static class AssemblyExtensions
79
{
810
[CLSCompliant(false)]
911
public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) => throw new NotImplementedException();
1012

11-
public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDelta, ReadOnlySpan<byte> ilDelta, ReadOnlySpan<byte> pdbDelta) => throw new NotImplementedException();
13+
/// <summary>
14+
/// Updates the specified assembly using the provided metadata, IL and PDB deltas.
15+
/// </summary>
16+
/// <remarks>
17+
/// Currently executing methods will continue to use the existing IL. New executions of modified methods will
18+
/// use the new IL. Different runtimes may have different limitations on what kinds of changes are supported,
19+
/// and runtimes make no guarantees as to the state of the assembly and process if the delta includes
20+
/// unsupported changes.
21+
/// </remarks>
22+
/// <param name="assembly">The assembly to update.</param>
23+
/// <param name="metadataDelta">The metadata changes to be applied.</param>
24+
/// <param name="ilDelta">The IL changes to be applied.</param>
25+
/// <param name="pdbDelta">The PDB changes to be applied.</param>
26+
/// <exception cref="ArgumentNullException">The assembly argument is null.</exception>
27+
/// <exception cref="NotSupportedException">The update could not be applied.</exception>
28+
public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDelta, ReadOnlySpan<byte> ilDelta, ReadOnlySpan<byte> pdbDelta = default)
29+
{
30+
#if !FEATURE_METADATA_UPDATE
31+
throw new NotSupportedException ("Method body replacement not supported in this runtime");
32+
#else
33+
if (assembly == null)
34+
{
35+
throw new ArgumentNullException(nameof(assembly));
36+
}
37+
38+
RuntimeAssembly? runtimeAssembly = assembly as RuntimeAssembly;
39+
if (runtimeAssembly == null)
40+
{
41+
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
42+
}
43+
44+
unsafe
45+
{
46+
IntPtr monoAssembly = runtimeAssembly.GetUnderlyingNativeHandle ();
47+
fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta)
48+
{
49+
ApplyUpdate_internal(monoAssembly, metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length);
50+
}
51+
}
52+
#endif
53+
}
54+
55+
internal static void ApplyUpdateSdb(Assembly assembly, byte[] metadataDelta, byte[] ilDelta, byte[]? pdbDelta = null)
56+
{
57+
ReadOnlySpan<byte> md = metadataDelta;
58+
ReadOnlySpan<byte> il = ilDelta;
59+
ReadOnlySpan<byte> dpdb = pdbDelta == null ? default : pdbDelta;
60+
ApplyUpdate (assembly, md, il, dpdb);
61+
}
62+
63+
#if FEATURE_METADATA_UPDATE
64+
[MethodImpl (MethodImplOptions.InternalCall)]
65+
private static unsafe extern void ApplyUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length, byte *dpdb_bytes, int dpdb_length);
66+
#endif
67+
1268
}
1369
}

src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs

-19
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,5 @@ public static bool IsDynamicCodeCompiled
1717
[Intrinsic] // the JIT/AOT compiler will change this flag to false for FullAOT scenarios, otherwise true
1818
get => IsDynamicCodeCompiled;
1919
}
20-
21-
#if !FEATURE_METADATA_UPDATE
22-
internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) {
23-
throw new NotSupportedException ("Method body replacement not supported in this runtime");
24-
}
25-
#else
26-
[MethodImplAttribute (MethodImplOptions.InternalCall)]
27-
private static unsafe extern void LoadMetadataUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length);
28-
29-
internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) {
30-
unsafe {
31-
fixed (byte* dmeta_bytes = dmeta_data)
32-
fixed (byte* dil_bytes = dil_data) {
33-
IntPtr mono_assembly = ((RuntimeAssembly)assm).GetUnderlyingNativeHandle ();
34-
LoadMetadataUpdate_internal (mono_assembly, dmeta_bytes, dmeta_data.Length, dil_bytes, dil_data.Length);
35-
}
36-
}
37-
}
38-
#endif
3920
}
4021
}

src/mono/mono/metadata/icall-decl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int
239239
#endif
240240

241241
#ifdef ENABLE_METADATA_UPDATE
242-
ICALL_EXPORT void ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len);
242+
ICALL_EXPORT void ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len, gconstpointer dpdb_bytes, int32_t dpdb_len);
243243
#endif
244244

245245
#endif // __MONO_METADATA_ICALL_DECL_H__

src/mono/mono/metadata/icall-def-netcore.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,11 @@ HANDLES(FILEDI_1, "get_marshal_info", ves_icall_System_Reflection_FieldInfo_get_
239239

240240
HANDLES(FILEDI_2, "internal_from_handle_type", ves_icall_System_Reflection_FieldInfo_internal_from_handle_type, MonoReflectionField, 2, (MonoClassField_ref, MonoType_ref))
241241

242+
#ifdef ENABLE_METADATA_UPDATE
243+
ICALL_TYPE(ASSMEXT, "System.Reflection.Metadata.AssemblyExtensions", ASSMEXT_1)
244+
NOHANDLES(ICALL(ASSMEXT_1, "ApplyUpdate_internal", ves_icall_AssemblyExtensions_ApplyUpdate))
245+
#endif
246+
242247
ICALL_TYPE(MBASE, "System.Reflection.MethodBase", MBASE_1)
243248
HANDLES(MBASE_1, "GetCurrentMethod", ves_icall_GetCurrentMethod, MonoReflectionMethod, 0, ())
244249

@@ -324,11 +329,6 @@ HANDLES_REUSE_WRAPPER(MPROP_3, "get_metadata_token", ves_icall_reflection_get_to
324329
HANDLES(MPROP_4, "get_property_info", ves_icall_RuntimePropertyInfo_get_property_info, void, 3, (MonoReflectionProperty, MonoPropertyInfo_ref, PInfo))
325330
HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_RuntimePropertyInfo_internal_from_handle_type, MonoReflectionProperty, 2, (MonoProperty_ptr, MonoType_ptr))
326331

327-
#ifdef ENABLE_METADATA_UPDATE
328-
ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1)
329-
NOHANDLES(ICALL(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate))
330-
#endif
331-
332332
ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1)
333333
HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject))
334334
HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr))

src/mono/mono/metadata/icall.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -5855,15 +5855,17 @@ ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportab
58555855

58565856
#ifdef ENABLE_METADATA_UPDATE
58575857
void
5858-
ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm,
5858+
ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm,
58595859
gconstpointer dmeta_bytes, int32_t dmeta_len,
5860-
gconstpointer dil_bytes, int32_t dil_len)
5860+
gconstpointer dil_bytes, int32_t dil_len,
5861+
gconstpointer dpdb_bytes, int32_t dpdb_len)
58615862
{
58625863
ERROR_DECL (error);
58635864
g_assert (assm);
58645865
g_assert (dmeta_len >= 0);
58655866
MonoImage *image_base = assm->image;
58665867
g_assert (image_base);
5868+
// TODO: use dpdb_bytes
58675869

58685870
MonoDomain *domain = mono_domain_get ();
58695871
mono_image_load_enc_delta (domain, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error);

src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs

+13-14
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,23 @@
66

77
namespace MonoDelta {
88
public class DeltaHelper {
9-
const string name = "System.Runtime.CompilerServices.RuntimeFeature";
10-
119
private static MethodBase _updateMethod;
1210

1311
private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod();
1412

1513
private static MethodBase InitUpdateMethod ()
1614
{
17-
var monoType = Type.GetType (name, throwOnError: true);
18-
if (monoType == null)
19-
throw new Exception ($"Couldn't get the type {name}");
20-
_updateMethod = monoType.GetMethod ("LoadMetadataUpdate", BindingFlags.NonPublic | BindingFlags.Static);
21-
if (_updateMethod == null)
22-
throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}");
23-
return _updateMethod;
24-
}
15+
var monoType = typeof(System.Reflection.Metadata.AssemblyExtensions);
16+
const string methodName = "ApplyUpdateSdb";
17+
_updateMethod = monoType.GetMethod (methodName, BindingFlags.NonPublic | BindingFlags.Static);
18+
if (_updateMethod == null)
19+
throw new Exception ($"Couldn't get {methodName} from {monoType.FullName}");
20+
return _updateMethod;
21+
}
2522

26-
private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data)
23+
private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data, byte[] dpdb_data)
2724
{
28-
UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data});
25+
UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data, dpdb_data});
2926
}
3027

3128
DeltaHelper () { }
@@ -49,7 +46,8 @@ public static void InjectUpdate (string assemblyName, string dmeta_base64, strin
4946
throw new ArgumentException ("assemblyName");
5047
var dmeta_data = Convert.FromBase64String (dmeta_base64);
5148
var dil_data = Convert.FromBase64String (dil_base64);
52-
LoadMetadataUpdate (assm, dmeta_data, dil_data);
49+
byte[] dpdb_data = null;
50+
LoadMetadataUpdate (assm, dmeta_data, dil_data, dpdb_data);
5351
}
5452

5553
private Dictionary<Assembly, int> assembly_count = new Dictionary<Assembly, int> ();
@@ -72,8 +70,9 @@ public void Update (Assembly assm) {
7270
string dil_name = $"{basename}.{count}.dil";
7371
byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name);
7472
byte[] dil_data = System.IO.File.ReadAllBytes (dil_name);
73+
byte[] dpdb_data = null; // TODO also use the dpdb data
7574

76-
LoadMetadataUpdate (assm, dmeta_data, dil_data);
75+
LoadMetadataUpdate (assm, dmeta_data, dil_data, dpdb_data);
7776
}
7877
}
7978
}

0 commit comments

Comments
 (0)