Skip to content

Commit 4e92ef8

Browse files
committed
More NAOT goodies
1 parent 5b23202 commit 4e92ef8

File tree

8 files changed

+143
-63
lines changed

8 files changed

+143
-63
lines changed

tools/apput/src/Detector.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ public class Detector
1919
typeof (AssemblyStore),
2020
typeof (ApplicationAssembly),
2121
typeof (NativeAotSharedLibrary),
22+
typeof (LibXamarinApp),
23+
typeof (SharedLibrary),
24+
};
25+
26+
readonly static List<Type> KnownSharedLibraryAspects = new () {
27+
typeof (NativeAotSharedLibrary),
28+
typeof (LibXamarinApp),
2229
typeof (SharedLibrary),
2330
};
2431

@@ -30,16 +37,23 @@ public class Detector
3037
}
3138

3239
using Stream fs = File.OpenRead (path);
33-
return TryFindAspect (fs, path);
40+
return TryFindTopLevelAspect (fs, path);
3441
}
3542

3643
public static IAspect? FindAspect (Stream stream, string? description = null)
3744
{
3845
Log.Debug ($"Looking for aspect supporting a stream ('{description}')");
39-
return TryFindAspect (stream, description);
46+
return TryFindTopLevelAspect (stream, description);
47+
}
48+
49+
public static SharedLibrary? FindSharedLibraryAspect (Stream stream, string? description = null)
50+
{
51+
Log.Debug ($"Looking for shared library aspect ('{description}')");
52+
// TODO: implement
53+
return null;
4054
}
4155

