diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c
index 75249bea3ad1c6..b8682fa5798c41 100644
--- a/src/mono/mono/mini/aot-compiler.c
+++ b/src/mono/mono/mini/aot-compiler.c
@@ -187,6 +187,7 @@ typedef struct MonoAotOptions {
char *outfile;
char *llvm_outfile;
char *data_outfile;
+ char *export_symbols_outfile;
GList *profile_files;
GList *mibc_profile_files;
gboolean save_temps;
@@ -5103,6 +5104,7 @@ add_wrappers (MonoAotCompile *acfg)
}
}
+ GString *export_symbols = g_string_new ("");
/* native-to-managed wrappers */
rows = table_info_get_rows (&acfg->image->tables [MONO_TABLE_METHOD]);
for (int i = 0; i < rows; ++i) {
@@ -5252,8 +5254,10 @@ MONO_RESTORE_WARNING
mono_error_assert_ok (error);
add_method (acfg, wrapper);
- if (export_name)
+ if (export_name) {
g_hash_table_insert (acfg->export_names, wrapper, export_name);
+ g_string_append_printf (export_symbols, "%s%s\n", acfg->user_symbol_prefix, export_name);
+ }
}
g_free (cattr);
@@ -5265,6 +5269,19 @@ MONO_RESTORE_WARNING
}
}
+ if (acfg->aot_opts.export_symbols_outfile) {
+ char *export_symbols_out = g_string_free (export_symbols, FALSE);
+ FILE* export_symbols_outfile = fopen (acfg->aot_opts.export_symbols_outfile, "w");
+ if (!export_symbols_outfile) {
+ fprintf (stderr, "Unable to open specified export_symbols_outfile '%s' to append symbols '%s': %s\n", acfg->aot_opts.export_symbols_outfile, export_symbols_out, strerror (errno));
+ g_free (export_symbols_out);
+ exit (1);
+ }
+ fprintf (export_symbols_outfile, "%s", export_symbols_out);
+ g_free (export_symbols_out);
+ fclose (export_symbols_outfile);
+ }
+
/* StructureToPtr/PtrToStructure wrappers */
rows = table_info_get_rows (&acfg->image->tables [MONO_TABLE_TYPEDEF]);
for (int i = 0; i < rows; ++i) {
@@ -8445,6 +8462,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts)
opts->outfile = g_strdup (arg + strlen ("outfile="));
} else if (str_begins_with (arg, "llvm-outfile=")) {
opts->llvm_outfile = g_strdup (arg + strlen ("llvm-outfile="));
+ } else if (str_begins_with (arg, "export-symbols-outfile=")) {
+ opts->export_symbols_outfile = g_strdup (arg + strlen ("export-symbols-outfile="));
} else if (str_begins_with (arg, "temp-path=")) {
opts->temp_path = clean_path (g_strdup (arg + strlen ("temp-path=")));
} else if (str_begins_with (arg, "save-temps")) {
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index 113ed9addafc12..5b3e6b8ad62af4 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -163,6 +163,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
///
public string LibraryFilePrefix { get; set; } = "";
+ ///
+ /// Enables exporting symbols of methods decorated with UnmanagedCallersOnly Attribute containing a specified EntryPoint
+ ///
+ public bool EnableUnmanagedCallersOnlyMethodsExport { get; set; }
+
///
/// Path to the directory where LLVM binaries (opt and llc) are found.
/// It's required if UseLLVM is set
@@ -478,6 +483,7 @@ all assigned to that one partition.
}
}
+ CheckExportSymbolsFile(_assembliesToCompile);
CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
return !Log.HasLoggedErrors;
}
@@ -729,6 +735,16 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
}
}
+ if (EnableUnmanagedCallersOnlyMethodsExport)
+ {
+ string exportSymbolsFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".exportsymbols"));
+ ProxyFile proxyFile = _cache.NewFile(exportSymbolsFile);
+ proxyFiles.Add(proxyFile);
+
+ aotArgs.Add($"export-symbols-outfile={proxyFile.TempFile}");
+ aotAssembly.SetMetadata("ExportSymbolsFile", proxyFile.TargetFile);
+ }
+
// pass msym-dir if specified
if (MsymPath != null)
{
@@ -1040,6 +1056,21 @@ private bool TryGetAssemblyName(string asmPath, [NotNullWhen(true)] out string?
}
}
+ private void CheckExportSymbolsFile(IList assemblies)
+ {
+ if (!EnableUnmanagedCallersOnlyMethodsExport)
+ return;
+
+ foreach (var assemblyItem in assemblies)
+ {
+ string assembly = assemblyItem.GetMetadata("FullPath");
+ string assemblyFilename = Path.GetFileName(assembly);
+ string exportSymbolsFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".exportsymbols"));
+ if (!File.Exists(exportSymbolsFile))
+ Log.LogWarning($"EnableUnmanagedCallersOnlyMethodsExport is true, but no .exportsymbols file generated for assembly '{assemblyFilename}'. Check that the AOT compilation mode is full.");
+ }
+ }
+
private static IList ConvertAssembliesDictToOrderedList(ConcurrentDictionary dict, IList originalAssemblies)
{
List outItems = new(originalAssemblies.Count);