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 @@ + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy