Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@ sealed class Header

// Index size in bytes
public readonly uint index_size;
public readonly ulong content_id;

public uint NativeSize => (uint)(5 * sizeof (uint) + ((version & ASSEMBLY_STORE_FORMAT_NUMBER_MASK) >= 4 ? sizeof (ulong) : 0));
public const uint NativeSize = 5 * sizeof (uint);
Comment thread
simonrozsival marked this conversation as resolved.

public Header (uint magic, uint version, uint entry_count, uint index_entry_count, uint index_size, ulong content_id)
public Header (uint magic, uint version, uint entry_count, uint index_entry_count, uint index_size)
{
this.magic = magic;
this.version = version;
this.entry_count = entry_count;
this.index_entry_count = index_entry_count;
this.index_size = index_size;
this.content_id = content_id;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ partial class StoreReader_V2 : AssemblyStoreReader
const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT_V2 = 0x00000002;
const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT_V3 = 0x80000003; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant
const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT_V3 = 0x00000003;
const uint ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT_V4 = 0x80000004; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant
const uint ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT_V4 = 0x00000004;
const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000;
const uint ASSEMBLY_STORE_FORMAT_NUMBER_MASK = 0x0000FFFF;

const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000;
const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000;
Expand Down Expand Up @@ -90,10 +87,6 @@ public StoreReader_V2 (Stream store, string path)
ASSEMBLY_STORE_FORMAT_VERSION_64BIT_V3 | ASSEMBLY_STORE_ABI_X64,
ASSEMBLY_STORE_FORMAT_VERSION_32BIT_V3 | ASSEMBLY_STORE_ABI_ARM,
ASSEMBLY_STORE_FORMAT_VERSION_32BIT_V3 | ASSEMBLY_STORE_ABI_X86,
ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT_V4 | ASSEMBLY_STORE_ABI_AARCH64,
ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT_V4 | ASSEMBLY_STORE_ABI_X64,
ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT_V4 | ASSEMBLY_STORE_ABI_ARM,
ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT_V4 | ASSEMBLY_STORE_ABI_X86,
};
}

Expand Down Expand Up @@ -140,9 +133,8 @@ protected override bool IsSupported ()
uint entry_count = reader.ReadUInt32 ();
uint index_entry_count = reader.ReadUInt32 ();
uint index_size = reader.ReadUInt32 ();
ulong content_id = (version & ASSEMBLY_STORE_FORMAT_NUMBER_MASK) >= 4 ? reader.ReadUInt64 () : 0;

header = new Header (magic, version, entry_count, index_entry_count, index_size, content_id);
header = new Header (magic, version, entry_count, index_entry_count, index_size);
return true;
}

Expand All @@ -164,7 +156,7 @@ protected override void Prepare ()
AssemblyCount = header.entry_count;
IndexEntryCount = header.index_entry_count;

StoreStream.Seek ((long)elfOffset + header.NativeSize, SeekOrigin.Begin);
StoreStream.Seek ((long)elfOffset + Header.NativeSize, SeekOrigin.Begin);
using var reader = CreateReader ();

uint indexEntrySize = GetIndexEntrySize ();
Expand Down
6 changes: 0 additions & 6 deletions Documentation/building/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,6 @@ Overridable MSBuild properties include:
assemblies placed in the APK will be compressed in `Release` builds. `Debug`
builds are not affected.

* `$(AndroidEnableAssemblyStoreDecompressionCache)`: Defaults to `False`. When
enabled for a CoreCLR `Release` build, decompressed assemblies are cached in
the app's Android code-cache directory and mapped from there on subsequent
launches. The cache consumes additional on-device storage and is rebuilt
after app or platform updates.

## Options suitable for local development

### Native runtime (`src/native`)
Expand Down
8 changes: 2 additions & 6 deletions Documentation/project-docs/AssemblyStores.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,10 @@ and aligned to a byte boundary.
The header is a fixed-size structure at the beginning of each assembly store file:

