Skip to content

Commit 4ea02df

Browse files
vm: add a shebang parser
Signed-off-by: Andy-Python-Programmer <[email protected]>
1 parent f4f4ef2 commit 4ea02df

File tree

3 files changed

+110
-48
lines changed

3 files changed

+110
-48
lines changed

.vscode/launch.json

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,6 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4-
{
5-
"type": "cppdbg",
6-
"request": "launch",
7-
"name": "Attach Qemu",
8-
"program": "${workspaceFolder}/src/target/x86_64-aero_os/release/aero_kernel",
9-
"args": [],
10-
"cwd": "${workspaceFolder}",
11-
"externalConsole": false,
12-
"MIMode": "gdb",
13-
"setupCommands": [
14-
{
15-
"description": "Enable pretty-printing for gdb",
16-
"text": "-enable-pretty-printing",
17-
"ignoreFailures": true
18-
}
19-
],
20-
"targetArchitecture": "x86_64",
21-
"customLaunchSetupCommands": [
22-
{
23-
"text": "target remote :1234",
24-
"description": "Connect to QEMU remote debugger"
25-
},
26-
{
27-
"text": "symbol-file ${workspaceFolder}/src/target/x86_64-aero_os/release/aero_kernel",
28-
"description": "Get kernel symbols"
29-
}
30-
],
31-
"avoidWindowsConsoleRedirection": true,
32-
},
33-
{
34-
"name": "Launch (debug)",
35-
"type": "lldb",
36-
"request": "custom",
37-
"initCommands": [
38-
"platform select remote-gdb-server"
39-
],
40-
"targetCreateCommands": [
41-
"target create ${workspaceFolder}/src/target/x86_64-aero_os/release/aero_kernel"
42-
],
43-
"processCreateCommands": [
44-
"gdb-remote 127.0.0.1:1234" // Connect to the GDB Server
45-
]
46-
},
474
{
485
"name": "Launch (release)",
496
"type": "lldb",

base-files/shebang_test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#! /bin/bash

src/aero_kernel/src/userland/vm.rs

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,25 @@
1717
* along with Aero. If not, see <https://www.gnu.org/licenses/>.
1818
*/
1919

20+
use core::fmt::Write;
21+
2022
use aero_syscall::{MMapFlags, MMapProt};
2123

2224
use alloc::boxed::Box;
2325
use alloc::collections::linked_list::CursorMut;
2426
use alloc::collections::LinkedList;
2527

28+
use alloc::string::String;
2629
use xmas_elf::header::*;
2730
use xmas_elf::program::*;
2831
use xmas_elf::*;
2932

3033
use crate::arch::task::userland_last_address;
3134
use crate::fs;
35+
use crate::fs::cache::DirCacheImpl;
3236
use crate::fs::cache::DirCacheItem;
3337
use crate::fs::FileSystemError;
38+
use crate::fs::Path;
3439
use crate::mem;
3540
use crate::mem::paging::*;
3641
use crate::mem::AddressSpace;
@@ -44,8 +49,8 @@ const ELF_PT2_64_SIZE: usize = core::mem::size_of::<HeaderPt2_<P64>>();
4449

4550
#[derive(Debug)]
4651
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),
4954
/// The PT1 header has an invalid magic number.
5055
InvalidMagic,
5156
/// The ELF header contains an invalid class.
@@ -63,7 +68,7 @@ fn parse_elf_header<'header>(file: DirCacheItem) -> Result<Header<'header>, ElfL
6368

6469
file.inode()
6570
.read_at(0, &mut pt1_hdr_slice)
66-
.map_err(|err| ElfLoadError::ReadError(err))?;
71+
.map_err(|err| ElfLoadError::IOError(err))?;
6772

6873
let pt1_header: &'header _ = unsafe { &*(pt1_hdr_slice.as_ptr() as *const HeaderPt1) };
6974

@@ -79,7 +84,7 @@ fn parse_elf_header<'header>(file: DirCacheItem) -> Result<Header<'header>, ElfL
7984

8085
file.inode()
8186
.read_at(ELF_PT1_SIZE, &mut pt2_hdr_slice)
82-
.map_err(|err| ElfLoadError::ReadError(err))?;
87+
.map_err(|err| ElfLoadError::IOError(err))?;
8388

8489
let pt2_header_ptr = pt2_hdr_slice.as_ptr();
8590
let pt2_header: &'header _ = unsafe { &*(pt2_header_ptr as *const HeaderPt2_<P64>) };
@@ -123,7 +128,7 @@ fn parse_program_header<'pheader>(
123128

124129
file.inode()
125130
.read_at(start, &mut phdr_buffer)
126-
.map_err(|err| ElfLoadError::ReadError(err))?;
131+
.map_err(|err| ElfLoadError::IOError(err))?;
127132

128133
let phdr_ptr = phdr_buffer.as_ptr();
129134

@@ -145,6 +150,94 @@ fn parse_program_header<'pheader>(
145150
}
146151
}
147152

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+
148241
pub struct Elf<'this> {
149242
pub header: Header<'this>,
150243
pub file: DirCacheItem,
@@ -782,6 +875,17 @@ impl VmProtected {
782875
&mut self,
783876
bin: DirCacheItem,
784877
) -> 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+
785889
let elf = Elf::new(bin.clone())?;
786890
let header = &elf.header;
787891

0 commit comments

Comments
 (0)