17
17
* along with Aero. If not, see <https://www.gnu.org/licenses/>.
18
18
*/
19
19
20
+ use core:: fmt:: Write ;
21
+
20
22
use aero_syscall:: { MMapFlags , MMapProt } ;
21
23
22
24
use alloc:: boxed:: Box ;
23
25
use alloc:: collections:: linked_list:: CursorMut ;
24
26
use alloc:: collections:: LinkedList ;
25
27
28
+ use alloc:: string:: String ;
26
29
use xmas_elf:: header:: * ;
27
30
use xmas_elf:: program:: * ;
28
31
use xmas_elf:: * ;
29
32
30
33
use crate :: arch:: task:: userland_last_address;
31
34
use crate :: fs;
35
+ use crate :: fs:: cache:: DirCacheImpl ;
32
36
use crate :: fs:: cache:: DirCacheItem ;
33
37
use crate :: fs:: FileSystemError ;
38
+ use crate :: fs:: Path ;
34
39
use crate :: mem;
35
40
use crate :: mem:: paging:: * ;
36
41
use crate :: mem:: AddressSpace ;
@@ -44,8 +49,8 @@ const ELF_PT2_64_SIZE: usize = core::mem::size_of::<HeaderPt2_<P64>>();
44
49
45
50
#[ derive( Debug ) ]
46
51
pub enum ElfLoadError {
47
- /// Unexpected file system error occured when reading the file.
48
- ReadError ( FileSystemError ) ,
52
+ /// Unexpected file system error occured on an IO operation on the file.
53
+ IOError ( FileSystemError ) ,
49
54
/// The PT1 header has an invalid magic number.
50
55
InvalidMagic ,
51
56
/// The ELF header contains an invalid class.
@@ -63,7 +68,7 @@ fn parse_elf_header<'header>(file: DirCacheItem) -> Result<Header<'header>, ElfL
63
68
64
69
file. inode ( )
65
70
. read_at ( 0 , & mut pt1_hdr_slice)
66
- . map_err ( |err| ElfLoadError :: ReadError ( err) ) ?;
71
+ . map_err ( |err| ElfLoadError :: IOError ( err) ) ?;
67
72
68
73
let pt1_header: & ' header _ = unsafe { & * ( pt1_hdr_slice. as_ptr ( ) as * const HeaderPt1 ) } ;
69
74
@@ -79,7 +84,7 @@ fn parse_elf_header<'header>(file: DirCacheItem) -> Result<Header<'header>, ElfL
79
84
80
85
file. inode ( )
81
86
. read_at ( ELF_PT1_SIZE , & mut pt2_hdr_slice)
82
- . map_err ( |err| ElfLoadError :: ReadError ( err) ) ?;
87
+ . map_err ( |err| ElfLoadError :: IOError ( err) ) ?;
83
88
84
89
let pt2_header_ptr = pt2_hdr_slice. as_ptr ( ) ;
85
90
let pt2_header: & ' header _ = unsafe { & * ( pt2_header_ptr as * const HeaderPt2_ < P64 > ) } ;
@@ -123,7 +128,7 @@ fn parse_program_header<'pheader>(
123
128
124
129
file. inode ( )
125
130
. read_at ( start, & mut phdr_buffer)
126
- . map_err ( |err| ElfLoadError :: ReadError ( err) ) ?;
131
+ . map_err ( |err| ElfLoadError :: IOError ( err) ) ?;
127
132
128
133
let phdr_ptr = phdr_buffer. as_ptr ( ) ;
129
134
@@ -145,6 +150,94 @@ fn parse_program_header<'pheader>(
145
150
}
146
151
}
147
152
153
+ struct Shebang {
154
+ interpreter : DirCacheItem ,
155
+ argument : String ,
156
+ }
157
+
158
+ impl Shebang {
159
+ fn new ( path : String , argument : String ) -> Result < Self , ElfLoadError > {
160
+ let path = Path :: new ( & path) ;
161
+ let interpreter = fs:: lookup_path ( path) . map_err ( |err| ElfLoadError :: IOError ( err) ) ?;
162
+
163
+ Ok ( Self {
164
+ interpreter,
165
+ argument,
166
+ } )
167
+ }
168
+ }
169
+
170
+ /// Returns [`true`] if the provided executable (`bin`) contains a shebang
171
+ /// at the start.
172
+ fn contains_shebang ( bin : DirCacheItem ) -> Result < bool , ElfLoadError > {
173
+ let shebang = & mut [ 0u8 ; 2 ] ;
174
+
175
+ bin. inode ( )
176
+ . read_at ( 0 , shebang)
177
+ . map_err ( |err| ElfLoadError :: IOError ( err) ) ?;
178
+
179
+ Ok ( shebang[ 0 ] == '#' as u8 && shebang[ 1 ] == '!' as u8 )
180
+ }
181
+
182
+ fn parse_shebang ( bin : DirCacheItem ) -> Result < Option < Shebang > , ElfLoadError > {
183
+ if !contains_shebang ( bin. clone ( ) ) ? {
184
+ return Ok ( None ) ;
185
+ }
186
+
187
+ // Syntax: #![whitespace]interpreter_path [single-argument][new-line]
188
+ //
189
+ // NOTE: We set the position to `2` since we skip the `#!` prefix.
190
+ let mut idx = 2 ;
191
+
192
+ let read_at_index = |idx : usize | -> Result < char , ElfLoadError > {
193
+ let c = & mut [ 0u8 ; 1 ] ;
194
+
195
+ bin. inode ( )
196
+ . read_at ( idx, c)
197
+ . map_err ( |err| ElfLoadError :: IOError ( err) ) ?;
198
+
199
+ Ok ( c[ 0 ] as char )
200
+ } ;
201
+
202
+ // 1. check for the optional whitespace (ignore it):
203
+ if read_at_index ( idx) ? == ' ' {
204
+ idx += 1 ;
205
+ }
206
+
207
+ // we build the string with `16` capicity to avoid reallocations.
208
+ let mut path = String :: with_capacity ( 16 ) ;
209
+ let mut arg = String :: with_capacity ( 16 ) ;
210
+
211
+ // 2. parse the interpreter path:
212
+ loop {
213
+ let char = read_at_index ( idx) ?;
214
+
215
+ if char == ' ' {
216
+ idx += 1 ;
217
+ break ;
218
+ } else if char == '\n' {
219
+ // there is no argument, early return:
220
+ return Ok ( Some ( Shebang :: new ( path, arg) ?) ) ;
221
+ }
222
+
223
+ idx += 1 ;
224
+ path. write_char ( char)
225
+ . expect ( "parse_shebang: internal error" ) ;
226
+ }
227
+
228
+ // 3. parse the argument:
229
+ loop {
230
+ let char = read_at_index ( idx) ?;
231
+ idx += 1 ;
232
+
233
+ if char == '\n' || char == ' ' {
234
+ return Ok ( Some ( Shebang :: new ( path, arg) ?) ) ;
235
+ }
236
+
237
+ arg. write_char ( char) . expect ( "parse_shebang: internal error" ) ;
238
+ }
239
+ }
240
+
148
241
pub struct Elf < ' this > {
149
242
pub header : Header < ' this > ,
150
243
pub file : DirCacheItem ,
@@ -782,6 +875,17 @@ impl VmProtected {
782
875
& mut self ,
783
876
bin : DirCacheItem ,
784
877
) -> Result < LoadedBinary < ' header > , ElfLoadError > {
878
+ // check for a shebang before proceeding.
879
+ if let Some ( shebang) = parse_shebang ( bin. clone ( ) ) ? {
880
+ log:: debug!(
881
+ "shebang: (interpreter={}, argument={})" ,
882
+ shebang. interpreter. absolute_path_str( ) ,
883
+ shebang. argument
884
+ ) ;
885
+
886
+ unimplemented ! ( )
887
+ }
888
+
785
889
let elf = Elf :: new ( bin. clone ( ) ) ?;
786
890
let header = & elf. header ;
787
891
0 commit comments