diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets
index 8a64f834b80..c01bf0248cd 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets
@@ -29,4 +29,5 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets.
+
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyStores.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyStores.targets
new file mode 100644
index 00000000000..1547a850e7a
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyStores.targets
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+ <_CreateEmbeddedAssemblyStoreAssembly Include="@(_ShrunkUserAssemblies);@(_AndroidResolvedSatellitePaths);@(_ShrunkFrameworkAssemblies)"/>
+ <_CreateEmbeddedAssemblyStoreAssembly Condition=" '@(_CreateEmbeddedAssemblyStoreAssembly->Count())' == '0' " Include="@(_ResolvedUserAssemblies);@(_ResolvedFrameworkAssemblies);@(_AndroidResolvedSatellitePaths)" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets
index 17558b8efcd..b3285c44966 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets
@@ -62,7 +62,6 @@ properties that determine build ordering.
<_PrepareBuildApkDependsOnTargets>
_SetLatestTargetFrameworkVersion;
_GetLibraryImports;
- _RemoveRegisterAttribute;
_ResolveAssemblies;
_ResolveSatellitePaths;
_CreatePackageWorkspace;
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs
deleted file mode 100644
index ca53119efcb..00000000000
--- a/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.IO;
-using Microsoft.Android.Build.Tasks;
-using Microsoft.Build.Framework;
-
-namespace Xamarin.Android.Tasks;
-
-///
-/// Collects rc.bin to be added to the final archive.
-///
-public class CollectRuntimeConfigFilesForArchive : AndroidTask
-{
- const string ArchiveLibPath = "lib";
-
- public override string TaskPrefix => "CRF";
-
- [Required]
- public string AndroidBinUtilsDirectory { get; set; } = "";
-
- [Required]
- public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty ();
-
- [Required]
- public string IntermediateOutputPath { get; set; } = "";
-
- public string RuntimeConfigBinFilePath { get; set; } = "";
-
- [Required]
- public string [] SupportedAbis { get; set; } = [];
-
- [Output]
- public ITaskItem [] FilesToAddToArchive { get; set; } = [];
-
- public override bool RunTask ()
- {
- var files = new PackageFileListBuilder ();
- var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, RuntimePackLibraryDirectories, IntermediateOutputPath);
-
- // We will place rc.bin in the `lib` directory next to the blob, to make startup slightly faster, as we will find the config file right after we encounter
- // our assembly store. Not only that, but also we'll be able to skip scanning the `base.apk` archive when split configs are enabled (which they are in 99%
- // of cases these days, since AAB enforces that split). `base.apk` contains only ABI-agnostic file, while one of the split config files contains only
- // ABI-specific data+code.
- if (!string.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath)) {
- foreach (var abi in SupportedAbis) {
- // Prefix it with `a` because bundletool sorts entries alphabetically, and this will place it right next to `assemblies.*.blob.so`, which is what we
- // like since we can finish scanning the zip central directory earlier at startup.
- var inArchivePath = MakeArchiveLibPath (abi, "libarc.bin.so");
- var wrappedSourcePath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, MonoAndroidHelper.AbiToTargetArch (abi), RuntimeConfigBinFilePath, Path.GetFileName (inArchivePath));
- files.AddItem (wrappedSourcePath, inArchivePath);
- }
- }
-
- FilesToAddToArchive = files.ToArray ();
-
- return !Log.HasLoggedErrors;
- }
-
- static string MakeArchiveLibPath (string abi, string fileName) => MonoAndroidHelper.MakeZipArchivePath (ArchiveLibPath, abi, fileName);
-}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CompileNativeAssembly.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CompileNativeAssembly.cs
index 9d6d324f678..8fed9a084b9 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/CompileNativeAssembly.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CompileNativeAssembly.cs
@@ -2,12 +2,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Threading;
using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
-
-using Xamarin.Android.Tools;
using Microsoft.Android.Build.Tasks;
namespace Xamarin.Android.Tasks
@@ -37,109 +33,34 @@ sealed class Config
public override System.Threading.Tasks.Task RunTaskAsync ()
{
- return this.WhenAll (GetAssemblerConfigs (), RunAssembler);
+ var context = new NativeAssemblerCompilation.AssemblerRunContext (
+ Log,
+ Path.GetFullPath (WorkingDirectory),
+ registerForCancellation: RegisterForCancellation,
+ cancel: Cancel
+ );
+
+ return this.WhenAll (
+ GetAssemblerConfigs (),
+ (NativeAssemblerCompilation.AssemblerConfig config) => NativeAssemblerCompilation.RunAssembler (context, config)
+ );
}
- void RunAssembler (Config config)
+ void RegisterForCancellation (Process proc)
{
- var stdout_completed = new ManualResetEvent (false);
- var stderr_completed = new ManualResetEvent (false);
- var psi = new ProcessStartInfo () {
- FileName = config.AssemblerPath,
- Arguments = config.AssemblerOptions,
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- WorkingDirectory = WorkingDirectory,
- };
-
- string assemblerName = Path.GetFileName (config.AssemblerPath);
- LogDebugMessage ($"[LLVM llc] {psi.FileName} {psi.Arguments}");
-
- var stdoutLines = new List ();
- var stderrLines = new List ();
-
- using (var proc = new Process ()) {
- proc.OutputDataReceived += (s, e) => {
- if (e.Data != null) {
- OnOutputData (assemblerName, s, e);
- stdoutLines.Add (e.Data);
- } else
- stdout_completed.Set ();
- };
-
- proc.ErrorDataReceived += (s, e) => {
- if (e.Data != null) {
- OnErrorData (assemblerName, s, e);
- stderrLines.Add (e.Data);
- } else
- stderr_completed.Set ();
- };
-
- proc.StartInfo = psi;
- proc.Start ();
- proc.BeginOutputReadLine ();
- proc.BeginErrorReadLine ();
- CancellationToken.Register (() => { try { proc.Kill (); } catch (Exception) { } });
- proc.WaitForExit ();
-
- if (psi.RedirectStandardError)
- stderr_completed.WaitOne (TimeSpan.FromSeconds (30));
-
- if (psi.RedirectStandardOutput)
- stdout_completed.WaitOne (TimeSpan.FromSeconds (30));
-
- if (proc.ExitCode != 0) {
- var sb = MonoAndroidHelper.MergeStdoutAndStderrMessages (stdoutLines, stderrLines);
- LogCodedError ("XA3006", Properties.Resources.XA3006, Path.GetFileName (config.InputSource), sb.ToString ());
- Cancel ();
+ CancellationToken.Register (() => {
+ try {
+ proc.Kill ();
+ } catch (Exception) {
}
- }
+ });
}
- IEnumerable GetAssemblerConfigs ()
+ IEnumerable GetAssemblerConfigs ()
{
- const string assemblerOptions =
- "-O2 " +
- "--debugger-tune=lldb " + // NDK uses lldb now
- "--debugify-level=location+variables " +
- "--fatal-warnings " +
- "--filetype=obj " +
- "--relocation-model=pic";
- string llcPath = Path.Combine (AndroidBinUtilsDirectory, "llc");
-
foreach (ITaskItem item in Sources) {
- // We don't need the directory since our WorkingDirectory is where all the sources are
- string sourceFile = Path.GetFileName (item.ItemSpec);
- string outputFile = QuoteFileName (sourceFile.Replace (".ll", ".o"));
- string executableDir = Path.GetDirectoryName (llcPath);
- string executableName = MonoAndroidHelper.GetExecutablePath (executableDir, Path.GetFileName (llcPath));
-
- yield return new Config {
- InputSource = item.ItemSpec,
- AssemblerPath = Path.Combine (executableDir, executableName),
- AssemblerOptions = $"{assemblerOptions} -o={outputFile} {QuoteFileName (sourceFile)}",
- };
+ yield return NativeAssemblerCompilation.GetAssemblerConfig (AndroidBinUtilsDirectory, item, stripFilePaths: true);
}
}
-
- void OnOutputData (string assemblerName, object sender, DataReceivedEventArgs e)
- {
- LogDebugMessage ($"[{assemblerName} stdout] {e.Data}");
- }
-
- void OnErrorData (string assemblerName, object sender, DataReceivedEventArgs e)
- {
- LogMessage ($"[{assemblerName} stderr] {e.Data}", MessageImportance.High);
- }
-
- static string QuoteFileName (string fileName)
- {
- var builder = new CommandLineBuilder ();
- builder.AppendFileNameIfNotNull (fileName);
- return builder.ToString ();
- }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs
index eb0a3724295..55ef0d7252d 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs
@@ -36,49 +36,16 @@ public class CreateAssemblyStore : AndroidTask
public override bool RunTask ()
{
// Get all the user and framework assemblies we may need to package
- var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(ShouldSkipAssembly (asm))).ToArray ();
+ var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(AssemblyPackagingHelper.ShouldSkipAssembly (Log, asm)));
if (!UseAssemblyStore) {
- AssembliesToAddToArchive = assemblies;
+ AssembliesToAddToArchive = assemblies.ToArray ();
return !Log.HasLoggedErrors;
}
- var store_builder = new AssemblyStoreBuilder (Log);
- var per_arch_assemblies = MonoAndroidHelper.GetPerArchAssemblies (assemblies, SupportedAbis, true);
-
- foreach (var kvp in per_arch_assemblies) {
- Log.LogDebugMessage ($"Adding assemblies for architecture '{kvp.Key}'");
-
- foreach (var assembly in kvp.Value.Values) {
- var sourcePath = assembly.GetMetadataOrDefault ("CompressedAssembly", assembly.ItemSpec);
- store_builder.AddAssembly (sourcePath, assembly, includeDebugSymbols: IncludeDebugSymbols);
-
- Log.LogDebugMessage ($"Added '{sourcePath}' to assembly store.");
- }
- }
-
- var assembly_store_paths = store_builder.Generate (AppSharedLibrariesDir);
-
- if (assembly_store_paths.Count == 0) {
- throw new InvalidOperationException ("Assembly store generator did not generate any stores");
- }
-
- if (assembly_store_paths.Count != SupportedAbis.Length) {
- throw new InvalidOperationException ("Internal error: assembly store did not generate store for each supported ABI");
- }
-
+ var assembly_store_paths = AssemblyPackagingHelper.CreateAssemblyStore (Log, assemblies, AppSharedLibrariesDir, SupportedAbis, IncludeDebugSymbols);
AssembliesToAddToArchive = assembly_store_paths.Select (kvp => new TaskItem (kvp.Value, new Dictionary { { "Abi", MonoAndroidHelper.ArchToAbi (kvp.Key) } })).ToArray ();
return !Log.HasLoggedErrors;
}
-
- bool ShouldSkipAssembly (ITaskItem asm)
- {
- var should_skip = asm.GetMetadataOrDefault ("AndroidSkipAddToPackage", false);
-
- if (should_skip)
- Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
-
- return should_skip;
- }
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateEmbeddedAssemblyStore.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateEmbeddedAssemblyStore.cs
new file mode 100644
index 00000000000..c75474ef7aa
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateEmbeddedAssemblyStore.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using Microsoft.Android.Build.Tasks;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Xamarin.Android.Tools;
+
+namespace Xamarin.Android.Tasks;
+
+public class CreateEmbeddedAssemblyStore : AndroidTask
+{
+ public override string TaskPrefix => "CEAS";
+
+ [Required]
+ public string AndroidBinUtilsDirectory { get; set; } = "";
+
+ [Required]
+ public string AppSharedLibrariesDir { get; set; } = "";
+
+ [Required]
+ public string AssemblySourcesDir { get; set; } = "";
+
+ [Required]
+ public bool AssemblyStoreEmbeddedInRuntime { get; set; }
+
+ [Required]
+ public bool IncludeDebugSymbols { get; set; }
+
+ [Required]
+ public ITaskItem[] ResolvedUserAssemblies { get; set; } = [];
+
+ [Required]
+ public ITaskItem[] ResolvedFrameworkAssemblies { get; set; } = [];
+
+ [Required]
+ public string [] SupportedAbis { get; set; } = [];
+
+ public override bool RunTask ()
+ {
+ if (AssemblyStoreEmbeddedInRuntime) {
+ return EmbedAssemblyStore ();
+ }
+
+ // Generate sources to satisfy libmonodroid's ABI requirements
+ foreach (string abi in SupportedAbis) {
+ ELFEmbeddingHelper.EmbedBinary (
+ Log,
+ abi,
+ AndroidBinUtilsDirectory,
+ inputFile: null,
+ ELFEmbeddingHelper.KnownEmbedItems.AssemblyStore,
+ AssemblySourcesDir,
+ missingContentOK: true
+ );
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ bool EmbedAssemblyStore ()
+ {
+ var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(AssemblyPackagingHelper.ShouldSkipAssembly (Log, asm)));
+ var assemblyStorePaths = AssemblyPackagingHelper.CreateAssemblyStore (
+ Log, assemblies,
+ Path.Combine (AppSharedLibrariesDir, "embedded"),
+ SupportedAbis,
+ IncludeDebugSymbols
+ );
+
+ foreach (var kvp in assemblyStorePaths) {
+ string abi = MonoAndroidHelper.ArchToAbi (kvp.Key);
+ string inputFile = kvp.Value;
+
+ ELFEmbeddingHelper.EmbedBinary (
+ Log,
+ abi,
+ AndroidBinUtilsDirectory,
+ inputFile,
+ ELFEmbeddingHelper.KnownEmbedItems.AssemblyStore,
+ AssemblySourcesDir,
+ missingContentOK: false
+ );
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs
index fd6b78fcebd..be5346385bf 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs
@@ -66,7 +66,7 @@ void GenerateCompressedAssemblySources ()
var assemblyKey = CompressedAssemblyInfo.GetDictionaryKey (assembly);
if (assemblies.ContainsKey (assemblyKey)) {
- Log.LogDebugMessage ($"Skipping duplicate assembly: {assembly.ItemSpec} (arch {MonoAndroidHelper.GetAssemblyAbi(assembly)})");
+ Log.LogDebugMessage ($"Skipping duplicate assembly: {assembly.ItemSpec} (arch {MonoAndroidHelper.GetItemAbi(assembly)})");
continue;
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs
index c905016a9c3..acb45995b70 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs
@@ -8,6 +8,7 @@
using System.Reflection.PortableExecutable;
using System.Text;
using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
using Java.Interop.Tools.TypeNameMappings;
using Xamarin.Android.Tools;
@@ -50,6 +51,9 @@ public class GenerateNativeApplicationConfigSources : AndroidTask
[Required]
public bool TargetsCLR { get; set; }
+ [Required]
+ public string AndroidBinUtilsDirectory { get; set; } = "";
+
public bool EnableMarshalMethods { get; set; }
public bool EnableManagedMarshalMethodsLookup { get; set; }
public string? RuntimeConfigBinFilePath { get; set; }
@@ -234,6 +238,10 @@ public override bool RunTask ()
}
var uniqueNativeLibraries = new List ();
+
+ // Number of DSOs that will be packaged, it may be different to the number of items in the above
+ // `uniqueNativeLibraries` list.
+ uint packagedNativeLibrariesCount = 0;
var seenNativeLibraryNames = new HashSet (StringComparer.OrdinalIgnoreCase);
if (NativeLibraries != null) {
foreach (ITaskItem item in NativeLibraries) {
@@ -243,12 +251,21 @@ public override bool RunTask ()
continue;
}
+ if (!ELFHelper.IsEmptyAOTLibrary (Log, item.ItemSpec)) {
+ packagedNativeLibrariesCount++;
+ }
+
seenNativeLibraryNames.Add (name);
uniqueNativeLibraries.Add (item);
}
+
+ // libxamarin-app.so is not in NativeLibraries, but we must count it
+ if (!seenNativeLibraryNames.Contains ("libxamarin-app.so")) {
+ uniqueNativeLibraries.Add (new TaskItem ("libxamarin-app.so"));
+ packagedNativeLibrariesCount++;
+ }
}
- bool haveRuntimeConfigBlob = !String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath);
var jniRemappingNativeCodeInfo = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal (ProjectSpecificTaskObjectKey (GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfoKey), RegisteredTaskObjectLifetime.Build);
LLVMIR.LlvmIrComposer appConfigAsmGen;
@@ -262,6 +279,7 @@ public override bool RunTask ()
NumberOfAssembliesInApk = assemblyCount,
BundledAssemblyNameWidth = assemblyNameWidth,
NativeLibraries = uniqueNativeLibraries,
+ PackagedNativeLibrariesCount = packagedNativeLibrariesCount,
AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token,
JNIEnvInitializeToken = jnienv_initialize_method_token,
JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token,
@@ -272,6 +290,17 @@ public override bool RunTask ()
IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (),
};
} else {
+ bool haveRuntimeConfigBlob = !String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath);
+ ELFEmbeddingHelper.EmbedBinary (
+ Log,
+ SupportedAbis,
+ AndroidBinUtilsDirectory,
+ RuntimeConfigBinFilePath,
+ ELFEmbeddingHelper.KnownEmbedItems.RuntimeConfig,
+ EnvironmentOutputDirectory,
+ missingContentOK: !haveRuntimeConfigBlob
+ );
+
appConfigAsmGen = new ApplicationConfigNativeAssemblyGenerator (environmentVariables, systemProperties, Log) {
UsesMonoAOT = usesMonoAOT,
UsesMonoLLVM = EnableLLVM,
@@ -283,11 +312,11 @@ public override bool RunTask ()
PackageNamingPolicy = pnp,
BoundExceptionType = boundExceptionType,
JniAddNativeMethodRegistrationAttributePresent = NativeCodeGenState.TemplateJniAddNativeMethodRegistrationAttributePresent,
- HaveRuntimeConfigBlob = haveRuntimeConfigBlob,
NumberOfAssembliesInApk = assemblyCount,
BundledAssemblyNameWidth = assemblyNameWidth,
MonoComponents = (MonoComponent)monoComponents,
NativeLibraries = uniqueNativeLibraries,
+ PackagedNativeLibrariesCount = packagedNativeLibrariesCount,
HaveAssemblyStore = UseAssemblyStore,
AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token,
JNIEnvInitializeToken = jnienv_initialize_method_token,
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs
index cad5439df21..1f9148b4f9f 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs
@@ -1,5 +1,3 @@
-using System;
-using System.IO;
using System.Collections.Generic;
using Microsoft.Build.Framework;
@@ -10,13 +8,6 @@ namespace Xamarin.Android.Tasks
{
public class PrepareAbiItems : AndroidTask
{
- const string ArmV7a = "armeabi-v7a";
- const string TypeMapBase = "typemaps";
- const string EnvBase = "environment";
- const string CompressedAssembliesBase = "compressed_assemblies";
- const string JniRemappingBase = "jni_remap";
- const string MarshalMethodsBase = "marshal_methods";
-
public override string TaskPrefix => "PAI";
[Required]
@@ -28,9 +19,6 @@ public class PrepareAbiItems : AndroidTask
[Required]
public string Mode { get; set; } = "";
- [Required]
- public bool Debug { get; set; }
-
[Output]
public ITaskItem[]? AssemblySources { get; set; }
@@ -40,33 +28,17 @@ public class PrepareAbiItems : AndroidTask
public override bool RunTask ()
{
var sources = new List ();
- var includes = new List ();
- string baseName;
-
- if (String.Compare ("typemap", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
- baseName = TypeMapBase;
- } else if (String.Compare ("environment", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
- baseName = EnvBase;
- } else if (String.Compare ("compressed", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
- baseName = CompressedAssembliesBase;
- } else if (String.Compare ("jniremap", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
- baseName = JniRemappingBase;
- } else if (String.Compare ("marshal_methods", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
- baseName = MarshalMethodsBase;
- } else {
- Log.LogError ($"Unknown mode: {Mode}");
- return false;
- }
TaskItem item;
+ NativeAssemblerItemsHelper.KnownMode mode = NativeAssemblerItemsHelper.ToKnownMode (Mode);
foreach (string abi in BuildTargetAbis) {
- item = new TaskItem (Path.Combine (NativeSourcesDir, $"{baseName}.{abi}.ll"));
+ item = new TaskItem (NativeAssemblerItemsHelper.GetSourcePath (Log, mode, NativeSourcesDir, abi));
item.SetMetadata ("abi", abi);
+ item.SetMetadata ("RuntimeIdentifier", MonoAndroidHelper.AbiToRid (abi));
sources.Add (item);
}
AssemblySources = sources.ToArray ();
- AssemblyIncludes = includes.ToArray ();
return !Log.HasLoggedErrors;
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
index 21f48d77025..5931e9843e5 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
@@ -129,7 +129,6 @@ public void CheckIncludedAssemblies ([Values (false, true)] bool usesAssemblySto
"System.Collections.dll",
"System.Collections.Concurrent.dll",
"System.Text.RegularExpressions.dll",
- "libarc.bin.so",
};
using (var b = CreateApkBuilder ()) {
@@ -797,5 +796,46 @@ void CreateEmptyFile (string path)
}
}
+ [Test]
+ [TestCase (false)]
+ [TestCase (true)]
+ public void CheckEmbeddedAssemblyStorePackaging (bool useCLR)
+ {
+ var proj = new XamarinAndroidApplicationProject {
+ IsRelease = true
+ };
+
+ AndroidTargetArch[] supportedArches = new[] {
+ AndroidTargetArch.Arm64,
+ AndroidTargetArch.X86_64,
+ };
+
+ proj.SetRuntimeIdentifiers (supportedArches);
+ proj.SetProperty ("AndroidUseAssemblyStore", "true");
+ proj.SetProperty ("_AndroidEmbedAssemblyStoreInRuntime", "true");
+ proj.SetProperty ("UseMonoRuntime", useCLR ? "false" : "true");
+
+ using var b = CreateApkBuilder ();
+ Assert.IsTrue (b.Build (proj), "build should have succeeded.");
+ string apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk");
+
+ var knownAssemblyStoreFiles = new HashSet (StringComparer.Ordinal);
+ foreach (AndroidTargetArch arch in supportedArches) {
+ string archName = MonoAndroidHelper.ArchToAbi (arch);
+ knownAssemblyStoreFiles.Add ($"lib/{archName}/libassemblies.{archName}.blob.so");
+ }
+
+ var foundAssemblyStoreFiles = new HashSet (StringComparer.Ordinal);
+ using var zip = ZipHelper.OpenZip (apk);
+
+ foreach (var entry in zip) {
+ if (knownAssemblyStoreFiles.Contains (entry.FullName)) {
+ foundAssemblyStoreFiles.Add (entry.FullName);
+ }
+ }
+
+ Assert.IsFalse (foundAssemblyStoreFiles.Count != 0, $"APK should not contain any of the files: {FoundFiles ()}");
+ string FoundFiles () => String.Join (", ", foundAssemblyStoreFiles);
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs
index 7a86601ce43..7aff38ae573 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs
@@ -45,7 +45,6 @@ public sealed class ApplicationConfig
public bool uses_assembly_preload;
public bool broken_exception_transitions;
public bool jni_add_native_method_registration_attribute_present;
- public bool have_runtime_config_blob;
public bool have_assemblies_blob;
public bool marshal_methods_enabled;
public bool ignore_split_configs;
@@ -68,7 +67,7 @@ public sealed class ApplicationConfig
public bool managed_marshal_methods_lookup_enabled;
}
- const uint ApplicationConfigFieldCount = 27;
+ const uint ApplicationConfigFieldCount = 26;
const string ApplicationConfigSymbolName = "application_config";
const string AppEnvironmentVariablesSymbolName = "app_environment_variables";
@@ -237,107 +236,102 @@ static ApplicationConfig ReadApplicationConfig (EnvironmentFile envFile)
ret.jni_add_native_method_registration_attribute_present = ConvertFieldToBool ("jni_add_native_method_registration_attribute_present", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 6: // have_runtime_config_blob: bool / .byte
- AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
- ret.have_runtime_config_blob = ConvertFieldToBool ("have_runtime_config_blob", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
- break;
-
- case 7: // have_assemblies_blob: bool / .byte
+ case 6: // have_assemblies_blob: bool / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.have_assemblies_blob = ConvertFieldToBool ("have_assemblies_blob", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 8: // marshal_methods_enabled: bool / .byte
+ case 7: // marshal_methods_enabled: bool / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.marshal_methods_enabled = ConvertFieldToBool ("marshal_methods_enabled", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 9: // ignore_split_configs: bool / .byte
+ case 8: // ignore_split_configs: bool / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.ignore_split_configs = ConvertFieldToBool ("ignore_split_configs", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 10: // bound_stream_io_exception_type: byte / .byte
+ case 9: // bound_stream_io_exception_type: byte / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.bound_stream_io_exception_type = ConvertFieldToByte ("bound_stream_io_exception_type", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 11: // package_naming_policy: uint32_t / .word | .long
+ case 10: // package_naming_policy: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.package_naming_policy = ConvertFieldToUInt32 ("package_naming_policy", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 12: // environment_variable_count: uint32_t / .word | .long
+ case 11: // environment_variable_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.environment_variable_count = ConvertFieldToUInt32 ("environment_variable_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 13: // system_property_count: uint32_t / .word | .long
+ case 12: // system_property_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.system_property_count = ConvertFieldToUInt32 ("system_property_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 14: // number_of_assemblies_in_apk: uint32_t / .word | .long
+ case 13: // number_of_assemblies_in_apk: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_assemblies_in_apk = ConvertFieldToUInt32 ("number_of_assemblies_in_apk", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 15: // bundled_assembly_name_width: uint32_t / .word | .long
+ case 14: // bundled_assembly_name_width: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.bundled_assembly_name_width = ConvertFieldToUInt32 ("bundled_assembly_name_width", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 16: // number_of_assembly_store_files: uint32_t / .word | .long
+ case 15: // number_of_assembly_store_files: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_assembly_store_files = ConvertFieldToUInt32 ("number_of_assembly_store_files", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 17: // number_of_dso_cache_entries: uint32_t / .word | .long
+ case 16: // number_of_dso_cache_entries: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_dso_cache_entries = ConvertFieldToUInt32 ("number_of_dso_cache_entries", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 18: // number_of_aot_cache_entries: uint32_t / .word | .long
+ case 17: // number_of_aot_cache_entries: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_aot_cache_entries = ConvertFieldToUInt32 ("number_of_aot_cache_entries", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 19: // android_runtime_jnienv_class_token: uint32_t / .word | .long
+ case 18: // android_runtime_jnienv_class_token: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.android_runtime_jnienv_class_token = ConvertFieldToUInt32 ("android_runtime_jnienv_class_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 20: // jnienv_initialize_method_token: uint32_t / .word | .long
+ case 19: // jnienv_initialize_method_token: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.jnienv_initialize_method_token = ConvertFieldToUInt32 ("jnienv_initialize_method_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 21: // jnienv_registerjninatives_method_token: uint32_t / .word | .long
+ case 20: // jnienv_registerjninatives_method_token: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.jnienv_registerjninatives_method_token = ConvertFieldToUInt32 ("jnienv_registerjninatives_method_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 22: // jni_remapping_replacement_type_count: uint32_t / .word | .long
+ case 21: // jni_remapping_replacement_type_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.jni_remapping_replacement_type_count = ConvertFieldToUInt32 ("jni_remapping_replacement_type_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 23: // jni_remapping_replacement_method_index_entry_count: uint32_t / .word | .long
+ case 22: // jni_remapping_replacement_method_index_entry_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.jni_remapping_replacement_method_index_entry_count = ConvertFieldToUInt32 ("jni_remapping_replacement_method_index_entry_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 24: // mono_components_mask: uint32_t / .word | .long
+ case 23: // mono_components_mask: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.mono_components_mask = ConvertFieldToUInt32 ("mono_components_mask", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 25: // android_package_name: string / [pointer type]
+ case 24: // android_package_name: string / [pointer type]
Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
pointers.Add (field [1].Trim ());
break;
- case 26: // managed_marshal_methods_lookup_enabled: bool / .byte
+ case 25: // managed_marshal_methods_lookup_enabled: bool / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.managed_marshal_methods_lookup_enabled = ConvertFieldToBool ("managed_marshal_methods_lookup_enabled", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc
index 0d09c5250f4..53233030a42 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc
@@ -5,31 +5,31 @@
"Size": 3036
},
"classes.dex": {
- "Size": 22484
+ "Size": 22488
},
"lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": {
"Size": 18288
},
"lib/arm64-v8a/lib_Java.Interop.dll.so": {
- "Size": 86688
+ "Size": 87624
},
"lib/arm64-v8a/lib_Mono.Android.dll.so": {
- "Size": 117712
+ "Size": 120768
},
"lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": {
- "Size": 22384
+ "Size": 23160
},
"lib/arm64-v8a/lib_System.Console.dll.so": {
- "Size": 24392
+ "Size": 24408
},
"lib/arm64-v8a/lib_System.Linq.dll.so": {
- "Size": 25336
+ "Size": 25344
},
"lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": {
- "Size": 628216
+ "Size": 636760
},
"lib/arm64-v8a/lib_System.Runtime.dll.so": {
- "Size": 20056
+ "Size": 20096
},
"lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": {
"Size": 21480
@@ -37,41 +37,38 @@
"lib/arm64-v8a/lib_UnnamedProject.dll.so": {
"Size": 20024
},
- "lib/arm64-v8a/libarc.bin.so": {
- "Size": 18872
- },
"lib/arm64-v8a/libmono-component-marshal-ilgen.so": {
- "Size": 36440
+ "Size": 36616
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 1524752
+ "Size": 1525968
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
- "Size": 3101112
+ "Size": 3118632
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
- "Size": 71976
+ "Size": 71952
},
"lib/arm64-v8a/libSystem.IO.Compression.Native.so": {
- "Size": 758896
+ "Size": 759304
},
"lib/arm64-v8a/libSystem.Native.so": {
- "Size": 103520
+ "Size": 104312
},
"lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": {
- "Size": 165000
+ "Size": 165240
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 19536
+ "Size": 22384
},
"META-INF/BNDLTOOL.RSA": {
- "Size": 1221
+ "Size": 1223
},
"META-INF/BNDLTOOL.SF": {
- "Size": 3266
+ "Size": 3167
},
"META-INF/MANIFEST.MF": {
- "Size": 3139
+ "Size": 3040
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 2178
@@ -98,5 +95,5 @@
"Size": 1904
}
},
- "PackageSize": 3078677
+ "PackageSize": 3099084
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc
index cf5b327b70c..c397f8e99d2 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc
@@ -5,7 +5,7 @@
"Size": 6652
},
"classes.dex": {
- "Size": 9179108
+ "Size": 9173200
},
"kotlin/annotation/annotation.kotlin_builtins": {
"Size": 928
@@ -35,13 +35,13 @@
"Size": 25424
},
"lib/arm64-v8a/lib_Java.Interop.dll.so": {
- "Size": 96104
+ "Size": 96320
},
"lib/arm64-v8a/lib_Mono.Android.dll.so": {
- "Size": 562336
+ "Size": 562416
},
"lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": {
- "Size": 23224
+ "Size": 23200
},
"lib/arm64-v8a/lib_mscorlib.dll.so": {
"Size": 21456
@@ -50,25 +50,25 @@
"Size": 23096
},
"lib/arm64-v8a/lib_System.Collections.Concurrent.dll.so": {
- "Size": 29896
+ "Size": 29904
},
"lib/arm64-v8a/lib_System.Collections.dll.so": {
"Size": 36304
},
"lib/arm64-v8a/lib_System.Collections.NonGeneric.dll.so": {
- "Size": 25776
+ "Size": 25784
},
"lib/arm64-v8a/lib_System.Collections.Specialized.dll.so": {
"Size": 23856
},
"lib/arm64-v8a/lib_System.ComponentModel.dll.so": {
- "Size": 19608
+ "Size": 19600
},
"lib/arm64-v8a/lib_System.ComponentModel.Primitives.dll.so": {
- "Size": 21336
+ "Size": 21328
},
"lib/arm64-v8a/lib_System.ComponentModel.TypeConverter.dll.so": {
- "Size": 42440
+ "Size": 42432
},
"lib/arm64-v8a/lib_System.Console.dll.so": {
"Size": 24440
@@ -80,25 +80,25 @@
"Size": 24704
},
"lib/arm64-v8a/lib_System.dll.so": {
- "Size": 19856
+ "Size": 19864
},
"lib/arm64-v8a/lib_System.Drawing.dll.so": {
- "Size": 19456
+ "Size": 19448
},
"lib/arm64-v8a/lib_System.Drawing.Primitives.dll.so": {
"Size": 30064
},
"lib/arm64-v8a/lib_System.Formats.Asn1.dll.so": {
- "Size": 50312
+ "Size": 50320
},
"lib/arm64-v8a/lib_System.IO.Compression.Brotli.dll.so": {
- "Size": 29496
+ "Size": 29504
},
"lib/arm64-v8a/lib_System.IO.Compression.dll.so": {
"Size": 33800
},
"lib/arm64-v8a/lib_System.IO.IsolatedStorage.dll.so": {
- "Size": 28336
+ "Size": 28328
},
"lib/arm64-v8a/lib_System.Linq.dll.so": {
"Size": 40696
@@ -107,7 +107,7 @@
"Size": 185864
},
"lib/arm64-v8a/lib_System.Net.Http.dll.so": {
- "Size": 85904
+ "Size": 85856
},
"lib/arm64-v8a/lib_System.Net.Primitives.dll.so": {
"Size": 42184
@@ -116,13 +116,13 @@
"Size": 21568
},
"lib/arm64-v8a/lib_System.ObjectModel.dll.so": {
- "Size": 27088
+ "Size": 27096
},
"lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": {
- "Size": 967200
+ "Size": 968480
},
"lib/arm64-v8a/lib_System.Private.DataContractSerialization.dll.so": {
- "Size": 216496
+ "Size": 216504
},
"lib/arm64-v8a/lib_System.Private.Uri.dll.so": {
"Size": 62728
@@ -137,10 +137,10 @@
"Size": 20264
},
"lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": {
- "Size": 21488
+ "Size": 21480
},
"lib/arm64-v8a/lib_System.Runtime.Numerics.dll.so": {
- "Size": 55784
+ "Size": 55840
},
"lib/arm64-v8a/lib_System.Runtime.Serialization.dll.so": {
"Size": 19376
@@ -149,16 +149,16 @@
"Size": 20352
},
"lib/arm64-v8a/lib_System.Runtime.Serialization.Primitives.dll.so": {
- "Size": 21472
+ "Size": 21464
},
"lib/arm64-v8a/lib_System.Security.Cryptography.dll.so": {
- "Size": 81288
+ "Size": 81280
},
"lib/arm64-v8a/lib_System.Text.RegularExpressions.dll.so": {
- "Size": 187056
+ "Size": 187120
},
"lib/arm64-v8a/lib_System.Xml.dll.so": {
- "Size": 19272
+ "Size": 19264
},
"lib/arm64-v8a/lib_System.Xml.Linq.dll.so": {
"Size": 19288
@@ -235,17 +235,14 @@
"lib/arm64-v8a/lib_Xamarin.Google.Android.Material.dll.so": {
"Size": 84912
},
- "lib/arm64-v8a/libarc.bin.so": {
- "Size": 18936
- },
"lib/arm64-v8a/libmono-component-marshal-ilgen.so": {
- "Size": 36600
+ "Size": 36616
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 1516584
+ "Size": 1525968
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
- "Size": 3110944
+ "Size": 3118632
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 71952
@@ -254,13 +251,13 @@
"Size": 759304
},
"lib/arm64-v8a/libSystem.Native.so": {
- "Size": 103520
+ "Size": 104312
},
"lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": {
- "Size": 165000
+ "Size": 165240
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 353048
+ "Size": 357088
},
"META-INF/androidx.activity_activity.version": {
"Size": 6
@@ -416,7 +413,7 @@
"Size": 1221
},
"META-INF/BNDLTOOL.SF": {
- "Size": 98445
+ "Size": 98346
},
"META-INF/com.android.tools/proguard/coroutines.pro": {
"Size": 1345
@@ -443,7 +440,7 @@
"Size": 5
},
"META-INF/MANIFEST.MF": {
- "Size": 98318
+ "Size": 98219
},
"META-INF/maven/com.google.guava/listenablefuture/pom.properties": {
"Size": 96
@@ -2483,5 +2480,5 @@
"Size": 812848
}
},
- "PackageSize": 10955937
+ "PackageSize": 10959960
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
index 40ea29ee665..f9fcecb7e44 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
@@ -30,7 +30,6 @@ sealed class ApplicationConfig
public bool uses_assembly_preload;
public bool broken_exception_transitions;
public bool jni_add_native_method_registration_attribute_present;
- public bool have_runtime_config_blob;
public bool have_assemblies_blob;
public bool marshal_methods_enabled;
public bool ignore_split_configs;
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
index 5844b9fb427..c45eb1fcaf6 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
@@ -182,7 +182,6 @@ sealed class XamarinAndroidBundledAssembly
public bool BrokenExceptionTransitions { get; set; }
public global::Android.Runtime.BoundExceptionType BoundExceptionType { get; set; }
public bool JniAddNativeMethodRegistrationAttributePresent { get; set; }
- public bool HaveRuntimeConfigBlob { get; set; }
public bool HaveAssemblyStore { get; set; }
public int NumberOfAssembliesInApk { get; set; }
public int BundledAssemblyNameWidth { get; set; } // including the trailing NUL
@@ -194,6 +193,7 @@ sealed class XamarinAndroidBundledAssembly
public MonoComponent MonoComponents { get; set; }
public PackageNamingPolicy PackageNamingPolicy { get; set; }
public List NativeLibraries { get; set; }
+ public uint PackagedNativeLibrariesCount { get; set; }
public bool MarshalMethodsEnabled { get; set; }
public bool ManagedMarshalMethodsLookupEnabled { get; set; }
public bool IgnoreSplitConfigs { get; set; }
@@ -237,7 +237,6 @@ protected override void Construct (LlvmIrModule module)
uses_assembly_preload = UsesAssemblyPreload,
broken_exception_transitions = BrokenExceptionTransitions,
jni_add_native_method_registration_attribute_present = JniAddNativeMethodRegistrationAttributePresent,
- have_runtime_config_blob = HaveRuntimeConfigBlob,
have_assemblies_blob = HaveAssemblyStore,
marshal_methods_enabled = MarshalMethodsEnabled,
managed_marshal_methods_lookup_enabled = ManagedMarshalMethodsLookupEnabled,
@@ -247,7 +246,7 @@ protected override void Construct (LlvmIrModule module)
environment_variable_count = (uint)(environmentVariables == null ? 0 : environmentVariables.Count * 2),
system_property_count = (uint)(systemProperties == null ? 0 : systemProperties.Count * 2),
number_of_assemblies_in_apk = (uint)NumberOfAssembliesInApk,
- number_of_shared_libraries = (uint)NativeLibraries.Count,
+ number_of_shared_libraries = PackagedNativeLibrariesCount,
bundled_assembly_name_width = (uint)BundledAssemblyNameWidth,
number_of_dso_cache_entries = (uint)dsoCache.Count,
number_of_aot_cache_entries = (uint)aotDsoCache.Count,
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs
index ca8bc0d5f55..88bd177000c 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs
@@ -211,6 +211,7 @@ sealed class XamarinAndroidBundledAssembly
public bool MarshalMethodsEnabled { get; set; }
public bool ManagedMarshalMethodsLookupEnabled { get; set; }
public bool IgnoreSplitConfigs { get; set; }
+ public uint PackagedNativeLibrariesCount { get; set; }
public ApplicationConfigNativeAssemblyGeneratorCLR (IDictionary environmentVariables, IDictionary systemProperties,
IDictionary? runtimeProperties, TaskLoggingHelper log)
@@ -266,7 +267,7 @@ protected override void Construct (LlvmIrModule module)
environment_variable_count = (uint)(environmentVariables == null ? 0 : environmentVariables.Count * 2),
system_property_count = (uint)(systemProperties == null ? 0 : systemProperties.Count * 2),
number_of_assemblies_in_apk = (uint)NumberOfAssembliesInApk,
- number_of_shared_libraries = (uint)NativeLibraries.Count,
+ number_of_shared_libraries = PackagedNativeLibrariesCount,
bundled_assembly_name_width = (uint)BundledAssemblyNameWidth,
number_of_dso_cache_entries = (uint)dsoCache.Count,
number_of_aot_cache_entries = (uint)aotDsoCache.Count,
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs
index 9ef32a4dcb8..71da2ab254a 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs
@@ -170,7 +170,7 @@ static string GetCompressedAssemblyOutputDirectory (ITaskItem assembly, string c
{
string assemblyOutputDir;
string subDirectory = assembly.GetMetadata ("DestinationSubDirectory");
- string abi = MonoAndroidHelper.GetAssemblyAbi (assembly);
+ string abi = MonoAndroidHelper.GetItemAbi (assembly);
if (!string.IsNullOrEmpty (subDirectory) && !(subDirectory.EndsWith ($"{abi}/", StringComparison.Ordinal) || subDirectory.EndsWith ($"{abi}\\", StringComparison.Ordinal))) {
assemblyOutputDir = Path.Combine (compressedOutputDir, abi, subDirectory);
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPackagingHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPackagingHelper.cs
index 1599a8e581e..572af36521b 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPackagingHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPackagingHelper.cs
@@ -10,20 +10,52 @@ namespace Xamarin.Android.Tasks;
static class AssemblyPackagingHelper
{
+ public static bool ShouldSkipAssembly (TaskLoggingHelper log, ITaskItem asm)
+ {
+ var should_skip = asm.GetMetadataOrDefault ("AndroidSkipAddToPackage", false);
+
+ if (should_skip) {
+ log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
+ }
+
+ return should_skip;
+ }
+
+ public static Dictionary CreateAssemblyStore (TaskLoggingHelper log, IEnumerable assemblies, string outputDir, string[] supportedAbis, bool includeDebugSymbols)
+ {
+ var storeBuilder = new AssemblyStoreBuilder (log);
+ var per_arch_assemblies = MonoAndroidHelper.GetPerArchAssemblies (assemblies, supportedAbis, true);
+
+ foreach (var kvp in per_arch_assemblies) {
+ log.LogDebugMessage ($"Adding assemblies for architecture '{kvp.Key}'");
+
+ foreach (var assembly in kvp.Value.Values) {
+ var sourcePath = assembly.GetMetadataOrDefault ("CompressedAssembly", assembly.ItemSpec);
+ storeBuilder.AddAssembly (sourcePath, assembly, includeDebugSymbols: includeDebugSymbols);
+
+ log.LogDebugMessage ($"Added '{sourcePath}' to assembly store.");
+ }
+ }
+
+ Dictionary assemblyStorePaths = storeBuilder.Generate (outputDir);
+ if (assemblyStorePaths.Count == 0) {
+ throw new InvalidOperationException ("Assembly store generator did not generate any stores");
+ }
+
+ if (assemblyStorePaths.Count != supportedAbis.Length) {
+ throw new InvalidOperationException ("Internal error: assembly store did not generate store for each supported ABI");
+ }
+
+ return assemblyStorePaths;
+ }
+
public static void AddAssembliesFromCollection (TaskLoggingHelper Log, ICollection SupportedAbis, ICollection assemblies, Action doAddAssembly)
{
Dictionary> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (
assemblies,
SupportedAbis,
validate: true,
- shouldSkip: (ITaskItem asm) => {
- if (bool.TryParse (asm.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value) {
- Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
- return true;
- }
-
- return false;
- }
+ shouldSkip: (ITaskItem asm) => ShouldSkipAssembly (Log, asm)
);
foreach (var kvp in perArchAssemblies) {
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs
index 024f2cf0ac2..35c355a5872 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs
@@ -104,7 +104,9 @@ string Generate (string baseOutputDirectory, AndroidTargetArch arch, List ();
var descriptors = new List ();
ulong namesSize = 0;
@@ -118,7 +120,8 @@ string Generate (string baseOutputDirectory, AndroidTargetArch arch, List {
"--add-section",
$"payload={MonoAndroidHelper.QuoteFileNameArgument (payloadFilePath)}",
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ELFEmbeddingHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ELFEmbeddingHelper.cs
new file mode 100644
index 00000000000..7481164a829
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ELFEmbeddingHelper.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using Microsoft.Android.Build.Tasks;
+using Microsoft.Build.Utilities;
+using Xamarin.Android.Tools;
+
+namespace Xamarin.Android.Tasks;
+
+class ELFEmbeddingHelper
+{
+ public sealed class EmbedItem
+ {
+ public readonly string SymbolName;
+ public readonly string BaseFileName;
+ public readonly NativeAssemblerItemsHelper.KnownMode NativeAssemblerMode;
+
+ public EmbedItem (string symbolName, string baseFileName, NativeAssemblerItemsHelper.KnownMode nativeAssemblerMode)
+ {
+ SymbolName = symbolName;
+ BaseFileName = baseFileName;
+ NativeAssemblerMode = nativeAssemblerMode;
+ }
+ }
+
+ public static class KnownEmbedItems
+ {
+ public static readonly EmbedItem RuntimeConfig = new ("embedded_runtime_config", "runtime_config", NativeAssemblerItemsHelper.KnownMode.EmbeddedRuntimeConfig);
+ public static readonly EmbedItem AssemblyStore = new ("embedded_assembly_store", "assembly_store", NativeAssemblerItemsHelper.KnownMode.EmbeddedAssemblyStore);
+ }
+
+ static readonly Encoding asmFileEncoding = new UTF8Encoding (false);
+
+ public static void EmbedBinary (
+ TaskLoggingHelper log,
+ ICollection supportedAbis,
+ string androidBinUtilsDirectory,
+ string? inputFile,
+ EmbedItem embedItem,
+ string outputDirectory,
+ bool missingContentOK)
+ {
+ if (supportedAbis.Count < 1) {
+ log.LogDebugMessage ("ELFEmbeddingHelper: at least one target ABI must be specified. Probably a DTB build, skipping generation.");
+ return;
+ }
+
+ foreach (string abi in supportedAbis) {
+ DoEmbed (
+ log,
+ MonoAndroidHelper.AbiToTargetArch (abi),
+ inputFile, outputDirectory,
+ embedItem,
+ missingContentOK
+ );
+ }
+ }
+
+ public static void EmbedBinary (
+ TaskLoggingHelper log,
+ string abi,
+ string androidBinUtilsDirectory,
+ string? inputFile,
+ EmbedItem embedItem,
+ string outputDirectory,
+ bool missingContentOK)
+ {
+ if (String.IsNullOrEmpty (abi)) {
+ log.LogDebugMessage ("ELFEmbeddingHelper: ABI must be specified. Probably a DTB build, skipping generation.");
+ return;
+ }
+
+ DoEmbed (
+ log,
+ MonoAndroidHelper.AbiToTargetArch (abi),
+ inputFile,
+ outputDirectory,
+ embedItem,
+ missingContentOK
+ );
+ }
+
+ static void DoEmbed (
+ TaskLoggingHelper log,
+ AndroidTargetArch arch,
+ string? inputFile,
+ string outputDirectory,
+ EmbedItem item,
+ bool missingContentOK)
+ {
+ NativeAssemblerCompilation.LlvmMcTargetConfig cfg = NativeAssemblerCompilation.GetLlvmMcConfig (arch);
+
+ bool haveInputFile = !String.IsNullOrEmpty (inputFile);
+ if (!haveInputFile) {
+ if (!missingContentOK) {
+ throw new InvalidOperationException ("Internal error: input file must be specified");
+ }
+ } else {
+ inputFile = Path.GetFullPath (inputFile);
+ }
+
+ long inputFileSize = 0;
+ string? sanitizedInputFilePath = null;
+
+ if (haveInputFile) {
+ var fi = new FileInfo (inputFile);
+ if (fi.Exists) {
+ inputFileSize = fi.Length;
+ sanitizedInputFilePath = inputFile!.Replace ("\\", "\\\\");
+ } else if (!missingContentOK) {
+ throw new InvalidOperationException ($"Internal error: input file '{inputFile}' does not exist");
+ }
+ }
+
+ string? asmSourceFile = NativeAssemblerItemsHelper.GetSourcePath (log, item.NativeAssemblerMode, outputDirectory, arch);
+ if (String.IsNullOrEmpty (asmSourceFile)) {
+ log.LogError ("Unable to embed a binary file in native assembly, no assembly source path given.");
+ return;
+ }
+
+ Directory.CreateDirectory (Path.GetDirectoryName (asmSourceFile));
+ using var fs = File.Open (asmSourceFile, FileMode.Create, FileAccess.Write, FileShare.Read);
+ using var sw = new StreamWriter (fs, asmFileEncoding);
+
+ string symbolName = item.SymbolName;
+ sw.WriteLine ($".section .rodata,\"a\",{cfg.AssemblerDirectivePrefix}progbits");
+ sw.WriteLine (".p2align 3, 0x00"); // Put the data at the 4k boundary
+ sw.WriteLine ();
+ sw.WriteLine ($".global {symbolName}");
+ sw.WriteLine ($".type {symbolName},{cfg.AssemblerDirectivePrefix}object");
+ sw.WriteLine ($"{symbolName}:");
+
+ if (!String.IsNullOrEmpty (sanitizedInputFilePath)) {
+ sw.WriteLine ($"\t.incbin \"{sanitizedInputFilePath}\"");
+ }
+ sw.WriteLine ($"\t.size {symbolName}, {inputFileSize}");
+ sw.WriteLine ();
+
+ symbolName += "_size";
+ sw.WriteLine ($".global {symbolName}");
+ sw.WriteLine ($"{symbolName}:");
+ sw.WriteLine ($"\t{cfg.SizeType}\t{inputFileSize}");
+ sw.WriteLine ($"\t.size {symbolName}, {cfg.WordSize}");
+
+ sw.Flush ();
+ sw.Close ();
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.Basic.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.Basic.cs
new file mode 100644
index 00000000000..df401b2f462
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.Basic.cs
@@ -0,0 +1,61 @@
+using System;
+
+using ELFSharp.ELF;
+using ELFSharp.ELF.Sections;
+
+namespace Xamarin.Android.Tasks;
+
+static partial class ELFHelper
+{
+ public static ISymbolTable? GetSymbolTable (IELF elf, string sectionName)
+ {
+ ISection? section = GetSection (elf, sectionName);
+ if (section == null) {
+ return null;
+ }
+
+ var symtab = section as ISymbolTable;
+ if (symtab == null) {
+ return null;
+ }
+
+ return symtab;
+ }
+
+ public static ISection? GetSection (IELF elf, string sectionName)
+ {
+ if (!elf.TryGetSection (sectionName, out ISection section)) {
+ return null;
+ }
+
+ return section;
+ }
+
+ public static SymbolEntry? FindSymbol (ISymbolTable? symbolTable, string symbolName) where T: struct
+ {
+ if (symbolTable == null) {
+ return null;
+ }
+
+ ISymbolEntry? symbol = null;
+ foreach (ISymbolEntry entry in symbolTable.Entries) {
+ if (String.Compare (entry.Name, symbolName, StringComparison.Ordinal) != 0) {
+ continue;
+ }
+
+ symbol = entry;
+ break;
+ }
+
+ if (symbol == null) {
+ return null;
+ }
+
+ Type t = typeof(T);
+ if (t == typeof(ulong) || t == typeof(uint)) {
+ return (SymbolEntry)symbol;
+ }
+
+ throw new InvalidOperationException ($"Only `ulong` and `uint` types are accepted");
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs
index 5c4879dc323..f51b9fb30b9 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs
@@ -16,7 +16,7 @@
namespace Xamarin.Android.Tasks
{
- static class ELFHelper
+ static partial class ELFHelper
{
public static void AssertValidLibraryAlignment (TaskLoggingHelper log, int alignmentInPages, string path, ITaskItem? item)
{
@@ -226,29 +226,5 @@ bool IsNonEmptyCodeSymbol (SymbolEntry? symbolEntry) where T : struct
return size != 0 && symbolEntry.PointedSection.Type == ELFSectionType.ProgBits;
}
}
-
- static ISymbolTable? GetSymbolTable (IELF elf, string sectionName)
- {
- ISection? section = GetSection (elf, sectionName);
- if (section == null) {
- return null;
- }
-
- var symtab = section as ISymbolTable;
- if (symtab == null) {
- return null;
- }
-
- return symtab;
- }
-
- static ISection? GetSection (IELF elf, string sectionName)
- {
- if (!elf.TryGetSection (sectionName, out ISection section)) {
- return null;
- }
-
- return section;
- }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
index 416ccb3f78f..48369483e06 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
@@ -290,7 +290,7 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li
string.IsNullOrEmpty (VersionName) ? "1.0" : VersionName);
}
- app = CreateApplicationElement (manifest, applicationClass, subclasses, cache);
+ app = CreateApplicationElement (log, manifest, applicationClass, subclasses, cache);
if (app.Attribute (androidNs + "label") == null && !string.IsNullOrEmpty (ApplicationLabel))
app.SetAttributeValue (androidNs + "label", ApplicationLabel);
@@ -570,7 +570,7 @@ Func GetGenerator (T
return null;
}
- XElement CreateApplicationElement (XElement manifest, string applicationClass, List subclasses, TypeDefinitionCache cache)
+ XElement CreateApplicationElement (TaskLoggingHelper log, XElement manifest, string applicationClass, List subclasses, TypeDefinitionCache cache)
{
var application = manifest.Descendants ("application").FirstOrDefault ();
@@ -581,6 +581,11 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L
List usesConfigurationAttr = [];
foreach (var assemblyPath in Assemblies) {
var assembly = Resolver.GetAssembly (assemblyPath);
+ if (assembly == null) {
+ log.LogDebugMessage ($"Assembly '{assemblyPath}' not found.");
+ continue;
+ }
+
if (ApplicationAttribute.FromCustomAttributeProvider (assembly, cache) is ApplicationAttribute a) {
assemblyAttr.Add (a);
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
index 518477d9ec4..6e49b7973af 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
@@ -568,17 +568,17 @@ public static int ConvertSupportedOSPlatformVersionToApiLevel (string version)
}
#if MSBUILD
- public static string GetAssemblyAbi (ITaskItem asmItem)
+ public static string GetItemAbi (ITaskItem asmItem)
{
string? abi = asmItem.GetMetadata ("Abi");
if (String.IsNullOrEmpty (abi)) {
- throw new InvalidOperationException ($"Internal error: assembly '{asmItem}' lacks ABI metadata");
+ throw new InvalidOperationException ($"Internal error: item '{asmItem}' lacks ABI metadata");
}
return abi;
}
- public static AndroidTargetArch GetTargetArch (ITaskItem asmItem) => AbiToTargetArch (GetAssemblyAbi (asmItem));
+ public static AndroidTargetArch GetTargetArch (ITaskItem asmItem) => AbiToTargetArch (GetItemAbi (asmItem));
public static AndroidTargetArch GetRequiredValidArchitecture (ITaskItem item)
@@ -783,6 +783,15 @@ public static string QuoteFileNameArgument (string? fileName)
return builder.ToString ();
}
+ public static string GetLlvmObjcopyPath (string androidBinUtilsDirectory) => GetBinUtilsToolPath (androidBinUtilsDirectory, "llvm-objcopy");
+ public static string GetLlvmMcPath (string androidBinUtilsDirectory) => GetBinUtilsToolPath (androidBinUtilsDirectory, "llvm-mc");
+ public static string GetLlvmLlcPath (string androidBinUtilsDirectory) => GetBinUtilsToolPath (androidBinUtilsDirectory, "llc");
+
+ static string GetBinUtilsToolPath (string androidBinUtilsDirectory, string toolName)
+ {
+ return Path.Combine (androidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (androidBinUtilsDirectory, toolName));
+ }
+
public static AndroidRuntime ParseAndroidRuntime (string androidRuntime)
{
if (string.Equals (androidRuntime, "CoreCLR", StringComparison.OrdinalIgnoreCase))
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerCompilation.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerCompilation.cs
new file mode 100644
index 00000000000..6e4c08478ce
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerCompilation.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+
+using Microsoft.Android.Build.Tasks;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Xamarin.Android.Tools;
+
+namespace Xamarin.Android.Tasks;
+
+class NativeAssemblerCompilation
+{
+ public sealed class AssemblerConfig
+ {
+ public readonly string ExecutablePath;
+ public readonly string Options;
+ public readonly string InputSource;
+
+ public AssemblerConfig (string executablePath, string options, string inputSource)
+ {
+ ExecutablePath = executablePath;
+ Options = options;
+ InputSource = inputSource;
+ }
+ }
+
+ public sealed class AssemblerRunContext
+ {
+ public readonly TaskLoggingHelper Log;
+ public readonly Action? RegisterForCancellation;
+ public readonly Action? Cancel;
+ public readonly string? WorkingDirectory;
+
+ public AssemblerRunContext (TaskLoggingHelper log, string? workingDirectory = null, Action? registerForCancellation = null, Action? cancel = null)
+ {
+ Log = log;
+ RegisterForCancellation = registerForCancellation;
+ Cancel = cancel;
+ WorkingDirectory = workingDirectory;
+ }
+ }
+
+ public sealed class LlvmMcTargetConfig
+ {
+ public readonly string TargetArch;
+ public readonly string TripleArch;
+ public readonly string TripleApiPrefix;
+ public readonly string AssemblerDirectivePrefix;
+ public readonly string SizeType;
+ public readonly uint WordSize;
+
+ public LlvmMcTargetConfig (string targetArch, string tripleArch, string tripleApiPrefix, string assemblerDirectivePrefix, string sizeType, uint wordSize)
+ {
+ TargetArch = targetArch;
+ TripleArch = tripleArch;
+ TripleApiPrefix = tripleApiPrefix;
+ AssemblerDirectivePrefix = assemblerDirectivePrefix;
+ SizeType = sizeType;
+ WordSize = wordSize;
+ }
+ }
+
+ static readonly Dictionary llvmMcConfigs = new () {
+ { AndroidTargetArch.Arm64, new ("aarch64", "aarch64", "android", "@", ".xword", 8) },
+ { AndroidTargetArch.Arm, new ("arm", "armv7a", "androideabi", "%", ".long", 4) },
+ { AndroidTargetArch.X86_64, new ("x86-64", "x86_64", "android", "@", ".quad", 8) },
+ { AndroidTargetArch.X86, new ("x86", "i686", "android", "@", ".long", 4) },
+ };
+
+ static readonly List llcArguments = new () {
+ "-O2",
+ "--debugger-tune=lldb", // NDK uses lldb now
+ "--debugify-level=location+variables",
+ "--fatal-warnings",
+ "--filetype=obj",
+ "--relocation-model=pic",
+ };
+
+ static readonly List llvmMcArguments = new () {
+ "--assemble",
+ "--filetype=obj",
+ "-g",
+ };
+
+ public static AssemblerConfig GetAssemblerConfig (string androidBinUtilsDir, ITaskItem source, bool stripFilePaths)
+ {
+ string sourceFile = stripFilePaths ? Path.GetFileName (source.ItemSpec) : source.ItemSpec;
+ string sourceExtension = Path.GetExtension (sourceFile);
+ string executable;
+ var arguments = new List ();
+
+ if (String.Compare (".ll", sourceExtension, StringComparison.OrdinalIgnoreCase) == 0) {
+ executable = MonoAndroidHelper.GetLlvmLlcPath (androidBinUtilsDir);
+ arguments.AddRange (llcArguments);
+ } else if (String.Compare (".s", sourceExtension, StringComparison.OrdinalIgnoreCase) == 0) {
+ executable = MonoAndroidHelper.GetLlvmMcPath (androidBinUtilsDir);
+ arguments.AddRange (llvmMcArguments);
+
+ LlvmMcTargetConfig cfg = GetLlvmMcConfig (MonoAndroidHelper.GetTargetArch (source));
+ arguments.Add ($"--arch={cfg.TargetArch}");
+ arguments.Add ($"--triple={cfg.TripleArch}-linux-{cfg.TripleApiPrefix}{XABuildConfig.AndroidMinimumDotNetApiLevel}");
+ } else {
+ throw new InvalidOperationException ($"Internal exception: unknown native assembler source {source.ItemSpec}");
+ }
+
+ string outputFile = Path.ChangeExtension (sourceFile, ".o");
+ arguments.Add ("-o");
+ arguments.Add (MonoAndroidHelper.QuoteFileNameArgument (outputFile));
+ arguments.Add (MonoAndroidHelper.QuoteFileNameArgument (sourceFile));
+
+ return new AssemblerConfig (executable, String.Join (" ", arguments), source.ItemSpec);
+ }
+
+ public static LlvmMcTargetConfig GetLlvmMcConfig (AndroidTargetArch arch)
+ {
+ if (!llvmMcConfigs.TryGetValue (arch, out LlvmMcTargetConfig cfg)) {
+ throw new NotSupportedException ($"Internal error: unsupported target arch '{arch}'");
+ }
+
+ return cfg;
+ }
+
+ public static void RunAssembler (AssemblerRunContext context, AssemblerConfig config)
+ {
+ var stdout_completed = new ManualResetEvent (false);
+ var stderr_completed = new ManualResetEvent (false);
+ var psi = new ProcessStartInfo () {
+ FileName = config.ExecutablePath,
+ Arguments = config.Options,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ WorkingDirectory = context.WorkingDirectory,
+ };
+
+ string assemblerName = Path.GetFileName (config.ExecutablePath);
+ context.Log.LogDebugMessage ($"[{assemblerName}] {psi.FileName} {psi.Arguments}");
+
+ var stdoutLines = new List ();
+ var stderrLines = new List ();
+
+ using var proc = new Process ();
+ proc.OutputDataReceived += (s, e) => {
+ if (e.Data != null) {
+ OnOutputData (context, assemblerName, s, e);
+ stdoutLines.Add (e.Data);
+ } else {
+ stdout_completed.Set ();
+ }
+ };
+
+ proc.ErrorDataReceived += (s, e) => {
+ if (e.Data != null) {
+ OnErrorData (context, assemblerName, s, e);
+ stderrLines.Add (e.Data);
+ } else {
+ stderr_completed.Set ();
+ }
+ };
+
+ proc.StartInfo = psi;
+ proc.Start ();
+ proc.BeginOutputReadLine ();
+ proc.BeginErrorReadLine ();
+ context.RegisterForCancellation?.Invoke (proc);
+
+ proc.WaitForExit ();
+
+ if (psi.RedirectStandardError) {
+ stderr_completed.WaitOne (TimeSpan.FromSeconds (30));
+ }
+
+ if (psi.RedirectStandardOutput) {
+ stdout_completed.WaitOne (TimeSpan.FromSeconds (30));
+ }
+
+ if (proc.ExitCode != 0) {
+ var sb = MonoAndroidHelper.MergeStdoutAndStderrMessages (stdoutLines, stderrLines);
+ context.Log.LogCodedError ("XA3006", Properties.Resources.XA3006, Path.GetFileName (config.InputSource), sb.ToString ());
+ context.Cancel?.Invoke ();
+ }
+ }
+
+ static void OnOutputData (AssemblerRunContext context, string assemblerName, object sender, DataReceivedEventArgs e)
+ {
+ context.Log.LogDebugMessage ($"[{assemblerName} stdout] {e.Data}");
+ }
+
+ static void OnErrorData (AssemblerRunContext context, string assemblerName, object sender, DataReceivedEventArgs e)
+ {
+ context.Log.LogMessage ($"[{assemblerName} stderr] {e.Data}", MessageImportance.High);
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerItemsHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerItemsHelper.cs
new file mode 100644
index 00000000000..6781f2461eb
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerItemsHelper.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Microsoft.Build.Utilities;
+using Xamarin.Android.Tools;
+
+namespace Xamarin.Android.Tasks;
+
+static class NativeAssemblerItemsHelper
+{
+ public enum KnownMode
+ {
+ CompressedAssemblies,
+ EmbeddedAssemblyStore,
+ EmbeddedRuntimeConfig,
+ Environment,
+ JNIRemap,
+ MarshalMethods,
+ TypeMap,
+ }
+
+ sealed class ModeConfig
+ {
+ public readonly string FileNameBase;
+ public readonly string Extension;
+
+ public ModeConfig (string fileNameBase, string extension)
+ {
+ FileNameBase = fileNameBase;
+ Extension = extension;
+ }
+ }
+
+ const string LlvmIrExtension = "ll";
+ const string NativeAssemblerExtension = "s";
+
+ static readonly Dictionary ModeConfigs = new () {
+ { KnownMode.CompressedAssemblies, new ("compressed_assemblies", LlvmIrExtension) },
+ { KnownMode.EmbeddedAssemblyStore, new ("embed_assembly_store", NativeAssemblerExtension) },
+ { KnownMode.EmbeddedRuntimeConfig, new ("embed_runtime_config", NativeAssemblerExtension) },
+ { KnownMode.Environment, new ("environment", LlvmIrExtension) },
+ { KnownMode.JNIRemap, new ("jni_remap", LlvmIrExtension) },
+ { KnownMode.MarshalMethods, new ("marshal_methods", LlvmIrExtension) },
+ { KnownMode.TypeMap, new ("typemaps", LlvmIrExtension) },
+ };
+
+ public static string? GetSourcePath (TaskLoggingHelper log, KnownMode mode, string nativeSourcesDir, AndroidTargetArch arch)
+ {
+ return GetSourcePath (log, mode, nativeSourcesDir, MonoAndroidHelper.ArchToAbi (arch));
+ }
+
+ public static string? GetSourcePath (TaskLoggingHelper log, KnownMode mode, string nativeSourcesDir, string abi)
+ {
+ if (!ModeConfigs.TryGetValue (mode, out ModeConfig config)) {
+ log.LogError ($"Unknown mode: {mode}");
+ return null;
+ }
+
+ return Path.Combine (nativeSourcesDir, $"{config.FileNameBase}.{abi.ToLowerInvariant ()}.{config.Extension}");
+ }
+
+ public static KnownMode ToKnownMode (string mode)
+ {
+ if (!Enum.TryParse (mode, ignoreCase: true, out KnownMode result)) {
+ throw new InvalidOperationException ($"Internal exception: uknown native assembler generator mode '{mode}'");
+ }
+
+ return result;
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index 6421833e60e..d5289830858 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -47,7 +47,6 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
-
@@ -312,6 +311,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<_AndroidFastDeployEnvironmentFiles Condition=" '$(_AndroidFastDeployEnvironmentFiles)' == '' And '$(EmbedAssembliesIntoApk)' == 'False' ">True
<_AndroidFastDeployEnvironmentFiles Condition=" '$(_AndroidFastDeployEnvironmentFiles)' == '' ">False
+ <_AndroidCompressedAssembliesDir>$(IntermediateOutputPath)android\lz4
<_AndroidUseCLR Condition=" '$(_AndroidRuntime)' == 'CoreCLR' ">True
<_AndroidUseCLR Condition=" '$(_AndroidRuntime)' != 'CoreCLR' ">False
@@ -353,6 +353,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
+ <_EmbedAssemblyStoreInRuntime Condition=" '$(_AndroidUseAssemblyStore)' == 'True' And '$(_AndroidEmbedAssemblyStoreInRuntime)' == 'True' ">true
+ <_EmbedAssemblyStoreInRuntime Condition=" '$(_EmbedAssemblyStoreInRuntime)' != 'true' ">false
+
<_AndroidAotStripLibraries Condition=" '$(_AndroidAotStripLibraries)' == '' And '$(AndroidIncludeDebugSymbols)' != 'true' ">True
True
False
@@ -363,6 +366,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False
<_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods)
+ <_AndroidEmbedAssemblyStoreInRuntime Condition=" '$(AndroidUseAssemblyStore)' == 'True' And '$(EmbedAssembliesIntoApk)' == 'true' And '$(_AndroidEmbedAssemblyStoreInRuntime)' == '' ">True
+ <_AndroidEmbedAssemblyStoreInRuntime Condition="'$(_AndroidEmbedAssemblyStoreInRuntime)' == '' ">False
+
<_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' and '$(_AndroidUseMarshalMethods)' == 'True' and '$(_AndroidRuntime)' != 'MonoVM' ">True
<_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' ">False
@@ -1536,10 +1542,8 @@ because xbuild doesn't support framework reference assemblies.
+ Mode="TypeMap">
-
@@ -1682,7 +1686,6 @@ because xbuild doesn't support framework reference assemblies.
-
@@ -1754,24 +1757,28 @@ because xbuild doesn't support framework reference assemblies.
+ Mode="Environment">
+ Mode="CompressedAssemblies">
+ Mode="MarshalMethods">
+
+
+
@@ -1796,8 +1803,7 @@ because xbuild doesn't support framework reference assemblies.
+ Mode="JNIRemap">
@@ -1869,6 +1875,7 @@ because xbuild doesn't support framework reference assemblies.
<_GeneratePackageManagerJavaInputs Include="@(_GenerateJavaStubsInputs)" />
+ <_GeneratePackageManagerJavaInputs Include="$(_BinaryRuntimeConfigPath)" />
@@ -1877,7 +1884,7 @@ because xbuild doesn't support framework reference assemblies.
DependsOnTargets="$(_GeneratePackageManagerJavaDependsOn)"
Inputs="@(_GeneratePackageManagerJavaInputs)"
Outputs="$(_AndroidStampDirectory)_GeneratePackageManagerJava.stamp">
-
+
+ ProjectRuntimeConfigFilePath="$(ProjectRuntimeConfigFilePath)"
+ AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)">
-
+
+
@@ -2039,6 +2048,7 @@ because xbuild doesn't support framework reference assemblies.
<_CompileToDalvikDependsOnTargets>
_CompileJava;
+ _RemoveRegisterAttribute;
_CreateApplicationSharedLibraries;
_GetMonoPlatformJarPath;
_GetLibraryImports;
@@ -2131,7 +2141,7 @@ because xbuild doesn't support framework reference assemblies.
-
+
<_NativeAssemblyTarget Include="@(_TypeMapAssemblySource->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')">
%(_TypeMapAssemblySource.abi)
@@ -2151,6 +2161,22 @@ because xbuild doesn't support framework reference assemblies.
<_NativeAssemblyTarget Include="@(_AndroidRemapAssemblySource->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')">
%(_AndroidRemapAssemblySource.abi)
+ <_NativeAssemblyTarget Include="@(_EmbeddedAssemblyStoreSourceFiles->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')">
+ %(_EmbeddedAssemblyStoreSourceFiles.abi)
+
+ <_NativeAssemblyTarget Include="@(_EmbeddedRuntimeConfigAssemblySource->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')">
+ %(_EmbeddedRuntimeConfigAssemblySource.abi)
+
+
+
+
+ <_NativeAssemblySource Include="@(_AndroidRemapAssemblySource)" />
+ <_NativeAssemblySource Include="@(_CompressedAssembliesAssemblySource)" />
+ <_NativeAssemblySource Include="@(_EmbeddedAssemblyStoreSourceFiles)" />
+ <_NativeAssemblySource Include="@(_EmbeddedRuntimeConfigAssemblySource)" />
+ <_NativeAssemblySource Include="@(_EnvironmentAssemblySource)" />
+ <_NativeAssemblySource Include="@(_MarshalMethodsAssemblySource)" />
+ <_NativeAssemblySource Include="@(_TypeMapAssemblySource)" />
@@ -2169,11 +2195,11 @@ because xbuild doesn't support framework reference assemblies.
-
-
-
-
-
-
-
-
-
-
(payload_start);
-
- auto get_full_store_path = [&apk_path, &store_path]() -> std::string {
- std::string full_store_path;
-
- if (!apk_path.empty ()) {
- full_store_path.append (apk_path);
- // store path will be relative, to the apk
- full_store_path.append ("!/"sv);
- full_store_path.append (store_path);
- } else {
- full_store_path.append (store_path);
- }
-
- return full_store_path;
- };
+ auto header = static_cast(data_start);
if (header->magic != ASSEMBLY_STORE_MAGIC) {
Helpers::abort_application (
LOG_ASSEMBLY,
std::format (
"Assembly store '{}' is not a valid .NET for Android assembly store file"sv,
- get_full_store_path ()
+ name
)
);
}
@@ -274,7 +256,7 @@ void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_v
LOG_ASSEMBLY,
std::format (
"Assembly store '{}' uses format version {:x}, instead of the expected {:x}"sv,
- get_full_store_path (),
+ name,
header->version,
ASSEMBLY_STORE_FORMAT_VERSION
)
@@ -283,11 +265,35 @@ void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_v
constexpr size_t header_size = sizeof(AssemblyStoreHeader);
- assembly_store.data_start = static_cast(payload_start);
+ assembly_store.data_start = static_cast(data_start);
assembly_store.assembly_count = header->entry_count;
assembly_store.index_entry_count = header->index_entry_count;
assembly_store.assemblies = reinterpret_cast(assembly_store.data_start + header_size + header->index_size);
assembly_store_hashes = reinterpret_cast(assembly_store.data_start + header_size);
+}
+
+void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_view const& store_path, uint32_t offset, uint32_t size) noexcept
+{
+ detail::mmap_info assembly_store_map = Util::mmap_file (fd, offset, size, store_path);
+ auto [payload_start, payload_size] = Util::get_wrapper_dso_payload_pointer_and_size (assembly_store_map, store_path);
+ log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: {:p}; size: {}"sv, payload_start, payload_size);
- log_debug (LOG_ASSEMBLY, "Mapped assembly store {}"sv, get_full_store_path ());
+ std::string full_store_path;
+ if (!apk_path.empty ()) {
+ full_store_path.append (apk_path);
+ // store path will be relative, to the apk
+ full_store_path.append ("!/"sv);
+ full_store_path.append (store_path);
+ } else {
+ full_store_path.append (store_path);
+ }
+
+ verify_assembly_store_and_set_info (payload_start, full_store_path);
+ log_debug (LOG_ASSEMBLY, "Mapped assembly store {}"sv, full_store_path);
+}
+
+void AssemblyStore::map () noexcept
+{
+ verify_assembly_store_and_set_info (embedded_assembly_store, ""sv);
+ log_debug (LOG_ASSEMBLY, "Mapped embedded assembly store");
}
diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc
index c48d1a7ca9c..de333da14bb 100644
--- a/src/native/clr/host/host.cc
+++ b/src/native/clr/host/host.cc
@@ -231,11 +231,23 @@ void Host::scan_filesystem_for_assemblies_and_libraries () noexcept
void Host::gather_assemblies_and_libraries (jstring_array_wrapper& runtimeApks, bool have_split_apks)
{
+ // Embedded assembly takes priority over the one found on the filesystem.
+ if (found_assembly_store) {
+ // We have an embedded store, map it
+ AssemblyStore::map ();
+ }
+
if (!AndroidSystem::is_embedded_dso_mode_enabled ()) {
scan_filesystem_for_assemblies_and_libraries ();
return;
}
+ if (found_assembly_store) {
+ // In CoreCLR we only look in the APK for the assembly store. Since we have
+ // an embedded one, though, there's no need to waste time scanning the ZIP.
+ return;
+ }
+
int64_t apk_count = static_cast(runtimeApks.get_length ());
bool got_split_config_abi_apk = false;
diff --git a/src/native/clr/include/host/assembly-store.hh b/src/native/clr/include/host/assembly-store.hh
index 033e7d3ce1c..f49a85f94e3 100644
--- a/src/native/clr/include/host/assembly-store.hh
+++ b/src/native/clr/include/host/assembly-store.hh
@@ -14,6 +14,8 @@ namespace xamarin::android {
public:
static auto open_assembly (std::string_view const& name, int64_t &size) noexcept -> void*;
+ // Maps the embedded assembly store.
+ static void map () noexcept;
static void map (int fd, std::string_view const& apk_path, std::string_view const& store_path, uint32_t offset, uint32_t size) noexcept;
static void map (int fd, std::string_view const& file_path, uint32_t offset, uint32_t size) noexcept
@@ -23,6 +25,7 @@ namespace xamarin::android {
private:
static void set_assembly_data_and_size (uint8_t* source_assembly_data, uint32_t source_assembly_data_size, uint8_t*& dest_assembly_data, uint32_t& dest_assembly_data_size) noexcept;
+ static void verify_assembly_store_and_set_info (void *data_start, std::string_view const& name) noexcept;
// Returns a tuple of
static auto get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData const& e, std::string_view const& name) noexcept -> std::tuple;
diff --git a/src/native/clr/include/host/host.hh b/src/native/clr/include/host/host.hh
index c1ae90788c2..fb1d2f89bc7 100644
--- a/src/native/clr/include/host/host.hh
+++ b/src/native/clr/include/host/host.hh
@@ -8,8 +8,9 @@
#include
#include
-#include "../shared/log_types.hh"
+#include
#include "managed-interface.hh"
+#include
namespace xamarin::android {
class Host
@@ -53,7 +54,7 @@ namespace xamarin::android {
static inline void *clr_host = nullptr;
static inline unsigned int domain_id = 0;
static inline std::shared_ptr _timing{};
- static inline bool found_assembly_store = false;
+ static inline bool found_assembly_store = embedded_assembly_store_size > 0;
static inline jnienv_register_jni_natives_fn jnienv_register_jni_natives = nullptr;
static inline JavaVM *jvm = nullptr;
diff --git a/src/native/clr/include/xamarin-app.hh b/src/native/clr/include/xamarin-app.hh
index 8caa09323d4..f896014d910 100644
--- a/src/native/clr/include/xamarin-app.hh
+++ b/src/native/clr/include/xamarin-app.hh
@@ -427,4 +427,10 @@ struct MarshalMethodName
#endif // def RELEASE
using get_function_pointer_fn = void(*)(uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr);
-extern "C" [[gnu::visibility("default")]] void xamarin_app_init (JNIEnv *env, get_function_pointer_fn fn) noexcept;
+
+extern "C" {
+ [[gnu::visibility("default")]] extern size_t embedded_assembly_store_size;
+ [[gnu::visibility("default")]] extern uint8_t embedded_assembly_store[];
+
+ [[gnu::visibility("default")]] void xamarin_app_init (JNIEnv *env, get_function_pointer_fn fn) noexcept;
+}
diff --git a/src/native/clr/xamarin-app-stub/application_dso_stub.cc b/src/native/clr/xamarin-app-stub/application_dso_stub.cc
index 7919083ef1f..8a32a41fba4 100644
--- a/src/native/clr/xamarin-app-stub/application_dso_stub.cc
+++ b/src/native/clr/xamarin-app-stub/application_dso_stub.cc
@@ -355,3 +355,6 @@ const char *init_runtime_property_names[] = {
char *init_runtime_property_values[] {
nullptr,
};
+
+size_t embedded_assembly_store_size = 0;
+uint8_t embedded_assembly_store[0];
diff --git a/src/native/mono/monodroid/embedded-assemblies-zip.cc b/src/native/mono/monodroid/embedded-assemblies-zip.cc
index 95c36da3f3c..30e01a25fbd 100644
--- a/src/native/mono/monodroid/embedded-assemblies-zip.cc
+++ b/src/native/mono/monodroid/embedded-assemblies-zip.cc
@@ -16,7 +16,7 @@
using namespace xamarin::android::internal;
[[gnu::always_inline]] bool
-EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector const& buf, dynamic_local_string &entry_name, ZipEntryLoadState &state) noexcept
+EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::span const& buf, dynamic_local_string &entry_name, ZipEntryLoadState &state) noexcept
{
entry_name.clear ();
@@ -62,14 +62,6 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector const& buf, uint32_t num_entries, [[maybe_unused]] monodroid_should_register should_register, ZipEntryLoadState &state) noexcept
+EmbeddedAssemblies::zip_load_individual_assembly_entries (std::span const& buf, uint32_t num_entries, [[maybe_unused]] monodroid_should_register should_register, ZipEntryLoadState &state) noexcept
{
// TODO: do away with all the string manipulation here. Replace it with generating xxhash for the entry name
dynamic_local_string entry_name;
@@ -183,6 +175,47 @@ EmbeddedAssemblies::zip_load_individual_assembly_entries (std::vector c
}
}
+
+[[gnu::always_inline]] void
+EmbeddedAssemblies::verify_assembly_store_and_set_info (void *data_start, const char *name) noexcept
+{
+ auto header = static_cast(data_start);
+
+ if (header->magic != ASSEMBLY_STORE_MAGIC) {
+ Helpers::abort_application (
+ LOG_ASSEMBLY,
+ std::format (
+ "Assembly store '{}' is not a valid .NET for Android assembly store file",
+ optional_string (name)
+ )
+ );
+ }
+
+ if (header->version != ASSEMBLY_STORE_FORMAT_VERSION) {
+ Helpers::abort_application (
+ LOG_ASSEMBLY,
+ std::format (
+ "Assembly store '{}' uses format version 0x{:x}, instead of the expected 0x{:x}",
+ optional_string (name),
+ header->version,
+ ASSEMBLY_STORE_FORMAT_VERSION
+ )
+ );
+ }
+
+ constexpr size_t header_size = sizeof(AssemblyStoreHeader);
+
+ assembly_store.data_start = static_cast(data_start);
+ assembly_store.assembly_count = header->entry_count;
+ assembly_store.index_entry_count = header->index_entry_count;
+ assembly_store.assemblies = reinterpret_cast(assembly_store.data_start + header_size + header->index_size);
+ assembly_store_hashes = reinterpret_cast(assembly_store.data_start + header_size);
+
+ number_of_found_assemblies += assembly_store.assembly_count;
+ number_of_mapped_assembly_stores++;
+ have_and_want_debug_symbols = register_debug_symbols;
+}
+
inline void
EmbeddedAssemblies::map_assembly_store (dynamic_local_string const& entry_name, ZipEntryLoadState &state) noexcept
{
@@ -219,55 +252,26 @@ EmbeddedAssemblies::map_assembly_store (dynamic_local_string
}
auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map, entry_name.get ());
- log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: {:p}; size: {}", payload_start, payload_size);
- auto header = static_cast(payload_start);
-
- if (header->magic != ASSEMBLY_STORE_MAGIC) {
- Helpers::abort_application (
- LOG_ASSEMBLY,
- std::format (
- "Assembly store '{}' is not a valid .NET for Android assembly store file",
- optional_string (entry_name.get ())
- )
- );
- }
-
- if (header->version != ASSEMBLY_STORE_FORMAT_VERSION) {
- Helpers::abort_application (
- LOG_ASSEMBLY,
- std::format (
- "Assembly store '{}' uses format version {:x}, instead of the expected {:x}",
- optional_string (entry_name.get ()),
- header->version,
- ASSEMBLY_STORE_FORMAT_VERSION
- )
- );
- }
-
- constexpr size_t header_size = sizeof(AssemblyStoreHeader);
-
- assembly_store.data_start = static_cast(payload_start);
- assembly_store.assembly_count = header->entry_count;
- assembly_store.index_entry_count = header->index_entry_count;
- assembly_store.assemblies = reinterpret_cast(assembly_store.data_start + header_size + header->index_size);
- assembly_store_hashes = reinterpret_cast(assembly_store.data_start + header_size);
-
- number_of_found_assemblies += assembly_store.assembly_count;
- number_of_mapped_assembly_stores++;
- have_and_want_debug_symbols = register_debug_symbols;
+ log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size);
+ verify_assembly_store_and_set_info (payload_start, entry_name.get ());
}
[[gnu::always_inline]] void
-EmbeddedAssemblies::zip_load_assembly_store_entries (std::vector const& buf, uint32_t num_entries, ZipEntryLoadState &state) noexcept
+EmbeddedAssemblies::zip_load_assembly_store_entries (std::span const& buf, uint32_t num_entries, ZipEntryLoadState &state) noexcept
{
if (all_required_zip_entries_found ()) {
return;
}
dynamic_local_string entry_name;
- bool assembly_store_found = false;
+ bool assembly_store_found = embedded_assembly_store_size != 0;
+ if (assembly_store_found) {
+ load_embedded_assembly_store ();
+ log_debug (LOG_ASSEMBLY, "Looking for DSOs in APK");
+ } else {
+ log_debug (LOG_ASSEMBLY, "Looking for assembly store ('{}') and DSOs in APK", assembly_store_file_path);
+ }
- log_debug (LOG_ASSEMBLY, "Looking for assembly stores in APK ('{}')", assembly_store_file_path.data ());
for (size_t i = 0uz; i < num_entries; i++) {
if (all_required_zip_entries_found ()) {
need_to_scan_more_apks = false;
@@ -315,6 +319,7 @@ EmbeddedAssemblies::zip_load_assembly_store_entries (std::vector const&
}
}
+[[gnu::flatten]]
void
EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unused]] monodroid_should_register should_register) noexcept
{
@@ -322,7 +327,7 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
uint32_t cd_size;
uint16_t cd_entries;
- if (!zip_read_cd_info (fd, cd_offset, cd_size, cd_entries)) {
+ if (!zip_read_cd_info (fd, cd_offset, cd_size, cd_entries)) [[unlikely]] {
Helpers::abort_application (
LOG_ASSEMBLY,
std::format (
@@ -331,13 +336,13 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
)
);
}
-#ifdef DEBUG
- log_info (LOG_ASSEMBLY, "Central directory offset: {}", cd_offset);
- log_info (LOG_ASSEMBLY, "Central directory size: {}", cd_size);
- log_info (LOG_ASSEMBLY, "Central directory entries: {}", cd_entries);
-#endif
+
+ log_debug (LOG_ASSEMBLY, "Central directory offset: {}", cd_offset);
+ log_debug (LOG_ASSEMBLY, "Central directory size: {}", cd_size);
+ log_debug (LOG_ASSEMBLY, "Central directory entries: {}", cd_entries);
+
off_t retval = ::lseek (fd, static_cast(cd_offset), SEEK_SET);
- if (retval < 0) {
+ if (retval < 0) [[unlikely]] {
Helpers::abort_application (
LOG_ASSEMBLY,
std::format (
@@ -350,7 +355,6 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
);
}
- std::vector buf (cd_size);
const auto [prefix, prefix_len] = get_assemblies_prefix_and_length ();
ZipEntryLoadState state {
.file_fd = fd,
@@ -367,8 +371,10 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
.max_assembly_file_name_size = 0u,
};
+ std::unique_ptr raw_data (new uint8_t[cd_size]);
+ std::span buf (raw_data.get (), cd_size);
ssize_t nread = read (fd, buf.data (), buf.size ());
- if (static_cast(nread) != cd_size) {
+ if (static_cast(nread) != cd_size) [[unlikely]] {
Helpers::abort_application (
LOG_ASSEMBLY,
std::format (
@@ -647,7 +653,7 @@ EmbeddedAssemblies::zip_read_field (T const& buf, size_t index, size_t count, dy
}
bool
-EmbeddedAssemblies::zip_read_entry_info (std::vector const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state) noexcept
+EmbeddedAssemblies::zip_read_entry_info (std::span const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state) noexcept
{
constexpr size_t CD_COMPRESSION_METHOD_OFFSET = 10uz;
constexpr size_t CD_UNCOMPRESSED_SIZE_OFFSET = 24uz;
diff --git a/src/native/mono/monodroid/embedded-assemblies.cc b/src/native/mono/monodroid/embedded-assemblies.cc
index c35324f408b..73bb98e4751 100644
--- a/src/native/mono/monodroid/embedded-assemblies.cc
+++ b/src/native/mono/monodroid/embedded-assemblies.cc
@@ -910,51 +910,6 @@ EmbeddedAssemblies::typemap_managed_to_java (MonoReflectionType *reflection_type
return ret;
}
-EmbeddedAssemblies::md_mmap_info
-EmbeddedAssemblies::md_mmap_apk_file (int fd, uint32_t offset, size_t size, const char* filename)
-{
- md_mmap_info file_info;
- md_mmap_info mmap_info;
-
- size_t pageSize = static_cast(Util::monodroid_getpagesize ());
- size_t offsetFromPage = offset % pageSize;
- size_t offsetPage = offset - offsetFromPage;
- size_t offsetSize = size + offsetFromPage;
-
- mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast(offsetPage));
-
- if (mmap_info.area == MAP_FAILED) {
- Helpers::abort_application (
- LOG_ASSEMBLY,
- std::format (
- "Could not mmap APK fd {}: {}; File={}",
- fd,
- strerror (errno),
- optional_string (filename)
- )
- );
- }
-
- mmap_info.size = offsetSize;
- file_info.area = pointer_add (mmap_info.area, offsetFromPage);
- file_info.size = size;
-
- log_info (
- LOG_ASSEMBLY,
- " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}",
- mmap_info.area,
- pointer_add (mmap_info.area, mmap_info.size),
- mmap_info.size,
- file_info.area,
- pointer_add (file_info.area, file_info.size),
- file_info.size,
- fd,
- optional_string (filename)
- );
-
- return file_info;
-}
-
void
EmbeddedAssemblies::gather_bundled_assemblies_from_apk (const char* apk, monodroid_should_register should_register) noexcept
{
@@ -1450,13 +1405,21 @@ EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look
size_t
EmbeddedAssemblies::register_from_filesystem (monodroid_should_register should_register) noexcept
{
- log_debug (LOG_ASSEMBLY, "Registering assemblies from the filesystem"sv);
- constexpr bool LookForMangledNames = true;
- size_t assembly_count = register_from_filesystem (
- AndroidSystem::app_lib_directories[0],
- LookForMangledNames,
- should_register
- );
+ size_t assembly_count;
+
+ if (embedded_assembly_store_size > 0) {
+ log_debug (LOG_ASSEMBLY, "Filesystem mode, but registering assemblies from the embedded assembly store");
+ load_embedded_assembly_store ();
+ assembly_count = assembly_store.assembly_count;
+ } else {
+ log_debug (LOG_ASSEMBLY, "Registering assemblies from the filesystem");
+ constexpr bool LookForMangledNames = true;
+ assembly_count = register_from_filesystem (
+ AndroidSystem::app_lib_directories[0],
+ LookForMangledNames,
+ should_register
+ );
+ }
#if defined(DEBUG)
constexpr bool DoNotLookForMangledNames = false;
diff --git a/src/native/mono/monodroid/embedded-assemblies.hh b/src/native/mono/monodroid/embedded-assemblies.hh
index f3447a69840..da3a86328f6 100644
--- a/src/native/mono/monodroid/embedded-assemblies.hh
+++ b/src/native/mono/monodroid/embedded-assemblies.hh
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -193,11 +194,6 @@ namespace xamarin::android::internal {
runtime_config_data_size = 0uz;
}
- static bool have_runtime_config_blob () noexcept
- {
- return application_config.have_runtime_config_blob && runtime_config_blob_mmap.area != nullptr;
- }
-
static bool keep_scanning () noexcept
{
return need_to_scan_more_apks;
@@ -258,7 +254,57 @@ namespace xamarin::android::internal {
static const TypeMapEntry *typemap_managed_to_java (const char *managed_type_name) noexcept;
#endif // DEBUG
- static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, size_t size, const char* filename);
+ [[gnu::always_inline]]
+ static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, size_t size, const char* filename, md_mmap_info &original_info, md_mmap_info &adjusted_info) noexcept
+ {
+ size_t pageSize = static_cast(Util::monodroid_getpagesize ());
+ size_t offsetFromPage = offset % pageSize;
+ size_t offsetPage = offset - offsetFromPage;
+ size_t offsetSize = size + offsetFromPage;
+
+ original_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast(offsetPage));
+
+ if (original_info.area == MAP_FAILED) {
+ Helpers::abort_application (
+ LOG_ASSEMBLY,
+ Util::monodroid_strdup_printf (
+ "Could not mmap APK fd %d: %s; File=%s",
+ fd,
+ strerror (errno),
+ filename
+ )
+ );
+ }
+
+ original_info.size = offsetSize;
+ adjusted_info.area = (void*)((const char*)original_info.area + offsetFromPage);
+ adjusted_info.size = size;
+
+ log_info (
+ LOG_ASSEMBLY,
+ " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}",
+ original_info.area,
+ pointer_add (original_info.area, original_info.size),
+ original_info.size,
+ adjusted_info.area,
+ pointer_add (adjusted_info.area, adjusted_info.size),
+ adjusted_info.size,
+ fd,
+ optional_string (filename)
+ );
+
+ return adjusted_info;
+ }
+
+ [[gnu::flatten, gnu::always_inline]]
+ static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, size_t size, const char* filename) noexcept
+ {
+ md_mmap_info file_info;
+ md_mmap_info mmap_info;
+
+ return md_mmap_apk_file (fd, offset, size, filename, mmap_info, file_info);
+ }
+
static MonoAssembly* open_from_bundles_full (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
@@ -268,9 +314,9 @@ namespace xamarin::android::internal {
static void get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData const& e, uint8_t*& assembly_data, uint32_t& assembly_data_size) noexcept;
static void zip_load_entries (int fd, const char *apk_name, monodroid_should_register should_register) noexcept;
- static void zip_load_individual_assembly_entries (std::vector const& buf, uint32_t num_entries, monodroid_should_register should_register, ZipEntryLoadState &state) noexcept;
- static void zip_load_assembly_store_entries (std::vector const& buf, uint32_t num_entries, ZipEntryLoadState &state) noexcept;
- static bool zip_load_entry_common (size_t entry_index, std::vector const& buf, dynamic_local_string &entry_name, ZipEntryLoadState &state) noexcept;
+ static void zip_load_individual_assembly_entries (std::span const& buf, uint32_t num_entries, monodroid_should_register should_register, ZipEntryLoadState &state) noexcept;
+ static void zip_load_assembly_store_entries (std::span const& buf, uint32_t num_entries, ZipEntryLoadState &state) noexcept;
+ static bool zip_load_entry_common (size_t entry_index, std::span const& buf, dynamic_local_string &entry_name, ZipEntryLoadState &state) noexcept;
static bool zip_read_cd_info (int fd, uint32_t& cd_offset, uint32_t& cd_size, uint16_t& cd_entries) noexcept;
static bool zip_adjust_data_offset (int fd, ZipEntryLoadState &state) noexcept;
@@ -292,7 +338,7 @@ namespace xamarin::android::internal {
template
static bool zip_read_field (T const& buf, size_t index, size_t count, dynamic_local_string& characters) noexcept;
- static bool zip_read_entry_info (std::vector const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state) noexcept;
+ static bool zip_read_entry_info (std::span const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state) noexcept;
[[gnu::always_inline]]
static std::tuple get_wrapper_dso_payload_pointer_and_size (md_mmap_info const& map_info, const char *file_name) noexcept
@@ -351,8 +397,7 @@ namespace xamarin::android::internal {
static bool all_required_zip_entries_found () noexcept
{
return
- number_of_mapped_assembly_stores == number_of_assembly_store_files && number_of_zip_dso_entries >= application_config.number_of_shared_libraries
- && ((application_config.have_runtime_config_blob && runtime_config_blob_found) || !application_config.have_runtime_config_blob);
+ number_of_mapped_assembly_stores == number_of_assembly_store_files && number_of_zip_dso_entries >= application_config.number_of_shared_libraries;
}
[[gnu::always_inline]] static c_unique_ptr to_utf8 (const MonoString *s) noexcept
@@ -378,6 +423,15 @@ namespace xamarin::android::internal {
static void set_entry_data (XamarinAndroidBundledAssembly &entry, ZipEntryLoadState const& state, dynamic_local_string const& entry_name) noexcept;
static void set_assembly_entry_data (XamarinAndroidBundledAssembly &entry, ZipEntryLoadState const& state, dynamic_local_string const& entry_name) noexcept;
static void set_debug_entry_data (XamarinAndroidBundledAssembly &entry, ZipEntryLoadState const& state, dynamic_local_string const& entry_name) noexcept;
+
+ static void verify_assembly_store_and_set_info (void *data_start, const char *name) noexcept;
+
+ static void load_embedded_assembly_store () noexcept
+ {
+ log_debug (LOG_ASSEMBLY, "Loading embedded assembly store");
+ verify_assembly_store_and_set_info (embedded_assembly_store, "embedded");
+ }
+
static void map_assembly_store (dynamic_local_string const& entry_name, ZipEntryLoadState &state) noexcept;
static const AssemblyStoreIndexEntry* find_assembly_store_entry (hash_t hash, const AssemblyStoreIndexEntry *entries, size_t entry_count) noexcept;
static void store_individual_assembly_data (dynamic_local_string const& entry_name, ZipEntryLoadState const& state, monodroid_should_register should_register) noexcept;
@@ -458,11 +512,10 @@ namespace xamarin::android::internal {
size_t type_map_count;
#endif // DEBUG
static inline const char *assemblies_prefix_override = nullptr;
-
static inline md_mmap_info runtime_config_blob_mmap{};
static inline void *runtime_config_data = nullptr;
static inline size_t runtime_config_data_size = 0uz;
- static inline bool runtime_config_blob_found = false;
+ static inline bool runtime_config_blob_found = embedded_runtime_config_size > 0u;
static inline uint32_t number_of_mapped_assembly_stores = 0u;
static inline uint32_t number_of_zip_dso_entries = 0u;
static inline bool need_to_scan_more_apks = true;
diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc
index d8c722f48f2..954661663a2 100644
--- a/src/native/mono/monodroid/monodroid-glue.cc
+++ b/src/native/mono/monodroid/monodroid-glue.cc
@@ -728,13 +728,15 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks
gather_bundled_assemblies (runtimeApks, &user_assemblies_count, have_split_apks);
- if (EmbeddedAssemblies::have_runtime_config_blob ()) {
+ if (embedded_runtime_config_size > 0) {
if (FastTiming::enabled ()) [[unlikely]] {
internal_timing.start_event (TimingEventKind::RuntimeConfigBlob);
}
runtime_config_args.kind = 1;
- EmbeddedAssemblies::get_runtime_config_blob (runtime_config_args.runtimeconfig.data.data, runtime_config_args.runtimeconfig.data.data_len);
+ runtime_config_args.runtimeconfig.data.data = reinterpret_cast(embedded_runtime_config);
+ runtime_config_args.runtimeconfig.data.data_len = static_cast(embedded_runtime_config_size);
+
monovm_runtimeconfig_initialize (&runtime_config_args, cleanup_runtime_config, nullptr);
if (FastTiming::enabled ()) [[unlikely]] {
@@ -1155,7 +1157,8 @@ MonodroidRuntime::set_profile_options () noexcept
.append (OUTPUT_ARG)
.append (output_path.get (), output_path.length ());
}
- if (Util::create_directory (AndroidSystem::override_dirs[0], 0) < 0) {
+
+ if (Util::create_directory (AndroidSystem::override_dirs[0], 0777, 000) < 0) {
log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}", optional_string (AndroidSystem::override_dirs[0]), std::strerror (errno));
}
diff --git a/src/native/mono/runtime-base/android-system.cc b/src/native/mono/runtime-base/android-system.cc
index 0fcd825bead..62c358f3816 100644
--- a/src/native/mono/runtime-base/android-system.cc
+++ b/src/native/mono/runtime-base/android-system.cc
@@ -262,26 +262,14 @@ AndroidSystem::monodroid_get_system_property_from_overrides ([[maybe_unused]] co
return 0;
}
-// TODO: review this. Do we really have to create the dir in release?
void
AndroidSystem::create_update_dir (char *override_dir) noexcept
{
-#if defined (RELEASE)
- /*
- * Don't create .__override__ on Release builds, because Google requires
- * that pre-loaded apps not create world-writable directories.
- *
- * However, if any logging is enabled (which should _not_ happen with
- * pre-loaded apps!), we need the .__override__ directory...
- */
- if (log_categories == 0 && monodroid_get_system_property (SharedConstants::DEBUG_MONO_PROFILE_PROPERTY, nullptr) == 0) {
- return;
- }
-#endif // def RELEASE
-
override_dirs [0] = override_dir;
+#if defined(DEBUG)
+ log_debug (LOG_DEFAULT, "Creating public update directory: `%s`", override_dir);
Util::create_public_directory (override_dir);
- log_warn (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir);
+#endif
}
bool
diff --git a/src/native/mono/runtime-base/util.cc b/src/native/mono/runtime-base/util.cc
index fda41ecfd88..a5159f1f066 100644
--- a/src/native/mono/runtime-base/util.cc
+++ b/src/native/mono/runtime-base/util.cc
@@ -147,14 +147,14 @@ Util::path_combine (const char *path1, const char *path2)
void
Util::create_public_directory (const char *dir)
{
- int ret = create_directory (dir, 0777);
+ int ret = create_directory (dir, 0777, 0);
if (ret < 0) {
log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}", dir, std::strerror (errno));
}
}
int
-Util::create_directory (const char *pathname, mode_t mode)
+Util::create_directory (const char *pathname, mode_t mode, mode_t mask)
{
if (mode <= 0)
mode = DEFAULT_DIRECTORY_MODE;
@@ -163,7 +163,7 @@ Util::create_directory (const char *pathname, mode_t mode)
errno = EINVAL;
return -1;
}
- mode_t oldumask = umask (022);
+ mode_t oldumask = umask (mask);
std::unique_ptr path {strdup_new (pathname)};
int rv, ret = 0;
for (char *d = path.get (); d != nullptr && *d; ++d) {
diff --git a/src/native/mono/runtime-base/util.hh b/src/native/mono/runtime-base/util.hh
index a43009478ae..3bfd897658f 100644
--- a/src/native/mono/runtime-base/util.hh
+++ b/src/native/mono/runtime-base/util.hh
@@ -69,7 +69,7 @@ namespace xamarin::android
static char *monodroid_strdup_vprintf (const char *format, va_list vargs);
static char* path_combine (const char *path1, const char *path2);
static void create_public_directory (const char *dir);
- static int create_directory (const char *pathname, mode_t mode);
+ static int create_directory (const char *pathname, mode_t mode, mode_t mask = 022);
static void set_world_accessable (const char *path);
static auto set_world_accessible (int fd) noexcept -> bool;
static void set_user_executable (const char *path);
diff --git a/src/native/mono/xamarin-app-stub/application_dso_stub.cc b/src/native/mono/xamarin-app-stub/application_dso_stub.cc
index 0316a55408b..d60e11c82c9 100644
--- a/src/native/mono/xamarin-app-stub/application_dso_stub.cc
+++ b/src/native/mono/xamarin-app-stub/application_dso_stub.cc
@@ -48,7 +48,6 @@ const ApplicationConfig application_config = {
.uses_assembly_preload = false,
.broken_exception_transitions = false,
.jni_add_native_method_registration_attribute_present = false,
- .have_runtime_config_blob = false,
.have_assembly_store = false,
.marshal_methods_enabled = false,
.ignore_split_configs = false,
@@ -305,3 +304,9 @@ const JniRemappingTypeReplacementEntry jni_remapping_type_replacements[] = {
.replacement = "another/replacement/java/type",
},
};
+
+size_t embedded_runtime_config_size = 0;
+uint8_t embedded_runtime_config[0];
+
+size_t embedded_assembly_store_size = 0;
+uint8_t embedded_assembly_store[0];
diff --git a/src/native/mono/xamarin-app-stub/xamarin-app.hh b/src/native/mono/xamarin-app-stub/xamarin-app.hh
index 5b058a99d2d..d8badede691 100644
--- a/src/native/mono/xamarin-app-stub/xamarin-app.hh
+++ b/src/native/mono/xamarin-app-stub/xamarin-app.hh
@@ -238,7 +238,6 @@ struct ApplicationConfig
bool uses_assembly_preload;
bool broken_exception_transitions;
bool jni_add_native_method_registration_attribute_present;
- bool have_runtime_config_blob;
bool have_assembly_store;
bool marshal_methods_enabled;
bool ignore_split_configs;
@@ -394,6 +393,12 @@ MONO_API MONO_API_EXPORT const MarshalMethodName mm_method_names[];
#endif // def RELEASE
+MONO_API MONO_API_EXPORT size_t embedded_runtime_config_size;
+MONO_API MONO_API_EXPORT uint8_t embedded_runtime_config[];
+
+MONO_API MONO_API_EXPORT size_t embedded_assembly_store_size;
+MONO_API MONO_API_EXPORT uint8_t embedded_assembly_store[];
+
using get_function_pointer_fn = void(*)(uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr);
MONO_API MONO_API_EXPORT void xamarin_app_init (JNIEnv *env, get_function_pointer_fn fn) noexcept;
diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
index d179740e994..92593ce63c1 100644
--- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
+++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
@@ -1380,5 +1380,26 @@ public void AppStartsWithManagedMarshalMethodsLookupEnabled ()
Path.Combine (Root, builder.ProjectDirectory, "logcat.log"), 30);
Assert.IsTrue (didLaunch, "Activity should have started.");
}
+
+ [Test]
+ [TestCase (false)]
+ [TestCase (true)]
+ public void AppStartsWithEmbeddedAssemblyStore (bool useCLR)
+ {
+ var proj = new XamarinAndroidApplicationProject { IsRelease = true };
+ proj.SetProperty ("_AndroidEmbedAssemblyStoreInRuntime", "true");
+ proj.SetProperty ("UseMonoRuntime", useCLR ? "false" : "true");
+
+ using var builder = CreateApkBuilder ();
+ builder.Save (proj);
+
+ var dotnet = new DotNetCLI (Path.Combine (Root, builder.ProjectDirectory, proj.ProjectFilePath));
+ Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
+ Assert.IsTrue (dotnet.Run (), "`dotnet run --no-build` should succeed");
+
+ bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity",
+ Path.Combine (Root, builder.ProjectDirectory, "logcat.log"), 30);
+ Assert.IsTrue (didLaunch, "Activity should have started.");
+ }
}
}
diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs b/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs
index d6a5a638302..aef64a09d3d 100644
--- a/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs
+++ b/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs
@@ -140,7 +140,19 @@ public static (IList? explorers, string? errorMessage) Op
ZipEntry entry = zip.ReadEntry (path);
var stream = new MemoryStream ();
entry.Extract (stream);
- ret.Add (new AssemblyStoreExplorer (stream, $"{fi.FullName}!{path}"));
+ AssemblyStoreExplorer? explorer = null;
+ try {
+ // It may throw when opening an apk without any assembly stores, in which case the v2 store reader would
+ // always find `libxamarin-app.so` and try to read the embedded store and fail, throwing from the explorer
+ // constructor.
+ explorer = new AssemblyStoreExplorer (stream, $"{fi.FullName}!{path}");
+ } catch (NotSupportedException) {
+ // Ignore
+ }
+
+ if (explorer != null) {
+ ret.Add (explorer);
+ }
}
if (ret.Count == 0) {
diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs b/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs
index 932f151c80f..78372c26853 100644
--- a/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs
+++ b/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs
@@ -8,4 +8,5 @@ enum ELFPayloadError
NotSharedLibrary,
NotLittleEndian,
NoPayloadSection,
+ NoEmbeddedStore,
}
diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
index a43960102e2..36dad0c4c45 100644
--- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
+++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
@@ -40,6 +40,11 @@ static StoreReader_V2 ()
GetArchPath (AndroidTargetArch.Arm),
GetArchPath (AndroidTargetArch.X86_64),
GetArchPath (AndroidTargetArch.X86),
+
+ GetArchPath (AndroidTargetArch.Arm64, embeddedBlob: true),
+ GetArchPath (AndroidTargetArch.Arm, embeddedBlob: true),
+ GetArchPath (AndroidTargetArch.X86_64, embeddedBlob: true),
+ GetArchPath (AndroidTargetArch.X86, embeddedBlob: true),
};
ApkPaths = paths.AsReadOnly ();
AabBasePaths = ApkPaths;
@@ -50,10 +55,15 @@ static StoreReader_V2 ()
GetArchPath (AndroidTargetArch.Arm, AabBaseDir),
GetArchPath (AndroidTargetArch.X86_64, AabBaseDir),
GetArchPath (AndroidTargetArch.X86, AabBaseDir),
+
+ GetArchPath (AndroidTargetArch.Arm64, AabBaseDir, embeddedBlob: true),
+ GetArchPath (AndroidTargetArch.Arm, AabBaseDir, embeddedBlob: true),
+ GetArchPath (AndroidTargetArch.X86_64, AabBaseDir, embeddedBlob: true),
+ GetArchPath (AndroidTargetArch.X86, AabBaseDir, embeddedBlob: true),
};
AabPaths = paths.AsReadOnly ();
- string GetArchPath (AndroidTargetArch arch, string? root = null)
+ string GetArchPath (AndroidTargetArch arch, string? root = null, bool embeddedBlob = false)
{
const string LibDirName = "lib";
@@ -65,7 +75,7 @@ string GetArchPath (AndroidTargetArch arch, string? root = null)
root = LibDirName;
}
parts.Add (abi);
- parts.Add (GetBlobName (abi));
+ parts.Add (GetBlobName (abi, embeddedBlob));
return MonoAndroidHelper.MakeZipArchivePath (root, parts);
}
@@ -82,10 +92,23 @@ public StoreReader_V2 (Stream store, string path)
};
}
- static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so";
+ static string GetBlobName (string abi, bool embeddedBlob) => embeddedBlob ? "libxamarin-app.so" : $"libassemblies.{abi}.blob.so";
protected override ulong GetStoreStartDataOffset () => elfOffset;
+ string PayloadErrorToString (ELFPayloadError error)
+ {
+ return error switch {
+ ELFPayloadError.NotELF => $"Store '{StorePath}' is not a valid ELF binary",
+ ELFPayloadError.LoadFailed => $"Store '{StorePath}' could not be loaded",
+ ELFPayloadError.NotSharedLibrary => $"Store '{StorePath}' is not a shared ELF library",
+ ELFPayloadError.NotLittleEndian => $"Store '{StorePath}' is not a little-endian ELF image",
+ ELFPayloadError.NoPayloadSection => $"Store '{StorePath}' does not contain the 'payload' section",
+ ELFPayloadError.NoEmbeddedStore => $"Store '{StorePath}' does not contain embedded data blob",
+ _ => $"Unknown ELF payload section error for store '{StorePath}': {error}"
+ };
+ }
+
protected override bool IsSupported ()
{
StoreStream.Seek (0, SeekOrigin.Begin);
@@ -94,21 +117,24 @@ protected override bool IsSupported ()
uint magic = reader.ReadUInt32 ();
if (magic == Utils.ELF_MAGIC) {
ELFPayloadError error;
- (elfOffset, _, error) = Utils.FindELFPayloadSectionOffsetAndSize (StoreStream);
-
+ (elfOffset, _, error) = Utils.FindEmbeddedStoreOffsetAndSize (StoreStream);
if (error != ELFPayloadError.None) {
- string message = error switch {
- ELFPayloadError.NotELF => $"Store '{StorePath}' is not a valid ELF binary",
- ELFPayloadError.LoadFailed => $"Store '{StorePath}' could not be loaded",
- ELFPayloadError.NotSharedLibrary => $"Store '{StorePath}' is not a shared ELF library",
- ELFPayloadError.NotLittleEndian => $"Store '{StorePath}' is not a little-endian ELF image",
- ELFPayloadError.NoPayloadSection => $"Store '{StorePath}' does not contain the 'payload' section",
- _ => $"Unknown ELF payload section error for store '{StorePath}': {error}"
- };
- Log.Debug (message);
- } else if (elfOffset >= 0) {
+ MaybeLogError (error);
+
+ (elfOffset, _, error) = Utils.FindELFPayloadSectionOffsetAndSize (StoreStream);
+ if (error != ELFPayloadError.None) {
+ MaybeLogError (error);
+ return false;
+ }
+ }
+
+ // elfOffset cannot be 0, since we have ELF magic and headers (at least) at the beginning of
+ // the file.
+ if (elfOffset > 0) {
StoreStream.Seek ((long)elfOffset, SeekOrigin.Begin);
magic = reader.ReadUInt32 ();
+ } else {
+ return false;
}
}
@@ -129,6 +155,15 @@ protected override bool IsSupported ()
header = new Header (magic, version, entry_count, index_entry_count, index_size);
return true;
+
+ void MaybeLogError (ELFPayloadError error)
+ {
+ if (error == ELFPayloadError.None) {
+ return;
+ }
+
+ Log.Debug (PayloadErrorToString (error));
+ }
}
protected override void Prepare ()
diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs b/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs
index 284f501c9ac..2e2f03e5e52 100644
--- a/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs
+++ b/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs
@@ -4,6 +4,7 @@
using ELFSharp.ELF;
using ELFSharp.ELF.Sections;
+using Xamarin.Android.Tasks;
using Xamarin.Tools.Zip;
namespace Xamarin.Android.AssemblyStore;
@@ -29,31 +30,28 @@ static class Utils
public static readonly ArrayPool BytePool = ArrayPool.Shared;
- public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize (Stream stream)
+ static (bool is64, IELF? elf, ELFPayloadError error) TryOpenELF (Stream stream)
{
+ bool is64 = false;
stream.Seek (0, SeekOrigin.Begin);
Class elfClass = ELFReader.CheckELFType (stream);
if (elfClass == Class.NotELF) {
- return ReturnError (null, ELFPayloadError.NotELF);
+ return ReturnError (is64, null, ELFPayloadError.NotELF);
}
if (!ELFReader.TryLoad (stream, shouldOwnStream: false, out IELF? elf)) {
- return ReturnError (elf, ELFPayloadError.LoadFailed);
+ return ReturnError (is64, elf, ELFPayloadError.LoadFailed);
}
if (elf.Type != FileType.SharedObject) {
- return ReturnError (elf, ELFPayloadError.NotSharedLibrary);
+ return ReturnError (is64, elf, ELFPayloadError.NotSharedLibrary);
}
if (elf.Endianess != ELFSharp.Endianess.LittleEndian) {
- return ReturnError (elf, ELFPayloadError.NotLittleEndian);
- }
-
- if (!elf.TryGetSection ("payload", out ISection? payloadSection)) {
- return ReturnError (elf, ELFPayloadError.NoPayloadSection);
+ return ReturnError (is64, elf, ELFPayloadError.NotLittleEndian);
}
- bool is64 = elf.Machine switch {
+ is64 = elf.Machine switch {
Machine.ARM => false,
Machine.Intel386 => false,
@@ -63,6 +61,51 @@ public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSe
_ => throw new NotSupportedException ($"Unsupported ELF architecture '{elf.Machine}'")
};
+ return (is64, elf, ELFPayloadError.None);
+
+ (bool is64, IELF? elf, ELFPayloadError error) ReturnError (bool is64, IELF? elf, ELFPayloadError error)
+ {
+ elf?.Dispose ();
+
+ return (is64, null, error);
+ }
+ }
+
+ public static (ulong offset, ulong size, ELFPayloadError error) FindEmbeddedStoreOffsetAndSize (Stream stream)
+ {
+ (bool is64, IELF? elf, ELFPayloadError error) = TryOpenELF (stream);
+ if (elf == null || error != ELFPayloadError.None) {
+ return ReturnError (elf, error);
+ }
+
+ const string SymbolName = "embedded_assembly_store";
+ ISymbolTable? dynsym = ELFHelper.GetSymbolTable (elf, ".dynsym");
+ if (is64) {
+ SymbolEntry? symbol = ELFHelper.FindSymbol (dynsym, SymbolName);
+ if (symbol != null) {
+ return (symbol.Value, symbol.Size, ELFPayloadError.None);
+ }
+ } else {
+ SymbolEntry? symbol = ELFHelper.FindSymbol (dynsym, SymbolName);
+ if (symbol != null) {
+ return ((ulong)symbol.Value, (ulong)symbol.Size, ELFPayloadError.None);
+ }
+ }
+
+ return (0, 0, ELFPayloadError.NoEmbeddedStore);
+ }
+
+ public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize (Stream stream)
+ {
+ (bool is64, IELF? elf, ELFPayloadError error) = TryOpenELF (stream);
+ if (elf == null || error != ELFPayloadError.None) {
+ return ReturnError (elf, error);
+ }
+
+ if (!elf.TryGetSection ("payload", out ISection? payloadSection)) {
+ return ReturnError (elf, ELFPayloadError.NoPayloadSection);
+ }
+
ulong offset;
ulong size;
@@ -84,13 +127,13 @@ public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSe
{
return ((ulong)payload.Offset, (ulong)payload.Size);
}
+ }
- (ulong offset, ulong size, ELFPayloadError error) ReturnError (IELF? elf, ELFPayloadError error)
- {
- elf?.Dispose ();
+ static (ulong offset, ulong size, ELFPayloadError error) ReturnError (IELF? elf, ELFPayloadError error)
+ {
+ elf?.Dispose ();
- return (0, 0, error);
- }
+ return (0, 0, error);
}
public static (FileFormat format, FileInfo? info) DetectFileFormat (string path)
diff --git a/tools/assembly-store-reader-mk2/assembly-store-reader.csproj b/tools/assembly-store-reader-mk2/assembly-store-reader.csproj
index ec4c90fecb1..3bd2852d04e 100644
--- a/tools/assembly-store-reader-mk2/assembly-store-reader.csproj
+++ b/tools/assembly-store-reader-mk2/assembly-store-reader.csproj
@@ -24,6 +24,7 @@
+