diff --git a/src/mono/msbuild/android/build/AndroidApp.targets b/src/mono/msbuild/android/build/AndroidApp.targets index d40e5a2784004b..4bc558943daebe 100644 --- a/src/mono/msbuild/android/build/AndroidApp.targets +++ b/src/mono/msbuild/android/build/AndroidApp.targets @@ -76,6 +76,11 @@ <_AOTMode Condition="'$(ForceFullAOT)' == 'true'">Full + + <_UsesRuntimeInitCallback>$(UsesRuntimeInitCallback) + <_UsesRuntimeInitCallback Condition="'$(_UsesRuntimeInitCallback)' == ''">true + + @@ -85,6 +90,8 @@ + + @@ -133,6 +140,12 @@ Condition="'$(RunAOTCompilation)' == 'true'" DependsOnTargets="_AndroidBeforeAotCompileApp"> + + <_EnableUnmanagedCallersOnlyMethodsExport>true + <_UseAotDataFile>$(UseAotDataFile) + <_UseAotDataFile Condition="'$(_UseAotDataFile)' == ''">false + + @@ -147,10 +160,6 @@ - - <_EnableUnmanagedCallersOnlyMethodsExport Condition="'$(_IsLibraryMode)' == 'true'">true - - diff --git a/src/mono/msbuild/apple/build/AppleApp.targets b/src/mono/msbuild/apple/build/AppleApp.targets index bf637dcc8b7ca8..93f4113c645360 100644 --- a/src/mono/msbuild/apple/build/AppleApp.targets +++ b/src/mono/msbuild/apple/build/AppleApp.targets @@ -91,8 +91,12 @@ <_AOTMode Condition="'$(UseMonoJustInterp)' == 'true'">JustInterp - - <_EnableUnmanagedCallersOnlyMethodsExport Condition="'$(_IsLibraryMode)' == 'true'">true + + <_EnableUnmanagedCallersOnlyMethodsExport>true + <_UseAotDataFile>$(UseAotDataFile) + <_UseAotDataFile Condition="'$(_UseAotDataFile)' == ''">false + <_UsesRuntimeInitCallback>$(UsesRuntimeInitCallback) + <_UsesRuntimeInitCallback Condition="'$(_UsesRuntimeInitCallback)' == ''">true @@ -104,6 +108,8 @@ + + @@ -166,6 +172,7 @@ Mode="$(_AOTMode)" OutputDir="$(_MobileIntermediateOutputPath)" OutputType="AsmOnly" + UseAotDataFile="$(_UseAotDataFile)" UseLLVM="$(MonoEnableLLVM)"> diff --git a/src/mono/msbuild/common/LibraryBuilder.targets b/src/mono/msbuild/common/LibraryBuilder.targets index ae920b8dec07aa..5b4da1edea3f99 100644 --- a/src/mono/msbuild/common/LibraryBuilder.targets +++ b/src/mono/msbuild/common/LibraryBuilder.targets @@ -8,6 +8,8 @@ <_IsSharedLibrary>false <_IsSharedLibrary Condition="'$(NativeLib)' == 'shared'">true + <_UsesCustomRuntimeInitCallback>false + <_UsesCustomRuntimeInitCallback Condition="$(CustomRuntimeInitCallback) != ''">true @@ -17,15 +19,18 @@ + TargetOS="$(TargetOS)" + UsesCustomRuntimeInitCallback="$(_UsesCustomRuntimeInitCallback)" + UsesRuntimeInitCallback="$(_UsesRuntimeInitCallback)"> diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.cs b/src/tasks/LibraryBuilder/LibraryBuilder.cs index 0b3489db177ff6..dd3a51324f5a8e 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.cs +++ b/src/tasks/LibraryBuilder/LibraryBuilder.cs @@ -19,6 +19,8 @@ public class LibraryBuilderTask : AppBuilderTask private string cmakeProjectLanguages = ""; private string targetOS = ""; + private bool usesAOTDataFile; + private List exportedAssemblies = new List(); /// /// The name of the library being generated @@ -66,6 +68,23 @@ public bool IsSharedLibrary } } + /// + /// Determines whether or not the mono runtime auto initialization + /// template, autoinit.c, is used. + /// + public bool UsesCustomRuntimeInitCallback { get; set; } + + /// + /// Determines if there is a mono runtime init callback + /// + public bool UsesRuntimeInitCallback { get; set; } + + /// + /// The environment variable name that will point to where assemblies + /// are located on the app host device. + /// + public string? AssembliesLocation { get; set; } + public bool StripDebugSymbols { get; set; } /// @@ -113,6 +132,17 @@ public override bool Execute() GatherAotSourcesObjects(aotSources, aotObjects, extraSources, linkerArgs); GatherLinkerArgs(linkerArgs); + File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), + Utils.GetEmbeddedResource("library-builder.h")); + + GenerateAssembliesLoader(); + + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + WriteAutoInitializationFromTemplate(); + extraSources.AppendLine(" autoinit.c"); + } + WriteCMakeFileFromTemplate(aotSources.ToString(), aotObjects.ToString(), extraSources.ToString(), linkerArgs.ToString()); OutputPath = BuildLibrary(); @@ -122,14 +152,18 @@ public override bool Execute() private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aotObjects, StringBuilder extraSources, StringBuilder linkerArgs) { List exportedSymbols = new List(); - List exportedAssemblies = new List(); bool hasExports = false; foreach (CompiledAssembly compiledAssembly in CompiledAssemblies) { if (!string.IsNullOrEmpty(compiledAssembly.AssemblerFile)) { - aotSources.AppendLine(compiledAssembly.AssemblerFile); + aotSources.AppendLine($" {compiledAssembly.AssemblerFile}"); + } + + if (!usesAOTDataFile && !string.IsNullOrEmpty(compiledAssembly.DataFile)) + { + usesAOTDataFile = true; } if (!string.IsNullOrEmpty(compiledAssembly.LlvmObjectFile)) @@ -145,10 +179,14 @@ private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aot if (symbolsAdded > 0) { - exportedAssemblies.Add(Path.GetFileName(compiledAssembly.Path)); + exportedAssemblies.Add(Path.GetFileNameWithoutExtension(compiledAssembly.Path)); } } } + if (IsSharedLibrary && exportedAssemblies.Count == 0) + { + throw new LogAsErrorException($"None of the compiled assemblies contain exported symbols. Resulting shared library would be unusable."); + } // for android, all symbols to keep go in one linker script // @@ -167,11 +205,9 @@ private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aot WriteExportedSymbolsArg(MobileSymbolFileName, linkerArgs); } - WriteAssembliesToLoadList(exportedAssemblies); - foreach (ITaskItem item in ExtraSources) { - extraSources.AppendLine(item.ItemSpec); + extraSources.AppendLine($" {item.ItemSpec}"); } } @@ -226,8 +262,30 @@ private static void WriteLinkerScriptFile(string exportsFile, List expor .Replace("%GLOBAL_SYMBOLS%", globalExports)); } + private void WriteAutoInitializationFromTemplate() + { + File.WriteAllText(Path.Combine(OutputDirectory, "autoinit.c"), + Utils.GetEmbeddedResource("autoinit.c") + .Replace("%ASSEMBLIES_LOCATION%", !string.IsNullOrEmpty(AssembliesLocation) ? AssembliesLocation : "DOTNET_LIBRARY_ASSEMBLY_PATH") + .Replace("%RUNTIME_IDENTIFIER%", RuntimeIdentifier)); + } + + private void GenerateAssembliesLoader() + { + var assemblyPreloaders = new List(); + foreach (string exportedAssembly in exportedAssemblies) + { + assemblyPreloaders.Add($"preload_assembly(\"{exportedAssembly}\");"); + } + + File.WriteAllText(Path.Combine(OutputDirectory, "preloaded-assemblies.c"), + Utils.GetEmbeddedResource("preloaded-assemblies.c") + .Replace("%ASSEMBLIES_PRELOADER%", string.Join("\n ", assemblyPreloaders))); + } + private void WriteCMakeFileFromTemplate(string aotSources, string aotObjects, string extraSources, string linkerArgs) { + string extraDefinitions = GenerateExtraDefinitions(); // BundleDir File.WriteAllText(Path.Combine(OutputDirectory, "CMakeLists.txt"), Utils.GetEmbeddedResource("CMakeLists.txt.template") @@ -237,35 +295,21 @@ private void WriteCMakeFileFromTemplate(string aotSources, string aotObjects, st .Replace("%MonoInclude%", MonoRuntimeHeaders) .Replace("%AotSources%", aotSources) .Replace("%AotObjects%", aotObjects) + .Replace("%ExtraDefinitions%", extraDefinitions) .Replace("%ExtraSources%", extraSources) .Replace("%LIBRARY_LINKER_ARGS%", linkerArgs)); } - private void WriteAssembliesToLoadList(List assemblies) + private string GenerateExtraDefinitions() { - string content; + var extraDefinitions = new StringBuilder(); - if (assemblies.Count == 0) - { - content = " return NULL;"; - } - else + if (usesAOTDataFile) { - StringBuilder sb = new StringBuilder(); - sb.AppendLine($" char** assembly_list = (char**)malloc({assemblies.Count.ToString()} * sizeof(char*));"); - - for (int i = 0; i < assemblies.Count; i++) - { - sb.AppendLine($" assembly_list[{i.ToString()}] = \"{assemblies[i]}\";"); - } - - sb.AppendLine(" return assembly_list;"); - content = sb.ToString(); + extraDefinitions.AppendLine("add_definitions(-DUSES_AOT_DATA=1)"); } - File.WriteAllText(Path.Combine(OutputDirectory, "assembly_list.c"), - Utils.GetEmbeddedResource("assembly_list.c") - .Replace("%LOADABLE_ASSEMBLIES%", content)); + return extraDefinitions.ToString(); } private string BuildLibrary() diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.csproj b/src/tasks/LibraryBuilder/LibraryBuilder.csproj index c2a8ae77a3c51e..bdd1543fd13a53 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.csproj +++ b/src/tasks/LibraryBuilder/LibraryBuilder.csproj @@ -19,6 +19,7 @@ + diff --git a/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template b/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template index 1fa43e84a9edeb..8f138c5fd25445 100644 --- a/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template +++ b/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template @@ -5,21 +5,21 @@ project(%LIBRARY_NAME%) enable_language(%CMAKE_LANGS%) set(DOTNET_AOT_SOURCES - %AotSources% +%AotSources% ) set(DOTNET_AOT_OBJECTS - %AotObjects% +%AotObjects% ) set(DOTNET_EXTRA_SOURCES - %ExtraSources% - assembly_list.c -) + library-builder.h + preloaded-assemblies.c +%ExtraSources%) include_directories("%MonoInclude%") add_library( aot_library STATIC - ${DOTNET_AOT_SOURCES} + ${DOTNET_AOT_SOURCES} ) target_link_libraries( aot_library @@ -35,9 +35,10 @@ add_library( if(TARGETS_ANDROID) set(MOBILE_SYSTEM_LIBS libz.so - log + log ) -else() + add_definitions(-DHOST_ANDROID=1) +elseif(TARGETS_APPLE_MOBILE) set(MOBILE_SYSTEM_LIBS "-framework Foundation" "-framework Security" @@ -46,12 +47,16 @@ else() "-lc++" "-liconv" ) + add_definitions(-DHOST_APPLE_MOBILE=1) +else() + message(FATAL_ERROR "Unsupported Platform. Ensure the TargetOS is supported by the LibraryBuilder and the platform specific libs are added here.") endif() +%ExtraDefinitions% + target_link_libraries( %LIBRARY_NAME% PUBLIC aot_library ${MOBILE_SYSTEM_LIBS} - %LIBRARY_LINKER_ARGS% -) +%LIBRARY_LINKER_ARGS%) diff --git a/src/tasks/LibraryBuilder/Templates/assembly_list.c b/src/tasks/LibraryBuilder/Templates/assembly_list.c deleted file mode 100644 index ef2168d3e50bf7..00000000000000 --- a/src/tasks/LibraryBuilder/Templates/assembly_list.c +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -#include -#include - -char** get_loadable_assemblies () -{ -%LOADABLE_ASSEMBLIES% -} \ No newline at end of file diff --git a/src/tasks/LibraryBuilder/Templates/autoinit.c b/src/tasks/LibraryBuilder/Templates/autoinit.c new file mode 100644 index 00000000000000..6fd37510d00a1c --- /dev/null +++ b/src/tasks/LibraryBuilder/Templates/autoinit.c @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include +#include +#include +#include +#include +#include +#if defined(USES_AOT_DATA) +#include +#include +#include +#endif + +#include +#include +#include + +#include "library-builder.h" + +static void +cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) +{ + free ((void *)args->runtimeconfig.name.path); + free (args); + free (user_data); +} + +static void +initialize_runtimeconfig (const char *bundle_path) +{ + char *file_name = "runtimeconfig.bin"; + size_t str_len = sizeof (char) * (strlen (bundle_path) + strlen (file_name) + 2); // +1 "/", +1 null-terminating char + char *file_path = (char *)malloc (str_len); + if (!file_path) + LOG_ERROR ("Out of memory.\n"); + + int num_char = snprintf (file_path, str_len, "%s/%s", bundle_path, file_name); + if (num_char <= 0 || num_char >= str_len) + LOG_ERROR ("Encoding error while formatting '%s' and '%s' into \"%%s/%%s\".\n", bundle_path, file_name); + + struct stat buffer; + + if (stat (file_path, &buffer) == 0) { + MonovmRuntimeConfigArguments *arg = (MonovmRuntimeConfigArguments *)malloc (sizeof (MonovmRuntimeConfigArguments)); + if (!arg) + LOG_ERROR ("Out of memory.\n"); + + arg->kind = 0; + arg->runtimeconfig.name.path = file_path; + monovm_runtimeconfig_initialize (arg, cleanup_runtime_config, NULL); + } else { + free (file_path); + } +} + +static void +initialize_appctx_env_variables (const char *bundle_path) +{ + const char *appctx_keys[2], *appctx_values[2]; + + appctx_keys[0] = "RUNTIME_IDENTIFIER"; + appctx_values[0] = "%RUNTIME_IDENTIFIER%"; + + appctx_keys[1] = "APP_CONTEXT_BASE_DIRECTORY"; + appctx_values[1] = bundle_path; + + monovm_initialize (2, appctx_keys, appctx_values); +} + +#if defined(USES_AOT_DATA) +static unsigned char * +load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_handle) +{ + *out_handle = NULL; + const char *bundle_path = (const char*)user_data; + + MonoAssemblyName *assembly_name = mono_assembly_get_name (assembly); + const char *aname = mono_assembly_name_get_name (assembly_name); + + size_t str_len = sizeof (char) * (strlen (bundle_path) + strlen (aname) + 10); // +1 "/", +8 ".aotdata", +1 null-terminating char + char *file_path = (char *)malloc (str_len); + if (!file_path) + LOG_ERROR ("Out of memory.\n"); + + int res = snprintf (file_path, str_len, "%s/%s.aotdata", bundle_path, aname); + if (res <= 0 || res >= str_len) + LOG_ERROR ("Encoding error while formatting '%s' and '%s' into \"%%s/%%s\".\n", bundle_path, aname); + + int fd = open (file_path, O_RDONLY); + if (fd < 0) + LOG_ERROR ("Could not open file '%s'.\n", file_path); + + void *ptr = mmap (NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + close (fd); + if (ptr == MAP_FAILED) + LOG_ERROR ("Could not map file '%s' to memory.\n", file_path); + + *out_handle = ptr; + return (unsigned char *) ptr; +} + +static void +free_aot_data (MonoAssembly *assembly, int size, void *user_data, void *handle) +{ + munmap (handle, size); +} +#endif + +static void +runtime_init_callback () +{ + const char *assemblies_location = getenv ("%ASSEMBLIES_LOCATION%"); + if (!assemblies_location || assemblies_location[0] == '\0') + assemblies_location = "./"; + + // Don't free as load_aot_data may be called later on, if used. + const char *bundle_path = strdup (assemblies_location); + if (!bundle_path) + LOG_ERROR ("Out of memory.\n"); + + initialize_runtimeconfig (bundle_path); + + initialize_appctx_env_variables (bundle_path); + + register_aot_modules (); + + mono_set_assemblies_path (bundle_path); + + mono_jit_set_aot_only (true); + +#if defined(USES_AOT_DATA) + mono_install_load_aot_data_hook (load_aot_data, free_aot_data, bundle_path); +#endif + + mono_set_signal_chaining (true); + + MonoDomain *domain = mono_jit_init ("mono.self.contained.library"); + if (!domain) + LOG_ERROR ("Could not auto initialize runtime.\n"); + + preload_assemblies_with_exported_symbols (); +} + +void __attribute__((constructor)) +autoinit () +{ + mono_set_runtime_init_callback (&runtime_init_callback); +} diff --git a/src/tasks/LibraryBuilder/Templates/library-builder.h b/src/tasks/LibraryBuilder/Templates/library-builder.h new file mode 100644 index 00000000000000..c0dee67be95adc --- /dev/null +++ b/src/tasks/LibraryBuilder/Templates/library-builder.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __MONO_LIBRARY_BUILDER_H__ +#define __MONO_LIBRARY_BUILDER_H__ + +#include + +#if defined(HOST_ANDROID) + +#include + +#define LOG_ERROR(fmt, ...) \ + do \ + { \ + __android_log_print(ANDROID_LOG_ERROR, "MONO_SELF_CONTAINED_LIBRARY", fmt, ##__VA_ARGS__); \ + abort (); \ + } while (0) + +#elif defined(HOST_APPLE_MOBILE) + +#include + +#define LOG_ERROR(fmt, ...) \ + do \ + { \ + os_log_error (OS_LOG_DEFAULT, fmt, ##__VA_ARGS__); \ + abort (); \ + } while (0) + +#else + +#error Unsupported Host Platform. Ensure the hosting platform is supported by the LibraryBuilder and the appropriate logging functions are added. + +#endif + +void register_aot_modules (void); +void preload_assemblies_with_exported_symbols (); +typedef void (*MonoRuntimeInitCallback) (void); +void mono_set_runtime_init_callback (MonoRuntimeInitCallback callback); + +#endif /*__MONO_LIBRARY_BUILDER_H__*/ diff --git a/src/tasks/LibraryBuilder/Templates/preloaded-assemblies.c b/src/tasks/LibraryBuilder/Templates/preloaded-assemblies.c new file mode 100644 index 00000000000000..71be26d870dc0b --- /dev/null +++ b/src/tasks/LibraryBuilder/Templates/preloaded-assemblies.c @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include + +#include + +#include "library-builder.h" + +static void +preload_assembly (const char* filename) +{ + MonoAssembly *assembly = mono_assembly_load_with_partial_name (filename, NULL); + if (!assembly) + LOG_ERROR ("Could not open assembly '%s'.\n", filename); +} + +void +preload_assemblies_with_exported_symbols () +{ + %ASSEMBLIES_PRELOADER% +} \ No newline at end of file