Compare commits
1 Commits
master
...
feature/debug
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ee2e0c5ab |
@@ -350,6 +350,7 @@ impl Thread {
|
|||||||
if debug.tracer.is_some() {
|
if debug.tracer.is_some() {
|
||||||
let timestamp = monotonic_time();
|
let timestamp = monotonic_time();
|
||||||
debug.store_state(frame);
|
debug.store_state(frame);
|
||||||
|
// log::info!("TRACE {payload:?}");
|
||||||
self.events.trace.write(TraceEvent {
|
self.events.trace.write(TraceEvent {
|
||||||
suspend,
|
suspend,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -513,7 +514,11 @@ impl Thread {
|
|||||||
|
|
||||||
/// Returns `true` if the thread is a tracee of given process
|
/// Returns `true` if the thread is a tracee of given process
|
||||||
pub fn is_tracee_of(&self, pid: ProcessId) -> bool {
|
pub fn is_tracee_of(&self, pid: ProcessId) -> bool {
|
||||||
self.debug.lock().tracer == Some(pid)
|
{
|
||||||
|
let tracer = self.debug.lock().tracer;
|
||||||
|
// log::info!("{:?}'s tracer: {:?}", self.id, tracer);
|
||||||
|
tracer == Some(pid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attach_trace(&self, tracer: ProcessId) -> Result<(), Error> {
|
pub fn attach_trace(&self, tracer: ProcessId) -> Result<(), Error> {
|
||||||
@@ -577,11 +582,13 @@ impl Thread {
|
|||||||
DebugControl::Detach => todo!(),
|
DebugControl::Detach => todo!(),
|
||||||
DebugControl::Resume => {
|
DebugControl::Resume => {
|
||||||
let single_step = debug::Resume::load_request(input)?;
|
let single_step = debug::Resume::load_request(input)?;
|
||||||
|
// log::info!("Resume single_step={single_step}");
|
||||||
self.resume(single_step);
|
self.resume(single_step);
|
||||||
debug::Resume::store_response(&(), buffer)
|
debug::Resume::store_response(&(), buffer)
|
||||||
}
|
}
|
||||||
DebugControl::SetTraceFlags => {
|
DebugControl::SetTraceFlags => {
|
||||||
let flags = debug::SetTraceFlags::load_request(buffer)?;
|
let flags = debug::SetTraceFlags::load_request(buffer)?;
|
||||||
|
// log::info!("SetTraceFlags {flags:?}");
|
||||||
self.set_trace_flags(flags);
|
self.set_trace_flags(flags);
|
||||||
debug::SetTraceFlags::store_response(&(), buffer)
|
debug::SetTraceFlags::store_response(&(), buffer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,10 @@ cfg_if::cfg_if! {
|
|||||||
if #[cfg(target_arch = "x86_64")] {
|
if #[cfg(target_arch = "x86_64")] {
|
||||||
extern crate ygg_driver_net_igbe;
|
extern crate ygg_driver_net_igbe;
|
||||||
} else if #[cfg(target_arch = "aarch64")] {
|
} else if #[cfg(target_arch = "aarch64")] {
|
||||||
|
extern crate ygg_driver_bsp_arm;
|
||||||
extern crate ygg_driver_bsp_bcm283x;
|
extern crate ygg_driver_bsp_bcm283x;
|
||||||
} else if #[cfg(target_arch = "riscv64")] {
|
} else if #[cfg(target_arch = "riscv64")] {
|
||||||
|
extern crate ygg_driver_bsp_riscv;
|
||||||
extern crate ygg_driver_bsp_jh7110;
|
extern crate ygg_driver_bsp_jh7110;
|
||||||
|
|
||||||
extern crate ygg_driver_net_stmmac;
|
extern crate ygg_driver_net_stmmac;
|
||||||
|
|||||||
Generated
+2
@@ -2970,6 +2970,8 @@ name = "strace"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"cross",
|
||||||
|
"libc",
|
||||||
"runtime",
|
"runtime",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
pub(crate) mod sys;
|
pub(crate) mod sys;
|
||||||
|
|
||||||
|
pub mod debug;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
|
|||||||
@@ -1,6 +1,64 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TraceEvent {
|
||||||
|
SyscallEntry(u64, [u64; 6]),
|
||||||
|
SyscallExit(i64),
|
||||||
|
Exited,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ChildTrace {
|
||||||
|
fn next_event(&mut self) -> io::Result<TraceEvent>;
|
||||||
|
fn resume(&mut self) -> io::Result<()>;
|
||||||
|
fn kill(&mut self) -> io::Result<()>;
|
||||||
|
|
||||||
|
unsafe fn peek_u8(&mut self, address: usize) -> io::Result<u8> {
|
||||||
|
let aligned = address & !7;
|
||||||
|
let shift = (address & 7) << 3;
|
||||||
|
let word = self.peek_u64(aligned)?;
|
||||||
|
Ok((word >> shift) as u8)
|
||||||
|
}
|
||||||
|
unsafe fn peek_u32(&mut self, address: usize) -> io::Result<u32> {
|
||||||
|
eprintln!("PEEK U32 @ {address:#x}");
|
||||||
|
assert_eq!(address & 3, 0);
|
||||||
|
let aligned = address & !7;
|
||||||
|
let shift = (address & 7) << 3;
|
||||||
|
let word = self.peek_u64(aligned)?;
|
||||||
|
Ok((word >> shift) as u32)
|
||||||
|
}
|
||||||
|
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64>;
|
||||||
|
|
||||||
|
unsafe fn peek_bytes(&mut self, address: usize, buffer: &mut [u8]) -> io::Result<()> {
|
||||||
|
for i in 0..buffer.len() {
|
||||||
|
buffer[i] = self.peek_u8(address + i)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn peek_cstr(&mut self, address: usize, buffer: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let mut len = 0;
|
||||||
|
while len < buffer.len() - 1 {
|
||||||
|
let byte = self.peek_u8(address + len)?;
|
||||||
|
buffer[len] = byte;
|
||||||
|
if byte == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn peek_usize(&mut self, address: usize) -> io::Result<usize> {
|
||||||
|
// FIXME I only support 64-bit archs
|
||||||
|
Ok(self.peek_u64(address)? as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait CommandSpawnExt {
|
pub trait CommandSpawnExt {
|
||||||
|
type ChildTrace: ChildTrace;
|
||||||
|
|
||||||
fn create_session(&mut self) -> io::Result<&mut Self>;
|
fn create_session(&mut self) -> io::Result<&mut Self>;
|
||||||
fn create_process_group(&mut self) -> io::Result<&mut Self>;
|
fn create_process_group(&mut self) -> io::Result<&mut Self>;
|
||||||
|
// TODO options for tracing across subchildren, etc
|
||||||
|
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ pub mod time;
|
|||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::c_int,
|
ffi::{c_int, c_void},
|
||||||
io,
|
io,
|
||||||
|
mem::MaybeUninit,
|
||||||
os::{fd::RawFd, unix::process::CommandExt},
|
os::{fd::RawFd, unix::process::CommandExt},
|
||||||
process::Command,
|
process::Command,
|
||||||
|
ptr::null_mut,
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,7 +28,126 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
|||||||
pub use term::RawStdinImpl;
|
pub use term::RawStdinImpl;
|
||||||
pub use timer::TimerFdImpl;
|
pub use timer::TimerFdImpl;
|
||||||
|
|
||||||
use crate::process::CommandSpawnExt;
|
use crate::process::{ChildTrace, CommandSpawnExt, TraceEvent};
|
||||||
|
|
||||||
|
pub struct ChildTraceImpl {
|
||||||
|
child: libc::pid_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChildTrace for ChildTraceImpl {
|
||||||
|
fn next_event(&mut self) -> io::Result<TraceEvent> {
|
||||||
|
let ev = loop {
|
||||||
|
unsafe {
|
||||||
|
let mut status = 0;
|
||||||
|
if libc::waitpid(self.child, &mut status, 0) < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
if libc::WIFSTOPPED(status) {
|
||||||
|
let stopsig = libc::WSTOPSIG(status);
|
||||||
|
if stopsig == libc::SIGTRAP | 0x80 {
|
||||||
|
let mut syscall_info = MaybeUninit::<libc::ptrace_syscall_info>::zeroed();
|
||||||
|
if libc::ptrace(
|
||||||
|
libc::PTRACE_GET_SYSCALL_INFO,
|
||||||
|
self.child,
|
||||||
|
size_of::<libc::ptrace_syscall_info>(),
|
||||||
|
syscall_info.as_mut_ptr(),
|
||||||
|
) < 0
|
||||||
|
{
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
let syscall_info = syscall_info.assume_init();
|
||||||
|
match syscall_info.op {
|
||||||
|
libc::PTRACE_SYSCALL_INFO_EXIT => {
|
||||||
|
break TraceEvent::SyscallExit(syscall_info.u.exit.sval);
|
||||||
|
}
|
||||||
|
libc::PTRACE_SYSCALL_INFO_ENTRY => {
|
||||||
|
break TraceEvent::SyscallEntry(
|
||||||
|
syscall_info.u.entry.nr,
|
||||||
|
syscall_info.u.entry.args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => self.resume()?,
|
||||||
|
}
|
||||||
|
} else if stopsig == libc::SIGTRAP {
|
||||||
|
let event = (status >> 16) & 0xffff;
|
||||||
|
eprintln!("event = {event}");
|
||||||
|
match event {
|
||||||
|
libc::PTRACE_EVENT_EXIT => {
|
||||||
|
// Ignore
|
||||||
|
eprintln!("PTRACE_EVENT_EXIT");
|
||||||
|
self.resume()?;
|
||||||
|
}
|
||||||
|
libc::PTRACE_EVENT_VFORK => {
|
||||||
|
eprintln!("PTRACE_EVENT_VFORK");
|
||||||
|
self.resume()?;
|
||||||
|
}
|
||||||
|
libc::PTRACE_EVENT_EXEC => {
|
||||||
|
// Ignore
|
||||||
|
eprintln!("PTRACE_EVENT_EXEC");
|
||||||
|
self.resume()?;
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!("signum == {stopsig}");
|
||||||
|
}
|
||||||
|
} else if libc::WIFEXITED(status) {
|
||||||
|
break TraceEvent::Exited;
|
||||||
|
} else {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill(&mut self) -> io::Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self) -> io::Result<()> {
|
||||||
|
if unsafe {
|
||||||
|
libc::ptrace(
|
||||||
|
libc::PTRACE_SYSCALL,
|
||||||
|
self.child,
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
)
|
||||||
|
} == 0
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64> {
|
||||||
|
libc::__errno_location().write_volatile(0);
|
||||||
|
let word = libc::ptrace(
|
||||||
|
libc::PTRACE_PEEKDATA,
|
||||||
|
self.child,
|
||||||
|
address,
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
);
|
||||||
|
if libc::__errno_location().read_volatile() != 0 {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(word as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ChildTraceImpl {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut ignore = 0;
|
||||||
|
unsafe {
|
||||||
|
libc::ptrace(libc::PTRACE_DETACH, self.child);
|
||||||
|
libc::waitpid(self.child, &mut ignore, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn dummy_sigint() {}
|
fn dummy_sigint() {}
|
||||||
|
|
||||||
@@ -61,6 +182,8 @@ pub fn clone_fd(fd: RawFd) -> io::Result<RawFd> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CommandSpawnExt for Command {
|
impl CommandSpawnExt for Command {
|
||||||
|
type ChildTrace = ChildTraceImpl;
|
||||||
|
|
||||||
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
||||||
Ok(self.process_group(0))
|
Ok(self.process_group(0))
|
||||||
}
|
}
|
||||||
@@ -69,4 +192,54 @@ impl CommandSpawnExt for Command {
|
|||||||
// TODO
|
// TODO
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace> {
|
||||||
|
unsafe {
|
||||||
|
match libc::fork() {
|
||||||
|
0 => {
|
||||||
|
// Child
|
||||||
|
// if libc::ptrace(libc::PTRACE_TRACEME, libc::getpid()) != 0 {
|
||||||
|
// panic!("PTRACE_TRACEME: {}", io::Error::last_os_error());
|
||||||
|
// }
|
||||||
|
libc::raise(libc::SIGSTOP);
|
||||||
|
panic!("exec error: {}", self.exec());
|
||||||
|
}
|
||||||
|
code if code < 0 => {
|
||||||
|
// Error
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
}
|
||||||
|
pid => {
|
||||||
|
let options = libc::PTRACE_O_TRACESYSGOOD | libc::PTRACE_O_TRACEEXEC;
|
||||||
|
let mut ignore = 0;
|
||||||
|
// Parent
|
||||||
|
if libc::ptrace(
|
||||||
|
libc::PTRACE_SEIZE,
|
||||||
|
pid,
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
) != 0
|
||||||
|
{
|
||||||
|
// TODO waitpid child? kill child?
|
||||||
|
eprintln!("PTRACE_SEIZE error");
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
if libc::waitpid(pid, &mut ignore, 0) < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
if libc::ptrace(libc::PTRACE_SETOPTIONS, pid, null_mut::<c_void>(), options)
|
||||||
|
!= 0
|
||||||
|
{
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
libc::ptrace(
|
||||||
|
libc::PTRACE_CONT,
|
||||||
|
pid,
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
null_mut::<c_void>(),
|
||||||
|
);
|
||||||
|
Ok(ChildTraceImpl { child: pid })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,20 @@ pub mod timer;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
os::{fd::RawFd, yggdrasil::process::CommandExt},
|
mem::MaybeUninit,
|
||||||
process::Command,
|
os::{
|
||||||
|
fd::RawFd,
|
||||||
|
yggdrasil::process::{ChildExt, CommandExt},
|
||||||
|
},
|
||||||
|
process::{Child, Command},
|
||||||
|
};
|
||||||
|
|
||||||
|
use runtime::{
|
||||||
|
abi::{
|
||||||
|
arch::SavedFrame,
|
||||||
|
debug::{self, TraceFlags},
|
||||||
|
},
|
||||||
|
rt::{debug::debug_control, process::ThreadEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use mem::{FileMappingImpl, SharedMemoryImpl};
|
pub use mem::{FileMappingImpl, SharedMemoryImpl};
|
||||||
@@ -24,7 +36,131 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
|||||||
pub use term::RawStdinImpl;
|
pub use term::RawStdinImpl;
|
||||||
pub use timer::TimerFdImpl;
|
pub use timer::TimerFdImpl;
|
||||||
|
|
||||||
use crate::process::CommandSpawnExt;
|
use crate::process::{ChildTrace, CommandSpawnExt, TraceEvent};
|
||||||
|
|
||||||
|
pub struct ChildTraceImpl {
|
||||||
|
#[allow(unused)]
|
||||||
|
child: Child,
|
||||||
|
buffer: [u8; 512],
|
||||||
|
tid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChildTraceImpl {
|
||||||
|
fn attach(child: Child) -> io::Result<Self> {
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
let tid = child.main_thread_id()?;
|
||||||
|
|
||||||
|
debug_control::<debug::SetTraceFlags>(
|
||||||
|
tid,
|
||||||
|
&mut buffer,
|
||||||
|
&(TraceFlags::SYSCALL_ENTRY | TraceFlags::SYSCALL_EXIT),
|
||||||
|
)
|
||||||
|
.map_err(io::Error::from)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(Self { child, tid, buffer })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_regs(&mut self) -> io::Result<SavedFrame> {
|
||||||
|
debug_control::<debug::GetRegisters>(self.tid, &mut self.buffer, &())
|
||||||
|
.map_err(io::Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
|
||||||
|
impl ChildTraceImpl {
|
||||||
|
fn read_syscall_entry(&mut self) -> io::Result<(u64, [u64; 6])> {
|
||||||
|
let frame = self.read_regs()?;
|
||||||
|
Ok((
|
||||||
|
frame.gp_regs[8] as _,
|
||||||
|
[
|
||||||
|
frame.gp_regs[0] as _,
|
||||||
|
frame.gp_regs[1] as _,
|
||||||
|
frame.gp_regs[2] as _,
|
||||||
|
frame.gp_regs[3] as _,
|
||||||
|
frame.gp_regs[4] as _,
|
||||||
|
frame.gp_regs[5] as _,
|
||||||
|
],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_syscall_exit(&mut self) -> io::Result<u64> {
|
||||||
|
let frame = self.read_regs()?;
|
||||||
|
Ok(frame.gp_regs[0] as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(rust_analyzer, target_arch = "x86_64"))]
|
||||||
|
impl ChildTraceImpl {
|
||||||
|
fn read_syscall_entry(&mut self) -> io::Result<(u64, [u64; 6])> {
|
||||||
|
let frame = self.read_regs()?;
|
||||||
|
Ok((
|
||||||
|
frame.rax as _,
|
||||||
|
[
|
||||||
|
frame.rdi as _,
|
||||||
|
frame.rsi as _,
|
||||||
|
frame.rdx as _,
|
||||||
|
frame.r10 as _,
|
||||||
|
frame.r8 as _,
|
||||||
|
frame.r9 as _,
|
||||||
|
],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_syscall_exit(&mut self) -> io::Result<u64> {
|
||||||
|
let frame = self.read_regs()?;
|
||||||
|
Ok(frame.rax as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChildTrace for ChildTraceImpl {
|
||||||
|
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64> {
|
||||||
|
let word = debug_control::<debug::ReadMemory>(self.tid, &mut self.buffer, &address)?;
|
||||||
|
Ok(word as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill(&mut self) -> io::Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self) -> io::Result<()> {
|
||||||
|
debug_control::<debug::Resume>(self.tid, &mut self.buffer, &false).map_err(io::Error::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_event(&mut self) -> io::Result<TraceEvent> {
|
||||||
|
loop {
|
||||||
|
let mut event = MaybeUninit::uninit();
|
||||||
|
unsafe { runtime::rt::sys::wait_thread(self.tid, &mut event) }
|
||||||
|
.map_err(io::Error::from)?;
|
||||||
|
let event = unsafe { event.assume_init() };
|
||||||
|
|
||||||
|
match event {
|
||||||
|
ThreadEvent::Trace(trace) => {
|
||||||
|
let event = match trace.payload {
|
||||||
|
debug::TraceEventPayload::SyscallEntry(_seq) => {
|
||||||
|
let (nr, args) = self.read_syscall_entry()?;
|
||||||
|
TraceEvent::SyscallEntry(nr, args)
|
||||||
|
}
|
||||||
|
debug::TraceEventPayload::SyscallExit(_seq) => {
|
||||||
|
let status = self.read_syscall_exit()?;
|
||||||
|
TraceEvent::SyscallExit(status as _)
|
||||||
|
}
|
||||||
|
debug::TraceEventPayload::SingleStep => {
|
||||||
|
self.resume()?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
break Ok(event);
|
||||||
|
}
|
||||||
|
ThreadEvent::Exited => {
|
||||||
|
// TODO status
|
||||||
|
break Ok(TraceEvent::Exited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_sigint_handler(_handler: fn()) {}
|
pub fn set_sigint_handler(_handler: fn()) {}
|
||||||
|
|
||||||
@@ -45,6 +181,13 @@ pub fn clone_fd(fd: RawFd) -> io::Result<RawFd> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CommandSpawnExt for Command {
|
impl CommandSpawnExt for Command {
|
||||||
|
type ChildTrace = ChildTraceImpl;
|
||||||
|
|
||||||
|
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace> {
|
||||||
|
unsafe { self.attach_tracing() };
|
||||||
|
self.spawn().and_then(ChildTraceImpl::attach)
|
||||||
|
}
|
||||||
|
|
||||||
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
||||||
let group = unsafe { runtime::rt::sys::create_process_group() };
|
let group = unsafe { runtime::rt::sys::create_process_group() };
|
||||||
Ok(self.process_group(group))
|
Ok(self.process_group(group))
|
||||||
|
|||||||
@@ -1,68 +1,3 @@
|
|||||||
#![feature(yggdrasil_os, if_let_guard)]
|
#![feature(yggdrasil_os, if_let_guard)]
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
// use std::{fmt::{LowerHex, Display}, io, path::PathBuf, process::Command};
|
|
||||||
//
|
|
||||||
// use clap::Parser;
|
|
||||||
// use debugger::{Debugger, SymbolResolver};
|
|
||||||
// use imp::TargetImpl;
|
|
||||||
// use yggdrasil_abi::arch::SavedFrame;
|
|
||||||
//
|
|
||||||
// #[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))]
|
|
||||||
// #[path = "x86.rs"]
|
|
||||||
// pub mod imp;
|
|
||||||
// #[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
|
||||||
// #[path = "aarch64.rs"]
|
|
||||||
// pub mod imp;
|
|
||||||
//
|
|
||||||
// pub mod comm;
|
|
||||||
// pub mod state;
|
|
||||||
//
|
|
||||||
// pub mod debugger;
|
|
||||||
//
|
|
||||||
// #[derive(thiserror::Error, Debug)]
|
|
||||||
// pub enum Error {
|
|
||||||
// #[error("I/O error: {0}")]
|
|
||||||
// IoError(#[from] io::Error),
|
|
||||||
// #[error("Terminal error: {0}")]
|
|
||||||
// TermError(#[from] libterm::Error),
|
|
||||||
// #[error("Debug control error: {0:?}")]
|
|
||||||
// DebugError(yggdrasil_rt::Error),
|
|
||||||
// #[error("Invalid address: {0:?}")]
|
|
||||||
// InvalidAddress(String)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub trait Target {
|
|
||||||
// type Instruction;
|
|
||||||
// type InstructionFormatter: InstructionFormatter<Self::Instruction>;
|
|
||||||
// type Register: LowerHex;
|
|
||||||
//
|
|
||||||
// fn disassemble(
|
|
||||||
// window: &[u8],
|
|
||||||
// ip: usize,
|
|
||||||
// limit: usize,
|
|
||||||
// ) -> Result<Vec<(usize, Self::Instruction)>, Error>;
|
|
||||||
// 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;
|
|
||||||
// fn real_ip(frame: &SavedFrame) -> usize;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub trait InstructionFormatter<I> {
|
|
||||||
// fn format_instruction(&mut self, insn: &I, out: &mut String);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[derive(Parser)]
|
|
||||||
// struct Args {
|
|
||||||
// program: PathBuf,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn main() {
|
|
||||||
// let args = Args::parse();
|
|
||||||
//
|
|
||||||
// let command = Command::new(&args.program);
|
|
||||||
// let debug: Debugger<TargetImpl> = Debugger::from_command(&args.program, command).unwrap();
|
|
||||||
//
|
|
||||||
// debug.run().unwrap();
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -4,9 +4,19 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
runtime.workspace = true
|
cross.workspace = true
|
||||||
|
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "yggdrasil")'.dependencies]
|
||||||
|
runtime.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
libc = "*"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
runtime.workspace = true
|
||||||
|
libc = "*"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
#[cfg(any(rust_analyzer, target_arch = "x86_64"))]
|
||||||
|
#[path = "x86_64.rs"]
|
||||||
|
pub mod imp;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub use imp::*;
|
||||||
|
use libc::{c_int, c_uint};
|
||||||
|
|
||||||
|
macro_rules! impl_ioctls {
|
||||||
|
($f:expr, $v:expr, $ty:ty, [$($ioctl:ident),* $(,)?]) => {{
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
let v = match $v as $ty {
|
||||||
|
$(libc::$ioctl => stringify!($ioctl),)*
|
||||||
|
_ => return write!($f, "{:#x}", $v),
|
||||||
|
};
|
||||||
|
write!($f, "{v}")
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_ioctl<W: fmt::Write>(f: &mut W, v: c_uint) -> fmt::Result {
|
||||||
|
impl_ioctls!(
|
||||||
|
f,
|
||||||
|
v,
|
||||||
|
u64,
|
||||||
|
[
|
||||||
|
TCGETS,
|
||||||
|
TCSETS,
|
||||||
|
TCSETSW,
|
||||||
|
TCSETSF,
|
||||||
|
TCGETA,
|
||||||
|
TCSETA,
|
||||||
|
TCSETAW,
|
||||||
|
TCSETAF,
|
||||||
|
TCSBRK,
|
||||||
|
TCXONC,
|
||||||
|
TCFLSH,
|
||||||
|
TIOCEXCL,
|
||||||
|
TIOCNXCL,
|
||||||
|
TIOCSCTTY,
|
||||||
|
TIOCGPGRP,
|
||||||
|
TIOCSPGRP,
|
||||||
|
TIOCOUTQ,
|
||||||
|
TIOCSTI,
|
||||||
|
TIOCGWINSZ,
|
||||||
|
TIOCSWINSZ,
|
||||||
|
TIOCMGET,
|
||||||
|
TIOCMBIS,
|
||||||
|
TIOCMBIC,
|
||||||
|
TIOCMSET,
|
||||||
|
TIOCGSOFTCAR,
|
||||||
|
TIOCSSOFTCAR,
|
||||||
|
FIONREAD,
|
||||||
|
TIOCLINUX,
|
||||||
|
TIOCCONS,
|
||||||
|
TIOCGSERIAL,
|
||||||
|
TIOCSSERIAL,
|
||||||
|
TIOCPKT,
|
||||||
|
FIONBIO,
|
||||||
|
TIOCNOTTY,
|
||||||
|
TIOCSETD,
|
||||||
|
TIOCGETD,
|
||||||
|
TCSBRKP,
|
||||||
|
TIOCSBRK,
|
||||||
|
TIOCCBRK,
|
||||||
|
TIOCGSID,
|
||||||
|
TCGETS2,
|
||||||
|
TCSETS2,
|
||||||
|
TCSETSW2,
|
||||||
|
TCSETSF2,
|
||||||
|
TIOCGRS485,
|
||||||
|
TIOCSRS485,
|
||||||
|
TIOCGPTN,
|
||||||
|
TIOCSPTLCK,
|
||||||
|
TIOCGDEV,
|
||||||
|
TCGETX,
|
||||||
|
TCSETX,
|
||||||
|
TCSETXF,
|
||||||
|
TCSETXW,
|
||||||
|
TIOCSIG,
|
||||||
|
TIOCVHANGUP,
|
||||||
|
TIOCGPKT,
|
||||||
|
TIOCGPTLCK,
|
||||||
|
TIOCGEXCL,
|
||||||
|
TIOCGPTPEER,
|
||||||
|
FIONCLEX,
|
||||||
|
FIOCLEX,
|
||||||
|
FIOASYNC,
|
||||||
|
TIOCSERCONFIG,
|
||||||
|
TIOCSERGWILD,
|
||||||
|
TIOCSERSWILD,
|
||||||
|
TIOCGLCKTRMIOS,
|
||||||
|
TIOCSLCKTRMIOS,
|
||||||
|
TIOCSERGSTRUCT,
|
||||||
|
TIOCSERGETLSR,
|
||||||
|
TIOCSERGETMULTI,
|
||||||
|
TIOCSERSETMULTI,
|
||||||
|
TIOCMIWAIT,
|
||||||
|
TIOCGICOUNT,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_prctl<W: fmt::Write>(f: &mut W, c: c_int) -> fmt::Result {
|
||||||
|
impl_ioctls!(
|
||||||
|
f,
|
||||||
|
c,
|
||||||
|
c_int,
|
||||||
|
[
|
||||||
|
PR_SET_PDEATHSIG,
|
||||||
|
PR_GET_PDEATHSIG,
|
||||||
|
PR_GET_DUMPABLE,
|
||||||
|
PR_SET_DUMPABLE,
|
||||||
|
PR_GET_UNALIGN,
|
||||||
|
PR_SET_UNALIGN,
|
||||||
|
PR_GET_KEEPCAPS,
|
||||||
|
PR_SET_KEEPCAPS,
|
||||||
|
PR_GET_FPEMU,
|
||||||
|
PR_SET_FPEMU,
|
||||||
|
PR_GET_FPEXC,
|
||||||
|
PR_SET_FPEXC,
|
||||||
|
PR_GET_TIMING,
|
||||||
|
PR_SET_TIMING,
|
||||||
|
PR_SET_NAME,
|
||||||
|
PR_GET_NAME,
|
||||||
|
PR_GET_ENDIAN,
|
||||||
|
PR_SET_ENDIAN,
|
||||||
|
PR_GET_SECCOMP,
|
||||||
|
PR_SET_SECCOMP,
|
||||||
|
PR_CAPBSET_READ,
|
||||||
|
PR_CAPBSET_DROP,
|
||||||
|
PR_GET_TSC,
|
||||||
|
PR_SET_TSC,
|
||||||
|
PR_GET_SECUREBITS,
|
||||||
|
PR_SET_SECUREBITS,
|
||||||
|
PR_SET_TIMERSLACK,
|
||||||
|
PR_GET_TIMERSLACK,
|
||||||
|
PR_TASK_PERF_EVENTS_DISABLE,
|
||||||
|
PR_TASK_PERF_EVENTS_ENABLE,
|
||||||
|
PR_MCE_KILL,
|
||||||
|
PR_SET_MM,
|
||||||
|
PR_SET_PTRACER,
|
||||||
|
PR_SET_CHILD_SUBREAPER,
|
||||||
|
PR_GET_CHILD_SUBREAPER,
|
||||||
|
PR_SET_NO_NEW_PRIVS,
|
||||||
|
PR_GET_NO_NEW_PRIVS,
|
||||||
|
PR_GET_TID_ADDRESS,
|
||||||
|
PR_SET_THP_DISABLE,
|
||||||
|
PR_GET_THP_DISABLE,
|
||||||
|
PR_MPX_ENABLE_MANAGEMENT,
|
||||||
|
PR_MPX_DISABLE_MANAGEMENT,
|
||||||
|
PR_SET_FP_MODE,
|
||||||
|
PR_GET_FP_MODE,
|
||||||
|
PR_CAP_AMBIENT,
|
||||||
|
PR_GET_SPECULATION_CTRL,
|
||||||
|
PR_SET_SPECULATION_CTRL,
|
||||||
|
PR_SCHED_CORE,
|
||||||
|
PR_SET_MDWE,
|
||||||
|
PR_GET_MDWE,
|
||||||
|
PR_SET_VMA,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_access_mode<W: fmt::Write>(f: &mut W, mode: c_int) -> fmt::Result {
|
||||||
|
let s = match mode {
|
||||||
|
libc::F_OK => "F_OK",
|
||||||
|
libc::W_OK => "W_OK",
|
||||||
|
libc::R_OK => "R_OK",
|
||||||
|
libc::X_OK => "X_OK",
|
||||||
|
_ => return write!(f, "{mode:#x}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{s}")
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
use std::{ffi::c_void, fmt};
|
||||||
|
|
||||||
|
use cross::process::ChildTrace;
|
||||||
|
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||||
|
|
||||||
|
use crate::definition::{
|
||||||
|
imp::{print_access_mode, print_ioctl, print_prctl},
|
||||||
|
print_cstring, PrintSyscallArgument, Syscall,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum SyscallArgument {
|
||||||
|
Fd(c_int),
|
||||||
|
MaybeFd(c_int),
|
||||||
|
Bytes(*const c_char),
|
||||||
|
BytesMut(*const c_char),
|
||||||
|
Size(usize),
|
||||||
|
Filename(*const c_char),
|
||||||
|
OpenOptions(c_int),
|
||||||
|
FileMode(c_int),
|
||||||
|
Ptr(*const c_void),
|
||||||
|
Long(c_long),
|
||||||
|
ULong(c_ulong),
|
||||||
|
Int(c_int),
|
||||||
|
UInt(c_uint),
|
||||||
|
Whence(c_int),
|
||||||
|
Off(libc::off_t),
|
||||||
|
LOff(libc::loff_t),
|
||||||
|
IoctlCmd(c_uint),
|
||||||
|
PrctlCmd(c_int),
|
||||||
|
AccessMode(c_int),
|
||||||
|
AtFd(c_int),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrintSyscallArgument for SyscallArgument {
|
||||||
|
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||||
|
match self {
|
||||||
|
Self::Fd(libc::STDIN_FILENO) => write!(f, "STDIN_FILENO"),
|
||||||
|
Self::Fd(libc::STDOUT_FILENO) => write!(f, "STDOUT_FILENO"),
|
||||||
|
Self::Fd(libc::STDERR_FILENO) => write!(f, "STDERR_FILENO"),
|
||||||
|
Self::Fd(fd) => write!(f, "{fd}"),
|
||||||
|
Self::MaybeFd(-1) => write!(f, "<none>"),
|
||||||
|
Self::MaybeFd(fd) => write!(f, "{fd}"),
|
||||||
|
Self::Bytes(bytes) => write!(f, "{bytes:p}"),
|
||||||
|
Self::BytesMut(bytes) => write!(f, "{bytes:p}"),
|
||||||
|
Self::Size(size) if *size <= 999 => write!(f, "{size}"),
|
||||||
|
Self::Size(size) => write!(f, "{size:#x}"),
|
||||||
|
Self::Filename(filename) => print_cstring(f, trace, filename.addr()),
|
||||||
|
Self::OpenOptions(opts) => write!(f, "{opts:#x}"),
|
||||||
|
Self::FileMode(mode) => write!(f, "0{mode:o}"),
|
||||||
|
Self::Ptr(ptr) => write!(f, "{ptr:p}"),
|
||||||
|
Self::Long(val) => write!(f, "{val}"),
|
||||||
|
Self::ULong(val) => write!(f, "{val}"),
|
||||||
|
Self::Int(val) => write!(f, "{val}"),
|
||||||
|
Self::UInt(val) => write!(f, "{val}"),
|
||||||
|
Self::Whence(libc::SEEK_SET) => write!(f, "SEEK_SET"),
|
||||||
|
Self::Whence(libc::SEEK_CUR) => write!(f, "SEEK_CUR"),
|
||||||
|
Self::Whence(libc::SEEK_END) => write!(f, "SEEK_END"),
|
||||||
|
Self::Whence(w) => write!(f, "{w}"),
|
||||||
|
Self::Off(off) => write!(f, "{off}"),
|
||||||
|
Self::LOff(off) => write!(f, "{off}"),
|
||||||
|
&Self::IoctlCmd(v) => print_ioctl(f, v),
|
||||||
|
&Self::PrctlCmd(v) => print_prctl(f, v),
|
||||||
|
&Self::AccessMode(m) => print_access_mode(f, m),
|
||||||
|
Self::AtFd(libc::AT_FDCWD) => write!(f, "AT_FDCWD"),
|
||||||
|
Self::AtFd(fd) => write!(f, "{fd}"),
|
||||||
|
}
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_syscall_args {
|
||||||
|
($input:expr, [$($collect:expr),*], $i:expr, [$arg:ident, $($tail:ident),+]) => {
|
||||||
|
impl_syscall_args!($input, [$($collect,)* (SyscallArgument::$arg($input[$i] as _))], $i + 1, [$($tail),+])
|
||||||
|
};
|
||||||
|
($input:expr, [$($collect:expr),*], $i:expr, [$arg:ident]) => {
|
||||||
|
vec![$($collect,)* (SyscallArgument::$arg($input[$i] as _))]
|
||||||
|
};
|
||||||
|
($input:expr, [], $i:expr, []) => {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_syscall_parse {
|
||||||
|
($(
|
||||||
|
$nr:literal => $name:ident ( $($arg:ident),* )
|
||||||
|
),+ $(,)?) => {
|
||||||
|
pub fn parse_syscall(nr: u64, args: &[u64]) -> Option<Syscall> {
|
||||||
|
Some(match nr {
|
||||||
|
$($nr => Syscall(
|
||||||
|
stringify!($name),
|
||||||
|
impl_syscall_args!(args, [], 0, [$($arg),*])
|
||||||
|
),)+
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_syscall_parse!(
|
||||||
|
0 => read(Fd, BytesMut, Size),
|
||||||
|
1 => write(Fd, Bytes, Size),
|
||||||
|
2 => open(Filename, OpenOptions, FileMode),
|
||||||
|
3 => close(Fd),
|
||||||
|
4 => stat(Filename, BytesMut),
|
||||||
|
5 => fstat(Fd, BytesMut),
|
||||||
|
6 => lstat(Filename, BytesMut),
|
||||||
|
7 => poll(Ptr, ULong, Long),
|
||||||
|
8 => lseek(Fd, Off, Whence),
|
||||||
|
9 => mmap(Ptr, ULong, ULong, ULong, MaybeFd, ULong),
|
||||||
|
10 => mprotect(Ptr, Size, ULong),
|
||||||
|
11 => munmap(Ptr, Size),
|
||||||
|
12 => brk(Ptr),
|
||||||
|
// TODO rt_...
|
||||||
|
16 => ioctl(Fd, IoctlCmd, ULong),
|
||||||
|
17 => pread64(Fd, BytesMut, Size, LOff),
|
||||||
|
21 => access(Filename, AccessMode),
|
||||||
|
157 => prctl(PrctlCmd, ULong, ULong, ULong),
|
||||||
|
218 => set_tid_address(Ptr),
|
||||||
|
334 => rseq(Ptr, UInt, Int, UInt),
|
||||||
|
257 => openat(AtFd, Filename, OpenOptions, FileMode),
|
||||||
|
262 => newfstatat(AtFd, Filename, Ptr, Int),
|
||||||
|
24 => sched_yield(),
|
||||||
|
);
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use cross::process::ChildTrace;
|
||||||
|
|
||||||
|
#[cfg(any(rust_analyzer, target_os = "linux"))]
|
||||||
|
#[path = "linux/mod.rs"]
|
||||||
|
pub mod imp;
|
||||||
|
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
||||||
|
#[path = "yggdrasil.rs"]
|
||||||
|
pub mod imp;
|
||||||
|
|
||||||
|
pub use imp::*;
|
||||||
|
|
||||||
|
pub trait PrintSyscallArgument {
|
||||||
|
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Syscall(pub &'static str, pub Vec<SyscallArgument>);
|
||||||
|
|
||||||
|
impl Syscall {
|
||||||
|
pub fn print<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||||
|
write!(f, "{}(", self.0).ok();
|
||||||
|
for (i, arg) in self.1.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, ", ").ok();
|
||||||
|
}
|
||||||
|
arg.print_syscall_argument(f, trace);
|
||||||
|
}
|
||||||
|
write!(f, ")").ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_string<W: fmt::Write, T: ChildTrace>(
|
||||||
|
f: &mut W,
|
||||||
|
trace: &mut T,
|
||||||
|
address: usize,
|
||||||
|
len: usize,
|
||||||
|
) -> fmt::Result {
|
||||||
|
let mut buffer = [0; 64];
|
||||||
|
let len = len.min(buffer.len());
|
||||||
|
if let Ok(()) = unsafe { trace.peek_bytes(address, &mut buffer[..len]) } {
|
||||||
|
if let Ok(s) = std::str::from_utf8(&buffer[..len]) {
|
||||||
|
write!(f, "{s:?}")
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(f, "<error str {address:#x}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_cstring<W: fmt::Write, T: ChildTrace>(
|
||||||
|
f: &mut W,
|
||||||
|
trace: &mut T,
|
||||||
|
address: usize,
|
||||||
|
) -> fmt::Result {
|
||||||
|
let mut buffer = [0; 64];
|
||||||
|
if let Ok(len) = unsafe { trace.peek_cstr(address, &mut buffer) } {
|
||||||
|
if let Ok(s) = std::str::from_utf8(&buffer[..len]) {
|
||||||
|
write!(f, "{s:?}")
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(f, "<error str {address:#x}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use cross::process::ChildTrace;
|
||||||
|
use runtime::{abi::SyscallFunction, abi_lib::SyscallRegister, rt::io::RawFd};
|
||||||
|
|
||||||
|
use crate::definition::{print_string, PrintSyscallArgument, Syscall};
|
||||||
|
|
||||||
|
pub enum SyscallArgument {
|
||||||
|
Bytes(u64, u64),
|
||||||
|
BytesMut(u64, u64),
|
||||||
|
MaybePtr(u64),
|
||||||
|
Fd(u64),
|
||||||
|
OptionFd(u64),
|
||||||
|
Ptr(u64),
|
||||||
|
Size(u64),
|
||||||
|
U64(u64),
|
||||||
|
I64(i64),
|
||||||
|
U32(u32),
|
||||||
|
I32(i32),
|
||||||
|
PtrSystemTime(u64),
|
||||||
|
ClockType(u64),
|
||||||
|
Filename(u64, u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ArgType {
|
||||||
|
Bytes,
|
||||||
|
BytesMut,
|
||||||
|
MaybePtr,
|
||||||
|
Fd,
|
||||||
|
OptionFd,
|
||||||
|
Ptr,
|
||||||
|
Size,
|
||||||
|
U64,
|
||||||
|
I64,
|
||||||
|
U32,
|
||||||
|
I32,
|
||||||
|
PtrSystemTime,
|
||||||
|
ClockType,
|
||||||
|
Filename,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SyscallDefinition(SyscallFunction, &'static str, &'static [ArgType]);
|
||||||
|
|
||||||
|
impl PrintSyscallArgument for SyscallArgument {
|
||||||
|
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||||
|
match self {
|
||||||
|
&Self::MaybePtr(0) => write!(f, "None"),
|
||||||
|
Self::Fd(fd) => {
|
||||||
|
let fd = unsafe { RawFd::from_raw(*fd as _) };
|
||||||
|
match fd {
|
||||||
|
RawFd::STDIN => write!(f, "STDIN"),
|
||||||
|
RawFd::STDOUT => write!(f, "STDOUT"),
|
||||||
|
RawFd::STDERR => write!(f, "STDERR"),
|
||||||
|
_ => write!(f, "{fd:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::OptionFd(fd) => match Option::<RawFd>::from_syscall_register(*fd as _) {
|
||||||
|
Some(fd) => write!(f, "Some({fd:?})"),
|
||||||
|
None => write!(f, "None"),
|
||||||
|
},
|
||||||
|
Self::MaybePtr(addr) => write!(f, "Some({addr:#x})"),
|
||||||
|
Self::Ptr(ptr) => write!(f, "{ptr:#x}"),
|
||||||
|
Self::U64(val) => write!(f, "{val}"),
|
||||||
|
Self::I64(val) => write!(f, "{val}"),
|
||||||
|
Self::U32(val) => write!(f, "{val}"),
|
||||||
|
Self::I32(val) => write!(f, "{val}"),
|
||||||
|
Self::Size(val) => write!(f, "{val}"),
|
||||||
|
Self::Bytes(addr, len) => write!(f, "&[{addr:#x}; {len}]"),
|
||||||
|
Self::BytesMut(addr, len) => write!(f, "&mut [{addr:#x}; {len}]"),
|
||||||
|
Self::PtrSystemTime(ptr) => write!(f, "{ptr:#x}"),
|
||||||
|
Self::ClockType(1) => write!(f, "RealTime"),
|
||||||
|
Self::ClockType(2) => write!(f, "Monotonic"),
|
||||||
|
Self::ClockType(v) => write!(f, "{v}"),
|
||||||
|
Self::Filename(ptr, len) => print_string(f, trace, *ptr as _, *len as _),
|
||||||
|
}
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFS: &[SyscallDefinition] = &const {
|
||||||
|
use ArgType::*;
|
||||||
|
[
|
||||||
|
SyscallDefinition(SyscallFunction::GetRandom, "get_random", &[BytesMut]),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetClock,
|
||||||
|
"get_clock",
|
||||||
|
&[ClockType, PtrSystemTime],
|
||||||
|
),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::SetClock,
|
||||||
|
"set_clock",
|
||||||
|
&[ClockType, PtrSystemTime],
|
||||||
|
),
|
||||||
|
SyscallDefinition(SyscallFunction::Mount, "mount", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Unmount, "unmount", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::LoadModule, "load_module", &[Filename]),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::FilesystemControl,
|
||||||
|
"filesystem_control",
|
||||||
|
&[OptionFd, U32, Size],
|
||||||
|
),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetSystemInfo,
|
||||||
|
"get_system_info",
|
||||||
|
&[U32, BytesMut],
|
||||||
|
), // TODO
|
||||||
|
// Memory management
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::MapMemory,
|
||||||
|
"map_memory",
|
||||||
|
&[MaybePtr, Size, U32, Ptr],
|
||||||
|
),
|
||||||
|
SyscallDefinition(SyscallFunction::UnmapMemory, "unmap_memory", &[Ptr, Size]),
|
||||||
|
// Process/thread management
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::CreateProcessGroup,
|
||||||
|
"create_process_group",
|
||||||
|
&[],
|
||||||
|
),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetProcessGroupId,
|
||||||
|
"get_process_group_id",
|
||||||
|
&[],
|
||||||
|
),
|
||||||
|
SyscallDefinition(SyscallFunction::ExitProcess, "exit_process", &[U64]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::SpawnProcess, "spawn_process", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::WaitProcess,
|
||||||
|
"wait_process",
|
||||||
|
&[Ptr, Ptr, U32],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::GetPid, "get_pid", &[]),
|
||||||
|
SyscallDefinition(SyscallFunction::GetTid, "get_tid", &[]),
|
||||||
|
SyscallDefinition(SyscallFunction::SpawnThread, "spawn_thread", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::ExitThread, "exit_thread", &[]),
|
||||||
|
SyscallDefinition(SyscallFunction::WaitThread, "wait_thread", &[U32, Ptr]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetThreadOption,
|
||||||
|
"get_thread_option",
|
||||||
|
&[U32, BytesMut],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::SetThreadOption,
|
||||||
|
"set_thread_option",
|
||||||
|
&[U32, Bytes],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetProcessOption,
|
||||||
|
"get_process_option",
|
||||||
|
&[U32, U32, BytesMut],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::SetProcessOption,
|
||||||
|
"set_process_option",
|
||||||
|
&[U32, U32, Bytes],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Nanosleep, "nanosleep", &[Ptr, Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::ExitSignal, "exit_signal", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::SendSignal, "send_signal", &[U32, U32]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Mutex, "mutex", &[Ptr, Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::StartSession, "start_session", &[]),
|
||||||
|
// I/O
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::Open,
|
||||||
|
"open",
|
||||||
|
&[OptionFd, Filename, U32, U32],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::CheckAccess,
|
||||||
|
"check_access",
|
||||||
|
&[OptionFd, Filename, U32],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Close, "close", &[Fd]),
|
||||||
|
SyscallDefinition(SyscallFunction::Write, "write", &[Fd, Bytes]),
|
||||||
|
SyscallDefinition(SyscallFunction::Read, "read", &[Fd, BytesMut]),
|
||||||
|
SyscallDefinition(SyscallFunction::Seek, "seek", &[Fd, U32, Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Truncate, "truncate", &[Fd, U64]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Fsync, "fsync", &[Fd, U32]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::ReadAt, "read_at", &[Fd, U64, BytesMut]),
|
||||||
|
SyscallDefinition(SyscallFunction::WriteAt, "write_at", &[Fd, U64, Bytes]),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetFileOption,
|
||||||
|
"get_file_option",
|
||||||
|
&[Fd, U32, BytesMut],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::SetFileOption,
|
||||||
|
"set_file_option",
|
||||||
|
&[Fd, U32, Bytes],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::OpenDirectory,
|
||||||
|
"open_directory",
|
||||||
|
&[OptionFd, Filename],
|
||||||
|
),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::ReadDirectoryEntries,
|
||||||
|
"read_directory_entries",
|
||||||
|
&[Fd, Ptr, Size],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::CreateDirectory,
|
||||||
|
"create_directory",
|
||||||
|
&[OptionFd, Filename, U32],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::ReadLink,
|
||||||
|
"read_link",
|
||||||
|
&[OptionFd, Filename, BytesMut],
|
||||||
|
),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::Remove,
|
||||||
|
"remove",
|
||||||
|
&[OptionFd, Filename, U32],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Rename, "rename", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::CloneFd, "clone_fd", &[Fd, OptionFd]),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::UpdateMetadata,
|
||||||
|
"update_metadata",
|
||||||
|
&[OptionFd, Filename, Ptr],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetMetadata,
|
||||||
|
"get_metadata",
|
||||||
|
&[OptionFd, Filename, Ptr, U32],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::DeviceRequest,
|
||||||
|
"device_request",
|
||||||
|
&[Fd, U32, BytesMut, Size],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::CreateTimer, "create_timer", &[U32]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::CreatePid, "create_pid", &[Ptr, U32]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::CreatePty, "create_pty", &[Ptr, Ptr, Ptr]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::CreateSharedMemory,
|
||||||
|
"create_shared_memory",
|
||||||
|
&[Size],
|
||||||
|
),
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::CreatePollChannel,
|
||||||
|
"create_poll_channel",
|
||||||
|
&[],
|
||||||
|
),
|
||||||
|
SyscallDefinition(SyscallFunction::CreatePipe, "create_pipe", &[Ptr]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::PollChannelWait,
|
||||||
|
"poll_channel_wait",
|
||||||
|
&[Fd, Ptr, U32, Ptr],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::PollChannelControl,
|
||||||
|
"poll_channel_control",
|
||||||
|
&[Fd, U32, Fd],
|
||||||
|
), // TODO
|
||||||
|
// Network
|
||||||
|
SyscallDefinition(SyscallFunction::CreateSocket, "create_socket", &[U32]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Bind, "bind", &[Fd, Bytes]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Listen, "listen", &[Fd]),
|
||||||
|
SyscallDefinition(SyscallFunction::Connect, "connect", &[Fd, Bytes]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Accept, "accept", &[Fd, MaybePtr]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::Shutdown, "shutdown", &[Fd, U32]), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::SendTo, "send_to", &[Fd, Bytes, MaybePtr]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::ReceiveFrom,
|
||||||
|
"receive_from",
|
||||||
|
&[Fd, BytesMut, MaybePtr],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::GetSocketOption,
|
||||||
|
"get_socket_option",
|
||||||
|
&[Fd, U32, BytesMut],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::SetSocketOption,
|
||||||
|
"set_socket_option",
|
||||||
|
&[Fd, U32, Bytes],
|
||||||
|
), // TODO
|
||||||
|
SyscallDefinition(SyscallFunction::SendMessage, "send_message", &[Fd, Ptr]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::ReceiveMessage,
|
||||||
|
"receive_message",
|
||||||
|
&[Fd, Ptr],
|
||||||
|
), // TODO
|
||||||
|
// C compat
|
||||||
|
SyscallDefinition(SyscallFunction::Fork, "fork", &[]),
|
||||||
|
SyscallDefinition(SyscallFunction::Execve, "execve", &[Ptr]), // TODO
|
||||||
|
// Debugging
|
||||||
|
SyscallDefinition(SyscallFunction::DebugTrace, "debug_trace", &[U32, Filename]), // TODO
|
||||||
|
SyscallDefinition(
|
||||||
|
SyscallFunction::DebugControl,
|
||||||
|
"debug_control",
|
||||||
|
&[U32, U32, BytesMut, Size],
|
||||||
|
), // TODO
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
impl SyscallDefinition {
|
||||||
|
fn parse(&self, args: &[u64]) -> Syscall {
|
||||||
|
let mut res = vec![];
|
||||||
|
let mut pos = 0;
|
||||||
|
for arg_def in self.2.iter() {
|
||||||
|
use SyscallArgument::*;
|
||||||
|
|
||||||
|
let (a, l) = match arg_def {
|
||||||
|
ArgType::MaybePtr => (MaybePtr(args[pos]), 1),
|
||||||
|
ArgType::Bytes => (Bytes(args[pos], args[pos + 1]), 2),
|
||||||
|
ArgType::BytesMut => (BytesMut(args[pos], args[pos + 1]), 2),
|
||||||
|
ArgType::Fd => (Fd(args[pos]), 1),
|
||||||
|
ArgType::OptionFd => (OptionFd(args[pos]), 1),
|
||||||
|
ArgType::Ptr => (Ptr(args[pos]), 1),
|
||||||
|
ArgType::Size => (Size(args[pos]), 1),
|
||||||
|
ArgType::U64 => (U64(args[pos]), 1),
|
||||||
|
ArgType::I64 => (I64(args[pos] as _), 1),
|
||||||
|
ArgType::U32 => (U32(args[pos] as _), 1),
|
||||||
|
ArgType::I32 => (I32(args[pos] as _), 1),
|
||||||
|
ArgType::PtrSystemTime => (PtrSystemTime(args[pos]), 1),
|
||||||
|
ArgType::ClockType => (ClockType(args[pos]), 1),
|
||||||
|
ArgType::Filename => (Filename(args[pos], args[pos + 1]), 2),
|
||||||
|
};
|
||||||
|
|
||||||
|
pos += l;
|
||||||
|
res.push(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
Syscall(self.1, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_syscall(nr: u64, args: &[u64]) -> Option<Syscall> {
|
||||||
|
let func = SyscallFunction::try_from(nr as usize).ok()?;
|
||||||
|
let def = DEFS.iter().find(|e| e.0 == func)?;
|
||||||
|
Some(def.parse(args))
|
||||||
|
}
|
||||||
@@ -1,36 +1,68 @@
|
|||||||
#![feature(rustc_private, yggdrasil_os, let_chains)]
|
#![feature(rustc_private)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
|
||||||
process::{Command, ExitCode},
|
process::{Command, ExitCode},
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use tracer::CommandTracer;
|
use cross::{
|
||||||
|
process::{ChildTrace, CommandSpawnExt, TraceEvent},
|
||||||
|
signal,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod format;
|
use crate::definition::parse_syscall;
|
||||||
pub mod tracer;
|
|
||||||
|
pub mod definition;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
command: String,
|
program: String,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(args: &Args) -> io::Result<()> {
|
|
||||||
let mut command = Command::new(&args.command);
|
|
||||||
command.args(&args.args);
|
|
||||||
let tracer = CommandTracer::spawn(command)?;
|
|
||||||
tracer.run(format::format_syscall)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
match run(&args) {
|
|
||||||
Ok(()) => ExitCode::SUCCESS,
|
static ABORT: AtomicBool = AtomicBool::new(false);
|
||||||
Err(error) => {
|
|
||||||
eprintln!("{}: {error}", args.command);
|
signal::set_sigint_handler(|| {
|
||||||
ExitCode::FAILURE
|
ABORT.store(true, Ordering::Release);
|
||||||
|
eprintln!("SIGINT");
|
||||||
|
});
|
||||||
|
let mut child = Command::new(args.program)
|
||||||
|
.args(args.args)
|
||||||
|
.spawn_with_trace()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut last_syscall: Option<(u64, [u64; 6])> = None;
|
||||||
|
|
||||||
|
while !ABORT.load(Ordering::Relaxed) {
|
||||||
|
let event = child.next_event().unwrap();
|
||||||
|
match event {
|
||||||
|
TraceEvent::Exited => {
|
||||||
|
eprintln!("Exit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TraceEvent::SyscallExit(ret) => match last_syscall {
|
||||||
|
Some((nr, args)) => {
|
||||||
|
let call = parse_syscall(nr, &args);
|
||||||
|
if let Some(call) = call {
|
||||||
|
let mut s = String::new();
|
||||||
|
call.print(&mut s, &mut child);
|
||||||
|
eprintln!("{s} -> {ret}");
|
||||||
|
} else {
|
||||||
|
eprintln!("syscall {nr} {args:?} -> {ret}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("??? -> {ret}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TraceEvent::SyscallEntry(nr, args) => {
|
||||||
|
last_syscall = Some((nr, args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
child.resume().ok();
|
||||||
}
|
}
|
||||||
|
ExitCode::SUCCESS
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user