Skip to content

New app analysis utility #10030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Xamarin.Android.sln
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proguard-android", "src\pro
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Runtime.NativeAOT", "src\Microsoft.Android.Runtime.NativeAOT\Microsoft.Android.Runtime.NativeAOT.csproj", "{E8831F32-11D7-D42C-E43C-711998BC357A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.AppTools", "tools\Microsoft.Android.AppTools\Microsoft.Android.AppTools.csproj", "{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xapp", "tools\xapp\xapp.csproj", "{4CDD887F-AACE-44E3-B179-FB668E87D253}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AnyCPU = Debug|AnyCPU
Expand Down Expand Up @@ -359,6 +363,14 @@ Global
{E8831F32-11D7-D42C-E43C-711998BC357A}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.Build.0 = Release|Any CPU
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Release|AnyCPU.Build.0 = Release|Any CPU
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Release|AnyCPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -420,6 +432,8 @@ Global
{5E806C9F-1B67-4B6B-A6AB-258834250DBB} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
{5FD0133B-69E5-4474-9B67-9FD1D0150C70} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
{E8831F32-11D7-D42C-E43C-711998BC357A} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{4CDD887F-AACE-44E3-B179-FB668E87D253} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,26 @@ sealed class ApplicationConfig
public uint number_of_aot_cache_entries;
public uint number_of_shared_libraries;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint android_runtime_jnienv_class_token;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint jnienv_initialize_method_token;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint jnienv_registerjninatives_method_token;
public uint jni_remapping_replacement_type_count;
public uint jni_remapping_replacement_method_index_entry_count;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint mono_components_mask;
public string android_package_name = String.Empty;
public bool managed_marshal_methods_lookup_enabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,19 @@ sealed class ApplicationConfigCLR
public uint number_of_aot_cache_entries;
public uint number_of_shared_libraries;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint android_runtime_jnienv_class_token;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint jnienv_initialize_method_token;

#if !IN_APPTOOLS
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
#endif
public uint jnienv_registerjninatives_method_token;
public uint jni_remapping_replacement_type_count;
public uint jni_remapping_replacement_method_index_entry_count;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;

using Xamarin.Android.Tools;

namespace Microsoft.Android.AppTools.Assemblies;

public class AssemblyStore
{
static List<AssemblyStoreItem>? emptyAssemblyList;

readonly ILogger log;
readonly AssemblyStoreExplorer explorer;

public IList<AssemblyStoreItem> Assemblies => explorer.Assemblies ?? GetEmptyAssemblyList ();
public uint FullFormatVersion { get; private set; } = 0;
public ulong NumberOfAssemblies => explorer.AssemblyCount;
public AndroidTargetArch TargetArchitecture => explorer.TargetArch ?? AndroidTargetArch.None;
public Version? Version { get; private set; }

internal AssemblyStore (ILogger log, AssemblyStoreExplorer explorer)
{
this.log = log;
this.explorer = explorer;
}

static List<AssemblyStoreItem> GetEmptyAssemblyList ()
{
if (emptyAssemblyList != null) {
return emptyAssemblyList;
}

emptyAssemblyList = new List<AssemblyStoreItem> ();
return emptyAssemblyList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;

using Xamarin.Android.Tools;
using Xamarin.Tools.Zip;

namespace Microsoft.Android.AppTools.Assemblies;

class AssemblyStoreExplorer
{
readonly AssemblyStoreReader reader;

public string StorePath { get; }
public AndroidTargetArch? TargetArch { get; }
public uint AssemblyCount { get; }
public uint IndexEntryCount { get; }
public IList<AssemblyStoreItem>? Assemblies { get; }
public IDictionary<string, AssemblyStoreItem>? AssembliesByName { get; }
public bool Is64Bit { get; }

protected AssemblyStoreExplorer (Stream storeStream, string path)
{
StorePath = path;
var storeReader = AssemblyStoreReader.Create (storeStream, path);
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;

if (Assemblies == null) {
return;
}

var dict = new Dictionary<string, AssemblyStoreItem> (StringComparer.Ordinal);
foreach (AssemblyStoreItem item in Assemblies) {
dict.Add (item.Name, item);
}
AssembliesByName = dict.AsReadOnly ();
}

protected AssemblyStoreExplorer (FileInfo storeInfo)
: this (storeInfo.OpenRead (), storeInfo.FullName)
{}

public static IList<AssemblyStoreExplorer>? Open (ILogger log, string inputFile, FileFormat format, FileInfo info)
{
switch (format) {
case FileFormat.Unknown:
log.Debug ($"File '{inputFile}' has an unknown format.");
return null;

case FileFormat.Zip:
log.Debug ($"File '{inputFile}' is a ZIP archive, but not an Android one.");
return null;

case FileFormat.AssemblyStore:
case FileFormat.ELF:
return new List<AssemblyStoreExplorer> { new AssemblyStoreExplorer (info)};

case FileFormat.Aab:
return OpenAab (log, info);

case FileFormat.AabBase:
return OpenAabBase (log, info);

case FileFormat.Apk:
return OpenApk (log, info);

default:
log.Debug ($"File '{inputFile}' has an unsupported format '{format}'");
return null;
}
}

static IList<AssemblyStoreExplorer>? OpenAab (ILogger log, FileInfo fi)
{
return OpenCommon (
log,
fi,
new List<IList<string>> {
StoreReader_V2.AabPaths,
}
);
}

static IList<AssemblyStoreExplorer>? OpenAabBase (ILogger log, FileInfo fi)
{
return OpenCommon (
log,
fi,
new List<IList<string>> {
StoreReader_V2.AabBasePaths,
}
);
}

static IList<AssemblyStoreExplorer>? OpenApk (ILogger log, FileInfo fi)
{
return OpenCommon (
log,
fi,
new List<IList<string>> {
StoreReader_V2.ApkPaths,
}
);
}

static IList<AssemblyStoreExplorer>? OpenCommon (ILogger log, FileInfo fi, List<IList<string>> pathLists)
{
using var zip = ZipArchive.Open (fi.FullName, FileMode.Open);
IList<AssemblyStoreExplorer>? explorers;
bool pathsFound;

foreach (IList<string> paths in pathLists) {
(explorers, pathsFound) = TryLoad (log, fi, zip, paths);
if (pathsFound) {
return explorers;
}
}

log.Debug ("Unable to find any blob entries");
return null;
}

static (IList<AssemblyStoreExplorer>? explorers, bool pathsFound) TryLoad (ILogger log, FileInfo fi, ZipArchive zip, IList<string> paths)
{
var ret = new List<AssemblyStoreExplorer> ();

foreach (string path in paths) {
if (!zip.ContainsEntry (path)) {
continue;
}

ZipEntry entry = zip.ReadEntry (path);
var stream = new MemoryStream ();
entry.Extract (stream);
ret.Add (new AssemblyStoreExplorer (stream, $"{fi.FullName}!{path}"));
}

if (ret.Count == 0) {
return (null, false);
}

return (ret, true);
}

public Stream? ReadImageData (AssemblyStoreItem item, bool uncompressIfNeeded = false)
{
return reader.ReadEntryImageData (item, uncompressIfNeeded);
}

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<AssemblyStoreItem>? Find (string assemblyName, AndroidTargetArch? targetArch = null)
{
if (Assemblies == null) {
return null;
}

assemblyName = EnsureCorrectAssemblyName (assemblyName);
var items = new List<AssemblyStoreItem> ();
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<AssemblyStoreItem>? items = Find (assemblyName, targetArch);
if (items == null || items.Count == 0) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Collections.Generic;

using Xamarin.Android.Tools;

namespace Microsoft.Android.AppTools.Assemblies;

public abstract class AssemblyStoreItem
{
public string Name { get; }
public IList<ulong> 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<ulong> hashes)
{
Name = name;
Hashes = hashes.AsReadOnly ();
Is64Bit = is64Bit;
}
}
Loading
Loading