From f17468582078026f28e3e084cb79b5c2a9c32ee1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 5 Jul 2022 14:06:59 +0200 Subject: [PATCH] Initial Commit. --- .gitignore | 1 + Cargo.lock | 113 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 25 ++++++++++ src/exec.rs | 41 ++++++++++++++++ src/lib.rs | 19 ++++++++ src/x86_64.asm | 42 +++++++++++++++++ src/x86_64.ld | 46 ++++++++++++++++++ src/x86_64.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 412 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/exec.rs create mode 100644 src/lib.rs create mode 100644 src/x86_64.asm create mode 100644 src/x86_64.ld create mode 100644 src/x86_64.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..a909a63bac --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bootstrap" +version = "0.0.0" +dependencies = [ + "arrayvec", + "goblin", + "libc", + "linked_list_allocator", + "memoffset", + "redox_syscall", + "scroll", +] + +[[package]] +name = "goblin" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926" +dependencies = [ + "plain", + "scroll", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "linked_list_allocator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=userspace_fexec#933adb23683e9d6529ff2f6ffe7f1211c952897c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" + +[[package]] +name = "spinning_top" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" +dependencies = [ + "lock_api", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..94cdc739ee --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "bootstrap" +version = "0.0.0" +authors = ["4lDO2 <4lDO2@protonmail.com>"] +edition = "2021" +license = "MIT" + +[lib] +name = "bootstrap" +crate-type = ["staticlib"] + +[dependencies] +arrayvec = { version = "0.7", default-features = false } +goblin = { version = "0.4", default-features = false, features = ["elf32", "elf64"] } +libc = "0.2" +memoffset = "0.6" +scroll = { version = "0.10", default-features = false } +linked_list_allocator = "0.9" +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "userspace_fexec" } + +[profile.release] +panic = "abort" + +[profile.dev] +panic = "abort" diff --git a/src/exec.rs b/src/exec.rs new file mode 100644 index 0000000000..9f2563f290 --- /dev/null +++ b/src/exec.rs @@ -0,0 +1,41 @@ +#[no_mangle] +pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> libc::c_int { + let envp = { + let len = 4096; + let buf = libc::calloc(len, 1); + if buf.is_null() { panic!("failed to allocate env buf"); } + let env = core::slice::from_raw_parts_mut(buf as *mut u8, len); + + let fd = syscall::open("sys:env", syscall::O_RDONLY | syscall::O_CLOEXEC).expect("bootstrap: failed to open env"); + let bytes_read = syscall::read(fd, env).expect("bootstrap: failed to read env"); + + if bytes_read >= len { + // TODO: Handle this, we can allocate as much as we want in theory. + panic!("env is too large"); + } + + for c in &mut *env { + if *c == b'\n' { + *c = b'\0'; + } + } + let env_count = env.split(|c| *c == b'\0').count(); + + let envp = libc::calloc(env_count, core::mem::size_of::<*const u8>()) as *mut *const libc::c_char; + if envp.is_null() { panic!("failed to allocate envp buf"); } + + for (idx, var) in env.split(|c| *c == b'\0').enumerate() { + envp.add(idx).write(var.as_ptr() as *const libc::c_char); + } + + envp + }; + + let name_ptr = b"initfs:bin/init\0".as_ptr() as *const libc::c_char; + let argv = [name_ptr, core::ptr::null()]; + + if libc::execve(name_ptr, argv.as_ptr(), envp) == -1 { + libc::perror(b"Failed to execute init\0".as_ptr() as *const libc::c_char); + } + unreachable!() +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..c47e6c82cb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,19 @@ +#![no_std] +#![feature(alloc_error_handler, core_intrinsics, lang_items)] + +#[cfg(target_arch = "x86_64")] +pub mod x86_64; + +pub mod exec; + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + core::intrinsics::abort(); +} + +#[alloc_error_handler] +fn alloc_error_handler(_: core::alloc::Layout) -> ! { + core::intrinsics::abort(); +} +#[lang = "eh_personality"] +extern "C" fn rust_eh_personality() {} diff --git a/src/x86_64.asm b/src/x86_64.asm new file mode 100644 index 0000000000..e8e49136c8 --- /dev/null +++ b/src/x86_64.asm @@ -0,0 +1,42 @@ +BITS 64 + +section .text + +global ustart +extern start + +ustart: + ; Setup a stack. + mov rax, 0x21000384 ; SYS_CLASS_FILE+SYS_ARG_SLICE+SYS_MMAP + mov rdi, 0xFFFFFFFFFFFFFFFF ; dummy `fd`, indicates anonymous map + mov rsi, map ; pointer to Map struct + mov rdx, map_size ; size of Map struct + syscall + + ; Test for success (nonzero value). + cmp rax, 0 + jg .continue + ; (failure) + ud2 +.continue: + ; Subtract 16 since all instructions seem to hate non-canonical RSP values :) + lea rsp, [rax+size-16] + mov rbp, rsp + + ; Stack has the same alignment as `size`. + call start + ; `start` must never return. + ud2 + +section .rodata +map: + dq 0 ; offset (unused) + dq size + dq flags + dq address - size + +map_size equ $ - map + +size equ 65536 ; 64 KiB +address equ 0x0000800000000000 ; highest possible (normal) user address, why not +flags equ 0x0006000E ; PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_FIXED_NOREPLACE diff --git a/src/x86_64.ld b/src/x86_64.ld new file mode 100644 index 0000000000..c8c89bc868 --- /dev/null +++ b/src/x86_64.ld @@ -0,0 +1,46 @@ +ENTRY(ustart) +OUTPUT_FORMAT(elf64-x86-64) + +SECTIONS { + . = 0; + . += SIZEOF_HEADERS; + . = ALIGN(4096); + + .text : { + __text_start = .; + *(.text*) + . = ALIGN(4096); + __text_end = .; + } + .rodata : { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + .data : { + __data_start = .; + *(.data*) + . = ALIGN(4096); + __data_end = .; + + *(.tbss*) + . = ALIGN(4096); + *(.tdata*) + . = ALIGN(4096); + + __bss_start = .; + *(.bss*) + . = ALIGN(4096); + __bss_end = .; + } + __end = .; + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} diff --git a/src/x86_64.rs b/src/x86_64.rs new file mode 100644 index 0000000000..199b19ff68 --- /dev/null +++ b/src/x86_64.rs @@ -0,0 +1,125 @@ +use syscall::flag::MapFlags; + +mod offsets { + extern "C" { + // text (R-X) + static __text_start: u8; + static __text_end: u8; + // rodata (R--) + static __rodata_start: u8; + static __rodata_end: u8; + // data+bss (RW-) + static __data_start: u8; + static __bss_end: u8; + + static __end: u8; + } + pub fn text() -> (usize, usize) { + unsafe { (&__text_start as *const u8 as usize, &__text_end as *const u8 as usize) } + } + pub fn rodata() -> (usize, usize) { + unsafe { (&__rodata_start as *const u8 as usize, &__rodata_end as *const u8 as usize) } + } + pub fn data_and_bss() -> (usize, usize) { + unsafe { (&__data_start as *const u8 as usize, &__bss_end as *const u8 as usize) } + } + #[allow(dead_code)] + pub fn end() -> usize { + unsafe { &__end as *const u8 as usize } + } +} + +// relibc linkage stuff +#[no_mangle] +extern "C" fn _init() {} +#[no_mangle] +extern "C" fn _fini() {} + +extern "C" fn nop() {} + +#[no_mangle] +static __preinit_array_start: extern "C" fn() = nop; +#[no_mangle] +static __preinit_array_end: extern "C" fn() = nop; +#[no_mangle] +static __init_array_start: extern "C" fn() = nop; +#[no_mangle] +static __init_array_end: extern "C" fn() = nop; + +#[no_mangle] +static __fini_array_start: extern "C" fn() = nop; +#[no_mangle] +static __fini_array_end: extern "C" fn() = nop; + +#[no_mangle] +pub unsafe extern "sysv64" fn start() -> ! { + // Remap self, from the previous RWX + + let (text_start, text_end) = offsets::text(); + let (rodata_start, rodata_end) = offsets::rodata(); + let (data_start, data_end) = offsets::data_and_bss(); + + let _ = syscall::open("debug:", syscall::O_RDONLY); // stdin + let _ = syscall::open("debug:", syscall::O_WRONLY); // stdout + let _ = syscall::open("debug:", syscall::O_WRONLY); // stderr + + let _ = syscall::mprotect(text_start, text_end - text_start, MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE).expect("mprotect failed for .text"); + let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); + let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); + + /*const FD_ANONYMOUS: usize = !0; + + { + let heap_size = 1024 * 1024; + let heap_start = syscall::fmap(FD_ANONYMOUS, &syscall::Map { offset: 0, address: 0, size: heap_size, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE }).expect("failed to map heap"); + crate::ALLOCATOR.0.get().write(linked_list_allocator::Heap::new(heap_start, heap_size)); + } + + let init_fd = syscall::open("initfs:/bin/init", syscall::O_CLOEXEC | syscall::O_RDONLY).expect("failed to open init"); + + const STACK_SIZE: usize = 1024 * 1024; + let stack = syscall::fmap(FD_ANONYMOUS, &syscall::Map { offset: 0, address: 0, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, size: STACK_SIZE }).unwrap(); + + let stack_top = 1_usize << 48; + memranges.push(syscall::ExecMemRange { address: stack_top - STACK_SIZE, size: STACK_SIZE, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE, old_address: stack }); + + // Now, push things such as argv, env (i.e. kernel cmdline when executing init) and auxv. + // TODO: Figure out reason behind subtraction by 256 (inherited from kernel behavior). + let mut target_sp = stack_top - 256; + let actual_sp = (stack + STACK_SIZE) as *mut usize; + + let mut push = |value: usize| { + target_sp -= core::mem::size_of::(); + actual_sp = actual_sp.sub(core::mem::size_of::()); + actual_sp.write(value); + };*/ + extern "C" { + fn relibc_start(stack: usize); + } + use goblin::elf::header::header64::Header; + use memoffset::offset_of; + + let stack = [ + // argc + 0, + // argv null terminator + 0_usize, + // envp null terminator + 0_usize, + + // Make the TLS part of ld.so happy, even though we do not use TLS. + syscall::AT_PHDR, + // The kernel loads the entire ELF, so the program header offset can be trivially received. + // TODO: Use goblin for the ELF header (except rust accepts no null pointers...). + (offset_of!(Header, e_phoff) as *mut u64).read() as usize, + syscall::AT_PHENT, + (offset_of!(Header, e_phentsize) as *mut u16).read() as usize, + syscall::AT_PHNUM, + (offset_of!(Header, e_phnum) as *mut u16).read() as usize, + // auxv null terminator + syscall::AT_NULL, + 0, + ]; + relibc_start(stack.as_ptr() as usize); + panic!(); +}