From 7e7e512a82557ce3204da4c87ec922fc0e6b482d Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 13 Mar 2025 13:16:08 +1300 Subject: [PATCH 01/22] Ensure device tests run on net8.0 and net9.0 --- .github/workflows/device-tests-android.yml | 27 +++++++++++----- scripts/device-test.ps1 | 31 ++++++++++++++----- .../AndroidAssemblyReaderTests.cs | 6 ++-- .../ApiApprovalTests.verify.cs | 2 ++ ...Sentry.Android.AssemblyReader.Tests.csproj | 3 +- .../Sentry.Maui.Device.TestApp.csproj | 4 +-- 6 files changed, 54 insertions(+), 19 deletions(-) diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 64749e1b8f..3f1a83f392 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -12,6 +12,10 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + tfm: [net8.0, net9.0] env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_NOLOGO: 1 @@ -32,15 +36,24 @@ jobs: uses: ./.github/actions/buildnative - name: Build Android Test App - run: pwsh ./scripts/device-test.ps1 android -Build + run: pwsh ./scripts/device-test.ps1 android -Build -Tfm ${{ matrix.tfm }} - - name: Upload Android Test App + - name: Upload Android Test App (net8.0) + if: matrix.tfm == 'net8.0' uses: actions/upload-artifact@v4 with: - name: device-test-android + name: device-test-android-net8.0 if-no-files-found: error path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android34.0/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk + - name: Upload Android Test App (net9.0) + if: matrix.tfm == 'net9.0' + uses: actions/upload-artifact@v4 + with: + name: device-test-android-net9.0 + if-no-files-found: error + path: test/Sentry.Maui.Device.TestApp/bin/Release/net9.0-android35.0/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk + android: needs: [build] name: Run Android API-${{ matrix.api-level }} Test @@ -51,6 +64,7 @@ jobs: strategy: fail-fast: false matrix: + tfm: [net8.0, net9.0] # We run against both an older and a newer API api-level: [27, 33] env: @@ -70,7 +84,7 @@ jobs: - name: Download test app artifact uses: actions/download-artifact@v4 with: - name: device-test-android + name: device-test-android-${{ matrix.tfm }} path: bin - name: Setup Gradle @@ -78,7 +92,6 @@ jobs: # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md - - name: Run Tests timeout-minutes: 40 uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # Tag: v2.33.0 @@ -92,11 +105,11 @@ jobs: disk-size: 4096M emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: false - script: pwsh scripts/device-test.ps1 android -Run + script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }} - name: Upload results if: success() || failure() uses: actions/upload-artifact@v4 with: - name: device-test-android-${{ matrix.api-level }}-results + name: device-test-android-${{ matrix.api-level }}-${{ matrix.tfm }}-results path: test_output diff --git a/scripts/device-test.ps1 b/scripts/device-test.ps1 index 6812c42dff..03a7f54042 100644 --- a/scripts/device-test.ps1 +++ b/scripts/device-test.ps1 @@ -5,7 +5,8 @@ param( [String] $Platform, [Switch] $Build, - [Switch] $Run + [Switch] $Run, + [String] $Tfm ) Set-StrictMode -Version latest @@ -21,13 +22,29 @@ $CI = Test-Path env:CI Push-Location $PSScriptRoot/.. try { - $tfm = 'net8.0-' + if (!$Tfm) + { + $Tfm = 'net8.0' + } + switch ($Tfm) { + 'net8.0' { + $androidLevel = '34.0' + $iosLevel = '17.0' + } + 'net9.0' { + $androidLevel = '35.0' + $iosLevel = '18.0' + } + default { + throw "Unsupported Target Framework: $Tfm" + } + } $arch = (!$IsWindows -and $(uname -m) -eq 'arm64') ? 'arm64' : 'x64' if ($Platform -eq 'android') { - $tfm += 'android34.0' + $Tfm += '-android' + $androidLevel $group = 'android' - $buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/android-$arch" + $buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$Tfm/android-$arch" $arguments = @( '--app', "$buildDir/io.sentry.dotnet.maui.device.testapp-Signed.apk", '--package-name', 'io.sentry.dotnet.maui.device.testapp', @@ -43,11 +60,11 @@ try } elseif ($Platform -eq 'ios') { - $tfm += 'ios17.0' + $Tfm += '-ios' + $iosLevel $group = 'apple' # Always use x64 on iOS, since arm64 doesn't support JIT, which is required for tests using NSubstitute $arch = 'x64' - $buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/iossimulator-$arch" + $buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$Tfm/iossimulator-$arch" $envValue = $CI ? 'true' : 'false' $arguments = @( '--app', "$buildDir/Sentry.Maui.Device.TestApp.app", @@ -60,7 +77,7 @@ try if ($Build) { # We disable AOT for device tests: https://github.com/nsubstitute/NSubstitute/issues/834 - dotnet build -f $tfm -c Release -p:EnableAot=false -p:NoSymbolStrip=true test/Sentry.Maui.Device.TestApp + dotnet build -f $Tfm -c Release -p:EnableAot=false -p:NoSymbolStrip=true test/Sentry.Maui.Device.TestApp if ($LASTEXITCODE -ne 0) { throw 'Failed to build Sentry.Maui.Device.TestApp' diff --git a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs index 4b98a43257..3f2a15c7f6 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs +++ b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs @@ -1,3 +1,5 @@ +using Sentry.Android.AssemblyReader.V1; + namespace Sentry.Android.AssemblyReader.Tests; public class AndroidAssemblyReaderTests @@ -41,11 +43,11 @@ public void CreatesCorrectReader(bool isAssemblyStore) using var sut = GetSut(isAssemblyStore, isCompressed: true); if (isAssemblyStore) { - Assert.IsType(sut); + Assert.IsType(sut); } else { - Assert.IsType(sut); + Assert.IsType(sut); } } diff --git a/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.verify.cs b/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.verify.cs index 32bcf57cb7..53706496bf 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.verify.cs +++ b/test/Sentry.Android.AssemblyReader.Tests/ApiApprovalTests.verify.cs @@ -1,3 +1,5 @@ +using Sentry.Android.AssemblyReader.V1; + namespace Sentry.Android.AssemblyReader.Tests; public class ApiApprovalTests diff --git a/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj b/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj index d6dd9fc202..67b9fbad2b 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj +++ b/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj @@ -2,7 +2,8 @@ net9.0;net8.0 - $(TargetFrameworks);net8.0-android34.0 + + $(TargetFrameworks);net8.0-android34.0;net9.0-android35.0 enable diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index 5953b65d70..dbbd081db7 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -2,8 +2,8 @@ - $(TargetFrameworks);net8.0-android34.0 - $(TargetFrameworks);net8.0-ios17.0 + $(TargetFrameworks);net8.0-android34.0;net9.0-android35.0 + $(TargetFrameworks);net8.0-ios17.0;net9.0-ios18.0 true + + - @@ -27,8 +28,8 @@ <_ConfigString>Store=$(_Store)-Compressed=$(_Compressed) - - + + From 75e15c7f5a4cbb531b41a53ba46f742f8a2d021b Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 19 Mar 2025 02:49:50 +0000 Subject: [PATCH 11/22] Format code --- .../V2/AndroidAssemblyStoreReaderV2.cs | 2 +- .../V2/AndroidTargetArch.cs | 2 +- .../V2/AssemblyStoreExplorer.cs | 356 ++++++++---- .../V2/AssemblyStoreItem.cs | 32 +- .../V2/AssemblyStoreReader.cs | 152 +++--- .../V2/ELFPayloadError.cs | 12 +- .../V2/FileFormat.cs | 14 +- .../V2/MonoAndroidHelper.Basic.cs | 511 +++++++++++------- .../V2/StoreReader_V1.cs | 56 +- .../V2/StoreReader_V2.Classes.cs | 188 ++++--- .../V2/StoreReader_V2.cs | 449 ++++++++------- src/Sentry.Android.AssemblyReader/V2/Utils.cs | 317 ++++++----- 12 files changed, 1239 insertions(+), 852 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs index 6b954563ea..edcbd1e51e 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs @@ -99,7 +99,7 @@ private bool FindBestAssembly(string name, out ExplorerStoreItem? explorerAssemb { if (explorer.AssembliesByName?.TryGetValue(name, out var assembly) is true) { - explorerAssembly = new(explorer,assembly); + explorerAssembly = new(explorer, assembly); return true; } } diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs index af3bf3f260..8564e04e6f 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidTargetArch.cs @@ -1,4 +1,4 @@ -/* +/* * Adapted from https://github.com/dotnet/android-tools/blob/ab2165daf27d4fcb29e88bc022e0ab0be33aff69/src/Xamarin.Android.Tools.AndroidSdk/AndroidTargetArch.cs * Original code licensed under the MIT License (https://github.com/dotnet/android-tools/blob/ab2165daf27d4fcb29e88bc022e0ab0be33aff69/LICENSE) */ diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index 0297d13e40..f0d37159b7 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -10,33 +10,34 @@ namespace Sentry.Android.AssemblyReader.V2; internal class AssemblyStoreExplorer { - readonly AssemblyStoreReader reader; - - public string StorePath { get; } - public AndroidTargetArch? TargetArch { get; } - public uint AssemblyCount { get; } - public uint IndexEntryCount { get; } - public IList? Assemblies { get; } - public IDictionary? AssembliesByName { get; } - public bool Is64Bit { get; } - - protected AssemblyStoreExplorer (Stream storeStream, string path, DebugLogger? logger) - { - StorePath = path; - var storeReader = AssemblyStoreReader.Create (storeStream, path, logger); - if (storeReader == null) { - storeStream.Dispose (); - throw new NotSupportedException ($"Format of assembly store '{path}' is unsupported"); - } + private readonly AssemblyStoreReader reader; + + public string StorePath { get; } + public AndroidTargetArch? TargetArch { get; } + public uint AssemblyCount { get; } + public uint IndexEntryCount { get; } + public IList? Assemblies { get; } + public IDictionary? AssembliesByName { get; } + public bool Is64Bit { get; } + + protected AssemblyStoreExplorer(Stream storeStream, string path, DebugLogger? logger) + { + StorePath = path; + var storeReader = AssemblyStoreReader.Create(storeStream, path, logger); + if (storeReader == null) + { + storeStream.Dispose(); + throw new NotSupportedException($"Format of assembly store '{path}' is unsupported"); + } - reader = storeReader; - TargetArch = reader.TargetArch; - AssemblyCount = reader.AssemblyCount; - IndexEntryCount = reader.IndexEntryCount; - Assemblies = reader.Assemblies; - Is64Bit = reader.Is64Bit; + reader = storeReader; + TargetArch = reader.TargetArch; + AssemblyCount = reader.AssemblyCount; + IndexEntryCount = reader.IndexEntryCount; + Assemblies = reader.Assemblies; + Is64Bit = reader.Is64Bit; - var dict = new Dictionary (StringComparer.Ordinal); + var dict = new Dictionary(StringComparer.Ordinal); if (Assemblies is not null) { foreach (var item in Assemblies) @@ -44,45 +45,47 @@ protected AssemblyStoreExplorer (Stream storeStream, string path, DebugLogger? l dict.Add(item.Name, item); } } - AssembliesByName = dict.AsReadOnly (); - } + AssembliesByName = dict.AsReadOnly(); + } - protected AssemblyStoreExplorer (FileInfo storeInfo, DebugLogger? logger) - : this (storeInfo.OpenRead (), storeInfo.FullName, logger) - {} + protected AssemblyStoreExplorer(FileInfo storeInfo, DebugLogger? logger) + : this(storeInfo.OpenRead(), storeInfo.FullName, logger) + { } - public static (IList? explorers, string? errorMessage) Open (string inputFile, DebugLogger? logger) - { - (FileFormat format, FileInfo? info) = Utils.DetectFileFormat (inputFile); - if (info == null) { - return (null, $"File '{inputFile}' does not exist."); - } + public static (IList? explorers, string? errorMessage) Open(string inputFile, DebugLogger? logger) + { + (FileFormat format, FileInfo? info) = Utils.DetectFileFormat(inputFile); + if (info == null) + { + return (null, $"File '{inputFile}' does not exist."); + } - switch (format) { - case FileFormat.Unknown: - return (null, $"File '{inputFile}' has an unknown format."); + switch (format) + { + case FileFormat.Unknown: + return (null, $"File '{inputFile}' has an unknown format."); - case FileFormat.Zip: - return (null, $"File '{inputFile}' is a ZIP archive, but not an Android one."); + case FileFormat.Zip: + return (null, $"File '{inputFile}' is a ZIP archive, but not an Android one."); - case FileFormat.AssemblyStore: - case FileFormat.ELF: - return (new List { new AssemblyStoreExplorer (info, logger)}, null); + case FileFormat.AssemblyStore: + case FileFormat.ELF: + return (new List { new AssemblyStoreExplorer(info, logger) }, null); - case FileFormat.Aab: - return OpenAab (info, logger); + case FileFormat.Aab: + return OpenAab(info, logger); - case FileFormat.AabBase: - return OpenAabBase (info, logger); + case FileFormat.AabBase: + return OpenAabBase(info, logger); - case FileFormat.Apk: - return OpenApk (info, logger); + case FileFormat.Apk: + return OpenApk(info, logger); - default: - return (null, $"File '{inputFile}' has an unsupported format '{format}'"); - } - } + default: + return (null, $"File '{inputFile}' has an unsupported format '{format}'"); +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) { return OpenCommon ( @@ -137,82 +140,211 @@ public static (IList? explorers, string? errorMessage) Op } static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) +After: + private static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) { - var ret = new List (); - - foreach (string path in paths) { - if (!zip.ContainsEntry (path)) { - continue; - } - - var entry = zip.ReadEntry (path); - var stream = new MemoryStream (); - entry.Extract (stream); - ret.Add (new AssemblyStoreExplorer (stream, $"{fi.FullName}!{path}", logger)); - } - - if (ret.Count == 0) { - return (null, null, false); - } + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabPaths, + StoreReader_V1.AabPaths, + }, + logger + ); + } - return (ret, null, true); + private static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabBasePaths, + StoreReader_V1.AabBasePaths, + }, + logger + ); } - public MemoryStream? ReadImageData (AssemblyStoreItem item, bool uncompressIfNeeded = false) + private static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) { - return reader.ReadEntryImageData (item, uncompressIfNeeded); + return OpenCommon ( + fi, + new List> { + StoreReader_V2.ApkPaths, + StoreReader_V1.ApkPaths, + }, + logger + ); } - private string EnsureCorrectAssemblyName (string assemblyName) + private static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) { - assemblyName = Path.GetFileName (assemblyName); - if (reader.NeedsExtensionInName) { - if (!assemblyName.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)) { - return $"{assemblyName}.dll"; - } - } else { - if (assemblyName.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)) { - return Path.GetFileNameWithoutExtension (assemblyName); + using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); + IList? explorers; + string? errorMessage; + bool pathsFound; + + foreach (IList paths in pathLists) { + (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); + if (pathsFound) { + return (explorers, errorMessage); } } - return assemblyName; + return (null, "Unable to find any blob entries"); } - public IList? Find (string assemblyName, AndroidTargetArch? targetArch = null) - { - if (Assemblies == null) { - return null; - } + private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) +*/ + } + } + + private static (IList? explorers, string? errorMessage) OpenAab(FileInfo fi, DebugLogger? logger) + { + return OpenCommon( + fi, + new List> { + StoreReader_V2.AabPaths, + StoreReader_V1.AabPaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenAabBase(FileInfo fi, DebugLogger? logger) + { + return OpenCommon( + fi, + new List> { + StoreReader_V2.AabBasePaths, + StoreReader_V1.AabBasePaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenApk(FileInfo fi, DebugLogger? logger) + { + return OpenCommon( + fi, + new List> { + StoreReader_V2.ApkPaths, + StoreReader_V1.ApkPaths, + }, + logger + ); + } - assemblyName = EnsureCorrectAssemblyName (assemblyName); - var items = new List (); - foreach (AssemblyStoreItem item in Assemblies) { - if (String.CompareOrdinal (assemblyName, item.Name) != 0) { - continue; - } + private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, List> pathLists, DebugLogger? logger) + { + using var zip = ZipArchive.Open(fi.FullName, FileMode.Open); + IList? explorers; + string? errorMessage; + bool pathsFound; - if (targetArch != null && item.TargetArch != targetArch) { - continue; - } + foreach (IList paths in pathLists) + { + (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger); + if (pathsFound) + { + return (explorers, errorMessage); + } + } - items.Add (item); - } + return (null, "Unable to find any blob entries"); + } - if (items.Count == 0) { - return null; - } + private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad(FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) + { + var ret = new List(); - return items; - } + foreach (string path in paths) + { + if (!zip.ContainsEntry(path)) + { + continue; + } - public bool Contains (string assemblyName, AndroidTargetArch? targetArch = null) - { - IList? items = Find (assemblyName, targetArch); - if (items == null || items.Count == 0) { - return false; - } + var entry = zip.ReadEntry(path); + var stream = new MemoryStream(); + entry.Extract(stream); + ret.Add(new AssemblyStoreExplorer(stream, $"{fi.FullName}!{path}", logger)); + } - return true; - } + if (ret.Count == 0) + { + return (null, null, false); + } + + return (ret, null, true); + } + + public MemoryStream? ReadImageData(AssemblyStoreItem item, bool uncompressIfNeeded = false) + { + return reader.ReadEntryImageData(item, uncompressIfNeeded); + } + + private string EnsureCorrectAssemblyName(string assemblyName) + { + assemblyName = Path.GetFileName(assemblyName); + if (reader.NeedsExtensionInName) + { + if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + return $"{assemblyName}.dll"; + } + } + else + { + if (assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + return Path.GetFileNameWithoutExtension(assemblyName); + } + } + + return assemblyName; + } + + public IList? Find(string assemblyName, AndroidTargetArch? targetArch = null) + { + if (Assemblies == null) + { + return null; + } + + assemblyName = EnsureCorrectAssemblyName(assemblyName); + var items = new List(); + foreach (AssemblyStoreItem item in Assemblies) + { + if (string.CompareOrdinal(assemblyName, item.Name) != 0) + { + continue; + } + + if (targetArch != null && item.TargetArch != targetArch) + { + continue; + } + + items.Add(item); + } + + if (items.Count == 0) + { + return null; + } + + return items; + } + + public bool Contains(string assemblyName, AndroidTargetArch? targetArch = null) + { + IList? items = Find(assemblyName, targetArch); + if (items == null || items.Count == 0) + { + return false; + } + + return true; + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs index dff1ff71d6..a132f51ead 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreItem.cs @@ -7,21 +7,21 @@ namespace Sentry.Android.AssemblyReader.V2; internal abstract class AssemblyStoreItem { - public string Name { get; } - public IList Hashes { get; } - public bool Is64Bit { get; } - public uint DataOffset { get; protected set; } - public uint DataSize { get; protected set; } - public uint DebugOffset { get; protected set; } - public uint DebugSize { get; protected set; } - public uint ConfigOffset { get; protected set; } - public uint ConfigSize { get; protected set; } - public AndroidTargetArch TargetArch { get; protected set; } + public string Name { get; } + public IList Hashes { get; } + public bool Is64Bit { get; } + public uint DataOffset { get; protected set; } + public uint DataSize { get; protected set; } + public uint DebugOffset { get; protected set; } + public uint DebugSize { get; protected set; } + public uint ConfigOffset { get; protected set; } + public uint ConfigSize { get; protected set; } + public AndroidTargetArch TargetArch { get; protected set; } - protected AssemblyStoreItem (string name, bool is64Bit, List hashes) - { - Name = name; - Hashes = hashes.AsReadOnly (); - Is64Bit = is64Bit; - } + protected AssemblyStoreItem(string name, bool is64Bit, List hashes) + { + Name = name; + Hashes = hashes.AsReadOnly(); + Is64Bit = is64Bit; + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs index 392e71a434..87c08a44f1 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs @@ -9,80 +9,92 @@ internal abstract class AssemblyStoreReader { protected DebugLogger? Logger { get; } - static readonly UTF8Encoding ReaderEncoding = new UTF8Encoding (false); + private static readonly UTF8Encoding ReaderEncoding = new UTF8Encoding(false); - protected Stream StoreStream { get; } + protected Stream StoreStream { get; } - public abstract string Description { get; } - public abstract bool NeedsExtensionInName { get; } - public string StorePath { get; } + public abstract string Description { get; } + public abstract bool NeedsExtensionInName { get; } + public string StorePath { get; } - public AndroidTargetArch TargetArch { get; protected set; } = AndroidTargetArch.Arm; - public uint AssemblyCount { get; protected set; } - public uint IndexEntryCount { get; protected set; } - public IList? Assemblies { get; protected set; } - public bool Is64Bit { get; protected set; } + public AndroidTargetArch TargetArch { get; protected set; } = AndroidTargetArch.Arm; + public uint AssemblyCount { get; protected set; } + public uint IndexEntryCount { get; protected set; } + public IList? Assemblies { get; protected set; } + public bool Is64Bit { get; protected set; } - protected AssemblyStoreReader (Stream store, string path, DebugLogger? logger) - { - StoreStream = store; - StorePath = path; + protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger) + { + StoreStream = store; + StorePath = path; Logger = logger; - } - - public static AssemblyStoreReader? Create (Stream store, string path, DebugLogger? logger) - { - AssemblyStoreReader? reader = MakeReaderReady (new StoreReader_V1 (store, path, logger)); - if (reader != null) { - return reader; - } - - reader = MakeReaderReady (new StoreReader_V2 (store, path, logger)); - if (reader != null) { - return reader; - } - - return null; - } - + } + + public static AssemblyStoreReader? Create(Stream store, string path, DebugLogger? logger) + { + AssemblyStoreReader? reader = MakeReaderReady(new StoreReader_V1(store, path, logger)); + if (reader != null) + { + return reader; + } + + reader = MakeReaderReady(new StoreReader_V2(store, path, logger)); + if (reader != null) + { + return reader; + } + + return null; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) - { - if (!reader.IsSupported ()) { - return null; - } - - reader.Prepare (); - return reader; - } - - protected BinaryReader CreateReader () => new BinaryReader (StoreStream, ReaderEncoding, leaveOpen: true); - - protected abstract bool IsSupported (); - protected abstract void Prepare (); - protected abstract ulong GetStoreStartDataOffset (); - - public MemoryStream ReadEntryImageData (AssemblyStoreItem entry, bool uncompressIfNeeded = false) - { - ulong startOffset = GetStoreStartDataOffset (); - StoreStream.Seek ((uint)startOffset + entry.DataOffset, SeekOrigin.Begin); - var stream = new MemoryStream (); - - if (uncompressIfNeeded) { - throw new NotImplementedException (); - } - - const long BufferSize = 65535; - byte[] buffer = Utils.BytePool.Rent ((int)BufferSize); - long remainingToRead = entry.DataSize; - - while (remainingToRead > 0) { - int nread = StoreStream.Read (buffer, 0, (int)Math.Min (BufferSize, remainingToRead)); - stream.Write (buffer, 0, nread); - remainingToRead -= (long)nread; - } - stream.Flush (); - stream.Seek (0, SeekOrigin.Begin); - - return stream; - } +After: + private static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) +*/ + } + + private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader) + { + if (!reader.IsSupported()) + { + return null; + } + + reader.Prepare(); + return reader; + } + + protected BinaryReader CreateReader() => new BinaryReader(StoreStream, ReaderEncoding, leaveOpen: true); + + protected abstract bool IsSupported(); + protected abstract void Prepare(); + protected abstract ulong GetStoreStartDataOffset(); + + public MemoryStream ReadEntryImageData(AssemblyStoreItem entry, bool uncompressIfNeeded = false) + { + ulong startOffset = GetStoreStartDataOffset(); + StoreStream.Seek((uint)startOffset + entry.DataOffset, SeekOrigin.Begin); + var stream = new MemoryStream(); + + if (uncompressIfNeeded) + { + throw new NotImplementedException(); + } + + const long BufferSize = 65535; + byte[] buffer = Utils.BytePool.Rent((int)BufferSize); + long remainingToRead = entry.DataSize; + + while (remainingToRead > 0) + { + int nread = StoreStream.Read(buffer, 0, (int)Math.Min(BufferSize, remainingToRead)); + stream.Write(buffer, 0, nread); + remainingToRead -= (long)nread; + } + stream.Flush(); + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs b/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs index 28122d14c0..b7644d68af 100644 --- a/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs +++ b/src/Sentry.Android.AssemblyReader/V2/ELFPayloadError.cs @@ -7,10 +7,10 @@ namespace Sentry.Android.AssemblyReader.V2; internal enum ELFPayloadError { - None, - NotELF, - LoadFailed, - NotSharedLibrary, - NotLittleEndian, - NoPayloadSection, + None, + NotELF, + LoadFailed, + NotSharedLibrary, + NotLittleEndian, + NoPayloadSection, } diff --git a/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs b/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs index 9d8c2aa7bc..d1cef2ad05 100644 --- a/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs +++ b/src/Sentry.Android.AssemblyReader/V2/FileFormat.cs @@ -7,11 +7,11 @@ namespace Sentry.Android.AssemblyReader.V2; internal enum FileFormat { - Aab, - AabBase, - Apk, - AssemblyStore, - ELF, - Zip, - Unknown, + Aab, + AabBase, + Apk, + AssemblyStore, + ELF, + Zip, + Unknown, } diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs index acf5abe5b0..5c5d472e10 100644 --- a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -9,235 +9,330 @@ namespace Sentry.Android.AssemblyReader.V2; internal partial class MonoAndroidHelper { - public static class AndroidAbi - { - public const string Arm32 = "armeabi-v7a"; - public const string Arm64 = "arm64-v8a"; - public const string X86 = "x86"; - public const string X64 = "x86_64"; - } - - public static class RuntimeIdentifier - { - public const string Arm32 = "android-arm"; - public const string Arm64 = "android-arm64"; - public const string X86 = "android-x86"; - public const string X64 = "android-x64"; - } - - public static readonly HashSet SupportedTargetArchitectures = new HashSet { - AndroidTargetArch.Arm, - AndroidTargetArch.Arm64, - AndroidTargetArch.X86, - AndroidTargetArch.X86_64, - }; - + public static class AndroidAbi + { + public const string Arm32 = "armeabi-v7a"; + public const string Arm64 = "arm64-v8a"; + public const string X86 = "x86"; + public const string X64 = "x86_64"; + } + + public static class RuntimeIdentifier + { + public const string Arm32 = "android-arm"; + public const string Arm64 = "android-arm64"; + public const string X86 = "android-x86"; + public const string X64 = "android-x64"; + } + + public static readonly HashSet SupportedTargetArchitectures = new HashSet { + AndroidTargetArch.Arm, + AndroidTargetArch.Arm64, + AndroidTargetArch.X86, + AndroidTargetArch.X86_64, + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly char[] ZipPathTrimmedChars = {'/', '\\'}; static readonly Dictionary ClangAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { - {"arm64-v8a", "aarch64"}, - {"armeabi-v7a", "arm"}, - {"x86", "i686"}, - {"x86_64", "x86_64"} - }; +After: + private static readonly char[] ZipPathTrimmedChars = {'/', '\\'}; + private static readonly Dictionary ClangAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly Dictionary AbiToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { - { AndroidAbi.Arm32, AndroidTargetArch.Arm }, - { AndroidAbi.Arm64, AndroidTargetArch.Arm64 }, - { AndroidAbi.X86, AndroidTargetArch.X86 }, - { AndroidAbi.X64, AndroidTargetArch.X86_64 }, - }; +After: + private static readonly Dictionary AbiToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly Dictionary AbiToRidMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { - { AndroidAbi.Arm32, RuntimeIdentifier.Arm32 }, - { AndroidAbi.Arm64, RuntimeIdentifier.Arm64 }, - { AndroidAbi.X86, RuntimeIdentifier.X86 }, - { AndroidAbi.X64, RuntimeIdentifier.X64 }, - }; +After: + private static readonly Dictionary AbiToRidMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly Dictionary RidToAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { - { RuntimeIdentifier.Arm32, AndroidAbi.Arm32 }, - { RuntimeIdentifier.Arm64, AndroidAbi.Arm64 }, - { RuntimeIdentifier.X86, AndroidAbi.X86 }, - { RuntimeIdentifier.X64, AndroidAbi.X64 }, - }; +After: + private static readonly Dictionary RidToAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly Dictionary RidToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { - { RuntimeIdentifier.Arm32, AndroidTargetArch.Arm }, - { RuntimeIdentifier.Arm64, AndroidTargetArch.Arm64 }, - { RuntimeIdentifier.X86, AndroidTargetArch.X86 }, - { RuntimeIdentifier.X64, AndroidTargetArch.X86_64 }, - }; +After: + private static readonly Dictionary RidToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly Dictionary ArchToRidMap = new Dictionary { - { AndroidTargetArch.Arm, RuntimeIdentifier.Arm32 }, - { AndroidTargetArch.Arm64, RuntimeIdentifier.Arm64 }, - { AndroidTargetArch.X86, RuntimeIdentifier.X86 }, - { AndroidTargetArch.X86_64, RuntimeIdentifier.X64 }, - }; +After: + private static readonly Dictionary ArchToRidMap = new Dictionary { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly Dictionary ArchToAbiMap = new Dictionary { - { AndroidTargetArch.Arm, AndroidAbi.Arm32 }, - { AndroidTargetArch.Arm64, AndroidAbi.Arm64 }, - { AndroidTargetArch.X86, AndroidAbi.X86 }, - { AndroidTargetArch.X86_64, AndroidAbi.X64 }, - }; - - public static AndroidTargetArch AbiToTargetArch (string abi) - { - if (!AbiToArchMap.TryGetValue (abi, out AndroidTargetArch arch)) { - throw new NotSupportedException ($"Internal error: unsupported ABI '{abi}'"); - }; - - return arch; - } - - public static string AbiToRid (string abi) - { - if (!AbiToRidMap.TryGetValue (abi, out var rid)) { - throw new NotSupportedException ($"Internal error: unsupported ABI '{abi}'"); - }; - - return rid; - } - - public static string RidToAbi (string rid) - { - if (!RidToAbiMap.TryGetValue (rid, out var abi)) { - throw new NotSupportedException ($"Internal error: unsupported Runtime Identifier '{rid}'"); - }; - - return abi; - } - - public static AndroidTargetArch RidToArchMaybe (string rid) - { - if (!RidToArchMap.TryGetValue (rid, out AndroidTargetArch arch)) { - return AndroidTargetArch.None; - }; - - return arch; - } - - public static AndroidTargetArch RidToArch (string rid) - { - AndroidTargetArch arch = RidToArchMaybe (rid); - if (arch == AndroidTargetArch.None) { - throw new NotSupportedException ($"Internal error: unsupported Runtime Identifier '{rid}'"); - }; - - return arch; - } - - public static string ArchToRid (AndroidTargetArch arch) - { - if (!ArchToRidMap.TryGetValue (arch, out var rid)) { - throw new InvalidOperationException ($"Internal error: unsupported architecture '{arch}'"); - }; - - return rid; - } - - public static string ArchToAbi (AndroidTargetArch arch) - { - if (!ArchToAbiMap.TryGetValue (arch, out var abi)) { - throw new InvalidOperationException ($"Internal error: unsupported architecture '{arch}'"); - }; - - return abi; - } - - public static bool IsValidAbi (string abi) => AbiToRidMap.ContainsKey (abi); - public static bool IsValidRID (string rid) => RidToAbiMap.ContainsKey (rid); - - public static string? CultureInvariantToString (object? obj) - { - if (obj == null) { - return null; - } - - return Convert.ToString (obj, CultureInfo.InvariantCulture); - } - - public static string? MapAndroidAbiToClang (string androidAbi) - { - if (ClangAbiMap.TryGetValue (androidAbi, out var clangAbi)) { - return clangAbi; - } - return null; - } - - public static string MakeZipArchivePath (string part1, params string[]? pathParts) - { - return MakeZipArchivePath (part1, (ICollection?)pathParts); - } - - public static string MakeZipArchivePath (string part1, ICollection? pathParts) - { - var parts = new List (); +After: + private static readonly Dictionary ArchToAbiMap = new Dictionary { +*/ + }; + private static readonly char[] ZipPathTrimmedChars = { '/', '\\' }; + private static readonly Dictionary ClangAbiMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { + {"arm64-v8a", "aarch64"}, + {"armeabi-v7a", "arm"}, + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: if (!String.IsNullOrEmpty (part1)) { - parts.Add (part1.TrimEnd (ZipPathTrimmedChars)); - }; +After: + if (!string.IsNullOrEmpty (part1)) { +*/ - if (pathParts != null && pathParts.Count > 0) { - foreach (string p in pathParts) { +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: if (String.IsNullOrEmpty (p)) { - continue; - } - parts.Add (p.TrimEnd (ZipPathTrimmedChars)); - } - } +After: + if (string.IsNullOrEmpty (p)) { +*/ - if (parts.Count == 0) { +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: return String.Empty; - } +After: + return string.Empty; +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: return String.Join ("/", parts); - } - - // These 3 MUST be the same as the like-named constants in src/monodroid/jni/shared-constants.hh - public const string MANGLED_ASSEMBLY_NAME_EXT = ".so"; - public const string MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER = "lib_"; - public const string MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER = "lib-"; - public const string SATELLITE_CULTURE_END_MARKER_CHAR = "_"; - - /// - /// Mangles APK/AAB entry name for assembly and their associated pdb and config entries in the - /// way expected by our native runtime. Must **NOT** be used to mangle names when assembly stores - /// are used. Must **NOT** be used for entries other than assemblies and their associated files. - /// - public static string MakeDiscreteAssembliesEntryName (string name, string? culture = null) - { +After: + return string.Join ("/", parts); +*/ + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: if (!String.IsNullOrEmpty (culture)) { - return $"{MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER}{culture}_{name}{MANGLED_ASSEMBLY_NAME_EXT}"; - } - - return $"{MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER}{name}{MANGLED_ASSEMBLY_NAME_EXT}"; - } - - /// - /// Returns size of the extension + length of the prefix for mangled assembly names. This is - /// used to pre-allocate space for assembly names in `libxamarin-app.so` - /// - /// - public static ulong GetMangledAssemblyNameSizeOverhead () - { - // Satellite marker is one character more, for the `-` closing the culture part - return (ulong)MANGLED_ASSEMBLY_NAME_EXT.Length + - (ulong)Math.Max (MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER.Length + 1, MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER.Length); - } - - public static byte[] Utf8StringToBytes (string str) => Encoding.UTF8.GetBytes (str); - public static byte[] Utf16StringToBytes (string str) => Encoding.Unicode.GetBytes (str); - - public static ulong GetXxHash (string str, bool is64Bit) => GetXxHash (Utf8StringToBytes (str), is64Bit); - - public static ulong GetXxHash (byte[] stringBytes, bool is64Bit) - { - if (is64Bit) { - return XxHash3.HashToUInt64 (stringBytes); - } - - return (ulong)XxHash32.HashToUInt32 (stringBytes); - } +After: + if (!string.IsNullOrEmpty (culture)) { +*/ + {"x86", "i686"}, + {"x86_64", "x86_64"} + }; + private static readonly Dictionary AbiToArchMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { AndroidAbi.Arm32, AndroidTargetArch.Arm }, + { AndroidAbi.Arm64, AndroidTargetArch.Arm64 }, + { AndroidAbi.X86, AndroidTargetArch.X86 }, + { AndroidAbi.X64, AndroidTargetArch.X86_64 }, + }; + private static readonly Dictionary AbiToRidMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { AndroidAbi.Arm32, RuntimeIdentifier.Arm32 }, + { AndroidAbi.Arm64, RuntimeIdentifier.Arm64 }, + { AndroidAbi.X86, RuntimeIdentifier.X86 }, + { AndroidAbi.X64, RuntimeIdentifier.X64 }, + }; + private static readonly Dictionary RidToAbiMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { RuntimeIdentifier.Arm32, AndroidAbi.Arm32 }, + { RuntimeIdentifier.Arm64, AndroidAbi.Arm64 }, + { RuntimeIdentifier.X86, AndroidAbi.X86 }, + { RuntimeIdentifier.X64, AndroidAbi.X64 }, + }; + private static readonly Dictionary RidToArchMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { RuntimeIdentifier.Arm32, AndroidTargetArch.Arm }, + { RuntimeIdentifier.Arm64, AndroidTargetArch.Arm64 }, + { RuntimeIdentifier.X86, AndroidTargetArch.X86 }, + { RuntimeIdentifier.X64, AndroidTargetArch.X86_64 }, + }; + private static readonly Dictionary ArchToRidMap = new Dictionary { + { AndroidTargetArch.Arm, RuntimeIdentifier.Arm32 }, + { AndroidTargetArch.Arm64, RuntimeIdentifier.Arm64 }, + { AndroidTargetArch.X86, RuntimeIdentifier.X86 }, + { AndroidTargetArch.X86_64, RuntimeIdentifier.X64 }, + }; + private static readonly Dictionary ArchToAbiMap = new Dictionary { + { AndroidTargetArch.Arm, AndroidAbi.Arm32 }, + { AndroidTargetArch.Arm64, AndroidAbi.Arm64 }, + { AndroidTargetArch.X86, AndroidAbi.X86 }, + { AndroidTargetArch.X86_64, AndroidAbi.X64 }, + }; + + public static AndroidTargetArch AbiToTargetArch(string abi) + { + if (!AbiToArchMap.TryGetValue(abi, out AndroidTargetArch arch)) + { + throw new NotSupportedException($"Internal error: unsupported ABI '{abi}'"); + }; + + return arch; + } + + public static string AbiToRid(string abi) + { + if (!AbiToRidMap.TryGetValue(abi, out var rid)) + { + throw new NotSupportedException($"Internal error: unsupported ABI '{abi}'"); + }; + + return rid; + } + + public static string RidToAbi(string rid) + { + if (!RidToAbiMap.TryGetValue(rid, out var abi)) + { + throw new NotSupportedException($"Internal error: unsupported Runtime Identifier '{rid}'"); + }; + + return abi; + } + + public static AndroidTargetArch RidToArchMaybe(string rid) + { + if (!RidToArchMap.TryGetValue(rid, out AndroidTargetArch arch)) + { + return AndroidTargetArch.None; + }; + + return arch; + } + + public static AndroidTargetArch RidToArch(string rid) + { + AndroidTargetArch arch = RidToArchMaybe(rid); + if (arch == AndroidTargetArch.None) + { + throw new NotSupportedException($"Internal error: unsupported Runtime Identifier '{rid}'"); + }; + + return arch; + } + + public static string ArchToRid(AndroidTargetArch arch) + { + if (!ArchToRidMap.TryGetValue(arch, out var rid)) + { + throw new InvalidOperationException($"Internal error: unsupported architecture '{arch}'"); + }; + + return rid; + } + + public static string ArchToAbi(AndroidTargetArch arch) + { + if (!ArchToAbiMap.TryGetValue(arch, out var abi)) + { + throw new InvalidOperationException($"Internal error: unsupported architecture '{arch}'"); + }; + + return abi; + } + + public static bool IsValidAbi(string abi) => AbiToRidMap.ContainsKey(abi); + public static bool IsValidRID(string rid) => RidToAbiMap.ContainsKey(rid); + + public static string? CultureInvariantToString(object? obj) + { + if (obj == null) + { + return null; + } + + return Convert.ToString(obj, CultureInfo.InvariantCulture); + } + + public static string? MapAndroidAbiToClang(string androidAbi) + { + if (ClangAbiMap.TryGetValue(androidAbi, out var clangAbi)) + { + return clangAbi; + } + return null; + } + + public static string MakeZipArchivePath(string part1, params string[]? pathParts) + { + return MakeZipArchivePath(part1, (ICollection?)pathParts); + } + + public static string MakeZipArchivePath(string part1, ICollection? pathParts) + { + var parts = new List(); + if (!string.IsNullOrEmpty(part1)) + { + parts.Add(part1.TrimEnd(ZipPathTrimmedChars)); + }; + + if (pathParts != null && pathParts.Count > 0) + { + foreach (string p in pathParts) + { + if (string.IsNullOrEmpty(p)) + { + continue; + } + parts.Add(p.TrimEnd(ZipPathTrimmedChars)); + } + } + + if (parts.Count == 0) + { + return string.Empty; + } + + return string.Join("/", parts); + } + + // These 3 MUST be the same as the like-named constants in src/monodroid/jni/shared-constants.hh + public const string MANGLED_ASSEMBLY_NAME_EXT = ".so"; + public const string MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER = "lib_"; + public const string MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER = "lib-"; + public const string SATELLITE_CULTURE_END_MARKER_CHAR = "_"; + + /// + /// Mangles APK/AAB entry name for assembly and their associated pdb and config entries in the + /// way expected by our native runtime. Must **NOT** be used to mangle names when assembly stores + /// are used. Must **NOT** be used for entries other than assemblies and their associated files. + /// + public static string MakeDiscreteAssembliesEntryName(string name, string? culture = null) + { + if (!string.IsNullOrEmpty(culture)) + { + return $"{MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER}{culture}_{name}{MANGLED_ASSEMBLY_NAME_EXT}"; + } + + return $"{MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER}{name}{MANGLED_ASSEMBLY_NAME_EXT}"; + } + + /// + /// Returns size of the extension + length of the prefix for mangled assembly names. This is + /// used to pre-allocate space for assembly names in `libxamarin-app.so` + /// + /// + public static ulong GetMangledAssemblyNameSizeOverhead() + { + // Satellite marker is one character more, for the `-` closing the culture part + return (ulong)MANGLED_ASSEMBLY_NAME_EXT.Length + + (ulong)Math.Max(MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER.Length + 1, MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER.Length); + } + + public static byte[] Utf8StringToBytes(string str) => Encoding.UTF8.GetBytes(str); + public static byte[] Utf16StringToBytes(string str) => Encoding.Unicode.GetBytes(str); + + public static ulong GetXxHash(string str, bool is64Bit) => GetXxHash(Utf8StringToBytes(str), is64Bit); + + public static ulong GetXxHash(byte[] stringBytes, bool is64Bit) + { + if (is64Bit) + { + return XxHash3.HashToUInt64(stringBytes); + } + + return (ulong)XxHash32.HashToUInt32(stringBytes); + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V1.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V1.cs index 49e8bb417a..c1a3fe3c2d 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V1.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V1.cs @@ -7,32 +7,32 @@ 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; + 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/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs index c54e08953d..2f66fbcf2e 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs @@ -7,89 +7,125 @@ namespace Sentry.Android.AssemblyReader.V2; internal partial class StoreReader_V2 { - sealed class Header - { - public const uint NativeSize = 5 * sizeof (uint); - - public readonly uint magic; - public readonly uint version; - public readonly uint entry_count; - public readonly uint index_entry_count; - - // Index size in bytes - public readonly uint index_size; - - 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; - } - } - + private sealed class Header + { + public const uint NativeSize = 5 * sizeof(uint); + + public readonly uint magic; + public readonly uint version; + public readonly uint entry_count; + public readonly uint index_entry_count; + + // Index size in bytes + public readonly uint index_size; + + 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; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: sealed class IndexEntry - { - public readonly ulong name_hash; - public readonly uint descriptor_index; - - public IndexEntry (ulong name_hash, uint descriptor_index) - { - this.name_hash = name_hash; - this.descriptor_index = descriptor_index; - } - } - +After: + private sealed class IndexEntry +*/ + } + } + + private sealed class IndexEntry + { + public readonly ulong name_hash; + public readonly uint descriptor_index; + + public IndexEntry(ulong name_hash, uint descriptor_index) + { + this.name_hash = name_hash; + this.descriptor_index = descriptor_index; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: sealed class EntryDescriptor - { - public uint mapping_index; +After: + private sealed class EntryDescriptor +*/ + } + } - public uint data_offset; - public uint data_size; + private sealed class EntryDescriptor + { + public uint mapping_index; - public uint debug_data_offset; - public uint debug_data_size; + public uint data_offset; + public uint data_size; - public uint config_data_offset; - public uint config_data_size; - } + public uint debug_data_offset; + public uint debug_data_size; - sealed class StoreItem_V2 : AssemblyStoreItem - { - public StoreItem_V2 (AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor) - : base (name, is64Bit, IndexToHashes (indexEntries)) - { - DataOffset = descriptor.data_offset; - DataSize = descriptor.data_size; - DebugOffset = descriptor.debug_data_offset; - DebugSize = descriptor.debug_data_size; - ConfigOffset = descriptor.config_data_offset; - ConfigSize = descriptor.config_data_size; - TargetArch = targetArch; - } + public uint config_data_offset; + public uint config_data_size; +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + sealed class StoreItem_V2 : AssemblyStoreItem +After: + private sealed class StoreItem_V2 : AssemblyStoreItem +*/ + } + + private sealed class StoreItem_V2 : AssemblyStoreItem + { + public StoreItem_V2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor) + : base(name, is64Bit, IndexToHashes(indexEntries)) + { + DataOffset = descriptor.data_offset; + DataSize = descriptor.data_size; + DebugOffset = descriptor.debug_data_offset; + DebugSize = descriptor.debug_data_size; + ConfigOffset = descriptor.config_data_offset; + ConfigSize = descriptor.config_data_size; + TargetArch = targetArch; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static List IndexToHashes (List indexEntries) - { - var ret = new List (); - foreach (IndexEntry ie in indexEntries) { - ret.Add (ie.name_hash); - } - - return ret; - } - } - +After: + private static List IndexToHashes (List indexEntries) +*/ + } + + private static List IndexToHashes(List indexEntries) + { + var ret = new List(); + foreach (IndexEntry ie in indexEntries) + { + ret.Add(ie.name_hash); + } + + return ret; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: sealed class TemporaryItem - { - public readonly string Name; - public readonly List IndexEntries = new List (); - public readonly EntryDescriptor Descriptor; - - public TemporaryItem (string name, EntryDescriptor descriptor) - { - Name = name; - Descriptor = descriptor; - } - } +After: + private sealed class TemporaryItem +*/ + } + } + + private sealed class TemporaryItem + { + public readonly string Name; + public readonly List IndexEntries = new List(); + public readonly EntryDescriptor Descriptor; + + public TemporaryItem(string name, EntryDescriptor descriptor) + { + Name = name; + Descriptor = descriptor; + } + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs index 9805102216..d22e74dad4 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs @@ -7,6 +7,9 @@ namespace Sentry.Android.AssemblyReader.V2; internal partial class StoreReader_V2 : AssemblyStoreReader { + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; @@ -17,204 +20,262 @@ internal partial class StoreReader_V2 : AssemblyStoreReader const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; - - public override string Description => "Assembly store v2"; - public override bool NeedsExtensionInName => true; - - public static IList ApkPaths { get; } - public static IList AabPaths { get; } - public static IList AabBasePaths { get; } - - readonly HashSet supportedVersions; - +After: + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + 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; + 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; + private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; +*/ + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + 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; + 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; + private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; + + public override string Description => "Assembly store v2"; + public override bool NeedsExtensionInName => true; + + public static IList ApkPaths { get; } + public static IList AabPaths { get; } + public static IList AabBasePaths { get; } + + private readonly HashSet supportedVersions; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: Header? header; ulong elfOffset = 0; - - static StoreReader_V2 () - { - var paths = new List { - GetArchPath (AndroidTargetArch.Arm64), - GetArchPath (AndroidTargetArch.Arm), - GetArchPath (AndroidTargetArch.X86_64), - GetArchPath (AndroidTargetArch.X86), - }; - ApkPaths = paths.AsReadOnly (); - AabBasePaths = ApkPaths; - - const string AabBaseDir = "base"; - paths = new List { - GetArchPath (AndroidTargetArch.Arm64, AabBaseDir), - GetArchPath (AndroidTargetArch.Arm, AabBaseDir), - GetArchPath (AndroidTargetArch.X86_64, AabBaseDir), - GetArchPath (AndroidTargetArch.X86, AabBaseDir), - }; - AabPaths = paths.AsReadOnly (); - - string GetArchPath (AndroidTargetArch arch, string? root = null) - { - const string LibDirName = "lib"; - - string abi = MonoAndroidHelper.ArchToAbi (arch); - var parts = new List (); - if (!String.IsNullOrEmpty (root)) { - parts.Add (LibDirName); - } else { - root = LibDirName; - } - parts.Add (abi); - parts.Add (GetBlobName (abi)); - - return MonoAndroidHelper.MakeZipArchivePath (root, parts); - } - } - - public StoreReader_V2 (Stream store, string path, DebugLogger? logger) - : base (store, path, logger) - { - supportedVersions = new HashSet { - ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_AARCH64, - ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_X64, - ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_ARM, - ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, - }; - } - +After: + private Header? header; + private ulong elfOffset = 0; +*/ + private Header? header; + private ulong elfOffset = 0; + + static StoreReader_V2() + { + var paths = new List { + GetArchPath (AndroidTargetArch.Arm64), + GetArchPath (AndroidTargetArch.Arm), + GetArchPath (AndroidTargetArch.X86_64), + GetArchPath (AndroidTargetArch.X86), + }; + ApkPaths = paths.AsReadOnly(); + AabBasePaths = ApkPaths; + + const string AabBaseDir = "base"; + paths = new List { + GetArchPath (AndroidTargetArch.Arm64, AabBaseDir), + GetArchPath (AndroidTargetArch.Arm, AabBaseDir), + GetArchPath (AndroidTargetArch.X86_64, AabBaseDir), + GetArchPath (AndroidTargetArch.X86, AabBaseDir), + }; + AabPaths = paths.AsReadOnly(); + + string GetArchPath(AndroidTargetArch arch, string? root = null) + { + const string LibDirName = "lib"; + + string abi = MonoAndroidHelper.ArchToAbi(arch); + var parts = new List(); + if (!string.IsNullOrEmpty(root)) + { + parts.Add(LibDirName); + } + else + { + root = LibDirName; + } + parts.Add(abi); + parts.Add(GetBlobName(abi)); + + return MonoAndroidHelper.MakeZipArchivePath(root, parts); + } + } + + public StoreReader_V2(Stream store, string path, DebugLogger? logger) + : base(store, path, logger) + { + supportedVersions = new HashSet { + ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_AARCH64, + ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_X64, + ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_ARM, + ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, + }; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; - - protected override ulong GetStoreStartDataOffset () => elfOffset; - - protected override bool IsSupported () - { - StoreStream.Seek (0, SeekOrigin.Begin); - using var reader = CreateReader (); - - uint magic = reader.ReadUInt32 (); - if (magic == Utils.ELF_MAGIC) { - ELFPayloadError error; - (elfOffset, _, error) = Utils.FindELFPayloadSectionOffsetAndSize (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}" - }; - Logger?.Invoke(message); - // Was originally: - // ``` - // } else if (elfOffset >= 0) { - // ``` - // However since elfOffset is an ulong, it will never be less than 0 - } else { - StoreStream.Seek ((long)elfOffset, SeekOrigin.Begin); - magic = reader.ReadUInt32 (); - } - } - - if (magic != Utils.ASSEMBLY_STORE_MAGIC) { +After: + private static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; +*/ + } + + private static string GetBlobName(string abi) => $"libassemblies.{abi}.blob.so"; + + protected override ulong GetStoreStartDataOffset() => elfOffset; + + protected override bool IsSupported() + { + StoreStream.Seek(0, SeekOrigin.Begin); + using var reader = CreateReader(); + + uint magic = reader.ReadUInt32(); + if (magic == Utils.ELF_MAGIC) + { + ELFPayloadError error; + (elfOffset, _, error) = Utils.FindELFPayloadSectionOffsetAndSize(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}" + }; + Logger?.Invoke(message); + // Was originally: + // ``` + // } else if (elfOffset >= 0) { + // ``` + // However since elfOffset is an ulong, it will never be less than 0 + } + else + { + StoreStream.Seek((long)elfOffset, SeekOrigin.Begin); + magic = reader.ReadUInt32(); + } + } + + if (magic != Utils.ASSEMBLY_STORE_MAGIC) + { Logger?.Invoke("Store '{0}' has invalid header magic number.", StorePath); - return false; - } + return false; + } - uint version = reader.ReadUInt32 (); - if (!supportedVersions.Contains (version)) { + uint version = reader.ReadUInt32(); + if (!supportedVersions.Contains(version)) + { Logger?.Invoke("Store '{0}' has unsupported version 0x{1:x}", StorePath, version); - return false; - } - - uint entry_count = reader.ReadUInt32 (); - uint index_entry_count = reader.ReadUInt32 (); - uint index_size = reader.ReadUInt32 (); - - header = new Header (magic, version, entry_count, index_entry_count, index_size); - return true; - } - - protected override void Prepare () - { - if (header == null) { - throw new InvalidOperationException ("Internal error: header not set, was IsSupported() called?"); - } - - TargetArch = (header.version & ASSEMBLY_STORE_ABI_MASK) switch { - ASSEMBLY_STORE_ABI_AARCH64 => AndroidTargetArch.Arm64, - ASSEMBLY_STORE_ABI_ARM => AndroidTargetArch.Arm, - ASSEMBLY_STORE_ABI_X64 => AndroidTargetArch.X86_64, - ASSEMBLY_STORE_ABI_X86 => AndroidTargetArch.X86, - _ => throw new NotSupportedException ($"Unsupported ABI in store version: 0x{header.version:x}") - }; - - Is64Bit = (header.version & ASSEMBLY_STORE_FORMAT_VERSION_MASK) != 0; - AssemblyCount = header.entry_count; - IndexEntryCount = header.index_entry_count; - - StoreStream.Seek ((long)elfOffset + Header.NativeSize, SeekOrigin.Begin); - using var reader = CreateReader (); - - var index = new List (); - for (uint i = 0; i < header.index_entry_count; i++) { - ulong name_hash; - if (Is64Bit) { - name_hash = reader.ReadUInt64 (); - } else { - name_hash = (ulong)reader.ReadUInt32 (); - } - - uint descriptor_index = reader.ReadUInt32 (); - index.Add (new IndexEntry (name_hash, descriptor_index)); - } - - var descriptors = new List (); - for (uint i = 0; i < header.entry_count; i++) { - uint mapping_index = reader.ReadUInt32 (); - uint data_offset = reader.ReadUInt32 (); - uint data_size = reader.ReadUInt32 (); - uint debug_data_offset = reader.ReadUInt32 (); - uint debug_data_size = reader.ReadUInt32 (); - uint config_data_offset = reader.ReadUInt32 (); - uint config_data_size = reader.ReadUInt32 (); - - var desc = new EntryDescriptor { - mapping_index = mapping_index, - data_offset = data_offset, - data_size = data_size, - debug_data_offset = debug_data_offset, - debug_data_size = debug_data_size, - config_data_offset = config_data_offset, - config_data_size = config_data_size, - }; - descriptors.Add (desc); - } - - var names = new List (); - for (uint i = 0; i < header.entry_count; i++) { - uint name_length = reader.ReadUInt32 (); - byte[] name_bytes = reader.ReadBytes ((int)name_length); - names.Add (Encoding.UTF8.GetString (name_bytes)); - } - - var tempItems = new Dictionary (); - foreach (IndexEntry ie in index) { - if (!tempItems.TryGetValue (ie.descriptor_index, out TemporaryItem? item)) { - item = new TemporaryItem (names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index]); - tempItems.Add (ie.descriptor_index, item); - } - item.IndexEntries.Add (ie); - } - - if (tempItems.Count != descriptors.Count) { - throw new InvalidOperationException ($"Assembly store '{StorePath}' index is corrupted."); - } - - var storeItems = new List (); - foreach (var kvp in tempItems) { - TemporaryItem ti = kvp.Value; - var item = new StoreItem_V2 (TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor); - storeItems.Add (item); - } - Assemblies = storeItems.AsReadOnly (); - } + return false; + } + + uint entry_count = reader.ReadUInt32(); + uint index_entry_count = reader.ReadUInt32(); + uint index_size = reader.ReadUInt32(); + + header = new Header(magic, version, entry_count, index_entry_count, index_size); + return true; + } + + protected override void Prepare() + { + if (header == null) + { + throw new InvalidOperationException("Internal error: header not set, was IsSupported() called?"); + } + + TargetArch = (header.version & ASSEMBLY_STORE_ABI_MASK) switch + { + ASSEMBLY_STORE_ABI_AARCH64 => AndroidTargetArch.Arm64, + ASSEMBLY_STORE_ABI_ARM => AndroidTargetArch.Arm, + ASSEMBLY_STORE_ABI_X64 => AndroidTargetArch.X86_64, + ASSEMBLY_STORE_ABI_X86 => AndroidTargetArch.X86, + _ => throw new NotSupportedException($"Unsupported ABI in store version: 0x{header.version:x}") + }; + + Is64Bit = (header.version & ASSEMBLY_STORE_FORMAT_VERSION_MASK) != 0; + AssemblyCount = header.entry_count; + IndexEntryCount = header.index_entry_count; + + StoreStream.Seek((long)elfOffset + Header.NativeSize, SeekOrigin.Begin); + using var reader = CreateReader(); + + var index = new List(); + for (uint i = 0; i < header.index_entry_count; i++) + { + ulong name_hash; + if (Is64Bit) + { + name_hash = reader.ReadUInt64(); + } + else + { + name_hash = (ulong)reader.ReadUInt32(); + } + + uint descriptor_index = reader.ReadUInt32(); + index.Add(new IndexEntry(name_hash, descriptor_index)); + } + + var descriptors = new List(); + for (uint i = 0; i < header.entry_count; i++) + { + uint mapping_index = reader.ReadUInt32(); + uint data_offset = reader.ReadUInt32(); + uint data_size = reader.ReadUInt32(); + uint debug_data_offset = reader.ReadUInt32(); + uint debug_data_size = reader.ReadUInt32(); + uint config_data_offset = reader.ReadUInt32(); + uint config_data_size = reader.ReadUInt32(); + + var desc = new EntryDescriptor + { + mapping_index = mapping_index, + data_offset = data_offset, + data_size = data_size, + debug_data_offset = debug_data_offset, + debug_data_size = debug_data_size, + config_data_offset = config_data_offset, + config_data_size = config_data_size, + }; + descriptors.Add(desc); + } + + var names = new List(); + for (uint i = 0; i < header.entry_count; i++) + { + uint name_length = reader.ReadUInt32(); + byte[] name_bytes = reader.ReadBytes((int)name_length); + names.Add(Encoding.UTF8.GetString(name_bytes)); + } + + var tempItems = new Dictionary(); + foreach (IndexEntry ie in index) + { + if (!tempItems.TryGetValue(ie.descriptor_index, out TemporaryItem? item)) + { + item = new TemporaryItem(names[(int)ie.descriptor_index], descriptors[(int)ie.descriptor_index]); + tempItems.Add(ie.descriptor_index, item); + } + item.IndexEntries.Add(ie); + } + + if (tempItems.Count != descriptors.Count) + { + throw new InvalidOperationException($"Assembly store '{StorePath}' index is corrupted."); + } + + var storeItems = new List(); + foreach (var kvp in tempItems) + { + TemporaryItem ti = kvp.Value; + var item = new StoreItem_V2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor); + storeItems.Add(item); + } + Assemblies = storeItems.AsReadOnly(); + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs index bc6f9f9a3d..83f4c53c51 100644 --- a/src/Sentry.Android.AssemblyReader/V2/Utils.cs +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -11,144 +11,195 @@ namespace Sentry.Android.AssemblyReader.V2; internal static class Utils { + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly string[] aabZipEntries = { - "base/manifest/AndroidManifest.xml", - "BundleConfig.pb", - }; +After: + private static readonly string[] aabZipEntries = { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly string[] aabBaseZipEntries = { - "manifest/AndroidManifest.xml", - }; +After: + private static readonly string[] aabBaseZipEntries = { +*/ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static readonly string[] apkZipEntries = { - "AndroidManifest.xml", - }; - - public const uint ZIP_MAGIC = 0x4034b50; - public const uint ASSEMBLY_STORE_MAGIC = 0x41424158; - public const uint ELF_MAGIC = 0x464c457f; - - public static readonly ArrayPool BytePool = ArrayPool.Shared; - - public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize (Stream stream) - { - stream.Seek (0, SeekOrigin.Begin); - Class elfClass = ELFReader.CheckELFType (stream); - if (elfClass == Class.NotELF) { - return ReturnError (null, ELFPayloadError.NotELF); - } - - if (!ELFReader.TryLoad (stream, shouldOwnStream: false, out IELF? elf)) { - return ReturnError (elf, ELFPayloadError.LoadFailed); - } - - if (elf.Type != FileType.SharedObject) { - return ReturnError (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); - } - - bool is64 = elf.Machine switch { - Machine.ARM => false, - Machine.Intel386 => false, - - Machine.AArch64 => true, - Machine.AMD64 => true, - - _ => throw new NotSupportedException ($"Unsupported ELF architecture '{elf.Machine}'") - }; - - ulong offset; - ulong size; - - if (is64) { - (offset, size) = GetOffsetAndSize64 ((Section)payloadSection); - } else { - (offset, size) = GetOffsetAndSize32 ((Section)payloadSection); - } - - elf.Dispose (); - return (offset, size, ELFPayloadError.None); - - (ulong offset, ulong size) GetOffsetAndSize64 (Section payload) - { - return (payload.Offset, payload.Size); - } - - (ulong offset, ulong size) GetOffsetAndSize32 (Section payload) - { - return ((ulong)payload.Offset, (ulong)payload.Size); - } - - (ulong offset, ulong size, ELFPayloadError error) ReturnError (IELF? elf, ELFPayloadError error) - { - elf?.Dispose (); - - return (0, 0, error); - } - } - - public static (FileFormat format, FileInfo? info) DetectFileFormat (string path) - { - if (String.IsNullOrEmpty (path)) { - return (FileFormat.Unknown, null); - } - - var info = new FileInfo (path); - if (!info.Exists) { - return (FileFormat.Unknown, null); - } - - using var reader = new BinaryReader (info.OpenRead ()); - - // ATM, all formats we recognize have 4-byte magic at the start - FileFormat format = reader.ReadUInt32 () switch { - Utils.ZIP_MAGIC => FileFormat.Zip, - Utils.ELF_MAGIC => FileFormat.ELF, - Utils.ASSEMBLY_STORE_MAGIC => FileFormat.AssemblyStore, - _ => FileFormat.Unknown - }; - - if (format == FileFormat.Unknown || format != FileFormat.Zip) { - return (format, info); - } - - return (DetectAndroidArchive (info, format), info); - } - +After: + private static readonly string[] apkZipEntries = { +*/ + private static readonly string[] aabZipEntries = { + "base/manifest/AndroidManifest.xml", + "BundleConfig.pb", + }; + private static readonly string[] aabBaseZipEntries = { + "manifest/AndroidManifest.xml", + }; + private static readonly string[] apkZipEntries = { + "AndroidManifest.xml", + }; + + public const uint ZIP_MAGIC = 0x4034b50; + public const uint ASSEMBLY_STORE_MAGIC = 0x41424158; + public const uint ELF_MAGIC = 0x464c457f; + + public static readonly ArrayPool BytePool = ArrayPool.Shared; + + public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + Class elfClass = ELFReader.CheckELFType(stream); + if (elfClass == Class.NotELF) + { + return ReturnError(null, ELFPayloadError.NotELF); + } + + if (!ELFReader.TryLoad(stream, shouldOwnStream: false, out IELF? elf)) + { + return ReturnError(elf, ELFPayloadError.LoadFailed); + } + + if (elf.Type != FileType.SharedObject) + { + return ReturnError(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); + } + + bool is64 = elf.Machine switch + { + Machine.ARM => false, + Machine.Intel386 => false, + + Machine.AArch64 => true, + Machine.AMD64 => true, + + _ => throw new NotSupportedException($"Unsupported ELF architecture '{elf.Machine}'") + }; + + ulong offset; + ulong size; + + if (is64) + { + (offset, size) = GetOffsetAndSize64((Section)payloadSection); + } + else + { + (offset, size) = GetOffsetAndSize32((Section)payloadSection); + } + + elf.Dispose(); + return (offset, size, ELFPayloadError.None); + + (ulong offset, ulong size) GetOffsetAndSize64(Section payload) + { + return (payload.Offset, payload.Size); + } + + (ulong offset, ulong size) GetOffsetAndSize32(Section payload) + { + return ((ulong)payload.Offset, (ulong)payload.Size); + } + + (ulong offset, ulong size, ELFPayloadError error) ReturnError(IELF? elf, ELFPayloadError error) + { + elf?.Dispose(); + + return (0, 0, error); + } + } + + public static (FileFormat format, FileInfo? info) DetectFileFormat(string path) + { + if (string.IsNullOrEmpty(path)) + { + return (FileFormat.Unknown, null); + } + + var info = new FileInfo(path); + if (!info.Exists) + { + return (FileFormat.Unknown, null); + } + + using var reader = new BinaryReader(info.OpenRead()); + + // ATM, all formats we recognize have 4-byte magic at the start + FileFormat format = reader.ReadUInt32() switch + { + Utils.ZIP_MAGIC => FileFormat.Zip, + Utils.ELF_MAGIC => FileFormat.ELF, + Utils.ASSEMBLY_STORE_MAGIC => FileFormat.AssemblyStore, + _ => FileFormat.Unknown + }; + + if (format == FileFormat.Unknown || format != FileFormat.Zip) + { + return (format, info); + } + + return (DetectAndroidArchive(info, format), info); + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) - { - using var zip = ZipArchive.Open (info.FullName, FileMode.Open); - - if (HasAllEntries (zip, aabZipEntries)) { - return FileFormat.Aab; - } - - if (HasAllEntries (zip, apkZipEntries)) { - return FileFormat.Apk; - } - - if (HasAllEntries (zip, aabBaseZipEntries)) { - return FileFormat.AabBase; - } - - return defaultFormat; - } - +After: + private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) +*/ + } + + private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) + { + using var zip = ZipArchive.Open(info.FullName, FileMode.Open); + + if (HasAllEntries(zip, aabZipEntries)) + { + return FileFormat.Aab; + } + + if (HasAllEntries(zip, apkZipEntries)) + { + return FileFormat.Apk; + } + + if (HasAllEntries(zip, aabBaseZipEntries)) + { + return FileFormat.AabBase; + } + + return defaultFormat; + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: static bool HasAllEntries (ZipArchive zip, string[] entries) - { - foreach (string entry in entries) { - if (!zip.ContainsEntry (entry, caseSensitive: true)) { - return false; - } - } - - return true; - } +After: + private static bool HasAllEntries (ZipArchive zip, string[] entries) +*/ + } + + private static bool HasAllEntries(ZipArchive zip, string[] entries) + { + foreach (string entry in entries) + { + if (!zip.ContainsEntry(entry, caseSensitive: true)) + { + return false; + } + } + + return true; + } } From fc3e17a2b64b1e55d1f8e3a81f6254c82cdfea16 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Mar 2025 11:30:43 +1300 Subject: [PATCH 12/22] Removed Xamarin.LibZipSharp dependency --- .../AndroidAssemblyDirectoryReader.cs | 8 ++------ .../Sentry.Android.AssemblyReader.csproj | 1 - .../V2/AssemblyStoreExplorer.cs | 20 ++++++------------- src/Sentry.Android.AssemblyReader/V2/Utils.cs | 16 +++++++++++---- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs index 8b0c0db59a..964aa6035a 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs +++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs @@ -1,4 +1,5 @@ using Sentry.Android.AssemblyReader.V1; +using Sentry.Android.AssemblyReader.V2; namespace Sentry.Android.AssemblyReader; @@ -27,12 +28,7 @@ public AndroidAssemblyDirectoryReader(ZipArchive zip, IList supportedAbi Logger?.Invoke("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 = new MemoryStream((int)zipEntry.Length); - using (var zipStream = zipEntry.Open()) - { - zipStream.CopyTo(memStream); - memStream.Position = 0; - } + var memStream = zipEntry.Extract(); return CreatePEReader(name, memStream, Logger); } diff --git a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj index 7ac3f22789..b14c2fa38e 100644 --- a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj +++ b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj @@ -9,7 +9,6 @@ - diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index f0d37159b7..119774b414 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -3,9 +3,6 @@ * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) */ -using Xamarin.Tools.Zip; -using ZipArchive = Xamarin.Tools.Zip.ZipArchive; - namespace Sentry.Android.AssemblyReader.V2; internal class AssemblyStoreExplorer @@ -237,14 +234,11 @@ private static (IList? explorers, string? errorMessage) O private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, List> pathLists, DebugLogger? logger) { - using var zip = ZipArchive.Open(fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; + using var zip = ZipFile.Open(fi.FullName, ZipArchiveMode.Read); - foreach (IList paths in pathLists) + foreach (var paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger); + var (explorers, errorMessage, pathsFound) = TryLoad(fi, zip, paths, logger); if (pathsFound) { return (explorers, errorMessage); @@ -258,16 +252,14 @@ private static (IList? explorers, string? errorMessage, b { var ret = new List(); - foreach (string path in paths) + foreach (var path in paths) { - if (!zip.ContainsEntry(path)) + if (zip.GetEntry(path) is not {} entry) { continue; } - var entry = zip.ReadEntry(path); - var stream = new MemoryStream(); - entry.Extract(stream); + var stream = entry.Extract(); ret.Add(new AssemblyStoreExplorer(stream, $"{fi.FullName}!{path}", logger)); } diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs index 83f4c53c51..752e732775 100644 --- a/src/Sentry.Android.AssemblyReader/V2/Utils.cs +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -5,7 +5,6 @@ using ELFSharp.ELF; using ELFSharp.ELF.Sections; using Machine = ELFSharp.ELF.Machine; -using ZipArchive = Xamarin.Tools.Zip.ZipArchive; namespace Sentry.Android.AssemblyReader.V2; @@ -163,7 +162,7 @@ private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaul private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) { - using var zip = ZipArchive.Open(info.FullName, FileMode.Open); + using var zip = ZipFile.Open(info.FullName, ZipArchiveMode.Read); if (HasAllEntries(zip, aabZipEntries)) { @@ -192,9 +191,9 @@ private static bool HasAllEntries (ZipArchive zip, string[] entries) private static bool HasAllEntries(ZipArchive zip, string[] entries) { - foreach (string entry in entries) + foreach (var entry in entries) { - if (!zip.ContainsEntry(entry, caseSensitive: true)) + if (zip.GetEntry(entry) is null) { return false; } @@ -202,4 +201,13 @@ private static bool HasAllEntries(ZipArchive zip, string[] entries) return true; } + + internal static MemoryStream Extract(this ZipArchiveEntry zipEntry) + { + var memStream = new MemoryStream((int)zipEntry.Length); + using var zipStream = zipEntry.Open(); + zipStream.CopyTo(memStream); + memStream.Position = 0; + return memStream; + } } From d618ac2f9ac79a4b65c315fc03c3cd9c8f6bd945 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Mar 2025 11:38:19 +1300 Subject: [PATCH 13/22] Removed dependency on System.IO.Hashing --- .../Sentry.Android.AssemblyReader.csproj | 1 - .../V2/MonoAndroidHelper.Basic.cs | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj index b14c2fa38e..dac014317f 100644 --- a/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj +++ b/src/Sentry.Android.AssemblyReader/Sentry.Android.AssemblyReader.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs index 5c5d472e10..9f13c962b4 100644 --- a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -3,8 +3,6 @@ * Original code licensed under the MIT License (https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/LICENSE.TXT) */ -using System.IO.Hashing; - namespace Sentry.Android.AssemblyReader.V2; internal partial class MonoAndroidHelper @@ -323,16 +321,4 @@ public static ulong GetMangledAssemblyNameSizeOverhead() public static byte[] Utf8StringToBytes(string str) => Encoding.UTF8.GetBytes(str); public static byte[] Utf16StringToBytes(string str) => Encoding.Unicode.GetBytes(str); - - public static ulong GetXxHash(string str, bool is64Bit) => GetXxHash(Utf8StringToBytes(str), is64Bit); - - public static ulong GetXxHash(byte[] stringBytes, bool is64Bit) - { - if (is64Bit) - { - return XxHash3.HashToUInt64(stringBytes); - } - - return (ulong)XxHash32.HashToUInt32(stringBytes); - } } From 509482ba3b80e113d242fc8be714cdd5cc7e7465 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 19 Mar 2025 22:43:29 +0000 Subject: [PATCH 14/22] Format code --- .../V2/AssemblyStoreExplorer.cs | 226 +++++++++--------- .../V2/AssemblyStoreReader.cs | 12 +- .../V2/StoreReader_V2.Classes.cs | 60 ++--- .../V2/StoreReader_V2.cs | 74 +++--- src/Sentry.Android.AssemblyReader/V2/Utils.cs | 64 ++--- 5 files changed, 218 insertions(+), 218 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index 119774b414..38e90c4ad2 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -81,118 +81,118 @@ public static (IList? explorers, string? errorMessage) Op default: return (null, $"File '{inputFile}' has an unsupported format '{format}'"); -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabPaths, - StoreReader_V1.AabPaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabBasePaths, - StoreReader_V1.AabBasePaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.ApkPaths, - StoreReader_V1.ApkPaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) - { - using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; - - foreach (IList paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); - if (pathsFound) { - return (explorers, errorMessage); - } - } - - return (null, "Unable to find any blob entries"); - } - - static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) -After: - private static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabPaths, - StoreReader_V1.AabPaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabBasePaths, - StoreReader_V1.AabBasePaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.ApkPaths, - StoreReader_V1.ApkPaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) - { - using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; - - foreach (IList paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); - if (pathsFound) { - return (explorers, errorMessage); - } - } - - return (null, "Unable to find any blob entries"); - } - - private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabPaths, + StoreReader_V1.AabPaths, + }, + logger + ); + } + + static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabBasePaths, + StoreReader_V1.AabBasePaths, + }, + logger + ); + } + + static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.ApkPaths, + StoreReader_V1.ApkPaths, + }, + logger + ); + } + + static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) + { + using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); + IList? explorers; + string? errorMessage; + bool pathsFound; + + foreach (IList paths in pathLists) { + (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); + if (pathsFound) { + return (explorers, errorMessage); + } + } + + return (null, "Unable to find any blob entries"); + } + + static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) + After: + private static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabPaths, + StoreReader_V1.AabPaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabBasePaths, + StoreReader_V1.AabBasePaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.ApkPaths, + StoreReader_V1.ApkPaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) + { + using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); + IList? explorers; + string? errorMessage; + bool pathsFound; + + foreach (IList paths in pathLists) { + (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); + if (pathsFound) { + return (explorers, errorMessage); + } + } + + return (null, "Unable to find any blob entries"); + } + + private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) + */ } } @@ -254,7 +254,7 @@ private static (IList? explorers, string? errorMessage, b foreach (var path in paths) { - if (zip.GetEntry(path) is not {} entry) + if (zip.GetEntry(path) is not { } entry) { continue; } diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs index 87c08a44f1..67e7e85f03 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs @@ -46,12 +46,12 @@ protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger) return null; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) -After: - private static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) + After: + private static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) + */ } private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader) diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs index 2f66fbcf2e..3822144920 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs @@ -27,12 +27,12 @@ public Header(uint magic, uint version, uint entry_count, uint index_entry_count this.index_entry_count = index_entry_count; this.index_size = index_size; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class IndexEntry -After: - private sealed class IndexEntry -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + sealed class IndexEntry + After: + private sealed class IndexEntry + */ } } @@ -46,12 +46,12 @@ public IndexEntry(ulong name_hash, uint descriptor_index) this.name_hash = name_hash; this.descriptor_index = descriptor_index; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class EntryDescriptor -After: - private sealed class EntryDescriptor -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + sealed class EntryDescriptor + After: + private sealed class EntryDescriptor + */ } } @@ -68,12 +68,12 @@ private sealed class EntryDescriptor public uint config_data_offset; public uint config_data_size; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class StoreItem_V2 : AssemblyStoreItem -After: - private sealed class StoreItem_V2 : AssemblyStoreItem -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + sealed class StoreItem_V2 : AssemblyStoreItem + After: + private sealed class StoreItem_V2 : AssemblyStoreItem + */ } private sealed class StoreItem_V2 : AssemblyStoreItem @@ -89,12 +89,12 @@ public StoreItem_V2(AndroidTargetArch targetArch, string name, bool is64Bit, Lis ConfigSize = descriptor.config_data_size; TargetArch = targetArch; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static List IndexToHashes (List indexEntries) -After: - private static List IndexToHashes (List indexEntries) -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static List IndexToHashes (List indexEntries) + After: + private static List IndexToHashes (List indexEntries) + */ } private static List IndexToHashes(List indexEntries) @@ -107,12 +107,12 @@ private static List IndexToHashes(List indexEntries) return ret; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class TemporaryItem -After: - private sealed class TemporaryItem -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + sealed class TemporaryItem + After: + private sealed class TemporaryItem + */ } } diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs index d22e74dad4..fba2def0cf 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs @@ -8,29 +8,29 @@ namespace Sentry.Android.AssemblyReader.V2; internal partial class StoreReader_V2 : AssemblyStoreReader { -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant - const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; - const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; - - const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; - const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; - const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; - const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; - const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; -After: - // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - 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; - 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; - private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; - private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; - private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant + const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; + const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; + + const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; + const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; + const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; + After: + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + 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; + 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; + private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; + */ // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones 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; @@ -50,14 +50,14 @@ internal partial class StoreReader_V2 : AssemblyStoreReader private readonly HashSet supportedVersions; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - Header? header; - ulong elfOffset = 0; -After: - private Header? header; - private ulong elfOffset = 0; -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + Header? header; + ulong elfOffset = 0; + After: + private Header? header; + private ulong elfOffset = 0; + */ private Header? header; private ulong elfOffset = 0; @@ -112,12 +112,12 @@ public StoreReader_V2(Stream store, string path, DebugLogger? logger) ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, }; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; -After: - private static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; + After: + private static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; + */ } private static string GetBlobName(string abi) => $"libassemblies.{abi}.blob.so"; diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs index 752e732775..69d6285f2e 100644 --- a/src/Sentry.Android.AssemblyReader/V2/Utils.cs +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -11,26 +11,26 @@ namespace Sentry.Android.AssemblyReader.V2; internal static class Utils { -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly string[] aabZipEntries = { -After: - private static readonly string[] aabZipEntries = { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly string[] aabBaseZipEntries = { -After: - private static readonly string[] aabBaseZipEntries = { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly string[] apkZipEntries = { -After: - private static readonly string[] apkZipEntries = { -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static readonly string[] aabZipEntries = { + After: + private static readonly string[] aabZipEntries = { + */ + + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static readonly string[] aabBaseZipEntries = { + After: + private static readonly string[] aabBaseZipEntries = { + */ + + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static readonly string[] apkZipEntries = { + After: + private static readonly string[] apkZipEntries = { + */ private static readonly string[] aabZipEntries = { "base/manifest/AndroidManifest.xml", "BundleConfig.pb", @@ -152,12 +152,12 @@ public static (FileFormat format, FileInfo? info) DetectFileFormat(string path) return (DetectAndroidArchive(info, format), info); -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) -After: - private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) + After: + private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) + */ } private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) @@ -181,12 +181,12 @@ private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat default return defaultFormat; -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static bool HasAllEntries (ZipArchive zip, string[] entries) -After: - private static bool HasAllEntries (ZipArchive zip, string[] entries) -*/ + /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' + Before: + static bool HasAllEntries (ZipArchive zip, string[] entries) + After: + private static bool HasAllEntries (ZipArchive zip, string[] entries) + */ } private static bool HasAllEntries(ZipArchive zip, string[] entries) From 956f1c6e291876e7af3323a7764f2dcbeeb2a067 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Mar 2025 12:02:55 +1300 Subject: [PATCH 15/22] Update AndroidAssemblyReaderTests.cs --- .../AndroidAssemblyReaderTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs index 17503e72bd..3a070ac27b 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs +++ b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs @@ -89,6 +89,8 @@ public void ReadsAssembly(bool isAssemblyStore, bool isCompressed, string assemb #endif #if NET9_0_OR_GREATER // Building without an assembly store is not yet supported in net9.0 and above + // TODO: See if we can support this. There might be some hints as to how this works at: + // https://github.com/dotnet/android/blob/6394773fad5108b0d7b4e6f087dc3e6ea997401a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs#L61-L65 Skip.If(!isAssemblyStore); #endif using var sut = GetSut(isAssemblyStore, isCompressed); From 0a9580b0446a9d907383e44768f73383a57a3a4e Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Mar 2025 12:43:20 +1300 Subject: [PATCH 16/22] Revert "Merge branch 'store-v2' of github.com:getsentry/sentry-dotnet into store-v2" This reverts commit 8b955554c4b26c1281da4860920848d2fc22bbdc, reversing changes made to 956f1c6e291876e7af3323a7764f2dcbeeb2a067. --- .../V2/AssemblyStoreExplorer.cs | 226 +++++++++--------- .../V2/AssemblyStoreReader.cs | 12 +- .../V2/StoreReader_V2.Classes.cs | 60 ++--- .../V2/StoreReader_V2.cs | 74 +++--- src/Sentry.Android.AssemblyReader/V2/Utils.cs | 64 ++--- 5 files changed, 218 insertions(+), 218 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index 38e90c4ad2..119774b414 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -81,118 +81,118 @@ public static (IList? explorers, string? errorMessage) Op default: return (null, $"File '{inputFile}' has an unsupported format '{format}'"); - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabPaths, - StoreReader_V1.AabPaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabBasePaths, - StoreReader_V1.AabBasePaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.ApkPaths, - StoreReader_V1.ApkPaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) - { - using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; - - foreach (IList paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); - if (pathsFound) { - return (explorers, errorMessage); - } - } - - return (null, "Unable to find any blob entries"); - } - - static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) - After: - private static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabPaths, - StoreReader_V1.AabPaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabBasePaths, - StoreReader_V1.AabBasePaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.ApkPaths, - StoreReader_V1.ApkPaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) - { - using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; - - foreach (IList paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); - if (pathsFound) { - return (explorers, errorMessage); - } - } - - return (null, "Unable to find any blob entries"); - } - - private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabPaths, + StoreReader_V1.AabPaths, + }, + logger + ); + } + + static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabBasePaths, + StoreReader_V1.AabBasePaths, + }, + logger + ); + } + + static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.ApkPaths, + StoreReader_V1.ApkPaths, + }, + logger + ); + } + + static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) + { + using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); + IList? explorers; + string? errorMessage; + bool pathsFound; + + foreach (IList paths in pathLists) { + (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); + if (pathsFound) { + return (explorers, errorMessage); + } + } + + return (null, "Unable to find any blob entries"); + } + + static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) +After: + private static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabPaths, + StoreReader_V1.AabPaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.AabBasePaths, + StoreReader_V1.AabBasePaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) + { + return OpenCommon ( + fi, + new List> { + StoreReader_V2.ApkPaths, + StoreReader_V1.ApkPaths, + }, + logger + ); + } + + private static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) + { + using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); + IList? explorers; + string? errorMessage; + bool pathsFound; + + foreach (IList paths in pathLists) { + (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); + if (pathsFound) { + return (explorers, errorMessage); + } + } + + return (null, "Unable to find any blob entries"); + } + + private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) +*/ } } @@ -254,7 +254,7 @@ private static (IList? explorers, string? errorMessage, b foreach (var path in paths) { - if (zip.GetEntry(path) is not { } entry) + if (zip.GetEntry(path) is not {} entry) { continue; } diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs index 67e7e85f03..87c08a44f1 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs @@ -46,12 +46,12 @@ protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger) return null; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) - After: - private static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) +After: + private static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) +*/ } private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader) diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs index 3822144920..2f66fbcf2e 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs @@ -27,12 +27,12 @@ public Header(uint magic, uint version, uint entry_count, uint index_entry_count this.index_entry_count = index_entry_count; this.index_size = index_size; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - sealed class IndexEntry - After: - private sealed class IndexEntry - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + sealed class IndexEntry +After: + private sealed class IndexEntry +*/ } } @@ -46,12 +46,12 @@ public IndexEntry(ulong name_hash, uint descriptor_index) this.name_hash = name_hash; this.descriptor_index = descriptor_index; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - sealed class EntryDescriptor - After: - private sealed class EntryDescriptor - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + sealed class EntryDescriptor +After: + private sealed class EntryDescriptor +*/ } } @@ -68,12 +68,12 @@ private sealed class EntryDescriptor public uint config_data_offset; public uint config_data_size; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - sealed class StoreItem_V2 : AssemblyStoreItem - After: - private sealed class StoreItem_V2 : AssemblyStoreItem - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + sealed class StoreItem_V2 : AssemblyStoreItem +After: + private sealed class StoreItem_V2 : AssemblyStoreItem +*/ } private sealed class StoreItem_V2 : AssemblyStoreItem @@ -89,12 +89,12 @@ public StoreItem_V2(AndroidTargetArch targetArch, string name, bool is64Bit, Lis ConfigSize = descriptor.config_data_size; TargetArch = targetArch; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static List IndexToHashes (List indexEntries) - After: - private static List IndexToHashes (List indexEntries) - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static List IndexToHashes (List indexEntries) +After: + private static List IndexToHashes (List indexEntries) +*/ } private static List IndexToHashes(List indexEntries) @@ -107,12 +107,12 @@ private static List IndexToHashes(List indexEntries) return ret; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - sealed class TemporaryItem - After: - private sealed class TemporaryItem - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + sealed class TemporaryItem +After: + private sealed class TemporaryItem +*/ } } diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs index fba2def0cf..d22e74dad4 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs @@ -8,29 +8,29 @@ namespace Sentry.Android.AssemblyReader.V2; internal partial class StoreReader_V2 : AssemblyStoreReader { - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant - const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; - const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; - - const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; - const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; - const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; - const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; - const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; - After: - // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - 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; - 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; - private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; - private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; - private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant + const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; + const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; + + const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; + const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; + const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; +After: + // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones + 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; + 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; + private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; + private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; + private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; +*/ // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones 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; @@ -50,14 +50,14 @@ internal partial class StoreReader_V2 : AssemblyStoreReader private readonly HashSet supportedVersions; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - Header? header; - ulong elfOffset = 0; - After: - private Header? header; - private ulong elfOffset = 0; - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + Header? header; + ulong elfOffset = 0; +After: + private Header? header; + private ulong elfOffset = 0; +*/ private Header? header; private ulong elfOffset = 0; @@ -112,12 +112,12 @@ public StoreReader_V2(Stream store, string path, DebugLogger? logger) ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, }; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; - After: - private static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; +After: + private static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; +*/ } private static string GetBlobName(string abi) => $"libassemblies.{abi}.blob.so"; diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs index 69d6285f2e..752e732775 100644 --- a/src/Sentry.Android.AssemblyReader/V2/Utils.cs +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -11,26 +11,26 @@ namespace Sentry.Android.AssemblyReader.V2; internal static class Utils { - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static readonly string[] aabZipEntries = { - After: - private static readonly string[] aabZipEntries = { - */ - - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static readonly string[] aabBaseZipEntries = { - After: - private static readonly string[] aabBaseZipEntries = { - */ - - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static readonly string[] apkZipEntries = { - After: - private static readonly string[] apkZipEntries = { - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static readonly string[] aabZipEntries = { +After: + private static readonly string[] aabZipEntries = { +*/ + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static readonly string[] aabBaseZipEntries = { +After: + private static readonly string[] aabBaseZipEntries = { +*/ + +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static readonly string[] apkZipEntries = { +After: + private static readonly string[] apkZipEntries = { +*/ private static readonly string[] aabZipEntries = { "base/manifest/AndroidManifest.xml", "BundleConfig.pb", @@ -152,12 +152,12 @@ public static (FileFormat format, FileInfo? info) DetectFileFormat(string path) return (DetectAndroidArchive(info, format), info); - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) - After: - private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) +After: + private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) +*/ } private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) @@ -181,12 +181,12 @@ private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat default return defaultFormat; - /* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' - Before: - static bool HasAllEntries (ZipArchive zip, string[] entries) - After: - private static bool HasAllEntries (ZipArchive zip, string[] entries) - */ +/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' +Before: + static bool HasAllEntries (ZipArchive zip, string[] entries) +After: + private static bool HasAllEntries (ZipArchive zip, string[] entries) +*/ } private static bool HasAllEntries(ZipArchive zip, string[] entries) From bc58628c43c1aa05f431a52783ff686950836044 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 20 Mar 2025 13:18:52 +1300 Subject: [PATCH 17/22] Removed junk comments added by dotnet format --- .../V2/AssemblyStoreExplorer.cs | 119 +----------------- .../V2/AssemblyStoreReader.cs | 9 +- .../V2/MonoAndroidHelper.Basic.cs | 87 ------------- .../{StoreReader_V1.cs => StoreReaderV1.cs} | 0 ...V2.Classes.cs => StoreReaderV2.Classes.cs} | 41 +----- .../{StoreReader_V2.cs => StoreReaderV2.cs} | 48 +------ src/Sentry.Android.AssemblyReader/V2/Utils.cs | 35 ------ 7 files changed, 11 insertions(+), 328 deletions(-) rename src/Sentry.Android.AssemblyReader/V2/{StoreReader_V1.cs => StoreReaderV1.cs} (100%) rename src/Sentry.Android.AssemblyReader/V2/{StoreReader_V2.Classes.cs => StoreReaderV2.Classes.cs} (70%) rename src/Sentry.Android.AssemblyReader/V2/{StoreReader_V2.cs => StoreReaderV2.cs} (80%) diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index 119774b414..6a185b85e0 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -80,119 +80,6 @@ public static (IList? explorers, string? errorMessage) Op default: return (null, $"File '{inputFile}' has an unsupported format '{format}'"); - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabPaths, - StoreReader_V1.AabPaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabBasePaths, - StoreReader_V1.AabBasePaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.ApkPaths, - StoreReader_V1.ApkPaths, - }, - logger - ); - } - - static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) - { - using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; - - foreach (IList paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); - if (pathsFound) { - return (explorers, errorMessage); - } - } - - return (null, "Unable to find any blob entries"); - } - - static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) -After: - private static (IList? explorers, string? errorMessage) OpenAab (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabPaths, - StoreReader_V1.AabPaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenAabBase (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.AabBasePaths, - StoreReader_V1.AabBasePaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenApk (FileInfo fi, DebugLogger? logger) - { - return OpenCommon ( - fi, - new List> { - StoreReader_V2.ApkPaths, - StoreReader_V1.ApkPaths, - }, - logger - ); - } - - private static (IList? explorers, string? errorMessage) OpenCommon (FileInfo fi, List> pathLists, DebugLogger? logger) - { - using var zip = ZipArchive.Open (fi.FullName, FileMode.Open); - IList? explorers; - string? errorMessage; - bool pathsFound; - - foreach (IList paths in pathLists) { - (explorers, errorMessage, pathsFound) = TryLoad (fi, zip, paths, logger); - if (pathsFound) { - return (explorers, errorMessage); - } - } - - return (null, "Unable to find any blob entries"); - } - - private static (IList? explorers, string? errorMessage, bool pathsFound) TryLoad (FileInfo fi, ZipArchive zip, IList paths, DebugLogger? logger) -*/ } } @@ -201,7 +88,7 @@ private static (IList? explorers, string? errorMessage) O return OpenCommon( fi, new List> { - StoreReader_V2.AabPaths, + StoreReaderV2.AabPaths, StoreReader_V1.AabPaths, }, logger @@ -213,7 +100,7 @@ private static (IList? explorers, string? errorMessage) O return OpenCommon( fi, new List> { - StoreReader_V2.AabBasePaths, + StoreReaderV2.AabBasePaths, StoreReader_V1.AabBasePaths, }, logger @@ -225,7 +112,7 @@ private static (IList? explorers, string? errorMessage) O return OpenCommon( fi, new List> { - StoreReader_V2.ApkPaths, + StoreReaderV2.ApkPaths, StoreReader_V1.ApkPaths, }, logger diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs index 87c08a44f1..856f7c35ea 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreReader.cs @@ -38,20 +38,13 @@ protected AssemblyStoreReader(Stream store, string path, DebugLogger? logger) return reader; } - reader = MakeReaderReady(new StoreReader_V2(store, path, logger)); + reader = MakeReaderReady(new StoreReaderV2(store, path, logger)); if (reader != null) { return reader; } return null; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) -After: - private static AssemblyStoreReader? MakeReaderReady (AssemblyStoreReader reader) -*/ } private static AssemblyStoreReader? MakeReaderReady(AssemblyStoreReader reader) diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs index 9f13c962b4..cb391dc253 100644 --- a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -28,98 +28,11 @@ public static class RuntimeIdentifier AndroidTargetArch.Arm64, AndroidTargetArch.X86, AndroidTargetArch.X86_64, - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly char[] ZipPathTrimmedChars = {'/', '\\'}; - - static readonly Dictionary ClangAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -After: - private static readonly char[] ZipPathTrimmedChars = {'/', '\\'}; - private static readonly Dictionary ClangAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly Dictionary AbiToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -After: - private static readonly Dictionary AbiToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly Dictionary AbiToRidMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -After: - private static readonly Dictionary AbiToRidMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly Dictionary RidToAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -After: - private static readonly Dictionary RidToAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly Dictionary RidToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -After: - private static readonly Dictionary RidToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly Dictionary ArchToRidMap = new Dictionary { -After: - private static readonly Dictionary ArchToRidMap = new Dictionary { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly Dictionary ArchToAbiMap = new Dictionary { -After: - private static readonly Dictionary ArchToAbiMap = new Dictionary { -*/ }; private static readonly char[] ZipPathTrimmedChars = { '/', '\\' }; private static readonly Dictionary ClangAbiMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { {"arm64-v8a", "aarch64"}, {"armeabi-v7a", "arm"}, - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - if (!String.IsNullOrEmpty (part1)) { -After: - if (!string.IsNullOrEmpty (part1)) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - if (String.IsNullOrEmpty (p)) { -After: - if (string.IsNullOrEmpty (p)) { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - return String.Empty; -After: - return string.Empty; -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - return String.Join ("/", parts); -After: - return string.Join ("/", parts); -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - if (!String.IsNullOrEmpty (culture)) { -After: - if (!string.IsNullOrEmpty (culture)) { -*/ {"x86", "i686"}, {"x86_64", "x86_64"} }; diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V1.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs similarity index 100% rename from src/Sentry.Android.AssemblyReader/V2/StoreReader_V1.cs rename to src/Sentry.Android.AssemblyReader/V2/StoreReaderV1.cs diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs similarity index 70% rename from src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs rename to src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs index 2f66fbcf2e..b1ed2e392e 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.Classes.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.Classes.cs @@ -5,7 +5,7 @@ namespace Sentry.Android.AssemblyReader.V2; -internal partial class StoreReader_V2 +internal partial class StoreReaderV2 { private sealed class Header { @@ -26,13 +26,6 @@ public Header(uint magic, uint version, uint entry_count, uint index_entry_count this.entry_count = entry_count; this.index_entry_count = index_entry_count; this.index_size = index_size; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class IndexEntry -After: - private sealed class IndexEntry -*/ } } @@ -45,13 +38,6 @@ public IndexEntry(ulong name_hash, uint descriptor_index) { this.name_hash = name_hash; this.descriptor_index = descriptor_index; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class EntryDescriptor -After: - private sealed class EntryDescriptor -*/ } } @@ -67,18 +53,11 @@ private sealed class EntryDescriptor public uint config_data_offset; public uint config_data_size; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class StoreItem_V2 : AssemblyStoreItem -After: - private sealed class StoreItem_V2 : AssemblyStoreItem -*/ } - private sealed class StoreItem_V2 : AssemblyStoreItem + private sealed class StoreItemV2 : AssemblyStoreItem { - public StoreItem_V2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor) + public StoreItemV2(AndroidTargetArch targetArch, string name, bool is64Bit, List indexEntries, EntryDescriptor descriptor) : base(name, is64Bit, IndexToHashes(indexEntries)) { DataOffset = descriptor.data_offset; @@ -88,13 +67,6 @@ public StoreItem_V2(AndroidTargetArch targetArch, string name, bool is64Bit, Lis ConfigOffset = descriptor.config_data_offset; ConfigSize = descriptor.config_data_size; TargetArch = targetArch; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static List IndexToHashes (List indexEntries) -After: - private static List IndexToHashes (List indexEntries) -*/ } private static List IndexToHashes(List indexEntries) @@ -106,13 +78,6 @@ private static List IndexToHashes(List indexEntries) } return ret; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - sealed class TemporaryItem -After: - private sealed class TemporaryItem -*/ } } diff --git a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs similarity index 80% rename from src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs rename to src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs index d22e74dad4..a60ade7e82 100644 --- a/src/Sentry.Android.AssemblyReader/V2/StoreReader_V2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/StoreReaderV2.cs @@ -5,32 +5,8 @@ namespace Sentry.Android.AssemblyReader.V2; -internal partial class StoreReader_V2 : AssemblyStoreReader +internal partial class StoreReaderV2 : AssemblyStoreReader { - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000002; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant - const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000002; - const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; - - const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; - const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; - const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; - const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; - const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; -After: - // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - 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; - 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; - private const uint ASSEMBLY_STORE_ABI_X64 = 0x00030000; - private const uint ASSEMBLY_STORE_ABI_X86 = 0x00040000; - private const uint ASSEMBLY_STORE_ABI_MASK = 0x00FF0000; -*/ // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones 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; @@ -49,19 +25,10 @@ internal partial class StoreReader_V2 : AssemblyStoreReader public static IList AabBasePaths { get; } private readonly HashSet supportedVersions; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - Header? header; - ulong elfOffset = 0; -After: - private Header? header; - private ulong elfOffset = 0; -*/ private Header? header; private ulong elfOffset = 0; - static StoreReader_V2() + static StoreReaderV2() { var paths = new List { GetArchPath (AndroidTargetArch.Arm64), @@ -102,7 +69,7 @@ string GetArchPath(AndroidTargetArch arch, string? root = null) } } - public StoreReader_V2(Stream store, string path, DebugLogger? logger) + public StoreReaderV2(Stream store, string path, DebugLogger? logger) : base(store, path, logger) { supportedVersions = new HashSet { @@ -111,13 +78,6 @@ public StoreReader_V2(Stream store, string path, DebugLogger? logger) ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_ARM, ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, }; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; -After: - private static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; -*/ } private static string GetBlobName(string abi) => $"libassemblies.{abi}.blob.so"; @@ -273,7 +233,7 @@ protected override void Prepare() foreach (var kvp in tempItems) { TemporaryItem ti = kvp.Value; - var item = new StoreItem_V2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor); + var item = new StoreItemV2(TargetArch, ti.Name, Is64Bit, ti.IndexEntries, ti.Descriptor); storeItems.Add(item); } Assemblies = storeItems.AsReadOnly(); diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs index 752e732775..de9d26ce87 100644 --- a/src/Sentry.Android.AssemblyReader/V2/Utils.cs +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -10,27 +10,6 @@ namespace Sentry.Android.AssemblyReader.V2; internal static class Utils { - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly string[] aabZipEntries = { -After: - private static readonly string[] aabZipEntries = { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly string[] aabBaseZipEntries = { -After: - private static readonly string[] aabBaseZipEntries = { -*/ - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static readonly string[] apkZipEntries = { -After: - private static readonly string[] apkZipEntries = { -*/ private static readonly string[] aabZipEntries = { "base/manifest/AndroidManifest.xml", "BundleConfig.pb", @@ -151,13 +130,6 @@ public static (FileFormat format, FileInfo? info) DetectFileFormat(string path) } return (DetectAndroidArchive(info, format), info); - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) -After: - private static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat) -*/ } private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) @@ -180,13 +152,6 @@ private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat default } return defaultFormat; - -/* Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)' -Before: - static bool HasAllEntries (ZipArchive zip, string[] entries) -After: - private static bool HasAllEntries (ZipArchive zip, string[] entries) -*/ } private static bool HasAllEntries(ZipArchive zip, string[] entries) From bbfa502d2b227e6e8fddc9e24d1c38a96f00bb3d Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 20 Mar 2025 00:35:40 +0000 Subject: [PATCH 18/22] Format code --- src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index 6a185b85e0..d84cb23f8f 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -141,7 +141,7 @@ private static (IList? explorers, string? errorMessage, b foreach (var path in paths) { - if (zip.GetEntry(path) is not {} entry) + if (zip.GetEntry(path) is not { } entry) { continue; } From 6f7cbb7fdce829e36699ba95d7ccde4efac9eacd Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 21 Mar 2025 14:00:26 +1300 Subject: [PATCH 19/22] Tweaked ZipFile calls --- .../AndroidAssemblyReaderFactory.cs | 2 +- src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs | 2 +- src/Sentry.Android.AssemblyReader/V2/Utils.cs | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs index ed8199333f..b17c2ffc03 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs +++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs @@ -27,7 +27,7 @@ public static IAndroidAssemblyReader Open(string apkPath, IList supporte } // Try to read using the v1 store format - var zipArchive = ZipFile.Open(apkPath, ZipArchiveMode.Read); + var zipArchive = ZipFile.OpenRead(apkPath); if (zipArchive.GetEntry("assemblies/assemblies.manifest") is not null) { logger?.Invoke("APK uses AssemblyStore V1"); diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index d84cb23f8f..01cd3e37fb 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -121,7 +121,7 @@ private static (IList? explorers, string? errorMessage) O private static (IList? explorers, string? errorMessage) OpenCommon(FileInfo fi, List> pathLists, DebugLogger? logger) { - using var zip = ZipFile.Open(fi.FullName, ZipArchiveMode.Read); + using var zip = ZipFile.OpenRead(fi.FullName); foreach (var paths in pathLists) { diff --git a/src/Sentry.Android.AssemblyReader/V2/Utils.cs b/src/Sentry.Android.AssemblyReader/V2/Utils.cs index de9d26ce87..a8978e78e4 100644 --- a/src/Sentry.Android.AssemblyReader/V2/Utils.cs +++ b/src/Sentry.Android.AssemblyReader/V2/Utils.cs @@ -134,7 +134,7 @@ public static (FileFormat format, FileInfo? info) DetectFileFormat(string path) private static FileFormat DetectAndroidArchive(FileInfo info, FileFormat defaultFormat) { - using var zip = ZipFile.Open(info.FullName, ZipArchiveMode.Read); + using var zip = ZipFile.OpenRead(info.FullName); if (HasAllEntries(zip, aabZipEntries)) { @@ -175,4 +175,6 @@ internal static MemoryStream Extract(this ZipArchiveEntry zipEntry) memStream.Position = 0; return memStream; } + + internal static bool ContainsEntry(this ZipArchive zip, string entry) => zip.GetEntry(entry) is not null; } From 440baf27ba15129998f37b6aa9e286d8670d5a25 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 21 Mar 2025 15:42:09 +1300 Subject: [PATCH 20/22] Added support for non AssemblyStore APKs --- .../AndroidAssemblyReaderFactory.cs | 13 +- .../AndroidAssemblyDirectoryReaderV1.cs} | 7 +- .../V2/AndroidAssemblyDirectoryReaderV2.cs | 50 ++ .../V2/ArchiveAssemblyHelper.cs | 609 ++++++++++++++++++ .../AndroidAssemblyReaderTests.cs | 10 +- 5 files changed, 674 insertions(+), 15 deletions(-) rename src/Sentry.Android.AssemblyReader/{AndroidAssemblyDirectoryReader.cs => V1/AndroidAssemblyDirectoryReaderV1.cs} (84%) create mode 100644 src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs create mode 100644 src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs index b17c2ffc03..62670899a9 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs +++ b/src/Sentry.Android.AssemblyReader/AndroidAssemblyReaderFactory.cs @@ -19,14 +19,19 @@ public static IAndroidAssemblyReader Open(string apkPath, IList supporte { logger?.Invoke("Opening APK: {0}", apkPath); - // Try to read using the v2 store format +#if NET9_0 + logger?.Invoke("Reading files using V2 APK layout."); if (AndroidAssemblyStoreReaderV2.TryReadStore(apkPath, supportedAbis, logger, out var readerV2)) { logger?.Invoke("APK uses AssemblyStore V2"); return readerV2; } - // Try to read using the v1 store format + logger?.Invoke("APK doesn't use AssemblyStore"); + return new AndroidAssemblyDirectoryReaderV2(apkPath, supportedAbis, logger); +#else + logger?.Invoke("Reading files using V1 APK layout."); + var zipArchive = ZipFile.OpenRead(apkPath); if (zipArchive.GetEntry("assemblies/assemblies.manifest") is not null) { @@ -34,8 +39,8 @@ public static IAndroidAssemblyReader Open(string apkPath, IList supporte return new AndroidAssemblyStoreReaderV1(zipArchive, supportedAbis, logger); } - // Finally, try to read from file system logger?.Invoke("APK doesn't use AssemblyStore"); - return new AndroidAssemblyDirectoryReader(zipArchive, supportedAbis, logger); + return new AndroidAssemblyDirectoryReaderV1(zipArchive, supportedAbis, logger); +#endif } } diff --git a/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs similarity index 84% rename from src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs rename to src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs index 964aa6035a..eaa000638f 100644 --- a/src/Sentry.Android.AssemblyReader/AndroidAssemblyDirectoryReader.cs +++ b/src/Sentry.Android.AssemblyReader/V1/AndroidAssemblyDirectoryReaderV1.cs @@ -1,12 +1,11 @@ -using Sentry.Android.AssemblyReader.V1; using Sentry.Android.AssemblyReader.V2; -namespace Sentry.Android.AssemblyReader; +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 AndroidAssemblyDirectoryReader : AndroidAssemblyReader, IAndroidAssemblyReader +internal sealed class AndroidAssemblyDirectoryReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader { - public AndroidAssemblyDirectoryReader(ZipArchive zip, IList supportedAbis, DebugLogger? logger) + public AndroidAssemblyDirectoryReaderV1(ZipArchive zip, IList supportedAbis, DebugLogger? logger) : base(zip, supportedAbis, logger) { } public PEReader? TryReadAssembly(string name) diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs new file mode 100644 index 0000000000..8ca5a6e4b9 --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs @@ -0,0 +1,50 @@ +using Sentry.Android.AssemblyReader.V2; + +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 +{ + private DebugLogger? Logger { get; } + private HashSet SupportedArchitectures { get; } = new (); + private readonly ArchiveAssemblyHelper _archiveAssemblyHelper; + + public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedAbis, DebugLogger? logger) + { + Logger = logger; + foreach (var abi in supportedAbis) + { + SupportedArchitectures.Add(abi.AbiToDeviceArchitecture()); + } + _archiveAssemblyHelper = new ArchiveAssemblyHelper(apkPath, logger, false); + } + + 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); + } + + foreach (var arch in SupportedArchitectures) + { + if (_archiveAssemblyHelper.ReadEntry($"assemblies/{name}", arch) is not { } memStream) + { + continue; + } + + Logger?.Invoke("Resolved assembly {0} in the APK", name); + return AndroidAssemblyReader.CreatePEReader(name, memStream, Logger); + } + + Logger?.Invoke("Couldn't find assembly {0} in the APK", name); + return null; + } + + public void Dispose() + { + // No-op + } +} diff --git a/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs b/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs new file mode 100644 index 0000000000..5d40c9373c --- /dev/null +++ b/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs @@ -0,0 +1,609 @@ +/* + * Adapted from https://github.com/dotnet/android/blob/6394773fad5108b0d7b4e6f087dc3e6ea997401a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs + * Original code licensed under the MIT License (https://github.com/dotnet/android-tools/blob/ab2165daf27d4fcb29e88bc022e0ab0be33aff69/LICENSE) + */ +namespace Sentry.Android.AssemblyReader.V2; + +internal class ArchiveAssemblyHelper +{ + private const string DefaultAssemblyStoreEntryPrefix = "{storeReader}"; + + private static readonly HashSet SpecialExtensions = new (StringComparer.OrdinalIgnoreCase) { + ".dll", + ".config", + ".pdb", + }; + + private static readonly ArrayPool Buffers = ArrayPool.Shared; + + private readonly string _archivePath; + private readonly DebugLogger? _logger; + private readonly string _assembliesRootDir; + private readonly bool _useAssemblyStores; + private List? _archiveContents; + + public ArchiveAssemblyHelper (string archivePath, DebugLogger? logger, bool useAssemblyStores = true) + { + if (string.IsNullOrEmpty (archivePath)) { + throw new ArgumentException ("must not be null or empty", nameof (archivePath)); + } + + _archivePath = archivePath; + _logger = logger; + _useAssemblyStores = useAssemblyStores; + + var extension = Path.GetExtension (archivePath) ?? string.Empty; + if (string.Compare (".aab", extension, StringComparison.OrdinalIgnoreCase) == 0) { + _assembliesRootDir = "base/lib/"; + } else if (string.Compare (".apk", extension, StringComparison.OrdinalIgnoreCase) == 0) { + _assembliesRootDir = "lib/"; + } else if (string.Compare (".zip", extension, StringComparison.OrdinalIgnoreCase) == 0) { + _assembliesRootDir = "lib/"; + } else { + _assembliesRootDir = string.Empty; + } + } + + public MemoryStream? ReadEntry (string path, AndroidTargetArch arch = AndroidTargetArch.None, bool uncompressIfNecessary = false) + { + var ret = _useAssemblyStores + ? ReadStoreEntry (path, arch, uncompressIfNecessary) + : ReadZipEntry (path, arch); + + if (ret == null) { + return null; + } + + ret.Flush (); + ret.Seek (0, SeekOrigin.Begin); + var (elfPayloadOffset, elfPayloadSize, error) = Utils.FindELFPayloadSectionOffsetAndSize (ret); + + if (error != ELFPayloadError.None) { + var message = error switch { + ELFPayloadError.NotELF => $"Entry '{path}' is not a valid ELF binary", + ELFPayloadError.LoadFailed => $"Entry '{path}' could not be loaded", + ELFPayloadError.NotSharedLibrary => $"Entry '{path}' is not a shared ELF library", + ELFPayloadError.NotLittleEndian => $"Entry '{path}' is not a little-endian ELF image", + ELFPayloadError.NoPayloadSection => $"Entry '{path}' does not contain the 'payload' section", + _ => $"Unknown ELF payload section error for entry '{path}': {error}" + }; + Console.WriteLine (message); + } else { + Console.WriteLine ($"Extracted content from ELF image '{path}'"); + } + + if (elfPayloadOffset == 0) { + ret.Seek (0, SeekOrigin.Begin); + return ret; + } + + // Make a copy of JUST the payload section, so that it contains only the data the tests expect and support + var payload = new MemoryStream (); + var data = Buffers.Rent (16384); + var toRead = data.Length; + var nRead = 0; + var remaining = elfPayloadSize; + + ret.Seek ((long)elfPayloadOffset, SeekOrigin.Begin); + while (remaining > 0 && (nRead = ret.Read (data, 0, toRead)) > 0) { + payload.Write (data, 0, nRead); + remaining -= (ulong)nRead; + + if (remaining < (ulong)data.Length) { + // Make sure the last chunk doesn't gobble in more than we need + toRead = (int)remaining; + } + } + Buffers.Return (data); + + payload.Flush (); + ret.Dispose (); + + payload.Seek (0, SeekOrigin.Begin); + return payload; + } + + private MemoryStream? ReadZipEntry (string path, AndroidTargetArch arch) + { + var potentialEntries = TransformArchiveAssemblyPath (path, arch); + if (potentialEntries == null || potentialEntries.Count == 0) { + return null; + } + + using var zip = ZipFile.OpenRead(_archivePath); + foreach (var assemblyPath in potentialEntries) { + if (zip.GetEntry(assemblyPath) is not {} entry) + { + continue; + } + + var ret = entry.Extract(); + ret.Flush(); + return ret; + } + + return null; + } + + private MemoryStream? ReadStoreEntry (string path, AndroidTargetArch arch, bool uncompressIfNecessary) + { + var name = Path.GetFileNameWithoutExtension (path); + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); + var explorer = SelectExplorer (explorers, arch); + if (explorer == null) { + Console.WriteLine ($"Failed to read assembly '{name}' from '{_archivePath}'. {errorMessage}"); + return null; + } + + if (arch == AndroidTargetArch.None) { + if (explorer.TargetArch == null) { + throw new InvalidOperationException ($"Internal error: explorer should not have its TargetArch unset"); + } + + arch = (AndroidTargetArch)explorer.TargetArch; + } + + Console.WriteLine ($"Trying to read store entry: {name}"); + var assemblies = explorer.Find (name, arch); + if (assemblies == null) { + Console.WriteLine ($"Failed to locate assembly '{name}' in assembly store for architecture '{arch}', in archive '{_archivePath}'"); + return null; + } + + AssemblyStoreItem? assembly = null; + foreach (var item in assemblies) { + if (arch == AndroidTargetArch.None || item.TargetArch == arch) { + assembly = item; + break; + } + } + + if (assembly == null) { + Console.WriteLine ($"Failed to find assembly '{name}' in assembly store for architecture '{arch}', in archive '{_archivePath}'"); + return null; + } + + return explorer.ReadImageData (assembly, uncompressIfNecessary); + } + + public List ListArchiveContents (string storeEntryPrefix = DefaultAssemblyStoreEntryPrefix, bool forceRefresh = false, AndroidTargetArch arch = AndroidTargetArch.None) + { + if (!forceRefresh && _archiveContents != null) { + return _archiveContents; + } + + if (string.IsNullOrEmpty (storeEntryPrefix)) { + throw new ArgumentException (nameof (storeEntryPrefix), "must not be null or empty"); + } + + var entries = new List (); + using (var zip = ZipFile.OpenRead(_archivePath)) { + foreach (var entry in zip.Entries) { + entries.Add(entry.FullName); + } + } + + _archiveContents = entries; + if (!_useAssemblyStores) { + Console.WriteLine ("Not using assembly stores"); + return entries; + } + + Console.WriteLine ($"Creating AssemblyStoreExplorer for archive '{_archivePath}'"); + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); + + if (arch == AndroidTargetArch.None) { + if (explorers == null || explorers.Count == 0) { + return entries; + } + + foreach (var explorer in explorers) { + SynthetizeAssemblies (explorer); + } + } else { + SynthetizeAssemblies (SelectExplorer (explorers, arch)); + } + + Console.WriteLine ("Archive entries with synthetised assembly storeReader entries:"); + foreach (string e in entries) { + Console.WriteLine ($" {e}"); + } + + return entries; + + void SynthetizeAssemblies (AssemblyStoreExplorer? explorer) + { + if (explorer == null) { + return; + } + + Console.WriteLine ($"Explorer for {explorer.TargetArch} found {explorer.AssemblyCount} assemblies"); + if (explorer.Assemblies is null) + { + return; + } + + foreach (var asm in explorer.Assemblies) { + var prefix = storeEntryPrefix; + var abi = MonoAndroidHelper.ArchToAbi (asm.TargetArch); + prefix = $"{prefix}{abi}/"; + + var cultureIndex = asm.Name.IndexOf ('/'); + string? culture = null; + string name; + + if (cultureIndex > 0) { + culture = asm.Name.Substring (0, cultureIndex); + name = asm.Name.Substring (cultureIndex + 1); + } else { + name = asm.Name; + } + + // Mangle name in the same fashion the discrete assembly entries are named, makes other + // code in this class simpler. + var mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (name, culture); + entries.Add ($"{prefix}{mangledName}"); + if (asm.DebugOffset > 0) { + mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.ChangeExtension (name, "pdb")); + entries.Add ($"{prefix}{mangledName}"); + } + + if (asm.ConfigOffset > 0) { + mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.ChangeExtension (name, "config")); + entries.Add ($"{prefix}{mangledName}"); + } + } + } + } + + internal AssemblyStoreExplorer? SelectExplorer (IList? explorers, string rid) + { + return SelectExplorer (explorers, MonoAndroidHelper.RidToArch (rid)); + } + + internal AssemblyStoreExplorer? SelectExplorer (IList? explorers, AndroidTargetArch arch) + { + if (explorers == null || explorers.Count == 0) { + return null; + } + + // If we don't care about target architecture, we check the first store, since all of them will have the same + // assemblies. Otherwise, we try to locate the correct store. + if (arch == AndroidTargetArch.None) { + return explorers[0]; + } + + foreach (var e in explorers) { + if (e.TargetArch == null || e.TargetArch != arch) { + continue; + } + return e; + } + + Console.WriteLine ($"Failed to find assembly store for architecture '{arch}' in archive '{_archivePath}'"); + return null; + } + + /// + /// Takes "old style" `assemblies/assembly.dll` path and returns (if possible) a set of paths that reflect the new + /// location of `lib/{ARCH}/assembly.dll.so`. A list is returned because, if `arch` is `None`, we'll return all + /// the possible architectural paths. + /// An exception is thrown if we cannot transform the path for some reason. It should **not** be handled. + /// + private static List? TransformArchiveAssemblyPath (string path, AndroidTargetArch arch) + { + if (string.IsNullOrEmpty (path)) { + throw new ArgumentException (nameof (path), "must not be null or empty"); + } + + if (!path.StartsWith ("assemblies/", StringComparison.Ordinal)) { + return new List { path }; + } + + var parts = path.Split ('/'); + if (parts.Length < 2) { + throw new InvalidOperationException ($"Path '{path}' must consist of at least two segments separated by `/`"); + } + + // We accept: + // assemblies/assembly.dll + // assemblies/{CULTURE}/assembly.dll + // assemblies/{ABI}/assembly.dll + // assemblies/{ABI}/{CULTURE}/assembly.dll + if (parts.Length > 4) { + throw new InvalidOperationException ($"Path '{path}' must not consist of more than 4 segments separated by `/`"); + } + + string? fileName = null; + string? culture = null; + string? abi = null; + + switch (parts.Length) { + // Full satellite assembly path, with abi + case 4: + abi = parts[1]; + culture = parts[2]; + fileName = parts[3]; + break; + + // Assembly path with abi or culture + case 3: + // If the middle part isn't a valid abi, we treat it as a culture name + if (MonoAndroidHelper.IsValidAbi (parts[1])) { + abi = parts[1]; + } else { + culture = parts[1]; + } + fileName = parts[2]; + break; + + // Assembly path without abi or culture + case 2: + fileName = parts[1]; + break; + } + + var fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER; + var abis = new List (); + if (!string.IsNullOrEmpty (abi)) { + abis.Add (abi); + } else if (arch == AndroidTargetArch.None) { + foreach (AndroidTargetArch targetArch in MonoAndroidHelper.SupportedTargetArchitectures) { + abis.Add (MonoAndroidHelper.ArchToAbi (targetArch)); + } + } else { + abis.Add (MonoAndroidHelper.ArchToAbi (arch)); + } + + if (!string.IsNullOrEmpty (culture)) { + // Android doesn't allow us to put satellite assemblies in lib/{CULTURE}/assembly.dll.so, we must instead + // mangle the name. + fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER; + fileName = $"{culture}{MonoAndroidHelper.SATELLITE_CULTURE_END_MARKER_CHAR}{fileName}"; + } + + var ret = new List (); + var newParts = new List { + string.Empty, // ABI placeholder + $"{fileTypeMarker}{fileName}.so", + }; + + foreach (var a in abis) { + newParts[0] = a; + ret.Add (MonoAndroidHelper.MakeZipArchivePath ("lib", newParts)); + } + + return ret; + } + + internal static bool ArchiveContains (List archiveContents, string entryPath, AndroidTargetArch arch) + { + if (archiveContents.Count == 0) { + return false; + } + + var potentialEntries = TransformArchiveAssemblyPath (entryPath, arch); + if (potentialEntries == null || potentialEntries.Count == 0) { + return false; + } + + foreach (var wantedEntry in potentialEntries) { + Console.WriteLine ($"Wanted entry: {wantedEntry}"); + foreach (var existingEntry in archiveContents) { + if (string.Compare (existingEntry, wantedEntry, StringComparison.Ordinal) == 0) { + return true; + } + } + } + + return false; + } + + /// + /// Checks whether exists in the archive or assembly store. The path should use the + /// "old style" `assemblies/{ABI}/assembly.dll` format. + /// + public bool Exists (string entryPath, bool forceRefresh = false, AndroidTargetArch arch = AndroidTargetArch.None) + { + return ArchiveContains (ListArchiveContents (_assembliesRootDir, forceRefresh), entryPath, arch); + } + + public void Contains (ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) + { + if (fileNames == null) { + throw new ArgumentNullException (nameof (fileNames)); + } + + if (fileNames.Count == 0) { + throw new ArgumentException ("must not be empty", nameof (fileNames)); + } + + if (_useAssemblyStores) { + StoreContains (fileNames, out existingFiles, out missingFiles, out additionalFiles, targetArches); + } else { + ArchiveContains (fileNames, out existingFiles, out missingFiles, out additionalFiles, targetArches); + } + } + + private List GetSupportedArches(IEnumerable? runtimeIdentifiers) + { + var rids = new List (); + if (runtimeIdentifiers != null) { + rids.AddRange (runtimeIdentifiers); + } + + if (rids.Count == 0) { + rids.AddRange (MonoAndroidHelper.SupportedTargetArchitectures); + } + + return rids; + } + + void ListFiles (List existingFiles, List missingFiles, List additionalFiles) + { + Console.WriteLine ("Archive contents:"); + ListFiles ("existing files", existingFiles); + ListFiles ("missing files", missingFiles); + ListFiles ("additional files", additionalFiles); + + void ListFiles (string label, List list) + { + Console.WriteLine ($" {label}:"); + if (list.Count == 0) { + Console.WriteLine (" none"); + return; + } + + foreach (string file in list) { + Console.WriteLine ($" {file}"); + } + } + } + + (string prefixAssemblies, string prefixLib) GetArchivePrefixes (string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath (_assembliesRootDir, abi)}/", $"lib/{abi}/"); + + internal void ArchiveContains (ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) + { + using var zip = ZipFile.OpenRead(_archivePath); + existingFiles = zip.Entries.Where (a => a.FullName.StartsWith (_assembliesRootDir, StringComparison.InvariantCultureIgnoreCase)).Select (a => a.FullName).ToList (); + existingFiles.AddRange (zip.Entries.Where (a => a.FullName.StartsWith ("lib/", StringComparison.OrdinalIgnoreCase)).Select (a => a.FullName)); + + var arches = GetSupportedArches (targetArches); + + missingFiles = []; + additionalFiles = []; + foreach (var arch in arches) { + string abi = MonoAndroidHelper.ArchToAbi (arch); + missingFiles.AddRange (GetMissingFilesForAbi (abi)); + additionalFiles.AddRange (GetAdditionalFilesForAbi (abi, existingFiles)); + } + ListFiles (existingFiles, missingFiles, additionalFiles); + return; + + IEnumerable GetMissingFilesForAbi (string abi) + { + var (prefixAssemblies, prefixLib) = GetArchivePrefixes (abi); + return fileNames.Where (x => { + string? culture = null; + var fileName = x; + var slashIndex = x.IndexOf ('/'); + if (slashIndex > 0) { + culture = x.Substring (0, slashIndex); + fileName = x.Substring (slashIndex + 1); + } + + return !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixAssemblies, x)) && + !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixLib, x)) && + !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixAssemblies, MonoAndroidHelper.MakeDiscreteAssembliesEntryName (fileName, culture))) && + !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixLib, MonoAndroidHelper.MakeDiscreteAssembliesEntryName (fileName, culture))); + }); + } + + IEnumerable GetAdditionalFilesForAbi (string abi, List existingFiles) + { + var (prefixAssemblies, prefixLib) = GetArchivePrefixes (abi); + return existingFiles.Where (x => !fileNames.Contains (x.Replace (prefixAssemblies, string.Empty)) && !fileNames.Contains (x.Replace (prefixLib, string.Empty))); + } + } + + internal void StoreContains (ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) + { + var assemblyNames = fileNames.Where (x => x.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)).ToList (); + var configFiles = fileNames.Where (x => x.EndsWith (".config", StringComparison.OrdinalIgnoreCase)).ToList (); + var debugFiles = fileNames.Where (x => x.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase)).ToList (); + var otherFiles = fileNames.Where (x => !SpecialExtensions.Contains (Path.GetExtension (x))).ToList (); + + existingFiles = new List (); + missingFiles = new List (); + additionalFiles = new List (); + + using var zip = ZipFile.OpenRead(_archivePath); + + var arches = GetSupportedArches (targetArches); + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); + + foreach (var arch in arches) { + var explorer = SelectExplorer (explorers, arch); + if (explorer == null) { + continue; + } + + if (otherFiles.Count > 0) { + var (prefixAssemblies, prefixLib) = GetArchivePrefixes (MonoAndroidHelper.ArchToAbi (arch)); + + foreach (string file in otherFiles) { + var fullPath = prefixAssemblies + file; + if (zip.ContainsEntry (fullPath)) { + existingFiles.Add (file); + } + + fullPath = prefixLib + file; + if (zip.ContainsEntry (fullPath)) { + existingFiles.Add (file); + } + } + } + + if (explorer.AssembliesByName is not null) + { + foreach (var f in explorer.AssembliesByName) + { + Console.WriteLine($"DEBUG!\tKey:{f.Key}"); + } + + if (explorer.AssembliesByName.Count != 0) + { + existingFiles.AddRange(explorer.AssembliesByName.Keys); + + // We need to fake config and debug files since they have no named entries in the storeReader + foreach (var file in configFiles) + { + var asm = GetStoreAssembly(explorer, file); + if (asm == null) + { + continue; + } + + if (asm.ConfigOffset > 0) + { + existingFiles.Add(file); + } + } + + foreach (string file in debugFiles) + { + var asm = GetStoreAssembly(explorer, file); + if (asm == null) + { + continue; + } + + if (asm.DebugOffset > 0) + { + existingFiles.Add(file); + } + } + } + } + } + + foreach (string file in fileNames) { + if (existingFiles.Contains (file)) { + continue; + } + missingFiles.Add (file); + } + + additionalFiles = existingFiles.Where (x => !fileNames.Contains (x)).ToList (); + ListFiles (existingFiles, missingFiles, additionalFiles); + return; + + AssemblyStoreItem? GetStoreAssembly (AssemblyStoreExplorer explorer, string file) + { + var assemblyName = Path.GetFileNameWithoutExtension (file); + return explorer.AssembliesByName?.TryGetValue(assemblyName, out var asm) is true + ? asm + : null; + } + } +} diff --git a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs index 3a070ac27b..7eae0392ce 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs +++ b/test/Sentry.Android.AssemblyReader.Tests/AndroidAssemblyReaderTests.cs @@ -1,3 +1,5 @@ +using Sentry.Android.AssemblyReader.V1; + namespace Sentry.Android.AssemblyReader.Tests; public class AndroidAssemblyReaderTests @@ -58,7 +60,7 @@ public void CreatesCorrectReader(bool isAssemblyStore) } else { - Assert.IsType(sut); + Assert.IsType(sut); } } @@ -86,12 +88,6 @@ public void ReadsAssembly(bool isAssemblyStore, bool isCompressed, string assemb // No need to run all combinations - we only test the current APK which is (likely) compressed assembly store. Skip.If(!isAssemblyStore); Skip.If(!isCompressed); -#endif -#if NET9_0_OR_GREATER - // Building without an assembly store is not yet supported in net9.0 and above - // TODO: See if we can support this. There might be some hints as to how this works at: - // https://github.com/dotnet/android/blob/6394773fad5108b0d7b4e6f087dc3e6ea997401a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs#L61-L65 - Skip.If(!isAssemblyStore); #endif using var sut = GetSut(isAssemblyStore, isCompressed); From de304b984f4d477231b99fdc4863fa463649b7f0 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Fri, 21 Mar 2025 02:57:19 +0000 Subject: [PATCH 21/22] Format code --- .../V2/AndroidAssemblyDirectoryReaderV2.cs | 2 +- .../V2/ArchiveAssemblyHelper.cs | 1142 +++++++++-------- .../V2/MonoAndroidHelper.Basic.cs | 24 +- 3 files changed, 638 insertions(+), 530 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs index 8ca5a6e4b9..1ea64b2471 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs @@ -6,7 +6,7 @@ namespace Sentry.Android.AssemblyReader.V2; internal sealed class AndroidAssemblyDirectoryReaderV2 : IAndroidAssemblyReader { private DebugLogger? Logger { get; } - private HashSet SupportedArchitectures { get; } = new (); + private HashSet SupportedArchitectures { get; } = new(); private readonly ArchiveAssemblyHelper _archiveAssemblyHelper; public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedAbis, DebugLogger? logger) diff --git a/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs b/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs index 5d40c9373c..a2044fbe8b 100644 --- a/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs +++ b/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs @@ -8,11 +8,11 @@ internal class ArchiveAssemblyHelper { private const string DefaultAssemblyStoreEntryPrefix = "{storeReader}"; - private static readonly HashSet SpecialExtensions = new (StringComparer.OrdinalIgnoreCase) { - ".dll", - ".config", - ".pdb", - }; + private static readonly HashSet SpecialExtensions = new(StringComparer.OrdinalIgnoreCase) { + ".dll", + ".config", + ".pdb", + }; private static readonly ArrayPool Buffers = ArrayPool.Shared; @@ -22,527 +22,625 @@ internal class ArchiveAssemblyHelper private readonly bool _useAssemblyStores; private List? _archiveContents; - public ArchiveAssemblyHelper (string archivePath, DebugLogger? logger, bool useAssemblyStores = true) - { - if (string.IsNullOrEmpty (archivePath)) { - throw new ArgumentException ("must not be null or empty", nameof (archivePath)); - } + public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger, bool useAssemblyStores = true) + { + if (string.IsNullOrEmpty(archivePath)) + { + throw new ArgumentException("must not be null or empty", nameof(archivePath)); + } - _archivePath = archivePath; + _archivePath = archivePath; _logger = logger; _useAssemblyStores = useAssemblyStores; - var extension = Path.GetExtension (archivePath) ?? string.Empty; - if (string.Compare (".aab", extension, StringComparison.OrdinalIgnoreCase) == 0) { - _assembliesRootDir = "base/lib/"; - } else if (string.Compare (".apk", extension, StringComparison.OrdinalIgnoreCase) == 0) { - _assembliesRootDir = "lib/"; - } else if (string.Compare (".zip", extension, StringComparison.OrdinalIgnoreCase) == 0) { - _assembliesRootDir = "lib/"; - } else { - _assembliesRootDir = string.Empty; - } - } - - public MemoryStream? ReadEntry (string path, AndroidTargetArch arch = AndroidTargetArch.None, bool uncompressIfNecessary = false) - { - var ret = _useAssemblyStores - ? ReadStoreEntry (path, arch, uncompressIfNecessary) - : ReadZipEntry (path, arch); - - if (ret == null) { - return null; - } - - ret.Flush (); - ret.Seek (0, SeekOrigin.Begin); - var (elfPayloadOffset, elfPayloadSize, error) = Utils.FindELFPayloadSectionOffsetAndSize (ret); - - if (error != ELFPayloadError.None) { - var message = error switch { - ELFPayloadError.NotELF => $"Entry '{path}' is not a valid ELF binary", - ELFPayloadError.LoadFailed => $"Entry '{path}' could not be loaded", - ELFPayloadError.NotSharedLibrary => $"Entry '{path}' is not a shared ELF library", - ELFPayloadError.NotLittleEndian => $"Entry '{path}' is not a little-endian ELF image", - ELFPayloadError.NoPayloadSection => $"Entry '{path}' does not contain the 'payload' section", - _ => $"Unknown ELF payload section error for entry '{path}': {error}" - }; - Console.WriteLine (message); - } else { - Console.WriteLine ($"Extracted content from ELF image '{path}'"); - } - - if (elfPayloadOffset == 0) { - ret.Seek (0, SeekOrigin.Begin); - return ret; - } - - // Make a copy of JUST the payload section, so that it contains only the data the tests expect and support - var payload = new MemoryStream (); - var data = Buffers.Rent (16384); - var toRead = data.Length; - var nRead = 0; - var remaining = elfPayloadSize; - - ret.Seek ((long)elfPayloadOffset, SeekOrigin.Begin); - while (remaining > 0 && (nRead = ret.Read (data, 0, toRead)) > 0) { - payload.Write (data, 0, nRead); - remaining -= (ulong)nRead; - - if (remaining < (ulong)data.Length) { - // Make sure the last chunk doesn't gobble in more than we need - toRead = (int)remaining; - } - } - Buffers.Return (data); - - payload.Flush (); - ret.Dispose (); - - payload.Seek (0, SeekOrigin.Begin); - return payload; - } - - private MemoryStream? ReadZipEntry (string path, AndroidTargetArch arch) - { - var potentialEntries = TransformArchiveAssemblyPath (path, arch); - if (potentialEntries == null || potentialEntries.Count == 0) { - return null; - } - - using var zip = ZipFile.OpenRead(_archivePath); - foreach (var assemblyPath in potentialEntries) { - if (zip.GetEntry(assemblyPath) is not {} entry) + var extension = Path.GetExtension(archivePath) ?? string.Empty; + if (string.Compare(".aab", extension, StringComparison.OrdinalIgnoreCase) == 0) + { + _assembliesRootDir = "base/lib/"; + } + else if (string.Compare(".apk", extension, StringComparison.OrdinalIgnoreCase) == 0) + { + _assembliesRootDir = "lib/"; + } + else if (string.Compare(".zip", extension, StringComparison.OrdinalIgnoreCase) == 0) + { + _assembliesRootDir = "lib/"; + } + else + { + _assembliesRootDir = string.Empty; + } + } + + public MemoryStream? ReadEntry(string path, AndroidTargetArch arch = AndroidTargetArch.None, bool uncompressIfNecessary = false) + { + var ret = _useAssemblyStores + ? ReadStoreEntry(path, arch, uncompressIfNecessary) + : ReadZipEntry(path, arch); + + if (ret == null) + { + return null; + } + + ret.Flush(); + ret.Seek(0, SeekOrigin.Begin); + var (elfPayloadOffset, elfPayloadSize, error) = Utils.FindELFPayloadSectionOffsetAndSize(ret); + + if (error != ELFPayloadError.None) + { + var message = error switch + { + ELFPayloadError.NotELF => $"Entry '{path}' is not a valid ELF binary", + ELFPayloadError.LoadFailed => $"Entry '{path}' could not be loaded", + ELFPayloadError.NotSharedLibrary => $"Entry '{path}' is not a shared ELF library", + ELFPayloadError.NotLittleEndian => $"Entry '{path}' is not a little-endian ELF image", + ELFPayloadError.NoPayloadSection => $"Entry '{path}' does not contain the 'payload' section", + _ => $"Unknown ELF payload section error for entry '{path}': {error}" + }; + Console.WriteLine(message); + } + else + { + Console.WriteLine($"Extracted content from ELF image '{path}'"); + } + + if (elfPayloadOffset == 0) + { + ret.Seek(0, SeekOrigin.Begin); + return ret; + } + + // Make a copy of JUST the payload section, so that it contains only the data the tests expect and support + var payload = new MemoryStream(); + var data = Buffers.Rent(16384); + var toRead = data.Length; + var nRead = 0; + var remaining = elfPayloadSize; + + ret.Seek((long)elfPayloadOffset, SeekOrigin.Begin); + while (remaining > 0 && (nRead = ret.Read(data, 0, toRead)) > 0) + { + payload.Write(data, 0, nRead); + remaining -= (ulong)nRead; + + if (remaining < (ulong)data.Length) + { + // Make sure the last chunk doesn't gobble in more than we need + toRead = (int)remaining; + } + } + Buffers.Return(data); + + payload.Flush(); + ret.Dispose(); + + payload.Seek(0, SeekOrigin.Begin); + return payload; + } + + private MemoryStream? ReadZipEntry(string path, AndroidTargetArch arch) + { + var potentialEntries = TransformArchiveAssemblyPath(path, arch); + if (potentialEntries == null || potentialEntries.Count == 0) + { + return null; + } + + using var zip = ZipFile.OpenRead(_archivePath); + foreach (var assemblyPath in potentialEntries) + { + if (zip.GetEntry(assemblyPath) is not { } entry) { continue; - } - - var ret = entry.Extract(); - ret.Flush(); - return ret; - } - - return null; - } - - private MemoryStream? ReadStoreEntry (string path, AndroidTargetArch arch, bool uncompressIfNecessary) - { - var name = Path.GetFileNameWithoutExtension (path); - var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); - var explorer = SelectExplorer (explorers, arch); - if (explorer == null) { - Console.WriteLine ($"Failed to read assembly '{name}' from '{_archivePath}'. {errorMessage}"); - return null; - } - - if (arch == AndroidTargetArch.None) { - if (explorer.TargetArch == null) { - throw new InvalidOperationException ($"Internal error: explorer should not have its TargetArch unset"); - } - - arch = (AndroidTargetArch)explorer.TargetArch; - } - - Console.WriteLine ($"Trying to read store entry: {name}"); - var assemblies = explorer.Find (name, arch); - if (assemblies == null) { - Console.WriteLine ($"Failed to locate assembly '{name}' in assembly store for architecture '{arch}', in archive '{_archivePath}'"); - return null; - } - - AssemblyStoreItem? assembly = null; - foreach (var item in assemblies) { - if (arch == AndroidTargetArch.None || item.TargetArch == arch) { - assembly = item; - break; - } - } - - if (assembly == null) { - Console.WriteLine ($"Failed to find assembly '{name}' in assembly store for architecture '{arch}', in archive '{_archivePath}'"); - return null; - } - - return explorer.ReadImageData (assembly, uncompressIfNecessary); - } - - public List ListArchiveContents (string storeEntryPrefix = DefaultAssemblyStoreEntryPrefix, bool forceRefresh = false, AndroidTargetArch arch = AndroidTargetArch.None) - { - if (!forceRefresh && _archiveContents != null) { - return _archiveContents; - } - - if (string.IsNullOrEmpty (storeEntryPrefix)) { - throw new ArgumentException (nameof (storeEntryPrefix), "must not be null or empty"); - } - - var entries = new List (); - using (var zip = ZipFile.OpenRead(_archivePath)) { - foreach (var entry in zip.Entries) { - entries.Add(entry.FullName); - } - } - - _archiveContents = entries; - if (!_useAssemblyStores) { - Console.WriteLine ("Not using assembly stores"); - return entries; - } - - Console.WriteLine ($"Creating AssemblyStoreExplorer for archive '{_archivePath}'"); - var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); - - if (arch == AndroidTargetArch.None) { - if (explorers == null || explorers.Count == 0) { - return entries; - } - - foreach (var explorer in explorers) { - SynthetizeAssemblies (explorer); - } - } else { - SynthetizeAssemblies (SelectExplorer (explorers, arch)); - } - - Console.WriteLine ("Archive entries with synthetised assembly storeReader entries:"); - foreach (string e in entries) { - Console.WriteLine ($" {e}"); - } - - return entries; - - void SynthetizeAssemblies (AssemblyStoreExplorer? explorer) - { - if (explorer == null) { - return; - } - - Console.WriteLine ($"Explorer for {explorer.TargetArch} found {explorer.AssemblyCount} assemblies"); + } + + var ret = entry.Extract(); + ret.Flush(); + return ret; + } + + return null; + } + + private MemoryStream? ReadStoreEntry(string path, AndroidTargetArch arch, bool uncompressIfNecessary) + { + var name = Path.GetFileNameWithoutExtension(path); + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); + var explorer = SelectExplorer(explorers, arch); + if (explorer == null) + { + Console.WriteLine($"Failed to read assembly '{name}' from '{_archivePath}'. {errorMessage}"); + return null; + } + + if (arch == AndroidTargetArch.None) + { + if (explorer.TargetArch == null) + { + throw new InvalidOperationException($"Internal error: explorer should not have its TargetArch unset"); + } + + arch = (AndroidTargetArch)explorer.TargetArch; + } + + Console.WriteLine($"Trying to read store entry: {name}"); + var assemblies = explorer.Find(name, arch); + if (assemblies == null) + { + Console.WriteLine($"Failed to locate assembly '{name}' in assembly store for architecture '{arch}', in archive '{_archivePath}'"); + return null; + } + + AssemblyStoreItem? assembly = null; + foreach (var item in assemblies) + { + if (arch == AndroidTargetArch.None || item.TargetArch == arch) + { + assembly = item; + break; + } + } + + if (assembly == null) + { + Console.WriteLine($"Failed to find assembly '{name}' in assembly store for architecture '{arch}', in archive '{_archivePath}'"); + return null; + } + + return explorer.ReadImageData(assembly, uncompressIfNecessary); + } + + public List ListArchiveContents(string storeEntryPrefix = DefaultAssemblyStoreEntryPrefix, bool forceRefresh = false, AndroidTargetArch arch = AndroidTargetArch.None) + { + if (!forceRefresh && _archiveContents != null) + { + return _archiveContents; + } + + if (string.IsNullOrEmpty(storeEntryPrefix)) + { + throw new ArgumentException(nameof(storeEntryPrefix), "must not be null or empty"); + } + + var entries = new List(); + using (var zip = ZipFile.OpenRead(_archivePath)) + { + foreach (var entry in zip.Entries) + { + entries.Add(entry.FullName); + } + } + + _archiveContents = entries; + if (!_useAssemblyStores) + { + Console.WriteLine("Not using assembly stores"); + return entries; + } + + Console.WriteLine($"Creating AssemblyStoreExplorer for archive '{_archivePath}'"); + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); + + if (arch == AndroidTargetArch.None) + { + if (explorers == null || explorers.Count == 0) + { + return entries; + } + + foreach (var explorer in explorers) + { + SynthetizeAssemblies(explorer); + } + } + else + { + SynthetizeAssemblies(SelectExplorer(explorers, arch)); + } + + Console.WriteLine("Archive entries with synthetised assembly storeReader entries:"); + foreach (string e in entries) + { + Console.WriteLine($" {e}"); + } + + return entries; + + void SynthetizeAssemblies(AssemblyStoreExplorer? explorer) + { + if (explorer == null) + { + return; + } + + Console.WriteLine($"Explorer for {explorer.TargetArch} found {explorer.AssemblyCount} assemblies"); if (explorer.Assemblies is null) { return; } - foreach (var asm in explorer.Assemblies) { - var prefix = storeEntryPrefix; - var abi = MonoAndroidHelper.ArchToAbi (asm.TargetArch); - prefix = $"{prefix}{abi}/"; - - var cultureIndex = asm.Name.IndexOf ('/'); - string? culture = null; - string name; - - if (cultureIndex > 0) { - culture = asm.Name.Substring (0, cultureIndex); - name = asm.Name.Substring (cultureIndex + 1); - } else { - name = asm.Name; - } - - // Mangle name in the same fashion the discrete assembly entries are named, makes other - // code in this class simpler. - var mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (name, culture); - entries.Add ($"{prefix}{mangledName}"); - if (asm.DebugOffset > 0) { - mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.ChangeExtension (name, "pdb")); - entries.Add ($"{prefix}{mangledName}"); - } - - if (asm.ConfigOffset > 0) { - mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.ChangeExtension (name, "config")); - entries.Add ($"{prefix}{mangledName}"); - } - } - } - } - - internal AssemblyStoreExplorer? SelectExplorer (IList? explorers, string rid) - { - return SelectExplorer (explorers, MonoAndroidHelper.RidToArch (rid)); - } - - internal AssemblyStoreExplorer? SelectExplorer (IList? explorers, AndroidTargetArch arch) - { - if (explorers == null || explorers.Count == 0) { - return null; - } - - // If we don't care about target architecture, we check the first store, since all of them will have the same - // assemblies. Otherwise, we try to locate the correct store. - if (arch == AndroidTargetArch.None) { - return explorers[0]; - } - - foreach (var e in explorers) { - if (e.TargetArch == null || e.TargetArch != arch) { - continue; - } - return e; - } - - Console.WriteLine ($"Failed to find assembly store for architecture '{arch}' in archive '{_archivePath}'"); - return null; - } - - /// - /// Takes "old style" `assemblies/assembly.dll` path and returns (if possible) a set of paths that reflect the new - /// location of `lib/{ARCH}/assembly.dll.so`. A list is returned because, if `arch` is `None`, we'll return all - /// the possible architectural paths. - /// An exception is thrown if we cannot transform the path for some reason. It should **not** be handled. - /// - private static List? TransformArchiveAssemblyPath (string path, AndroidTargetArch arch) - { - if (string.IsNullOrEmpty (path)) { - throw new ArgumentException (nameof (path), "must not be null or empty"); - } - - if (!path.StartsWith ("assemblies/", StringComparison.Ordinal)) { - return new List { path }; - } - - var parts = path.Split ('/'); - if (parts.Length < 2) { - throw new InvalidOperationException ($"Path '{path}' must consist of at least two segments separated by `/`"); - } - - // We accept: - // assemblies/assembly.dll - // assemblies/{CULTURE}/assembly.dll - // assemblies/{ABI}/assembly.dll - // assemblies/{ABI}/{CULTURE}/assembly.dll - if (parts.Length > 4) { - throw new InvalidOperationException ($"Path '{path}' must not consist of more than 4 segments separated by `/`"); - } - - string? fileName = null; - string? culture = null; - string? abi = null; - - switch (parts.Length) { - // Full satellite assembly path, with abi - case 4: - abi = parts[1]; - culture = parts[2]; - fileName = parts[3]; - break; - - // Assembly path with abi or culture - case 3: - // If the middle part isn't a valid abi, we treat it as a culture name - if (MonoAndroidHelper.IsValidAbi (parts[1])) { - abi = parts[1]; - } else { - culture = parts[1]; - } - fileName = parts[2]; - break; - - // Assembly path without abi or culture - case 2: - fileName = parts[1]; - break; - } - - var fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER; - var abis = new List (); - if (!string.IsNullOrEmpty (abi)) { - abis.Add (abi); - } else if (arch == AndroidTargetArch.None) { - foreach (AndroidTargetArch targetArch in MonoAndroidHelper.SupportedTargetArchitectures) { - abis.Add (MonoAndroidHelper.ArchToAbi (targetArch)); - } - } else { - abis.Add (MonoAndroidHelper.ArchToAbi (arch)); - } - - if (!string.IsNullOrEmpty (culture)) { - // Android doesn't allow us to put satellite assemblies in lib/{CULTURE}/assembly.dll.so, we must instead - // mangle the name. - fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER; - fileName = $"{culture}{MonoAndroidHelper.SATELLITE_CULTURE_END_MARKER_CHAR}{fileName}"; - } - - var ret = new List (); - var newParts = new List { - string.Empty, // ABI placeholder + foreach (var asm in explorer.Assemblies) + { + var prefix = storeEntryPrefix; + var abi = MonoAndroidHelper.ArchToAbi(asm.TargetArch); + prefix = $"{prefix}{abi}/"; + + var cultureIndex = asm.Name.IndexOf('/'); + string? culture = null; + string name; + + if (cultureIndex > 0) + { + culture = asm.Name.Substring(0, cultureIndex); + name = asm.Name.Substring(cultureIndex + 1); + } + else + { + name = asm.Name; + } + + // Mangle name in the same fashion the discrete assembly entries are named, makes other + // code in this class simpler. + var mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName(name, culture); + entries.Add($"{prefix}{mangledName}"); + if (asm.DebugOffset > 0) + { + mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName(Path.ChangeExtension(name, "pdb")); + entries.Add($"{prefix}{mangledName}"); + } + + if (asm.ConfigOffset > 0) + { + mangledName = MonoAndroidHelper.MakeDiscreteAssembliesEntryName(Path.ChangeExtension(name, "config")); + entries.Add($"{prefix}{mangledName}"); + } + } + } + } + + internal AssemblyStoreExplorer? SelectExplorer(IList? explorers, string rid) + { + return SelectExplorer(explorers, MonoAndroidHelper.RidToArch(rid)); + } + + internal AssemblyStoreExplorer? SelectExplorer(IList? explorers, AndroidTargetArch arch) + { + if (explorers == null || explorers.Count == 0) + { + return null; + } + + // If we don't care about target architecture, we check the first store, since all of them will have the same + // assemblies. Otherwise, we try to locate the correct store. + if (arch == AndroidTargetArch.None) + { + return explorers[0]; + } + + foreach (var e in explorers) + { + if (e.TargetArch == null || e.TargetArch != arch) + { + continue; + } + return e; + } + + Console.WriteLine($"Failed to find assembly store for architecture '{arch}' in archive '{_archivePath}'"); + return null; + } + + /// + /// Takes "old style" `assemblies/assembly.dll` path and returns (if possible) a set of paths that reflect the new + /// location of `lib/{ARCH}/assembly.dll.so`. A list is returned because, if `arch` is `None`, we'll return all + /// the possible architectural paths. + /// An exception is thrown if we cannot transform the path for some reason. It should **not** be handled. + /// + private static List? TransformArchiveAssemblyPath(string path, AndroidTargetArch arch) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException(nameof(path), "must not be null or empty"); + } + + if (!path.StartsWith("assemblies/", StringComparison.Ordinal)) + { + return new List { path }; + } + + var parts = path.Split('/'); + if (parts.Length < 2) + { + throw new InvalidOperationException($"Path '{path}' must consist of at least two segments separated by `/`"); + } + + // We accept: + // assemblies/assembly.dll + // assemblies/{CULTURE}/assembly.dll + // assemblies/{ABI}/assembly.dll + // assemblies/{ABI}/{CULTURE}/assembly.dll + if (parts.Length > 4) + { + throw new InvalidOperationException($"Path '{path}' must not consist of more than 4 segments separated by `/`"); + } + + string? fileName = null; + string? culture = null; + string? abi = null; + + switch (parts.Length) + { + // Full satellite assembly path, with abi + case 4: + abi = parts[1]; + culture = parts[2]; + fileName = parts[3]; + break; + + // Assembly path with abi or culture + case 3: + // If the middle part isn't a valid abi, we treat it as a culture name + if (MonoAndroidHelper.IsValidAbi(parts[1])) + { + abi = parts[1]; + } + else + { + culture = parts[1]; + } + fileName = parts[2]; + break; + + // Assembly path without abi or culture + case 2: + fileName = parts[1]; + break; + } + + var fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER; + var abis = new List(); + if (!string.IsNullOrEmpty(abi)) + { + abis.Add(abi); + } + else if (arch == AndroidTargetArch.None) + { + foreach (AndroidTargetArch targetArch in MonoAndroidHelper.SupportedTargetArchitectures) + { + abis.Add(MonoAndroidHelper.ArchToAbi(targetArch)); + } + } + else + { + abis.Add(MonoAndroidHelper.ArchToAbi(arch)); + } + + if (!string.IsNullOrEmpty(culture)) + { + // Android doesn't allow us to put satellite assemblies in lib/{CULTURE}/assembly.dll.so, we must instead + // mangle the name. + fileTypeMarker = MonoAndroidHelper.MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER; + fileName = $"{culture}{MonoAndroidHelper.SATELLITE_CULTURE_END_MARKER_CHAR}{fileName}"; + } + + var ret = new List(); + var newParts = new List { + string.Empty, // ABI placeholder $"{fileTypeMarker}{fileName}.so", - }; - - foreach (var a in abis) { - newParts[0] = a; - ret.Add (MonoAndroidHelper.MakeZipArchivePath ("lib", newParts)); - } - - return ret; - } - - internal static bool ArchiveContains (List archiveContents, string entryPath, AndroidTargetArch arch) - { - if (archiveContents.Count == 0) { - return false; - } - - var potentialEntries = TransformArchiveAssemblyPath (entryPath, arch); - if (potentialEntries == null || potentialEntries.Count == 0) { - return false; - } - - foreach (var wantedEntry in potentialEntries) { - Console.WriteLine ($"Wanted entry: {wantedEntry}"); - foreach (var existingEntry in archiveContents) { - if (string.Compare (existingEntry, wantedEntry, StringComparison.Ordinal) == 0) { - return true; - } - } - } - - return false; - } - - /// - /// Checks whether exists in the archive or assembly store. The path should use the - /// "old style" `assemblies/{ABI}/assembly.dll` format. - /// - public bool Exists (string entryPath, bool forceRefresh = false, AndroidTargetArch arch = AndroidTargetArch.None) - { - return ArchiveContains (ListArchiveContents (_assembliesRootDir, forceRefresh), entryPath, arch); - } - - public void Contains (ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) - { - if (fileNames == null) { - throw new ArgumentNullException (nameof (fileNames)); - } - - if (fileNames.Count == 0) { - throw new ArgumentException ("must not be empty", nameof (fileNames)); - } - - if (_useAssemblyStores) { - StoreContains (fileNames, out existingFiles, out missingFiles, out additionalFiles, targetArches); - } else { - ArchiveContains (fileNames, out existingFiles, out missingFiles, out additionalFiles, targetArches); - } - } - - private List GetSupportedArches(IEnumerable? runtimeIdentifiers) - { - var rids = new List (); - if (runtimeIdentifiers != null) { - rids.AddRange (runtimeIdentifiers); - } - - if (rids.Count == 0) { - rids.AddRange (MonoAndroidHelper.SupportedTargetArchitectures); - } - - return rids; - } + }; + + foreach (var a in abis) + { + newParts[0] = a; + ret.Add(MonoAndroidHelper.MakeZipArchivePath("lib", newParts)); + } + + return ret; + } + + internal static bool ArchiveContains(List archiveContents, string entryPath, AndroidTargetArch arch) + { + if (archiveContents.Count == 0) + { + return false; + } + + var potentialEntries = TransformArchiveAssemblyPath(entryPath, arch); + if (potentialEntries == null || potentialEntries.Count == 0) + { + return false; + } + + foreach (var wantedEntry in potentialEntries) + { + Console.WriteLine($"Wanted entry: {wantedEntry}"); + foreach (var existingEntry in archiveContents) + { + if (string.Compare(existingEntry, wantedEntry, StringComparison.Ordinal) == 0) + { + return true; + } + } + } + + return false; + } + + /// + /// Checks whether exists in the archive or assembly store. The path should use the + /// "old style" `assemblies/{ABI}/assembly.dll` format. + /// + public bool Exists(string entryPath, bool forceRefresh = false, AndroidTargetArch arch = AndroidTargetArch.None) + { + return ArchiveContains(ListArchiveContents(_assembliesRootDir, forceRefresh), entryPath, arch); + } + + public void Contains(ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) + { + if (fileNames == null) + { + throw new ArgumentNullException(nameof(fileNames)); + } + + if (fileNames.Count == 0) + { + throw new ArgumentException("must not be empty", nameof(fileNames)); + } + + if (_useAssemblyStores) + { + StoreContains(fileNames, out existingFiles, out missingFiles, out additionalFiles, targetArches); + } + else + { + ArchiveContains(fileNames, out existingFiles, out missingFiles, out additionalFiles, targetArches); + } + } + + private List GetSupportedArches(IEnumerable? runtimeIdentifiers) + { + var rids = new List(); + if (runtimeIdentifiers != null) + { + rids.AddRange(runtimeIdentifiers); + } + + if (rids.Count == 0) + { + rids.AddRange(MonoAndroidHelper.SupportedTargetArchitectures); + } + return rids; + +<<<<<<< TODO: Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)', Before: void ListFiles (List existingFiles, List missingFiles, List additionalFiles) - { - Console.WriteLine ("Archive contents:"); - ListFiles ("existing files", existingFiles); - ListFiles ("missing files", missingFiles); - ListFiles ("additional files", additionalFiles); - - void ListFiles (string label, List list) - { - Console.WriteLine ($" {label}:"); - if (list.Count == 0) { - Console.WriteLine (" none"); - return; - } - - foreach (string file in list) { - Console.WriteLine ($" {file}"); - } - } - } +======= + private void ListFiles (List existingFiles, List missingFiles, List additionalFiles) +>>>>>>> After + } + + private void ListFiles(List existingFiles, List missingFiles, List additionalFiles) + { + Console.WriteLine("Archive contents:"); + ListFiles("existing files", existingFiles); + ListFiles("missing files", missingFiles); + ListFiles("additional files", additionalFiles); + + void ListFiles(string label, List list) + { + Console.WriteLine($" {label}:"); + if (list.Count == 0) + { + Console.WriteLine(" none"); + return; + } + + foreach (string file in list) + { + Console.WriteLine($" {file}"); +<<<<<<< TODO: Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)', Before: (string prefixAssemblies, string prefixLib) GetArchivePrefixes (string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath (_assembliesRootDir, abi)}/", $"lib/{abi}/"); +======= + private (string prefixAssemblies, string prefixLib) GetArchivePrefixes (string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath (_assembliesRootDir, abi)}/", $"lib/{abi}/"); +>>>>>>> After + } + } + } + + private (string prefixAssemblies, string prefixLib) GetArchivePrefixes(string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath(_assembliesRootDir, abi)}/", $"lib/{abi}/"); - internal void ArchiveContains (ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) - { - using var zip = ZipFile.OpenRead(_archivePath); - existingFiles = zip.Entries.Where (a => a.FullName.StartsWith (_assembliesRootDir, StringComparison.InvariantCultureIgnoreCase)).Select (a => a.FullName).ToList (); - existingFiles.AddRange (zip.Entries.Where (a => a.FullName.StartsWith ("lib/", StringComparison.OrdinalIgnoreCase)).Select (a => a.FullName)); - - var arches = GetSupportedArches (targetArches); - - missingFiles = []; - additionalFiles = []; - foreach (var arch in arches) { - string abi = MonoAndroidHelper.ArchToAbi (arch); - missingFiles.AddRange (GetMissingFilesForAbi (abi)); - additionalFiles.AddRange (GetAdditionalFilesForAbi (abi, existingFiles)); - } - ListFiles (existingFiles, missingFiles, additionalFiles); + internal void ArchiveContains(ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) + { + using var zip = ZipFile.OpenRead(_archivePath); + existingFiles = zip.Entries.Where(a => a.FullName.StartsWith(_assembliesRootDir, StringComparison.InvariantCultureIgnoreCase)).Select(a => a.FullName).ToList(); + existingFiles.AddRange(zip.Entries.Where(a => a.FullName.StartsWith("lib/", StringComparison.OrdinalIgnoreCase)).Select(a => a.FullName)); + + var arches = GetSupportedArches(targetArches); + + missingFiles = []; + additionalFiles = []; + foreach (var arch in arches) + { + string abi = MonoAndroidHelper.ArchToAbi(arch); + missingFiles.AddRange(GetMissingFilesForAbi(abi)); + additionalFiles.AddRange(GetAdditionalFilesForAbi(abi, existingFiles)); + } + ListFiles(existingFiles, missingFiles, additionalFiles); return; - IEnumerable GetMissingFilesForAbi (string abi) - { - var (prefixAssemblies, prefixLib) = GetArchivePrefixes (abi); - return fileNames.Where (x => { - string? culture = null; - var fileName = x; - var slashIndex = x.IndexOf ('/'); - if (slashIndex > 0) { - culture = x.Substring (0, slashIndex); - fileName = x.Substring (slashIndex + 1); - } - - return !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixAssemblies, x)) && - !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixLib, x)) && - !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixAssemblies, MonoAndroidHelper.MakeDiscreteAssembliesEntryName (fileName, culture))) && - !zip.ContainsEntry (MonoAndroidHelper.MakeZipArchivePath (prefixLib, MonoAndroidHelper.MakeDiscreteAssembliesEntryName (fileName, culture))); - }); - } - - IEnumerable GetAdditionalFilesForAbi (string abi, List existingFiles) - { - var (prefixAssemblies, prefixLib) = GetArchivePrefixes (abi); - return existingFiles.Where (x => !fileNames.Contains (x.Replace (prefixAssemblies, string.Empty)) && !fileNames.Contains (x.Replace (prefixLib, string.Empty))); - } - } - - internal void StoreContains (ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) - { - var assemblyNames = fileNames.Where (x => x.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)).ToList (); - var configFiles = fileNames.Where (x => x.EndsWith (".config", StringComparison.OrdinalIgnoreCase)).ToList (); - var debugFiles = fileNames.Where (x => x.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase)).ToList (); - var otherFiles = fileNames.Where (x => !SpecialExtensions.Contains (Path.GetExtension (x))).ToList (); - - existingFiles = new List (); - missingFiles = new List (); - additionalFiles = new List (); - - using var zip = ZipFile.OpenRead(_archivePath); - - var arches = GetSupportedArches (targetArches); - var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); - - foreach (var arch in arches) { - var explorer = SelectExplorer (explorers, arch); - if (explorer == null) { - continue; - } - - if (otherFiles.Count > 0) { - var (prefixAssemblies, prefixLib) = GetArchivePrefixes (MonoAndroidHelper.ArchToAbi (arch)); - - foreach (string file in otherFiles) { - var fullPath = prefixAssemblies + file; - if (zip.ContainsEntry (fullPath)) { - existingFiles.Add (file); - } - - fullPath = prefixLib + file; - if (zip.ContainsEntry (fullPath)) { - existingFiles.Add (file); - } - } - } + IEnumerable GetMissingFilesForAbi(string abi) + { + var (prefixAssemblies, prefixLib) = GetArchivePrefixes(abi); + return fileNames.Where(x => + { + string? culture = null; + var fileName = x; + var slashIndex = x.IndexOf('/'); + if (slashIndex > 0) + { + culture = x.Substring(0, slashIndex); + fileName = x.Substring(slashIndex + 1); + } + + return !zip.ContainsEntry(MonoAndroidHelper.MakeZipArchivePath(prefixAssemblies, x)) && + !zip.ContainsEntry(MonoAndroidHelper.MakeZipArchivePath(prefixLib, x)) && + !zip.ContainsEntry(MonoAndroidHelper.MakeZipArchivePath(prefixAssemblies, MonoAndroidHelper.MakeDiscreteAssembliesEntryName(fileName, culture))) && + !zip.ContainsEntry(MonoAndroidHelper.MakeZipArchivePath(prefixLib, MonoAndroidHelper.MakeDiscreteAssembliesEntryName(fileName, culture))); + }); + } + + IEnumerable GetAdditionalFilesForAbi(string abi, List existingFiles) + { + var (prefixAssemblies, prefixLib) = GetArchivePrefixes(abi); + return existingFiles.Where(x => !fileNames.Contains(x.Replace(prefixAssemblies, string.Empty)) && !fileNames.Contains(x.Replace(prefixLib, string.Empty))); + } + } + + internal void StoreContains(ICollection fileNames, out List existingFiles, out List missingFiles, out List additionalFiles, IEnumerable? targetArches = null) + { + var assemblyNames = fileNames.Where(x => x.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)).ToList(); + var configFiles = fileNames.Where(x => x.EndsWith(".config", StringComparison.OrdinalIgnoreCase)).ToList(); + var debugFiles = fileNames.Where(x => x.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)).ToList(); + var otherFiles = fileNames.Where(x => !SpecialExtensions.Contains(Path.GetExtension(x))).ToList(); + + existingFiles = new List(); + missingFiles = new List(); + additionalFiles = new List(); + + using var zip = ZipFile.OpenRead(_archivePath); + + var arches = GetSupportedArches(targetArches); + var (explorers, errorMessage) = AssemblyStoreExplorer.Open(_archivePath, _logger); + + foreach (var arch in arches) + { + var explorer = SelectExplorer(explorers, arch); + if (explorer == null) + { + continue; + } + + if (otherFiles.Count > 0) + { + var (prefixAssemblies, prefixLib) = GetArchivePrefixes(MonoAndroidHelper.ArchToAbi(arch)); + + foreach (string file in otherFiles) + { + var fullPath = prefixAssemblies + file; + if (zip.ContainsEntry(fullPath)) + { + existingFiles.Add(file); + } + + fullPath = prefixLib + file; + if (zip.ContainsEntry(fullPath)) + { + existingFiles.Add(file); + } + } + } if (explorer.AssembliesByName is not null) { @@ -587,23 +685,25 @@ internal void StoreContains (ICollection fileNames, out List exi } } - foreach (string file in fileNames) { - if (existingFiles.Contains (file)) { - continue; - } - missingFiles.Add (file); - } + foreach (string file in fileNames) + { + if (existingFiles.Contains(file)) + { + continue; + } + missingFiles.Add(file); + } - additionalFiles = existingFiles.Where (x => !fileNames.Contains (x)).ToList (); - ListFiles (existingFiles, missingFiles, additionalFiles); + additionalFiles = existingFiles.Where(x => !fileNames.Contains(x)).ToList(); + ListFiles(existingFiles, missingFiles, additionalFiles); return; - AssemblyStoreItem? GetStoreAssembly (AssemblyStoreExplorer explorer, string file) - { - var assemblyName = Path.GetFileNameWithoutExtension (file); - return explorer.AssembliesByName?.TryGetValue(assemblyName, out var asm) is true + AssemblyStoreItem? GetStoreAssembly(AssemblyStoreExplorer explorer, string file) + { + var assemblyName = Path.GetFileNameWithoutExtension(file); + return explorer.AssembliesByName?.TryGetValue(assemblyName, out var asm) is true ? asm : null; - } - } + } + } } diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs index cb391dc253..aa665e5dd9 100644 --- a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -78,7 +78,8 @@ public static AndroidTargetArch AbiToTargetArch(string abi) if (!AbiToArchMap.TryGetValue(abi, out AndroidTargetArch arch)) { throw new NotSupportedException($"Internal error: unsupported ABI '{abi}'"); - }; + } + ; return arch; } @@ -88,7 +89,8 @@ public static string AbiToRid(string abi) if (!AbiToRidMap.TryGetValue(abi, out var rid)) { throw new NotSupportedException($"Internal error: unsupported ABI '{abi}'"); - }; + } + ; return rid; } @@ -98,7 +100,8 @@ public static string RidToAbi(string rid) if (!RidToAbiMap.TryGetValue(rid, out var abi)) { throw new NotSupportedException($"Internal error: unsupported Runtime Identifier '{rid}'"); - }; + } + ; return abi; } @@ -108,7 +111,8 @@ public static AndroidTargetArch RidToArchMaybe(string rid) if (!RidToArchMap.TryGetValue(rid, out AndroidTargetArch arch)) { return AndroidTargetArch.None; - }; + } + ; return arch; } @@ -119,7 +123,8 @@ public static AndroidTargetArch RidToArch(string rid) if (arch == AndroidTargetArch.None) { throw new NotSupportedException($"Internal error: unsupported Runtime Identifier '{rid}'"); - }; + } + ; return arch; } @@ -129,7 +134,8 @@ public static string ArchToRid(AndroidTargetArch arch) if (!ArchToRidMap.TryGetValue(arch, out var rid)) { throw new InvalidOperationException($"Internal error: unsupported architecture '{arch}'"); - }; + } + ; return rid; } @@ -139,7 +145,8 @@ public static string ArchToAbi(AndroidTargetArch arch) if (!ArchToAbiMap.TryGetValue(arch, out var abi)) { throw new InvalidOperationException($"Internal error: unsupported architecture '{arch}'"); - }; + } + ; return abi; } @@ -177,7 +184,8 @@ public static string MakeZipArchivePath(string part1, ICollection? pathP if (!string.IsNullOrEmpty(part1)) { parts.Add(part1.TrimEnd(ZipPathTrimmedChars)); - }; + } + ; if (pathParts != null && pathParts.Count > 0) { From 3423f09a9d0ae2e96707f6cf346154ba5ae4c318 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Fri, 21 Mar 2025 23:58:36 +0000 Subject: [PATCH 22/22] Format code --- src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs b/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs index a2044fbe8b..7dae377b06 100644 --- a/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs +++ b/src/Sentry.Android.AssemblyReader/V2/ArchiveAssemblyHelper.cs @@ -514,7 +514,7 @@ private List GetSupportedArches(IEnumerable existingFiles, List missingFiles, List additionalFiles) + void ListFiles(List existingFiles, List missingFiles, List additionalFiles) ======= private void ListFiles (List existingFiles, List missingFiles, List additionalFiles) >>>>>>> After @@ -541,7 +541,7 @@ void ListFiles(string label, List list) Console.WriteLine($" {file}"); <<<<<<< TODO: Unmerged change from project 'Sentry.Android.AssemblyReader(net9.0)', Before: - (string prefixAssemblies, string prefixLib) GetArchivePrefixes (string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath (_assembliesRootDir, abi)}/", $"lib/{abi}/"); + (string prefixAssemblies, string prefixLib) GetArchivePrefixes(string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath(_assembliesRootDir, abi)}/", $"lib/{abi}/"); ======= private (string prefixAssemblies, string prefixLib) GetArchivePrefixes (string abi) => ($"{MonoAndroidHelper.MakeZipArchivePath (_assembliesRootDir, abi)}/", $"lib/{abi}/"); >>>>>>> After