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.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();
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 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}