42-
static IAspect? TryFindAspect (Stream stream, string? description)
56+
static IAspect? TryFindTopLevelAspect (Stream stream, string? description)
4357
{
4458
var flags = BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static;
4559

tools/apput/src/Native/NativeAotSharedLibrary.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace ApplicationUtility;
88

9-
class NativeAotSharedLibrary : SharedLibrary
9+
public class NativeAotSharedLibrary : SharedLibrary
1010
{
1111
readonly static List<(string sectionName, SectionType type)> NativeAotSections = new () {
1212
("__managedcode", SectionType.ProgBits),

tools/apput/src/Native/SharedLibrary.cs

Lines changed: 87 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,45 @@
88

99
namespace ApplicationUtility;
1010

11-
class SharedLibrary : IAspect, IDisposable
11+
public class SharedLibrary : IAspect, IDisposable
1212
{
1313
const uint ELF_MAGIC = 0x464c457f;
1414
const string DebugLinkSectionName = ".gnu_debuglink";
1515
const string PayloadSectionName = "payload";
1616

17-
readonly IELF elf;
18-
readonly NativeArchitecture nativeArch = NativeArchitecture.Unknown;
19-
readonly Stream libraryStream;
20-
readonly bool hasDebugInfo;
21-
readonly bool is64Bit;
22-
readonly string libraryName;
2317
readonly string? androidIdent;
2418
readonly string? buildId;
2519
readonly string? debugLink;
20+
readonly IELF elf;
21+
readonly bool hasDebugInfo;
22+
readonly bool is64Bit;
2623
readonly ulong libraryAlignment;
24+
readonly string libraryName;
25+
readonly Stream libraryStream;
26+
readonly NativeArchitecture nativeArch = NativeArchitecture.Unknown;
2727
readonly ulong payloadOffset;
2828
readonly ulong payloadSize;
29+
readonly string? soname;
2930

3031
bool disposed;
3132

3233
public static string AspectName { get; } = "Native shared library";
3334

34-
public NativeArchitecture TargetArchitecture => nativeArch;
35+
public ulong Alignment => libraryAlignment;
36+
public bool AlignmentCompatibleWith16k => libraryAlignment >= 0x4000 && (libraryAlignment % 0x4000 == 0);
37+
public string? AndroidIdent => androidIdent;
38+
public string? BuildID => buildId;
39+
public string? DebugLink => debugLink;
3540
public bool HasAndroidIdent => !String.IsNullOrEmpty (androidIdent);
3641
public bool HasAndroidPayload => payloadSize > 0;
3742
public bool HasBuildID => !String.IsNullOrEmpty (buildId);
3843
public bool HasDebugInfo => hasDebugInfo;
3944
public bool HasDebugLink => !String.IsNullOrEmpty (debugLink);
45+
public bool HasSoname => !String.IsNullOrEmpty (soname);
4046
public bool Is64Bit => is64Bit;
4147
public string Name => libraryName;
42-
public string? AndroidIdent => androidIdent;
43-
public string? BuildID => buildId;
44-
public string? DebugLink => debugLink;
45-
public ulong Alignment => libraryAlignment;
48+
public string? Soname => soname;
49+
public NativeArchitecture TargetArchitecture => nativeArch;
4650

4751
protected IELF ELF => elf;
4852

@@ -56,6 +60,7 @@ protected SharedLibrary (Stream stream, string libraryName)
5660
(hasDebugInfo, debugLink) = DetectDebugInfo (elf, libraryName);
5761
buildId = GetBuildID (elf, is64Bit);
5862
androidIdent = GetAndroidIdent (elf, is64Bit);
63+
soname = GetSoname (elf, is64Bit);
5964
}
6065

6166
public static IAspect LoadAspect (Stream stream, IAspectState? state, string? description)
@@ -291,23 +296,73 @@ static ulong DetectAlignment (IELF elf, bool is64Bit)
291296
return 0;
292297
}
293298

299+
static string? GetSoname (IELF elf, bool is64Bit)
300+
{
301+
IDynamicEntry? sonameEntry = null;
302+
303+
foreach (IDynamicSection section in elf.GetSections<IDynamicSection> ()) {
304+
foreach (IDynamicEntry dyne in section.Entries) {
305+
if (dyne.Tag != DynamicTag.SoName) {
306+
continue;
307+
}
308+
sonameEntry = dyne;
309+
break;
310+
}
311+
312+
if (sonameEntry != null) {
313+
break;
314+
}
315+
}
316+
317+
if (sonameEntry == null) {
318+
return null;
319+
}
320+
321+
ulong stringIndex = is64Bit switch {
322+
true => ((DynamicEntry<ulong>)sonameEntry).Value,
323+
false => ((DynamicEntry<uint>)sonameEntry).Value
324+
};
325+
326+
// Offset is into the .dynstr section
327+
if (!elf.TryGetSection (".dynstr", out ISection? strtabSection) || strtabSection == null || strtabSection.Type != SectionType.StringTable) {
328+
return null;
329+
}
330+
331+
var strtab = (IStringTable)strtabSection;
332+
try {
333+
return strtab[(long)stringIndex];
334+
} catch (Exception ex) {
335+
Log.Debug ($"Failed to obtain soname from the string table (asked for index {stringIndex})", ex);
336+
return null;
337+
}
338+
}
339+
294340
static string? GetBuildID (IELF elf, bool is64Bit)
295341
{
296-
byte[]? contents = GetNoteSectionContents (elf, is64Bit, ".note.gnu.build-id");
342+
// From elf_common.h
343+
//
344+
// #define NT_GNU_BUILD_ID 3
345+
//
346+
const ulong NT_GNU_BUILD_ID = 0;
347+
byte[]? contents = GetNoteSectionContents (elf, is64Bit, ".note.gnu.build-id", NT_GNU_BUILD_ID);
297348
if (contents == null) {
298349
return null;
299350
}
300351

301-
// TODO: decode
302-
return "decoding not implemented yet";
352+
var sb = new StringBuilder ();
353+
foreach (byte b in contents) {
354+
sb.Append ($"{b:x02}");
355+
}
356+
357+
return sb.ToString ();
303358
}
304359

305360
static string? GetAndroidIdent (IELF elf, bool is64Bit)
306361
{
307362
return GetNoteSectionContentsAsString (elf, is64Bit, ".note.android.ident");;
308363
}
309364

310-
static string? GetNoteSectionContentsAsString (IELF elf, bool is64Bit, string sectionName)
365+
static string? GetNoteSectionContentsAsString (IELF elf, bool is64Bit, string sectionName, ulong noteType = 0)
311366
{
312367
byte[]? contents = GetNoteSectionContents (elf, is64Bit, sectionName);
313368
if (contents == null) {
@@ -317,13 +372,27 @@ static ulong DetectAlignment (IELF elf, bool is64Bit)
317372
return Encoding.UTF8.GetString (contents);
318373
}
319374

320-
static byte[]? GetNoteSectionContents (IELF elf, bool is64Bit, string sectionName)
375+
static byte[]? GetNoteSectionContents (IELF elf, bool is64Bit, string sectionName, ulong noteType = 0)
321376
{
322377
if (!elf.TryGetSection (sectionName, out ISection? section) || section == null || section.Type != SectionType.Note) {
323378
return null;
324379
}
325380

326-
return ((INoteSection)section).Description;
381+
var note = (INoteSection)section;
382+
if (noteType == 0) {
383+
return note.Description;
384+
}
385+
386+
ulong type = is64Bit switch {
387+
true => ((NoteSection<ulong>)note).NoteType,
388+
false => ((NoteSection<uint>)note).NoteType
389+
};
390+
391+
if (type != noteType) {
392+
return null;
393+
}
394+
395+
return note.Description;
327396
}
328397

329398
public bool HasSection (string name, SectionType type = SectionType.Null)

tools/apput/src/Package/ApplicationPackage.cs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.IO.Compression;
55
using System.Linq;
66

7-
using ELFSharp.ELF.Sections;
87
using Xamarin.Android.Tasks;
98
using Xamarin.Android.Tools;
109

@@ -33,14 +32,6 @@ public abstract class ApplicationPackage : IAspect
3332
"META-INF/ANDROIDD.RSA",
3433
};
3534

36-
readonly static List<(string sectionName, SectionType type)> NativeAotSections = new () {
37-
("__managedcode", SectionType.ProgBits),
38-
(".dotnet_eh_table", SectionType.ProgBits),
39-
("__unbox", SectionType.ProgBits),
40-
("__modules", SectionType.ProgBits),
41-
(".hydrated", SectionType.NoBits),
42-
};
43-
4435
public static string AspectName { get; } = "Application package";
4536

4637
public abstract string PackageFormat { get; }
@@ -59,6 +50,7 @@ public abstract class ApplicationPackage : IAspect
5950
public List<AssemblyStore>? AssemblyStores { get; protected set; }
6051
public List<AndroidTargetArch> Architectures { get; protected set; } = new ();
6152
public List<NativeAppInfo> NativeAppInfos { get; protected set; } = new ();
53+
public List<SharedLibrary> SharedLibraries { get; protected set; } = new ();
6254

6355
AndroidManifest? manifest;
6456

@@ -91,6 +83,7 @@ public static IAspect LoadAspect (Stream stream, IAspectState state, string? des
9183
// TODO: for all of the below, add support for detection of older XA apps (just to warn that this version doesn't support
9284
// and that people should use older tools)
9385
ret.TryDetectArchitectures (); // This must be called first, some further steps depend on it
86+
ret.CollectSharedLibraries (); // This must be called second, some further steps need the collection of shared libraries
9487
ret.TryDetectRuntime ();
9588
ret.TryDetectWhetherIsSigned ();
9689
ret.TryLoadAssemblyStores ();
@@ -100,6 +93,11 @@ public static IAspect LoadAspect (Stream stream, IAspectState state, string? des
10093
return ret;
10194
}
10295

96+
void CollectSharedLibraries ()
97+
{
98+
// TODO: find and detect shared libraries
99+
}
100+
103101
void TryDetectArchitectures ()
104102
{
105103
foreach (AndroidTargetArch arch in Enum.GetValues <AndroidTargetArch> ()) {
@@ -175,6 +173,7 @@ bool TryDetectNativeAotRuntime ()
175173
foreach (AndroidTargetArch arch in Architectures) {
176174
string libDir = GetNativeLibDir (arch);
177175

176+
// TODO: move .so search code to CollectSharedLibraries and just leave NAOT detection here
178177
foreach (ZipArchiveEntry? entry in Zip.Entries) {
179178
if (entry == null) {
180179
continue;
@@ -212,23 +211,11 @@ bool SharedLibraryIsNativeAOT (ZipArchiveEntry entry)
212211
return false;
213212
}
214213

215-
IAspectState aspectState = SharedLibrary.ProbeAspect (stream, entry.FullName);
214+
IAspectState aspectState = NativeAotSharedLibrary.ProbeAspect (stream, entry.FullName);
216215
if (!aspectState.Success) {
217216
return false;
218217
}
219218

220-
var dso = SharedLibrary.LoadAspect (stream, aspectState, entry.FullName) as SharedLibrary;
221-
if (dso == null) {
222-
throw new InvalidOperationException ("Internal error: unexpected SharedLibrary load result.");
223-
}
224-
225-
// Just one match should be enough
226-
foreach (var naotSection in NativeAotSections) {
227-
if (dso.HasSection (naotSection.sectionName, naotSection.type)) {
228-
return true;
229-
}
230-
}
231-
232219
return false;
233220
}
234221

tools/apput/src/Reporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public static void Report (IAspect aspect)
2121
}
2222

2323
if (knownType == null) {
24-
throw new InvalidOperationException ($"Internal error: cannot generate report for type '{aspectType}'");
24+
throw new InvalidOperationException ($"Internal error: cannot generate report for unsupported type '{aspectType}'");
2525
}
2626

2727
IReporter? reporter = CreateSpecificReporter (aspectType, aspect);

tools/apput/src/Reporters/BaseReporter.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,19 @@ abstract class BaseReporter : IReporter
77
protected const ConsoleColor LabelColor = ConsoleColor.White;
88
protected const ConsoleColor ValidValueColor = ConsoleColor.Green;
99
protected const ConsoleColor InvalidValueColor = ConsoleColor.Red;
10+
protected const ConsoleColor BannerColor = ConsoleColor.Cyan;
1011

11-
public abstract void Report ();
12+
protected abstract string AspectName { get; }
13+
protected abstract string ShortDescription { get; }
14+
15+
public void Report ()
16+
{
17+
WriteLine (BannerColor, $"# {AspectName} ({ShortDescription})");
18+
DoReport ();
19+
WriteLine ();
20+
}
21+
22+
protected abstract void DoReport ();
1223

1324
protected void WriteAspectDesc (string text)
1425
{
@@ -54,4 +65,5 @@ protected void Write (ConsoleColor color, string text)
5465
}
5566

5667
protected string YesNo (bool yes) => yes ? "yes" : "no";
68+
protected string ValueOrNone (string? s) => String.IsNullOrEmpty (s) ? "none" : s;
5769
}

tools/apput/src/Reporters/NativeAotSharedLibraryReporter.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,10 @@ namespace ApplicationUtility;
33
[AspectReporter (typeof (NativeAotSharedLibrary))]
44
class NativeAotSharedLibraryReporter : SharedLibraryReporter
55
{
6-
readonly NativeAotSharedLibrary library;
7-
6+
protected override string AspectName => NativeAotSharedLibrary.AspectName;
87
protected override string LibraryKind => "NativeAOT shared library";
98

109
public NativeAotSharedLibraryReporter (NativeAotSharedLibrary library)
1110
: base (library)
12-
{
13-
this.library = library;
14-
}
15-
16-
public override void Report ()
17-
{
18-
base.Report ();
19-
}
11+
{}
2012
}

0 commit comments

Comments
 (0)