arch: aarch64 fp context save, proper single-step
This commit is contained in:
parent
0daf7c677c
commit
0436381b33
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -838,6 +838,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"aarch64-cpu",
|
||||
"bitflags 2.6.0",
|
||||
"cc",
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
|
@ -6,7 +6,7 @@
|
||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||
"max-atomic-width": 128,
|
||||
"target-pointer-width": "64",
|
||||
"features": "+v8a,+strict-align,+neon,+fp-armv8",
|
||||
"features": "+v8a,+strict-align,-neon,-fp-armv8",
|
||||
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
|
@ -14,3 +14,6 @@ bitflags = "2.6.0"
|
||||
static_assertions = "1.1.0"
|
||||
aarch64-cpu = "9.4.0"
|
||||
tock-registers = "0.8.1"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
21
kernel/arch/aarch64/build.rs
Normal file
21
kernel/arch/aarch64/build.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use std::env;
|
||||
|
||||
fn build_fp_context_obj() {
|
||||
const FP_CONTEXT_S: &str = "src/fp_context.S";
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed={}", FP_CONTEXT_S);
|
||||
|
||||
cc::Build::new()
|
||||
.out_dir(&out_dir)
|
||||
.compiler("clang")
|
||||
.target("aarch64-unknown-none")
|
||||
.flag("-march=armv8-a+fp")
|
||||
.file(FP_CONTEXT_S)
|
||||
.compile("fp_context");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
build_fp_context_obj();
|
||||
}
|
@ -52,9 +52,10 @@ __aarch64_task_enter_kernel:
|
||||
eret
|
||||
|
||||
__aarch64_task_enter_user:
|
||||
// x0 == sp, x1 == ignored
|
||||
// x0 == sp, x1 == mdscr_el1
|
||||
ldp x0, x1, [sp, #16 * 0]
|
||||
msr sp_el0, x0
|
||||
msr mdscr_el1, x1
|
||||
|
||||
# EL0t, IRQs unmasked
|
||||
msr spsr_el1, xzr
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! AArch64-specific task context implementation
|
||||
use core::{arch::global_asm, cell::UnsafeCell, fmt, marker::PhantomData};
|
||||
use core::{arch::global_asm, cell::UnsafeCell, ffi::c_void, fmt, marker::PhantomData};
|
||||
|
||||
use kernel_arch_interface::{
|
||||
mem::{KernelTableManager, PhysicalMemoryAllocator},
|
||||
@ -29,6 +29,12 @@ struct TaskContextInner {
|
||||
sp: usize,
|
||||
}
|
||||
|
||||
#[repr(align(0x10))]
|
||||
pub struct FpContext {
|
||||
// q0..q31 x 128bit + fpcr/fpsr
|
||||
inner: [u8; 16 * 32 + 16],
|
||||
}
|
||||
|
||||
/// AArch64 implementation of a task context
|
||||
#[allow(unused)]
|
||||
pub struct TaskContextImpl<
|
||||
@ -36,6 +42,7 @@ pub struct TaskContextImpl<
|
||||
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
|
||||
> {
|
||||
inner: UnsafeCell<TaskContextInner>,
|
||||
fp_context: UnsafeCell<FpContext>,
|
||||
stack_base_phys: PhysicalAddress,
|
||||
stack_size: usize,
|
||||
|
||||
@ -45,6 +52,22 @@ pub struct TaskContextImpl<
|
||||
|
||||
const COMMON_CONTEXT_SIZE: usize = 8 * 14;
|
||||
|
||||
impl FpContext {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: [0; 16 * 32 + 16],
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn store(this: *mut Self) {
|
||||
__aarch64_fp_store_context(this as _)
|
||||
}
|
||||
|
||||
pub unsafe fn restore(this: *const Self) {
|
||||
__aarch64_fp_restore_context(this as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskFrame for ExceptionFrame {
|
||||
fn store(&self) -> SavedFrame {
|
||||
SavedFrame {
|
||||
@ -151,6 +174,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
Ok(Self {
|
||||
inner: UnsafeCell::new(TaskContextInner { sp }),
|
||||
fp_context: UnsafeCell::new(FpContext::new()),
|
||||
stack_base_phys,
|
||||
stack_size: KERNEL_TASK_PAGES * 0x1000,
|
||||
|
||||
@ -166,9 +190,11 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
|
||||
|
||||
let mdscr_el1 = if context.single_step { 1 << 0 } else { 0 };
|
||||
|
||||
stack.push(context.entry);
|
||||
stack.push(context.argument);
|
||||
stack.push(0);
|
||||
stack.push(mdscr_el1);
|
||||
stack.push(context.stack_pointer);
|
||||
|
||||
setup_common_context(
|
||||
@ -182,6 +208,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
Ok(Self {
|
||||
inner: UnsafeCell::new(TaskContextInner { sp }),
|
||||
fp_context: UnsafeCell::new(FpContext::new()),
|
||||
stack_base_phys,
|
||||
stack_size: USER_TASK_PAGES * 0x1000,
|
||||
|
||||
@ -191,14 +218,28 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
}
|
||||
|
||||
unsafe fn enter(&self) -> ! {
|
||||
FpContext::restore(self.fp_context.get());
|
||||
|
||||
__aarch64_enter_task(self.inner.get())
|
||||
}
|
||||
|
||||
unsafe fn switch(&self, from: &Self) {
|
||||
__aarch64_switch_task(self.inner.get(), from.inner.get())
|
||||
let dst = self.inner.get();
|
||||
let src = from.inner.get();
|
||||
|
||||
if dst != src {
|
||||
// Save the old context
|
||||
FpContext::store(from.fp_context.get());
|
||||
// Load next context
|
||||
FpContext::restore(self.fp_context.get());
|
||||
|
||||
__aarch64_switch_task(self.inner.get(), from.inner.get())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn switch_and_drop(&self, thread: *const ()) {
|
||||
FpContext::restore(self.fp_context.get());
|
||||
|
||||
__aarch64_switch_task_and_drop(self.inner.get(), thread);
|
||||
}
|
||||
}
|
||||
@ -241,6 +282,8 @@ extern "C" {
|
||||
fn __aarch64_switch_task_and_drop(to: *mut TaskContextInner, thread: *const ()) -> !;
|
||||
fn __aarch64_task_enter_kernel();
|
||||
fn __aarch64_task_enter_user();
|
||||
fn __aarch64_fp_store_context(to: *mut c_void);
|
||||
fn __aarch64_fp_restore_context(from: *const c_void);
|
||||
}
|
||||
|
||||
global_asm!(include_str!("context.S"), context_size = const COMMON_CONTEXT_SIZE);
|
||||
|
52
kernel/arch/aarch64/src/fp_context.S
Normal file
52
kernel/arch/aarch64/src/fp_context.S
Normal file
@ -0,0 +1,52 @@
|
||||
.section .text
|
||||
.global __aarch64_fp_store_context
|
||||
.global __aarch64_fp_restore_context
|
||||
|
||||
__aarch64_fp_store_context:
|
||||
// x0 - destination
|
||||
stp q0, q1, [x0, #16 * 0 ]
|
||||
stp q2, q3, [x0, #16 * 2 ]
|
||||
stp q4, q5, [x0, #16 * 4 ]
|
||||
stp q6, q7, [x0, #16 * 6 ]
|
||||
stp q8, q9, [x0, #16 * 8 ]
|
||||
stp q10, q11, [x0, #16 * 10]
|
||||
stp q12, q13, [x0, #16 * 12]
|
||||
stp q14, q15, [x0, #16 * 14]
|
||||
stp q16, q17, [x0, #16 * 16]
|
||||
stp q18, q19, [x0, #16 * 18]
|
||||
stp q20, q21, [x0, #16 * 20]
|
||||
stp q22, q23, [x0, #16 * 22]
|
||||
stp q24, q25, [x0, #16 * 24]
|
||||
stp q26, q27, [x0, #16 * 26]
|
||||
stp q28, q29, [x0, #16 * 28]
|
||||
stp q30, q31, [x0, #16 * 30]!
|
||||
mrs x1, fpsr
|
||||
str x1, [x0, #16 * 2]
|
||||
mrs x1, fpcr
|
||||
str x1, [x0, #16 * 2 + 8]
|
||||
|
||||
ret
|
||||
|
||||
__aarch64_fp_restore_context:
|
||||
// x0 - source
|
||||
ldp q0, q1, [x0, #16 * 0 ]
|
||||
ldp q2, q3, [x0, #16 * 2 ]
|
||||
ldp q4, q5, [x0, #16 * 4 ]
|
||||
ldp q6, q7, [x0, #16 * 6 ]
|
||||
ldp q8, q9, [x0, #16 * 8 ]
|
||||
ldp q10, q11, [x0, #16 * 10]
|
||||
ldp q12, q13, [x0, #16 * 12]
|
||||
ldp q14, q15, [x0, #16 * 14]
|
||||
ldp q16, q17, [x0, #16 * 16]
|
||||
ldp q18, q19, [x0, #16 * 18]
|
||||
ldp q20, q21, [x0, #16 * 20]
|
||||
ldp q22, q23, [x0, #16 * 22]
|
||||
ldp q24, q25, [x0, #16 * 24]
|
||||
ldp q26, q27, [x0, #16 * 26]
|
||||
ldp q28, q29, [x0, #16 * 28]
|
||||
ldp q30, q31, [x0, #16 * 30]!
|
||||
ldp x0, x1, [x0, #16 * 2]
|
||||
msr fpsr, x0
|
||||
msr fpcr, x1
|
||||
|
||||
ret
|
@ -50,6 +50,9 @@ impl ArchitectureImpl {
|
||||
impl Architecture for ArchitectureImpl {
|
||||
type PerCpuData = PerCpuData;
|
||||
type CpuFeatures = ();
|
||||
type BreakpointType = u32;
|
||||
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType = 0x200020D4;
|
||||
|
||||
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||
(MPIDR_EL1.get() & 0xFF) as u32
|
||||
|
@ -101,8 +101,13 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
let stack_base = stack_base_phys.raw_virtualize::<K>();
|
||||
|
||||
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
|
||||
let mut flags = 0x200;
|
||||
|
||||
stack.push(0x200);
|
||||
if context.single_step {
|
||||
flags |= 1 << 8;
|
||||
}
|
||||
|
||||
stack.push(flags);
|
||||
stack.push(context.entry as _);
|
||||
stack.push(context.stack_pointer);
|
||||
|
||||
@ -372,7 +377,7 @@ impl TaskFrame for ExceptionFrame {
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
todo!()
|
||||
self.eip as _
|
||||
}
|
||||
|
||||
fn argument(&self) -> u64 {
|
||||
|
@ -48,6 +48,9 @@ extern "C" fn idle_task(_: usize) -> ! {
|
||||
impl Architecture for ArchitectureImpl {
|
||||
type PerCpuData = PerCpuData;
|
||||
type CpuFeatures = CpuFeatures;
|
||||
type BreakpointType = u8;
|
||||
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
|
||||
|
||||
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
|
||||
use alloc::boxed::Box;
|
||||
|
@ -26,6 +26,9 @@ pub trait Architecture: Sized {
|
||||
type PerCpuData;
|
||||
type CpuFeatures: CpuFeatureSet;
|
||||
|
||||
type BreakpointType;
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType;
|
||||
|
||||
// Cpu management
|
||||
|
||||
/// # Safety
|
||||
|
@ -443,13 +443,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
})
|
||||
}
|
||||
|
||||
fn user(
|
||||
context: UserContextInfo, // entry: usize,
|
||||
// arg: usize,
|
||||
// cr3: u64,
|
||||
// user_stack_sp: usize,
|
||||
// fs_base: usize,
|
||||
) -> Result<Self, Error> {
|
||||
fn user(context: UserContextInfo) -> Result<Self, Error> {
|
||||
const USER_TASK_PAGES: usize = 8;
|
||||
|
||||
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
|
||||
@ -457,7 +451,12 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
|
||||
|
||||
stack.push(0x200);
|
||||
let mut flags = 0x200;
|
||||
if context.single_step {
|
||||
flags |= 1 << 8;
|
||||
}
|
||||
|
||||
stack.push(flags);
|
||||
stack.push(context.entry as _);
|
||||
stack.push(context.argument);
|
||||
stack.push(context.stack_pointer);
|
||||
|
@ -89,6 +89,9 @@ impl ArchitectureImpl {
|
||||
impl Architecture for ArchitectureImpl {
|
||||
type PerCpuData = PerCpuData;
|
||||
type CpuFeatures = CpuFeatures;
|
||||
type BreakpointType = u8;
|
||||
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
|
||||
|
||||
unsafe fn set_local_cpu(cpu: *mut ()) {
|
||||
MSR_IA32_KERNEL_GS_BASE.set(cpu as u64);
|
||||
|
@ -37,6 +37,15 @@ pub mod elf;
|
||||
|
||||
pub type LoadedProcess = (Arc<Process>, Arc<Thread>);
|
||||
|
||||
pub struct LoadOptions<'e, P: AsRef<Path>> {
|
||||
pub parent: Option<Weak<Process>>,
|
||||
pub group_id: ProcessGroupId,
|
||||
pub path: P,
|
||||
pub args: &'e [&'e str],
|
||||
pub envs: &'e [&'e str],
|
||||
pub single_step: bool,
|
||||
}
|
||||
|
||||
struct BufferPlacer<'a> {
|
||||
buffer: &'a mut [u8],
|
||||
virtual_offset: usize,
|
||||
@ -122,12 +131,16 @@ fn setup_program_env(
|
||||
Ok(in_user as *const _ as usize)
|
||||
}
|
||||
|
||||
fn setup_context(
|
||||
fn setup_context<P>(
|
||||
options: &LoadOptions<P>,
|
||||
space: &ProcessAddressSpace,
|
||||
image: &ProcessImage,
|
||||
args: &Vec<String>,
|
||||
envs: &Vec<String>,
|
||||
) -> Result<TaskContextImpl, Error> {
|
||||
) -> Result<TaskContextImpl, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
const USER_STACK_PAGES: usize = 32;
|
||||
|
||||
let virt_stack_base = 0x3000000;
|
||||
@ -174,14 +187,13 @@ fn setup_context(
|
||||
stack_pointer: ptr.addr(),
|
||||
tls: tls_address,
|
||||
address_space: space.as_address_with_asid(),
|
||||
single_step: false,
|
||||
single_step: options.single_step,
|
||||
})
|
||||
}
|
||||
|
||||
fn setup_binary<S>(
|
||||
fn setup_binary<S, P>(
|
||||
options: &LoadOptions<P>,
|
||||
name: S,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<Process>>,
|
||||
space: ProcessAddressSpace,
|
||||
image: ProcessImage,
|
||||
args: &Vec<String>,
|
||||
@ -189,12 +201,13 @@ fn setup_binary<S>(
|
||||
) -> Result<LoadedProcess, Error>
|
||||
where
|
||||
S: Into<String>,
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let context = setup_context(&space, &image, args, envs)?;
|
||||
let context = setup_context(options, &space, &image, args, envs)?;
|
||||
let (process, main) = Process::new_with_main(
|
||||
name,
|
||||
group_id,
|
||||
parent,
|
||||
options.group_id,
|
||||
options.parent.clone(),
|
||||
Arc::new(space),
|
||||
context,
|
||||
Some(image),
|
||||
@ -263,24 +276,16 @@ fn xxx_load_program<P: AsRef<Path>>(
|
||||
} else {
|
||||
Err(Error::UnrecognizedExecutable)
|
||||
}
|
||||
|
||||
// let image = load_binary(head, file, space)?;
|
||||
|
||||
// Ok((image, args, envs))
|
||||
}
|
||||
|
||||
/// Loads a program from given `path`
|
||||
pub fn load<P: AsRef<Path>>(
|
||||
ioctx: &mut IoContext,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<Process>>,
|
||||
path: P,
|
||||
args: &[&str],
|
||||
envs: &[&str],
|
||||
options: &LoadOptions<P>,
|
||||
) -> Result<LoadedProcess, Error> {
|
||||
let path = path.as_ref();
|
||||
let args = args.iter().map(|&s| s.to_owned()).collect();
|
||||
let envs = envs.iter().map(|&s| s.to_owned()).collect();
|
||||
let path = options.path.as_ref();
|
||||
let args = options.args.iter().map(|&s| s.to_owned()).collect();
|
||||
let envs = options.envs.iter().map(|&s| s.to_owned()).collect();
|
||||
|
||||
let space = ProcessAddressSpace::new()?;
|
||||
let (image, args, envs) = xxx_load_program(&space, ioctx, path, args, envs)?;
|
||||
@ -289,22 +294,22 @@ pub fn load<P: AsRef<Path>>(
|
||||
Some((_, name)) => name,
|
||||
None => name,
|
||||
};
|
||||
setup_binary(name, group_id, parent, space, image, &args, &envs)
|
||||
setup_binary(options, name, space, image, &args, &envs)
|
||||
}
|
||||
|
||||
pub fn load_into<P: AsRef<Path>>(
|
||||
process: &Process,
|
||||
path: P,
|
||||
options: &LoadOptions<P>,
|
||||
args: Vec<String>,
|
||||
envs: Vec<String>,
|
||||
) -> Result<(TaskContextImpl, ProcessImage), Error> {
|
||||
let process = options.parent.as_ref().unwrap().upgrade().unwrap();
|
||||
let mut io = process.io.lock();
|
||||
// Have to make the Path owned, going to drop the address space from which it came
|
||||
let path = path.as_ref().to_owned();
|
||||
let path = options.path.as_ref().to_owned();
|
||||
let space = process.space();
|
||||
space.clear()?;
|
||||
let (image, args, envs) = xxx_load_program(&space, io.ioctx_mut(), &path, args, envs)?;
|
||||
let context = setup_context(&space, &image, &args, &envs)?;
|
||||
let context = setup_context(options, &space, &image, &args, &envs)?;
|
||||
|
||||
Ok((context, image))
|
||||
}
|
||||
|
@ -27,6 +27,17 @@ pub trait ForeignPointer: Sized {
|
||||
/// As this function allows direct memory writes, it is inherently unsafe.
|
||||
unsafe fn write_foreign_volatile(self: *mut Self, space: &ProcessAddressSpace, value: Self);
|
||||
|
||||
unsafe fn try_write_foreign_volatile(
|
||||
self: *mut Self,
|
||||
space: &ProcessAddressSpace,
|
||||
value: Self,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
unsafe fn try_read_foreign_volatile(
|
||||
self: *const Self,
|
||||
space: &ProcessAddressSpace,
|
||||
) -> Result<Self, Error>;
|
||||
|
||||
/// Performs pointer validation for given address space:
|
||||
///
|
||||
/// * Checks if the pointer has proper alignment for the type.
|
||||
@ -80,6 +91,15 @@ pub trait ForeignPointer: Sized {
|
||||
|
||||
impl<T> ForeignPointer for T {
|
||||
unsafe fn write_foreign_volatile(self: *mut Self, space: &ProcessAddressSpace, value: T) {
|
||||
self.try_write_foreign_volatile(space, value)
|
||||
.expect("Invalid foreign pointer, could not write")
|
||||
}
|
||||
|
||||
unsafe fn try_write_foreign_volatile(
|
||||
self: *mut Self,
|
||||
space: &ProcessAddressSpace,
|
||||
value: Self,
|
||||
) -> Result<(), Error> {
|
||||
// TODO check align
|
||||
let addr = self as usize;
|
||||
let start_page = addr & !0xFFF;
|
||||
@ -90,12 +110,30 @@ impl<T> ForeignPointer for T {
|
||||
todo!("Foreign pointer write crossed a page boundary");
|
||||
}
|
||||
|
||||
let phys_page = space
|
||||
.translate(start_page)
|
||||
.expect("Address is not mapped in the target address space");
|
||||
let phys_page = space.translate(start_page)?;
|
||||
|
||||
let virt_ptr = phys_page.add(page_offset).virtualize() as *mut T;
|
||||
virt_ptr.write_volatile(value);
|
||||
virt_ptr.write_unaligned(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn try_read_foreign_volatile(
|
||||
self: *const Self,
|
||||
space: &ProcessAddressSpace,
|
||||
) -> Result<Self, Error> {
|
||||
let addr = self as usize;
|
||||
let start_page = addr & !0xFFF;
|
||||
let end_page = (addr + size_of::<T>() - 1) & !0xFFF;
|
||||
let page_offset = addr & 0xFFF;
|
||||
|
||||
if start_page != end_page {
|
||||
todo!("Foreign pointer write crossed a page boundary");
|
||||
}
|
||||
|
||||
let phys_page = space.translate(start_page)?;
|
||||
let virt_ptr = phys_page.add(page_offset).virtualize() as *const Self;
|
||||
|
||||
Ok(virt_ptr.read_unaligned())
|
||||
}
|
||||
|
||||
unsafe fn validate_user_slice_mut<'a>(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::{cell::Cell, mem::size_of, ops::Deref};
|
||||
|
||||
use alloc::{
|
||||
collections::BTreeMap,
|
||||
collections::{btree_map, BTreeMap, BTreeSet},
|
||||
string::String,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
@ -9,7 +9,7 @@ use crossbeam_queue::SegQueue;
|
||||
use futures_util::task::ArcWake;
|
||||
use kernel_arch::{
|
||||
task::{Scheduler, TaskContext, TaskFrame},
|
||||
CpuImpl,
|
||||
Architecture, ArchitectureImpl, CpuImpl,
|
||||
};
|
||||
use libk_mm::process::ProcessAddressSpace;
|
||||
use libk_util::{
|
||||
@ -24,7 +24,7 @@ use yggdrasil_abi::{
|
||||
};
|
||||
|
||||
use crate::task::{
|
||||
mem::ForeignPointer,
|
||||
mem::{self, ForeignPointer},
|
||||
sched::CpuQueue,
|
||||
types::{ThreadAffinity, ThreadId, ThreadState},
|
||||
TaskContextImpl,
|
||||
@ -32,6 +32,9 @@ use crate::task::{
|
||||
|
||||
use super::{debug::ThreadDebugger, process::Process};
|
||||
|
||||
type BreakpointType = <ArchitectureImpl as Architecture>::BreakpointType;
|
||||
const BREAKPOINT_VALUE: BreakpointType = ArchitectureImpl::BREAKPOINT_VALUE;
|
||||
|
||||
/// Provides details about how the thread is scheduled onto CPUs
|
||||
pub struct ThreadSchedulingInfo {
|
||||
/// Current state
|
||||
@ -45,8 +48,10 @@ pub struct ThreadSchedulingInfo {
|
||||
|
||||
pub struct ThreadDebuggingInfo {
|
||||
pub single_step: bool,
|
||||
pub restore_breakpoint: Option<usize>,
|
||||
pub debugger: Option<ThreadDebugger>,
|
||||
pub saved_frame: Option<SavedFrame>,
|
||||
pub breakpoints: BTreeMap<usize, BreakpointType>,
|
||||
}
|
||||
|
||||
struct SignalEntry {
|
||||
@ -115,10 +120,13 @@ impl Thread {
|
||||
in_queue: false,
|
||||
queue: None,
|
||||
}),
|
||||
// TODO lazy initialization for debugging info
|
||||
debug: IrqSafeSpinlock::new(ThreadDebuggingInfo {
|
||||
single_step: false,
|
||||
restore_breakpoint: None,
|
||||
debugger: None,
|
||||
saved_frame: None,
|
||||
breakpoints: BTreeMap::new(),
|
||||
}),
|
||||
context: Cell::new(context),
|
||||
process,
|
||||
@ -243,6 +251,46 @@ impl Thread {
|
||||
debug.debugger = Some(debugger);
|
||||
|
||||
self.signal_queue.push(Signal::Debug);
|
||||
|
||||
let frame = self
|
||||
.process()
|
||||
.map_image(|img| DebugFrame::Startup {
|
||||
image_base: img.load_base,
|
||||
ip_offset: img.ip_offset,
|
||||
ip: img.entry,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
debug.debugger.as_mut().unwrap().send(&frame).ok();
|
||||
}
|
||||
|
||||
pub fn set_breakpoint(&self, address: usize) -> Result<(), Error> {
|
||||
log::debug!(
|
||||
"Set breakpoint in {} ({:?}) @ {:#x}",
|
||||
self.id,
|
||||
self.name,
|
||||
address
|
||||
);
|
||||
let mut debug = self.debug.lock();
|
||||
debug.set_breakpoint_inner(self.address_space(), address)
|
||||
}
|
||||
|
||||
pub fn read_memory(&self, address: usize, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
log::debug!(
|
||||
"Read memory in {} ({:?}) @ {:#x}",
|
||||
self.id,
|
||||
self.name,
|
||||
address
|
||||
);
|
||||
|
||||
let space = self.address_space();
|
||||
|
||||
// TODO optimize this later
|
||||
for i in 0..buffer.len() {
|
||||
buffer[i] = unsafe { ((address + i) as *const u8).try_read_foreign_volatile(space) }?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resume(&self, single_step: bool) {
|
||||
@ -453,23 +501,77 @@ impl CurrentThread {
|
||||
pub fn handle_single_step<F: TaskFrame>(&self, frame: &mut F) {
|
||||
{
|
||||
let mut debug = self.debug.lock();
|
||||
let space = self.address_space();
|
||||
|
||||
// Single step cleared
|
||||
if !debug.single_step {
|
||||
log::debug!("Clear single step ({} {:?})", self.id, self.name);
|
||||
frame.set_single_step(false);
|
||||
return;
|
||||
if let Some(original) = debug.restore_breakpoint.take() {
|
||||
let brk_range = original..original + size_of::<BreakpointType>();
|
||||
assert!(!brk_range.contains(&frame.user_ip()));
|
||||
|
||||
log::debug!(
|
||||
"Restore breakpoint, current_ip={:#x}, breakpoint_ip={:#x}",
|
||||
frame.user_ip(),
|
||||
original
|
||||
);
|
||||
debug.set_breakpoint_inner(space, original).unwrap();
|
||||
} else {
|
||||
// Single step cleared
|
||||
if !debug.single_step {
|
||||
log::debug!("Clear single step ({} {:?})", self.id, self.name);
|
||||
frame.set_single_step(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let frame = frame.store();
|
||||
debug.saved_frame = Some(frame.clone());
|
||||
// TODO handle cases of detached debugger
|
||||
let debugger = debug.debugger.as_ref().unwrap();
|
||||
|
||||
debugger.send(&DebugFrame::Step { frame }).ok();
|
||||
}
|
||||
|
||||
self.suspend().unwrap();
|
||||
match self.suspend() {
|
||||
Ok(_) | Err(Error::Interrupted) => (),
|
||||
Err(err) => panic!("TODO: handle error in debug suspend: {:?}", err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_breakpoint<F: TaskFrame>(&self, frame: &mut F) -> bool {
|
||||
let mut debug = self.debug.lock();
|
||||
let ip = frame.user_ip();
|
||||
|
||||
if let Some(value) = debug.breakpoints.remove(&ip) {
|
||||
let space = self.address_space();
|
||||
|
||||
// Restore original code
|
||||
let pointer = ip as *mut BreakpointType;
|
||||
unsafe { pointer.write_foreign_volatile(space, value) };
|
||||
|
||||
log::debug!(
|
||||
"Thread {} ({:?}) hit a breakpoint @ {:#x}, step={}",
|
||||
self.id,
|
||||
self.name,
|
||||
ip,
|
||||
debug.single_step
|
||||
);
|
||||
|
||||
// TODO handle cases when no debugger is attached (clear breakpoint and resume)
|
||||
frame.set_single_step(true);
|
||||
|
||||
let frame = frame.store();
|
||||
debug.restore_breakpoint = Some(ip);
|
||||
debug.saved_frame = Some(frame.clone());
|
||||
|
||||
let debugger = debug.debugger.as_ref().unwrap();
|
||||
debugger.send(&DebugFrame::HitBreakpoint { frame }).ok();
|
||||
|
||||
drop(debug);
|
||||
|
||||
self.suspend().unwrap();
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up a return frame to handle a pending signal, if any is present in the task's queue.
|
||||
@ -485,26 +587,18 @@ impl CurrentThread {
|
||||
|
||||
if let Some(signal) = self.signal_queue.pop() {
|
||||
if signal == Signal::Debug {
|
||||
log::info!("Entered debug signal");
|
||||
frame.set_single_step(true);
|
||||
|
||||
let frame = frame.store();
|
||||
let mut debug = self.debug.lock();
|
||||
|
||||
debug.single_step = true;
|
||||
debug.saved_frame = Some(frame.clone());
|
||||
|
||||
let debugger = debug.debugger.as_ref().unwrap();
|
||||
let process = self.process();
|
||||
|
||||
frame.set_single_step(true);
|
||||
|
||||
// Send initial frame
|
||||
let saved_frame = frame.store();
|
||||
let (image_base, ip_offset) = process
|
||||
.map_image(|img| (img.load_base, img.ip_offset))
|
||||
.unwrap();
|
||||
debugger
|
||||
.send(&DebugFrame::Startup {
|
||||
image_base,
|
||||
ip_offset,
|
||||
frame: saved_frame,
|
||||
})
|
||||
.ok();
|
||||
debugger.send(&DebugFrame::Step { frame }).ok();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -558,3 +652,28 @@ impl Deref for CurrentThread {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadDebuggingInfo {
|
||||
fn set_breakpoint_inner(
|
||||
&mut self,
|
||||
space: &ProcessAddressSpace,
|
||||
address: usize,
|
||||
) -> Result<(), Error> {
|
||||
match self.breakpoints.entry(address) {
|
||||
btree_map::Entry::Vacant(vacant) => {
|
||||
let pointer = address as *mut BreakpointType;
|
||||
|
||||
// Read old code from the address space at that location
|
||||
let original =
|
||||
unsafe { (pointer as *const BreakpointType).try_read_foreign_volatile(space) }?;
|
||||
|
||||
unsafe { pointer.write_foreign_volatile(space, BREAKPOINT_VALUE) };
|
||||
|
||||
vacant.insert(original);
|
||||
Ok(())
|
||||
}
|
||||
// No need, breakpoint already present
|
||||
btree_map::Entry::Occupied(_) => Err(Error::AlreadyExists),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ pub trait TerminalOutput: Sync {
|
||||
|
||||
fn size(&self) -> TerminalSize {
|
||||
TerminalSize {
|
||||
rows: 80,
|
||||
columns: 24,
|
||||
rows: 24,
|
||||
columns: 80,
|
||||
}
|
||||
}
|
||||
fn set_size(&self, size: TerminalSize) -> Result<(), Error> {
|
||||
|
1
kernel/modules/test_mod/Cargo.lock
generated
1
kernel/modules/test_mod/Cargo.lock
generated
@ -237,6 +237,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"aarch64-cpu",
|
||||
"bitflags",
|
||||
"cc",
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
|
@ -149,6 +149,16 @@ impl Entry {
|
||||
|
||||
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
|
||||
|
||||
fn dump_user_exception(kind: ExceptionKind, frame: &ExceptionFrame) {
|
||||
let thread = Thread::current();
|
||||
warnln!("{:?} in {} ({:?})", kind, thread.id, thread.name);
|
||||
warnln!("ip = {:02x}:{:08x}", frame.cs, frame.eip);
|
||||
warnln!("cr3 = {:#010x}", CR3.get());
|
||||
if kind == ExceptionKind::PageFault {
|
||||
warnln!("cr2 = {:#010x}", CR2.get());
|
||||
}
|
||||
}
|
||||
|
||||
fn kernel_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) -> ! {
|
||||
let cr3 = CR3.get();
|
||||
|
||||
@ -165,35 +175,36 @@ fn kernel_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) -> ! {
|
||||
fn user_exception_inner(kind: ExceptionKind, frame: &mut ExceptionFrame) {
|
||||
let thread = Thread::current();
|
||||
|
||||
if kind != ExceptionKind::Debug {
|
||||
let cr3 = CR3.get();
|
||||
|
||||
warnln!("{:?} in {} {:?}", kind, thread.id, thread.name);
|
||||
warnln!("CS:EIP = {:#02x}:{:#010x}", frame.cs, frame.eip);
|
||||
warnln!("CR3 = {:#x}", cr3);
|
||||
}
|
||||
|
||||
match kind {
|
||||
let dump = match kind {
|
||||
ExceptionKind::PageFault => {
|
||||
let cr2 = CR2.get();
|
||||
warnln!("CR2 = {:#x}", cr2);
|
||||
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
true
|
||||
}
|
||||
ExceptionKind::GeneralProtectionFault => {
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
if thread.handle_breakpoint(frame) {
|
||||
false
|
||||
} else {
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
true
|
||||
}
|
||||
}
|
||||
ExceptionKind::InvalidOpcode => {
|
||||
thread.raise_signal(Signal::Aborted);
|
||||
true
|
||||
}
|
||||
ExceptionKind::Debug => {
|
||||
// TODO check if the thread was really in single-step mode or has debugging related to
|
||||
// the address in exception description
|
||||
thread.handle_single_step(frame);
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
if dump {
|
||||
dump_user_exception(kind, frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,45 +113,54 @@ impl Entry {
|
||||
|
||||
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
|
||||
|
||||
fn dump_user_exception(kind: ExceptionKind, frame: &ExceptionFrame) {
|
||||
let thread = Thread::current();
|
||||
warnln!("{:?} in {} ({:?})", kind, thread.id, thread.name);
|
||||
warnln!("ip = {:02x}:{:08x}", frame.cs, frame.rip);
|
||||
warnln!("cr3 = {:#010x}", CR3.get());
|
||||
if kind == ExceptionKind::PageFault {
|
||||
warnln!("cr2 = {:#010x}", CR2.get());
|
||||
}
|
||||
}
|
||||
|
||||
fn user_exception_inner(kind: ExceptionKind, frame: &mut ExceptionFrame) {
|
||||
let thread = Thread::current();
|
||||
let cr3 = CR3.get();
|
||||
|
||||
if kind != ExceptionKind::Debug {
|
||||
warnln!("{:?} in {} {:?}", kind, thread.id, thread.name);
|
||||
// XXX
|
||||
// frame.dump(debug::LogLevel::Warning);
|
||||
warnln!("CR3 = {:#x}", cr3);
|
||||
}
|
||||
|
||||
log::warn!("{:#x?}", frame);
|
||||
|
||||
match kind {
|
||||
let dump = match kind {
|
||||
ExceptionKind::Debug => {
|
||||
// TODO check if the thread was really in single-step mode or has debugging related to
|
||||
// the address in exception description
|
||||
thread.handle_single_step(frame);
|
||||
false
|
||||
}
|
||||
ExceptionKind::PageFault => {
|
||||
let cr2 = CR2.get();
|
||||
|
||||
warnln!("CR2 = {:#x}", cr2);
|
||||
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
true
|
||||
}
|
||||
ExceptionKind::GeneralProtectionFault => {
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
if thread.handle_breakpoint(frame) {
|
||||
false
|
||||
} else {
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
true
|
||||
}
|
||||
}
|
||||
ExceptionKind::FpuException => {
|
||||
todo!()
|
||||
}
|
||||
ExceptionKind::InvalidOpcode => {
|
||||
// TODO handle ud2 as breakpoint? (it's 2 bytes)
|
||||
thread.raise_signal(Signal::Aborted);
|
||||
true
|
||||
}
|
||||
ExceptionKind::Breakpoint => {
|
||||
todo!()
|
||||
}
|
||||
_ => todo!("No handler for exception: {:?}", kind),
|
||||
};
|
||||
|
||||
if dump {
|
||||
dump_user_exception(kind, frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use kernel_fs::devfs;
|
||||
use libk::{
|
||||
module::load_kernel_symbol_table,
|
||||
random,
|
||||
task::{process::Process, runtime, thread::Thread},
|
||||
task::{binary::LoadOptions, process::Process, runtime, thread::Thread},
|
||||
vfs::{impls::fn_symlink, IoContext, NodeRef},
|
||||
};
|
||||
use memfs::MemoryFilesystem;
|
||||
@ -75,8 +75,15 @@ pub fn kinit() -> Result<(), Error> {
|
||||
|
||||
{
|
||||
let group_id = Process::create_group();
|
||||
let (user_init, user_init_main) =
|
||||
proc::load_binary(&mut ioctx, group_id, None, "/init", &["/init", "xxx"], &[])?;
|
||||
let options = LoadOptions {
|
||||
group_id,
|
||||
parent: None,
|
||||
path: "/init",
|
||||
args: &["/init", "xxx"],
|
||||
envs: &[],
|
||||
single_step: false,
|
||||
};
|
||||
let (user_init, user_init_main) = proc::load_binary(&mut ioctx, &options)?;
|
||||
|
||||
let mut io = user_init.io.lock();
|
||||
io.set_ioctx(ioctx);
|
||||
|
@ -3,7 +3,7 @@
|
||||
use abi::{error::Error, path::Path, process::ProcessGroupId};
|
||||
use alloc::sync::{Arc, Weak};
|
||||
use libk::{
|
||||
task::{process::Process, thread::Thread},
|
||||
task::{binary::LoadOptions, process::Process, thread::Thread},
|
||||
vfs::IoContext,
|
||||
};
|
||||
|
||||
@ -11,11 +11,7 @@ use libk::{
|
||||
#[inline]
|
||||
pub fn load_binary<P: AsRef<Path>>(
|
||||
ioctx: &mut IoContext,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<Process>>,
|
||||
path: P,
|
||||
args: &[&str],
|
||||
envs: &[&str],
|
||||
options: &LoadOptions<P>,
|
||||
) -> Result<(Arc<Process>, Arc<Thread>), Error> {
|
||||
libk::task::binary::load(ioctx, group_id, parent, path, args, envs)
|
||||
libk::task::binary::load(ioctx, options)
|
||||
}
|
||||
|
@ -16,16 +16,21 @@ pub(crate) fn debug_trace(message: &str) {
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn debug_control(pid: ProcessId, op: &DebugOperation) -> Result<(), Error> {
|
||||
pub(crate) fn debug_control(pid: ProcessId, op: &mut DebugOperation) -> Result<(), Error> {
|
||||
let target = Process::get(pid).ok_or(Error::DoesNotExist)?;
|
||||
let target_thread = target.as_single_thread().unwrap();
|
||||
|
||||
match op {
|
||||
&DebugOperation::Continue(single_step) => {
|
||||
&mut DebugOperation::Continue(single_step) => {
|
||||
// TODO check if it's paused currently
|
||||
target_thread.resume(single_step);
|
||||
Ok(())
|
||||
}
|
||||
&mut DebugOperation::SetBreakpoint(address) => target_thread.set_breakpoint(address),
|
||||
DebugOperation::ReadMemory { address, buffer } => {
|
||||
target_thread.read_memory(*address, buffer)
|
||||
}
|
||||
DebugOperation::WriteMemory { address, buffer } => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,10 @@ use abi::{
|
||||
use alloc::sync::Arc;
|
||||
use libk::{
|
||||
block,
|
||||
task::{debug::ThreadDebugger, process::Process, runtime, thread::Thread, ThreadId},
|
||||
task::{
|
||||
binary::LoadOptions, debug::ThreadDebugger, process::Process, runtime, thread::Thread,
|
||||
ThreadId,
|
||||
},
|
||||
vfs::IoContext,
|
||||
};
|
||||
use libk_mm::{
|
||||
@ -89,7 +92,6 @@ pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Err
|
||||
let process = thread.process();
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
// let mut attach_debugger = None;
|
||||
let attach_debugger = options
|
||||
.optional
|
||||
.iter()
|
||||
@ -105,14 +107,15 @@ pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Err
|
||||
})?;
|
||||
|
||||
// Setup a new process from the file
|
||||
let (child_process, child_main) = proc::load_binary(
|
||||
io.ioctx_mut(),
|
||||
process.group_id(),
|
||||
Some(Arc::downgrade(&process)),
|
||||
options.program,
|
||||
options.arguments,
|
||||
options.environment,
|
||||
)?;
|
||||
let load_options = LoadOptions {
|
||||
group_id: process.group_id(),
|
||||
parent: Some(Arc::downgrade(&process)),
|
||||
path: options.program,
|
||||
args: options.arguments,
|
||||
envs: options.arguments,
|
||||
single_step: attach_debugger.is_some(),
|
||||
};
|
||||
let (child_process, child_main) = proc::load_binary(io.ioctx_mut(), &load_options)?;
|
||||
let pid = child_process.id;
|
||||
|
||||
// Inherit group and session from the creator
|
||||
@ -166,8 +169,9 @@ pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Err
|
||||
|
||||
if let Some(debugger) = attach_debugger {
|
||||
child_main.attach_debugger(ThreadDebugger::new(debugger));
|
||||
} else {
|
||||
child_main.enqueue();
|
||||
}
|
||||
child_main.enqueue();
|
||||
|
||||
Ok(pid as _)
|
||||
})
|
||||
|
@ -138,4 +138,4 @@ syscall execve(options: &ExecveOptions<'_>) -> Result<()>;
|
||||
|
||||
// Debugging
|
||||
syscall debug_trace(message: &str);
|
||||
syscall debug_control(pid: ProcessId, op: &DebugOperation<'_>) -> Result<()>;
|
||||
syscall debug_control(pid: ProcessId, op: &mut DebugOperation<'_>) -> Result<()>;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use super::FrameOps;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[repr(C)]
|
||||
@ -10,3 +12,13 @@ pub struct SavedFrame {
|
||||
pub sp_el0: u64,
|
||||
pub mdscr_el1: u64,
|
||||
}
|
||||
|
||||
impl FrameOps for SavedFrame {
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
self.elr_el1 = value as _;
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
self.elr_el1 as _
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use super::FrameOps;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[repr(C)]
|
||||
@ -16,3 +18,13 @@ pub struct SavedFrame {
|
||||
pub user_sp: u32,
|
||||
pub eflags: u32,
|
||||
}
|
||||
|
||||
impl FrameOps for SavedFrame {
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
self.user_ip = value as _;
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
self.user_ip as _
|
||||
}
|
||||
}
|
||||
|
@ -16,3 +16,8 @@ pub(crate) mod i686;
|
||||
use i686 as arch_impl;
|
||||
|
||||
pub use arch_impl::SavedFrame;
|
||||
|
||||
pub trait FrameOps {
|
||||
fn set_user_ip(&mut self, value: usize);
|
||||
fn user_ip(&self) -> usize;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use super::FrameOps;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[repr(C)]
|
||||
@ -25,3 +27,13 @@ pub struct SavedFrame {
|
||||
pub user_sp: u64,
|
||||
pub rflags: u64,
|
||||
}
|
||||
|
||||
impl FrameOps for SavedFrame {
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
self.user_ip = value as _;
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
self.user_ip as _
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ pub enum DebugOperation<'a> {
|
||||
|
||||
Interrupt,
|
||||
Continue(bool),
|
||||
SetBreakpoint(usize),
|
||||
|
||||
ReadMemory {
|
||||
address: usize,
|
||||
@ -25,11 +26,14 @@ pub enum DebugFrame {
|
||||
Startup {
|
||||
image_base: usize,
|
||||
ip_offset: usize,
|
||||
frame: SavedFrame,
|
||||
ip: usize,
|
||||
},
|
||||
Step {
|
||||
frame: SavedFrame,
|
||||
},
|
||||
HitBreakpoint {
|
||||
frame: SavedFrame,
|
||||
},
|
||||
// TODO exit status
|
||||
Exited,
|
||||
}
|
||||
|
7
userspace/Cargo.lock
generated
7
userspace/Cargo.lock
generated
@ -701,6 +701,7 @@ dependencies = [
|
||||
"iced-x86",
|
||||
"libterm",
|
||||
"rangemap",
|
||||
"rustc-demangle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
@ -729,6 +730,12 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
|
@ -14,6 +14,7 @@ serde = { version = "1.0.193", features = ["derive"], default-features = false }
|
||||
thiserror = "1.0.58"
|
||||
elf = "0.7.4"
|
||||
rangemap = "1.5.1"
|
||||
rustc-demangle = "0.1.24"
|
||||
|
||||
[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dependencies]
|
||||
iced-x86 = { version = "1.21.0", default-features = false, features = ["gas", "decoder", "std"] }
|
||||
|
@ -2,6 +2,8 @@ use std::fmt::{self, Display};
|
||||
|
||||
use yggdrasil_abi::arch::SavedFrame;
|
||||
|
||||
use crate::debugger::SymbolResolver;
|
||||
|
||||
use super::{Error, InstructionFormatter, Target};
|
||||
|
||||
pub struct TargetImpl;
|
||||
@ -38,7 +40,7 @@ impl Target for TargetImpl {
|
||||
) -> Result<Vec<(usize, Self::Instruction)>, Error> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn new_instruction_formatter() -> Self::InstructionFormatter {
|
||||
fn new_instruction_formatter(_resolver: SymbolResolver) -> Self::InstructionFormatter {
|
||||
Unsupported
|
||||
}
|
||||
|
||||
@ -46,7 +48,7 @@ impl Target for TargetImpl {
|
||||
FlagFormat(frame.spsr_el1)
|
||||
}
|
||||
fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>) {
|
||||
for i in 0..30 {
|
||||
for i in 0..10 {
|
||||
out.push((format!("x{}", i), frame.gp_regs[i]));
|
||||
}
|
||||
out.push(("pc".into(), frame.elr_el1 as _));
|
||||
|
@ -1,4 +1,6 @@
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
collections::HashMap,
|
||||
fmt::Write,
|
||||
fs::File,
|
||||
io::{BufReader, Read, Seek, SeekFrom},
|
||||
@ -8,9 +10,10 @@ use std::{
|
||||
},
|
||||
path::Path,
|
||||
process::{Child, Command},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use elf::{endian::AnyEndian, segment::ProgramHeader, ElfStream};
|
||||
use elf::{endian::AnyEndian, segment::ProgramHeader, symbol::Symbol, ElfStream};
|
||||
use libterm::{Color, Term, TermKey};
|
||||
use rangemap::RangeMap;
|
||||
use yggdrasil_rt::{debug::DebugFrame, process::ProcessId};
|
||||
@ -19,12 +22,28 @@ use crate::state::State;
|
||||
use crate::InstructionFormatter;
|
||||
use crate::{comm::Comm, Error, Target};
|
||||
|
||||
pub struct SymbolResolver {
|
||||
image: Arc<ImageInfo>,
|
||||
ip_offset: usize,
|
||||
ip: u64,
|
||||
}
|
||||
|
||||
pub struct ImageInfo {
|
||||
symbol_table: HashMap<String, Symbol>,
|
||||
segment_headers: RangeMap<usize, ProgramHeader>,
|
||||
segments: RefCell<RangeMap<usize, Vec<u8>>>,
|
||||
functions: RangeMap<u64, String>,
|
||||
}
|
||||
|
||||
pub struct Debugger<T: Target> {
|
||||
comm: Comm,
|
||||
term: Term,
|
||||
file: BufReader<File>,
|
||||
segment_headers: RangeMap<usize, ProgramHeader>,
|
||||
segments: RangeMap<usize, Vec<u8>>,
|
||||
|
||||
image: Arc<ImageInfo>,
|
||||
|
||||
command: Option<String>,
|
||||
status: Option<String>,
|
||||
|
||||
term_fd: RawFd,
|
||||
comm_fd: RawFd,
|
||||
@ -40,7 +59,7 @@ impl<T: Target> Debugger<T> {
|
||||
pub fn from_command<P: AsRef<Path>>(image: P, mut command: Command) -> Result<Self, Error> {
|
||||
let image = image.as_ref();
|
||||
|
||||
let segment_headers = extract_segments(image)?;
|
||||
let image_info = read_image(image)?;
|
||||
|
||||
let file = BufReader::new(File::open(image)?);
|
||||
let comm = Comm::open("rdb-1")?;
|
||||
@ -64,8 +83,10 @@ impl<T: Target> Debugger<T> {
|
||||
comm,
|
||||
term,
|
||||
|
||||
segment_headers,
|
||||
segments: RangeMap::new(),
|
||||
image: image_info.into(),
|
||||
|
||||
command: None,
|
||||
status: None,
|
||||
|
||||
term_fd,
|
||||
comm_fd,
|
||||
@ -79,11 +100,20 @@ impl<T: Target> Debugger<T> {
|
||||
}
|
||||
|
||||
fn handle_frame(&mut self, frame: DebugFrame) -> Result<(), Error> {
|
||||
self.status = None;
|
||||
|
||||
match frame {
|
||||
DebugFrame::Startup { image_base, frame, ip_offset } => {
|
||||
DebugFrame::Startup {
|
||||
image_base,
|
||||
ip_offset,
|
||||
ip,
|
||||
} => {
|
||||
let pid = unsafe { ProcessId::from_raw(self.child.id()) };
|
||||
let mut state = State::new(image_base, ip_offset, pid);
|
||||
state.update(&frame, true)?;
|
||||
if ip >= ip_offset {
|
||||
state.update_ip(ip - ip_offset);
|
||||
}
|
||||
self.status = Some(format!("Attached to #{} @ {:#x}", pid, state.current_ip));
|
||||
self.state = Some(state);
|
||||
Ok(())
|
||||
}
|
||||
@ -92,6 +122,12 @@ impl<T: Target> Debugger<T> {
|
||||
state.update(&frame, true)?;
|
||||
Ok(())
|
||||
}
|
||||
DebugFrame::HitBreakpoint { frame } => {
|
||||
let state = self.state.as_mut().unwrap();
|
||||
state.update(&frame, true)?;
|
||||
self.status = Some(format!("Hit breakpoint @ {:#x}", state.current_ip));
|
||||
Ok(())
|
||||
}
|
||||
DebugFrame::Exited => {
|
||||
self.child_exited = true;
|
||||
Ok(())
|
||||
@ -100,22 +136,99 @@ impl<T: Target> Debugger<T> {
|
||||
}
|
||||
|
||||
fn handle_key(&mut self, key: TermKey) -> Result<(), Error> {
|
||||
match key {
|
||||
TermKey::Char('q') => {
|
||||
// TODO send exit to the child
|
||||
// self.child.kill()?;
|
||||
todo!();
|
||||
if let Some(command) = self.command.as_mut() {
|
||||
match key {
|
||||
TermKey::Char('\x7F') => {
|
||||
if !command.is_empty() {
|
||||
command.pop();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TermKey::Char('\n') => {
|
||||
let command = command.clone();
|
||||
self.command = None;
|
||||
self.run_command(&command)?;
|
||||
Ok(())
|
||||
}
|
||||
TermKey::Char(ch) if ch.is_ascii_graphic() || ch == ' ' => {
|
||||
command.push(ch);
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
TermKey::Char('s') => {
|
||||
// Send resume to the debugee
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.resume(true)?;
|
||||
} else {
|
||||
match key {
|
||||
TermKey::Char(':') => {
|
||||
self.command = Some(String::new());
|
||||
Ok(())
|
||||
}
|
||||
TermKey::Char('q') => {
|
||||
// TODO send exit to the child
|
||||
// self.child.kill()?;
|
||||
todo!();
|
||||
}
|
||||
TermKey::Char('s') => {
|
||||
// Send resume to the debugee
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.resume(true)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TermKey::Char('c') => {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.resume(false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_command(&mut self, command: &str) -> Result<(), Error> {
|
||||
let words: Vec<&str> = command.split(' ').collect();
|
||||
|
||||
if words.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(state) = self.state.as_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match words[0] {
|
||||
"break" | "b" if words.len() == 2 => {
|
||||
let address = Self::parse_location(&self.image, state.ip_offset, words[1])?;
|
||||
if let Err(error) = state.set_breakpoint(address) {
|
||||
self.status = Some(format!(
|
||||
"Couldn't set breakpoint @ {:#x}: {:?}",
|
||||
address, error
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TermKey::Char('c') => {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.resume(false)?;
|
||||
"read" | "r" if words.len() == 3 => {
|
||||
let ty = match words[1] {
|
||||
"b" => 1,
|
||||
"w" => 2,
|
||||
"d" => 4,
|
||||
"q" => 8,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let address = convert_address(words[2])?;
|
||||
|
||||
let mut buf = [0; 8];
|
||||
if let Ok(_) = state.read_memory(address, &mut buf[..ty]) {
|
||||
let value = match ty {
|
||||
1 => buf[0] as u64,
|
||||
2 => u16::from_ne_bytes([buf[0], buf[1]]) as u64,
|
||||
4 => u32::from_ne_bytes([buf[0], buf[1], buf[2], buf[3]]) as u64,
|
||||
8 => u64::from_ne_bytes(buf),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.status = Some(format!("*{:#x} -> {:#x}", address, value));
|
||||
} else {
|
||||
self.status = Some(format!("Could not read memory at {:#x}", address));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -128,10 +241,12 @@ impl<T: Target> Debugger<T> {
|
||||
return Ok(vec![]);
|
||||
};
|
||||
|
||||
let mut segments = self.image.segments.borrow_mut();
|
||||
|
||||
// Find segment
|
||||
let (range, segment) = match self.segments.get_key_value(&state.current_ip) {
|
||||
let (range, segment) = match segments.get_key_value(&state.current_ip) {
|
||||
Some(seg) => seg,
|
||||
None if let Some(header) = self.segment_headers.get(&state.current_ip) => {
|
||||
None if let Some(header) = self.image.segment_headers.get(&state.current_ip) => {
|
||||
let start = header.p_vaddr as usize;
|
||||
let end = (header.p_vaddr + header.p_memsz) as usize;
|
||||
let mut buffer = vec![0; header.p_memsz as usize];
|
||||
@ -139,18 +254,19 @@ impl<T: Target> Debugger<T> {
|
||||
self.file.seek(SeekFrom::Start(header.p_offset))?;
|
||||
self.file.read_exact(&mut buffer)?;
|
||||
|
||||
self.segments.insert(start..end, buffer);
|
||||
segments.insert(start..end, buffer);
|
||||
|
||||
self.segments.get_key_value(&state.current_ip).unwrap()
|
||||
segments.get_key_value(&state.current_ip).unwrap()
|
||||
}
|
||||
// Outside of any segments
|
||||
None => return Ok(vec![]),
|
||||
};
|
||||
|
||||
let offset_within_segment = state.current_ip - range.start;
|
||||
let upper_limit = std::cmp::min(segment.len(), offset_within_segment + amount * 8);
|
||||
|
||||
T::disassemble(
|
||||
&segment[offset_within_segment..offset_within_segment + amount * 8],
|
||||
&segment[offset_within_segment..upper_limit],
|
||||
state.current_ip + state.ip_offset,
|
||||
amount,
|
||||
)
|
||||
@ -163,7 +279,7 @@ impl<T: Target> Debugger<T> {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
let columns = (width + REG_WIDTH - 3) / REG_WIDTH;
|
||||
let columns = core::cmp::max((width - 2) / REG_WIDTH, 1);
|
||||
let mut gpregs = vec![];
|
||||
T::register_list(&state.last_frame, &mut gpregs);
|
||||
let rows = 1 + (gpregs.len() + columns - 1) / columns;
|
||||
@ -213,22 +329,31 @@ impl<T: Target> Debugger<T> {
|
||||
}
|
||||
|
||||
fn redraw(&mut self) -> Result<(), Error> {
|
||||
let resolver = self.symbol_resolver();
|
||||
let (width, height) = self.term.size()?;
|
||||
self.term.clear(libterm::Clear::All)?;
|
||||
|
||||
// Show register block
|
||||
let regs_rows = self.print_registers(width)?;
|
||||
|
||||
let disassembly = self.disassembly(height - regs_rows - 1)?;
|
||||
if let Some((symbol, offset)) = resolver
|
||||
.as_ref()
|
||||
.and_then(SymbolResolver::resolve_current_function)
|
||||
{
|
||||
self.term.set_cursor_position(regs_rows, 1).ok();
|
||||
write!(self.term, "<{}> + {}", symbol, offset).ok();
|
||||
}
|
||||
|
||||
let disassembly = self.disassembly(height - regs_rows - 2)?;
|
||||
|
||||
if !disassembly.is_empty() {
|
||||
let mut formatter = T::new_instruction_formatter();
|
||||
let mut formatter = T::new_instruction_formatter(resolver.unwrap());
|
||||
let mut buffer = String::new();
|
||||
|
||||
// Show disassembly block
|
||||
for (i, (ip, insn)) in disassembly.into_iter().enumerate() {
|
||||
let is_current = i == 0;
|
||||
self.term.set_cursor_position(i + regs_rows, 0)?;
|
||||
self.term.set_cursor_position(i + regs_rows + 1, 0)?;
|
||||
buffer.clear();
|
||||
formatter.format_instruction(&insn, &mut buffer);
|
||||
if is_current {
|
||||
@ -256,7 +381,13 @@ impl<T: Target> Debugger<T> {
|
||||
write!(self.term, " Waiting for inferior process").ok();
|
||||
}
|
||||
|
||||
// TODO deconflict status and command
|
||||
self.term.set_cursor_position(height - 1, 0)?;
|
||||
if let Some(command) = self.command.as_ref() {
|
||||
write!(self.term, ":{}", command).ok();
|
||||
} else if let Some(status) = self.status.as_ref() {
|
||||
write!(self.term, ">> {}", status).ok();
|
||||
}
|
||||
self.term.reset_style()?;
|
||||
self.term.flush()?;
|
||||
|
||||
@ -287,13 +418,75 @@ impl<T: Target> Debugger<T> {
|
||||
println!("Program exited with status {:?}", status);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn symbol_resolver(&self) -> Option<SymbolResolver> {
|
||||
let state = self.state.as_ref()?;
|
||||
Some(SymbolResolver {
|
||||
image: self.image.clone(),
|
||||
ip_offset: state.ip_offset,
|
||||
ip: state.current_ip as _,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_location(image: &ImageInfo, ip_offset: usize, string: &str) -> Result<u64, Error> {
|
||||
// TODO validate that the breakpoint is within .text segment (or maybe delegate this
|
||||
// validation to the kernel?)
|
||||
if let Some(sym) = image.symbol_table.get(string) {
|
||||
Ok(sym.st_value + ip_offset as u64)
|
||||
} else {
|
||||
convert_address(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_segments<P: AsRef<Path>>(path: P) -> Result<RangeMap<usize, ProgramHeader>, Error> {
|
||||
impl SymbolResolver {
|
||||
fn resolve_symbol_inner(image: &ImageInfo, ip: u64) -> Option<(&str, usize)> {
|
||||
if let Some((range, function)) = image.functions.get_key_value(&ip) {
|
||||
assert!(range.start <= ip);
|
||||
return Some((function.as_str(), (ip - range.start).try_into().unwrap()));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn resolve_symbol(&self, ip: u64) -> Option<(&str, usize)> {
|
||||
Self::resolve_symbol_inner(&self.image, ip)
|
||||
}
|
||||
|
||||
pub fn resolve_current_function(&self) -> Option<(&str, usize)> {
|
||||
Self::resolve_symbol_inner(&self.image, self.ip)
|
||||
}
|
||||
|
||||
pub fn to_image_address(&self, address: u64) -> Option<u64> {
|
||||
address.checked_sub(self.ip_offset as _)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_image<P: AsRef<Path>>(path: P) -> Result<ImageInfo, Error> {
|
||||
let file = BufReader::new(File::open(path)?);
|
||||
let elf = ElfStream::<AnyEndian, _>::open_stream(file).unwrap();
|
||||
let mut elf = ElfStream::<AnyEndian, _>::open_stream(file).unwrap();
|
||||
|
||||
let mut ranges = RangeMap::new();
|
||||
let mut symbols = HashMap::new();
|
||||
let mut functions = RangeMap::new();
|
||||
|
||||
let (symtab, strtab) = elf.symbol_table().unwrap().unwrap();
|
||||
|
||||
#[cfg(any(not(target_arch = "aarch64"), rust_analyzer))]
|
||||
for sym in symtab {
|
||||
let raw_name = strtab.get(sym.st_name as _).unwrap();
|
||||
let demangled_name = rustc_demangle::demangle(raw_name).to_string();
|
||||
|
||||
if sym.st_symtype() == elf::abi::STT_FUNC && sym.st_size != 0 {
|
||||
functions.insert(
|
||||
sym.st_value..sym.st_value + sym.st_size,
|
||||
demangled_name.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
symbols.insert(demangled_name, sym);
|
||||
}
|
||||
|
||||
for seg in elf.segments() {
|
||||
if seg.p_type != elf::abi::PT_LOAD {
|
||||
continue;
|
||||
@ -305,5 +498,17 @@ fn extract_segments<P: AsRef<Path>>(path: P) -> Result<RangeMap<usize, ProgramHe
|
||||
ranges.insert(start..end, *seg);
|
||||
}
|
||||
|
||||
Ok(ranges)
|
||||
Ok(ImageInfo {
|
||||
functions,
|
||||
symbol_table: symbols,
|
||||
segment_headers: ranges,
|
||||
segments: RangeMap::new().into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_address(s: &str) -> Result<u64, Error> {
|
||||
if let Some(v) = s.strip_prefix("0x") {
|
||||
return u64::from_str_radix(v, 16).map_err(|_| Error::InvalidAddress(s.into()));
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
use std::{fmt::{LowerHex, Display}, io, path::PathBuf, process::Command};
|
||||
|
||||
use clap::Parser;
|
||||
use debugger::Debugger;
|
||||
use debugger::{Debugger, SymbolResolver};
|
||||
use imp::TargetImpl;
|
||||
use yggdrasil_abi::arch::SavedFrame;
|
||||
|
||||
@ -26,6 +26,8 @@ pub enum Error {
|
||||
TermError(#[from] libterm::Error),
|
||||
#[error("Debug control error: {0:?}")]
|
||||
DebugError(yggdrasil_rt::Error),
|
||||
#[error("Invalid address: {0:?}")]
|
||||
InvalidAddress(String)
|
||||
}
|
||||
|
||||
pub trait Target {
|
||||
@ -38,7 +40,7 @@ pub trait Target {
|
||||
ip: usize,
|
||||
limit: usize,
|
||||
) -> Result<Vec<(usize, Self::Instruction)>, Error>;
|
||||
fn new_instruction_formatter() -> Self::InstructionFormatter;
|
||||
fn new_instruction_formatter(resolver: SymbolResolver) -> Self::InstructionFormatter;
|
||||
|
||||
fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>);
|
||||
fn flags_register_as_display(frame: &SavedFrame) -> impl Display;
|
||||
|
@ -33,15 +33,35 @@ impl<T: Target> State<T> {
|
||||
|
||||
pub fn resume(&mut self, step: bool) -> Result<(), Error> {
|
||||
unsafe {
|
||||
yggdrasil_rt::sys::debug_control(self.pid, &DebugOperation::Continue(step))
|
||||
yggdrasil_rt::sys::debug_control(self.pid, &mut DebugOperation::Continue(step))
|
||||
.map_err(Error::DebugError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_breakpoint(&mut self, address: u64) -> Result<(), Error> {
|
||||
unsafe {
|
||||
yggdrasil_rt::sys::debug_control(self.pid, &mut DebugOperation::SetBreakpoint(address.try_into().unwrap())).map_err(Error::DebugError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_memory(&mut self, address: u64, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
let mut op = DebugOperation::ReadMemory { address: address.try_into().unwrap(), buffer };
|
||||
unsafe {
|
||||
yggdrasil_rt::sys::debug_control(
|
||||
self.pid,
|
||||
&mut op
|
||||
).map_err(Error::DebugError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_ip(&mut self, ip: usize) {
|
||||
self.current_ip = ip;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, frame: &SavedFrame, _refresh: bool) -> Result<(), Error> {
|
||||
let ip = T::real_ip(frame) - self.ip_offset;
|
||||
self.update_ip(ip);
|
||||
self.last_frame = frame.clone();
|
||||
self.current_ip = ip;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display};
|
||||
use iced_x86::{Decoder, DecoderOptions, Formatter, GasFormatter, Instruction};
|
||||
use yggdrasil_abi::arch::SavedFrame;
|
||||
|
||||
use crate::{InstructionFormatter, Target};
|
||||
use crate::{debugger::SymbolResolver, InstructionFormatter, Target};
|
||||
|
||||
#[cfg(any(target_pointer_width = "32", rust_analyzer))]
|
||||
const BITNESS: u32 = 32;
|
||||
@ -44,6 +44,28 @@ impl InstructionFormatter<Instruction> for GasFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
impl iced_x86::SymbolResolver for SymbolResolver {
|
||||
fn symbol(
|
||||
&mut self,
|
||||
_instruction: &Instruction,
|
||||
_operand: u32,
|
||||
_instruction_operand: Option<u32>,
|
||||
address: u64,
|
||||
_address_size: u32,
|
||||
) -> Option<iced_x86::SymbolResult<'_>> {
|
||||
let image_addr = self.to_image_address(address)?;
|
||||
let (symbol, offset) = self.resolve_symbol(image_addr)?;
|
||||
|
||||
let string = if offset == 0 {
|
||||
format!("{:#x} <{}>", address, symbol)
|
||||
} else {
|
||||
format!("{:#x} <{}+{:#x}>", address, symbol, offset)
|
||||
};
|
||||
|
||||
Some(iced_x86::SymbolResult::with_string(address, string))
|
||||
}
|
||||
}
|
||||
|
||||
impl Target for TargetImpl {
|
||||
type Instruction = Instruction;
|
||||
type InstructionFormatter = GasFormatter;
|
||||
@ -62,15 +84,14 @@ impl Target for TargetImpl {
|
||||
instructions.push((insn.ip() as usize, insn));
|
||||
}
|
||||
|
||||
debug_trace!("{}", instructions.len());
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
fn new_instruction_formatter() -> Self::InstructionFormatter {
|
||||
let mut formatter = GasFormatter::new();
|
||||
formatter.options_mut().set_uppercase_hex(false);
|
||||
formatter.options_mut().set_branch_leading_zeros(false);
|
||||
formatter
|
||||
fn new_instruction_formatter(resolver: SymbolResolver) -> Self::InstructionFormatter {
|
||||
let mut formatter = GasFormatter::with_options(Some(Box::new(resolver)), None);
|
||||
formatter.options_mut().set_uppercase_hex(false);
|
||||
formatter.options_mut().set_branch_leading_zeros(false);
|
||||
formatter
|
||||
}
|
||||
|
||||
fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>) {
|
||||
|
@ -94,3 +94,7 @@ path = "src/chmod.rs"
|
||||
[[bin]]
|
||||
name = "sysmon"
|
||||
path = "src/sysmon.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "tst"
|
||||
path = "src/tst.rs"
|
||||
|
35
userspace/sysutils/src/tst.rs
Normal file
35
userspace/sysutils/src/tst.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use std::time::Duration;
|
||||
|
||||
fn f(v: f64, s : bool) {
|
||||
let mut c = 0;
|
||||
loop {
|
||||
let mut x = core::hint::black_box(1.0);
|
||||
let y = core::hint::black_box(2.0);
|
||||
for _ in 0..10 {
|
||||
x *= y;
|
||||
}
|
||||
let z = core::hint::black_box(v);
|
||||
let v = core::hint::black_box(core::hint::black_box(x * y) + z);
|
||||
if s {
|
||||
if v != 2050.000 {
|
||||
println!("{:.3} ", v);
|
||||
}
|
||||
}
|
||||
// if c == 100 {
|
||||
// println!();
|
||||
// c = 0;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
std::thread::spawn(|| f(1.5, false));
|
||||
std::thread::spawn(|| f(3.5, false));
|
||||
std::thread::spawn(|| f(0.75, false));
|
||||
std::thread::spawn(|| f(0.75, false));
|
||||
std::thread::spawn(|| f(0.75, false));
|
||||
std::thread::spawn(|| f(0.75, false));
|
||||
// std::thread::spawn(f);
|
||||
|
||||
f(2.0, true)
|
||||
}
|
@ -34,6 +34,7 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
("chmod", "bin/chmod"),
|
||||
// ("sha256sum", "bin/sha256sum"),
|
||||
("sysmon", "bin/sysmon"),
|
||||
("tst", "bin/tst"),
|
||||
// netutils
|
||||
("netconf", "sbin/netconf"),
|
||||
("dhcp-client", "sbin/dhcp-client"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user