From 5d0b4af0391e390e189c70dae7c2de794027e305 Mon Sep 17 00:00:00 2001 From: thesayol Date: Fri, 12 Apr 2024 20:37:32 +0800 Subject: [PATCH] fix bugs of SYS_execve that uses SYS_mmap not correctly, and add example app for ELF loader. --- .../src/imp/execve/load_elf.rs | 52 ++++------- api/ruxos_posix_api/src/imp/execve/mod.rs | 91 ++++++++++--------- api/ruxos_posix_api/src/imp/execve/stack.rs | 60 +++++------- apps/c/dl/.gitignore | 10 ++ apps/c/dl/README.md | 31 +++++++ apps/c/dl/axbuild.mk | 6 ++ apps/c/dl/config_linux.toml | 20 ++++ apps/c/dl/features.txt | 11 +++ apps/c/dl/main.c | 7 ++ apps/c/dl/rootfs/hello.c | 10 ++ apps/c/dl/rootfs/libadd.c | 6 ++ 11 files changed, 192 insertions(+), 112 deletions(-) create mode 100644 apps/c/dl/.gitignore create mode 100644 apps/c/dl/README.md create mode 100644 apps/c/dl/axbuild.mk create mode 100644 apps/c/dl/config_linux.toml create mode 100644 apps/c/dl/features.txt create mode 100644 apps/c/dl/main.c create mode 100644 apps/c/dl/rootfs/hello.c create mode 100644 apps/c/dl/rootfs/libadd.c diff --git a/api/ruxos_posix_api/src/imp/execve/load_elf.rs b/api/ruxos_posix_api/src/imp/execve/load_elf.rs index e8730cf39..2359148c1 100644 --- a/api/ruxos_posix_api/src/imp/execve/load_elf.rs +++ b/api/ruxos_posix_api/src/imp/execve/load_elf.rs @@ -1,19 +1,12 @@ -use crate::{ctypes::kstat, utils::char_ptr_to_str, *}; +use crate::{ctypes::kstat, *}; use alloc::{vec, vec::Vec}; -use core::{ - ffi::c_char, - ptr::{null, null_mut}, -}; +use core::ptr::null_mut; #[derive(Debug)] pub struct ElfProg { - pub name: Vec, - pub path: Vec, - pub platform: Vec, - pub rand: Vec, pub base: usize, pub entry: usize, - pub interp_path: *const c_char, + pub interp_path: Vec, pub phent: usize, pub phnum: usize, pub phdr: usize, @@ -23,13 +16,11 @@ impl ElfProg { /// read elf from `path`, and copy LOAD segments to a alloacated memory /// /// and load interp, if needed. - pub fn new(filepath: *const c_char) -> Self { - let name = char_ptr_to_str(filepath).unwrap().as_bytes().to_vec(); - let path = name.clone(); - debug!("sys_execve: new elf prog: {:?}", char_ptr_to_str(filepath)); + pub fn new(filepath: &str) -> Self { + debug!("sys_execve: new elf prog: {filepath}"); // open file - let fd = sys_open(filepath, ctypes::O_RDWR as i32, 0); + let fd = sys_open(filepath.as_ptr() as _, ctypes::O_RDWR as _, 0); // get file size let mut buf = ctypes::kstat { @@ -49,16 +40,23 @@ impl ElfProg { .expect("parse ELF failed"); // get program's LOAD mem size - let mut msize = 0; + let mut min_addr = 0; + let mut max_addr = 0; let segs = file.segments().unwrap(); for seg in segs { if seg.p_type == elf::abi::PT_LOAD { - msize += seg.p_memsz; + min_addr = min_addr.min(seg.p_vaddr); + max_addr = max_addr.max(seg.p_vaddr + seg.p_memsz); } } + let msize = (max_addr - min_addr) as usize; + + // alloc memory for LOAD + let prot = ctypes::PROT_WRITE | ctypes::PROT_READ | ctypes::PROT_EXEC; + let flags = ctypes::MAP_ANONYMOUS | ctypes::MAP_PRIVATE; + let base = crate::sys_mmap(null_mut(), msize, prot as _, flags as _, -1, 0) as usize; // copy LOAD segments - let base = crate::sys_mmap(null_mut(), msize as usize, 0, 0, 0, 0) as usize; for seg in segs { if seg.p_type == elf::abi::PT_LOAD { let data = file.segment_data(&seg).unwrap(); @@ -74,23 +72,15 @@ impl ElfProg { let entry = file.ehdr.e_entry as usize + base; // parse interpreter - let mut interp_path = null::(); + let mut interp_path = vec![]; for seg in file.segments().unwrap() { if seg.p_type == elf::abi::PT_INTERP { - let data = file.segment_data(&seg).unwrap(); - interp_path = data.as_ptr() as *const c_char; + let data = file.segment_data(&seg).unwrap().to_vec(); + interp_path = data; break; } } - // platform - #[cfg(target_arch = "aarch64")] - let platform = b"aarch64".to_vec(); - #[cfg(target_arch = "x86_64")] - let platform = b"x86_64".to_vec(); - #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))] - let platform = b"unknown".to_vec(); - // get address of .text for debugging let text_section_addr = base + file @@ -107,10 +97,6 @@ impl ElfProg { Self { base, entry, - name, - path, - platform, - rand: alloc::vec![1, 2], interp_path, phent: file.ehdr.e_phentsize as usize, phnum: file.ehdr.e_phnum as usize, diff --git a/api/ruxos_posix_api/src/imp/execve/mod.rs b/api/ruxos_posix_api/src/imp/execve/mod.rs index 93886d94a..4b1dc5c46 100644 --- a/api/ruxos_posix_api/src/imp/execve/mod.rs +++ b/api/ruxos_posix_api/src/imp/execve/mod.rs @@ -1,29 +1,32 @@ -use core::ffi::c_char; - mod auxv; mod load_elf; mod stack; use alloc::vec; +use core::ffi::c_char; use crate::{ + config, imp::stat::{sys_getgid, sys_getuid}, - sys_getegid, sys_geteuid, + sys_getegid, sys_geteuid, sys_random, + utils::char_ptr_to_str, }; /// int execve(const char *pathname, char *const argv[], char *const envp[] ); pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { use auxv::*; - let prog = load_elf::ElfProg::new(pathname); + let path = char_ptr_to_str(pathname).unwrap(); + let prog = load_elf::ElfProg::new(path); // get entry let mut entry = prog.entry; // if interp is needed let mut at_base = 0; - if !prog.interp_path.is_null() { - let interp_prog = load_elf::ElfProg::new(prog.interp_path); + if !prog.interp_path.is_empty() { + let interp_path = char_ptr_to_str(prog.interp_path.as_ptr() as _).unwrap(); + let interp_prog = load_elf::ElfProg::new(interp_path); entry = interp_prog.entry; at_base = interp_prog.base; debug!("sys_execve: INTERP base is {:x}", at_base); @@ -32,18 +35,13 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { // create stack let mut stack = stack::Stack::new(); - let name = prog.name; - let platform = prog.platform; - // non 8B info - stack.push(vec![0u8; 32], 16); - let p_progname = stack.push(name, 16); - let _p_plat = stack.push(platform, 16); // platform - let p_rand = stack.push(prog.rand, 16); // rand + stack.push(&[0u8; 32], 16); + let rand = unsafe { [sys_random(), sys_random()] }; + let p_rand = stack.push(&rand, 16); // auxv - // TODO: vdso and rand - // TODO: a way to get pagesz instead of a constant + // TODO: vdso let auxv = vec![ AT_PHDR, prog.phdr, @@ -54,9 +52,11 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { AT_BASE, at_base, AT_PAGESZ, - 0x1000, + config::PAGE_SIZE_4K, AT_HWCAP, 0, + AT_PLATFORM, + platform(), AT_CLKTCK, 100, AT_FLAGS, @@ -74,7 +74,7 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { AT_SECURE, 0, AT_EXECFN, - p_progname, + pathname as usize, AT_RANDOM, p_rand, AT_SYSINFO_EHDR, @@ -88,53 +88,48 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { // handle envs and args let mut env_vec = vec![]; let mut arg_vec = vec![]; - let mut argc = 0; - let envp = envp as *const usize; + let mut envp = envp as *const usize; unsafe { - let mut i = 0; - while *envp.add(i) != 0 { - env_vec.push(*envp.add(i)); - i += 1; + while *envp != 0 { + env_vec.push(*envp); + envp = envp.add(1); } env_vec.push(0); } - let argv = argv as *const usize; + let mut argv = argv as *const usize; unsafe { - let mut i = 0; - loop { - let p = *argv.add(i); - if p == 0 { - break; - } - arg_vec.push(p); - argc += 1; - i += 1; + while *argv != 0 { + arg_vec.push(*argv); + argv = argv.add(1); } - arg_vec.push(0); } // push - stack.push(auxv, 16); - stack.push(env_vec, 8); - stack.push(arg_vec, 8); - let _sp = stack.push(vec![argc as usize], 8); + stack.push(&auxv, 16); + stack.push(&env_vec, 8); + stack.push(&arg_vec, 8); + let sp = stack.push(&[arg_vec.len() - 1], 8); // argc // try run debug!( - "sys_execve: run at entry 0x{entry:x}, then it will jump to 0x{:x} ", + "sys_execve: sp is 0x{sp:x}, run at 0x{entry:x}, then jump to 0x{:x} ", prog.entry ); + set_sp_and_jmp(sp, entry); +} + +fn set_sp_and_jmp(sp: usize, entry: usize) -> ! { #[cfg(target_arch = "aarch64")] unsafe { core::arch::asm!(" mov sp, {} - blr {} + br {} ", - in(reg)_sp, + in(reg)sp, in(reg)entry, ); } @@ -144,10 +139,20 @@ pub fn sys_execve(pathname: *const c_char, argv: usize, envp: usize) -> ! { mov rsp, {} jmp {} ", - in(reg)_sp, + in(reg)sp, in(reg)entry, ); } + unreachable!("sys_execve: unknown arch, sp 0x{sp:x}, entry 0x{entry:x}"); +} + +fn platform() -> usize { + #[cfg(target_arch = "aarch64")] + const PLATFORM_STRING: &[u8] = b"aarch64\0"; + #[cfg(target_arch = "x86_64")] + const PLATFORM_STRING: &[u8] = b"x86_64\0"; + #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))] + const PLATFORM_STRING: &[u8] = b"unknown\0"; - unreachable!("sys_execve: unknown arch"); + PLATFORM_STRING.as_ptr() as usize } diff --git a/api/ruxos_posix_api/src/imp/execve/stack.rs b/api/ruxos_posix_api/src/imp/execve/stack.rs index a741263e7..4561a09f3 100644 --- a/api/ruxos_posix_api/src/imp/execve/stack.rs +++ b/api/ruxos_posix_api/src/imp/execve/stack.rs @@ -1,55 +1,43 @@ -use core::{mem::size_of, ptr::null_mut}; +use alloc::{vec, vec::Vec}; -use crate::*; +const STACK_SIZE: usize = ruxconfig::TASK_STACK_SIZE; #[derive(Debug)] pub struct Stack { - sp: usize, - start: usize, - end: usize, + /// stack + data: Vec, + /// index of top byte of stack + top: usize, } impl Stack { - // alloc a stack + /// alloc a stack pub fn new() -> Self { - let size = 0xa00000; // 10M - let p = sys_mmap(null_mut(), size, 0, 0, 0, 0); - - let start = p as usize; - let sp = start + size; - let end = sp; - - Self { sp, start, end } + Self { + data: vec![0u8; STACK_SIZE], + top: STACK_SIZE, + } } - pub fn align(&mut self, align: usize) -> usize { - self.sp -= self.sp % align; - self.sp + /// addr of top of stack + pub fn sp(&self) -> usize { + self.data.as_ptr() as usize + self.top } - pub fn push(&mut self, thing: alloc::vec::Vec, align: usize) -> usize { - let size = thing.len() * size_of::(); - self.sp -= size; - self.sp = self.align(align); // align 16B + /// push data to stack and return the addr of sp + pub fn push(&mut self, data: &[T], align: usize) -> usize { + // move sp to right place + self.top -= core::mem::size_of_val(data); + self.top = memory_addr::align_down(self.top, align); - if self.sp < self.start { - panic!("stack overflow"); - } + assert!(self.top <= self.data.len(), "sys_execve: stack overflow."); - let mut pt = self.sp as *mut T; + // write data into stack + let sp = self.sp() as *mut T; unsafe { - for t in thing { - *pt = t; - pt = pt.add(1); - } + sp.copy_from_nonoverlapping(data.as_ptr(), data.len()); } - self.sp - } -} - -impl Drop for Stack { - fn drop(&mut self) { - sys_munmap(self.start as *mut _, self.end - self.start); + sp as usize } } diff --git a/apps/c/dl/.gitignore b/apps/c/dl/.gitignore new file mode 100644 index 000000000..e3ce33a96 --- /dev/null +++ b/apps/c/dl/.gitignore @@ -0,0 +1,10 @@ +ruxgo_bld +compile_commands.json +.cache +/rootfs/bin/* +/rootfs/lib/* +/rootfs/dev +/rootfs/etc +/rootfs/proc +/rootfs/sys +/rootfs/tmp diff --git a/apps/c/dl/README.md b/apps/c/dl/README.md new file mode 100644 index 000000000..721c5bacb --- /dev/null +++ b/apps/c/dl/README.md @@ -0,0 +1,31 @@ +# ELF loader + +> Read the RuxOS Book for detail. + +## Quick Start + +1. Compile the C files with Musl in `rootfs/`. + +```sh +cd rootfs/ +musl-gcc libadd.c -shared -o lib/libadd.so +musl-gcc hello.c -Llib -ladd -o bin/hello +``` + +2. Copy the Musl dyanmic linker to `rootfs/lib`. + +3. Run + +Run with `ruxgo`: + +```sh +# in apps/c/dl +ruxgo -b && ruxgo -r +``` + +Run with `make` + +```sh +# in the RuxOS main directory. +make run ARCH=aarch64 A=apps/c/dl V9P=y MUSL=y LOG=debug +``` diff --git a/apps/c/dl/axbuild.mk b/apps/c/dl/axbuild.mk new file mode 100644 index 000000000..55cc2d0f4 --- /dev/null +++ b/apps/c/dl/axbuild.mk @@ -0,0 +1,6 @@ +app-objs=main.o + +ARGS = /bin/hello +ENVS = +V9P_PATH=${APP}/rootfs +# make run ARCH=aarch64 A=apps/c/dl V9P=y MUSL=y LOG=debug \ No newline at end of file diff --git a/apps/c/dl/config_linux.toml b/apps/c/dl/config_linux.toml new file mode 100644 index 000000000..9083a1ad2 --- /dev/null +++ b/apps/c/dl/config_linux.toml @@ -0,0 +1,20 @@ +[build] +compiler = "gcc" +loader_app = ["y", "/bin/hello"] + +[os] +name = "ruxos" +services = ["alloc", "paging", "musl", "multitask", "fs", "virtio-9p"] +ulib = "ruxmusl" +develop = "y" + +[os.platform] +name = "aarch64-qemu-virt" +mode = "release" +log = "info" + +[os.platform.qemu] +memory = "2g" +v9p = "y" +v9p_path = "./rootfs" +args = "/bin/hello" diff --git a/apps/c/dl/features.txt b/apps/c/dl/features.txt new file mode 100644 index 000000000..0b7506227 --- /dev/null +++ b/apps/c/dl/features.txt @@ -0,0 +1,11 @@ +paging +alloc +irq +musl +multitask +fs +pipe +poll +rtc +signal +virtio-9p \ No newline at end of file diff --git a/apps/c/dl/main.c b/apps/c/dl/main.c new file mode 100644 index 000000000..cb479e391 --- /dev/null +++ b/apps/c/dl/main.c @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char** argv, char**envp) { + execv(argv[0], argv); + return 0; +} \ No newline at end of file diff --git a/apps/c/dl/rootfs/hello.c b/apps/c/dl/rootfs/hello.c new file mode 100644 index 000000000..f7180dfaf --- /dev/null +++ b/apps/c/dl/rootfs/hello.c @@ -0,0 +1,10 @@ +#include + +extern int add(int, int); + +int main() +{ + printf("hello world\n"); + add(1, 2); + return 0; +} diff --git a/apps/c/dl/rootfs/libadd.c b/apps/c/dl/rootfs/libadd.c new file mode 100644 index 000000000..1be20d21b --- /dev/null +++ b/apps/c/dl/rootfs/libadd.c @@ -0,0 +1,6 @@ +#include + +int add(int a, int b) +{ + printf("%d + %d = %d\n", a, b, a + b); +} \ No newline at end of file