1
1
using Biohazrd . Transformation . Infrastructure ;
2
2
using Kaisa ;
3
+ using LibObjectFile . Elf ;
3
4
using System ;
4
5
using System . Collections . Generic ;
5
6
using System . Diagnostics ;
6
7
using System . Diagnostics . CodeAnalysis ;
7
8
using System . IO ;
9
+ using System . Linq ;
8
10
using System . Text ;
9
11
10
12
namespace Biohazrd . Transformation . Common
@@ -46,10 +48,117 @@ public bool TrackVerboseImportInformation
46
48
/// <remarks>You generally do not want to enable this option unless you have advanced needs for virtual methods to be exported.</remarks>
47
49
public bool ErrorOnMissingVirtualMethods { get ; set ; }
48
50
51
+ private static ReadOnlySpan < byte > ElfFileSignature => new byte [ ] { 0x7F , 0x45 , 0x4C , 0x46 } ; // 0x7F "ELF"
52
+ private static ReadOnlySpan < byte > WindowsArchiveSignature => new byte [ ] { 0x21 , 0x3C , 0x61 , 0x72 , 0x63 , 0x68 , 0x3E , 0xA } ; // "!<arch>\n"
53
+ private static int LongestSignatureLength => WindowsArchiveSignature . Length ;
49
54
public void AddLibrary ( string filePath )
50
55
{
51
56
using FileStream stream = new ( filePath , FileMode . Open , FileAccess . Read ) ;
52
- Archive library = new ( stream ) ;
57
+
58
+ // Determine if the library is an ELF shared library or a Windows archive file
59
+ Span < byte > header = stackalloc byte [ LongestSignatureLength ] ;
60
+ if ( stream . Read ( header ) != header . Length )
61
+ { throw new ArgumentException ( "The specified file is too small to be a library." , nameof ( filePath ) ) ; }
62
+
63
+ stream . Position = 0 ;
64
+
65
+ if ( header . StartsWith ( WindowsArchiveSignature ) )
66
+ {
67
+ Archive library = new ( stream ) ;
68
+
69
+ // Enumerate all import and export symbols from the package
70
+ foreach ( ArchiveMember member in library . ObjectFiles )
71
+ {
72
+ if ( member is ImportArchiveMember importMember )
73
+ {
74
+ SymbolImportExportInfo info = new ( importMember ) ;
75
+ GetOrCreateSymbolEntry ( importMember . Symbol ) . AddImport ( filePath , info ) ;
76
+ }
77
+ else if ( member is CoffArchiveMember coffMember )
78
+ {
79
+ foreach ( CoffSymbol coffSymbol in coffMember . Symbols )
80
+ { GetOrCreateSymbolEntry ( coffSymbol . Name ) . AddExport ( filePath ) ; }
81
+ }
82
+ }
83
+ }
84
+ else if ( header . StartsWith ( ElfFileSignature ) )
85
+ {
86
+ ElfReaderOptions options = new ( ) { ReadOnly = true } ;
87
+ ElfObjectFile elf = ElfObjectFile . Read ( stream , options ) ;
88
+
89
+ // Determine the name to use for the import
90
+ // In theory we could strip off the lib prefix and .so suffix here, however that will remove information required for loading the library via NativeLibrary.Load(string)
91
+ // If we want to strip these we should handle it in the output stage instead (that way it's consistent across the entire library too.)
92
+ string libraryFileName = Path . GetFileName ( filePath ) ;
93
+
94
+ // Find the .dynsym table
95
+ // (We do not bother with the symbol hash table because we'll most likely end up needing nearly every symbol as it is and it simplifies our architecture.)
96
+ const string sectionName = ".dynsym" ;
97
+ ElfSymbolTable ? symbolTable = elf . Sections . OfType < ElfSymbolTable > ( ) . FirstOrDefault ( s => s . Name == sectionName ) ;
98
+
99
+ if ( symbolTable is null )
100
+ { throw new ArgumentException ( $ "The specified ELF file does not contain a '{ sectionName } ' section.") ; }
101
+
102
+ // See https://refspecs.linuxbase.org/elf/gabi4+/ch4.symtab.html for details on what the symbol table entires mean
103
+ // Note that the spec is not very specific about what is and isn't allowed in the dynamic symbol table, so we're probably overly defensive here
104
+ // In general we ignore symbols which have OS and architecture-specific types or binding since we can't safely understand what they're for.
105
+ foreach ( ElfSymbol symbol in symbolTable . Entries )
106
+ {
107
+ // Ignore unnamed symbols
108
+ if ( String . IsNullOrEmpty ( symbol . Name ) )
109
+ { continue ; }
110
+
111
+ // Ignore symbols which are not publically accessible
112
+ // (Default effectively means public)
113
+ if ( symbol . Visibility != ElfSymbolVisibility . Default && symbol . Visibility != ElfSymbolVisibility . Protected )
114
+ { continue ; }
115
+
116
+ // Ignore symbols which are undefined
117
+ if ( symbol . Section . IsSpecial && symbol . Section . SpecialIndex == ElfNative . SHN_UNDEF )
118
+ { continue ; }
119
+
120
+ // Ignore symbols which are not functions or objects (variables)
121
+ if ( symbol . Type != ElfSymbolType . Function && symbol . Type != ElfSymbolType . Object )
122
+ { continue ; }
123
+
124
+ // Ignore symbols which are not global or weak
125
+ // Note that we do not actually differentiate between global and weak symbols, the distinction only matters for static linking
126
+ // See https://www.bottomupcs.com/libraries_and_the_linker.xhtml#d0e10440
127
+ if ( symbol . Bind != ElfSymbolBind . Global && symbol . Bind != ElfSymbolBind . Weak )
128
+ { continue ; }
129
+
130
+ // Determine the import type of the symbol based on its section
131
+ ImportType importType ;
132
+ switch ( symbol . Section . Section ? . Name )
133
+ {
134
+ case ".text" :
135
+ importType = ImportType . Code ;
136
+ break ;
137
+ case ".rodata" :
138
+ importType = ImportType . Const ;
139
+ break ;
140
+ case ".data" :
141
+ case ".bss" :
142
+ importType = ImportType . Data ;
143
+ break ;
144
+ case null when symbol . Section . IsSpecial :
145
+ Debug . Fail ( $ "ELF symbol from special section '{ symbol . Section } '") ;
146
+ continue ;
147
+ default :
148
+ if ( symbol . Section . Section ? . Name is not null )
149
+ { Debug . Fail ( $ "ELF symbol from unrecognized section '{ symbol . Section . Section . Name } '") ; }
150
+ else
151
+ { Debug . Fail ( $ "ELF symbol from unnamed/null section.") ; }
152
+ continue ;
153
+ }
154
+
155
+ // Add the symbol to our lookup
156
+ SymbolImportExportInfo info = new ( libraryFileName , symbol , importType ) ;
157
+ GetOrCreateSymbolEntry ( symbol . Name ) . AddImport ( filePath , info ) ;
158
+ }
159
+ }
160
+ else
161
+ { throw new ArgumentException ( "The specified file does not appear to be in a compatible format." , nameof ( filePath ) ) ; }
53
162
54
163
SymbolEntry GetOrCreateSymbolEntry ( string symbol )
55
164
{
@@ -61,21 +170,6 @@ SymbolEntry GetOrCreateSymbolEntry(string symbol)
61
170
}
62
171
return symbolEntry ;
63
172
}
64
-
65
- // Enumerate all import and export symbols from the package
66
- foreach ( ArchiveMember member in library . ObjectFiles )
67
- {
68
- if ( member is ImportArchiveMember importMember )
69
- {
70
- SymbolImportExportInfo info = new ( importMember ) ;
71
- GetOrCreateSymbolEntry ( importMember . Symbol ) . AddImport ( filePath , info ) ;
72
- }
73
- else if ( member is CoffArchiveMember coffMember )
74
- {
75
- foreach ( CoffSymbol coffSymbol in coffMember . Symbols )
76
- { GetOrCreateSymbolEntry ( coffSymbol . Name ) . AddExport ( filePath ) ; }
77
- }
78
- }
79
173
}
80
174
81
175
private bool Resolve ( string symbolName , [ NotNullWhen ( true ) ] out string ? dllFileName , [ NotNullWhen ( true ) ] out string ? mangledName , ref DiagnosticAccumulator diagnosticsAccumulator , bool isFunction , bool isVirtualMethod )
@@ -106,7 +200,9 @@ static string MakeErrorMessage(string message, SymbolEntry symbolEntry)
106
200
107
201
foreach ( ( string library , SymbolImportExportInfo info ) in symbolEntry . Sources )
108
202
{
109
- if ( info . IsImport )
203
+ if ( info . IsFromElf )
204
+ { builder . Append ( $ "\n '{ library } '") ; } // The library and the DllFileName are the same for ELF sources, avoid printing the redundant info.
205
+ else if ( info . IsImport )
110
206
{ builder . Append ( $ "\n '{ library } ': Import from '{ info . DllFileName } '") ; }
111
207
else
112
208
{ builder . Append ( $ "\n '{ library } ': Statically-linked export'") ; }
@@ -273,6 +369,7 @@ private readonly struct SymbolImportExportInfo
273
369
public ImportNameType ImportNameType { get ; }
274
370
public string ? DllFileName { get ; }
275
371
public ushort OrdinalOrHint { get ; }
372
+ public bool IsFromElf { get ; }
276
373
277
374
public SymbolImportExportInfo ( ImportArchiveMember importMember )
278
375
{
@@ -281,6 +378,17 @@ public SymbolImportExportInfo(ImportArchiveMember importMember)
281
378
ImportNameType = importMember . ImportHeader . NameType ;
282
379
DllFileName = importMember . Dll ;
283
380
OrdinalOrHint = importMember . ImportHeader . OrdinalOrHint ;
381
+ IsFromElf = false ;
382
+ }
383
+
384
+ public SymbolImportExportInfo ( string libraryFileName , ElfSymbol symbol , ImportType importType )
385
+ {
386
+ IsImport = true ;
387
+ ImportNameType = ImportNameType . Name ; // ELFs do not have ordinal members
388
+ DllFileName = libraryFileName ;
389
+ OrdinalOrHint = 0 ;
390
+ ImportType = importType ;
391
+ IsFromElf = true ;
284
392
}
285
393
286
394
public bool IsEquivalentTo ( SymbolImportExportInfo other )
0 commit comments