44
55using ELFSharp . ELF ;
66using ELFSharp . ELF . Sections ;
7+ using ELFSharp . ELF . Segments ;
78
89namespace ApplicationUtility ;
910
1011class 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 ) ;
0 commit comments