diff --git a/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets b/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets
index 516ecad2864fd1..cf8be3e4e906b6 100644
--- a/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets
+++ b/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets
@@ -19,7 +19,6 @@
<_ILLinkDescriptorsFilePaths Include="$(ILLinkDirectory)ILLink.Descriptors.Debug.xml"
Condition="'$(Configuration)' == 'Debug' or '$(Configuration)' == 'Checked'" />
<_ILLinkDescriptorsFilePaths Include="$(CoreLibSharedDir)ILLink\ILLink.Descriptors.Shared.xml" />
- <_ILLinkDescriptorsFilePaths Include="$(CoreLibSharedDir)ILLink\ILLink.Descriptors.EventSource.xml" />
+
+
+
diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h
index dd904a92269f65..d5049ab1eb647b 100644
--- a/src/mono/mono/metadata/object-internals.h
+++ b/src/mono/mono/metadata/object-internals.h
@@ -2148,4 +2148,7 @@ mono_string_instance_is_interned (MonoString *str);
gpointer
mono_method_get_unmanaged_wrapper_ftnptr_internal (MonoMethod *method, gboolean only_unmanaged_callers_only, MonoError *error);
+void
+mono_runtime_run_startup_hooks (void);
+
#endif /* __MONO_OBJECT_INTERNALS_H__ */
diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c
index ea784236040158..d2e6db64a40ed2 100644
--- a/src/mono/mono/metadata/object.c
+++ b/src/mono/mono/metadata/object.c
@@ -8124,6 +8124,25 @@ mono_runtime_get_managed_cmd_line (void)
return cmd_line ? g_string_free (cmd_line, FALSE) : NULL;
}
+void
+mono_runtime_run_startup_hooks (void)
+{
+ if (mono_runtime_get_no_exec ())
+ return;
+
+ MonoClass *klass = mono_class_try_load_from_name (mono_defaults.corlib, "System", "StartupHookProvider");
+ if (!klass)
+ return; // Linked away
+ ERROR_DECL (error);
+ MonoMethod *method = mono_class_get_method_from_name_checked (klass, "ProcessStartupHooks", -1, 0, error);
+ mono_error_cleanup (error);
+ if (!method)
+ return;
+ mono_runtime_invoke_checked (method, NULL, NULL, error);
+ // runtime hooks design doc says not to catch exceptions from the hooks
+ mono_error_raise_exception_deprecated (error);
+}
+
#if NEVER_DEFINED
/*
* The following section is purely to declare prototypes and
diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c
index 4972e1b1391513..b726edc23f9f41 100644
--- a/src/mono/mono/mini/mini-runtime.c
+++ b/src/mono/mono/mini/mini-runtime.c
@@ -4729,6 +4729,8 @@ mini_init (const char *filename)
MONO_PROFILER_RAISE (runtime_initialized, ());
+ mono_runtime_run_startup_hooks ();
+
MONO_VES_INIT_END ();
return domain;
diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/Android.Device_Emulator.StartupHook.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/Android.Device_Emulator.StartupHook.Test.csproj
new file mode 100644
index 00000000000000..16635288c9c9b1
--- /dev/null
+++ b/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/Android.Device_Emulator.StartupHook.Test.csproj
@@ -0,0 +1,19 @@
+
+
+ Exe
+ true
+ $(NetCoreAppCurrent)
+ Android.Device_Emulator.StartupHook.Test.dll
+ false
+ 42
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/Program.cs
new file mode 100644
index 00000000000000..d4bfdecf7f63a5
--- /dev/null
+++ b/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/Program.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+
+public static class Program
+{
+ public static int Main()
+ {
+ string appContextKey = "Test.StartupHookForFunctionalTest.DidRun";
+ var data = (string) AppContext.GetData (appContextKey);
+
+ if (data != "Yes") {
+ string msg = $"Expected startup hook to set {appContextKey} to 'Yes', got '{data}'";
+ Console.Error.WriteLine(msg);
+ return 104;
+ }
+ return 42;
+ }
+}
diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/runtimeconfig.template.json b/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/runtimeconfig.template.json
new file mode 100644
index 00000000000000..892c7b04ba3450
--- /dev/null
+++ b/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook/runtimeconfig.template.json
@@ -0,0 +1,5 @@
+{
+ "configProperties": {
+ "STARTUP_HOOKS": "StartupHookForFunctionalTest"
+ }
+}
diff --git a/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest/StartupHookForFunctionalTest.cs b/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest/StartupHookForFunctionalTest.cs
new file mode 100644
index 00000000000000..d96a64b5953092
--- /dev/null
+++ b/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest/StartupHookForFunctionalTest.cs
@@ -0,0 +1,9 @@
+using System;
+
+internal class StartupHook
+{
+ public static void Initialize()
+ {
+ AppContext.SetData("Test.StartupHookForFunctionalTest.DidRun", "Yes");
+ }
+}
diff --git a/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest/StartupHookForFunctionalTest.csproj b/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest/StartupHookForFunctionalTest.csproj
new file mode 100644
index 00000000000000..60a8062556e62e
--- /dev/null
+++ b/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest/StartupHookForFunctionalTest.csproj
@@ -0,0 +1,12 @@
+
+
+ Library
+ $(NetCoreAppCurrent)
+ false
+ false
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/Program.cs
new file mode 100644
index 00000000000000..0b7d8d6a2bdc11
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/Program.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.JavaScript;
+
+namespace Sample
+{
+ public partial class Test
+ {
+ public static void Main()
+ {
+ }
+
+ [JSExport]
+ public static int TestMeaning()
+ {
+ string appContextKey = "Test.StartupHookForFunctionalTest.DidRun";
+ var data = (string) AppContext.GetData (appContextKey);
+
+ if (data != "Yes") {
+ string msg = $"Expected startup hook to set {appContextKey} to 'Yes', got '{data}'";
+ Console.Error.WriteLine(msg);
+ return 104;
+ }
+ return 42;
+ }
+ }
+}
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/WebAssembly.Browser.StartupHook.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/WebAssembly.Browser.StartupHook.Test.csproj
new file mode 100644
index 00000000000000..80d3df9875d74b
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/WebAssembly.Browser.StartupHook.Test.csproj
@@ -0,0 +1,28 @@
+
+
+ true
+ WasmTestOnBrowser
+ $(TestArchiveRoot)browseronly/
+ $(TestArchiveTestsRoot)$(OSPlatformConfig)/
+ $(DefineConstants);TARGET_BROWSER
+ 42
+ main.js
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/index.html
new file mode 100644
index 00000000000000..7d64c79aec98d9
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Runtime config test
+
+
+
+
+
+
+
+ Answer to the Ultimate Question of Life, the Universe, and Everything is :
+
+
+
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/main.js b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/main.js
new file mode 100644
index 00000000000000..a7323e1a89a925
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/main.js
@@ -0,0 +1,24 @@
+import { dotnet } from './dotnet.js'
+
+function wasm_exit(exit_code) {
+ var tests_done_elem = document.createElement("label");
+ tests_done_elem.id = "tests_done";
+ tests_done_elem.innerHTML = exit_code.toString();
+ document.body.appendChild(tests_done_elem);
+
+ console.log(`WASM EXIT ${exit_code}`);
+}
+
+try {
+ const { getAssemblyExports } = await dotnet.create();
+ const exports = await getAssemblyExports("WebAssembly.Browser.StartupHook.Test.dll");
+ const testMeaning = exports.Sample.Test.TestMeaning;
+ const ret = testMeaning();
+ document.getElementById("out").innerHTML = `${ret}`;
+ console.debug(`ret: ${ret}`);
+
+ let exit_code = ret;
+ wasm_exit(exit_code);
+} catch (err) {
+ console.log(`WASM ERROR ${err}`);
+}
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/runtimeconfig.template.json b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/runtimeconfig.template.json
new file mode 100644
index 00000000000000..892c7b04ba3450
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/runtimeconfig.template.json
@@ -0,0 +1,5 @@
+{
+ "configProperties": {
+ "STARTUP_HOOKS": "StartupHookForFunctionalTest"
+ }
+}
diff --git a/src/tests/FunctionalTests/iOS/Simulator/StartupHook/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/StartupHook/Program.cs
new file mode 100644
index 00000000000000..04d30df669c95a
--- /dev/null
+++ b/src/tests/FunctionalTests/iOS/Simulator/StartupHook/Program.cs
@@ -0,0 +1,33 @@
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+public static class Program
+{
+ [DllImport("__Internal")]
+ public static extern void mono_ios_set_summary (string value);
+
+ public static async Task Main(string[] args)
+ {
+ string appContextKey = "Test.StartupHookForFunctionalTest.DidRun";
+ mono_ios_set_summary($"Starting functional test");
+
+ await Task.Delay(10);
+
+ var data = (string) AppContext.GetData (appContextKey);
+
+ if (data != "Yes") {
+ string msg = $"Expected startup hook to set {appContextKey} to 'Yes', got '{data}'";
+ mono_ios_set_summary(msg);
+ Console.Error.WriteLine(msg);
+ return 104;
+ }
+ return 42;
+ }
+}
diff --git a/src/tests/FunctionalTests/iOS/Simulator/StartupHook/iOS.Simulator.StartupHook.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/StartupHook/iOS.Simulator.StartupHook.Test.csproj
new file mode 100644
index 00000000000000..96e17135a3d1c6
--- /dev/null
+++ b/src/tests/FunctionalTests/iOS/Simulator/StartupHook/iOS.Simulator.StartupHook.Test.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ true
+ $(NetCoreAppCurrent)
+ iOSSimulator
+ iOS.Simulator.StartupHook.Test.dll
+ false
+ 42
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/iOS/Simulator/StartupHook/runtimeconfig.template.json b/src/tests/FunctionalTests/iOS/Simulator/StartupHook/runtimeconfig.template.json
new file mode 100644
index 00000000000000..892c7b04ba3450
--- /dev/null
+++ b/src/tests/FunctionalTests/iOS/Simulator/StartupHook/runtimeconfig.template.json
@@ -0,0 +1,5 @@
+{
+ "configProperties": {
+ "STARTUP_HOOKS": "StartupHookForFunctionalTest"
+ }
+}