-
-
Notifications
You must be signed in to change notification settings - Fork 230
Expand file tree
/
Copy pathAndroidAssemblyStoreReaderV2.cs
More file actions
146 lines (126 loc) · 5.07 KB
/
AndroidAssemblyStoreReaderV2.cs
File metadata and controls
146 lines (126 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
namespace Sentry.Android.AssemblyReader.V2;
internal class AndroidAssemblyStoreReaderV2 : IAndroidAssemblyReader
{
private readonly IList<AssemblyStoreExplorer> _explorers;
private readonly DebugLogger? _logger;
private AndroidAssemblyStoreReaderV2(IList<AssemblyStoreExplorer> explorers, DebugLogger? logger)
{
_explorers = explorers;
_logger = logger;
}
public static bool TryReadStore(string inputFile, IList<string> supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReaderV2? reader)
{
List<AssemblyStoreExplorer> supportedExplorers = [];
// First we check the base.apk for an assembly store
var (explorers, errorMessage) = AssemblyStoreExplorer.Open(inputFile, logger);
if (explorers is null)
{
logger?.Invoke("Unable to read store information for {0}: {1}", inputFile, errorMessage);
// Check for assembly stores in any device specific APKs
foreach (var supportedAbi in supportedAbis)
{
var splitFilePath = inputFile.GetArchivePathForAbi(supportedAbi, logger);
if (!File.Exists(splitFilePath))
{
logger?.Invoke("No split config detected at: '{0}'", splitFilePath);
continue;
}
(explorers, errorMessage) = AssemblyStoreExplorer.Open(splitFilePath, logger);
if (explorers is not null)
{
supportedExplorers.AddRange(explorers); // If the error is null then this is not null
}
else
{
logger?.Invoke("Unable to read store information for {0}: {1}", splitFilePath, errorMessage);
}
}
}
else
{
foreach (var explorer in explorers)
{
if (explorer.TargetArch is null)
{
continue;
}
foreach (var supportedAbi in supportedAbis)
{
if (supportedAbi.AbiToDeviceArchitecture() == explorer.TargetArch)
{
supportedExplorers.Add(explorer);
}
}
}
}
if (supportedExplorers.Count == 0)
{
logger?.Invoke("Could not find V2 AssemblyStoreExplorer for the supported ABIs: {0}", string.Join(", ", supportedAbis));
reader = null;
return false;
}
reader = new AndroidAssemblyStoreReaderV2(supportedExplorers, logger);
return true;
}
public PEReader? TryReadAssembly(string name)
{
var explorerAssembly = TryFindAssembly(name);
if (explorerAssembly is null)
{
_logger?.Invoke("Couldn't find assembly {0} in the APK AssemblyStore", name);
return null;
}
var (explorer, storeItem) = explorerAssembly;
_logger?.Invoke("Resolved assembly {0} in the APK {1} AssemblyStore", name, storeItem.TargetArch);
var stream = explorer.ReadImageData(storeItem, false);
if (stream is null)
{
_logger?.Invoke("Couldn't access assembly {0} image stream", name);
return null;
}
return ArchiveUtils.CreatePEReader(name, stream, _logger);
}
private ExplorerStoreItem? TryFindAssembly(string name)
{
if (FindBestAssembly(name, out var assembly))
{
return assembly;
}
// If the assembly name ends with .dll or .exe, try to find it without the extension.
if ((IsFileType(".dll") || IsFileType(".exe")) && FindBestAssembly(name[..^4], out assembly))
{
return assembly;
}
// Conversely, if there is no extension, try with the dll extension (sometimes required for class libraries).
// See: https://github.com/getsentry/sentry-dotnet/issues/4278#issuecomment-2986009125
if (!IsFileType(".dll") && !IsFileType(".exe") && FindBestAssembly(name + ".dll", out assembly))
{
return assembly;
}
return null;
bool IsFileType(string extension)
{
return name.EndsWith(extension, ignoreCase: true, CultureInfo.InvariantCulture);
}
}
private bool FindBestAssembly(string name, out ExplorerStoreItem? explorerAssembly)
{
foreach (var explorer in _explorers)
{
if (explorer.AssembliesByName?.TryGetValue(name, out var assembly) is true)
{
_logger?.Invoke("Found best assembly {0} in APK AssemblyStore for target arch {1}", name, explorer.TargetArch);
explorerAssembly = new(explorer, assembly);
return true;
}
}
_logger?.Invoke("No best assembly for {0} in APK AssemblyStore", name);
explorerAssembly = null;
return false;
}
private record ExplorerStoreItem(AssemblyStoreExplorer Explorer, AssemblyStoreItem StoreItem);
public void Dispose()
{
// No-op
}
}