diff --git a/CHANGELOG.md b/CHANGELOG.md
index b32853db5b..94ba273798 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
### BREAKING CHANGES
- This release adds support for .NET 10 and drops support for net8.0-android, net8.0-ios, net8.0-maccatalyst and net8.0-windows10.0.19041.0 ([#4461](https://github.com/getsentry/sentry-dotnet/pull/4461))
+- Added support for v3 of the Android AssemblyStore format that is used in .NET 10 and dropped support for v1 that was used in .NET 8 ([#4583](https://github.com/getsentry/sentry-dotnet/pull/4583))
+
## Unreleased
### Features
diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
index 77c4e079ee..add0969475 100644
--- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
+++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
@@ -6,7 +6,7 @@
On Mac, we'll also build for iOS and MacCatalyst.
On Windows, we'll also build for Windows 10.
-->
- $(TargetFrameworks);net9.0-android35.0
+ $(TargetFrameworks);net9.0-android
$(TargetFrameworks);net9.0-windows10.0.19041.0;net9.0-ios18.0;net9.0-maccatalyst18.0
$(TargetFrameworks);net9.0-ios18.0;net9.0-maccatalyst18.0
Exe
diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs
index e111a46f71..2e4b8d03ab 100644
--- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs
+++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs
@@ -1,4 +1,3 @@
-using Sentry.Android.AssemblyReader.V1;
using Sentry.Android.AssemblyReader.V2;
namespace Sentry.Android.AssemblyReader;
@@ -19,28 +18,13 @@ public static IAndroidAssemblyReader Open(string apkPath, IList supporte
{
logger?.Invoke(DebugLoggerLevel.Debug, "Opening APK: {0}", apkPath);
-#if NET9_0
- logger?.Invoke(DebugLoggerLevel.Debug, "Reading files using V2 APK layout.");
- if (AndroidAssemblyStoreReaderV2.TryReadStore(apkPath, supportedAbis, logger, out var readerV2))
+ if (AndroidAssemblyStoreReader.TryReadStore(apkPath, supportedAbis, logger, out var readerV2))
{
- logger?.Invoke(DebugLoggerLevel.Debug, "APK uses AssemblyStore V2");
+ logger?.Invoke(DebugLoggerLevel.Debug, "APK uses AssemblyStore");
return readerV2;
}
logger?.Invoke(DebugLoggerLevel.Debug, "APK doesn't use AssemblyStore");
- return new AndroidAssemblyDirectoryReaderV2(apkPath, supportedAbis, logger);
-#else
- logger?.Invoke(DebugLoggerLevel.Debug, "Reading files using V1 APK layout.");
-
- var zipArchive = ZipFile.OpenRead(apkPath);
- if (zipArchive.GetEntry("assemblies/assemblies.manifest") is not null)
- {
- logger?.Invoke(DebugLoggerLevel.Debug, "APK uses AssemblyStore V1");
- return new AndroidAssemblyStoreReaderV1(zipArchive, supportedAbis, logger);
- }
-
- logger?.Invoke(DebugLoggerLevel.Debug, "APK doesn't use AssemblyStore");
- return new AndroidAssemblyDirectoryReaderV1(zipArchive, supportedAbis, logger);
-#endif
+ return new AndroidAssemblyDirectoryReader(apkPath, supportedAbis, logger);
}
}
diff --git a/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt b/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt
deleted file mode 100644
index 16c3f15ed7..0000000000
--- a/src/Sentry.Android.AssemblyReader/V1/ATTRIBUTION.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Parts of the code in this subdirectory have been adapted from
-https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreExplorer.cs
-
-The original license is as follows:
-
-
-Xamarin.Android SDK
-
-The MIT License (MIT)
-
-Copyright (c) .NET Foundation Contributors
-
-All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs
deleted file mode 100644
index 7b5e1c1a7e..0000000000
--- a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-namespace Sentry.Android.AssemblyReader.V1;
-
-// The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file.
-internal sealed class AndroidAssemblyDirectoryReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader
-{
- public AndroidAssemblyDirectoryReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger)
- : base(zip, supportedAbis, logger) { }
-
- public PEReader? TryReadAssembly(string name)
- {
- if (File.Exists(name))
- {
- // The assembly is already extracted to the file system. Just read it.
- var stream = File.OpenRead(name);
- return new PEReader(stream);
- }
-
- var zipEntry = FindAssembly(name);
- if (zipEntry is null)
- {
- Logger?.Invoke(DebugLoggerLevel.Debug, "Couldn't find assembly {0} in the APK", name);
- return null;
- }
-
- Logger?.Invoke(DebugLoggerLevel.Debug, "Resolved assembly {0} in the APK at {1}", name, zipEntry.FullName);
-
- // We need a seekable stream for the PEReader (or even to check whether the DLL is compressed), so make a copy.
- var memStream = zipEntry.Extract();
- return ArchiveUtils.CreatePEReader(name, memStream, Logger);
- }
-
- private ZipArchiveEntry? FindAssembly(string name)
- {
- var zipEntry = ZipArchive.GetEntry($"assemblies/{name}");
-
- if (zipEntry is null)
- {
- foreach (var abi in SupportedAbis)
- {
- if (abi.Length > 0)
- {
- zipEntry = ZipArchive.GetEntry($"assemblies/{abi}/{name}");
- if (zipEntry is not null)
- {
- break;
- }
- }
- }
- }
-
- return zipEntry;
- }
-}
diff --git a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs
deleted file mode 100644
index 60c947b23e..0000000000
--- a/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyStoreReaderV1.cs
+++ /dev/null
@@ -1,510 +0,0 @@
-namespace Sentry.Android.AssemblyReader.V1;
-
-// See https://devblogs.microsoft.com/dotnet/performance-improvements-in-dotnet-maui/#single-file-assembly-stores
-internal sealed class AndroidAssemblyStoreReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader
-{
- private readonly AssemblyStoreExplorer _explorer;
-
- public AndroidAssemblyStoreReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger)
- : base(zip, supportedAbis, logger)
- {
- _explorer = new(zip, supportedAbis, logger);
- }
-
- public PEReader? TryReadAssembly(string name)
- {
- var assembly = TryFindAssembly(name);
- if (assembly is null)
- {
- Logger?.Invoke(DebugLoggerLevel.Debug, "Couldn't find assembly {0} in the APK AssemblyStore", name);
- return null;
- }
-
- Logger?.Invoke(DebugLoggerLevel.Debug, "Resolved assembly {0} in the APK {1} AssemblyStore", name, assembly.Store.Arch);
-
- var stream = assembly.GetImage();
- if (stream is null)
- {
- Logger?.Invoke(DebugLoggerLevel.Debug, "Couldn't access assembly {0} image stream", name);
- return null;
- }
-
- return ArchiveUtils.CreatePEReader(name, stream, Logger);
- }
-
- private AssemblyStoreAssembly? TryFindAssembly(string name)
- {
- if (_explorer.AssembliesByName.TryGetValue(name, out var assembly))
- {
- return assembly;
- }
-
- if (name.EndsWith(".dll", ignoreCase: true, CultureInfo.InvariantCulture) ||
- name.EndsWith(".exe", ignoreCase: true, CultureInfo.InvariantCulture))
- {
- if (_explorer.AssembliesByName.TryGetValue(name.Substring(0, name.Length - 4), out assembly))
- {
- return assembly;
- }
- }
-
- return null;
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreExplorer.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreExplorer
- {
- private AssemblyStoreReader? _indexStore;
- private readonly AssemblyStoreManifestReader _manifest;
- private readonly DebugLogger? _logger;
-
- public IDictionary AssembliesByName { get; } =
- new SortedDictionary(StringComparer.OrdinalIgnoreCase);
-
- public IDictionary AssembliesByHash32 { get; } =
- new Dictionary();
-
- public IDictionary AssembliesByHash64 { get; } =
- new Dictionary();
-
- public List Assemblies { get; } = new();
-
- public IDictionary> Stores { get; } =
- new SortedDictionary>();
-
- public AssemblyStoreExplorer(ZipArchive zip, IList supportedAbis, DebugLogger? logger)
- {
- _logger = logger;
- _manifest = new AssemblyStoreManifestReader(zip.GetEntry("assemblies/assemblies.manifest")!.Open());
-
- TryAddStore(zip, null);
- foreach (var abi in supportedAbis)
- {
- if (!string.IsNullOrEmpty(abi))
- {
- TryAddStore(zip, abi);
- }
- }
-
- zip.Dispose();
- ProcessStores();
- }
-
- private void ProcessStores()
- {
- if (Stores.Count == 0 || _indexStore == null)
- {
- return;
- }
-
- ProcessIndex(_indexStore.GlobalIndex32, "32", (he, assembly) =>
- {
- assembly.Hash32 = (uint)he.Hash;
- assembly.RuntimeIndex = he.MappingIndex;
-
- if (_manifest.EntriesByHash32.TryGetValue(assembly.Hash32, out var me))
- {
- assembly.Name = me.Name;
- }
-
- if (!AssembliesByHash32.ContainsKey(assembly.Hash32))
- {
- AssembliesByHash32.Add(assembly.Hash32, assembly);
- }
- });
-
- ProcessIndex(_indexStore.GlobalIndex64, "64", (he, assembly) =>
- {
- assembly.Hash64 = he.Hash;
- if (assembly.RuntimeIndex != he.MappingIndex)
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"assembly with hashes 0x{assembly.Hash32} and 0x{assembly.Hash64} has a different 32-bit runtime index ({assembly.RuntimeIndex}) than the 64-bit runtime index({he.MappingIndex})");
- }
-
- if (_manifest.EntriesByHash64.TryGetValue(assembly.Hash64, out var me))
- {
- if (string.IsNullOrEmpty(assembly.Name))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"32-bit hash 0x{assembly.Hash32:x} did not match any assembly name in the manifest");
- assembly.Name = me.Name;
- if (string.IsNullOrEmpty(assembly.Name))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"64-bit hash 0x{assembly.Hash64:x} did not match any assembly name in the manifest");
- }
- }
- else if (!string.Equals(assembly.Name, me.Name, StringComparison.Ordinal))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"32-bit hash 0x{assembly.Hash32:x} maps to assembly name '{assembly.Name}', however 64-bit hash 0x{assembly.Hash64:x} for the same entry matches assembly name '{me.Name}'");
- }
- }
-
- if (!AssembliesByHash64.ContainsKey(assembly.Hash64))
- {
- AssembliesByHash64.Add(assembly.Hash64, assembly);
- }
- });
-
- foreach (var kvp in Stores)
- {
- var list = kvp.Value;
- if (list.Count < 2)
- {
- continue;
- }
-
- var template = list[0];
- for (var i = 1; i < list.Count; i++)
- {
- var other = list[i];
- if (!template.HasIdenticalContent(other))
- {
- throw new Exception(
- $"Store ID {template.StoreId} for architecture {other.Arch} is not identical to other stores with the same ID");
- }
- }
- }
-
- void ProcessIndex(List index, string bitness,
- Action assemblyHandler)
- {
- foreach (var he in index)
- {
- if (!Stores.TryGetValue(he.StoreId, out var storeList))
- {
- _logger?.Invoke(DebugLoggerLevel.Debug, $"store with id {he.StoreId} not part of the set");
- continue;
- }
-
- foreach (var store in storeList)
- {
- if (he.LocalStoreIndex >= (uint)store.Assemblies.Count)
- {
- _logger?.Invoke(DebugLoggerLevel.Debug,
- $"{bitness}-bit index entry with hash 0x{he.Hash:x} has invalid store {store.StoreId} index {he.LocalStoreIndex} (maximum allowed is {store.Assemblies.Count})");
- continue;
- }
-
- var assembly = store.Assemblies[(int)he.LocalStoreIndex];
- assemblyHandler(he, assembly);
-
- if (!AssembliesByName.ContainsKey(assembly.Name))
- {
- AssembliesByName.Add(assembly.Name, assembly);
- }
- }
- }
- }
- }
-
- private void TryAddStore(ZipArchive archive, string? abi)
- {
- var infix = abi is null ? "" : $".{abi}";
- if (archive.GetEntry($"assemblies/assemblies{infix}.blob") is { } zipEntry)
- {
- var memStream = new MemoryStream((int)zipEntry.Length);
- using (var zipStream = zipEntry.Open())
- {
- zipStream.CopyTo(memStream);
- memStream.Position = 0;
- }
-
- AddStore(new AssemblyStoreReader(memStream, abi));
- }
- }
-
- private void AddStore(AssemblyStoreReader reader)
- {
- if (reader.HasGlobalIndex)
- {
- _indexStore = reader;
- }
-
- if (!Stores.TryGetValue(reader.StoreId, out var storeList))
- {
- storeList = new List();
- Stores.Add(reader.StoreId, storeList);
- }
-
- storeList.Add(reader);
-
- Assemblies.AddRange(reader.Assemblies);
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreManifestReader.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreManifestReader
- {
- public List Entries { get; } = new();
- public Dictionary EntriesByHash32 { get; } = new();
- public Dictionary EntriesByHash64 { get; } = new();
-
- public AssemblyStoreManifestReader(Stream manifest)
- {
- using var sr = new StreamReader(manifest, Encoding.UTF8, detectEncodingFromByteOrderMarks: false);
- ReadManifest(sr);
- }
-
- private void ReadManifest(StreamReader reader)
- {
- // First line is ignored, it contains headers
- reader.ReadLine();
-
- // Each subsequent line consists of fields separated with any number of spaces (for the pleasure of a human being reading the manifest)
- while (!reader.EndOfStream)
- {
- var fields = reader.ReadLine()?.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- if (fields == null)
- {
- continue;
- }
-
- var entry = new AssemblyStoreManifestEntry(fields);
- Entries.Add(entry);
- if (entry.Hash32 != 0)
- {
- EntriesByHash32.Add(entry.Hash32, entry);
- }
-
- if (entry.Hash64 != 0)
- {
- EntriesByHash64.Add(entry.Hash64, entry);
- }
- }
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreManifestEntry.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreManifestEntry
- {
- // Fields are:
- // Hash 32 | Hash 64 | Store ID | Store idx | Name
- private const int NumberOfFields = 5;
- private const int Hash32FieldIndex = 0;
- private const int Hash64FieldIndex = 1;
- private const int StoreIdFieldIndex = 2;
- private const int StoreIndexFieldIndex = 3;
- private const int NameFieldIndex = 4;
-
- public uint Hash32 { get; }
- public ulong Hash64 { get; }
- public uint StoreId { get; }
- public uint IndexInStore { get; }
- public string Name { get; }
-
- public AssemblyStoreManifestEntry(string[] fields)
- {
- if (fields.Length != NumberOfFields)
- {
- throw new ArgumentOutOfRangeException(nameof(fields), "Invalid number of fields");
- }
-
- Hash32 = GetUInt32(fields[Hash32FieldIndex]);
- Hash64 = GetUInt64(fields[Hash64FieldIndex]);
- StoreId = GetUInt32(fields[StoreIdFieldIndex]);
- IndexInStore = GetUInt32(fields[StoreIndexFieldIndex]);
- Name = fields[NameFieldIndex].Trim();
- }
-
- private static uint GetUInt32(string value)
- {
- if (uint.TryParse(PrepHexValue(value), NumberStyles.HexNumber, null, out var hash))
- {
- return hash;
- }
-
- return 0;
- }
-
- private static ulong GetUInt64(string value)
- {
- if (ulong.TryParse(PrepHexValue(value), NumberStyles.HexNumber, null, out var hash))
- {
- return hash;
- }
-
- return 0;
- }
-
- private static string PrepHexValue(string value)
- {
- if (value.StartsWith("0x", StringComparison.Ordinal))
- {
- return value.Substring(2);
- }
-
- return value;
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreHashEntry.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreHashEntry
- {
- public bool Is32Bit { get; }
- public ulong Hash { get; }
- public uint MappingIndex { get; }
- public uint LocalStoreIndex { get; }
- public uint StoreId { get; }
-
- internal AssemblyStoreHashEntry(BinaryReader reader, bool is32Bit)
- {
- Is32Bit = is32Bit;
- Hash = reader.ReadUInt64();
- MappingIndex = reader.ReadUInt32();
- LocalStoreIndex = reader.ReadUInt32();
- StoreId = reader.ReadUInt32();
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreReader.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreReader
- {
- // These two constants must be identical to the native ones in src/monodroid/jni/xamarin-app.hh
- private const uint ASSEMBLY_STORE_MAGIC = 0x41424158; // 'XABA', little-endian
- private const uint ASSEMBLY_STORE_FORMAT_VERSION = 1; // The highest format version this reader understands
-
- private readonly MemoryStream _storeData;
-
- public uint Version { get; private set; }
- public uint LocalEntryCount { get; private set; }
- public uint GlobalEntryCount { get; private set; }
- public uint StoreId { get; private set; }
- public List Assemblies { get; }
- public List GlobalIndex32 { get; } = new();
- public List GlobalIndex64 { get; } = new();
- public string Arch { get; }
-
- public bool HasGlobalIndex => StoreId == 0;
-
- public AssemblyStoreReader(MemoryStream store, string? arch = null)
- {
- Arch = arch ?? string.Empty;
- _storeData = store;
- using var reader = new BinaryReader(store, Encoding.UTF8, leaveOpen: true);
- ReadHeader(reader);
-
- Assemblies = new List();
- ReadLocalEntries(reader, Assemblies);
- if (HasGlobalIndex)
- {
- ReadGlobalIndex(reader, GlobalIndex32, GlobalIndex64);
- }
- }
-
- internal MemoryStream? GetAssemblyImageSlice(AssemblyStoreAssembly assembly) =>
- GetDataSlice(assembly.DataOffset, assembly.DataSize);
-
- internal MemoryStream? GetAssemblyDebugDataSlice(AssemblyStoreAssembly assembly) =>
- assembly.DebugDataOffset == 0 ? null : GetDataSlice(assembly.DebugDataOffset, assembly.DebugDataSize);
-
- internal MemoryStream? GetAssemblyConfigSlice(AssemblyStoreAssembly assembly) =>
- assembly.ConfigDataOffset == 0 ? null : GetDataSlice(assembly.ConfigDataOffset, assembly.ConfigDataSize);
-
- private MemoryStream? GetDataSlice(uint offset, uint size) =>
- size == 0 ? null : new ArchiveUtils.MemorySlice(_storeData, (int)offset, (int)size);
-
- public bool HasIdenticalContent(AssemblyStoreReader other)
- {
- return
- other.Version == Version &&
- other.LocalEntryCount == LocalEntryCount &&
- other.GlobalEntryCount == GlobalEntryCount &&
- other.StoreId == StoreId &&
- other.Assemblies.Count == Assemblies.Count &&
- other.GlobalIndex32.Count == GlobalIndex32.Count &&
- other.GlobalIndex64.Count == GlobalIndex64.Count;
- }
-
- private void ReadHeader(BinaryReader reader)
- {
- if (reader.ReadUInt32() != ASSEMBLY_STORE_MAGIC)
- {
- throw new InvalidOperationException("Invalid header magic number");
- }
-
- Version = reader.ReadUInt32();
- if (Version == 0)
- {
- throw new InvalidOperationException("Invalid version number: 0");
- }
-
- if (Version > ASSEMBLY_STORE_FORMAT_VERSION)
- {
- throw new InvalidOperationException(
- $"Store format version {Version} is higher than the one understood by this reader, {ASSEMBLY_STORE_FORMAT_VERSION}");
- }
-
- LocalEntryCount = reader.ReadUInt32();
- GlobalEntryCount = reader.ReadUInt32();
- StoreId = reader.ReadUInt32();
- }
-
- private void ReadLocalEntries(BinaryReader reader, List assemblies)
- {
- for (uint i = 0; i < LocalEntryCount; i++)
- {
- assemblies.Add(new AssemblyStoreAssembly(reader, this));
- }
- }
-
- private void ReadGlobalIndex(BinaryReader reader, List index32,
- List index64)
- {
- ReadIndex(true, index32);
- ReadIndex(false, index64);
-
- void ReadIndex(bool is32Bit, List index)
- {
- for (uint i = 0; i < GlobalEntryCount; i++)
- {
- index.Add(new AssemblyStoreHashEntry(reader, is32Bit));
- }
- }
- }
- }
-
- // Adapted from https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/assembly-store-reader/AssemblyStoreAssembly.cs
- // With the original code licensed under MIT License (https://github.com/xamarin/xamarin-android/blob/2bd13c4a00ae78db34663a4b9c7a4c5bfb20c344/LICENSE).
- private class AssemblyStoreAssembly
- {
- public uint DataOffset { get; }
- public uint DataSize { get; }
- public uint DebugDataOffset { get; }
- public uint DebugDataSize { get; }
- public uint ConfigDataOffset { get; }
- public uint ConfigDataSize { get; }
-
- public uint Hash32 { get; set; }
- public ulong Hash64 { get; set; }
- public string Name { get; set; } = string.Empty;
- public uint RuntimeIndex { get; set; }
-
- public AssemblyStoreReader Store { get; }
-
- internal AssemblyStoreAssembly(BinaryReader reader, AssemblyStoreReader store)
- {
- Store = store;
-
- DataOffset = reader.ReadUInt32();
- DataSize = reader.ReadUInt32();
- DebugDataOffset = reader.ReadUInt32();
- DebugDataSize = reader.ReadUInt32();
- ConfigDataOffset = reader.ReadUInt32();
- ConfigDataSize = reader.ReadUInt32();
- }
-
- public MemoryStream? GetImage() => Store.GetAssemblyImageSlice(this);
-
- public MemoryStream? GetDebugData() => Store.GetAssemblyDebugDataSlice(this);
-
- public MemoryStream? GetAssemblyConfig() => Store.GetAssemblyConfigSlice(this);
- }
-}
diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReader.cs
similarity index 98%
rename from src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs
rename to src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReader.cs
index b250640d1f..ca2b96d886 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReader.cs
@@ -1,13 +1,13 @@
namespace Sentry.Android.AssemblyReader.V2;
// The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file.
-internal sealed class AndroidAssemblyDirectoryReaderV2 : IAndroidAssemblyReader
+internal sealed class AndroidAssemblyDirectoryReader : IAndroidAssemblyReader
{
private DebugLogger? Logger { get; }
private HashSet SupportedArchitectures { get; } = new();
private readonly ArchiveAssemblyHelper _archiveAssemblyHelper;
- public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedAbis, DebugLogger? logger)
+ public AndroidAssemblyDirectoryReader(string apkPath, IList supportedAbis, DebugLogger? logger)
{
Logger = logger;
foreach (var abi in supportedAbis)
diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReader.cs
similarity index 94%
rename from src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs
rename to src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReader.cs
index 2840c7ec67..0e9c6586f4 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReader.cs
@@ -1,17 +1,17 @@
namespace Sentry.Android.AssemblyReader.V2;
-internal class AndroidAssemblyStoreReaderV2 : IAndroidAssemblyReader
+internal class AndroidAssemblyStoreReader : IAndroidAssemblyReader
{
private readonly IList _explorers;
private readonly DebugLogger? _logger;
- private AndroidAssemblyStoreReaderV2(IList explorers, DebugLogger? logger)
+ private AndroidAssemblyStoreReader(IList explorers, DebugLogger? logger)
{
_explorers = explorers;
_logger = logger;
}
- public static bool TryReadStore(string inputFile, IList supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReaderV2? reader)
+ public static bool TryReadStore(string inputFile, IList supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReader? reader)
{
List supportedExplorers = [];
@@ -67,7 +67,7 @@ public static bool TryReadStore(string inputFile, IList supportedAbis, D
return false;
}
- reader = new AndroidAssemblyStoreReaderV2(supportedExplorers, logger);
+ reader = new AndroidAssemblyStoreReader(supportedExplorers, logger);
return true;
}
diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs
index 0ab9c04e2e..5854d3c070 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs
@@ -79,28 +79,19 @@ public static (IList? explorers, string? errorMessage) Op
}
private static (IList? explorers, string? errorMessage) OpenAab(FileInfo fi, DebugLogger? logger)
- => OpenCommon(fi, [StoreReaderV2.AabPaths, StoreReader_V1.AabPaths], logger);
+ => OpenCommon(fi, StoreReader.AabPaths, logger);
private static (IList? explorers, string? errorMessage) OpenAabBase(FileInfo fi, DebugLogger? logger)
- => OpenCommon(fi, [StoreReaderV2.AabBasePaths, StoreReader_V1.AabBasePaths], logger);
+ => OpenCommon(fi, StoreReader.AabBasePaths, logger);
private static (IList? explorers, string? errorMessage) OpenApk(FileInfo fi, DebugLogger? logger)
- => OpenCommon(fi, [StoreReaderV2.ApkPaths, StoreReader_V1.ApkPaths], logger);
+ => OpenCommon(fi, StoreReader.ApkPaths, logger);
- private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, List> pathLists, DebugLogger? logger)
+ private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, IList paths, DebugLogger? logger)
{
using var zip = ZipFile.OpenRead(fi.FullName);
-
- foreach (var paths in pathLists)
- {
- var (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger);
- if (pathsFound)
- {
- return (explorers, errorMessage);
- }
- }
-
- return (null, "Unable to find any blob entries");
+ var (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger);
+ return pathsFound ? (explorers, errorMessage) : (null, "Unable to find any blob entries");
}
private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad(FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger)
diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs
index a132f51ead..2ecde5fd48 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs
@@ -1,5 +1,7 @@
/*
* Adapted from https://github.com/dotnet/android/blob/86260ed36dfe1a90c8ed6a2bb1cd0607d637f403/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreItem.cs
+ * Updated from https://github.com/dotnet/android/blob/64018e13e53cec7246e54866b520d3284de344e0/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreItem.cs
+ * - Adding support for AssemblyStore v3 format that shipped in .NET 10 (https://github.com/dotnet/android/pull/10249)
* Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
*/
@@ -17,11 +19,13 @@ internal abstract class AssemblyStoreItem
public uint ConfigOffset { get; protected set; }
public uint ConfigSize { get; protected set; }
public AndroidTargetArch TargetArch { get; protected set; }
+ public bool Ignore { get; }
- protected AssemblyStoreItem(string name, bool is64Bit, List hashes)
+ protected AssemblyStoreItem(string name, bool is64Bit, List hashes, bool ignore)
{
Name = name;
Hashes = hashes.AsReadOnly();
Is64Bit = is64Bit;
+ Ignore = ignore;
}
}
diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs
index 834cb2c36d..a3201754e1 100644
--- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs
@@ -32,23 +32,7 @@ protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger)
public static AssemblyStoreReader? Create(Stream store, string path, DebugLogger? logger)
{
- var reader = MakeReaderReady(new StoreReader_V1(store, path, logger));
- if (reader != null)
- {
- return reader;
- }
-
- reader = MakeReaderReady(new StoreReaderV2(store, path, logger));
- if (reader != null)
- {
- return reader;
- }
-
- return null;
- }
-
- private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader)
- {
+ var reader = new StoreReader(store, path, logger);
if (!reader.IsSupported())
{
return null;
diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader.Classes.cs
similarity index 80%
rename from src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs
rename to src/Sentry.Android.AssemblyReader/V2/StoreReader.Classes.cs
index 85aa91ba89..5ed713b6be 100644
--- a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader.Classes.cs
@@ -1,11 +1,13 @@
/*
* Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs
+ * Updated from https://github.com/dotnet/android/blob/64018e13e53cec7246e54866b520d3284de344e0/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs
+ * - Adding support for AssemblyStore v3 format that shipped in .NET 10 (https://github.com/dotnet/android/pull/10249)
* Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
*/
namespace Sentry.Android.AssemblyReader.V2;
-internal partial class StoreReaderV2
+internal partial class StoreReader
{
private sealed class Header
{
@@ -33,11 +35,13 @@ private sealed class IndexEntry
{
public readonly ulong name_hash;
public readonly uint descriptor_index;
+ public readonly bool ignore;
- public IndexEntry(ulong name_hash, uint descriptor_index)
+ public IndexEntry(ulong name_hash, uint descriptor_index, bool ignore)
{
this.name_hash = name_hash;
this.descriptor_index = descriptor_index;
+ this.ignore = ignore;
}
}
@@ -57,8 +61,9 @@ private sealed class EntryDescriptor
private sealed class StoreItemV2 : AssemblyStoreItem
{
- public StoreItemV2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor)
- : base(name, is64Bit, IndexToHashes(indexEntries))
+ public StoreItemV2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries,
+ EntryDescriptor descriptor, bool ignore)
+ : base(name, is64Bit, IndexToHashes(indexEntries), ignore)
{
DataOffset = descriptor.data_offset;
DataSize = descriptor.data_size;
@@ -86,11 +91,13 @@ private sealed class TemporaryItem
public readonly string Name;
public readonly List IndexEntries = new List();
public readonly EntryDescriptor Descriptor;
+ public readonly bool Ignored;
- public TemporaryItem(string name, EntryDescriptor descriptor)
+ public TemporaryItem(string name, EntryDescriptor descriptor, bool ignored)
{
Name = name;
Descriptor = descriptor;
+ Ignored = ignored;
}
}
}
diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader.cs
similarity index 90%
rename from src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs
rename to src/Sentry.Android.AssemblyReader/V2/StoreReader.cs
index 97bac95694..5220bf7dc7 100644
--- a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs
+++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader.cs
@@ -1,15 +1,22 @@
/*
* Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
+ * Updated from https://github.com/dotnet/android/blob/64018e13e53cec7246e54866b520d3284de344e0/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs
+ * - Adding support for AssemblyStore v3 format that shipped in .NET 10 (https://github.com/dotnet/android/pull/10249)
* Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
*/
namespace Sentry.Android.AssemblyReader.V2;
-internal partial class StoreReaderV2 : AssemblyStoreReader
+internal partial class StoreReader : AssemblyStoreReader
{
// Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones
+#if NET9_0
private const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant
private const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002;
+#else
+ private const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000003; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant
+ private const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000003;
+#endif
private const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000;
private const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000;
private const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000;
@@ -28,7 +35,7 @@ internal partial class StoreReaderV2 : AssemblyStoreReader
private Header? header;
private ulong elfOffset = 0;
- static StoreReaderV2()
+ static StoreReader()
{
var paths = new List {
GetArchPath (AndroidTargetArch.Arm64),
@@ -69,7 +76,7 @@ string GetArchPath(AndroidTargetArch arch, string? root = null)
}
}
- public StoreReaderV2(Stream store, string path, DebugLogger? logger)
+ public StoreReader(Stream store, string path, DebugLogger? logger)
: base(store, path, logger)
{
supportedVersions = new HashSet {
@@ -178,7 +185,12 @@ protected override void Prepare()
}
uint descriptor_index = reader.ReadUInt32();
- index.Add(new IndexEntry(name_hash, descriptor_index));
+#if NET10_0_OR_GREATER
+ bool ignore = reader.ReadByte () != 0;
+#else
+ bool ignore = false;
+#endif
+ index.Add(new IndexEntry(name_hash, descriptor_index, ignore));
}
var descriptors = new List();
@@ -218,7 +230,7 @@ protected override void Prepare()
{
if (!tempItems.TryGetValue(ie.descriptor_index, out TemporaryItem? item))
{
- item = new TemporaryItem(names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index]);
+ item = new TemporaryItem(names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index], ie.ignore);
tempItems.Add(ie.descriptor_index, item);
}
item.IndexEntries.Add(ie);
@@ -233,7 +245,7 @@ protected override void Prepare()
foreach (var kvp in tempItems)
{
TemporaryItem ti = kvp.Value;
- var item = new StoreItemV2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor);
+ var item = new StoreItemV2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor, ti.Ignored);
storeItems.Add(item);
}
Assemblies = storeItems.AsReadOnly();
diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs
deleted file mode 100644
index c1a3fe3c2d..0000000000
--- a/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Adapted from https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs
- * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT)
- */
-
-namespace Sentry.Android.AssemblyReader.V2;
-
-internal class StoreReader_V1 : AssemblyStoreReader
-{
- public override string Description => "Assembly store v1";
- public override bool NeedsExtensionInName => false;
-
- public static IList ApkPaths { get; }
- public static IList AabPaths { get; }
- public static IList AabBasePaths { get; }
-
- static StoreReader_V1()
- {
- ApkPaths = new List().AsReadOnly();
- AabPaths = new List().AsReadOnly();
- AabBasePaths = new List().AsReadOnly();
- }
-
- public StoreReader_V1(Stream store, string path, DebugLogger? logger)
- : base(store, path, logger)
- { }
-
- protected override bool IsSupported()
- {
- return false;
- }
-
- protected override void Prepare()
- {
- }
-
- protected override ulong GetStoreStartDataOffset() => 0;
-}
diff --git a/test/AndroidTestApp/AndroidTestApp.csproj b/test/AndroidTestApp/AndroidTestApp.csproj
index 261fc05b24..cefce5290f 100644
--- a/test/AndroidTestApp/AndroidTestApp.csproj
+++ b/test/AndroidTestApp/AndroidTestApp.csproj
@@ -2,6 +2,7 @@
net10.0-android;net9.0-android
false
+ false
21
Exe
enable
@@ -9,5 +10,6 @@
com.companyname.AndroidTestApp
1
1.0
+ android-x64
diff --git a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs
index f0d1a50c64..4675f4e02e 100644
--- a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs
+++ b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs
@@ -1,4 +1,3 @@
-using Sentry.Android.AssemblyReader.V1;
using Sentry.Android.AssemblyReader.V2;
namespace Sentry.Android.AssemblyReader.Tests;
@@ -11,8 +10,6 @@ public class AndroidAssemblyReaderTests
private static string TargetFramework => "net10.0";
#elif NET9_0
private static string TargetFramework => "net9.0";
-#elif NET8_0
- private static string TargetFramework => "net8.0";
#else
// Adding a new TFM to the project? Include it above
#error "Target Framework not yet supported for AndroidAssemblyReader"
@@ -54,11 +51,11 @@ public void CreatesCorrectStoreReader()
using var sut = GetSut(isAot: false, isAssemblyStore: true, isCompressed: true);
switch (TargetFramework)
{
- case "net9.0":
- Assert.IsType(sut);
+ case "net10.0":
+ Assert.IsType(sut);
break;
- case "net8.0":
- Assert.IsType(sut);
+ case "net9.0":
+ Assert.IsType(sut);
break;
default:
throw new NotSupportedException($"Unsupported target framework: {TargetFramework}");
@@ -74,11 +71,11 @@ public void CreatesCorrectArchiveReader()
using var sut = GetSut(isAot: false, isAssemblyStore: false, isCompressed: true);
switch (TargetFramework)
{
- case "net9.0":
- Assert.IsType(sut);
+ case "net10.0":
+ Assert.IsType(sut);
break;
- case "net8.0":
- Assert.IsType(sut);
+ case "net9.0":
+ Assert.IsType(sut);
break;
default:
throw new NotSupportedException($"Unsupported target framework: {TargetFramework}");
@@ -95,11 +92,7 @@ public void ReturnsNullIfAssemblyDoesntExist(bool isAssemblyStore)
}
public static IEnumerable