Save failed stack trace code for future reference.
This commit is contained in:
@@ -17,8 +17,5 @@ runner = "scripts/run_debug.sh"
|
|||||||
[target.'cfg(all(target_arch = "x86_64", target_os = "none", not(debug_assertions)))']
|
[target.'cfg(all(target_arch = "x86_64", target_os = "none", not(debug_assertions)))']
|
||||||
runner = "scripts/run_release.sh"
|
runner = "scripts/run_release.sh"
|
||||||
|
|
||||||
[target.x86_64-kernel]
|
|
||||||
rustflags = ["-C", "force-unwind-tables"]
|
|
||||||
|
|
||||||
[registries.gitea]
|
[registries.gitea]
|
||||||
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
|
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
|
||||||
|
|||||||
Generated
-21
@@ -99,18 +99,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "elf"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fallible-iterator"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -123,10 +111,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"elf",
|
|
||||||
"fallible-iterator",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gimli",
|
|
||||||
"libm",
|
"libm",
|
||||||
"limine",
|
"limine",
|
||||||
"linked_list_allocator",
|
"linked_list_allocator",
|
||||||
@@ -160,12 +145,6 @@ dependencies = [
|
|||||||
"pin-utils",
|
"pin-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.31.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|||||||
+5
-3
@@ -19,9 +19,11 @@ futures-util = { version = "0.3.31", default-features = false, features = [
|
|||||||
"alloc",
|
"alloc",
|
||||||
] }
|
] }
|
||||||
linked_list_allocator = { version = "0.10.5", features = ["use_spin"] }
|
linked_list_allocator = { version = "0.10.5", features = ["use_spin"] }
|
||||||
gimli = { version = "0.31.1", default-features = false, features = ["read"] }
|
# gimli = { version = "0.31.1", default-features = false, features = ["read"] }
|
||||||
elf = { version = "0.7.4", default-features = false, features = ["nightly"] }
|
# elf = { version = "0.7.4", default-features = false, features = ["nightly"] }
|
||||||
fallible-iterator = "0.3.0"
|
# fallible-iterator = "0.3.0"
|
||||||
|
# framehop = { version = "0.13.2", default-features = false }
|
||||||
|
# object = { version = "0.36.7", default-features = false, features = ["read"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.2.14"
|
cc = "1.2.14"
|
||||||
|
|||||||
@@ -138,10 +138,10 @@ extern "x86-interrupt" fn page_fault_handler(
|
|||||||
_stack_frame: InterruptStackFrame,
|
_stack_frame: InterruptStackFrame,
|
||||||
_error_code: PageFaultErrorCode,
|
_error_code: PageFaultErrorCode,
|
||||||
) {
|
) {
|
||||||
serial_println!("Exception: Page Fault");
|
// serial_println!("Exception: Page Fault");
|
||||||
serial_println!("Accessed Address: {:?}", Cr2::read());
|
// serial_println!("Accessed Address: {:?}", Cr2::read());
|
||||||
serial_println!("Error Code: {:?}", _error_code);
|
// serial_println!("Error Code: {:?}", _error_code);
|
||||||
serial_println!("{:#?}", _stack_frame);
|
// serial_println!("{:#?}", _stack_frame);
|
||||||
|
|
||||||
if let Some(frame_allocator) = FRAME_ALLOCATOR.get() {
|
if let Some(frame_allocator) = FRAME_ALLOCATOR.get() {
|
||||||
let mut f = frame_allocator.lock();
|
let mut f = frame_allocator.lock();
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ use x86_64::{
|
|||||||
structures::paging::{OffsetPageTable, PageTable},
|
structures::paging::{OffsetPageTable, PageTable},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000; // start address of the memory space where we store allocated stacks
|
/// Start address of the memory space where we store allocated stacks.
|
||||||
pub const HEAP_VIRTUAL_SPACE: usize = 0x4444_4444_0000; // start address of heap allocated memory
|
pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000;
|
||||||
|
/// Start address of heap allocated memory.
|
||||||
|
pub const HEAP_VIRTUAL_SPACE: usize = 0x4444_4444_0000;
|
||||||
pub const HEAP_SIZE: usize = MiB(1).to_bytes();
|
pub const HEAP_SIZE: usize = MiB(1).to_bytes();
|
||||||
|
|
||||||
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
|
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
|
||||||
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
|
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
|
||||||
|
|
||||||
/// Returns a mutable reference to the current level 4 page table.
|
/// Returns a mutable reference to the current level 4 page table.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|||||||
+4
-5
@@ -1,5 +1,5 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(abi_x86_interrupt)]
|
#![feature(abi_x86_interrupt, breakpoint)]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::correctness,
|
clippy::correctness,
|
||||||
clippy::nursery,
|
clippy::nursery,
|
||||||
@@ -35,7 +35,6 @@ use crate::{
|
|||||||
use alloc::{boxed::Box, format};
|
use alloc::{boxed::Box, format};
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use limine::BaseRevision;
|
use limine::BaseRevision;
|
||||||
use std::{debug, unwind::UNWINDER};
|
|
||||||
use x86_64::VirtAddr;
|
use x86_64::VirtAddr;
|
||||||
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
@@ -83,6 +82,7 @@ impl core::error::Error for NoError {}
|
|||||||
/// Panicking before this is initialised is unwise. We should probably extract
|
/// Panicking before this is initialised is unwise. We should probably extract
|
||||||
/// very early init into it's own function because Stack Traces may require
|
/// very early init into it's own function because Stack Traces may require
|
||||||
/// allocations etc.
|
/// allocations etc.
|
||||||
|
#[inline(never)]
|
||||||
pub fn boot() -> Result<(), Box<dyn core::error::Error>> {
|
pub fn boot() -> Result<(), Box<dyn core::error::Error>> {
|
||||||
if !BASE_REVISION.is_supported() {
|
if !BASE_REVISION.is_supported() {
|
||||||
return Err("Base revision not supported.".into());
|
return Err("Base revision not supported.".into());
|
||||||
@@ -149,10 +149,9 @@ pub fn boot() -> Result<(), Box<dyn core::error::Error>> {
|
|||||||
x86_64::instructions::interrupts::enable();
|
x86_64::instructions::interrupts::enable();
|
||||||
debugln!("[Success]");
|
debugln!("[Success]");
|
||||||
|
|
||||||
// Initialises the stack unwinder once and only once because this makes a
|
|
||||||
// heap allocation.
|
|
||||||
debug!(" Initializing Stack Unwinder... ");
|
debug!(" Initializing Stack Unwinder... ");
|
||||||
UNWINDER.lock();
|
// Force evaluate the constructor.
|
||||||
|
// let _unwinder = &*UNWINDER;
|
||||||
debugln!("[Success]");
|
debugln!("[Success]");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+73
-10
@@ -3,9 +3,18 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use foundry_os::arch::x86_64::processing::async_io::task::{Executor, Task};
|
use core::arch::asm;
|
||||||
use foundry_os::prelude::*;
|
|
||||||
use foundry_os::util::shell::shell;
|
use foundry_os::{
|
||||||
|
// arch::x86_64::processing::async_io::task::{Executor, Task},
|
||||||
|
prelude::*,
|
||||||
|
// std::unwind::UNWINDER,
|
||||||
|
// util::shell::shell,
|
||||||
|
};
|
||||||
|
// use framehop::{
|
||||||
|
// x86_64::{CacheX86_64, UnwindRegsX86_64},
|
||||||
|
// *,
|
||||||
|
// };
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn kmain() -> ! {
|
extern "C" fn kmain() -> ! {
|
||||||
@@ -24,15 +33,69 @@ extern "C" fn kmain() -> ! {
|
|||||||
|
|
||||||
test1();
|
test1();
|
||||||
|
|
||||||
let mut executor = Executor::new();
|
// let mut executor = Executor::new();
|
||||||
executor.spawn(Task::new(shell()));
|
// executor.spawn(Task::new(shell()));
|
||||||
executor.run()
|
// executor.run()
|
||||||
|
|
||||||
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test1() {
|
#[inline(never)]
|
||||||
test2()
|
pub fn test1() {
|
||||||
|
test2();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test2() {
|
#[inline(never)]
|
||||||
panic!("Test");
|
pub fn test2() {
|
||||||
|
test3();
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn read_stack(addr: u64) -> Result<u64, ()> {
|
||||||
|
Ok(unsafe { *(addr as *const u64) })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn test3() {
|
||||||
|
// let mut cache = CacheX86_64::new();
|
||||||
|
// let unwinder = &*UNWINDER;
|
||||||
|
|
||||||
|
// let mut rip: u64;
|
||||||
|
// let mut rsp: u64;
|
||||||
|
// let mut rbp: u64;
|
||||||
|
// let mut read_stack_fn = read_stack;
|
||||||
|
|
||||||
|
// unsafe {
|
||||||
|
// asm!("lea {0}, [rip]",
|
||||||
|
// "mov {1}, rsp",
|
||||||
|
// "mov {2}, rbp",
|
||||||
|
// out(reg) rip,
|
||||||
|
// out(reg) rsp,
|
||||||
|
// out(reg) rbp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let regs = UnwindRegsX86_64::new(rip, rsp, rbp);
|
||||||
|
// let mut frame_iter =
|
||||||
|
// unwinder.iter_frames(rip, regs, &mut cache, &mut read_stack_fn);
|
||||||
|
|
||||||
|
// // This is just me testing the unwinding. It seems to not return any
|
||||||
|
// stack // frames for some odd reason.
|
||||||
|
// loop {
|
||||||
|
// match frame_iter.next() {
|
||||||
|
// Ok(Some(frame)) => {
|
||||||
|
// debugln!("Got function with {:x?}",
|
||||||
|
// frame.address_for_lookup()); // match frame {
|
||||||
|
// // FrameAddress::InstructionPointer(rip) => (),
|
||||||
|
// // FrameAddress::ReturnAddress(ra) => {}
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
// Ok(None) => {
|
||||||
|
// debugln!("Hit end of stack.");
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// Err(why) => {
|
||||||
|
// debugln!("{}", why);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
arch::x86_64::drivers::framebuffer::colour::Colour,
|
arch::x86_64::drivers::framebuffer::colour::Colour,
|
||||||
debug, debugln, eprint, eprintln, print, print_log, print_oneshot, println,
|
debug, debugln, eprint, eprintln, hcf, print, print_log, print_oneshot,
|
||||||
println_log, serial_print, serial_println,
|
println, println_log, serial_print, serial_println,
|
||||||
std::debug::_debug,
|
std::debug::_debug,
|
||||||
std::io::{_print, _print_err, _print_log, _serial_write},
|
std::io::{_print, _print_err, _print_log, _serial_write},
|
||||||
};
|
};
|
||||||
|
|||||||
+62
-18
@@ -11,14 +11,14 @@
|
|||||||
//! * Add support for loading binary programs (this should probably be written
|
//! * Add support for loading binary programs (this should probably be written
|
||||||
//! in a different module)
|
//! in a different module)
|
||||||
|
|
||||||
use alloc::format;
|
use alloc::{format, vec::Vec};
|
||||||
use elf::{
|
use elf::{
|
||||||
ElfBytes, ParseError,
|
ElfBytes, ParseError,
|
||||||
endian::LittleEndian,
|
endian::LittleEndian,
|
||||||
parse::{ParseAt, ParsingTable},
|
parse::{ParseAt, ParsingTable},
|
||||||
section::{SectionHeader, SectionHeaderTable},
|
section::{SectionHeader, SectionHeaderTable},
|
||||||
string_table::StringTable,
|
string_table::StringTable,
|
||||||
symbol::SymbolTable,
|
symbol::{Symbol, SymbolTable},
|
||||||
};
|
};
|
||||||
use limine::request::KernelFileRequest;
|
use limine::request::KernelFileRequest;
|
||||||
|
|
||||||
@@ -38,6 +38,8 @@ pub static KERNEL_FILE_REQUEST: KernelFileRequest = KernelFileRequest::new();
|
|||||||
pub enum ElfError {
|
pub enum ElfError {
|
||||||
/// Returned if a section did not exist in [ElfReader::get_section_size].
|
/// Returned if a section did not exist in [ElfReader::get_section_size].
|
||||||
SectionNotExists,
|
SectionNotExists,
|
||||||
|
/// Returned if we failed to fetch the symbol table.
|
||||||
|
Symtab,
|
||||||
/// Parse errors returned by the `elf` crate.
|
/// Parse errors returned by the `elf` crate.
|
||||||
OtherParseError(elf::ParseError),
|
OtherParseError(elf::ParseError),
|
||||||
}
|
}
|
||||||
@@ -49,8 +51,14 @@ impl From<elf::ParseError> for ElfError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ElfReader {
|
pub struct ElfReader {
|
||||||
|
/// The underlying bytes for the ELF file.
|
||||||
|
pub bytes: &'static [u8],
|
||||||
/// Structure returned by the `elf` crate having parsed the ELF header.
|
/// Structure returned by the `elf` crate having parsed the ELF header.
|
||||||
elf: ElfBytes<'static, LittleEndian>,
|
pub elf: ElfBytes<'static, LittleEndian>,
|
||||||
|
/// A sorted list of symbols to binary search.
|
||||||
|
pub sorted_syms: Vec<Symbol>,
|
||||||
|
/// The string table for looking up symbol names.
|
||||||
|
pub symbol_strtab: StringTable<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElfReader {
|
impl ElfReader {
|
||||||
@@ -65,23 +73,26 @@ impl ElfReader {
|
|||||||
/// Both of these should be satisfied, but this function is marked unsafe
|
/// Both of these should be satisfied, but this function is marked unsafe
|
||||||
/// just in case, because we are derefererencing arbitrary pointers.
|
/// just in case, because we are derefererencing arbitrary pointers.
|
||||||
pub unsafe fn new() -> Result<Self, ElfError> {
|
pub unsafe fn new() -> Result<Self, ElfError> {
|
||||||
let response = KERNEL_FILE_REQUEST
|
// Store this for use with other libraries.
|
||||||
.get_response()
|
let bytes = get_elf_slice();
|
||||||
.expect("Didn't get the kernel file from Limine. That's odd.");
|
|
||||||
|
|
||||||
// We fetch these from Limine and use them to parse our own ELF file.
|
|
||||||
let file = response.file();
|
|
||||||
let file_start_ptr = file.addr();
|
|
||||||
let file_size = file.size() as usize;
|
|
||||||
|
|
||||||
// Safety: This slice should contain the whole bytes of the ELF file.
|
|
||||||
let elf_hdr_slice =
|
|
||||||
unsafe { core::slice::from_raw_parts(file_start_ptr, file_size) };
|
|
||||||
|
|
||||||
let elf: ElfBytes<'static, LittleEndian> =
|
let elf: ElfBytes<'static, LittleEndian> =
|
||||||
elf::ElfBytes::minimal_parse(elf_hdr_slice)?;
|
elf::ElfBytes::minimal_parse(bytes)?;
|
||||||
|
|
||||||
Ok(Self { elf })
|
let Some((symtab, strtab)) = elf.symbol_table()? else {
|
||||||
|
return Err(ElfError::Symtab);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort the symtab for later use and store the strtab.
|
||||||
|
let mut symbols = symtab.into_iter().collect::<Vec<Symbol>>();
|
||||||
|
symbols.sort_by_key(|sym| sym.st_value);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
elf,
|
||||||
|
sorted_syms: symbols,
|
||||||
|
symbol_strtab: strtab,
|
||||||
|
bytes,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_symbol_table(
|
pub fn get_symbol_table(
|
||||||
@@ -112,7 +123,7 @@ impl ElfReader {
|
|||||||
/// Gets the section header of `section_name`.
|
/// Gets the section header of `section_name`.
|
||||||
pub fn get_section_header(
|
pub fn get_section_header(
|
||||||
&self,
|
&self,
|
||||||
section_name: &'static str,
|
section_name: &str,
|
||||||
) -> Result<SectionHeader, ElfError> {
|
) -> Result<SectionHeader, ElfError> {
|
||||||
let section_hdr = self
|
let section_hdr = self
|
||||||
.elf
|
.elf
|
||||||
@@ -121,4 +132,37 @@ impl ElfReader {
|
|||||||
|
|
||||||
section_hdr.ok_or(ElfError::SectionNotExists)
|
section_hdr.ok_or(ElfError::SectionNotExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn search_symbol(&self, address: u64) -> Option<elf::symbol::Symbol> {
|
||||||
|
let entries = self.sorted_syms.iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let idx = entries
|
||||||
|
.as_slice()
|
||||||
|
.binary_search_by_key(&address, |sym| sym.st_value)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
Some(entries[idx].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_symbol_name(
|
||||||
|
&self,
|
||||||
|
sym: elf::symbol::Symbol,
|
||||||
|
) -> Option<&'static str> {
|
||||||
|
self.symbol_strtab.get(sym.st_name as usize).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a slice of the bytes of the kernel ELF file.
|
||||||
|
pub fn get_elf_slice() -> &'static [u8] {
|
||||||
|
let response = KERNEL_FILE_REQUEST
|
||||||
|
.get_response()
|
||||||
|
.expect("Didn't get the kernel file from Limine. That's odd.");
|
||||||
|
|
||||||
|
// We fetch these from Limine and use them to parse our own ELF file.
|
||||||
|
let file = response.file();
|
||||||
|
let file_start_ptr = file.addr();
|
||||||
|
let file_size = file.size() as usize;
|
||||||
|
|
||||||
|
// Safety: This slice should contain the whole bytes of the ELF file.
|
||||||
|
(unsafe { core::slice::from_raw_parts(file_start_ptr, file_size) }) as _
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub mod application;
|
pub mod application;
|
||||||
pub mod ascii;
|
pub mod ascii;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod elf;
|
// pub mod elf;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod maths;
|
pub mod maths;
|
||||||
pub mod unwind;
|
// pub mod unwind;
|
||||||
|
|||||||
@@ -1,23 +1,31 @@
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
use eh_info::ELF;
|
use alloc::vec::Vec;
|
||||||
|
use framehop::{
|
||||||
|
x86_64::{CacheX86_64, UnwinderX86_64},
|
||||||
|
*,
|
||||||
|
};
|
||||||
use spin::{Lazy, Mutex};
|
use spin::{Lazy, Mutex};
|
||||||
use unwinder::{RegisterSet, Unwinder};
|
|
||||||
|
use crate::arch::x86_64::memory::mapping::KERNEL_ADDRESS_REQUEST;
|
||||||
|
|
||||||
pub mod eh_info;
|
pub mod eh_info;
|
||||||
pub mod panic;
|
pub mod panic;
|
||||||
pub mod unwinder;
|
pub mod unwinder;
|
||||||
|
|
||||||
/// We should initialise on program start.
|
/// We should initialise on program start.
|
||||||
pub static UNWINDER: Lazy<Mutex<Unwinder>> = Lazy::new(|| {
|
pub static UNWINDER: Lazy<framehop::x86_64::UnwinderX86_64<Vec<u8>>> =
|
||||||
// Setup stack traces and proper panic handler. TODO: Handle panics
|
Lazy::new(|| {
|
||||||
// differently if not initialised.
|
let mut unwinder: UnwinderX86_64<_, MayAllocateDuringUnwind> =
|
||||||
let eh_frame_ptr = ELF
|
UnwinderX86_64::new();
|
||||||
.get_section_addr(".eh_frame_hdr")
|
|
||||||
.expect("Could not get `.eh_frame_hdr` address.");
|
|
||||||
|
|
||||||
let eh_info = unsafe { eh_info::EhInfo::from_hdr_ptr(eh_frame_ptr) };
|
panic::add_object(
|
||||||
let mut registers = RegisterSet::default();
|
&mut unwinder,
|
||||||
|
KERNEL_ADDRESS_REQUEST
|
||||||
|
.get_response()
|
||||||
|
.unwrap()
|
||||||
|
.virtual_base(),
|
||||||
|
);
|
||||||
|
|
||||||
Mutex::new(Unwinder::new(eh_info, registers))
|
unwinder
|
||||||
});
|
});
|
||||||
|
|||||||
+156
-39
@@ -1,16 +1,25 @@
|
|||||||
//! Defines a simple panic handler which handles stack traces as required.
|
//! Defines a simple panic handler which handles stack traces as required.
|
||||||
|
|
||||||
use core::{arch::asm, panic::PanicInfo};
|
use core::{arch::asm, ops::Range, panic::PanicInfo};
|
||||||
|
|
||||||
use alloc::string::ToString;
|
use alloc::{
|
||||||
use fallible_iterator::FallibleIterator;
|
borrow::{Cow, ToOwned},
|
||||||
use gimli::{Register, X86_64};
|
slice,
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use elf::segment::ProgramHeader;
|
||||||
|
use framehop::x86_64::{CacheX86_64, UnwinderX86_64};
|
||||||
|
use framehop::{Unwinder, x86_64::UnwindRegsX86_64};
|
||||||
|
|
||||||
use super::unwinder::Unwinder;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hcf,
|
arch::x86_64::memory::mapping::KERNEL_ADDRESS_REQUEST,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
std::unwind::{UNWINDER, eh_info::ELF, unwinder::RegisterSet},
|
std::{
|
||||||
|
self,
|
||||||
|
elf::ElfReader,
|
||||||
|
unwind::{UNWINDER, eh_info::ELF},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
@@ -18,37 +27,145 @@ use crate::{
|
|||||||
pub fn panic_handler(info: &PanicInfo<'_>) -> ! {
|
pub fn panic_handler(info: &PanicInfo<'_>) -> ! {
|
||||||
debugln!("Kernel panic: {}", info);
|
debugln!("Kernel panic: {}", info);
|
||||||
|
|
||||||
let mut rip: u64;
|
|
||||||
let mut rsp: u64;
|
|
||||||
unsafe {
|
|
||||||
asm!("lea {0}, [rip]",
|
|
||||||
"mov {1}, rsp",
|
|
||||||
out(reg) rip, out(reg) rsp);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut unwinder = UNWINDER.lock();
|
|
||||||
unwinder.regs.set_pc(rip);
|
|
||||||
unwinder.regs.set_stack_ptr(rsp);
|
|
||||||
|
|
||||||
while let Some(call_frame) = unwinder.next().unwrap_or_else(|err| {
|
|
||||||
// If an unwind error occurred.
|
|
||||||
debugln!("{:?}", err);
|
|
||||||
hcf()
|
|
||||||
}) {
|
|
||||||
serial_println!("Got frame: {:x?}", call_frame);
|
|
||||||
let Some((symtab, strtab)) =
|
|
||||||
ELF.get_symbol_table().unwrap_or_else(|e| {
|
|
||||||
debugln!("{:?}", e);
|
|
||||||
hcf()
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
// TODO: Omit symbol names but just print addresses.
|
|
||||||
debugln!("Didn't find symtab and strtab!");
|
|
||||||
hcf()
|
|
||||||
};
|
|
||||||
|
|
||||||
// let sym_name = symtab.get(call_frame.pc as usize).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::hcf()
|
crate::hcf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use object::{Object, ObjectSection, ObjectSegment};
|
||||||
|
|
||||||
|
use framehop::*;
|
||||||
|
|
||||||
|
pub fn add_object<U>(unwinder: &mut U, base_avma: u64)
|
||||||
|
where
|
||||||
|
U: Unwinder<Module = Module<Vec<u8>>>,
|
||||||
|
{
|
||||||
|
let mut buf = ELF.bytes;
|
||||||
|
|
||||||
|
let file = object::File::parse(buf).expect("Could not parse object file");
|
||||||
|
|
||||||
|
struct Module<'a>(object::File<'a, &'a [u8]>);
|
||||||
|
|
||||||
|
impl ModuleSectionInfo<Vec<u8>> for Module<'_> {
|
||||||
|
fn base_svma(&self) -> u64 {
|
||||||
|
relative_address_base(&self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn section_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
|
||||||
|
let section = self.0.section_by_name_bytes(name)?;
|
||||||
|
Some(section.address()..section.address() + section.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn section_data(&mut self, name: &[u8]) -> Option<Vec<u8>> {
|
||||||
|
match self.0.section_by_name_bytes(name) {
|
||||||
|
Some(section) => {
|
||||||
|
section.data().ok().map(|data| data.to_owned())
|
||||||
|
}
|
||||||
|
None if name == b".debug_frame" => {
|
||||||
|
let section =
|
||||||
|
self.0.section_by_name_bytes(b"__zdebug_frame")?;
|
||||||
|
get_uncompressed_section_data(§ion)
|
||||||
|
.map(|d| d.into_owned())
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn segment_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
|
||||||
|
let segment = self
|
||||||
|
.0
|
||||||
|
.segments()
|
||||||
|
.find(|s| s.name_bytes() == Ok(Some(name)))?;
|
||||||
|
Some(segment.address()..segment.address() + segment.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn segment_data(&mut self, name: &[u8]) -> Option<Vec<u8>> {
|
||||||
|
let segment = self
|
||||||
|
.0
|
||||||
|
.segments()
|
||||||
|
.find(|s| s.name_bytes() == Ok(Some(name)))?;
|
||||||
|
segment.data().ok().map(|data| data.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let module = framehop::Module::new(
|
||||||
|
"Kernel".to_string(),
|
||||||
|
base_avma..(base_avma + buf.len() as u64),
|
||||||
|
base_avma,
|
||||||
|
Module(file),
|
||||||
|
);
|
||||||
|
unwinder.add_module(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_uncompressed_section_data<'a>(
|
||||||
|
section: &impl object::ObjectSection<'a>,
|
||||||
|
) -> Option<Cow<'a, [u8]>> {
|
||||||
|
// let section_data = section.uncompressed_data().ok()?;
|
||||||
|
|
||||||
|
// // Make sure the data is actually decompressed.
|
||||||
|
// if section.name_bytes().ok()?.starts_with(b"__zdebug_")
|
||||||
|
// && section_data.starts_with(b"ZLIB\0\0\0\0")
|
||||||
|
// {
|
||||||
|
// // Object's built-in compressed section handling didn't detect this
|
||||||
|
// as a // compressed section. This happens on Go binaries which use
|
||||||
|
// compressed // sections like __zdebug_ranges, which is generally
|
||||||
|
// uncommon on macOS, // so object's mach-O parser doesn't handle
|
||||||
|
// them. // But we want to handle them.
|
||||||
|
// // Go stopped using zdebug sections for ELF files in https://github.com/golang/go/issues/50796
|
||||||
|
// // but still uses them for mach-O builds.
|
||||||
|
// let b = section_data.get(8..12)?;
|
||||||
|
// let uncompressed_size = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
|
||||||
|
// let compressed_bytes = §ion_data[12..];
|
||||||
|
|
||||||
|
// let mut decompressed = Vec::with_capacity(uncompressed_size as
|
||||||
|
// usize); let mut decompress = flate2::Decompress::new(true);
|
||||||
|
// decompress
|
||||||
|
// .decompress_vec(
|
||||||
|
// compressed_bytes,
|
||||||
|
// &mut decompressed,
|
||||||
|
// flate2::FlushDecompress::Finish,
|
||||||
|
// )
|
||||||
|
// .ok()?;
|
||||||
|
// Some(Cow::Owned(decompressed))
|
||||||
|
// } else {
|
||||||
|
// Some(section_data)
|
||||||
|
// }
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Relative addresses are u32 offsets which are relative to some "base
|
||||||
|
/// address".
|
||||||
|
///
|
||||||
|
/// This function computes that base address. It is defined as follows:
|
||||||
|
///
|
||||||
|
/// - For Windows binaries, the base address is the "image base address".
|
||||||
|
/// - For mach-O binaries, the base address is the vmaddr of the __TEXT
|
||||||
|
/// segment.
|
||||||
|
/// - For ELF binaries, the base address is zero.
|
||||||
|
///
|
||||||
|
/// Stand-alone mach-O dylibs usually have a base address of zero because their
|
||||||
|
/// __TEXT segment is at address zero.
|
||||||
|
///
|
||||||
|
/// In the following cases, the base address is usually non-zero:
|
||||||
|
///
|
||||||
|
/// - The "image base address" of Windows binaries is usually non-zero.
|
||||||
|
/// - mach-O executable files (not dylibs) usually have their __TEXT segment at
|
||||||
|
/// address 0x100000000.
|
||||||
|
/// - mach-O libraries in the dyld shared cache have a __TEXT segment at some
|
||||||
|
/// non-zero address in the cache.
|
||||||
|
pub fn relative_address_base<'data>(
|
||||||
|
object_file: &impl object::Object<'data>,
|
||||||
|
) -> u64 {
|
||||||
|
if let Some(text_segment) = object_file
|
||||||
|
.segments()
|
||||||
|
.find(|s| s.name() == Ok(Some("__TEXT")))
|
||||||
|
{
|
||||||
|
// This is a mach-O image. "Relative addresses" are relative to the
|
||||||
|
// vmaddr of the __TEXT segment.
|
||||||
|
return text_segment.address();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For PE binaries, relative_address_base() returns the image base address.
|
||||||
|
// Otherwise it returns zero. This gives regular ELF images a base address
|
||||||
|
// of zero, which is what we want.
|
||||||
|
object_file.relative_address_base()
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ impl FallibleIterator for Unwinder {
|
|||||||
let Some(pc) = self.regs.get_pc() else {
|
let Some(pc) = self.regs.get_pc() else {
|
||||||
return Err(UnwinderError::NoPcRegister);
|
return Err(UnwinderError::NoPcRegister);
|
||||||
};
|
};
|
||||||
|
// 0xffffffff8000014b b - 7 = 11 - 7 = 4
|
||||||
|
|
||||||
if self.is_first {
|
if self.is_first {
|
||||||
self.is_first = false;
|
self.is_first = false;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Set up test-specific flags
|
# Set up test-specific flags
|
||||||
if [ $is_test -eq 1 ]; then
|
if [$is_test]; then
|
||||||
test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"
|
test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"
|
||||||
serial_flags="-serial stdio"
|
serial_flags="-serial stdio"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user