Skip to content

Commit 5b23202

Browse files
committed
More info about shared libraries
1 parent 69dfcf6 commit 5b23202

File tree

5 files changed

+204
-19
lines changed

5 files changed

+204
-19
lines changed

tools/apput/src/Native/NativeAotSharedLibrary.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ class NativeAotSharedLibrary : SharedLibrary
1818

1919
protected NativeAotSharedLibrary (Stream stream, string libraryName)
2020
: base (stream, libraryName)
21-
{
22-
// TODO: perhaps gather some more stats? Code size, data size, alignment, stuff like that
23-
}
21+
{}
2422

2523
public new static IAspect LoadAspect (Stream stream, IAspectState? state, string? description)
2624
{

tools/apput/src/Native/SharedLibrary.cs

Lines changed: 135 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,45 @@
44

55
using ELFSharp.ELF;
66
using ELFSharp.ELF.Sections;
7+
using ELFSharp.ELF.Segments;
78

89
namespace ApplicationUtility;
910

1011
class SharedLibrary : IAspect, IDisposable
1112
{
1213
const uint ELF_MAGIC = 0x464c457f;
14+
const string DebugLinkSectionName = ".gnu_debuglink";
15+
const string PayloadSectionName = "payload";
1316

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;
23+
readonly string? androidIdent;
24+
readonly string? buildId;
25+
readonly string? debugLink;
26+
readonly ulong libraryAlignment;
1427
readonly ulong payloadOffset;
1528
readonly ulong payloadSize;
16-
readonly string libraryName;
17-
readonly bool is64Bit;
18-
readonly Stream libraryStream;
1929

20-
IELF elf;
2130
bool disposed;
22-
NativeArchitecture nativeArch = NativeArchitecture.Unknown;
2331

2432
public static string AspectName { get; } = "Native shared library";
2533

26-
public bool HasAndroidPayload => payloadSize > 0;
27-
public string Name => libraryName;
2834
public NativeArchitecture TargetArchitecture => nativeArch;
35+
public bool HasAndroidIdent => !String.IsNullOrEmpty (androidIdent);
36+
public bool HasAndroidPayload => payloadSize > 0;
37+
public bool HasBuildID => !String.IsNullOrEmpty (buildId);
38+
public bool HasDebugInfo => hasDebugInfo;
39+
public bool HasDebugLink => !String.IsNullOrEmpty (debugLink);
2940
public bool Is64Bit => is64Bit;
41+
public string Name => libraryName;
42+
public string? AndroidIdent => androidIdent;
43+
public string? BuildID => buildId;
44+
public string? DebugLink => debugLink;
45+
public ulong Alignment => libraryAlignment;
3046

3147
protected IELF ELF => elf;
3248

@@ -36,6 +52,10 @@ protected SharedLibrary (Stream stream, string libraryName)
3652
this.libraryName = libraryName;
3753
(elf, is64Bit, nativeArch) = LoadELF (stream, libraryName);
3854
(payloadOffset, payloadSize) = FindAndroidPayload (elf);
55+
libraryAlignment = DetectAlignment (elf, is64Bit);
56+
(hasDebugInfo, debugLink) = DetectDebugInfo (elf, libraryName);
57+
buildId = GetBuildID (elf, is64Bit);
58+
androidIdent = GetAndroidIdent (elf, is64Bit);
3959
}
4060

4161
public static IAspect LoadAspect (Stream stream, IAspectState? state, string? description)
@@ -172,8 +192,8 @@ protected static bool IsSupportedELFSharedLibrary (Stream stream, string? descri
172192

173193
(ulong offset, ulong size) FindAndroidPayload (IELF elf)
174194
{
175-
if (!elf.TryGetSection ("payload", out ISection? payloadSection)) {
176-
Log.Debug ($"SharedLibrary: shared library '{libraryName}' doesn't have the 'payload' section.");
195+
if (!elf.TryGetSection (PayloadSectionName, out ISection? payloadSection)) {
196+
Log.Debug ($"SharedLibrary: shared library '{libraryName}' doesn't have the '{PayloadSectionName}' section.");
177197
return (0, 0);
178198
}
179199

@@ -200,6 +220,112 @@ protected static bool IsSupportedELFSharedLibrary (Stream stream, string? descri
200220
}
201221
}
202222

223+
static (bool hasDebugInfo, string? debugLink) DetectDebugInfo (IELF elf, string libraryName)
224+
{
225+
bool hasDebugInfo = HasSection (elf, libraryName, ".debug_info", SectionType.ProgBits);
226+
227+
if (!HasSection (elf, libraryName, DebugLinkSectionName, SectionType.ProgBits)) {
228+
return (hasDebugInfo, null);
229+
}
230+
231+
return (hasDebugInfo, ReadDebugLinkSection (elf, libraryName));
232+
}
233+
234+
static string? ReadDebugLinkSection (IELF elf, string libraryName)
235+
{
236+
Log.Debug ($"Trying to load debug link section from {libraryName}");
237+
if (!elf.TryGetSection (DebugLinkSectionName, out ISection? section)) {
238+
Log.Debug ($"Debug link section '{DebugLinkSectionName}' could not be read");
239+
return null;
240+
}
241+
242+
// From https://sourceware.org/gdb/current/onlinedocs/gdb.html/Separate-Debug-Files.html
243+
//
244+
// * A filename, with any leading directory components removed, followed by a zero byte,
245+
// * zero to three bytes of padding, as needed to reach the next four-byte boundary within the section, and
246+
// * a four-byte CRC checksum, stored in the same endianness used for the executable file itself. The checksum is computed on the debugging information file’s full
247+
// contents by the function given below, passing zero as the crc argument.
248+
249+
// TODO: At this point we ignore the CRC, perhaps it would be worth reading it?
250+
byte[] contents = section.GetContents ();
251+
if (contents.Length < 4) {
252+
// there must at least be the 4-byte CRC, otherwise section content is invalid
253+
return null;
254+
}
255+
256+
int zeroIndex = 0;
257+
for (int i = 0; i < contents.Length; i++) {
258+
if (contents[i] != 0) {
259+
continue;
260+
}
261+
262+
zeroIndex = i;
263+
break;
264+
}
265+
266+
if (zeroIndex == 0) {
267+
return null;
268+
}
269+
270+
return Encoding.UTF8.GetString (contents, 0, zeroIndex);
271+
}
272+
273+
static ulong DetectAlignment (IELF elf, bool is64Bit)
274+
{
275+
if (!elf.HasSegmentHeader) {
276+
return 0;
277+
}
278+
279+
foreach (ISegment segment in elf.Segments) {
280+
if (segment.Type != SegmentType.Load) {
281+
continue;
282+
}
283+
284+
if (is64Bit) {
285+
return ((Segment<ulong>)segment).Alignment;
286+
}
287+
288+
return ((Segment<uint>)segment).Alignment;
289+
}
290+
291+
return 0;
292+
}
293+
294+
static string? GetBuildID (IELF elf, bool is64Bit)
295+
{
296+
byte[]? contents = GetNoteSectionContents (elf, is64Bit, ".note.gnu.build-id");
297+
if (contents == null) {
298+
return null;
299+
}
300+
301+
// TODO: decode
302+
return "decoding not implemented yet";
303+
}
304+
305+
static string? GetAndroidIdent (IELF elf, bool is64Bit)
306+
{
307+
return GetNoteSectionContentsAsString (elf, is64Bit, ".note.android.ident");;
308+
}
309+
310+
static string? GetNoteSectionContentsAsString (IELF elf, bool is64Bit, string sectionName)
311+
{
312+
byte[]? contents = GetNoteSectionContents (elf, is64Bit, sectionName);
313+
if (contents == null) {
314+
return null;
315+
}
316+
317+
return Encoding.UTF8.GetString (contents);
318+
}
319+
320+
static byte[]? GetNoteSectionContents (IELF elf, bool is64Bit, string sectionName)
321+
{
322+
if (!elf.TryGetSection (sectionName, out ISection? section) || section == null || section.Type != SectionType.Note) {
323+
return null;
324+
}
325+
326+
return ((INoteSection)section).Description;
327+
}
328+
203329
public bool HasSection (string name, SectionType type = SectionType.Null)
204330
{
205331
return HasSection (elf, libraryName, name, type);

tools/apput/src/Reporters/BaseReporter.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ namespace ApplicationUtility;
44

55
abstract class BaseReporter : IReporter
66
{
7-
const ConsoleColor LabelColor = ConsoleColor.White;
8-
const ConsoleColor ValueColor = ConsoleColor.Green;
7+
protected const ConsoleColor LabelColor = ConsoleColor.White;
8+
protected const ConsoleColor ValidValueColor = ConsoleColor.Green;
9+
protected const ConsoleColor InvalidValueColor = ConsoleColor.Red;
910

1011
public abstract void Report ();
1112

@@ -19,10 +20,15 @@ protected void WriteNativeArch (NativeArchitecture arch)
1920
WriteItem ("Native target architecture", arch.ToString ());
2021
}
2122

22-
protected void WriteItem (string label, string value)
23+
protected void WriteLabel (string label)
2324
{
2425
Write (LabelColor, $"{label}: ");
25-
WriteLine (ValueColor, value);
26+
}
27+
28+
protected void WriteItem (string label, string value)
29+
{
30+
WriteLabel (label);
31+
WriteLine (ValidValueColor, value);
2632
}
2733

2834
protected void WriteLine ()
@@ -35,6 +41,7 @@ protected void WriteLine (ConsoleColor color, string text)
3541
Write (color, text);
3642
WriteLine ();
3743
}
44+
3845
protected void Write (ConsoleColor color, string text)
3946
{
4047
ConsoleColor oldFG = Console.ForegroundColor;
@@ -45,4 +52,6 @@ protected void Write (ConsoleColor color, string text)
4552
Console.ForegroundColor = oldFG;
4653
}
4754
}
55+
56+
protected string YesNo (bool yes) => yes ? "yes" : "no";
4857
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
namespace ApplicationUtility;
22

33
[AspectReporter (typeof (NativeAotSharedLibrary))]
4-
class NativeAotSharedLibraryReporter : BaseReporter
4+
class NativeAotSharedLibraryReporter : SharedLibraryReporter
55
{
66
readonly NativeAotSharedLibrary library;
77

8+
protected override string LibraryKind => "NativeAOT shared library";
9+
810
public NativeAotSharedLibraryReporter (NativeAotSharedLibrary library)
11+
: base (library)
912
{
1013
this.library = library;
1114
}
1215

1316
public override void Report ()
1417
{
15-
WriteAspectDesc ("NativeAOT shared library");
16-
WriteNativeArch (library.TargetArchitecture);
18+
base.Report ();
1719
}
1820
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Text;
2+
3+
namespace ApplicationUtility;
4+
5+
[AspectReporter (typeof (NativeAotSharedLibrary))]
6+
class SharedLibraryReporter : BaseReporter
7+
{
8+
readonly SharedLibrary library;
9+
protected virtual string LibraryKind => "Shared library";
10+
11+
public SharedLibraryReporter (SharedLibrary library)
12+
{
13+
this.library = library;
14+
}
15+
16+
public override void Report ()
17+
{
18+
WriteAspectDesc (LibraryKind);
19+
WriteNativeArch (library.TargetArchitecture);
20+
WriteItem ("Build ID", library.HasBuildID ? library.BuildID! : "none");
21+
WriteDebugInfoDesc ();
22+
23+
if (library.HasAndroidIdent) {
24+
WriteItem ("Android ident", library.AndroidIdent!);
25+
}
26+
}
27+
28+
protected void WriteDebugInfoDesc ()
29+
{
30+
WriteItem ("Has debug info", YesNo (library.HasDebugInfo));
31+
32+
var sb = new StringBuilder (YesNo (library.HasDebugLink));
33+
if (library.HasDebugLink) {
34+
sb.Append (" ('");
35+
sb.Append (library.DebugLink);
36+
sb.Append ("')");
37+
}
38+
WriteItem ("Has debug link", sb.ToString ());
39+
40+
bool compatibleWith16k = library.Alignment >= 0x4000 && (library.Alignment % 0x4000 == 0);
41+
sb.Clear ();
42+
sb.Append ($"0x{library.Alignment:x} (");
43+
if (!compatibleWith16k) {
44+
sb.Append ("NOT ");
45+
}
46+
sb.Append ("compatible with Android 16k library alignment)");
47+
WriteLabel ("Alignment");
48+
WriteLine (compatibleWith16k ? ValidValueColor : InvalidValueColor, sb.ToString ());
49+
}
50+
}

0 commit comments

Comments
 (0)