debug/
gdb.rs

1#![allow(unused)]
2
3use core::slice;
4use std::{
5    collections::HashMap,
6    fs::{File, OpenOptions},
7    io::{Read, Write},
8    num::NonZero,
9    os::fd::{AsFd, FromRawFd},
10    sync::{
11        Arc, Mutex,
12        atomic::{AtomicU32, AtomicU64, Ordering},
13        mpsc::{Receiver, Sender},
14    },
15    thread::JoinHandle,
16    time::Duration,
17};
18
19use gdbstub::{
20    common::Signal,
21    conn::{Connection, ConnectionExt},
22    stub::{
23        BaseStopReason, MultiThreadStopReason,
24        run_blocking::{Event, WaitForStopReasonError},
25    },
26    target::{
27        Target, TargetError,
28        ext::{
29            base::{
30                BaseOps,
31                multithread::{
32                    MultiThreadBase, MultiThreadResume, MultiThreadResumeOps, MultiThreadSingleStep,
33                },
34            },
35            breakpoints::{Breakpoints, SwBreakpoint},
36            exec_file::ExecFile,
37            host_io::{
38                HostIo, HostIoClose, HostIoErrno, HostIoError, HostIoFstat, HostIoFstatOps,
39                HostIoOpen, HostIoOpenMode, HostIoPread, HostIoPwrite, HostIoPwriteOps,
40                HostIoReadlink, HostIoResult, HostIoStat,
41            },
42            libraries::LibrariesSvr4,
43            memory_map::MemoryMap,
44        },
45    },
46};
47use gdbstub_arch::x86::reg::{X86SegmentRegs, X87FpuInternalRegs};
48use miette::IntoDiagnostic;
49use monitor_api::{CompartmentFlags, CompartmentHandle};
50use twizzler::{
51    object::{Object, RawObject},
52    ptr::{RefMut, RefSlice, RefSliceMut},
53};
54use twizzler_abi::{
55    arch::{ArchRegisters, XSAVE_LEN},
56    object::{MAX_SIZE, NULLPAGE_SIZE, ObjID, Protections},
57    syscall::{
58        KernelConsoleReadFlags, KernelConsoleSource, KernelConsoleWriteFlags, MapControlCmd,
59        ThreadSync, ThreadSyncFlags, ThreadSyncOp, ThreadSyncReference, ThreadSyncSleep,
60        ThreadSyncWake, sys_map_ctrl, sys_object_read_map, sys_thread_sync,
61    },
62    thread::{ExecutionState, ThreadRepr},
63    upcall::UpcallFrame,
64};
65use twizzler_rt_abi::{
66    bindings::twz_rt_map_object,
67    debug::LinkMap,
68    error::{GenericError, ObjectError, ResourceError, SecurityError, TwzError},
69    object::MapFlags,
70};
71
72pub struct TwizzlerGdb {}
73
74#[cfg(target_arch = "x86_64")]
75type BpWord = u8;
76#[cfg(target_arch = "aarch64")]
77type BpWord = u32;
78
79#[cfg(target_arch = "x86_64")]
80const BREAK_WORD: BpWord = 0xcc;
81#[cfg(target_arch = "aarch64")]
82const BREAK_WORD: BpWord = 0xD4200000;
83
84type ChanMsg = Event<MultiThreadStopReason<u64>>;
85
86struct TwzRegs(ArchRegisters);
87
88impl From<TwzRegs> for gdbstub_arch::x86::reg::X86_64CoreRegs {
89    fn from(value: TwzRegs) -> Self {
90        gdbstub_arch::x86::reg::X86_64CoreRegs {
91            regs: [
92                value.0.frame.rax,
93                value.0.frame.rbx,
94                value.0.frame.rcx,
95                value.0.frame.rdx,
96                value.0.frame.rsi,
97                value.0.frame.rdi,
98                value.0.frame.rsp,
99                value.0.frame.rbp,
100                value.0.frame.r8,
101                value.0.frame.r9,
102                value.0.frame.r10,
103                value.0.frame.r11,
104                value.0.frame.r12,
105                value.0.frame.r13,
106                value.0.frame.r14,
107                value.0.frame.r15,
108            ],
109            eflags: value.0.frame.rflags as u32,
110            rip: value.0.frame.rip,
111            segments: X86SegmentRegs {
112                cs: value.0.cs,
113                ss: value.0.ss,
114                ds: value.0.ds,
115                es: value.0.es,
116                fs: value.0.fs,
117                gs: value.0.gs,
118            },
119            st: [[0; 10]; 8],
120            fpu: X87FpuInternalRegs::default(),
121            xmm: [0; 16],
122            mxcsr: 0,
123        }
124    }
125}
126
127impl From<&gdbstub_arch::x86::reg::X86_64CoreRegs> for TwzRegs {
128    fn from(value: &gdbstub_arch::x86::reg::X86_64CoreRegs) -> Self {
129        Self(ArchRegisters {
130            frame: UpcallFrame {
131                rax: value.regs[0],
132                rbx: value.regs[1],
133                rcx: value.regs[2],
134                rdx: value.regs[3],
135                rsi: value.regs[4],
136                rdi: value.regs[5],
137                rbp: value.regs[6],
138                rsp: value.regs[7],
139                r8: value.regs[8],
140                r9: value.regs[9],
141                r10: value.regs[10],
142                r11: value.regs[11],
143                r12: value.regs[12],
144                r13: value.regs[13],
145                r14: value.regs[14],
146                r15: value.regs[15],
147                xsave_region: [0; XSAVE_LEN],
148                rip: value.rip,
149                rflags: value.eflags as u64,
150                thread_ptr: 0,
151                prior_ctx: 0.into(),
152            },
153            fs: value.segments.fs,
154            gs: value.segments.gs,
155            es: value.segments.es,
156            ds: value.segments.ds,
157            ss: value.segments.ss,
158            cs: value.segments.cs,
159        })
160    }
161}
162
163impl TwizzlerGdb {
164    fn mon_main(inner: Arc<TargetInner>) {
165        let mut flags: CompartmentFlags = inner.comp.info().flags;
166        while inner.done.load(Ordering::SeqCst) == 0 {
167            if flags.contains(CompartmentFlags::EXITED) {
168                break;
169            }
170            flags = inner.comp.wait(flags);
171        }
172        tracing::debug!("comp mon exit");
173    }
174
175    fn thread_main(inner: Arc<TargetInner>, id: ObjID) {
176        use twizzler::object::TypedObject;
177        let repr = unsafe { Object::<ThreadRepr>::map_unchecked(id, MapFlags::READ) }.unwrap();
178        let mut old_state = repr.base().get_state();
179        while inner.done.load(Ordering::SeqCst) == 0 {
180            let cur_state = repr.base().get_state();
181            if cur_state != old_state {
182                let reason = match cur_state {
183                    ExecutionState::Suspended => {
184                        Some(MultiThreadStopReason::Signal(Signal::SIGSTOP))
185                    }
186                    ExecutionState::Exited => {
187                        Some(MultiThreadStopReason::Exited(repr.base().get_code() as u8))
188                    }
189                    _ => None,
190                };
191                if let Some(reason) = reason {
192                    let _ = inner.send.send(Event::TargetStopped(reason));
193                }
194                old_state = cur_state;
195            } else {
196                let wait = repr.base().waitable_until_not(old_state);
197                let wait2 = repr.base().waitable(ExecutionState::Exited);
198                let wait3 = ThreadSyncSleep::new(
199                    ThreadSyncReference::Virtual(&inner.done),
200                    0,
201                    ThreadSyncOp::Equal,
202                    ThreadSyncFlags::empty(),
203                );
204                let _ = sys_thread_sync(
205                    &mut [
206                        ThreadSync::new_sleep(wait),
207                        ThreadSync::new_sleep(wait2),
208                        ThreadSync::new_sleep(wait3),
209                    ],
210                    None,
211                );
212            }
213        }
214        tracing::debug!("thread mon exit");
215    }
216
217    fn chan_main(inner: Arc<TargetInner>) {
218        let sleep = ThreadSyncSleep::new(
219            ThreadSyncReference::Virtual(&inner.done),
220            0,
221            ThreadSyncOp::Equal,
222            ThreadSyncFlags::empty(),
223        );
224        while inner.done.load(Ordering::SeqCst) == 0 {
225            let mut bytes = [0];
226            let r = twizzler_abi::syscall::sys_kernel_console_read_interruptable(
227                KernelConsoleSource::DebugConsole,
228                &mut bytes,
229                KernelConsoleReadFlags::empty(),
230                None,
231                Some(sleep),
232            );
233            if matches!(r, Ok(1)) {
234                inner.conn.send(bytes[0]);
235            }
236            inner.send.send(Event::IncomingData(0));
237        }
238        tracing::debug!("channel mon exit");
239    }
240}
241
242impl gdbstub::stub::run_blocking::BlockingEventLoop for TwizzlerGdb {
243    type Target = TwizzlerTarget;
244
245    type Connection = TwizzlerConn;
246
247    type StopReason = MultiThreadStopReason<u64>;
248
249    fn wait_for_stop_reason(
250        target: &mut Self::Target,
251        conn: &mut Self::Connection,
252    ) -> Result<
253        Event<Self::StopReason>,
254        WaitForStopReasonError<
255            <Self::Target as Target>::Error,
256            <Self::Connection as Connection>::Error,
257        >,
258    > {
259        loop {
260            let event = target.recv.recv().unwrap();
261            match event {
262                Event::IncomingData(_) => {
263                    if conn
264                        .peek()
265                        .map_err(|e| WaitForStopReasonError::Connection(e))?
266                        .is_some()
267                    {
268                        let byte = conn
269                            .read()
270                            .map_err(|e| WaitForStopReasonError::Connection(e))?;
271                        return Ok(Event::IncomingData(byte));
272                    }
273                }
274                Event::TargetStopped(_) => {
275                    return Ok(event);
276                }
277            }
278        }
279    }
280
281    fn on_interrupt(
282        target: &mut Self::Target,
283    ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error> {
284        twizzler_abi::syscall::sys_thread_change_state(
285            target.get_thread_repr_id(NonZero::new(1).unwrap()),
286            ExecutionState::Running,
287        )?;
288        Ok(Some(BaseStopReason::Signal(Signal::SIGINT)))
289    }
290}
291
292#[derive(Clone, Debug)]
293struct Breaks {
294    map: HashMap<u64, Breakpoint>,
295}
296
297#[derive(Copy, Clone, Debug)]
298struct Breakpoint {
299    saved_data: BpWord,
300    addr: u64,
301}
302
303impl Breaks {
304    fn new() -> Self {
305        Self {
306            map: HashMap::new(),
307        }
308    }
309
310    fn insert(&mut self, addr: u64, saved_data: BpWord) -> &Breakpoint {
311        self.map.insert(addr, Breakpoint { saved_data, addr });
312        self.map.get(&addr).unwrap()
313    }
314
315    fn remove(&mut self, addr: u64) -> Option<Breakpoint> {
316        self.map.remove(&addr)
317    }
318
319    fn get(&self, addr: u64) -> Option<&Breakpoint> {
320        self.map.get(&addr)
321    }
322
323    fn get_mut(&mut self, addr: u64) -> Option<&mut Breakpoint> {
324        self.map.get_mut(&addr)
325    }
326}
327
328impl Breakpoint {
329    fn arm(&self, target: &mut TwizzlerTarget) -> Result<(), TwzError> {
330        tracing::debug!("arming breakpoint {:#x}", self.addr);
331        let mut slice = target.mem_slice_mut_writable(self.addr, size_of::<BpWord>())?;
332        let data = BREAK_WORD.to_ne_bytes();
333        slice.copy_from_slice(&data);
334        Ok(())
335    }
336
337    fn restore(&self, target: &mut TwizzlerTarget) -> Result<(), TwzError> {
338        tracing::debug!("restoring breakpoint {:#x}", self.addr);
339        let mut slice = target.mem_slice_mut_writable(self.addr, size_of::<BpWord>())?;
340        let data = self.saved_data.to_ne_bytes();
341        slice.copy_from_slice(&data);
342        Ok(())
343    }
344}
345
346pub struct TargetInner {
347    done: AtomicU64,
348    comp: CompartmentHandle,
349    send: Sender<ChanMsg>,
350    conn: Sender<u8>,
351    files: Mutex<FileMgr>,
352    bps: Mutex<Breaks>,
353}
354
355struct FileMgr {
356    next: u32,
357    map: HashMap<u32, File>,
358}
359
360impl FileMgr {
361    fn new() -> Self {
362        Self {
363            next: 1,
364            map: HashMap::new(),
365        }
366    }
367
368    fn open(
369        &mut self,
370        filename: &str,
371        flags: &std::fs::OpenOptions,
372    ) -> Result<u32, std::io::Error> {
373        let file = flags.open(filename)?;
374        let fd = self.next;
375        self.next += 1;
376        self.map.insert(fd, file);
377        Ok(fd)
378    }
379
380    fn pread(&mut self, fd: u32, buf: &mut [u8], offset: u64) -> Result<usize, std::io::Error> {
381        if let Some(file) = self.map.get_mut(&fd) {
382            use std::io::{Seek, SeekFrom};
383            file.seek(SeekFrom::Start(offset))?;
384            let bytes_read = file.read(buf)?;
385            Ok(bytes_read)
386        } else {
387            Err(std::io::Error::new(
388                std::io::ErrorKind::InvalidInput,
389                "Invalid file descriptor",
390            ))
391        }
392    }
393
394    fn pwrite(&mut self, fd: u32, buf: &[u8], offset: u64) -> Result<usize, std::io::Error> {
395        if let Some(file) = self.map.get_mut(&fd) {
396            use std::io::{Seek, SeekFrom};
397            let current_pos = file.stream_position()?;
398            file.seek(SeekFrom::Start(offset))?;
399            let bytes_written = file.write(buf)?;
400            file.seek(SeekFrom::Start(current_pos))?;
401            Ok(bytes_written)
402        } else {
403            Err(std::io::Error::new(
404                std::io::ErrorKind::InvalidInput,
405                "Invalid file descriptor",
406            ))
407        }
408    }
409
410    fn close(&mut self, fd: u32) -> Result<(), std::io::Error> {
411        if let Some(_file) = self.map.remove(&fd) {
412            Ok(())
413        } else {
414            Err(std::io::Error::new(
415                std::io::ErrorKind::InvalidInput,
416                "Invalid file descriptor",
417            ))
418        }
419    }
420}
421
422pub struct TwizzlerTarget {
423    recv: Receiver<ChanMsg>,
424    inner: Arc<TargetInner>,
425    mon_t: Option<JoinHandle<()>>,
426    chan_t: Option<JoinHandle<()>>,
427    t_t: Option<JoinHandle<()>>,
428    thread_repr_id: ObjID,
429    libs: Vec<(String, ObjID, u64, Box<LinkMap>)>,
430}
431
432impl Drop for TwizzlerTarget {
433    fn drop(&mut self) {
434        self.inner.done.store(1, Ordering::SeqCst);
435        let _ = sys_thread_sync(
436            &mut [ThreadSync::new_wake(ThreadSyncWake::new(
437                ThreadSyncReference::Virtual(&self.inner.done),
438                usize::MAX,
439            ))],
440            None,
441        );
442
443        //self.mon_t.take().map(|t| t.join()).unwrap();
444        self.chan_t.take().map(|t| t.join()).unwrap();
445        self.t_t.take().map(|t| t.join()).unwrap();
446    }
447}
448
449impl TwizzlerTarget {
450    pub fn new(comp: CompartmentHandle, conn: Sender<u8>) -> Self {
451        let (send, recv) = std::sync::mpsc::channel();
452        let thread_repr_id = comp
453            .threads()
454            .nth(0)
455            .map(|t| t.repr_id)
456            .unwrap_or(ObjID::new(0));
457        let libs = comp
458            .libs()
459            .map(|l| {
460                (
461                    l.info().name.clone(),
462                    l.info().objid,
463                    l.info().dl_info.addr as u64,
464                    Box::new(l.info().link_map),
465                )
466            })
467            .collect();
468        let inner = Arc::new(TargetInner {
469            done: AtomicU64::new(0),
470            comp,
471            send,
472            conn,
473            files: Mutex::new(FileMgr::new()),
474            bps: Mutex::new(Breaks::new()),
475        });
476        let inner_t = inner.clone();
477        let chan_t = std::thread::spawn(|| {
478            TwizzlerGdb::chan_main(inner_t);
479        });
480        //let inner_t = inner.clone();
481        //let mon_t = std::thread::spawn(|| {
482        //    TwizzlerGdb::mon_main(inner_t);
483        //});
484        let inner_t = inner.clone();
485        let t_t = std::thread::spawn(move || {
486            TwizzlerGdb::thread_main(inner_t, thread_repr_id);
487        });
488
489        Self {
490            recv,
491            inner,
492            mon_t: None,
493            chan_t: Some(chan_t),
494            t_t: Some(t_t),
495            thread_repr_id,
496            libs,
497        }
498    }
499
500    fn mem_access(
501        &mut self,
502        addr: <<TwizzlerTarget as gdbstub::target::Target>::Arch as gdbstub::arch::Arch>::Usize,
503        len: usize,
504        prot: Protections,
505    ) -> gdbstub::target::TargetResult<usize, Self> {
506        let slot = addr as usize / MAX_SIZE;
507        let info = sys_object_read_map(None, slot).map_err(|e| TargetError::Io(e.into()))?;
508        if prot & info.prot != prot {
509            return Err(TargetError::Io(
510                TwzError::Generic(GenericError::AccessDenied).into(),
511            ));
512        }
513        if (addr as usize % MAX_SIZE) < NULLPAGE_SIZE {
514            return Err(TargetError::Io(
515                TwzError::Generic(GenericError::AccessDenied).into(),
516            ));
517        }
518        let max_len = (MAX_SIZE - NULLPAGE_SIZE) - (addr as usize % MAX_SIZE);
519        Ok(len.min(max_len))
520    }
521
522    fn mem_slice(
523        &mut self,
524        addr: <<TwizzlerTarget as gdbstub::target::Target>::Arch as gdbstub::arch::Arch>::Usize,
525        len: usize,
526    ) -> gdbstub::target::TargetResult<&[u8], Self> {
527        let slice = unsafe { slice::from_raw_parts(addr as *const u8, len) };
528        Ok(slice)
529    }
530
531    fn mem_slice_mut(
532        &mut self,
533        addr: <<TwizzlerTarget as gdbstub::target::Target>::Arch as gdbstub::arch::Arch>::Usize,
534        len: usize,
535    ) -> gdbstub::target::TargetResult<&mut [u8], Self> {
536        let slice = unsafe { slice::from_raw_parts_mut(addr as *mut u8, len) };
537        Ok(slice)
538    }
539
540    fn mem_slice_mut_writable(
541        &mut self,
542        addr: <<TwizzlerTarget as gdbstub::target::Target>::Arch as gdbstub::arch::Arch>::Usize,
543        len: usize,
544    ) -> Result<RefSliceMut<u8>, TwzError> {
545        if (addr as usize % MAX_SIZE) < NULLPAGE_SIZE {
546            return Err(TwzError::Generic(GenericError::AccessDenied).into());
547        }
548        let slot = addr as usize / MAX_SIZE;
549        let info = sys_object_read_map(None, slot)?;
550        let slice = if info.prot.contains(Protections::WRITE) && false {
551            unsafe { RefSliceMut::from_ref(RefMut::from_ptr(addr as *mut u8), len) }
552        } else {
553            tracing::debug!("remapping {} as writable", info.id);
554            let handle =
555                unsafe { Object::<()>::map_unchecked(info.id, MapFlags::WRITE | MapFlags::READ) }?;
556            let offset = addr as usize % MAX_SIZE;
557            let ptr = handle
558                .lea_mut(offset, len)
559                .ok_or(TwzError::INVALID_ARGUMENT)?;
560            unsafe {
561                RefSliceMut::from_ref(
562                    RefMut::from_handle(handle.into_handle(), ptr as *mut u8),
563                    len,
564                )
565            }
566        };
567
568        Ok(slice)
569    }
570
571    fn get_thread_repr_id(&self, _tid: gdbstub::common::Tid) -> ObjID {
572        self.thread_repr_id
573    }
574}
575
576impl MultiThreadBase for TwizzlerTarget {
577    fn read_registers(
578        &mut self,
579        regs: &mut <Self::Arch as gdbstub::arch::Arch>::Registers,
580        tid: gdbstub::common::Tid,
581    ) -> gdbstub::target::TargetResult<(), Self> {
582        tracing::debug!("reading regs from {}", tid);
583        let old_state = twizzler_abi::syscall::sys_thread_change_state(
584            self.get_thread_repr_id(tid),
585            ExecutionState::Suspended,
586        )
587        .map_err(|e| TargetError::Io(e.into()))?;
588        let twzregs =
589            twizzler_abi::syscall::sys_thread_read_registers(self.get_thread_repr_id(tid))
590                .map_err(|e| TargetError::Io(e.into()))?;
591        *regs = TwzRegs(twzregs).into();
592
593        tracing::debug!("{}: old state = {:?}", tid, old_state);
594        if old_state == ExecutionState::Running {
595            twizzler_abi::syscall::sys_thread_change_state(
596                self.get_thread_repr_id(tid),
597                ExecutionState::Running,
598            )
599            .map_err(|e| TargetError::Io(e.into()))?;
600        }
601        Ok(())
602    }
603
604    fn write_registers(
605        &mut self,
606        regs: &<Self::Arch as gdbstub::arch::Arch>::Registers,
607        tid: gdbstub::common::Tid,
608    ) -> gdbstub::target::TargetResult<(), Self> {
609        let twzregs = TwzRegs::from(regs);
610        twizzler_abi::syscall::sys_thread_write_registers(self.get_thread_repr_id(tid), &twzregs.0)
611            .map_err(|e| TargetError::Io(e.into()))?;
612        Ok(())
613    }
614
615    fn read_addrs(
616        &mut self,
617        start_addr: <Self::Arch as gdbstub::arch::Arch>::Usize,
618        data: &mut [u8],
619        tid: gdbstub::common::Tid,
620    ) -> gdbstub::target::TargetResult<usize, Self> {
621        tracing::debug!("read addrs: {:x} {}", start_addr, data.len());
622        if (start_addr as usize) < MAX_SIZE {
623            return Err(TargetError::Errno(1));
624        }
625        let len = self.mem_access(start_addr, data.len(), Protections::READ)?;
626        let slice = self.mem_slice(start_addr, len)?;
627        (&mut data[0..len]).copy_from_slice(slice);
628        Ok(len)
629    }
630
631    fn write_addrs(
632        &mut self,
633        start_addr: <Self::Arch as gdbstub::arch::Arch>::Usize,
634        data: &[u8],
635        tid: gdbstub::common::Tid,
636    ) -> gdbstub::target::TargetResult<(), Self> {
637        tracing::debug!("write addrs: {:x} {}: {:?}", start_addr, data.len(), data);
638        if (start_addr as usize) < MAX_SIZE {
639            return Err(TargetError::Errno(1));
640        }
641        let len = self.mem_access(start_addr, data.len(), Protections::empty())?;
642        if len < data.len() {
643            return Err(TargetError::Io(
644                TwzError::Generic(GenericError::AccessDenied).into(),
645            ));
646        }
647        let mut slice = self
648            .mem_slice_mut_writable(start_addr, len)
649            .map_err(|e| TargetError::Fatal(e))?;
650        tracing::debug!("{:x} => w to {:p}", start_addr, slice.as_ptr());
651        slice.copy_from_slice(data);
652        let _ = sys_map_ctrl(
653            start_addr as usize as *const u8,
654            data.len(),
655            MapControlCmd::Invalidate,
656            0,
657        );
658        Ok(())
659    }
660
661    fn list_active_threads(
662        &mut self,
663        thread_is_active: &mut dyn FnMut(gdbstub::common::Tid),
664    ) -> Result<(), Self::Error> {
665        // TODO: support multithreading
666        thread_is_active(NonZero::new(1).unwrap());
667        Ok(())
668    }
669
670    fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> {
671        Some(self)
672    }
673}
674
675impl MultiThreadResume for TwizzlerTarget {
676    fn resume(&mut self) -> Result<(), Self::Error> {
677        twizzler_abi::syscall::sys_thread_change_state(
678            self.get_thread_repr_id(NonZero::new(1).unwrap()),
679            ExecutionState::Running,
680        )?;
681        Ok(())
682    }
683
684    fn clear_resume_actions(&mut self) -> Result<(), Self::Error> {
685        Ok(())
686    }
687
688    fn set_resume_action_continue(
689        &mut self,
690        tid: gdbstub::common::Tid,
691        signal: Option<gdbstub::common::Signal>,
692    ) -> Result<(), Self::Error> {
693        Ok(())
694    }
695
696    fn support_single_step(
697        &mut self,
698    ) -> Option<gdbstub::target::ext::base::multithread::MultiThreadSingleStepOps<'_, Self>> {
699        Some(self)
700    }
701}
702
703impl MultiThreadSingleStep for TwizzlerTarget {
704    fn set_resume_action_step(
705        &mut self,
706        tid: gdbstub::common::Tid,
707        signal: Option<Signal>,
708    ) -> Result<(), Self::Error> {
709        Ok(())
710    }
711}
712
713impl ExecFile for TwizzlerTarget {
714    fn get_exec_file(
715        &self,
716        pid: Option<gdbstub::common::Pid>,
717        offset: u64,
718        length: usize,
719        buf: &mut [u8],
720    ) -> gdbstub::target::TargetResult<usize, Self> {
721        let name = self.libs[0].0.as_bytes();
722        let offset = offset as usize;
723        let copy_len = length.min(buf.len()).min(name.len().saturating_sub(offset));
724        if copy_len > 0 {
725            (&mut buf[0..copy_len]).copy_from_slice(&name[offset..(copy_len + offset)]);
726        }
727        Ok(copy_len)
728    }
729}
730
731impl LibrariesSvr4 for TwizzlerTarget {
732    fn get_libraries_svr4(
733        &self,
734        offset: u64,
735        length: usize,
736        buf: &mut [u8],
737    ) -> gdbstub::target::TargetResult<usize, Self> {
738        let mut xml = format!(
739            "<library-list-svr4 version=\"1.0\" main-lm=\"{:p}\">",
740            self.libs[0].3
741        );
742        for lib in &self.libs {
743            xml.push_str(&format!(
744                "<library name=\"{}\" lm=\"{:p}\" l_addr=\"{:#x}\" l_ld=\"{:p}\" lmid=\"0x0\"/>",
745                lib.0,
746                lib.3,
747                lib.2,
748                (&*lib.3).0.ld,
749            ));
750        }
751        xml.push_str("</library-list-svr4>");
752        tracing::debug!("get libraries: {}", xml);
753        let xml_bytes = xml.as_bytes();
754
755        let offset = offset as usize;
756        let copy_len = length
757            .min(buf.len())
758            .min(xml_bytes.len().saturating_sub(offset));
759        if copy_len > 0 {
760            (&mut buf[0..copy_len]).copy_from_slice(&xml_bytes[offset..(copy_len + offset)]);
761        }
762        Ok(copy_len)
763    }
764}
765
766impl HostIoOpen for TwizzlerTarget {
767    fn open(
768        &mut self,
769        filename: &[u8],
770        flags: gdbstub::target::ext::host_io::HostIoOpenFlags,
771        mode: gdbstub::target::ext::host_io::HostIoOpenMode,
772    ) -> gdbstub::target::ext::host_io::HostIoResult<u32, Self> {
773        let mut fm = self.inner.files.lock().unwrap();
774        fm.open(
775            unsafe { str::from_utf8_unchecked(filename) },
776            OpenOptions::new().read(true),
777        )
778        .map_err(|e| HostIoError::Errno(HostIoErrno::EUNKNOWN))
779        .inspect(|x| {
780            tracing::debug!(
781                "open: {} -> {}",
782                unsafe { str::from_utf8_unchecked(filename) },
783                x
784            )
785        })
786    }
787}
788
789impl HostIoPread for TwizzlerTarget {
790    fn pread(
791        &mut self,
792        fd: u32,
793        count: usize,
794        offset: u64,
795        buf: &mut [u8],
796    ) -> gdbstub::target::ext::host_io::HostIoResult<usize, Self> {
797        let mut fm = self.inner.files.lock().unwrap();
798        tracing::debug!("pread: {}: {} {} {}", fd, count, buf.len(), offset);
799        let read_count = fm
800            .pread(fd, buf, offset)
801            .map_err(|e| HostIoError::Errno(HostIoErrno::EUNKNOWN))?;
802        Ok(read_count)
803    }
804}
805
806impl HostIoFstat for TwizzlerTarget {
807    fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self> {
808        let mut fm = self.inner.files.lock().unwrap();
809        let file = fm
810            .map
811            .get_mut(&fd)
812            .ok_or(HostIoError::Errno(HostIoErrno::EBADF))?;
813        let metadata = file.metadata()?;
814
815        macro_rules! time_to_secs {
816            ($time:expr) => {
817                $time
818                    .map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))?
819                    .duration_since(std::time::SystemTime::UNIX_EPOCH)
820                    .map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))?
821                    .as_secs() as u32
822            };
823        }
824        let atime = time_to_secs!(metadata.accessed());
825        let mtime = time_to_secs!(metadata.modified());
826        let ctime = time_to_secs!(metadata.created());
827
828        tracing::debug!("fstat: ret len {}", metadata.len());
829
830        Ok(HostIoStat {
831            st_dev: 0,
832            st_ino: fd,
833            st_mode: HostIoOpenMode::S_IFREG,
834            st_nlink: 1,
835            st_uid: 0,
836            st_gid: 0,
837            st_rdev: 0,
838            st_size: metadata.len(),
839            st_blksize: 0,
840            st_blocks: 0,
841            st_atime: atime,
842            st_mtime: mtime,
843            st_ctime: ctime,
844        })
845    }
846}
847
848impl HostIoReadlink for TwizzlerTarget {
849    fn readlink(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self> {
850        use std::os::twizzler::ffi::OsStrExt;
851        let target = std::fs::read_link(unsafe { str::from_utf8_unchecked(filename) })?;
852        let target_bytes = target.as_os_str().as_bytes();
853
854        let offset = 0;
855        let copy_len = buf.len().min(target_bytes.len().saturating_sub(offset));
856        if copy_len > 0 {
857            (&mut buf[0..copy_len]).copy_from_slice(&target_bytes[offset..(copy_len + offset)]);
858        }
859        Ok(copy_len)
860    }
861}
862
863impl HostIoClose for TwizzlerTarget {
864    fn close(&mut self, fd: u32) -> HostIoResult<(), Self> {
865        self.inner.files.lock().unwrap().close(fd);
866        Ok(())
867    }
868}
869
870impl HostIo for TwizzlerTarget {
871    fn support_open(&mut self) -> Option<gdbstub::target::ext::host_io::HostIoOpenOps<'_, Self>> {
872        Some(self)
873    }
874
875    fn support_close(&mut self) -> Option<gdbstub::target::ext::host_io::HostIoCloseOps<'_, Self>> {
876        Some(self)
877    }
878
879    fn support_pread(&mut self) -> Option<gdbstub::target::ext::host_io::HostIoPreadOps<'_, Self>> {
880        Some(self)
881    }
882
883    fn support_fstat(&mut self) -> Option<HostIoFstatOps<'_, Self>> {
884        Some(self)
885    }
886
887    fn support_readlink(
888        &mut self,
889    ) -> Option<gdbstub::target::ext::host_io::HostIoReadlinkOps<'_, Self>> {
890        Some(self)
891    }
892}
893
894impl Breakpoints for TwizzlerTarget {
895    fn support_sw_breakpoint(
896        &mut self,
897    ) -> Option<gdbstub::target::ext::breakpoints::SwBreakpointOps<'_, Self>> {
898        Some(self)
899    }
900
901    fn support_hw_breakpoint(
902        &mut self,
903    ) -> Option<gdbstub::target::ext::breakpoints::HwBreakpointOps<'_, Self>> {
904        None
905    }
906
907    fn support_hw_watchpoint(
908        &mut self,
909    ) -> Option<gdbstub::target::ext::breakpoints::HwWatchpointOps<'_, Self>> {
910        None
911    }
912}
913
914impl SwBreakpoint for TwizzlerTarget {
915    fn add_sw_breakpoint(
916        &mut self,
917        addr: <Self::Arch as gdbstub::arch::Arch>::Usize,
918        _kind: <Self::Arch as gdbstub::arch::Arch>::BreakpointKind,
919    ) -> gdbstub::target::TargetResult<bool, Self> {
920        let mut saved_data = [0u8; size_of::<BpWord>()];
921        if self.read_addrs(
922            addr,
923            &mut saved_data,
924            NonZero::new(size_of::<BpWord>()).unwrap(),
925        )? != size_of::<BpWord>()
926        {
927            return Err(TargetError::Fatal(TwzError::Generic(GenericError::Other)));
928        }
929        let val = BpWord::from_ne_bytes(saved_data);
930        let mut bps = self.inner.bps.lock().unwrap();
931        let bp = *bps.insert(addr, val);
932        drop(bps);
933        bp.arm(self).map_err(|e| TargetError::Fatal(e))?;
934        Ok(true)
935    }
936
937    fn remove_sw_breakpoint(
938        &mut self,
939        addr: <Self::Arch as gdbstub::arch::Arch>::Usize,
940        kind: <Self::Arch as gdbstub::arch::Arch>::BreakpointKind,
941    ) -> gdbstub::target::TargetResult<bool, Self> {
942        let mut bps = self.inner.bps.lock().unwrap();
943        let Some(bp) = bps.remove(addr) else {
944            return Ok(false);
945        };
946        drop(bps);
947        bp.restore(self).map_err(|e| TargetError::Fatal(e))?;
948        Ok(false)
949    }
950}
951
952impl Target for TwizzlerTarget {
953    type Arch = gdbstub_arch::x86::X86_64_SSE;
954
955    type Error = TwzError;
956
957    fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> {
958        BaseOps::MultiThread(self)
959    }
960
961    fn guard_rail_implicit_sw_breakpoints(&self) -> bool {
962        true
963    }
964
965    fn support_exec_file(
966        &mut self,
967    ) -> Option<gdbstub::target::ext::exec_file::ExecFileOps<'_, Self>> {
968        Some(self)
969    }
970
971    fn support_host_io(&mut self) -> Option<gdbstub::target::ext::host_io::HostIoOps<'_, Self>> {
972        Some(self)
973    }
974
975    fn support_libraries_svr4(
976        &mut self,
977    ) -> Option<gdbstub::target::ext::libraries::LibrariesSvr4Ops<'_, Self>> {
978        Some(self)
979    }
980}
981
982pub struct TwizzlerConn {
983    peek: Option<u8>,
984    recv: Receiver<u8>,
985}
986
987impl TwizzlerConn {
988    pub fn new(recv: Receiver<u8>) -> Self {
989        Self { peek: None, recv }
990    }
991}
992
993impl Connection for TwizzlerConn {
994    type Error = TwzError;
995
996    fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
997        twizzler_abi::syscall::sys_kernel_console_write(
998            KernelConsoleSource::DebugConsole,
999            &[byte],
1000            KernelConsoleWriteFlags::empty(),
1001        );
1002        Ok(())
1003    }
1004
1005    fn flush(&mut self) -> Result<(), Self::Error> {
1006        Ok(())
1007    }
1008}
1009
1010impl ConnectionExt for TwizzlerConn {
1011    fn read(&mut self) -> Result<u8, Self::Error> {
1012        if let Some(byte) = self.peek.take() {
1013            return Ok(byte);
1014        }
1015        Ok(self.recv.recv().unwrap())
1016    }
1017
1018    fn peek(&mut self) -> Result<Option<u8>, Self::Error> {
1019        if let Some(byte) = &self.peek {
1020            return Ok(Some(*byte));
1021        }
1022        if let Ok(byte) = self.recv.try_recv() {
1023            self.peek = Some(byte);
1024            Ok(Some(byte))
1025        } else {
1026            Ok(None)
1027        }
1028    }
1029}