4
4
5
5
using ELFSharp . ELF ;
6
6
using ELFSharp . ELF . Sections ;
7
+ using ELFSharp . ELF . Segments ;
7
8
8
9
namespace ApplicationUtility ;
9
10
10
11
class SharedLibrary : IAspect , IDisposable
11
12
{
12
13
const uint ELF_MAGIC = 0x464c457f ;
14
+ const string DebugLinkSectionName = ".gnu_debuglink" ;
15
+ const string PayloadSectionName = "payload" ;
13
16
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 ;
14
27
readonly ulong payloadOffset ;
15
28
readonly ulong payloadSize ;
16
- readonly string libraryName ;
17
- readonly bool is64Bit ;
18
- readonly Stream libraryStream ;
19
29
20
- IELF elf ;
21
30
bool disposed ;
22
- NativeArchitecture nativeArch = NativeArchitecture . Unknown ;
23
31
24
32
public static string AspectName { get ; } = "Native shared library" ;
25
33
26
- public bool HasAndroidPayload => payloadSize > 0 ;
27
- public string Name => libraryName ;
28
34
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 ) ;
29
40
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 ;
30
46
31
47
protected IELF ELF => elf ;
32
48
@@ -36,6 +52,10 @@ protected SharedLibrary (Stream stream, string libraryName)
36
52
this . libraryName = libraryName ;
37
53
( elf , is64Bit , nativeArch ) = LoadELF ( stream , libraryName ) ;
38
54
( 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 ) ;
39
59
}
40
60
41
61
public static IAspect LoadAspect ( Stream stream , IAspectState ? state , string ? description )
@@ -172,8 +192,8 @@ protected static bool IsSupportedELFSharedLibrary (Stream stream, string? descri
172
192
173
193
( ulong offset , ulong size ) FindAndroidPayload ( IELF elf )
174
194
{
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.") ;
177
197
return ( 0 , 0 ) ;
178
198
}
179
199
@@ -200,6 +220,112 @@ protected static bool IsSupportedELFSharedLibrary (Stream stream, string? descri
200
220
}
201
221
}
202
222
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
+
203
329
public bool HasSection ( string name , SectionType type = SectionType . Null )
204
330
{
205
331
return HasSection ( elf , libraryName , name , type ) ;
0 commit comments