Skip to content

Commit f930a23

Browse files
committed
WIP, very much so
1 parent 036b244 commit f930a23

32 files changed

+3613
-0
lines changed

Xamarin.Android.sln

+7
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Runtime.N
131131
EndProject
132132
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.AppTools", "tools\Microsoft.Android.AppTools\Microsoft.Android.AppTools.csproj", "{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}"
133133
EndProject
134+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xapp", "tools\xapp\xapp.csproj", "{4CDD887F-AACE-44E3-B179-FB668E87D253}"
135+
EndProject
134136
Global
135137
GlobalSection(SolutionConfigurationPlatforms) = preSolution
136138
Debug|AnyCPU = Debug|AnyCPU
@@ -365,6 +367,10 @@ Global
365367
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Debug|AnyCPU.Build.0 = Debug|Any CPU
366368
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Release|AnyCPU.ActiveCfg = Release|Any CPU
367369
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Release|AnyCPU.Build.0 = Release|Any CPU
370+
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
371+
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Debug|AnyCPU.Build.0 = Debug|Any CPU
372+
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Release|AnyCPU.ActiveCfg = Release|Any CPU
373+
{4CDD887F-AACE-44E3-B179-FB668E87D253}.Release|AnyCPU.Build.0 = Release|Any CPU
368374
EndGlobalSection
369375
GlobalSection(SolutionProperties) = preSolution
370376
HideSolutionNode = FALSE
@@ -427,6 +433,7 @@ Global
427433
{5FD0133B-69E5-4474-9B67-9FD1D0150C70} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
428434
{E8831F32-11D7-D42C-E43C-711998BC357A} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
429435
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
436+
{4CDD887F-AACE-44E3-B179-FB668E87D253} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
430437
EndGlobalSection
431438
GlobalSection(ExtensibilityGlobals) = postSolution
432439
SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Text;
5+
6+
using ELFSharp;
7+
using ELFSharp.ELF;
8+
using ELFSharp.ELF.Sections;
9+
10+
namespace Microsoft.Android.AppTools.Native;
11+
12+
abstract class AnELF
13+
{
14+
protected static readonly byte[] EmptyArray = Array.Empty<byte> ();
15+
16+
const string DynsymSectionName = ".dynsym";
17+
const string SymtabSectionName = ".symtab";
18+
const string RodataSectionName = ".rodata";
19+
20+
ISymbolTable dynamicSymbolsSection;
21+
ISection rodataSection;
22+
ISymbolTable? symbolsSection;
23+
string filePath;
24+
IELF elf;
25+
Stream elfStream;
26+
27+
protected ISymbolTable DynSymSection => dynamicSymbolsSection;
28+
protected ISymbolTable? SymSection => symbolsSection;
29+
protected ISection RodataSection => rodataSection;
30+
public IELF AnyELF => elf;
31+
protected Stream ELFStream => elfStream;
32+
protected ILogger Log { get; }
33+
34+
public string FilePath => filePath;
35+
public int PointerSize => Is64Bit ? 8 : 4;
36+
37+
public abstract bool Is64Bit { get; }
38+
public abstract string Bitness { get; }
39+
40+
protected AnELF (ILogger log, Stream stream, string filePath, IELF elf, ISymbolTable dynsymSection, ISection rodataSection, ISymbolTable? symSection)
41+
{
42+
Log = log;
43+
this.filePath = filePath;
44+
this.elf = elf;
45+
elfStream = stream;
46+
dynamicSymbolsSection = dynsymSection;
47+
this.rodataSection = rodataSection;
48+
symbolsSection = symSection;
49+
}
50+
51+
public ISymbolEntry? GetSymbol (string symbolName)
52+
{
53+
ISymbolEntry? symbol = null;
54+
55+
if (symbolsSection != null) {
56+
symbol = GetSymbol (symbolsSection, symbolName);
57+
}
58+
59+
if (symbol == null) {
60+
symbol = GetSymbol (dynamicSymbolsSection, symbolName);
61+
}
62+
63+
return symbol;
64+
}
65+
66+
protected static ISymbolEntry? GetSymbol (ISymbolTable symtab, string symbolName)
67+
{
68+
return symtab.Entries.Where (entry => String.Compare (entry.Name, symbolName, StringComparison.Ordinal) == 0).FirstOrDefault ();
69+
}
70+
71+
protected static SymbolEntry<T>? GetSymbol<T> (SymbolTable<T> symtab, T symbolValue) where T: struct
72+
{
73+
return symtab.Entries.Where (entry => entry.Value.Equals (symbolValue)).FirstOrDefault ();
74+
}
75+
76+
public bool HasSymbol (string symbolName)
77+
{
78+
return GetSymbol (symbolName) != null;
79+
}
80+
81+
public byte[] GetData (string symbolName)
82+
{
83+
return GetData (symbolName, out ISymbolEntry? _);
84+
}
85+
86+
public byte[] GetData (string symbolName, out ISymbolEntry? symbolEntry)
87+
{
88+
Log.DebugLine ($"Looking for symbol: {symbolName}");
89+
symbolEntry = GetSymbol (symbolName);
90+
if (symbolEntry == null)
91+
return EmptyArray;
92+
93+
if (Is64Bit) {
94+
var symbol64 = symbolEntry as SymbolEntry<ulong>;
95+
if (symbol64 == null)
96+
throw new InvalidOperationException ($"Symbol '{symbolName}' is not a valid 64-bit symbol");
97+
return GetData (symbol64);
98+
}
99+
100+
var symbol32 = symbolEntry as SymbolEntry<uint>;
101+
if (symbol32 == null)
102+
throw new InvalidOperationException ($"Symbol '{symbolName}' is not a valid 32-bit symbol");
103+
104+
return GetData (symbol32);
105+
}
106+
107+
public string? GetStringFromPointer (ISymbolEntry symbolEntry)
108+
{
109+
return GetStringFromPointerField (symbolEntry, 0);
110+
}
111+
112+
public abstract string? GetStringFromPointerField (ISymbolEntry symbolEntry, ulong pointerFieldOffset);
113+
public abstract byte[] GetData (ulong symbolValue, ulong size);
114+
115+
public string? GetASCIIZ (ulong symbolValue)
116+
{
117+
return GetASCIIZ (GetData (symbolValue, 0), 0);
118+
}
119+
120+
public string? GetASCIIZ (byte[] data, ulong offset)
121+
{
122+
if (offset >= (ulong)data.LongLength) {
123+
Log.DebugLine ("Not enough data to retrieve an ASCIIZ string");
124+
return null;
125+
}
126+
127+
int count = data.Length;
128+
129+
for (ulong i = offset; i < (ulong)data.LongLength; i++) {
130+
if (data[i] == 0) {
131+
count = (int)(i - offset);
132+
break;
133+
}
134+
}
135+
136+
return Encoding.ASCII.GetString (data, (int)offset, count);
137+
}
138+
139+
public ulong GetPaddedSize<S> (ulong sizeSoFar) => NativeHelpers.GetPaddedSize<S> (sizeSoFar, Is64Bit);
140+
141+
public ulong GetPaddedSize<S> (ulong sizeSoFar, S _)
142+
{
143+
return GetPaddedSize<S> (sizeSoFar);
144+
}
145+
146+
protected virtual byte[] GetData (SymbolEntry<ulong> symbol)
147+
{
148+
throw new NotSupportedException ();
149+
}
150+
151+
protected virtual byte[] GetData (SymbolEntry<uint> symbol)
152+
{
153+
throw new NotSupportedException ();
154+
}
155+
156+
protected byte[] GetData (ISymbolEntry symbol, ulong size, ulong offset)
157+
{
158+
return GetData (symbol.PointedSection, size, offset);
159+
}
160+
161+
protected byte[] GetData (ISection section, ulong size, ulong offset)
162+
{
163+
ulong sectionOffset = (elf.Class == Class.Bit64 ? ((Section<ulong>)section).Offset : ((Section<uint>)section).Offset);
164+
Log.VerboseLine ($"AnELF.GetData: section == {section.Name}; type == {section.Type}; flags == {section.Flags}; offset into binary == {sectionOffset}; size == {size}");
165+
byte[] data = section.GetContents ();
166+
167+
Log.VerboseLine ($" section data length: {data.Length} (long: {data.LongLength})");
168+
Log.VerboseLine ($" offset into section: {offset}; symbol data length: {size}");
169+
if ((ulong)data.LongLength < (offset + size)) {
170+
return EmptyArray;
171+
}
172+
173+
if (size == 0)
174+
size = (ulong)data.Length - offset;
175+
176+
var ret = new byte[size];
177+
checked {
178+
Array.Copy (data, (int)offset, ret, 0, (int)size);
179+
}
180+
181+
return ret;
182+
}
183+
184+
public uint GetUInt32 (string symbolName)
185+
{
186+
return GetUInt32 (GetData (symbolName), 0, symbolName);
187+
}
188+
189+
public uint GetUInt32 (ulong symbolValue)
190+
{
191+
return GetUInt32 (GetData (symbolValue, 4), 0, symbolValue.ToString ());
192+
}
193+
194+
protected uint GetUInt32 (byte[] data, ulong offset, string symbolName)
195+
{
196+
if (data.Length < 4) {
197+
throw new InvalidOperationException ($"Data not big enough to retrieve a 32-bit integer from it (need 4, got {data.Length})");
198+
}
199+
200+
return BitConverter.ToUInt32 (GetIntegerData (4, data, offset, symbolName), 0);
201+
}
202+
203+
public ulong GetUInt64 (string symbolName)
204+
{
205+
return GetUInt64 (GetData (symbolName), 0, symbolName);
206+
}
207+
208+
public ulong GetUInt64 (ulong symbolValue)
209+
{
210+
return GetUInt64 (GetData (symbolValue, 8), 0, symbolValue.ToString ());
211+
}
212+
213+
protected ulong GetUInt64 (byte[] data, ulong offset, string symbolName)
214+
{
215+
if (data.Length < 8)
216+
throw new InvalidOperationException ("Data not big enough to retrieve a 64-bit integer from it");
217+
return BitConverter.ToUInt64 (GetIntegerData (8, data, offset, symbolName), 0);
218+
}
219+
220+
byte[] GetIntegerData (uint size, byte[] data, ulong offset, string symbolName)
221+
{
222+
if ((ulong)data.LongLength < (offset + size)) {
223+
string bits = size == 4 ? "32" : "64";
224+
throw new InvalidOperationException ($"Unable to read UInt{bits} value for symbol '{symbolName}': data not long enough");
225+
}
226+
227+
byte[] ret = new byte[size];
228+
Array.Copy (data, (int)offset, ret, 0, ret.Length);
229+
Endianess myEndianness = BitConverter.IsLittleEndian ? Endianess.LittleEndian : Endianess.BigEndian;
230+
if (AnyELF.Endianess != myEndianness) {
231+
Array.Reverse (ret);
232+
}
233+
234+
return ret;
235+
}
236+
237+
public static bool TryLoad (ILogger log, string filePath, out AnELF? anElf)
238+
{
239+
using var fs = File.OpenRead (filePath);
240+
return TryLoad (log, fs, filePath, out anElf);
241+
}
242+
243+
public static bool TryLoad (ILogger log, Stream stream, string filePath, out AnELF? anElf)
244+
{
245+
anElf = null;
246+
Class elfClass = ELFReader.CheckELFType (stream);
247+
if (elfClass == Class.NotELF) {
248+
log.WarningLine ($"AnELF.TryLoad: {filePath} is not an ELF binary");
249+
return false;
250+
}
251+
252+
IELF elf = ELFReader.Load (stream, shouldOwnStream: false);
253+
254+
if (elf.Type != FileType.SharedObject) {
255+
log.WarningLine ($"AnELF.TryLoad: {filePath} is not a shared library");
256+
return false;
257+
}
258+
259+
if (elf.Endianess != Endianess.LittleEndian) {
260+
log.WarningLine ($"AnELF.TryLoad: {filePath} is not a little-endian binary");
261+
return false;
262+
}
263+
264+
bool is64;
265+
switch (elf.Machine) {
266+
case Machine.ARM:
267+
case Machine.Intel386:
268+
is64 = false;
269+
270+
break;
271+
272+
case Machine.AArch64:
273+
case Machine.AMD64:
274+
is64 = true;
275+
276+
break;
277+
278+
default:
279+
log.WarningLine ($"{filePath} is for an unsupported machine type {elf.Machine}");
280+
return false;
281+
}
282+
283+
ISymbolTable? symtab = GetSymbolTable (elf, DynsymSectionName);
284+
if (symtab == null) {
285+
log.WarningLine ($"{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.WarningLine ("${filePath} does not contain read-only data section ('{RodataSectionName}')");
293+
return false;
294+
}
295+
ISection rodata = sec;
296+
297+
ISymbolTable? sym = GetSymbolTable (elf, SymtabSectionName);
298+
299+
if (is64) {
300+
anElf = new ELF64 (log, stream, filePath, elf, dynsym, rodata, sym);
301+
} else {
302+
anElf = new ELF32 (log, stream, filePath, elf, dynsym, rodata, sym);
303+
}
304+
305+
log.DebugLine ($"AnELF.TryLoad: {filePath} is a {anElf.Bitness}-bit ELF binary ({elf.Machine})");
306+
return true;
307+
}
308+
309+
protected static ISymbolTable? GetSymbolTable (IELF elf, string sectionName)
310+
{
311+
ISection? section = GetSection (elf, sectionName);
312+
if (section == null) {
313+
return null;
314+
}
315+
316+
var symtab = section as ISymbolTable;
317+
if (symtab == null) {
318+
return null;
319+
}
320+
321+
return symtab;
322+
}
323+
324+
protected static ISection? GetSection (IELF elf, string sectionName)
325+
{
326+
if (!elf.TryGetSection (sectionName, out ISection section)) {
327+
return null;
328+
}
329+
330+
return section;
331+
}
332+
}

0 commit comments

Comments
 (0)