- **MAGIC** (`uint32_t`) - Magic value `0x41424158` ("XABA" in little-endian)
- **FORMAT_VERSION** (`uint32_t`) - Store format version number (includes ABI and 64-bit flags). Version `3` is used by MonoVM applications and version `4` by CoreCLR applications (see [Hash table format](#hash-table-format))
- **FORMAT_VERSION** (`uint32_t`) - Store format version number (includes ABI and 64-bit flags). Version `3` is used by MonoVM and CoreCLR applications (see [Hash table format](#hash-table-format))
- **ENTRY_COUNT** (`uint32_t`) - Number of assemblies in the store
- **INDEX_ENTRY_COUNT** (`uint32_t`) - Number of entries in the index (typically `ENTRY_COUNT * 2`)
- **INDEX_SIZE** (`uint32_t`) - Index size in bytes
- **CONTENT_ID** (`uint64_t`) - Deterministic xxHash3 of everything after the header

## [INDEX]

Expand Down Expand Up @@ -169,7 +168,6 @@ All kinds of stores share the following header format:
uint32_t entry_count;
uint32_t index_entry_count;
uint32_t index_size; // index size in bytes
uint64_t content_id;
};

Individual fields have the following meanings:
Expand All @@ -181,7 +179,6 @@ Individual fields have the following meanings:
table, see below)
- `index_entry_count`: number of entries in the index
- `index_size`: index size in bytes
- `content_id`: deterministic xxHash3 of the index, descriptors, names, and assembly data

## Assembly descriptor table

Expand Down Expand Up @@ -236,7 +233,7 @@ appending it in order to generate the hash for index lookup.

The hashing algorithm depends on the runtime the application targets:

- **CoreCLR** (store format version `4`): the hash is a 32-bit
- **CoreCLR** (store format version `3`): the hash is a 32-bit
Comment on lines -239 to +236

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we increment to version 5, or are we sure it was the same as before?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same it was before. It was disabled so nobody used it anyway. I would treat v4 as never shipped.

[CRC32](https://en.wikipedia.org/wiki/Cyclic_redundancy_check)
value, used on both 32-bit and 64-bit platforms.
- **MonoVM** (store format version `3`): the hash is obtained using the
Expand Down Expand Up @@ -287,7 +284,6 @@ struct [[gnu::packed]] AssemblyStoreHeader final
uint32_t entry_count;
uint32_t index_entry_count;
uint32_t index_size; // index size in bytes
uint64_t content_id;
};
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public class GenerateNativeApplicationConfigSources : AndroidTask
/// </summary>
public bool EmitLlvmIrComments { get; set; }

public bool AndroidEnableAssemblyStoreDecompressionCache { get; set; }
public string? RuntimeConfigBinFilePath { get; set; }
public string ProjectRuntimeConfigFilePath { get; set; } = String.Empty;
public string? ProjectRuntimeConfigDevFilePath { get; set; }
Expand Down Expand Up @@ -294,7 +293,6 @@ static bool ShouldSkipAssembly (ITaskItem assembly)
MarshalMethodsEnabled = EnableMarshalMethods,
IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (),
HaveAssemblyStore = UseAssemblyStore,
AssemblyStoreDecompressionCacheEnabled = AndroidEnableAssemblyStoreDecompressionCache,
};
} else {
appConfigAsmGen = new ApplicationConfigNativeAssemblyGenerator (envBuilder.EnvironmentVariables, envBuilder.SystemProperties, Log) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Hashing;
using System.Linq;

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NUnit.Framework;
using Xamarin.Android.Tasks;
Expand All @@ -16,14 +13,13 @@ namespace Xamarin.Android.Build.Tests.Tasks;
public class CreateAssemblyStoreTests : BaseTest
{
[Test]
public void ContentIdMatchesStoreContents ()
public void CoreCLRStoreUsesVersionThreeHeader ()
{
string testDirectory = Path.Combine (Root, "temp", nameof (ContentIdMatchesStoreContents));
string testDirectory = Path.Combine (Root, "temp", nameof (CoreCLRStoreUsesVersionThreeHeader));
Directory.CreateDirectory (testDirectory);

string assemblyPath = Path.Combine (testDirectory, "Example.dll.zst");
byte [] assemblyData = [1, 3, 3, 7, 9, 11, 17, 23];
File.WriteAllBytes (assemblyPath, assemblyData);
File.WriteAllBytes (assemblyPath, [1, 3, 3, 7, 9, 11, 17, 23]);

var metadata = new Dictionary<string, string> {
["Abi"] = "arm64-v8a",
Expand All @@ -41,17 +37,16 @@ public void ContentIdMatchesStoreContents ()
Assert.IsTrue (task.Execute (), "CreateAssemblyStore should succeed.");

string storePath = task.AssembliesToAddToArchive.Single ().ItemSpec;
byte [] store = File.ReadAllBytes (storePath);
using var reader = new BinaryReader (new MemoryStream (store));
using var reader = new BinaryReader (File.OpenRead (storePath));
Assert.AreEqual (0x41424158u, reader.ReadUInt32 (), "Unexpected assembly store magic.");
Assert.AreEqual (0x80010004u, reader.ReadUInt32 (), "Unexpected arm64 assembly store version.");
reader.BaseStream.Seek (3 * sizeof (uint), SeekOrigin.Current);
ulong contentId = reader.ReadUInt64 ();

Assert.AreEqual (
XxHash3.HashToUInt64 (store.AsSpan (5 * sizeof (uint) + sizeof (ulong))),
contentId,
"The content ID should hash everything after the assembly store header."
);
Assert.AreEqual (0x80010003u, reader.ReadUInt32 (), "Unexpected arm64 assembly store version.");
uint assemblyCount = reader.ReadUInt32 ();
uint indexEntryCount = reader.ReadUInt32 ();
Assert.AreEqual (assemblyCount * 2, indexEntryCount, "Unexpected index entry count.");
uint indexSize = reader.ReadUInt32 ();
Assert.AreEqual (5 * sizeof (uint), reader.BaseStream.Position, "Unexpected assembly store header size.");

reader.BaseStream.Seek (indexSize, SeekOrigin.Current);
Assert.AreEqual (0u, reader.ReadUInt32 (), "The first descriptor should immediately follow the index.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,37 +42,4 @@ public void HaveAssemblyStoreIsEmittedForCoreCLR (bool haveAssemblyStore)
var config = (EnvironmentHelper.ApplicationConfig_CoreCLR)EnvironmentHelper.ReadApplicationConfig (environmentFiles, AndroidRuntime.CoreCLR);
Assert.AreEqual (haveAssemblyStore, config.have_assembly_store);
}

[TestCase (false)]
[TestCase (true)]
public void AssemblyStoreDecompressionCacheSettingIsEmitted (bool enabled)
{
string outputRoot = Path.Combine (Root, "temp", $"{nameof (AssemblyStoreDecompressionCacheSettingIsEmitted)}-{enabled}");
string monoAndroidPath = Path.Combine (TestEnvironment.MonoAndroidFrameworkDirectory, "Mono.Android.dll");
FileAssert.Exists (monoAndroidPath);

var task = new GenerateNativeApplicationConfigSources {
BuildEngine = new MockBuildEngine (TestContext.Out),
ResolvedAssemblies = [new TaskItem (monoAndroidPath)],
EnvironmentOutputDirectory = Path.Combine (outputRoot, "android"),
SupportedAbis = ["arm64-v8a"],
AndroidPackageName = "com.microsoft.android.cachetest",
EnablePreloadAssembliesDefault = false,
TargetsCLR = true,
AndroidRuntime = "CoreCLR",
UseAssemblyStore = true,
AndroidEnableAssemblyStoreDecompressionCache = enabled,
};

Assert.IsTrue (task.Execute (), "GenerateNativeApplicationConfigSources should succeed.");

var environmentFiles = EnvironmentHelper.GatherEnvironmentFiles (
outputRoot,
"arm64-v8a",
required: true,
runtime: AndroidRuntime.CoreCLR
);
var config = (EnvironmentHelper.ApplicationConfig_CoreCLR)EnvironmentHelper.ReadApplicationConfig (environmentFiles, AndroidRuntime.CoreCLR);
Assert.AreEqual (enabled, config.assembly_store_decompression_cache_enabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@ public sealed class ApplicationConfig_CoreCLR : IApplicationConfig
public uint jni_remapping_replacement_method_index_entry_count;
public string android_package_name = String.Empty;
public bool have_assembly_store;
public bool assembly_store_decompression_cache_enabled;
}

const uint ApplicationConfigFieldCount_CoreCLR = 20;
const uint ApplicationConfigFieldCount_CoreCLR = 19;

// This must be identical to the ApplicationConfig structure in src/native/mono/xamarin-app-stub/xamarin-app.hh
public sealed class ApplicationConfig_MonoVM : IApplicationConfig
Expand Down Expand Up @@ -402,10 +401,6 @@ static IApplicationConfig ReadApplicationConfig_CoreCLR (EnvironmentFile envFile
ret.have_assembly_store = ConvertFieldToBool ("have_assembly_store", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;

case 19: // assembly_store_decompression_cache_enabled: bool / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.assembly_store_decompression_cache_enabled = ConvertFieldToBool ("assembly_store_decompression_cache_enabled", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
}
fieldCount++;
}
Expand Down Expand Up @@ -766,7 +761,6 @@ static void AssertApplicationConfigIsIdentical (ApplicationConfig_CoreCLR firstA
Assert.AreEqual (firstAppConfig.system_property_count, secondAppConfig.system_property_count, $"Field 'system_property_count' has different value in environment file '{secondEnvFile}' than in environment file '{firstEnvFile}'");
Assert.AreEqual (firstAppConfig.android_package_name, secondAppConfig.android_package_name, $"Field 'android_package_name' has different value in environment file '{secondEnvFile}' than in environment file '{firstEnvFile}'");
Assert.AreEqual (firstAppConfig.have_assembly_store, secondAppConfig.have_assembly_store, $"Field 'have_assembly_store' has different value in environment file '{secondEnvFile}' than in environment file '{firstEnvFile}'");
Assert.AreEqual (firstAppConfig.assembly_store_decompression_cache_enabled, secondAppConfig.assembly_store_decompression_cache_enabled, $"Field 'assembly_store_decompression_cache_enabled' has different value in environment file '{secondEnvFile}' than in environment file '{firstEnvFile}'");
}

static void AssertApplicationConfigIsIdentical (ApplicationConfig_MonoVM firstAppConfig, string firstEnvFile, ApplicationConfig_MonoVM secondAppConfig, string secondEnvFile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,4 @@ sealed class ApplicationConfigCLR
public uint jni_remapping_replacement_method_index_entry_count;
public string android_package_name = String.Empty;
public bool have_assembly_store;
public bool assembly_store_decompression_cache_enabled;
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ sealed class DsoCacheState
public bool MarshalMethodsEnabled { get; set; }
public bool IgnoreSplitConfigs { get; set; }
public bool HaveAssemblyStore { get; set; }
public bool AssemblyStoreDecompressionCacheEnabled { get; set; }

public ApplicationConfigNativeAssemblyGeneratorCLR (IDictionary<string, string> environmentVariables, IDictionary<string, string> systemProperties,
IDictionary<string, string>? runtimeProperties, TaskLoggingHelper log)
Expand Down Expand Up @@ -287,7 +286,6 @@ protected override void Construct (LlvmIrModule module)
jni_remapping_replacement_method_index_entry_count = (uint)JniRemappingReplacementMethodIndexEntryCount,
android_package_name = AndroidPackageName,
have_assembly_store = HaveAssemblyStore,
assembly_store_decompression_cache_enabled = AssemblyStoreDecompressionCacheEnabled,
};
application_config = new StructureInstance<ApplicationConfigCLR> (applicationConfigStructureInfo, app_cfg);
module.AddGlobalVariable ("application_config", application_config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ partial class AssemblyStoreGenerator
{
sealed class AssemblyStoreHeader
{
public const uint NativeSize = 5 * sizeof (uint) + sizeof (ulong);
public const uint NativeSize = 5 * sizeof (uint);

public readonly uint magic = ASSEMBLY_STORE_MAGIC;
public readonly uint version;
Expand All @@ -14,19 +14,17 @@ sealed class AssemblyStoreHeader

// Index size in bytes
public readonly uint index_size;
public readonly ulong content_id;

public AssemblyStoreHeader (uint version, uint entry_count, uint index_entry_count, uint index_size, ulong content_id)
public AssemblyStoreHeader (uint version, uint entry_count, uint index_entry_count, uint index_size)
{
this.version = version;
this.entry_count = entry_count;
this.index_entry_count = index_entry_count;
this.index_size = index_size;
this.content_id = content_id;
}
#if XABT_TESTS
public AssemblyStoreHeader (uint magic, uint version, uint entry_count, uint index_entry_count, uint index_size, ulong content_id)
: this (version, entry_count, index_entry_count, index_size, content_id)
public AssemblyStoreHeader (uint magic, uint version, uint entry_count, uint index_entry_count, uint index_size)
: this (version, entry_count, index_entry_count, index_size)
{
this.magic = magic;
}
Expand Down
Loading
Loading