Skip to content

Commit 5853f30

Browse files
committed
Various odds and ends
1 parent 25d64bb commit 5853f30

16 files changed

+256
-109
lines changed

tools/apput/src/Android/AndroidManifest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace ApplicationUtility;
77

8+
// TODO: implement support for AndroidManifest.xml in AAB packages. It's protobuf data, not binary/text XML
89
public class AndroidManifest : IAspect
910
{
1011
public string Description { get; }

tools/apput/src/AssemblyStore/AssemblyStore.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class AssemblyStore : IAspect
1515
public static string AspectName { get; } = "Assembly Store";
1616

1717
public IDictionary<string, ApplicationAssembly> Assemblies { get; private set; } = new Dictionary<string, ApplicationAssembly> (StringComparer.Ordinal);
18-
public AndroidTargetArch Architecture { get; private set; } = AndroidTargetArch.None;
18+
public AndroidTargetArch Architecture { get; }
1919
public ulong NumberOfAssemblies => (ulong)(Assemblies?.Count ?? 0);
2020

2121
AssemblyStoreAspectState storeState;
@@ -25,6 +25,19 @@ public class AssemblyStore : IAspect
2525
{
2626
storeState = state;
2727
this.description = description;
28+
29+
AssemblyStoreHeader? header = state.Format.Header;
30+
if (header == null) {
31+
throw new InvalidOperationException ("Internal error: state doesn't contain a valid store header.");
32+
}
33+
34+
Architecture = header.Version.ABI switch {
35+
AssemblyStoreABI.Arm => AndroidTargetArch.Arm,
36+
AssemblyStoreABI.Arm64 => AndroidTargetArch.Arm64,
37+
AssemblyStoreABI.X86 => AndroidTargetArch.X86,
38+
AssemblyStoreABI.X64 => AndroidTargetArch.X86_64,
39+
_ => throw new InvalidOperationException ($"Internal error: unsupported assembly store ABI '{header.Version.ABI}'")
40+
};
2841
}
2942

3043
bool Read ()
@@ -60,12 +73,12 @@ public static IAspectState ProbeAspect (Stream stream, string? description)
6073
Stream? storeStream = null;
6174

6275
try {
63-
IAspectState state = SharedLibrary.ProbeAspect (stream, description);
76+
IAspectState state = DotNetAndroidWrapperSharedLibrary.ProbeAspect (stream, description);
6477
if (!state.Success) {
6578
return DoProbeAspect (stream, description);
6679
}
6780

68-
var library = (SharedLibrary)SharedLibrary.LoadAspect (stream, state, description);
81+
var library = (DotNetAndroidWrapperSharedLibrary)DotNetAndroidWrapperSharedLibrary.LoadAspect (stream, state, description);
6982
if (!library.HasAndroidPayload) {
7083
Log.Debug ($"AssemblyStore: stream ('{description}') is an ELF shared library, without payload");
7184
return new BasicAspectState (false);

tools/apput/src/AssemblyStore/AssemblyStoreAspectState.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System.Collections.Generic;
2-
31
namespace ApplicationUtility;
42

53
class AssemblyStoreAspectState : BasicAspectState

tools/apput/src/Common/Log.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ public static void Warning (string message = "")
9191
Warning (tag: String.Empty, message);
9292
}
9393

94+
public static void Warning (string message, Exception ex)
95+
{
96+
Warning (tag: String.Empty, message);
97+
Warning (tag: String.Empty, ex.ToString ());
98+
}
99+
94100
public static void Warning (string tag, string message)
95101
{
96102
if (message.Length > 0) {

tools/apput/src/Detector.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ public class Detector
2323
typeof (NativeAotSharedLibrary),
2424
typeof (XamarinAppSharedLibrary),
2525
typeof (MonoAotSharedLibrary),
26+
typeof (DotNetAndroidWrapperSharedLibrary),
2627
typeof (SharedLibrary),
2728
};
2829

2930
readonly static List<Type> KnownSharedLibraryAspects = new () {
3031
typeof (NativeAotSharedLibrary),
3132
typeof (XamarinAppSharedLibrary),
3233
typeof (MonoAotSharedLibrary),
34+
typeof (DotNetAndroidWrapperSharedLibrary),
3335
typeof (SharedLibrary),
3436
};
3537

@@ -61,9 +63,13 @@ public class Detector
6163
static IAspect? TryFindAspect (List<Type> aspectTypes, Stream stream, string? description)
6264
{
6365
foreach (Type aspectType in aspectTypes) {
64-
IAspect? aspect = TryProbeAndLoadAspect (aspectType, stream, description);
65-
if (aspect != null) {
66-
return aspect;
66+
try {
67+
IAspect? aspect = TryProbeAndLoadAspect (aspectType, stream, description);
68+
if (aspect != null) {
69+
return aspect;
70+
}
71+
} catch (Exception ex) {
72+
Log.Warning ($"Failed to probe and load aspect '{aspectType}'", ex);
6773
}
6874
}
6975

tools/apput/src/Native/AnELF.cs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ abstract class AnELF
1717
const string SymtabSectionName = ".symtab";
1818
const string RodataSectionName = ".rodata";
1919

20-
ISymbolTable dynamicSymbolsSection;
21-
ISection rodataSection;
20+
ISymbolTable? dynamicSymbolsSection;
21+
ISection? rodataSection;
2222
ISymbolTable? symbolsSection;
2323
string filePath;
2424
IELF elf;
2525
Stream elfStream;
2626

27-
protected ISymbolTable DynSymSection => dynamicSymbolsSection;
27+
protected ISymbolTable? DynSymSection => dynamicSymbolsSection;
2828
protected ISymbolTable? SymSection => symbolsSection;
29-
protected ISection RodataSection => rodataSection;
29+
protected ISection? RodataSection => rodataSection;
3030
public IELF AnyELF => elf;
3131
protected Stream ELFStream => elfStream;
3232

@@ -36,7 +36,7 @@ abstract class AnELF
3636
public abstract bool Is64Bit { get; }
3737
public abstract string Bitness { get; }
3838

39-
protected AnELF (Stream stream, string filePath, IELF elf, ISymbolTable dynsymSection, ISection rodataSection, ISymbolTable? symSection)
39+
protected AnELF (Stream stream, string filePath, IELF elf, ISymbolTable? dynsymSection, ISection? rodataSection, ISymbolTable? symSection)
4040
{
4141
this.filePath = filePath;
4242
this.elf = elf;
@@ -61,14 +61,14 @@ protected AnELF (Stream stream, string filePath, IELF elf, ISymbolTable dynsymSe
6161
return symbol;
6262
}
6363

64-
protected static ISymbolEntry? GetSymbol (ISymbolTable symtab, string symbolName)
64+
protected static ISymbolEntry? GetSymbol (ISymbolTable? symtab, string symbolName)
6565
{
66-
return symtab.Entries.Where (entry => String.Compare (entry.Name, symbolName, StringComparison.Ordinal) == 0).FirstOrDefault ();
66+
return symtab?.Entries.Where (entry => String.Compare (entry.Name, symbolName, StringComparison.Ordinal) == 0).FirstOrDefault ();
6767
}
6868

69-
protected static SymbolEntry<T>? GetSymbol<T> (SymbolTable<T> symtab, T symbolValue) where T: struct
69+
protected static SymbolEntry<T>? GetSymbol<T> (SymbolTable<T>? symtab, T symbolValue) where T: struct
7070
{
71-
return symtab.Entries.Where (entry => entry.Value.Equals (symbolValue)).FirstOrDefault ();
71+
return symtab?.Entries.Where (entry => entry.Value.Equals (symbolValue)).FirstOrDefault ();
7272
}
7373

7474
public bool HasSymbol (string symbolName)
@@ -85,19 +85,22 @@ public byte[] GetData (string symbolName, out ISymbolEntry? symbolEntry)
8585
{
8686
Log.Debug ($"Looking for symbol: {symbolName}");
8787
symbolEntry = GetSymbol (symbolName);
88-
if (symbolEntry == null)
88+
if (symbolEntry == null) {
8989
return EmptyArray;
90+
}
9091

9192
if (Is64Bit) {
9293
var symbol64 = symbolEntry as SymbolEntry<ulong>;
93-
if (symbol64 == null)
94+
if (symbol64 == null) {
9495
throw new InvalidOperationException ($"Symbol '{symbolName}' is not a valid 64-bit symbol");
96+
}
9597
return GetData (symbol64);
9698
}
9799

98100
var symbol32 = symbolEntry as SymbolEntry<uint>;
99-
if (symbol32 == null)
101+
if (symbol32 == null) {
100102
throw new InvalidOperationException ($"Symbol '{symbolName}' is not a valid 32-bit symbol");
103+
}
101104

102105
return GetData (symbol32);
103106
}
@@ -168,8 +171,9 @@ protected byte[] GetData (ISection section, ulong size, ulong offset)
168171
return EmptyArray;
169172
}
170173

171-
if (size == 0)
174+
if (size == 0) {
172175
size = (ulong)data.Length - offset;
176+
}
173177

174178
var ret = new byte[size];
175179
checked {
@@ -280,20 +284,8 @@ public static bool TryLoad (Stream stream, string filePath, out AnELF? anElf)
280284
return false;
281285
}
282286

283-
ISymbolTable? symtab = GetSymbolTable (elf, DynsymSectionName);
284-
if (symtab == null) {
285-
Log.Warning ($"{filePath} does not contain dynamic symbol section '{DynsymSectionName}'");
286-
return false;
287-
}
288-
ISymbolTable dynsym = symtab;
289-
290-
ISection? sec = GetSection (elf, RodataSectionName);
291-
if (sec == null) {
292-
Log.Warning ($"{filePath} does not contain read-only data section ('{RodataSectionName}')");
293-
return false;
294-
}
295-
ISection rodata = sec;
296-
287+
ISymbolTable? dynsym = GetSymbolTable (elf, DynsymSectionName);
288+
ISection? rodata = GetSection (elf, RodataSectionName);
297289
ISymbolTable? sym = GetSymbolTable (elf, SymtabSectionName);
298290

299291
if (is64) {
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.IO;
3+
using ELFSharp.ELF;
4+
using ELFSharp.ELF.Sections;
5+
6+
namespace ApplicationUtility;
7+
8+
class DotNetAndroidWrapperSharedLibrary : SharedLibrary
9+
{
10+
const string PayloadSectionName = "payload";
11+
const string DotNetPayloadMarkerSymbol = "dotnet_for_android_data_payload";
12+
13+
readonly ulong payloadOffset;
14+
readonly ulong payloadSize;
15+
16+
public bool HasAndroidPayload => payloadSize > 0;
17+
18+
protected DotNetAndroidWrapperSharedLibrary (Stream stream, string libraryName, IAspectState state)
19+
: base (stream, libraryName, state)
20+
{
21+
var libState = EnsureValidAspectState<DotNetAndroidWrapperSharedLibraryAspectState> (state);
22+
(payloadOffset, payloadSize) = FindAndroidPayload (ELF, libState.Is64Bit, libraryName);
23+
}
24+
25+
public new static IAspect LoadAspect (Stream stream, IAspectState? state, string? description)
26+
{
27+
if (String.IsNullOrEmpty (description)) {
28+
throw new ArgumentException ("Must be a shared library name", nameof (description));
29+
}
30+
31+
var libState = EnsureValidAspectState<DotNetAndroidWrapperSharedLibraryAspectState> (state);
32+
return new DotNetAndroidWrapperSharedLibrary (stream, description, libState);
33+
}
34+
35+
public static new IAspectState ProbeAspect (Stream stream, string? description) => IsDotNetAndroidWrapperSharedLibrary (stream, description);
36+
37+
/// <summary>
38+
/// If the library has .NET for Android payload section, this
39+
/// method will read the data and write it to the <paramref name="dest"/>
40+
/// stream. All the data in the output stream will be overwritten.
41+
/// </summary>
42+
public void CopyAndroidPayload (Stream dest)
43+
{
44+
using Stream payload = OpenAndroidPayload ();
45+
payload.CopyTo (dest);
46+
}
47+
48+
/// <summary>
49+
/// Creates a stream referring to the Android payload data inside
50+
/// the shared library. No data is read, the open stream is returned
51+
/// to the user. Ownership of the stream is transferred to the caller.
52+
/// </summary>
53+
public Stream OpenAndroidPayload ()
54+
{
55+
if (!HasAndroidPayload) {
56+
throw new InvalidOperationException ("Payload section not found");
57+
}
58+
59+
if (payloadOffset > Int64.MaxValue) {
60+
throw new InvalidOperationException ($"Payload offset of {payloadOffset} is too large to support.");
61+
}
62+
63+
if (payloadSize > Int64.MaxValue) {
64+
throw new InvalidOperationException ($"Payload offset of {payloadSize} is too large to support.");
65+
}
66+
67+
return new SubStream (LibraryStream, (long)payloadOffset, (long)payloadSize);
68+
}
69+
70+
static DotNetAndroidWrapperSharedLibraryAspectState IsDotNetAndroidWrapperSharedLibrary (Stream stream, string? description)
71+
{
72+
Log.Debug ($"Checking if '{description}' is a Mono AOT shared library");
73+
if (!IsSupportedELFSharedLibrary (stream, description, out IELF? elf) || elf == null) {
74+
return GetErrorState ();
75+
}
76+
77+
if (!AnELF.TryLoad (stream, description ?? String.Empty, out AnELF? anElf) || anElf == null) {
78+
Log.Debug ($"Library '{description}' failed to load");
79+
return GetErrorState ();
80+
}
81+
82+
if (!anElf.HasSymbol (DotNetPayloadMarkerSymbol)) {
83+
Log.Debug ($"Symbol '{DotNetPayloadMarkerSymbol}' missing, not a .NET for Android wrapper shared library");
84+
return GetErrorState ();
85+
}
86+
87+
return new DotNetAndroidWrapperSharedLibraryAspectState (true, anElf);
88+
89+
DotNetAndroidWrapperSharedLibraryAspectState GetErrorState () => new DotNetAndroidWrapperSharedLibraryAspectState (false, null);
90+
}
91+
92+
(ulong offset, ulong size) FindAndroidPayload (IELF elf, bool is64Bit, string libraryName)
93+
{
94+
if (!elf.TryGetSection (PayloadSectionName, out ISection? payloadSection)) {
95+
Log.Debug ($"Shared library '{libraryName}' doesn't have the '{PayloadSectionName}' section.");
96+
return (0, 0);
97+
}
98+
99+
ulong offset;
100+
ulong size;
101+
102+
if (is64Bit) {
103+
(offset, size) = GetOffsetAndSize64 ((Section<ulong>)payloadSection);
104+
} else {
105+
(offset, size) = GetOffsetAndSize32 ((Section<uint>)payloadSection);
106+
}
107+
108+
Log.Debug ($"Found payload section at offset {offset}, size of {size} bytes.");
109+
return (offset, size);
110+
111+
(ulong offset, ulong size) GetOffsetAndSize64 (Section<ulong> payload)
112+
{
113+
return (payload.Offset, payload.Size);
114+
}
115+
116+
(ulong offset, ulong size) GetOffsetAndSize32 (Section<uint> payload)
117+
{
118+
return ((ulong)payload.Offset, (ulong)payload.Size);
119+
}
120+
}
121+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace ApplicationUtility;
2+
3+
class DotNetAndroidWrapperSharedLibraryAspectState : SharedLibraryAspectState
4+
{
5+
public AnELF? LoadedELF { get; }
6+
7+
public DotNetAndroidWrapperSharedLibraryAspectState (bool success, AnELF? elf)
8+
: base (success, elf?.AnyELF)
9+
{
10+
LoadedELF = elf;
11+
}
12+
}

tools/apput/src/Native/ELF32.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ class ELF32 : AnELF
1111
public override bool Is64Bit => false;
1212
public override string Bitness => "32";
1313

14-
SymbolTable<uint> DynamicSymbols => (SymbolTable<uint>)DynSymSection;
14+
SymbolTable<uint>? DynamicSymbols => (SymbolTable<uint>?)DynSymSection;
1515
SymbolTable<uint>? Symbols => (SymbolTable<uint>?)SymSection;
16-
Section<uint> Rodata => (Section<uint>)RodataSection;
16+
Section<uint>? Rodata => (Section<uint>?)RodataSection;
1717
ELF<uint> ELF => (ELF<uint>)AnyELF;
1818

19-
public ELF32 (Stream stream, string filePath, IELF elf, ISymbolTable dynsymSection, ISection rodataSection, ISymbolTable? symSection)
19+
public ELF32 (Stream stream, string filePath, IELF elf, ISymbolTable? dynsymSection, ISection? rodataSection, ISymbolTable? symSection)
2020
: base (stream, filePath, elf, dynsymSection, rodataSection, symSection)
2121
{}
2222

tools/apput/src/Native/ELF64.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ class ELF64 : AnELF
1111
public override bool Is64Bit => true;
1212
public override string Bitness => "64";
1313

14-
SymbolTable<ulong> DynamicSymbols => (SymbolTable<ulong>)DynSymSection;
14+
SymbolTable<ulong>? DynamicSymbols => (SymbolTable<ulong>?)DynSymSection;
1515
SymbolTable<ulong>? Symbols => (SymbolTable<ulong>?)SymSection;
16-
Section<ulong> Rodata => (Section<ulong>)RodataSection;
16+
Section<ulong>? Rodata => (Section<ulong>?)RodataSection;
1717
ELF<ulong> ELF => (ELF<ulong>)AnyELF;
1818

19-
public ELF64 (Stream stream, string filePath, IELF elf, ISymbolTable dynsymSection, ISection rodataSection, ISymbolTable? symSection)
19+
public ELF64 (Stream stream, string filePath, IELF elf, ISymbolTable? dynsymSection, ISection? rodataSection, ISymbolTable? symSection)
2020
: base (stream, filePath, elf, dynsymSection, rodataSection, symSection)
2121
{}
2222

0 commit comments

Comments
 (0)