diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c
index 5efa6184fb60a1..bad49486d99b12 100644
--- a/src/mono/mono/mini/aot-runtime.c
+++ b/src/mono/mono/mini/aot-runtime.c
@@ -1965,7 +1965,7 @@ load_aot_module (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, gpointer
g_free (err);
}
g_free (aot_name);
-#if !defined(PLATFORM_ANDROID) && !defined(TARGET_WASM)
+#if !defined(HOST_ANDROID) && !defined(HOST_WASM)
if (!sofile) {
char *basename = g_path_get_basename (assembly->image->name);
aot_name = g_strdup_printf ("%s/mono/aot-cache/%s/%s%s", mono_assembly_getrootdir(), MONO_ARCHITECTURE, basename, MONO_SOLIB_EXT);
diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj
index 8fb25b90b7189d..e85e49d7b3b1ad 100644
--- a/src/mono/sample/Android/AndroidSampleApp.csproj
+++ b/src/mono/sample/Android/AndroidSampleApp.csproj
@@ -46,13 +46,33 @@
+
+ <_AotOutputType>AsmOnly
+ <_AotModulesTablePath>$(BundleDir)\modules.c
+
+
+
+ <_AotOutputType>Library
+ <_AotLibraryFormat>So
+ <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-x86_64
+ <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">darwin-x86_64
+ <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('Windows'))">windows-x86_64
+ <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'arm'">arm-linux-androideabi$
+ <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'arm64'">aarch64-linux-android
+ <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'x64'">x86_64-linux-android
+ <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'x86'">i686-linux-android
+ <_AotToolPrefix>$(ANDROID_NDK_ROOT)\toolchains\llvm\prebuilt\$(_PrebuiltOS)\bin\$(_PrebuiltAbi)-
+
+
diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile
index 6d59ee860fe8b9..cbcf63db861f65 100644
--- a/src/mono/sample/Android/Makefile
+++ b/src/mono/sample/Android/Makefile
@@ -3,6 +3,7 @@ MONO_ARCH?=x64
DOTNET := ../../../../dotnet.sh
USE_LLVM=true
AOT=false
+AOT_WITH_LIBRARY_FILES=false
INTERP=false
DEPLOY_AND_RUN?=true
@@ -26,7 +27,9 @@ run:
/p:TargetOS=Android \
/p:Configuration=$(MONO_CONFIG) \
/p:DeployAndRun=$(DEPLOY_AND_RUN) \
+ /p:RunAOTCompilation=$(AOT) \
/p:ForceAOT=$(AOT) \
+ /p:AOTWithLibraryFiles=$(AOT_WITH_LIBRARY_FILES) \
/p:MonoForceInterpreter=$(INTERP) \
/p:UseLLVM=$(USE_LLVM) \
/p:RunActivity=false \
diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs
index b979ba316b33d9..23475c9e1e6c24 100644
--- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs
+++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs
@@ -125,11 +125,13 @@ public class ApkBuilder
var assemblerFiles = new StringBuilder();
var assemblerFilesToLink = new StringBuilder();
+ var aotLibraryFiles = new List();
foreach (ITaskItem file in Assemblies)
{
// use AOT files if available
var obj = file.GetMetadata("AssemblerFile");
var llvmObj = file.GetMetadata("LlvmObjectFile");
+ var lib = file.GetMetadata("LibraryFile");
if (!string.IsNullOrEmpty(obj))
{
@@ -143,9 +145,14 @@ public class ApkBuilder
var name = Path.GetFileNameWithoutExtension(llvmObj);
assemblerFilesToLink.AppendLine($" {llvmObj}");
}
+
+ if (!string.IsNullOrEmpty(lib))
+ {
+ aotLibraryFiles.Add(lib);
+ }
}
- if (ForceAOT && assemblerFiles.Length == 0)
+ if (ForceAOT && assemblerFiles.Length == 0 && aotLibraryFiles.Count == 0)
{
throw new InvalidOperationException("Need list of AOT files.");
}
@@ -165,7 +172,8 @@ public class ApkBuilder
// Copy sourceDir to OutputDir/assets-tozip (ignore native files)
// these files then will be zipped and copied to apk/assets/assets.zip
- Utils.DirectoryCopy(AppDir, Path.Combine(OutputDir, "assets-tozip"), file =>
+ var assetsToZipDirectory = Path.Combine(OutputDir, "assets-tozip");
+ Utils.DirectoryCopy(AppDir, assetsToZipDirectory, file =>
{
string fileName = Path.GetFileName(file);
string extension = Path.GetExtension(file);
@@ -184,6 +192,12 @@ public class ApkBuilder
return true;
});
+ // add AOT .so libraries
+ foreach (var aotlib in aotLibraryFiles)
+ {
+ File.Copy(aotlib, Path.Combine(assetsToZipDirectory, Path.GetFileName(aotlib)));
+ }
+
// tools:
string dx = Path.Combine(buildToolsFolder, "dx");
string aapt = Path.Combine(buildToolsFolder, "aapt");
@@ -195,8 +209,8 @@ public class ApkBuilder
string cmake = "cmake";
string zip = "zip";
- Utils.RunProcess(zip, workingDir: Path.Combine(OutputDir, "assets-tozip"), args: "-q -r ../assets/assets.zip .");
- Directory.Delete(Path.Combine(OutputDir, "assets-tozip"), true);
+ Utils.RunProcess(zip, workingDir: assetsToZipDirectory, args: "-q -r ../assets/assets.zip .");
+ Directory.Delete(assetsToZipDirectory, true);
if (!File.Exists(androidJar))
throw new ArgumentException($"API level={BuildApiLevel} is not downloaded in Android SDK");
@@ -283,22 +297,26 @@ public class ApkBuilder
.Replace("%AotSources%", aotSources)
.Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.c");
- string defines = "";
+ var defines = new StringBuilder();
if (ForceInterpreter)
{
- defines = "add_definitions(-DFORCE_INTERPRETER=1)";
+ defines.AppendLine("add_definitions(-DFORCE_INTERPRETER=1)");
}
else if (ForceAOT)
{
- defines = "add_definitions(-DFORCE_AOT=1)";
+ defines.AppendLine("add_definitions(-DFORCE_AOT=1)");
+ if (aotLibraryFiles.Count == 0)
+ {
+ defines.AppendLine("add_definitions(-DSTATIC_AOT=1)");
+ }
}
if (!string.IsNullOrEmpty(DiagnosticPorts))
{
- defines += "\nadd_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")";
+ defines.AppendLine("add_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")");
}
- cmakeLists = cmakeLists.Replace("%Defines%", defines);
+ cmakeLists = cmakeLists.Replace("%Defines%", defines.ToString());
File.WriteAllText(Path.Combine(OutputDir, "CMakeLists.txt"), cmakeLists);
diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid.c b/src/tasks/AndroidAppBuilder/Templates/monodroid.c
index 7bd0464075ebf9..4ed02eb216693f 100644
--- a/src/tasks/AndroidAppBuilder/Templates/monodroid.c
+++ b/src/tasks/AndroidAppBuilder/Templates/monodroid.c
@@ -191,7 +191,7 @@ log_callback (const char *log_domain, const char *log_level, const char *message
}
}
-#if FORCE_AOT
+#if defined(FORCE_AOT) && defined(STATIC_AOT)
void register_aot_modules (void);
#endif
@@ -270,7 +270,10 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed
LOG_INFO("Interp Enabled");
mono_jit_set_aot_mode(MONO_AOT_MODE_INTERP_ONLY);
#elif FORCE_AOT
+ LOG_INFO("AOT Enabled");
+#if STATIC_AOT
register_aot_modules();
+#endif
mono_jit_set_aot_mode(MONO_AOT_MODE_FULL);
#endif
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index cb3876cfc9c36f..259622da707856 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -48,13 +48,19 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
[Required]
public string? OutputDir { get; set; }
+ ///
+ /// Target triple passed to the AOT compiler.
+ ///
+ public string? Triple { get; set; }
+
///
/// Assemblies which were AOT compiled.
///
/// Successful AOT compilation will set the following metadata on the items:
/// - AssemblerFile (when using OutputType=AsmOnly)
/// - ObjectFile (when using OutputType=Normal)
- /// - AotDataFile
+ /// - LibraryFile (when using OutputType=Library)
+ /// - AotDataFile (when using UseAotDataFile=true)
/// - LlvmObjectFile (if using LLVM)
/// - LlvmBitcodeFile (if using LLVM-only)
///
@@ -73,13 +79,37 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
public bool UseLLVM { get; set; }
///
- /// Use a separate .aotdata file for the AOT data.
+ /// This instructs the AOT code generator to output certain data constructs into a separate file. This can reduce the executable images some five to twenty percent.
+ /// Developers need to then ship the resulting aotdata as a resource and register a hook to load the data on demand by using the mono_install_load_aot_data_hook() method.
/// Defaults to true.
///
public bool UseAotDataFile { get; set; } = true;
///
- /// File to use for profile-guided optimization.
+ /// Create an ELF object file (.o) or .s file which can be statically linked into an executable when embedding the mono runtime.
+ /// Only valid if OutputType is ObjectFile or AsmOnly.
+ ///
+ public bool UseStaticLinking { get; set; }
+
+ ///
+ /// When this option is specified, icalls (internal calls made from the standard library into the mono runtime code) are invoked directly instead of going through the operating system symbol lookup operation.
+ /// This requires UseStaticLinking=true.
+ ///
+ public bool UseDirectIcalls { get; set; }
+
+ ///
+ /// When this option is specified, P/Invoke methods are invoked directly instead of going through the operating system symbol lookup operation
+ /// This requires UseStaticLinking=true.
+ ///
+ public bool UseDirectPInvoke { get; set; }
+
+ ///
+ /// Instructs the AOT compiler to emit DWARF debugging information.
+ ///
+ public bool UseDwarfDebug { get; set; }
+
+ ///
+ /// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
///
public string? AotProfilePath { get; set; }
@@ -89,8 +119,8 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
public string[]? Profilers { get; set; }
///
- /// Generate a file containing mono_aot_register_module() calls for each AOT module
- /// Defaults to false.
+ /// Generate a file containing mono_aot_register_module() calls for each AOT module which can be compiled into the app embedding mono.
+ /// If set, this implies UseStaticLinking=true.
///
public string? AotModulesTablePath { get; set; }
@@ -101,7 +131,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
public string? AotModulesTableLanguage { get; set; } = nameof(MonoAotModulesTableLanguage.C);
///
- /// Choose between 'Normal', 'JustInterp', 'Full', 'FullInterp', 'LLVMOnly', 'LLVMOnlyInterp'.
+ /// Choose between 'Normal', 'JustInterp', 'Full', 'FullInterp', 'Hybrid', 'LLVMOnly', 'LLVMOnlyInterp'.
/// LLVMOnly means to use only LLVM for FullAOT, AOT result will be a LLVM Bitcode file (the cross-compiler must be built with LLVM support)
/// The "interp" options ('LLVMOnlyInterp' and 'FullInterp') mean generate necessary support to fall back to interpreter if AOT code is not possible for some methods.
/// The difference between 'JustInterp' and 'FullInterp' is that 'FullInterp' will AOT all the methods in the given assemblies, while 'JustInterp' will only AOT the wrappers and trampolines necessary for the runtime to execute the managed methods using the interpreter and to interoperate with P/Invokes and unmanaged callbacks.
@@ -109,10 +139,21 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
public string Mode { get; set; } = nameof(MonoAotMode.Normal);
///
- /// Choose between 'Normal', 'AsmOnly'
- /// AsmOnly means the AOT compiler will produce .s assembly code instead of an .o object file.
+ /// Choose between 'ObjectFile', 'AsmOnly', 'Library'
+ /// ObjectFile means the AOT compiler will produce an .o object file, AsmOnly will produce .s assembly code and Library will produce a .so/.dylib/.dll shared library.
+ ///
+ public string OutputType { get; set; } = nameof(MonoAotOutputType.ObjectFile);
+
+ ///
+ /// Choose between 'Dll', 'Dylib', 'So'. Only valid if OutputType is Library.
+ /// Dll means the AOT compiler will produce a Windows PE .dll file, Dylib means an Apple Mach-O .dylib and So means a Linux/Android ELF .so file.
+ ///
+ public string? LibraryFormat { get; set; }
+
+ ///
+ /// Prefix that will be added to the library file name, e.g. to add 'lib' prefix required by some platforms. Only valid if OutputType is Library.
///
- public string OutputType { get; set; } = nameof(MonoAotOutputType.Normal);
+ public string LibraryFilePrefix { get; set; } = "";
///
/// Path to the directory where LLVM binaries (opt and llc) are found.
@@ -120,6 +161,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
///
public string? LLVMPath { get; set; }
+ ///
+ /// Prepends a prefix to the name of tools ran by the AOT compiler, i.e. 'as'/'ld'.
+ ///
+ public string? ToolPrefix { get; set; }
+
///
/// Path to the directory where msym artifacts are stored.
///
@@ -143,6 +189,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
private ConcurrentBag compiledAssemblies = new ConcurrentBag();
private MonoAotMode parsedAotMode;
private MonoAotOutputType parsedOutputType;
+ private MonoAotLibraryFormat parsedLibraryFormat;
private MonoAotModulesTableLanguage parsedAotModulesTableLanguage;
public override bool Execute()
@@ -200,10 +247,25 @@ public override bool Execute()
switch (OutputType)
{
- case "Normal": parsedOutputType = MonoAotOutputType.Normal; break;
+ case "ObjectFile": parsedOutputType = MonoAotOutputType.ObjectFile; break;
case "AsmOnly": parsedOutputType = MonoAotOutputType.AsmOnly; break;
+ case "Library": parsedOutputType = MonoAotOutputType.Library; break;
+ case "Normal":
+ Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead.");
+ parsedOutputType = MonoAotOutputType.ObjectFile; break;
default:
- throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.Normal)}', '{nameof(MonoAotOutputType.AsmOnly)}'. Received: '{OutputType}'.", nameof(OutputType));
+ throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.", nameof(OutputType));
+ }
+
+ switch (LibraryFormat)
+ {
+ case "Dll": parsedLibraryFormat = MonoAotLibraryFormat.Dll; break;
+ case "Dylib": parsedLibraryFormat = MonoAotLibraryFormat.Dylib; break;
+ case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break;
+ default:
+ if (parsedOutputType == MonoAotOutputType.Library)
+ throw new ArgumentException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.", nameof(LibraryFormat));
+ break;
}
if (parsedAotMode == MonoAotMode.LLVMOnly && !UseLLVM)
@@ -219,8 +281,29 @@ public override bool Execute()
throw new ArgumentException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.", nameof(AotModulesTableLanguage));
}
- if (!string.IsNullOrEmpty(AotModulesTablePath) && !GenerateAotModulesTable(Assemblies, Profilers))
- return false;
+ if (!string.IsNullOrEmpty(AotModulesTablePath))
+ {
+ // AOT modules for static linking, needs the aot modules table
+ UseStaticLinking = true;
+
+ if (!GenerateAotModulesTable(Assemblies, Profilers))
+ return false;
+ }
+
+ if (UseDirectIcalls && !UseStaticLinking)
+ {
+ throw new ArgumentException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectIcalls));
+ }
+
+ if (UseDirectPInvoke && !UseStaticLinking)
+ {
+ throw new ArgumentException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectPInvoke));
+ }
+
+ if (UseStaticLinking && (parsedOutputType == MonoAotOutputType.Library))
+ {
+ throw new ArgumentException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.", nameof(OutputType));
+ }
string? monoPaths = null;
if (AdditionalAssemblySearchPaths != null)
@@ -287,6 +370,26 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
processArgs.Add("--nollvm");
}
+ if (UseStaticLinking)
+ {
+ aotArgs.Add($"static");
+ }
+
+ if (UseDwarfDebug)
+ {
+ aotArgs.Add($"dwarfdebug");
+ }
+
+ if (!string.IsNullOrEmpty(Triple))
+ {
+ aotArgs.Add($"mtriple={Triple}");
+ }
+
+ if (!string.IsNullOrEmpty(ToolPrefix))
+ {
+ aotArgs.Add($"tool-prefix={ToolPrefix}");
+ }
+
string assemblyFilename = Path.GetFileName(assembly);
if (isDedup)
@@ -328,12 +431,23 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
aotArgs.Add("full");
}
+ if (parsedAotMode == MonoAotMode.Hybrid)
+ {
+ aotArgs.Add("hybrid");
+ }
+
if (parsedAotMode == MonoAotMode.FullInterp || parsedAotMode == MonoAotMode.JustInterp)
{
aotArgs.Add("interp");
}
- if (parsedOutputType == MonoAotOutputType.AsmOnly)
+ if (parsedOutputType == MonoAotOutputType.ObjectFile)
+ {
+ string objectFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".dll.o"));
+ aotArgs.Add($"outfile={objectFile}");
+ aotAssembly.SetMetadata("ObjectFile", objectFile);
+ }
+ else if (parsedOutputType == MonoAotOutputType.AsmOnly)
{
aotArgs.Add("asmonly");
@@ -341,11 +455,19 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
aotArgs.Add($"outfile={assemblerFile}");
aotAssembly.SetMetadata("AssemblerFile", assemblerFile);
}
- else
+ else if (parsedOutputType == MonoAotOutputType.Library)
{
- string objectFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".dll.o"));
- aotArgs.Add($"outfile={objectFile}");
- aotAssembly.SetMetadata("ObjectFile", objectFile);
+ string extension = parsedLibraryFormat switch {
+ MonoAotLibraryFormat.Dll => ".dll",
+ MonoAotLibraryFormat.Dylib => ".dylib",
+ MonoAotLibraryFormat.So => ".so",
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ string libraryFileName = $"{LibraryFilePrefix}{assemblyFilename}{extension}";
+ string libraryFilePath = Path.Combine(OutputDir, libraryFileName);
+
+ aotArgs.Add($"outfile={libraryFilePath}");
+ aotAssembly.SetMetadata("LibraryFile", libraryFilePath);
}
if (UseLLVM)
@@ -405,7 +527,7 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
else
{
paths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
- processArgs.Add(assemblyFilename);
+ processArgs.Add('"' + assemblyFilename + '"');
}
var envVariables = new Dictionary
@@ -414,11 +536,20 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
{"MONO_ENV_OPTIONS", string.Empty} // we do not want options to be provided out of band to the cross compilers
};
+ var responseFileContent = string.Join(" ", processArgs);
+ var responseFilePath = Path.GetTempFileName();
+ using (var sw = new StreamWriter(responseFilePath, append: false, encoding: new UTF8Encoding(false)))
+ {
+ sw.WriteLine(responseFileContent);
+ }
+
+ Log.LogMessage(MessageImportance.Low, $"AOT compiler arguments: {responseFileContent}");
+
try
{
// run the AOT compiler
(int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath,
- string.Join(" ", processArgs),
+ $"--response=\"{responseFilePath}\"",
envVariables,
assemblyDir,
silent: false,
@@ -436,6 +567,8 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
return false;
}
+ File.Delete(responseFilePath);
+
compiledAssemblies.Add(aotAssembly);
return true;
}
@@ -560,14 +693,23 @@ public enum MonoAotMode
JustInterp,
Full,
FullInterp,
+ Hybrid,
LLVMOnly,
LLVMOnlyInterp
}
public enum MonoAotOutputType
{
- Normal,
+ ObjectFile,
AsmOnly,
+ Library,
+}
+
+public enum MonoAotLibraryFormat
+{
+ Dll,
+ Dylib,
+ So,
}
public enum MonoAotModulesTableLanguage
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props
index c6b2f2ad28e94e..4f2721d7483cef 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props
@@ -20,7 +20,7 @@
-
+