From 15ecefb5fbc0129f0af4e7435e795d3db6749c1b Mon Sep 17 00:00:00 2001 From: Julien THILLARD Date: Sun, 22 Mar 2026 14:27:56 +0100 Subject: [PATCH] Add debug infos on panic --- Cargo.toml | 4 +- build-tools/.cargo/config.toml | 2 + build-tools/Cargo.toml | 14 +++ build-tools/src/gen_symbols.rs | 128 ++++++++++++++++++++++++++++ crates/shared/src/syscall.rs | 44 ++++------ justfile | 7 +- library/std/patches/sys/pal/mod.sed | 2 - library/std/src/sys/pal/survos.rs | 12 +++ library/std/src/sys/stdio/survos.rs | 7 +- src/boot.rs | 8 ++ src/main.rs | 2 +- src/panic_handler.rs | 71 ++++++++++++++- src/process.rs | 19 ++--- user/test_pic/src/main.rs | 12 ++- 14 files changed, 282 insertions(+), 50 deletions(-) create mode 100644 build-tools/.cargo/config.toml create mode 100644 build-tools/Cargo.toml create mode 100644 build-tools/src/gen_symbols.rs diff --git a/Cargo.toml b/Cargo.toml index 3840326..4484786 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "3" -members = [ "user/*"] -exclude = ["library"] +members = ["user/*"] +exclude = ["library", "build-tools"] [package] name = "kernel-rust" diff --git a/build-tools/.cargo/config.toml b/build-tools/.cargo/config.toml new file mode 100644 index 0000000..2f05654 --- /dev/null +++ b/build-tools/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "x86_64-unknown-linux-gnu" diff --git a/build-tools/Cargo.toml b/build-tools/Cargo.toml new file mode 100644 index 0000000..a15dba6 --- /dev/null +++ b/build-tools/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "build-tools" +version = "0.1.0" +edition = "2024" + +[[bin]] +name = "gen-symbols" +path = "src/gen_symbols.rs" + +[dependencies] +rayon = "1.11" +object = "0.32" +addr2line = "0.21" +rustc-demangle = "0.1" diff --git a/build-tools/src/gen_symbols.rs b/build-tools/src/gen_symbols.rs new file mode 100644 index 0000000..0b55fea --- /dev/null +++ b/build-tools/src/gen_symbols.rs @@ -0,0 +1,128 @@ +use addr2line::Context; +use object::{Object, ObjectSymbol, SymbolKind}; +use std::collections::HashMap; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::Path; + +#[repr(C, packed(4))] +#[derive(Debug)] +struct RawSymbol { + addr: u64, + line: u32, + name_off: u32, + file_off: u32, +} + +fn main() -> Result<(), Box> { + let elf_path = "../target/riscv64/debug/kernel-rust"; + let bin_data = std::fs::read(elf_path)?; + let obj_file = object::File::parse(&*bin_data)?; + + let context = Context::new(&obj_file)?; + + let mut symbols_list = Vec::new(); + let mut string_table = Vec::new(); + let mut str_cache = HashMap::new(); + + // Helper pour gérer la table des chaînes (String Table) + let mut add_string = |s: &str| -> u32 { + *str_cache.entry(s.to_string()).or_insert_with(|| { + let off = string_table.len() as u32; + string_table.extend_from_slice(s.as_bytes()); + string_table.push(0); // Null terminator + off + }) + }; + + println!("Extraction des symboles depuis {}...", elf_path); + + obj_file.symbols().enumerate().for_each(|(i, sym)| { + // On ne garde que les fonctions (Text) + if sym.kind() == SymbolKind::Text && sym.size() > 0 { + let addr = sym.address(); + let raw_name = sym.name().unwrap_or("unknown"); + let name = format!("{:#}", rustc_demangle::demangle(raw_name)); + + let mut frames = context.find_frames(addr).skip_all_loads().unwrap(); + + let (file, line) = if let Ok(Some(frame)) = frames.next() { + let f = frame + .location + .as_ref() + .and_then(|l| { + l.file.and_then(|file| { + Path::new(file) + .strip_prefix(std::env::current_dir().unwrap().parent().unwrap()) + .map_or(Some(file), |p| p.to_str()) + }) + }) + .unwrap_or("unknown"); + let l = frame.location.as_ref().and_then(|l| l.line).unwrap_or(0); + (f, l) + } else { + ("unknown", 0) + }; + + symbols_list.push(RawSymbol { + addr, + line, + name_off: add_string(&name), + file_off: add_string(file), + }); + } + }); + + // Tri par adresse pour la recherche binaire au runtime + symbols_list.sort_by_key(|s| s.addr); + + let idx = match symbols_list.binary_search_by_key(&(0x000000008004073c), |s| s.addr) { + Ok(i) => i, + Err(i) if i > 0 => i - 1, + _ => panic!(), + }; + + println!("{:?}", unsafe { + get_str( + string_table + .as_ptr() + .add(symbols_list[idx].name_off as usize), + ) + }); + + // Écriture du fichier symbols.bin + let mut f = BufWriter::new(File::create("../target/symbols.bin")?); + + // Header : [u64: count] [u64: string_table_offset] + let header_size = 16; + let sym_table_size = symbols_list.len() * std::mem::size_of::(); + f.write_all(&(symbols_list.len() as u64).to_le_bytes())?; + f.write_all(&((header_size + sym_table_size) as u64).to_le_bytes())?; + + // Table des symboles + for sym in &symbols_list { + unsafe { + let bytes = std::slice::from_raw_parts( + (sym as *const RawSymbol) as *const u8, + std::mem::size_of::(), + ); + f.write_all(bytes)?; + } + } + + // Table des noms + f.write_all(&string_table)?; + + println!( + "Terminé ! {} symboles écrits dans symbols.bin", + symbols_list.len() + ); + Ok(()) +} +unsafe fn get_str(ptr: *const u8) -> &'static str { + let mut len = 0; + while *ptr.add(len) != 0 { + len += 1; + } + core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) +} diff --git a/crates/shared/src/syscall.rs b/crates/shared/src/syscall.rs index 9d02301..99356e7 100644 --- a/crates/shared/src/syscall.rs +++ b/crates/shared/src/syscall.rs @@ -1,10 +1,5 @@ use core::{alloc::Layout, time::Duration}; -use bffs::path::Path; -use io::SeekFrom; - -use crate::fs::File; - #[repr(u64)] pub enum SysCall { Read = 0, @@ -100,10 +95,12 @@ macro_rules! syscall { }; } -pub fn exit() { +pub fn exit() -> ! { unsafe { syscall!(SysCall::Exit); } + #[allow(clippy::empty_loop)] + loop {} } pub fn sleep(duration: Duration) { @@ -128,6 +125,8 @@ pub fn write_int_temp(content: u64) { } } +#[allow(unknown_lints)] +#[allow(fuzzy_provenance_casts)] pub fn alloc(layout: Layout) -> *mut u8 { unsafe { let size = layout.size(); @@ -143,13 +142,12 @@ pub fn dealloc(ptr: *mut u8, layout: core::alloc::Layout) { syscall!(SysCall::Dealloc, ptr as u64, size as u64, align as u64); } } -pub fn open>(path: P) -> File { +pub fn open(path: &str) -> u64 { unsafe { - let path_str = path.as_ref().as_str(); - let ptr = path_str.as_ptr(); - let size = path_str.len(); + let ptr = path.as_ptr(); + let size = path.len(); let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64); - File::from_raw_fd(fd) + fd } } pub fn close(file_descriptor: u64) { @@ -173,29 +171,23 @@ pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 { len } } -pub fn seek(file_descriptor: u64, seek: SeekFrom) { +/// seek_type: 0 -> start, 1 -> end, 2 -> current +pub fn seek(file_descriptor: u64, seek_type: u8, seek: u64) { unsafe { - let (discriminant, value) = match seek { - SeekFrom::Start(v) => (0, v), - SeekFrom::End(v) => (1, v as u64), - SeekFrom::Current(v) => (2, v as u64), - }; - syscall!(SysCall::Seek, file_descriptor, discriminant, value); + syscall!(SysCall::Seek, file_descriptor, seek_type as u64, seek); } } -pub fn spawn>(path: P) { +pub fn spawn(path: &str) { unsafe { - let path_str = path.as_ref().as_str(); - let ptr = path_str.as_ptr(); - let size = path_str.len(); + let ptr = path.as_ptr(); + let size = path.len(); syscall!(SysCall::Spawn, ptr as u64, size as u64); } } -pub fn execve>(path: P) { +pub fn execve(path: &str) { unsafe { - let path_str = path.as_ref().as_str(); - let ptr = path_str.as_ptr(); - let size = path_str.len(); + let ptr = path.as_ptr(); + let size = path.len(); syscall!(SysCall::ExecVE, ptr as u64, size as u64); } } diff --git a/justfile b/justfile index 087eece..87ef1a7 100644 --- a/justfile +++ b/justfile @@ -25,11 +25,15 @@ build_user_prog prog: fi cp {{ bin_path / prog + "-stripped" }} {{ "mnt/usr/bin" / prog }} +make-symbols: + cd build-tools && cargo r --bin gen-symbols --release + build: mount_filesystem @for file in `ls user`; do \ {{ just_executable() }} release="{{ release }}" cargo_flags="{{ cargo_flags }}" build_user_prog $file ; \ done - RUSTFLAGS="-Clink-arg=-Tilm.ld --sysroot {{ justfile_directory() / "sysroot" }}" cargo b {{ cargo_flags }} + RUSTFLAGS="-Cforce-frame-pointers=yes -Clink-arg=-Tilm.ld --sysroot {{ justfile_directory() / "sysroot" }}" cargo b {{ cargo_flags }} + {{ just_executable() }} make-symbols sync run: build (runner f"{{bin_path / "kernel-rust"}}") @@ -41,6 +45,7 @@ QEMU := f"qemu-system-riscv64 \ -device virtio-keyboard-pci \ -device virtio-mouse-pci \ -device loader,file=disk.img,addr=0xA0000000 \ + -device loader,file=target/symbols.bin,addr=0xB0000000 \ -bios none -m 1024M {{qemu_flags}}" # -trace \"virtio*\" diff --git a/library/std/patches/sys/pal/mod.sed b/library/std/patches/sys/pal/mod.sed index acd2d76..0f38d4c 100644 --- a/library/std/patches/sys/pal/mod.sed +++ b/library/std/patches/sys/pal/mod.sed @@ -1,6 +1,4 @@ 62a \ target_os = "survos" => { \ - mod unsupported; \ - pub use self::unsupported::*; \ mod survos; \ pub use self::survos::*; \ } diff --git a/library/std/src/sys/pal/survos.rs b/library/std/src/sys/pal/survos.rs index e68ea1d..bc33826 100644 --- a/library/std/src/sys/pal/survos.rs +++ b/library/std/src/sys/pal/survos.rs @@ -1,3 +1,10 @@ +#[path = "unsupported/mod.rs"] +mod unsupported; +pub use self::unsupported::*; + +#[path = "../../../../../crates/shared/src/syscall.rs"] +mod syscall; + /// # Safety /// `argc` and `argv` are passed by the kernel #[unsafe(no_mangle)] @@ -9,3 +16,8 @@ pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize { unsafe { main(argc, argv) } } + +pub fn abort_internal() -> ! { + // todo real abort + syscall::exit() +} diff --git a/library/std/src/sys/stdio/survos.rs b/library/std/src/sys/stdio/survos.rs index 7bfdf49..56cce36 100644 --- a/library/std/src/sys/stdio/survos.rs +++ b/library/std/src/sys/stdio/survos.rs @@ -2,7 +2,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; #[path = "unsupported.rs"] mod unsupported; -pub use self::unsupported::{STDIN_BUF_SIZE, Stderr, Stdin, is_ebadf, panic_output}; +pub use self::unsupported::{STDIN_BUF_SIZE, Stderr, Stdin, is_ebadf}; pub struct Stdout; // pub struct Stdin; @@ -36,6 +36,11 @@ impl io::Write for Stdout { // } // } +pub fn panic_output() -> Option { + // Todo, use Stderr + Some(Stdout::new()) +} + #[repr(u64)] pub enum SysCall { Read = 0, diff --git a/src/boot.rs b/src/boot.rs index 4ec8835..fdb9cfb 100644 --- a/src/boot.rs +++ b/src/boot.rs @@ -18,6 +18,14 @@ pub extern "C" fn _start() { naked_asm!( " la sp, _heap_end + + // Set BSS to zero + la t0, __bss_start + la t1, __bss_end + 1: sd zero, 0(t0) + addi t0, t0, 8 + bleu t0, t1, 1b + jal machine_mode_entry 1: j 1b" diff --git a/src/main.rs b/src/main.rs index b932234..f6176a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,7 @@ mod virtio; mod virtual_console; mod virtual_fs; -pub const HEAP_SIZE: usize = 1024 * 1024 * 32; // 32Mo RAM +pub const HEAP_SIZE: usize = 1024 * 1024 * 128; // 128Mo RAM #[global_allocator] static HEAP: Heap = Heap::empty(); diff --git a/src/panic_handler.rs b/src/panic_handler.rs index 080da81..957019d 100644 --- a/src/panic_handler.rs +++ b/src/panic_handler.rs @@ -4,7 +4,10 @@ //! framebuffer before halting the CPU. use core::arch::riscv64::wfi; -use alloc::{format, string::ToString}; +use alloc::{ + format, + string::{String, ToString}, +}; use log::error; use crate::{ @@ -14,6 +17,27 @@ use crate::{ vga::Vga, }; +fn print_stack_trace() { + let mut fp: usize; + unsafe { + core::arch::asm!("mv {}, s0", out(reg) fp); + } + + error!("STACK TRACE:"); + for i in 0..10 { + if fp == 0 || !fp.is_multiple_of(8) { + break; + } + unsafe { + // On RISC-V with FP : + // fp - 8 => Return Address (ra) + // fp - 16 => Previous Frame Pointer + let ra = *((fp - 8) as *const usize); + error!(" [{}] 0x{:016x} {}", i, ra, resolve_symbol(ra)); + fp = *((fp - 16) as *const usize); + } + } +} #[panic_handler] /// Kernel panic handler that displays the panic message on the framebuffer and halts. fn panic(panic_info: &core::panic::PanicInfo) -> ! { @@ -26,6 +50,7 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { panic_message = format!("{panic_message} at {}:{}", location.file(), location.line()); } error!("{panic_message}"); + print_stack_trace(); Vga.clear_screen(Color::WHITE); unsafe { Vga.draw_string(0, 0, "PANIC !", Color::BLACK, Color::WHITE) }; @@ -44,3 +69,47 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { unsafe { wfi() } } } + +#[repr(C, packed(4))] +#[derive(Debug)] +struct RawSymbol { + addr: u64, + line: u32, + name_off: u32, + file_off: u32, +} +pub fn resolve_symbol(pc: usize) -> String { + let start = 0xB000_0000 as *const u8; + + let count = unsafe { *(start as *const u64) }; + let str_table_off = unsafe { *((start as *const u64).add(1)) }; + let symbol_ptr = unsafe { start.add(16) as *const RawSymbol }; + let str_table_ptr = unsafe { start.add(str_table_off as usize) }; + + let symbols = unsafe { core::slice::from_raw_parts(symbol_ptr, count as usize) }; + + let idx = match symbols.binary_search_by_key(&(pc as u64), |s| s.addr) { + Ok(i) => i, + Err(i) if i > 0 => i - 1, + _ => return Default::default(), + }; + + let sym = &symbols[idx]; + + unsafe { + let name = get_str(str_table_ptr.add(sym.name_off as usize)); + let file = get_str(str_table_ptr.add(sym.file_off as usize)); + + format!("at {} ({}:{})", name, file, sym.line) + } +} + +unsafe fn get_str(ptr: *const u8) -> &'static str { + unsafe { + let mut len = 0; + while *ptr.add(len) != 0 { + len += 1; + } + core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) + } +} diff --git a/src/process.rs b/src/process.rs index 81acd80..f12c552 100644 --- a/src/process.rs +++ b/src/process.rs @@ -23,7 +23,7 @@ use crate::{ }; /// Size of the stack allocated to each process (in 64-bit words). -const STACK_SIZE: usize = 4096; +const STACK_SIZE: usize = 1024 * 1024 * 2; /// Represents the state of a process in the system. #[derive(Debug, PartialEq, Eq)] @@ -197,7 +197,6 @@ impl Scheduler { if ph.p_type == goblin::elf::program_header::PT_LOAD { let dst = unsafe { base.add((ph.p_vaddr - min_vaddr) as usize) }; let src_off = ph.p_offset as usize; - println!("off{:x?}", dst); let copy_len = ph.p_filesz as usize; if copy_len > 0 { unsafe { @@ -208,6 +207,12 @@ impl Scheduler { ) } } + } + } + for ph in gelf.program_headers.iter() { + if ph.p_type == goblin::elf::program_header::PT_LOAD { + let dst = unsafe { base.add((ph.p_vaddr - min_vaddr) as usize) }; + let copy_len = ph.p_filesz as usize; if ph.p_memsz as usize > copy_len { unsafe { core::slice::from_raw_parts_mut( @@ -229,16 +234,6 @@ impl Scheduler { let where_off = (rela.r_offset - min_vaddr) as usize; let where_ptr = unsafe { base.add(where_off) } as *mut u64; let val = (base as u64).wrapping_add(rela.r_addend.unwrap() as u64); - if rela.r_addend.unwrap() == 0x5bfb8 { - println!( - "Relocating GLOBAL_PANIC_COUNT: at {:p}, setting value to {:x}", - where_ptr, val - ); - assert!( - val % 8 == 0, - "DANGER: Address of GLOBAL_PANIC_COUNT is not 8-aligned!" - ); - } unsafe { core::ptr::write_unaligned(where_ptr, val) }; } _ => {} diff --git a/user/test_pic/src/main.rs b/user/test_pic/src/main.rs index c01ceec..ab9506c 100644 --- a/user/test_pic/src/main.rs +++ b/user/test_pic/src/main.rs @@ -1,10 +1,12 @@ #![allow(unused)] -#![feature(panic_internals, core_intrinsics)] +use core::sync::atomic::AtomicUsize; use io::{Read as Readio, Write}; -use shared::syscall; +use shared::{fs::File, syscall}; use std::io::{Read, Stdin, stdin}; +static FOO: AtomicUsize = AtomicUsize::new(0); + fn main() { let mut input = String::new(); input.push('a'); @@ -13,13 +15,15 @@ fn main() { // syscall::write(&mut file, &[255; 6400 * 50]); // syscall::sleep(Duration::from_secs_f64(2.0)); syscall::close(0); - let mut tty = syscall::open("/dev/tty0"); + let mut tty = unsafe { File::from_raw_fd(syscall::open("/dev/tty0")) }; syscall::close(1); let _ = syscall::open("/dev/tty0"); println!("test from test_pic"); tty.write(input.as_bytes()).unwrap(); syscall::spawn("/usr/bin/shell"); - core::panicking::panic("explicit panic"); + // panic!("explicit panic"); + // std::process::abort(); + unsafe {core::arch::asm!("unimp")}; loop { let mut test = [0; 2]; // let len = stdin().read(&mut test);