arch: aarch64 fp context save, proper single-step
This commit is contained in:
parent
0daf7c677c
commit
0436381b33
Cargo.lock
etc
kernel
arch
aarch64
i686/src
interface/src
x86_64/src
libk/src
modules/test_mod
src
lib/abi
userspace
xtask/src/build
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -838,6 +838,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aarch64-cpu",
|
"aarch64-cpu",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
|
"cc",
|
||||||
"device-api",
|
"device-api",
|
||||||
"kernel-arch-interface",
|
"kernel-arch-interface",
|
||||||
"libk-mm-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",
|
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||||
"max-atomic-width": 128,
|
"max-atomic-width": 128,
|
||||||
"target-pointer-width": "64",
|
"target-pointer-width": "64",
|
||||||
"features": "+v8a,+strict-align,+neon,+fp-armv8",
|
"features": "+v8a,+strict-align,-neon,-fp-armv8",
|
||||||
|
|
||||||
"disable-redzone": true,
|
"disable-redzone": true,
|
||||||
"executables": true,
|
"executables": true,
|
||||||
|
@ -14,3 +14,6 @@ bitflags = "2.6.0"
|
|||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
aarch64-cpu = "9.4.0"
|
aarch64-cpu = "9.4.0"
|
||||||
tock-registers = "0.8.1"
|
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
|
eret
|
||||||
|
|
||||||
__aarch64_task_enter_user:
|
__aarch64_task_enter_user:
|
||||||
// x0 == sp, x1 == ignored
|
// x0 == sp, x1 == mdscr_el1
|
||||||
ldp x0, x1, [sp, #16 * 0]
|
ldp x0, x1, [sp, #16 * 0]
|
||||||
msr sp_el0, x0
|
msr sp_el0, x0
|
||||||
|
msr mdscr_el1, x1
|
||||||
|
|
||||||
# EL0t, IRQs unmasked
|
# EL0t, IRQs unmasked
|
||||||
msr spsr_el1, xzr
|
msr spsr_el1, xzr
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//! AArch64-specific task context implementation
|
//! 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::{
|
use kernel_arch_interface::{
|
||||||
mem::{KernelTableManager, PhysicalMemoryAllocator},
|
mem::{KernelTableManager, PhysicalMemoryAllocator},
|
||||||
@ -29,6 +29,12 @@ struct TaskContextInner {
|
|||||||
sp: usize,
|
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
|
/// AArch64 implementation of a task context
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct TaskContextImpl<
|
pub struct TaskContextImpl<
|
||||||
@ -36,6 +42,7 @@ pub struct TaskContextImpl<
|
|||||||
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
|
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
|
||||||
> {
|
> {
|
||||||
inner: UnsafeCell<TaskContextInner>,
|
inner: UnsafeCell<TaskContextInner>,
|
||||||
|
fp_context: UnsafeCell<FpContext>,
|
||||||
stack_base_phys: PhysicalAddress,
|
stack_base_phys: PhysicalAddress,
|
||||||
stack_size: usize,
|
stack_size: usize,
|
||||||
|
|
||||||
@ -45,6 +52,22 @@ pub struct TaskContextImpl<
|
|||||||
|
|
||||||
const COMMON_CONTEXT_SIZE: usize = 8 * 14;
|
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 {
|
impl TaskFrame for ExceptionFrame {
|
||||||
fn store(&self) -> SavedFrame {
|
fn store(&self) -> SavedFrame {
|
||||||
SavedFrame {
|
SavedFrame {
|
||||||
@ -151,6 +174,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
inner: UnsafeCell::new(TaskContextInner { sp }),
|
inner: UnsafeCell::new(TaskContextInner { sp }),
|
||||||
|
fp_context: UnsafeCell::new(FpContext::new()),
|
||||||
stack_base_phys,
|
stack_base_phys,
|
||||||
stack_size: KERNEL_TASK_PAGES * 0x1000,
|
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 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.entry);
|
||||||
stack.push(context.argument);
|
stack.push(context.argument);
|
||||||
stack.push(0);
|
stack.push(mdscr_el1);
|
||||||
stack.push(context.stack_pointer);
|
stack.push(context.stack_pointer);
|
||||||
|
|
||||||
setup_common_context(
|
setup_common_context(
|
||||||
@ -182,6 +208,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
inner: UnsafeCell::new(TaskContextInner { sp }),
|
inner: UnsafeCell::new(TaskContextInner { sp }),
|
||||||
|
fp_context: UnsafeCell::new(FpContext::new()),
|
||||||
stack_base_phys,
|
stack_base_phys,
|
||||||
stack_size: USER_TASK_PAGES * 0x1000,
|
stack_size: USER_TASK_PAGES * 0x1000,
|
||||||
|
|
||||||
@ -191,14 +218,28 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn enter(&self) -> ! {
|
unsafe fn enter(&self) -> ! {
|
||||||
|
FpContext::restore(self.fp_context.get());
|
||||||
|
|
||||||
__aarch64_enter_task(self.inner.get())
|
__aarch64_enter_task(self.inner.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn switch(&self, from: &Self) {
|
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 ()) {
|
unsafe fn switch_and_drop(&self, thread: *const ()) {
|
||||||
|
FpContext::restore(self.fp_context.get());
|
||||||
|
|
||||||
__aarch64_switch_task_and_drop(self.inner.get(), thread);
|
__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_switch_task_and_drop(to: *mut TaskContextInner, thread: *const ()) -> !;
|
||||||
fn __aarch64_task_enter_kernel();
|
fn __aarch64_task_enter_kernel();
|
||||||
fn __aarch64_task_enter_user();
|
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);
|
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 {
|
impl Architecture for ArchitectureImpl {
|
||||||
type PerCpuData = PerCpuData;
|
type PerCpuData = PerCpuData;
|
||||||
type CpuFeatures = ();
|
type CpuFeatures = ();
|
||||||
|
type BreakpointType = u32;
|
||||||
|
|
||||||
|
const BREAKPOINT_VALUE: Self::BreakpointType = 0x200020D4;
|
||||||
|
|
||||||
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||||
(MPIDR_EL1.get() & 0xFF) as 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 stack_base = stack_base_phys.raw_virtualize::<K>();
|
||||||
|
|
||||||
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
|
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.entry as _);
|
||||||
stack.push(context.stack_pointer);
|
stack.push(context.stack_pointer);
|
||||||
|
|
||||||
@ -372,7 +377,7 @@ impl TaskFrame for ExceptionFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn user_ip(&self) -> usize {
|
fn user_ip(&self) -> usize {
|
||||||
todo!()
|
self.eip as _
|
||||||
}
|
}
|
||||||
|
|
||||||
fn argument(&self) -> u64 {
|
fn argument(&self) -> u64 {
|
||||||
|
@ -48,6 +48,9 @@ extern "C" fn idle_task(_: usize) -> ! {
|
|||||||
impl Architecture for ArchitectureImpl {
|
impl Architecture for ArchitectureImpl {
|
||||||
type PerCpuData = PerCpuData;
|
type PerCpuData = PerCpuData;
|
||||||
type CpuFeatures = CpuFeatures;
|
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) {
|
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
@ -26,6 +26,9 @@ pub trait Architecture: Sized {
|
|||||||
type PerCpuData;
|
type PerCpuData;
|
||||||
type CpuFeatures: CpuFeatureSet;
|
type CpuFeatures: CpuFeatureSet;
|
||||||
|
|
||||||
|
type BreakpointType;
|
||||||
|
const BREAKPOINT_VALUE: Self::BreakpointType;
|
||||||
|
|
||||||
// Cpu management
|
// Cpu management
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -443,13 +443,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user(
|
fn user(context: UserContextInfo) -> Result<Self, Error> {
|
||||||
context: UserContextInfo, // entry: usize,
|
|
||||||
// arg: usize,
|
|
||||||
// cr3: u64,
|
|
||||||
// user_stack_sp: usize,
|
|
||||||
// fs_base: usize,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
const USER_TASK_PAGES: usize = 8;
|
const USER_TASK_PAGES: usize = 8;
|
||||||
|
|
||||||
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
|
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);
|
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.entry as _);
|
||||||
stack.push(context.argument);
|
stack.push(context.argument);
|
||||||
stack.push(context.stack_pointer);
|
stack.push(context.stack_pointer);
|
||||||
|
@ -89,6 +89,9 @@ impl ArchitectureImpl {
|
|||||||
impl Architecture for ArchitectureImpl {
|
impl Architecture for ArchitectureImpl {
|
||||||
type PerCpuData = PerCpuData;
|
type PerCpuData = PerCpuData;
|
||||||
type CpuFeatures = CpuFeatures;
|
type CpuFeatures = CpuFeatures;
|
||||||
|
type BreakpointType = u8;
|
||||||
|
|
||||||
|
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
|
||||||
|
|
||||||
unsafe fn set_local_cpu(cpu: *mut ()) {
|
unsafe fn set_local_cpu(cpu: *mut ()) {
|
||||||
MSR_IA32_KERNEL_GS_BASE.set(cpu as u64);
|
MSR_IA32_KERNEL_GS_BASE.set(cpu as u64);
|
||||||
|
@ -37,6 +37,15 @@ pub mod elf;
|
|||||||
|
|
||||||
pub type LoadedProcess = (Arc<Process>, Arc<Thread>);
|
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> {
|
struct BufferPlacer<'a> {
|
||||||
buffer: &'a mut [u8],
|
buffer: &'a mut [u8],
|
||||||
virtual_offset: usize,
|
virtual_offset: usize,
|
||||||
@ -122,12 +131,16 @@ fn setup_program_env(
|
|||||||
Ok(in_user as *const _ as usize)
|
Ok(in_user as *const _ as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_context(
|
fn setup_context<P>(
|
||||||
|
options: &LoadOptions<P>,
|
||||||
space: &ProcessAddressSpace,
|
space: &ProcessAddressSpace,
|
||||||
image: &ProcessImage,
|
image: &ProcessImage,
|
||||||
args: &Vec<String>,
|
args: &Vec<String>,
|
||||||
envs: &Vec<String>,
|
envs: &Vec<String>,
|
||||||
) -> Result<TaskContextImpl, Error> {
|
) -> Result<TaskContextImpl, Error>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
const USER_STACK_PAGES: usize = 32;
|
const USER_STACK_PAGES: usize = 32;
|
||||||
|
|
||||||
let virt_stack_base = 0x3000000;
|
let virt_stack_base = 0x3000000;
|
||||||
@ -174,14 +187,13 @@ fn setup_context(
|
|||||||
stack_pointer: ptr.addr(),
|
stack_pointer: ptr.addr(),
|
||||||
tls: tls_address,
|
tls: tls_address,
|
||||||
address_space: space.as_address_with_asid(),
|
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,
|
name: S,
|
||||||
group_id: ProcessGroupId,
|
|
||||||
parent: Option<Weak<Process>>,
|
|
||||||
space: ProcessAddressSpace,
|
space: ProcessAddressSpace,
|
||||||
image: ProcessImage,
|
image: ProcessImage,
|
||||||
args: &Vec<String>,
|
args: &Vec<String>,
|
||||||
@ -189,12 +201,13 @@ fn setup_binary<S>(
|
|||||||
) -> Result<LoadedProcess, Error>
|
) -> Result<LoadedProcess, Error>
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
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(
|
let (process, main) = Process::new_with_main(
|
||||||
name,
|
name,
|
||||||
group_id,
|
options.group_id,
|
||||||
parent,
|
options.parent.clone(),
|
||||||
Arc::new(space),
|
Arc::new(space),
|
||||||
context,
|
context,
|
||||||
Some(image),
|
Some(image),
|
||||||
@ -263,24 +276,16 @@ fn xxx_load_program<P: AsRef<Path>>(
|
|||||||
} else {
|
} else {
|
||||||
Err(Error::UnrecognizedExecutable)
|
Err(Error::UnrecognizedExecutable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// let image = load_binary(head, file, space)?;
|
|
||||||
|
|
||||||
// Ok((image, args, envs))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a program from given `path`
|
/// Loads a program from given `path`
|
||||||
pub fn load<P: AsRef<Path>>(
|
pub fn load<P: AsRef<Path>>(
|
||||||
ioctx: &mut IoContext,
|
ioctx: &mut IoContext,
|
||||||
group_id: ProcessGroupId,
|
options: &LoadOptions<P>,
|
||||||
parent: Option<Weak<Process>>,
|
|
||||||
path: P,
|
|
||||||
args: &[&str],
|
|
||||||
envs: &[&str],
|
|
||||||
) -> Result<LoadedProcess, Error> {
|
) -> Result<LoadedProcess, Error> {
|
||||||
let path = path.as_ref();
|
let path = options.path.as_ref();
|
||||||
let args = args.iter().map(|&s| s.to_owned()).collect();
|
let args = options.args.iter().map(|&s| s.to_owned()).collect();
|
||||||
let envs = envs.iter().map(|&s| s.to_owned()).collect();
|
let envs = options.envs.iter().map(|&s| s.to_owned()).collect();
|
||||||
|
|
||||||
let space = ProcessAddressSpace::new()?;
|
let space = ProcessAddressSpace::new()?;
|
||||||
let (image, args, envs) = xxx_load_program(&space, ioctx, path, args, envs)?;
|
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,
|
Some((_, name)) => name,
|
||||||
None => 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>>(
|
pub fn load_into<P: AsRef<Path>>(
|
||||||
process: &Process,
|
options: &LoadOptions<P>,
|
||||||
path: P,
|
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
envs: Vec<String>,
|
envs: Vec<String>,
|
||||||
) -> Result<(TaskContextImpl, ProcessImage), Error> {
|
) -> Result<(TaskContextImpl, ProcessImage), Error> {
|
||||||
|
let process = options.parent.as_ref().unwrap().upgrade().unwrap();
|
||||||
let mut io = process.io.lock();
|
let mut io = process.io.lock();
|
||||||
// Have to make the Path owned, going to drop the address space from which it came
|
// 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();
|
let space = process.space();
|
||||||
space.clear()?;
|
space.clear()?;
|
||||||
let (image, args, envs) = xxx_load_program(&space, io.ioctx_mut(), &path, args, envs)?;
|
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))
|
Ok((context, image))
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,17 @@ pub trait ForeignPointer: Sized {
|
|||||||
/// As this function allows direct memory writes, it is inherently unsafe.
|
/// 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 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:
|
/// Performs pointer validation for given address space:
|
||||||
///
|
///
|
||||||
/// * Checks if the pointer has proper alignment for the type.
|
/// * Checks if the pointer has proper alignment for the type.
|
||||||
@ -80,6 +91,15 @@ pub trait ForeignPointer: Sized {
|
|||||||
|
|
||||||
impl<T> ForeignPointer for T {
|
impl<T> ForeignPointer for T {
|
||||||
unsafe fn write_foreign_volatile(self: *mut Self, space: &ProcessAddressSpace, value: 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
|
// TODO check align
|
||||||
let addr = self as usize;
|
let addr = self as usize;
|
||||||
let start_page = addr & !0xFFF;
|
let start_page = addr & !0xFFF;
|
||||||
@ -90,12 +110,30 @@ impl<T> ForeignPointer for T {
|
|||||||
todo!("Foreign pointer write crossed a page boundary");
|
todo!("Foreign pointer write crossed a page boundary");
|
||||||
}
|
}
|
||||||
|
|
||||||
let phys_page = space
|
let phys_page = space.translate(start_page)?;
|
||||||
.translate(start_page)
|
|
||||||
.expect("Address is not mapped in the target address space");
|
|
||||||
|
|
||||||
let virt_ptr = phys_page.add(page_offset).virtualize() as *mut T;
|
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>(
|
unsafe fn validate_user_slice_mut<'a>(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use core::{cell::Cell, mem::size_of, ops::Deref};
|
use core::{cell::Cell, mem::size_of, ops::Deref};
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
collections::BTreeMap,
|
collections::{btree_map, BTreeMap, BTreeSet},
|
||||||
string::String,
|
string::String,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
@ -9,7 +9,7 @@ use crossbeam_queue::SegQueue;
|
|||||||
use futures_util::task::ArcWake;
|
use futures_util::task::ArcWake;
|
||||||
use kernel_arch::{
|
use kernel_arch::{
|
||||||
task::{Scheduler, TaskContext, TaskFrame},
|
task::{Scheduler, TaskContext, TaskFrame},
|
||||||
CpuImpl,
|
Architecture, ArchitectureImpl, CpuImpl,
|
||||||
};
|
};
|
||||||
use libk_mm::process::ProcessAddressSpace;
|
use libk_mm::process::ProcessAddressSpace;
|
||||||
use libk_util::{
|
use libk_util::{
|
||||||
@ -24,7 +24,7 @@ use yggdrasil_abi::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::task::{
|
use crate::task::{
|
||||||
mem::ForeignPointer,
|
mem::{self, ForeignPointer},
|
||||||
sched::CpuQueue,
|
sched::CpuQueue,
|
||||||
types::{ThreadAffinity, ThreadId, ThreadState},
|
types::{ThreadAffinity, ThreadId, ThreadState},
|
||||||
TaskContextImpl,
|
TaskContextImpl,
|
||||||
@ -32,6 +32,9 @@ use crate::task::{
|
|||||||
|
|
||||||
use super::{debug::ThreadDebugger, process::Process};
|
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
|
/// Provides details about how the thread is scheduled onto CPUs
|
||||||
pub struct ThreadSchedulingInfo {
|
pub struct ThreadSchedulingInfo {
|
||||||
/// Current state
|
/// Current state
|
||||||
@ -45,8 +48,10 @@ pub struct ThreadSchedulingInfo {
|
|||||||
|
|
||||||
pub struct ThreadDebuggingInfo {
|
pub struct ThreadDebuggingInfo {
|
||||||
pub single_step: bool,
|
pub single_step: bool,
|
||||||
|
pub restore_breakpoint: Option<usize>,
|
||||||
pub debugger: Option<ThreadDebugger>,
|
pub debugger: Option<ThreadDebugger>,
|
||||||
pub saved_frame: Option<SavedFrame>,
|
pub saved_frame: Option<SavedFrame>,
|
||||||
|
pub breakpoints: BTreeMap<usize, BreakpointType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SignalEntry {
|
struct SignalEntry {
|
||||||
@ -115,10 +120,13 @@ impl Thread {
|
|||||||
in_queue: false,
|
in_queue: false,
|
||||||
queue: None,
|
queue: None,
|
||||||
}),
|
}),
|
||||||
|
// TODO lazy initialization for debugging info
|
||||||
debug: IrqSafeSpinlock::new(ThreadDebuggingInfo {
|
debug: IrqSafeSpinlock::new(ThreadDebuggingInfo {
|
||||||
single_step: false,
|
single_step: false,
|
||||||
|
restore_breakpoint: None,
|
||||||
debugger: None,
|
debugger: None,
|
||||||
saved_frame: None,
|
saved_frame: None,
|
||||||
|
breakpoints: BTreeMap::new(),
|
||||||
}),
|
}),
|
||||||
context: Cell::new(context),
|
context: Cell::new(context),
|
||||||
process,
|
process,
|
||||||
@ -243,6 +251,46 @@ impl Thread {
|
|||||||
debug.debugger = Some(debugger);
|
debug.debugger = Some(debugger);
|
||||||
|
|
||||||
self.signal_queue.push(Signal::Debug);
|
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) {
|
pub fn resume(&self, single_step: bool) {
|
||||||
@ -453,23 +501,77 @@ impl CurrentThread {
|
|||||||
pub fn handle_single_step<F: TaskFrame>(&self, frame: &mut F) {
|
pub fn handle_single_step<F: TaskFrame>(&self, frame: &mut F) {
|
||||||
{
|
{
|
||||||
let mut debug = self.debug.lock();
|
let mut debug = self.debug.lock();
|
||||||
|
let space = self.address_space();
|
||||||
|
|
||||||
// Single step cleared
|
if let Some(original) = debug.restore_breakpoint.take() {
|
||||||
if !debug.single_step {
|
let brk_range = original..original + size_of::<BreakpointType>();
|
||||||
log::debug!("Clear single step ({} {:?})", self.id, self.name);
|
assert!(!brk_range.contains(&frame.user_ip()));
|
||||||
frame.set_single_step(false);
|
|
||||||
return;
|
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();
|
let frame = frame.store();
|
||||||
debug.saved_frame = Some(frame.clone());
|
debug.saved_frame = Some(frame.clone());
|
||||||
// TODO handle cases of detached debugger
|
// TODO handle cases of detached debugger
|
||||||
let debugger = debug.debugger.as_ref().unwrap();
|
let debugger = debug.debugger.as_ref().unwrap();
|
||||||
|
|
||||||
debugger.send(&DebugFrame::Step { frame }).ok();
|
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.
|
/// 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 let Some(signal) = self.signal_queue.pop() {
|
||||||
if signal == Signal::Debug {
|
if signal == Signal::Debug {
|
||||||
log::info!("Entered debug signal");
|
frame.set_single_step(true);
|
||||||
|
|
||||||
|
let frame = frame.store();
|
||||||
let mut debug = self.debug.lock();
|
let mut debug = self.debug.lock();
|
||||||
|
|
||||||
debug.single_step = true;
|
debug.single_step = true;
|
||||||
|
debug.saved_frame = Some(frame.clone());
|
||||||
|
|
||||||
let debugger = debug.debugger.as_ref().unwrap();
|
let debugger = debug.debugger.as_ref().unwrap();
|
||||||
let process = self.process();
|
let process = self.process();
|
||||||
|
|
||||||
frame.set_single_step(true);
|
debugger.send(&DebugFrame::Step { frame }).ok();
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -558,3 +652,28 @@ impl Deref for CurrentThread {
|
|||||||
&self.0
|
&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 {
|
fn size(&self) -> TerminalSize {
|
||||||
TerminalSize {
|
TerminalSize {
|
||||||
rows: 80,
|
rows: 24,
|
||||||
columns: 24,
|
columns: 80,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_size(&self, size: TerminalSize) -> Result<(), Error> {
|
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 = [
|
dependencies = [
|
||||||
"aarch64-cpu",
|
"aarch64-cpu",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"cc",
|
||||||
"device-api",
|
"device-api",
|
||||||
"kernel-arch-interface",
|
"kernel-arch-interface",
|
||||||
"libk-mm-interface",
|
"libk-mm-interface",
|
||||||
|
@ -149,6 +149,16 @@ impl Entry {
|
|||||||
|
|
||||||
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
|
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) -> ! {
|
fn kernel_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) -> ! {
|
||||||
let cr3 = CR3.get();
|
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) {
|
fn user_exception_inner(kind: ExceptionKind, frame: &mut ExceptionFrame) {
|
||||||
let thread = Thread::current();
|
let thread = Thread::current();
|
||||||
|
|
||||||
if kind != ExceptionKind::Debug {
|
let dump = match kind {
|
||||||
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 {
|
|
||||||
ExceptionKind::PageFault => {
|
ExceptionKind::PageFault => {
|
||||||
let cr2 = CR2.get();
|
|
||||||
warnln!("CR2 = {:#x}", cr2);
|
|
||||||
|
|
||||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
ExceptionKind::GeneralProtectionFault => {
|
ExceptionKind::GeneralProtectionFault => {
|
||||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
if thread.handle_breakpoint(frame) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ExceptionKind::InvalidOpcode => {
|
ExceptionKind::InvalidOpcode => {
|
||||||
thread.raise_signal(Signal::Aborted);
|
thread.raise_signal(Signal::Aborted);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
ExceptionKind::Debug => {
|
ExceptionKind::Debug => {
|
||||||
// TODO check if the thread was really in single-step mode or has debugging related to
|
// TODO check if the thread was really in single-step mode or has debugging related to
|
||||||
// the address in exception description
|
// the address in exception description
|
||||||
thread.handle_single_step(frame);
|
thread.handle_single_step(frame);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if dump {
|
||||||
|
dump_user_exception(kind, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,45 +113,54 @@ impl Entry {
|
|||||||
|
|
||||||
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
|
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) {
|
fn user_exception_inner(kind: ExceptionKind, frame: &mut ExceptionFrame) {
|
||||||
let thread = Thread::current();
|
let thread = Thread::current();
|
||||||
let cr3 = CR3.get();
|
|
||||||
|
|
||||||
if kind != ExceptionKind::Debug {
|
let dump = match kind {
|
||||||
warnln!("{:?} in {} {:?}", kind, thread.id, thread.name);
|
|
||||||
// XXX
|
|
||||||
// frame.dump(debug::LogLevel::Warning);
|
|
||||||
warnln!("CR3 = {:#x}", cr3);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::warn!("{:#x?}", frame);
|
|
||||||
|
|
||||||
match kind {
|
|
||||||
ExceptionKind::Debug => {
|
ExceptionKind::Debug => {
|
||||||
// TODO check if the thread was really in single-step mode or has debugging related to
|
// TODO check if the thread was really in single-step mode or has debugging related to
|
||||||
// the address in exception description
|
// the address in exception description
|
||||||
thread.handle_single_step(frame);
|
thread.handle_single_step(frame);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
ExceptionKind::PageFault => {
|
ExceptionKind::PageFault => {
|
||||||
let cr2 = CR2.get();
|
|
||||||
|
|
||||||
warnln!("CR2 = {:#x}", cr2);
|
|
||||||
|
|
||||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
ExceptionKind::GeneralProtectionFault => {
|
ExceptionKind::GeneralProtectionFault => {
|
||||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
if thread.handle_breakpoint(frame) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ExceptionKind::FpuException => {
|
ExceptionKind::FpuException => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
ExceptionKind::InvalidOpcode => {
|
ExceptionKind::InvalidOpcode => {
|
||||||
|
// TODO handle ud2 as breakpoint? (it's 2 bytes)
|
||||||
thread.raise_signal(Signal::Aborted);
|
thread.raise_signal(Signal::Aborted);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
ExceptionKind::Breakpoint => {
|
ExceptionKind::Breakpoint => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
_ => todo!("No handler for exception: {:?}", kind),
|
_ => todo!("No handler for exception: {:?}", kind),
|
||||||
|
};
|
||||||
|
|
||||||
|
if dump {
|
||||||
|
dump_user_exception(kind, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use kernel_fs::devfs;
|
|||||||
use libk::{
|
use libk::{
|
||||||
module::load_kernel_symbol_table,
|
module::load_kernel_symbol_table,
|
||||||
random,
|
random,
|
||||||
task::{process::Process, runtime, thread::Thread},
|
task::{binary::LoadOptions, process::Process, runtime, thread::Thread},
|
||||||
vfs::{impls::fn_symlink, IoContext, NodeRef},
|
vfs::{impls::fn_symlink, IoContext, NodeRef},
|
||||||
};
|
};
|
||||||
use memfs::MemoryFilesystem;
|
use memfs::MemoryFilesystem;
|
||||||
@ -75,8 +75,15 @@ pub fn kinit() -> Result<(), Error> {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let group_id = Process::create_group();
|
let group_id = Process::create_group();
|
||||||
let (user_init, user_init_main) =
|
let options = LoadOptions {
|
||||||
proc::load_binary(&mut ioctx, group_id, None, "/init", &["/init", "xxx"], &[])?;
|
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();
|
let mut io = user_init.io.lock();
|
||||||
io.set_ioctx(ioctx);
|
io.set_ioctx(ioctx);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use abi::{error::Error, path::Path, process::ProcessGroupId};
|
use abi::{error::Error, path::Path, process::ProcessGroupId};
|
||||||
use alloc::sync::{Arc, Weak};
|
use alloc::sync::{Arc, Weak};
|
||||||
use libk::{
|
use libk::{
|
||||||
task::{process::Process, thread::Thread},
|
task::{binary::LoadOptions, process::Process, thread::Thread},
|
||||||
vfs::IoContext,
|
vfs::IoContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -11,11 +11,7 @@ use libk::{
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn load_binary<P: AsRef<Path>>(
|
pub fn load_binary<P: AsRef<Path>>(
|
||||||
ioctx: &mut IoContext,
|
ioctx: &mut IoContext,
|
||||||
group_id: ProcessGroupId,
|
options: &LoadOptions<P>,
|
||||||
parent: Option<Weak<Process>>,
|
|
||||||
path: P,
|
|
||||||
args: &[&str],
|
|
||||||
envs: &[&str],
|
|
||||||
) -> Result<(Arc<Process>, Arc<Thread>), Error> {
|
) -> 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 = Process::get(pid).ok_or(Error::DoesNotExist)?;
|
||||||
let target_thread = target.as_single_thread().unwrap();
|
let target_thread = target.as_single_thread().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
&DebugOperation::Continue(single_step) => {
|
&mut DebugOperation::Continue(single_step) => {
|
||||||
// TODO check if it's paused currently
|
// TODO check if it's paused currently
|
||||||
target_thread.resume(single_step);
|
target_thread.resume(single_step);
|
||||||
Ok(())
|
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!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,10 @@ use abi::{
|
|||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use libk::{
|
use libk::{
|
||||||
block,
|
block,
|
||||||
task::{debug::ThreadDebugger, process::Process, runtime, thread::Thread, ThreadId},
|
task::{
|
||||||
|
binary::LoadOptions, debug::ThreadDebugger, process::Process, runtime, thread::Thread,
|
||||||
|
ThreadId,
|
||||||
|
},
|
||||||
vfs::IoContext,
|
vfs::IoContext,
|
||||||
};
|
};
|
||||||
use libk_mm::{
|
use libk_mm::{
|
||||||
@ -89,7 +92,6 @@ pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Err
|
|||||||
let process = thread.process();
|
let process = thread.process();
|
||||||
|
|
||||||
run_with_io(&process, |mut io| {
|
run_with_io(&process, |mut io| {
|
||||||
// let mut attach_debugger = None;
|
|
||||||
let attach_debugger = options
|
let attach_debugger = options
|
||||||
.optional
|
.optional
|
||||||
.iter()
|
.iter()
|
||||||
@ -105,14 +107,15 @@ pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Err
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Setup a new process from the file
|
// Setup a new process from the file
|
||||||
let (child_process, child_main) = proc::load_binary(
|
let load_options = LoadOptions {
|
||||||
io.ioctx_mut(),
|
group_id: process.group_id(),
|
||||||
process.group_id(),
|
parent: Some(Arc::downgrade(&process)),
|
||||||
Some(Arc::downgrade(&process)),
|
path: options.program,
|
||||||
options.program,
|
args: options.arguments,
|
||||||
options.arguments,
|
envs: options.arguments,
|
||||||
options.environment,
|
single_step: attach_debugger.is_some(),
|
||||||
)?;
|
};
|
||||||
|
let (child_process, child_main) = proc::load_binary(io.ioctx_mut(), &load_options)?;
|
||||||
let pid = child_process.id;
|
let pid = child_process.id;
|
||||||
|
|
||||||
// Inherit group and session from the creator
|
// 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 {
|
if let Some(debugger) = attach_debugger {
|
||||||
child_main.attach_debugger(ThreadDebugger::new(debugger));
|
child_main.attach_debugger(ThreadDebugger::new(debugger));
|
||||||
|
} else {
|
||||||
|
child_main.enqueue();
|
||||||
}
|
}
|
||||||
child_main.enqueue();
|
|
||||||
|
|
||||||
Ok(pid as _)
|
Ok(pid as _)
|
||||||
})
|
})
|
||||||
|
@ -138,4 +138,4 @@ syscall execve(options: &ExecveOptions<'_>) -> Result<()>;
|
|||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
syscall debug_trace(message: &str);
|
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)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use super::FrameOps;
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -10,3 +12,13 @@ pub struct SavedFrame {
|
|||||||
pub sp_el0: u64,
|
pub sp_el0: u64,
|
||||||
pub mdscr_el1: 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)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use super::FrameOps;
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -16,3 +18,13 @@ pub struct SavedFrame {
|
|||||||
pub user_sp: u32,
|
pub user_sp: u32,
|
||||||
pub eflags: 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;
|
use i686 as arch_impl;
|
||||||
|
|
||||||
pub use arch_impl::SavedFrame;
|
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)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use super::FrameOps;
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -25,3 +27,13 @@ pub struct SavedFrame {
|
|||||||
pub user_sp: u64,
|
pub user_sp: u64,
|
||||||
pub rflags: 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,
|
Interrupt,
|
||||||
Continue(bool),
|
Continue(bool),
|
||||||
|
SetBreakpoint(usize),
|
||||||
|
|
||||||
ReadMemory {
|
ReadMemory {
|
||||||
address: usize,
|
address: usize,
|
||||||
@ -25,11 +26,14 @@ pub enum DebugFrame {
|
|||||||
Startup {
|
Startup {
|
||||||
image_base: usize,
|
image_base: usize,
|
||||||
ip_offset: usize,
|
ip_offset: usize,
|
||||||
frame: SavedFrame,
|
ip: usize,
|
||||||
},
|
},
|
||||||
Step {
|
Step {
|
||||||
frame: SavedFrame,
|
frame: SavedFrame,
|
||||||
},
|
},
|
||||||
|
HitBreakpoint {
|
||||||
|
frame: SavedFrame,
|
||||||
|
},
|
||||||
// TODO exit status
|
// TODO exit status
|
||||||
Exited,
|
Exited,
|
||||||
}
|
}
|
||||||
|
7
userspace/Cargo.lock
generated
7
userspace/Cargo.lock
generated
@ -701,6 +701,7 @@ dependencies = [
|
|||||||
"iced-x86",
|
"iced-x86",
|
||||||
"libterm",
|
"libterm",
|
||||||
"rangemap",
|
"rangemap",
|
||||||
|
"rustc-demangle",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -729,6 +730,12 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"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]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.34"
|
version = "0.38.34"
|
||||||
|
@ -14,6 +14,7 @@ serde = { version = "1.0.193", features = ["derive"], default-features = false }
|
|||||||
thiserror = "1.0.58"
|
thiserror = "1.0.58"
|
||||||
elf = "0.7.4"
|
elf = "0.7.4"
|
||||||
rangemap = "1.5.1"
|
rangemap = "1.5.1"
|
||||||
|
rustc-demangle = "0.1.24"
|
||||||
|
|
||||||
[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dependencies]
|
[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dependencies]
|
||||||
iced-x86 = { version = "1.21.0", default-features = false, features = ["gas", "decoder", "std"] }
|
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 yggdrasil_abi::arch::SavedFrame;
|
||||||
|
|
||||||
|
use crate::debugger::SymbolResolver;
|
||||||
|
|
||||||
use super::{Error, InstructionFormatter, Target};
|
use super::{Error, InstructionFormatter, Target};
|
||||||
|
|
||||||
pub struct TargetImpl;
|
pub struct TargetImpl;
|
||||||
@ -38,7 +40,7 @@ impl Target for TargetImpl {
|
|||||||
) -> Result<Vec<(usize, Self::Instruction)>, Error> {
|
) -> Result<Vec<(usize, Self::Instruction)>, Error> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
fn new_instruction_formatter() -> Self::InstructionFormatter {
|
fn new_instruction_formatter(_resolver: SymbolResolver) -> Self::InstructionFormatter {
|
||||||
Unsupported
|
Unsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +48,7 @@ impl Target for TargetImpl {
|
|||||||
FlagFormat(frame.spsr_el1)
|
FlagFormat(frame.spsr_el1)
|
||||||
}
|
}
|
||||||
fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>) {
|
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((format!("x{}", i), frame.gp_regs[i]));
|
||||||
}
|
}
|
||||||
out.push(("pc".into(), frame.elr_el1 as _));
|
out.push(("pc".into(), frame.elr_el1 as _));
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
cell::{RefCell, RefMut},
|
||||||
|
collections::HashMap,
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufReader, Read, Seek, SeekFrom},
|
io::{BufReader, Read, Seek, SeekFrom},
|
||||||
@ -8,9 +10,10 @@ use std::{
|
|||||||
},
|
},
|
||||||
path::Path,
|
path::Path,
|
||||||
process::{Child, Command},
|
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 libterm::{Color, Term, TermKey};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
use yggdrasil_rt::{debug::DebugFrame, process::ProcessId};
|
use yggdrasil_rt::{debug::DebugFrame, process::ProcessId};
|
||||||
@ -19,12 +22,28 @@ use crate::state::State;
|
|||||||
use crate::InstructionFormatter;
|
use crate::InstructionFormatter;
|
||||||
use crate::{comm::Comm, Error, Target};
|
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> {
|
pub struct Debugger<T: Target> {
|
||||||
comm: Comm,
|
comm: Comm,
|
||||||
term: Term,
|
term: Term,
|
||||||
file: BufReader<File>,
|
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,
|
term_fd: RawFd,
|
||||||
comm_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> {
|
pub fn from_command<P: AsRef<Path>>(image: P, mut command: Command) -> Result<Self, Error> {
|
||||||
let image = image.as_ref();
|
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 file = BufReader::new(File::open(image)?);
|
||||||
let comm = Comm::open("rdb-1")?;
|
let comm = Comm::open("rdb-1")?;
|
||||||
@ -64,8 +83,10 @@ impl<T: Target> Debugger<T> {
|
|||||||
comm,
|
comm,
|
||||||
term,
|
term,
|
||||||
|
|
||||||
segment_headers,
|
image: image_info.into(),
|
||||||
segments: RangeMap::new(),
|
|
||||||
|
command: None,
|
||||||
|
status: None,
|
||||||
|
|
||||||
term_fd,
|
term_fd,
|
||||||
comm_fd,
|
comm_fd,
|
||||||
@ -79,11 +100,20 @@ impl<T: Target> Debugger<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_frame(&mut self, frame: DebugFrame) -> Result<(), Error> {
|
fn handle_frame(&mut self, frame: DebugFrame) -> Result<(), Error> {
|
||||||
|
self.status = None;
|
||||||
|
|
||||||
match frame {
|
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 pid = unsafe { ProcessId::from_raw(self.child.id()) };
|
||||||
let mut state = State::new(image_base, ip_offset, pid);
|
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);
|
self.state = Some(state);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -92,6 +122,12 @@ impl<T: Target> Debugger<T> {
|
|||||||
state.update(&frame, true)?;
|
state.update(&frame, true)?;
|
||||||
Ok(())
|
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 => {
|
DebugFrame::Exited => {
|
||||||
self.child_exited = true;
|
self.child_exited = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -100,22 +136,99 @@ impl<T: Target> Debugger<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key(&mut self, key: TermKey) -> Result<(), Error> {
|
fn handle_key(&mut self, key: TermKey) -> Result<(), Error> {
|
||||||
match key {
|
if let Some(command) = self.command.as_mut() {
|
||||||
TermKey::Char('q') => {
|
match key {
|
||||||
// TODO send exit to the child
|
TermKey::Char('\x7F') => {
|
||||||
// self.child.kill()?;
|
if !command.is_empty() {
|
||||||
todo!();
|
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') => {
|
} else {
|
||||||
// Send resume to the debugee
|
match key {
|
||||||
if let Some(state) = self.state.as_mut() {
|
TermKey::Char(':') => {
|
||||||
state.resume(true)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
TermKey::Char('c') => {
|
"read" | "r" if words.len() == 3 => {
|
||||||
if let Some(state) = self.state.as_mut() {
|
let ty = match words[1] {
|
||||||
state.resume(false)?;
|
"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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -128,10 +241,12 @@ impl<T: Target> Debugger<T> {
|
|||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut segments = self.image.segments.borrow_mut();
|
||||||
|
|
||||||
// Find segment
|
// 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,
|
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 start = header.p_vaddr as usize;
|
||||||
let end = (header.p_vaddr + header.p_memsz) as usize;
|
let end = (header.p_vaddr + header.p_memsz) as usize;
|
||||||
let mut buffer = vec![0; 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.seek(SeekFrom::Start(header.p_offset))?;
|
||||||
self.file.read_exact(&mut buffer)?;
|
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
|
// Outside of any segments
|
||||||
None => return Ok(vec![]),
|
None => return Ok(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset_within_segment = state.current_ip - range.start;
|
let offset_within_segment = state.current_ip - range.start;
|
||||||
|
let upper_limit = std::cmp::min(segment.len(), offset_within_segment + amount * 8);
|
||||||
|
|
||||||
T::disassemble(
|
T::disassemble(
|
||||||
&segment[offset_within_segment..offset_within_segment + amount * 8],
|
&segment[offset_within_segment..upper_limit],
|
||||||
state.current_ip + state.ip_offset,
|
state.current_ip + state.ip_offset,
|
||||||
amount,
|
amount,
|
||||||
)
|
)
|
||||||
@ -163,7 +279,7 @@ impl<T: Target> Debugger<T> {
|
|||||||
return Ok(0);
|
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![];
|
let mut gpregs = vec![];
|
||||||
T::register_list(&state.last_frame, &mut gpregs);
|
T::register_list(&state.last_frame, &mut gpregs);
|
||||||
let rows = 1 + (gpregs.len() + columns - 1) / columns;
|
let rows = 1 + (gpregs.len() + columns - 1) / columns;
|
||||||
@ -213,22 +329,31 @@ impl<T: Target> Debugger<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn redraw(&mut self) -> Result<(), Error> {
|
fn redraw(&mut self) -> Result<(), Error> {
|
||||||
|
let resolver = self.symbol_resolver();
|
||||||
let (width, height) = self.term.size()?;
|
let (width, height) = self.term.size()?;
|
||||||
self.term.clear(libterm::Clear::All)?;
|
self.term.clear(libterm::Clear::All)?;
|
||||||
|
|
||||||
// Show register block
|
// Show register block
|
||||||
let regs_rows = self.print_registers(width)?;
|
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() {
|
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();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
// Show disassembly block
|
// Show disassembly block
|
||||||
for (i, (ip, insn)) in disassembly.into_iter().enumerate() {
|
for (i, (ip, insn)) in disassembly.into_iter().enumerate() {
|
||||||
let is_current = i == 0;
|
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();
|
buffer.clear();
|
||||||
formatter.format_instruction(&insn, &mut buffer);
|
formatter.format_instruction(&insn, &mut buffer);
|
||||||
if is_current {
|
if is_current {
|
||||||
@ -256,7 +381,13 @@ impl<T: Target> Debugger<T> {
|
|||||||
write!(self.term, " Waiting for inferior process").ok();
|
write!(self.term, " Waiting for inferior process").ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO deconflict status and command
|
||||||
self.term.set_cursor_position(height - 1, 0)?;
|
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.reset_style()?;
|
||||||
self.term.flush()?;
|
self.term.flush()?;
|
||||||
|
|
||||||
@ -287,13 +418,75 @@ impl<T: Target> Debugger<T> {
|
|||||||
println!("Program exited with status {:?}", status);
|
println!("Program exited with status {:?}", status);
|
||||||
Ok(())
|
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 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 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() {
|
for seg in elf.segments() {
|
||||||
if seg.p_type != elf::abi::PT_LOAD {
|
if seg.p_type != elf::abi::PT_LOAD {
|
||||||
continue;
|
continue;
|
||||||
@ -305,5 +498,17 @@ fn extract_segments<P: AsRef<Path>>(path: P) -> Result<RangeMap<usize, ProgramHe
|
|||||||
ranges.insert(start..end, *seg);
|
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 std::{fmt::{LowerHex, Display}, io, path::PathBuf, process::Command};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use debugger::Debugger;
|
use debugger::{Debugger, SymbolResolver};
|
||||||
use imp::TargetImpl;
|
use imp::TargetImpl;
|
||||||
use yggdrasil_abi::arch::SavedFrame;
|
use yggdrasil_abi::arch::SavedFrame;
|
||||||
|
|
||||||
@ -26,6 +26,8 @@ pub enum Error {
|
|||||||
TermError(#[from] libterm::Error),
|
TermError(#[from] libterm::Error),
|
||||||
#[error("Debug control error: {0:?}")]
|
#[error("Debug control error: {0:?}")]
|
||||||
DebugError(yggdrasil_rt::Error),
|
DebugError(yggdrasil_rt::Error),
|
||||||
|
#[error("Invalid address: {0:?}")]
|
||||||
|
InvalidAddress(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Target {
|
pub trait Target {
|
||||||
@ -38,7 +40,7 @@ pub trait Target {
|
|||||||
ip: usize,
|
ip: usize,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
) -> Result<Vec<(usize, Self::Instruction)>, Error>;
|
) -> 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 register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>);
|
||||||
fn flags_register_as_display(frame: &SavedFrame) -> impl Display;
|
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> {
|
pub fn resume(&mut self, step: bool) -> Result<(), Error> {
|
||||||
unsafe {
|
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)
|
.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> {
|
pub fn update(&mut self, frame: &SavedFrame, _refresh: bool) -> Result<(), Error> {
|
||||||
let ip = T::real_ip(frame) - self.ip_offset;
|
let ip = T::real_ip(frame) - self.ip_offset;
|
||||||
|
self.update_ip(ip);
|
||||||
self.last_frame = frame.clone();
|
self.last_frame = frame.clone();
|
||||||
self.current_ip = ip;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display};
|
|||||||
use iced_x86::{Decoder, DecoderOptions, Formatter, GasFormatter, Instruction};
|
use iced_x86::{Decoder, DecoderOptions, Formatter, GasFormatter, Instruction};
|
||||||
use yggdrasil_abi::arch::SavedFrame;
|
use yggdrasil_abi::arch::SavedFrame;
|
||||||
|
|
||||||
use crate::{InstructionFormatter, Target};
|
use crate::{debugger::SymbolResolver, InstructionFormatter, Target};
|
||||||
|
|
||||||
#[cfg(any(target_pointer_width = "32", rust_analyzer))]
|
#[cfg(any(target_pointer_width = "32", rust_analyzer))]
|
||||||
const BITNESS: u32 = 32;
|
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 {
|
impl Target for TargetImpl {
|
||||||
type Instruction = Instruction;
|
type Instruction = Instruction;
|
||||||
type InstructionFormatter = GasFormatter;
|
type InstructionFormatter = GasFormatter;
|
||||||
@ -62,15 +84,14 @@ impl Target for TargetImpl {
|
|||||||
instructions.push((insn.ip() as usize, insn));
|
instructions.push((insn.ip() as usize, insn));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_trace!("{}", instructions.len());
|
|
||||||
Ok(instructions)
|
Ok(instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_instruction_formatter() -> Self::InstructionFormatter {
|
fn new_instruction_formatter(resolver: SymbolResolver) -> Self::InstructionFormatter {
|
||||||
let mut formatter = GasFormatter::new();
|
let mut formatter = GasFormatter::with_options(Some(Box::new(resolver)), None);
|
||||||
formatter.options_mut().set_uppercase_hex(false);
|
formatter.options_mut().set_uppercase_hex(false);
|
||||||
formatter.options_mut().set_branch_leading_zeros(false);
|
formatter.options_mut().set_branch_leading_zeros(false);
|
||||||
formatter
|
formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>) {
|
fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>) {
|
||||||
|
@ -94,3 +94,7 @@ path = "src/chmod.rs"
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "sysmon"
|
name = "sysmon"
|
||||||
path = "src/sysmon.rs"
|
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"),
|
("chmod", "bin/chmod"),
|
||||||
// ("sha256sum", "bin/sha256sum"),
|
// ("sha256sum", "bin/sha256sum"),
|
||||||
("sysmon", "bin/sysmon"),
|
("sysmon", "bin/sysmon"),
|
||||||
|
("tst", "bin/tst"),
|
||||||
// netutils
|
// netutils
|
||||||
("netconf", "sbin/netconf"),
|
("netconf", "sbin/netconf"),
|
||||||
("dhcp-client", "sbin/dhcp-client"),
|
("dhcp-client", "sbin/dhcp-client"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user