diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8de9921 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "user/apps/doomgeneric"] + path = user/apps/doomgeneric + url = git@github.com:xunilGroup/doomgeneric-xunil.git diff --git a/assets/helloworld.elf b/assets/helloworld.elf index 61a550d..12f4335 100755 Binary files a/assets/helloworld.elf and b/assets/helloworld.elf differ diff --git a/kernel/src/driver/elf/header.rs b/kernel/src/driver/elf/header.rs index 1355965..5d1ed80 100644 --- a/kernel/src/driver/elf/header.rs +++ b/kernel/src/driver/elf/header.rs @@ -101,6 +101,39 @@ pub const AT_UID: u64 = 11; pub const AT_GID: u64 = 13; pub const AT_NULL: u64 = 0; +// Dynamic structure types +pub const DT_NULL: i64 = 0; +pub const DT_NEEDED: i64 = 1; +pub const DT_PLTRELSZ: i64 = 2; +pub const DT_PLTGOT: i64 = 3; +pub const DT_HASH: i64 = 4; +pub const DT_STRTAB: i64 = 5; +pub const DT_SYMTAB: i64 = 6; +pub const DT_RELA: i64 = 7; +pub const DT_RELASZ: i64 = 8; +pub const DT_RELAENT: i64 = 9; +pub const DT_STRSZ: i64 = 10; +pub const DT_SYMENT: i64 = 11; +pub const DT_INIT: i64 = 12; +pub const DT_FINI: i64 = 13; +pub const DT_SONAME: i64 = 14; +pub const DT_RPATH: i64 = 15; +pub const DT_SYMBOLIC: i64 = 16; +pub const DT_REL: i64 = 17; +pub const DT_RELSZ: i64 = 18; +pub const DT_RELENT: i64 = 19; +pub const DT_PLTREL: i64 = 20; +pub const DT_DEBUG: i64 = 21; +pub const DT_TEXTREL: i64 = 22; +pub const DT_JMPREL: i64 = 23; +pub const DT_BIND_NOW: i64 = 24; +pub const DT_RUNPATH: i64 = 29; +pub const DT_FLAGS: i64 = 30; +pub const DT_GNU_HASH: i64 = 0x6ffffef5; +pub const DT_VERSYM: i64 = 0x6ffffff0; +pub const DT_VERNEED: i64 = 0x6ffffffe; +pub const DT_VERNEEDNUM: i64 = 0x6fffffff; + #[repr(C)] pub struct Elf64Ehdr { pub e_ident: [u8; 16], @@ -155,6 +188,13 @@ pub struct Elf64Sym { pub st_size: u64, } +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct Elf64Dyn { + pub d_tag: i64, + pub d_val: u64, +} + #[repr(C)] pub struct Elf64Rela { pub r_offset: u64, diff --git a/kernel/src/driver/elf/loader.rs b/kernel/src/driver/elf/loader.rs index 4149613..2022004 100644 --- a/kernel/src/driver/elf/loader.rs +++ b/kernel/src/driver/elf/loader.rs @@ -36,8 +36,8 @@ pub fn load_file( } return match elf_header.e_type { - ET_EXEC => unsafe { load_program(frame_allocator, mapper, elf_header, elf_bytes) }, - ET_DYN => return null(), // TODO + ET_EXEC => unsafe { load_program(frame_allocator, mapper, elf_header, elf_bytes, false) }, + ET_DYN => unsafe { load_program(frame_allocator, mapper, elf_header, elf_bytes, true) }, // TODO ET_REL => return null(), _ => return null(), }; diff --git a/kernel/src/driver/elf/program.rs b/kernel/src/driver/elf/program.rs index c27a7e5..0fa7d69 100644 --- a/kernel/src/driver/elf/program.rs +++ b/kernel/src/driver/elf/program.rs @@ -1,3 +1,9 @@ +use core::{ + ffi::CStr, + ptr::{null, null_mut}, +}; + +use alloc::vec::Vec; use x86_64::{ VirtAddr, structures::paging::{ @@ -8,7 +14,16 @@ use x86_64::{ use crate::{ arch::x86_64::paging::XunilFrameAllocator, driver::{ - elf::header::{Elf64Ehdr, Elf64Phdr, PF_X, PT_LOAD}, + elf::{ + header::{ + DT_JMPREL, DT_NEEDED, DT_NULL, DT_PLTREL, DT_PLTRELSZ, DT_RELA, DT_RELAENT, + DT_RELASZ, DT_STRSZ, DT_STRTAB, DT_SYMENT, DT_SYMTAB, Elf64Dyn, Elf64Ehdr, + Elf64Phdr, Elf64Rela, Elf64Sym, PF_X, PT_DYNAMIC, PT_LOAD, R_X86_64_64, + R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLOT, R_X86_64_NONE, R_X86_64_RELATIVE, SHN_UNDEF, + STB_WEAK, + }, + reloc::elf_do_reloc, + }, syscall::memset, }, util::{align_down, align_up}, @@ -20,24 +35,229 @@ pub unsafe fn elf_pheader(hdr: *const Elf64Ehdr) -> *const Elf64Phdr { unsafe { (hdr as *const u8).add((*hdr).e_phoff as usize) as *const Elf64Phdr } } +pub fn get_vaddr(phdr: *const Elf64Phdr, load_bias: u64) -> *mut u8 { + unsafe { ((*phdr).p_vaddr + load_bias) as *mut u8 } +} + pub unsafe fn load_program( frame_allocator: &mut XunilFrameAllocator, mapper: &mut OffsetPageTable, hdr: *const Elf64Ehdr, elf_bytes: &[u8], + pie: bool, ) -> *const u8 { let phdr = unsafe { elf_pheader(hdr) }; let phnum = unsafe { (*hdr).e_phnum }; + let mut program_headers: Vec<*const Elf64Phdr> = Vec::new(); + let mut pt_dynamic_header: *const Elf64Phdr = core::ptr::null(); for i in 0..phnum { let program_header = unsafe { phdr.add(i as usize) }; + let p_type = unsafe { (*program_header).p_type }; - if unsafe { (*program_header).p_type } == PT_LOAD { - load_segment_to_memory(frame_allocator, mapper, program_header, elf_bytes); + if p_type == PT_LOAD { + program_headers.push(program_header); + } else if p_type == PT_DYNAMIC { + pt_dynamic_header = program_header as *const Elf64Phdr; } } - return unsafe { (*hdr).e_entry as *const u8 }; + if !pie { + for program_header in program_headers { + load_segment_to_memory(frame_allocator, mapper, program_header, elf_bytes, 0); + } + return unsafe { (*hdr).e_entry as *const u8 }; + } else { + let base_address = 0x0000_0100_0000; // TODO: add per-process memory + let min_vaddr = align_down( + program_headers + .iter() + .map(|&phdr| get_vaddr(phdr, 0) as u64) + .min() + .unwrap(), + 4096, + ); + + let load_bias = base_address - min_vaddr; + + for program_header in program_headers { + load_segment_to_memory( + frame_allocator, + mapper, + program_header, + elf_bytes, + load_bias, + ); + } + + if pt_dynamic_header.is_null() { + return null(); + } + + parse_dyn( + hdr, + pt_dynamic_header, + unsafe { elf_bytes.as_ptr().add((*phdr).p_offset as usize) as *const Elf64Dyn }, + load_bias, + ); + + return unsafe { ((*hdr).e_entry + load_bias) as *const u8 }; + } +} + +fn cstr_from_strtab( + strtab_ptr: *const u8, + strtab_size: u64, + off: u32, +) -> Option<&'static core::ffi::CStr> { + let off = off as u64; + if strtab_ptr.is_null() || off >= strtab_size { + return None; + } + + let mut i = off; + + while i < strtab_size { + let b = unsafe { *strtab_ptr.add(i as usize) }; + if b == 0 { + let start = unsafe { strtab_ptr.add(off as usize) } as *const core::ffi::c_char; + return Some(unsafe { CStr::from_ptr(start as *const i8) }); + } + i += 1; + } + + None +} + +pub fn dyn_get_symaddr( + strtab_ptr: *const u8, + strtab_size: u64, + symtab_ptr: *const Elf64Sym, + idx: u64, + load_bias: u64, +) -> Result { + let sym = unsafe { &*symtab_ptr.add(idx as usize) }; + if sym.st_shndx != SHN_UNDEF { + return Ok(load_bias + sym.st_value); + } + + let name = cstr_from_strtab(strtab_ptr, strtab_size, idx as u32); + + let bind = sym.st_info >> 4; + if bind == STB_WEAK { Ok(0) } else { Err(()) } +} + +fn apply_relocations( + hdr: *const Elf64Ehdr, + rela_ptr: *mut Elf64Rela, + rela_table_size: u64, + symtab_ptr: *const Elf64Sym, + strtab_ptr: *const u8, + strtab_size: u64, + load_bias: u64, +) { + for i in 0..rela_table_size as usize / size_of::() { + let rela_ptr = unsafe { rela_ptr.add(i) }; + let ptr = unsafe { (load_bias + (*rela_ptr).r_offset) as *mut u64 }; + let mut value: u64 = 0; + match unsafe { ((*rela_ptr).r_info & 0xffff_ffff) as u32 } { + x if x == R_X86_64_RELATIVE as u32 => unsafe { + value = (load_bias as i64 + (*rela_ptr).r_addend) as u64; + }, + x if x == R_X86_64_64 as u32 => unsafe { + value = (dyn_get_symaddr( + strtab_ptr, + strtab_size, + symtab_ptr, + (*rela_ptr).r_info >> 32, + load_bias, + ) + .unwrap() as i64 + + (*rela_ptr).r_addend) as u64; + }, + x if [R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLOT].contains(&x) => unsafe { + value = dyn_get_symaddr( + strtab_ptr, + strtab_size, + symtab_ptr, + (*rela_ptr).r_info >> 32, + load_bias, + ) + .unwrap() as u64; + }, + x if x == R_X86_64_NONE as u32 => { + continue; // explicitly do nothing + } + _ => { + continue; + } + } + unsafe { *ptr = value }; + } +} + +fn parse_dyn( + hdr: *const Elf64Ehdr, + phdr: *const Elf64Phdr, + dyn_hdr_original: *const Elf64Dyn, + load_bias: u64, +) { + let mut i: usize = 0; + let file_size: u64 = unsafe { (*phdr).p_filesz }; + + let mut rela_ptr: *mut Elf64Rela = null_mut(); + let mut rela_table_size: u64 = 0; + + let mut symtab_ptr: *const Elf64Sym = null(); + + let mut strtab_ptr: *const u8 = null(); + let mut strtab_size: u64 = 0; + + let max = file_size as usize / size_of::(); + + while unsafe { (*dyn_hdr_original.add(i)).d_tag != DT_NULL } && i < max { + let dyn_hdr = unsafe { *(dyn_hdr_original.add(i)) }; + match dyn_hdr.d_tag { + DT_RELA => rela_ptr = (dyn_hdr.d_val + load_bias) as *mut Elf64Rela, + DT_RELASZ => { + rela_table_size = dyn_hdr.d_val; + } + DT_JMPREL => { + // TODO: plt relocations + } + DT_PLTREL => { + // TODO: plt relocations + } + DT_PLTRELSZ => { + // TODO: plt relocations + } + DT_NEEDED => { + // TODO: do dynamic loading + } + DT_SYMTAB => symtab_ptr = (dyn_hdr.d_val + load_bias) as *const Elf64Sym, + DT_STRTAB => strtab_ptr = (dyn_hdr.d_val + load_bias) as *const u8, + DT_STRSZ => { + strtab_size = dyn_hdr.d_val; + } + _ => (), + } + + i += 1; + } + + if rela_ptr.is_null() || symtab_ptr.is_null() || strtab_ptr.is_null() { + return; + } + + apply_relocations( + hdr, + rela_ptr, + rela_table_size, + symtab_ptr, + strtab_ptr, + strtab_size, + load_bias, + ); } pub fn load_segment_to_memory( @@ -45,12 +265,13 @@ pub fn load_segment_to_memory( mapper: &mut OffsetPageTable, phdr: *const Elf64Phdr, elf_bytes: &[u8], + load_bias: u64, ) { let mem_size: u64 = unsafe { (*phdr).p_memsz }; let p_offset: u64 = unsafe { (*phdr).p_offset }; let file_size: u64 = unsafe { (*phdr).p_filesz }; - let vaddr: *mut u8 = unsafe { (*phdr).p_vaddr as *mut u8 }; + let vaddr: *mut u8 = get_vaddr(phdr, load_bias); if p_offset > elf_bytes.len() as u64 || file_size > elf_bytes.len() as u64 diff --git a/kernel/src/driver/elf/section.rs b/kernel/src/driver/elf/section.rs index 1900782..64aa46a 100644 --- a/kernel/src/driver/elf/section.rs +++ b/kernel/src/driver/elf/section.rs @@ -11,25 +11,29 @@ pub unsafe fn elf_section(hdr: *const Elf64Ehdr, idx: usize) -> *const Elf64Shdr unsafe { elf_sheader(hdr).add(idx) } } -// pub unsafe fn elf_str_table(hdr: *const Elf64Ehdr) -> *const u8 { -// if unsafe { (*hdr).e_shstrndx == SHN_UNDEF } { -// return core::ptr::null(); -// } +pub unsafe fn elf_str_table(hdr: *const Elf64Ehdr) -> *const u8 { + if unsafe { (*hdr).e_shstrndx == SHN_UNDEF } { + return core::ptr::null(); + } -// let shdr = unsafe { elf_section(hdr, (*hdr).e_shstrndx as usize) }; + let shdr = unsafe { elf_section(hdr, (*hdr).e_shstrndx as usize) }; -// unsafe { (hdr as *const u8).add((*shdr).sh_offset as usize) } -// } + unsafe { (hdr as *const u8).add((*shdr).sh_offset as usize) } +} -// pub unsafe fn elf_lookup_string(hdr: *const Elf64Ehdr, offset: usize) -> *const u8 { -// let str_tab: *const u8 = unsafe { elf_str_table(hdr) }; +fn elf_lookup_string<'a>(hdr: *const Elf64Ehdr, offset: usize) -> &'a str { + let strtab: *const u8 = unsafe { elf_str_table(hdr) }; + let mut len = 0; -// if str_tab.is_null() { -// return core::ptr::null(); -// } + let start = unsafe { strtab.add(offset) }; -// return unsafe { str_tab.add(offset) }; -// } + unsafe { + while *start.add(len) != 0 { + len += 1; + } + core::str::from_raw_parts(start, len) + } +} pub fn elf_lookup_symbol(name: &CStr) -> *const u8 { return core::ptr::null(); diff --git a/kernel/src/driver/syscall.rs b/kernel/src/driver/syscall.rs index 9e268a0..62fafc4 100644 --- a/kernel/src/driver/syscall.rs +++ b/kernel/src/driver/syscall.rs @@ -39,6 +39,10 @@ pub unsafe fn free(ptr: *mut u8, size: usize, align: usize) { } } +pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) { + unsafe { core::ptr::write_bytes(ptr, val, count) }; +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn syscall_dispatch( num: usize, @@ -65,9 +69,5 @@ pub unsafe extern "C" fn syscall_dispatch( } } -pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) { - unsafe { core::ptr::write_bytes(ptr, val, count) }; -} - pub type Fd = i32; pub type Off = i64; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index b7d1297..98c395a 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,7 +1,8 @@ #![no_std] #![no_main] #![feature(abi_x86_interrupt)] - +#![feature(str_from_raw_parts)] +#![allow(warnings)] extern crate alloc; use core::fmt::Write; diff --git a/libxunil/tests/compile.sh b/libxunil/tests/compile.sh deleted file mode 100644 index 6f67051..0000000 --- a/libxunil/tests/compile.sh +++ /dev/null @@ -1,4 +0,0 @@ -gcc -nostdlib -nostdinc -static -no-pie \ - -o $1 $1.c \ - -L../target/release -l:libxunil.a -mv $1 ../../assets/$1.elf diff --git a/libxunil/tests/helloworld.c b/libxunil/tests/helloworld.c deleted file mode 100644 index 9f4caf3..0000000 --- a/libxunil/tests/helloworld.c +++ /dev/null @@ -1,7 +0,0 @@ -void write(int fd, const char* buf, unsigned long count); -void exit(int code); - -void _start() { - write(1, "Hello from C!\n", 14); - exit(0); -} diff --git a/user/apps/helloworld/compile.sh b/user/apps/helloworld/compile.sh new file mode 100644 index 0000000..9dd4556 --- /dev/null +++ b/user/apps/helloworld/compile.sh @@ -0,0 +1,4 @@ +gcc -nostdlib -nostdinc -static -static-pie \ + -o $1 $1.c \ + -L../../libxunil/target/release -l:libxunil.a +mv $1 ../../../assets/$1.elf diff --git a/user/apps/helloworld/helloworld.c b/user/apps/helloworld/helloworld.c new file mode 100644 index 0000000..ac017d3 --- /dev/null +++ b/user/apps/helloworld/helloworld.c @@ -0,0 +1,6 @@ +#include "../src/stdio.h" + +void _start() { + write(0, "Hello, World!", 13); + exit(0); +} diff --git a/libxunil/.gitignore b/user/libxunil/.gitignore similarity index 100% rename from libxunil/.gitignore rename to user/libxunil/.gitignore diff --git a/libxunil/Cargo.lock b/user/libxunil/Cargo.lock similarity index 100% rename from libxunil/Cargo.lock rename to user/libxunil/Cargo.lock diff --git a/libxunil/Cargo.toml b/user/libxunil/Cargo.toml similarity index 100% rename from libxunil/Cargo.toml rename to user/libxunil/Cargo.toml diff --git a/user/libxunil/include/inttypes.h b/user/libxunil/include/inttypes.h new file mode 100644 index 0000000..c2d4f4b --- /dev/null +++ b/user/libxunil/include/inttypes.h @@ -0,0 +1,2 @@ +#include +#include diff --git a/user/libxunil/include/stdio.h b/user/libxunil/include/stdio.h new file mode 100644 index 0000000..1749750 --- /dev/null +++ b/user/libxunil/include/stdio.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +typedef struct FILE FILE; + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +#define EOF (-1) + +FILE *fopen(const char *path, const char *mode); +int fclose(FILE *fp); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp); +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp); +int fseek(FILE *fp, long offset, int whence); +long ftell(FILE *fp); +int fflush(FILE *fp); +char *fgets(char *s, int size, FILE *fp); +int fputs(const char *s, FILE *fp); +int feof(FILE *fp); +int ferror(FILE *fp); + +int printf(const char *fmt, ...); +int fprintf(FILE *fp, const char *fmt, ...); +int sprintf(char *buf, const char *fmt, ...); +int snprintf(char *buf, size_t size, const char *fmt, ...); +int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap); +int vfprintf(FILE *fp, const char *fmt, va_list ap); +void write(int fd, const char* buf, unsigned long count); +void exit(int code); diff --git a/user/libxunil/include/stdlib.h b/user/libxunil/include/stdlib.h new file mode 100644 index 0000000..c9fbb5f --- /dev/null +++ b/user/libxunil/include/stdlib.h @@ -0,0 +1,20 @@ +#pragma once +#include + +void *malloc(size_t size); +void *calloc(size_t nmemb, size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); + +void exit(int status); +void abort(void); + +int atoi(const char *s); +long atol(const char *s); +double atof(const char *s); +long strtol(const char *s, char **endptr, int base); +double strtod(const char *s, char **endptr); + +char *getenv(const char *name); +void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *)); +int abs(int x); diff --git a/user/libxunil/include/string.h b/user/libxunil/include/string.h new file mode 100644 index 0000000..c4209c4 --- /dev/null +++ b/user/libxunil/include/string.h @@ -0,0 +1,22 @@ +#pragma once +#include + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dst, const void *src, size_t n); +void *memmove(void *dst, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void *memchr(const void *s, int c, size_t n); + +size_t strlen(const char *s); +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, size_t n); +char *strcat(char *dst, const char *src); +char *strncat(char *dst, const char *src, size_t n); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strstr(const char *haystack, const char *needle); +char *strtok(char *str, const char *delim); +char *strdup(const char *s); +char *strerror(int errnum); diff --git a/user/libxunil/include/strings.h b/user/libxunil/include/strings.h new file mode 100644 index 0000000..5abbdee --- /dev/null +++ b/user/libxunil/include/strings.h @@ -0,0 +1,8 @@ +#pragma once +#include + +int strcasecmp(const char *s1, const char *s2); +int strncasecmp(const char *s1, const char *s2, size_t n); +void bzero(void *s, size_t n); +void bcopy(const void *src, void *dest, size_t n); +int bcmp(const void *s1, const void *s2, size_t n); diff --git a/libxunil/src/lib.rs b/user/libxunil/src/lib.rs similarity index 59% rename from libxunil/src/lib.rs rename to user/libxunil/src/lib.rs index 4f27d7e..94043a6 100644 --- a/libxunil/src/lib.rs +++ b/user/libxunil/src/lib.rs @@ -35,6 +35,32 @@ extern "C" fn puts(s: *const u8) -> isize { 0 } +#[unsafe(no_mangle)] +extern "C" fn abs(n: i32) -> i32 { + n.abs() +} + +#[unsafe(no_mangle)] +extern "C" fn atoi(mut c: *const u8) -> i32 { + let mut value: i32 = 0; + let mut sign: i32 = 1; + unsafe { + if (*c) == b'+' || (*c) == b'-' { + if *c == b'-' { + sign = -1; + } + c = c.add(1); + } + while (*c).is_ascii_digit() { + value *= 10; + value += ((*c) - b'0') as i32; + c = c.add(1); + } + } + + value * sign +} + #[panic_handler] fn panic(_: &core::panic::PanicInfo) -> ! { loop {} diff --git a/libxunil/src/syscall.rs b/user/libxunil/src/syscall.rs similarity index 100% rename from libxunil/src/syscall.rs rename to user/libxunil/src/syscall.rs diff --git a/user/libxunil/to_implement.txt b/user/libxunil/to_implement.txt new file mode 100644 index 0000000..d335a2a --- /dev/null +++ b/user/libxunil/to_implement.txt @@ -0,0 +1,22 @@ +abs +atoi +calloc +__ctype_toupper_loc +fprintf +free +malloc +memcpy +memset +printf +realloc +__stack_chk_fail +stderr +strcasecmp +strcmp +strdup +strlen +strncasecmp +strncmp +strncpy +strrchr +toupper