1use super::*;
2use crate::elf::*;
3use crate::os::udbg::{EventHandler, HandleResult};
4use crate::range::RangeValue;
5
6use anyhow::Context;
7use goblin::elf::sym::Sym;
8use nix::sys::ptrace::Options;
9use nix::sys::wait::waitpid;
10use parking_lot::RwLock;
11use procfs::process::{Stat as ThreadStat, Task};
12use serde_value::Value;
13use std::cell::{Cell, UnsafeCell};
14use std::collections::HashSet;
15use std::mem::transmute;
16use std::ops::Deref;
17use std::time::{Duration, Instant};
18
19const TRAP_BRKPT: i32 = 1;
20const TRAP_TRACE: i32 = 2;
21const TRAP_BRANCH: i32 = 3;
22const TRAP_HWBKPT: i32 = 4;
23const TRAP_UNK: i32 = 5;
24
25cfg_if! {
26 if #[cfg(target_os = "android")] {
27 const PTRACE_INTERRUPT: c_uint = 0x4207;
28 const PTRACE_SEIZE: c_uint = 0x4206;
29 }
30}
31
32pub struct ElfSymbol {
33 pub sym: Sym,
34 pub name: Arc<str>,
35}
36
37impl Deref for ElfSymbol {
38 type Target = Sym;
39
40 #[inline]
41 fn deref(&self) -> &Self::Target {
42 &self.sym
43 }
44}
45
46impl From<ElfSym<'_>> for ElfSymbol {
47 fn from(s: ElfSym<'_>) -> Self {
48 ElfSymbol {
49 sym: s.sym,
50 name: s.name.into(),
51 }
52 }
53}
54
55#[derive(Deref)]
56pub struct NixThread {
57 #[deref]
58 base: ThreadData,
59 stat: ThreadStat,
60}
61
62impl TryFrom<Task> for NixThread {
63 type Error = procfs::ProcError;
64
65 fn try_from(task: Task) -> Result<Self, Self::Error> {
66 Ok(NixThread {
67 base: ThreadData {
68 tid: task.tid,
69 wow64: false,
70 },
71 stat: task.stat()?,
72 })
73 }
74}
75
76impl GetProp for NixThread {}
77
78impl UDbgThread for NixThread {
79 fn name(&self) -> Arc<str> {
80 self.stat.comm.as_str().into()
81 }
82 fn status(&self) -> Arc<str> {
83 self.stat.state.to_string().into()
84 }
85 fn priority(&self) -> Option<i64> {
86 Some(self.stat.priority)
87 }
88}
89
90#[inline(always)]
91fn to_symbol(s: ElfSym) -> Symbol {
92 let flags = if s.is_function() {
93 SymbolFlags::FUNCTION
94 } else {
95 SymbolFlags::NONE
96 };
97 Symbol {
98 offset: s.st_value as u32,
99 name: s.name.into(),
100 flags: flags.bits(),
101 len: s.st_size as u32,
102 type_id: 0,
103 }
104}
105
106impl SymbolsData {
107 fn from_elf(path: &str) -> Self {
108 let mut this = Self::default();
109 this.load(path);
110 this
111 }
112
113 fn load(&mut self, path: &str) -> anyhow::Result<()> {
114 let map = Utils::mapfile(path.as_ref()).context("map")?;
115 let e = ElfHelper::parse(&map).context("parse")?;
116 let mut push_symbol = |s: ElfSym| {
117 if s.name.starts_with("$x.") {
118 return;
119 }
120 self.exports
121 .entry(s.offset())
122 .or_insert_with(|| to_symbol(s));
123 };
124 e.enum_symbol().for_each(&mut push_symbol);
125 e.enum_export().for_each(&mut push_symbol);
126 Ok(())
127 }
128}
129
130struct TimeCheck {
131 last: Cell<Instant>,
132 pub duration: Cell<Duration>,
133}
134
135impl TimeCheck {
136 pub fn new(duration: Duration) -> Self {
137 Self {
138 last: Instant::now().checked_sub(duration).unwrap().into(),
139 duration: duration.into(),
140 }
141 }
142
143 pub fn check(&self, mut callback: impl FnMut()) {
144 if self.last.get().elapsed() > self.duration.get() {
145 callback();
146 self.last.set(Instant::now());
147 }
148 }
149}
150
151#[derive(Deref)]
152pub struct TargetCommon {
153 #[deref]
154 _base: CommonBase,
155 pub threads: RwLock<HashSet<tid_t>>,
156 tc_module: TimeCheck,
157 tc_memory: TimeCheck,
158 mem_pages: RwLock<Vec<MemoryPage>>,
159 waiting: Cell<bool>,
160 pub trace_opts: Options,
161 pub hwbps: UnsafeCell<user_hwdebug_state>,
162}
163
164impl TargetCommon {
165 pub fn new(ps: Process) -> Self {
166 const TIMEOUT: Duration = Duration::from_secs(5);
167
168 let mut base = CommonBase::new(ps);
169 let image_path = base.process.image_path().unwrap_or_default();
170 base.process
171 .enum_module()
172 .ok()
173 .and_then(|mut iter| iter.find(|m| m.path.as_ref() == &image_path))
174 .map(|m| base.image_base = m.base);
175
176 let trace_opts = Options::PTRACE_O_EXITKILL
177 | Options::PTRACE_O_TRACECLONE
178 | Options::PTRACE_O_TRACEEXEC
179 | Options::PTRACE_O_TRACEVFORK
180 | Options::PTRACE_O_TRACEFORK;
181 Self {
182 _base: base,
183 tc_module: TimeCheck::new(Duration::from_secs(10)),
184 tc_memory: TimeCheck::new(Duration::from_secs(10)),
185 mem_pages: RwLock::new(Vec::new()),
186 threads: RwLock::new(HashSet::new()),
187 trace_opts,
188 waiting: Cell::new(false),
189 hwbps: unsafe { core::mem::zeroed() },
190 }
191 }
192
193 pub fn update_memory_page(&self) -> IoResult<()> {
194 *self.mem_pages.write() = self.process.enum_memory()?.collect::<Vec<_>>();
195 Ok(())
196 }
197
198 fn update_memory_page_check_time(&self) {
199 self.tc_memory.check(|| {
200 self.update_memory_page();
201 });
202 }
203
204 fn module_name<'a>(&self, name: &'a str) -> &'a str {
217 let tv = trim_ver(name);
218 let te = trim_allext(name);
219 let base = self.symgr.base.read();
220 if tv.len() < te.len() && !base.contains(tv) {
221 return tv;
222 }
223 if !base.contains(te) {
224 return te;
225 }
226 let te = trim_lastext(name);
227 if !base.contains(te) {
228 return te;
229 }
230 name
231 }
232
233 pub fn update_module(&self) -> IoResult<()> {
234 use goblin::elf::header::header32::Header as Header32;
235 use goblin::elf::header::header64::Header as Header64;
236 use std::io::Read;
237
238 for m in self.process.enum_module()? {
240 if self.find_module(m.base).is_some()
241 || m.name.ends_with(".oat")
242 || m.name.ends_with(".apk")
243 {
244 continue;
245 }
246 let name = self.module_name(&m.name);
247
248 let mut f = match File::open(m.path.as_ref()) {
250 Ok(f) => f,
251 Err(_) => {
252 continue;
254 }
255 };
256
257 if !f
259 .metadata()
260 .map(|m| m.file_type().is_file())
261 .unwrap_or(false)
262 {
263 continue;
264 }
265
266 let mut buf: Header64 = unsafe { core::mem::zeroed() };
267 if f.read(buf.as_mut_byte_array()).is_err() {
268 continue;
270 }
271
272 let arch = match ElfHelper::arch_name(buf.e_machine) {
273 Some(a) => a,
274 None => {
275 continue;
277 }
278 };
279
280 let entry = match arch {
281 "arm64" | "x86_64" => buf.e_entry as usize,
282 "x86" | "arm" => unsafe { transmute::<_, &Header32>(&buf).e_entry as usize },
283 a => {
284 continue;
286 }
287 };
288
289 let base = m.base;
290 let path = m.path.clone();
291 self.symgr.base.write().add(Module {
292 data: ModuleData {
293 base,
294 size: m.size,
295 arch,
296 entry,
297 user_module: false.into(),
298 name: name.into(),
299 path: path.clone(),
300 },
301 loaded: false.into(),
302 syms: SymbolsData::from_elf(&path).into(),
303 });
304 }
307 Ok(())
308 }
309
310 fn wait_event(&self, tb: &mut TraceBuf) -> Option<WaitStatus> {
311 self.waiting.set(true);
312 let mut status = 0;
313 let tid = unsafe { libc::waitpid(-1, &mut status, __WALL | WUNTRACED) };
314 self.waiting.set(false);
316 self.base.event_tid.set(tid);
317
318 if tid <= 0 {
319 return None;
320 }
321
322 let status = WaitStatus::from_raw(Pid::from_raw(tid), status).unwrap();
323 println!("[status] {status:?}");
324 Some(status)
325 }
326
327 pub fn hwbps(&self) -> &mut user_hwdebug_state {
328 unsafe { self.hwbps.get().as_mut().unwrap() }
329 }
330
331 pub fn get_bp_(&self, id: BpID) -> Option<Arc<Breakpoint>> {
332 Some(self.bp_map.read().get(&id)?.clone())
333 }
334
335 pub fn handle_breakpoint(
336 &self,
337 this: &dyn UDbgTarget,
338 eh: &mut dyn EventHandler,
339 tb: &mut TraceBuf,
340 ) -> UDbgResult<HandleResult> {
341 let mut address = *tb.user.regs.ip();
343 let is_step = tb.si.si_code == TRAP_TRACE;
344 if IS_X86 {
345 if is_step || tb.si.si_code == TRAP_HWBKPT {
346 address = unsafe { tb.si.si_addr() as _ };
347 } else {
348 address -= 1;
349 }
350 }
351 *tb.user.regs.ip() = address;
352
353 let tid = self.base.event_tid.get();
354 let bp = match self
355 .get_bp_(address as _)
356 .or_else(|| self.get_hwbp(tb))
357 .ok_or(UDbgError::NotFound)
358 {
359 Ok(bp) => bp,
360 Err(_) if is_step => {
361 tb.user.set_step(false);
362 self.handle_reply(this, tb.call(UEvent::Step), &mut tb.user);
363 return Ok(None);
364 }
365 Err(err) => return Err(err),
366 };
367
368 bp.hit_count.set(bp.hit_count.get() + 1);
369 if bp.temp.get() {
370 self.remove_breakpoint(this, &bp);
371 }
372
373 let hitted = bp.hit_tid.map(|t| t == tid).unwrap_or(true);
375 if hitted {
376 self.handle_reply(this, tb.call(UEvent::Breakpoint(bp.clone())), &mut tb.user);
377 }
378
379 let id = bp.get_id();
380
381 #[cfg(target_arch = "x86_64")]
382 if bp.is_hard() && self.get_bp(id).is_some() {
383 tb.user.disable_hwbp_temporarily();
384 }
385
386 if bp.is_soft() && self.get_bp(id).is_some() {
388 if bp.enabled.get() {
390 self.enable_breadpoint(this, &bp, false)
392 .log_error("disable bp");
393 assert_ne!(&this.read_value::<BpInsn>(bp.address()).unwrap(), BP_INSN);
394
395 let user_step = tb.user.is_step();
396
397 tb.user.set_step(true);
399 #[cfg(any(target_arch = "aarch64"))]
400 ptrace::step(Pid::from_raw(tid), None);
401 loop {
402 eh.cont(None, tb);
403 match eh.fetch(tb) {
404 Some(_) => {
405 if core::ptr::eq(self, &tb.target.0) && self.base.event_tid.get() == tid
406 {
407 break;
408 } else if let Some(s) = eh.handle(tb) {
409 eh.cont(s, tb);
410 } else {
411 return Ok(None);
412 }
413 }
414 None => return Ok(None),
415 }
416 }
417
418 self.enable_breadpoint(this, &bp, true)
419 .log_error("enable bp");
420 return if user_step {
421 Ok(eh.handle(tb).unwrap_or(None))
422 } else {
423 tb.user.set_step(false);
424 Ok(None)
426 };
427 }
428 }
429
430 Ok(None)
431 }
432
433 fn enum_module<'a>(
434 &'a self,
435 ) -> UDbgResult<Box<dyn Iterator<Item = Arc<dyn UDbgModule + 'a>> + 'a>> {
436 self.update_module();
437 Ok(self.symgr.enum_module())
438 }
439
440 fn enum_memory<'a>(&'a self) -> Result<Box<dyn Iterator<Item = MemoryPage> + 'a>, UDbgError> {
441 self.update_memory_page();
442 Ok(Box::new(self.mem_pages.read().clone().into_iter()))
443 }
444
445 fn enum_handle<'a>(&'a self) -> UDbgResult<Box<dyn Iterator<Item = HandleInfo> + 'a>> {
446 use procfs::process::FDTarget;
447
448 Ok(Box::new(
449 self.process.fd().context("fd iter")?.flatten().map(|fd| {
450 let (ty, name) = match fd.target {
451 FDTarget::Path(p) => ("Path".into(), p.to_string_lossy().into_owned()),
452 FDTarget::Socket(s) => ("Socket".into(), s.to_string()),
453 FDTarget::Net(n) => ("Net".into(), n.to_string()),
454 FDTarget::Pipe(p) => ("Pipe".into(), p.to_string()),
455 FDTarget::AnonInode(i) => ("INode".into(), i),
456 FDTarget::MemFD(m) => ("MemFD".into(), m),
457 FDTarget::Other(a, b) => (a.into(), b.to_string()),
458 };
459 HandleInfo {
460 pid: self.process.pid,
461 ty: 0,
462 handle: fd.fd as _,
463 name,
464 type_name: ty,
465 }
466 }),
467 ))
468 }
469
470 pub fn enable_hwbp(
471 &self,
472 dbg: &dyn UDbgTarget,
473 bp: &Breakpoint,
474 info: HwbpInfo,
475 enable: bool,
476 ) -> UDbgResult<bool> {
477 let mut result = Ok(enable);
478 for &tid in self.threads.read().iter() {
480 if bp.hit_tid.is_some() && bp.hit_tid != Some(tid) {
481 continue;
482 }
483 result = self.enable_hwbp_for_thread(tid, bp, info, enable);
485 if let Err(e) = &result {
486 udbg_ui().error(format!("enable_hwbp_for_thread for {} failed {:?}", tid, e));
487 }
489 }
490 if result.is_ok() {
493 bp.enabled.set(enable);
494 }
495 result
496 }
497
498 }
624
625fn trim_ver(name: &str) -> &str {
626 use regex::Regex;
627 &name[..Regex::new(r"-\d")
628 .unwrap()
629 .find(name)
630 .map(|p| p.start())
631 .unwrap_or(name.len())]
632}
633
634#[inline]
635fn trim_allext(name: &str) -> &str {
636 &name[..name.find(|c| c == '.').unwrap_or(name.len())]
637}
638
639#[inline]
640fn trim_lastext(name: &str) -> &str {
641 &name[..name.rfind(|c| c == '.').unwrap_or(name.len())]
642}
643
644pub fn ptrace_interrupt(tid: tid_t) -> bool {
645 unsafe { ptrace(PTRACE_INTERRUPT as _, tid, 0, 0) == 0 }
646}
647
648pub fn ptrace_seize(tid: tid_t, flags: c_int) -> bool {
649 unsafe { ptrace(PTRACE_SEIZE as _, tid, 0, flags) == 0 }
650}
651
652pub fn ptrace_getevtmsg<T: Copy>(tid: tid_t, result: &mut T) -> bool {
653 unsafe { ptrace(PTRACE_GETEVENTMSG, tid, 0, result) == 0 }
654}
655
656pub fn ptrace_step_and_wait(tid: pid_t) -> bool {
657 let tid = Pid::from_raw(tid);
658 ptrace::step(tid, None);
659 match waitpid(tid, None) {
660 Ok(t) => {
661 let pid = t.pid();
662 if pid == Some(tid) {
663 return true;
664 }
665 udbg_ui().error(format!("step unexpect tid: {pid:?}"));
666 false
667 }
668 Err(_) => false,
669 }
670}
671
672#[derive(Deref)]
673pub struct ProcessTarget(pub TargetCommon);
674
675unsafe impl Send for ProcessTarget {}
676unsafe impl Sync for ProcessTarget {}
677
678impl ProcessTarget {
679 pub fn open(pid: pid_t) -> UDbgResult<Arc<Self>> {
680 let ps = Process::from_pid(pid)?;
681 Ok(Arc::new(Self(TargetCommon::new(ps))))
682 }
683
684 pub fn insert_thread(&self, tid: tid_t) -> bool {
685 if self.threads.write().insert(tid) {
686 if let Err(err) = ptrace::setoptions(Pid::from_raw(tid), self.trace_opts) {
687 udbg_ui().error(format!("ptrace_setopt {tid} {err:?}",));
688 }
689 true
690 } else {
691 false
692 }
693 }
694
695 pub fn remove_thread(&self, tid: tid_t, s: i32, tb: &mut TraceBuf) -> bool {
696 let mut threads = self.threads.write();
697 if threads.remove(&tid) {
698 tb.call(UEvent::ThreadExit(s as u32));
699 if threads.is_empty() {
700 tb.call(UEvent::ProcessExit(s as u32));
701 }
702 } else {
703 udbg_ui().error(&format!("tid {tid} not found"));
704 }
705 threads.is_empty()
706 }
707}
708
709impl WriteMemory for ProcessTarget {
710 fn write_memory(&self, addr: usize, data: &[u8]) -> Option<usize> {
711 self.process.write_memory(addr, data)
712 }
715}
716
717impl TargetMemory for ProcessTarget {
718 fn enum_memory<'a>(&'a self) -> UDbgResult<Box<dyn Iterator<Item = MemoryPage> + 'a>> {
719 self.0.enum_memory()
720 }
721
722 fn virtual_query(&self, address: usize) -> Option<MemoryPage> {
723 self.update_memory_page_check_time();
724 RangeValue::binary_search(&self.mem_pages.read().as_slice(), address).map(|r| r.clone())
725 }
726
727 fn collect_memory_info(&self) -> Vec<MemoryPage> {
728 self.0.enum_memory().unwrap().collect::<Vec<_>>()
729 }
730}
731
732impl GetProp for ProcessTarget {
733 fn get_prop(&self, key: &str) -> UDbgResult<serde_value::Value> {
734 Ok(Value::Unit)
740 }
741}
742
743impl TargetControl for ProcessTarget {
744 fn detach(&self) -> UDbgResult<()> {
745 let attached = self.base.status.get() == UDbgStatus::Attached;
746 self.base.status.set(UDbgStatus::Detaching);
747 if attached {
748 self.breakk()
749 } else {
750 Ok(())
751 }
752 }
753
754 fn kill(&self) -> UDbgResult<()> {
755 if unsafe { kill(self.process.pid, SIGKILL) } == 0 {
756 Ok(())
757 } else {
758 Err(UDbgError::system())
759 }
760 }
761
762 fn breakk(&self) -> UDbgResult<()> {
763 self.base.check_attached()?;
764 Ok(nix::sys::signal::kill(
773 Pid::from_raw(self.pid()),
774 Signal::SIGSTOP,
775 )?)
776 }
777
778 fn wait_exit(&self, timeout: Option<u32>) -> UDbgResult<Option<u32>> {
779 match nix::sys::wait::waitpid(Pid::from_raw(self.pid()), None).context("wait")? {
780 WaitStatus::Exited(_, code) => Ok(Some(code as _)),
781 err => Err(anyhow::anyhow!("unexpected status: {err:?}").into()),
782 }
783 }
784
785 fn suspend(&self) -> UDbgResult<()> {
786 if self.status() == UDbgStatus::Detaching || self.status() == UDbgStatus::Attached {
787 return Err(anyhow::anyhow!("target is attached").into());
788 }
789
790 Ok(nix::sys::signal::kill(
791 Pid::from_raw(self.pid()),
792 Signal::SIGSTOP,
793 )?)
794 }
795
796 fn resume(&self) -> UDbgResult<()> {
797 if self.status() == UDbgStatus::Detaching || self.status() == UDbgStatus::Attached {
798 return Err(anyhow::anyhow!("target is attached").into());
799 }
800
801 Ok(nix::sys::signal::kill(
802 Pid::from_raw(self.pid()),
803 Signal::SIGCONT,
804 )?)
805 }
806}
807
808impl Target for ProcessTarget {
812 fn base(&self) -> &TargetBase {
813 &self._base
814 }
815
816 fn process(&self) -> Option<&Process> {
817 Some(&self.process)
818 }
819
820 fn enum_module<'a>(
821 &'a self,
822 ) -> UDbgResult<Box<dyn Iterator<Item = Arc<dyn UDbgModule + 'a>> + 'a>> {
823 self.0.enum_module()
824 }
825
826 fn find_module(&self, module: usize) -> Option<Arc<dyn UDbgModule>> {
827 let mut result = self.symgr.find_module(module);
828 self.tc_module.check(|| {
829 self.update_module();
830 result = self.symgr.find_module(module);
831 });
832 Some(result?)
833 }
834
835 fn get_module(&self, module: &str) -> Option<Arc<dyn UDbgModule>> {
836 Some(self.symgr.get_module(module).or_else(|| {
837 self.0.update_module();
838 self.symgr.get_module(module)
839 })?)
840 }
841
842 fn open_thread(&self, tid: tid_t) -> UDbgResult<Box<dyn UDbgThread>> {
843 let task = self.process.task_from_tid(tid).context("task")?;
844 Ok(Box::new(NixThread {
845 base: ThreadData { tid, wow64: false },
846 stat: task.stat().context("stat")?,
847 }))
848 }
849
850 fn enum_handle<'a>(&'a self) -> UDbgResult<Box<dyn Iterator<Item = HandleInfo> + 'a>> {
851 self.0.enum_handle()
852 }
853
854 fn enum_thread(
855 &self,
856 detail: bool,
857 ) -> UDbgResult<Box<dyn Iterator<Item = Box<dyn UDbgThread>> + '_>> {
858 Ok(Box::new(
859 self.process
860 .tasks()?
861 .filter_map(Result::ok)
862 .filter_map(|task| {
863 Some(Box::new(NixThread::try_from(task).log_error("task stat")?)
864 as Box<dyn UDbgThread>)
865 }),
866 ))
867 }
868}
869
870impl UDbgTarget for ProcessTarget {}
871
872impl EventHandler for DefaultEngine {
873 fn fetch(&mut self, buf: &mut TraceBuf) -> Option<()> {
874 loop {
875 self.status = waitpid(None, Some(WaitPidFlag::__WALL)).ok()?;
876 self.tid = self
878 .status
879 .pid()
880 .map(|p| p.as_raw() as tid_t)
881 .unwrap_or_default();
882
883 if matches!(
884 self.status,
885 WaitStatus::Stopped(_, _) ) {
887 buf.update_regs(self.tid);
888 buf.update_siginfo(self.tid);
889 }
896
897 let target = self
898 .targets
899 .iter()
900 .find(|&t| self.tid == t.pid() as tid_t || t.threads.read().contains(&self.tid))
901 .cloned()
902 .or_else(|| {
903 self.targets
904 .iter()
905 .find(|&t| t.process.task_from_tid(self.tid).is_ok())
906 .cloned()
907 });
908
909 if let Some(target) = target {
910 buf.target = target.clone();
911 buf.target.base.event_tid.set(self.tid as _);
912
913 if target.base.status.get() == UDbgStatus::Detaching {
914 break;
915 }
916
917 let mut cont = false;
918 if target.base.status.get() < UDbgStatus::Attached {
919 target.base.status.set(UDbgStatus::Attached);
920 cont = true;
922 }
923
924 if buf.target.insert_thread(self.tid) {
926 buf.call(UEvent::ThreadCreate(self.tid));
927 cont = true;
928 }
929
930 if cont {
931 ptrace::cont(Pid::from_raw(self.tid), None);
932 continue;
933 }
934
935 break;
936 } else {
937 udbg_ui().warn(format!("{} is not traced", self.tid));
938 ptrace::cont(Pid::from_raw(self.tid), None);
939 }
940 }
941 Some(())
942 }
943
944 fn handle(&mut self, buf: &mut TraceBuf) -> Option<HandleResult> {
945 let status = self.status.clone();
946 let this = buf.target.clone();
947 let tid = self.tid;
948
949 if this.base.status.get() == UDbgStatus::Detaching {
950 return Some(None);
951 }
952 Some(match status {
953 WaitStatus::Stopped(_, sig) => loop {
954 if sig == Signal::SIGTRAP {
955 if let Some(result) = this
956 .handle_breakpoint(this.as_ref(), self, buf)
957 .log_error("handle trap")
958 {
959 break result;
960 }
961 }
962 break match buf.call(UEvent::Exception {
963 first: true,
964 code: sig as _,
965 }) {
966 UserReply::Run(false) => Some(sig),
967 reply => {
968 this.handle_reply(this.as_ref(), reply, &mut buf.user);
969 None
970 }
971 };
972 },
973 WaitStatus::PtraceEvent(_, sig, code) => {
974 match code {
975 PTRACE_EVENT_STOP => {
976 this.insert_thread(tid);
977 }
978 PTRACE_EVENT_CLONE => {
979 let new_tid =
980 ptrace::getevent(Pid::from_raw(tid)).unwrap_or_default() as tid_t;
981 buf.call(UEvent::ThreadCreate(new_tid));
982 ptrace::attach(Pid::from_raw(new_tid));
984 }
985 PTRACE_EVENT_FORK | PTRACE_EVENT_VFORK => {
986 let new_pid =
987 ptrace::getevent(Pid::from_raw(tid)).unwrap_or_default() as pid_t;
988 ProcessTarget::open(new_pid)
993 .log_error("open child")
994 .map(|t| {
995 t.base.status.set(if udbg_ui().base().trace_child.get() {
996 UDbgStatus::Attached
997 } else {
998 UDbgStatus::Detaching
999 });
1000 self.targets.push(t);
1001 });
1002 }
1003 PTRACE_EVENT_EXEC => {
1004 buf.call(UEvent::ProcessCreate);
1005 }
1006 _ => {}
1007 }
1008 None
1009 }
1010 WaitStatus::Signaled(_, sig, coredump) => {
1012 buf.call(UEvent::Exception {
1013 first: false,
1014 code: sig as _,
1015 });
1016 let code = ptrace::getevent(Pid::from_raw(self.tid)).unwrap_or(-1);
1017 if !matches!(sig, Signal::SIGSTOP) {
1018 if this.remove_thread(tid, code as _, buf) {
1019 self.targets.retain(|t| !Arc::ptr_eq(t, &this));
1020 }
1021 }
1022 Some(sig)
1023 }
1024 WaitStatus::Exited(_, code) => {
1026 if this.remove_thread(tid, code, buf) {
1027 self.targets.retain(|t| !Arc::ptr_eq(t, &this));
1028 }
1029 None
1030 }
1031 _ => unreachable!("status: {status:?}"),
1032 })
1033 }
1034
1035 fn cont(&mut self, sig: HandleResult, buf: &mut TraceBuf) {
1036 let this = buf.target.clone();
1037 let tid = Pid::from_raw(self.tid as _);
1038
1039 if this.base.status.get() == UDbgStatus::Detaching {
1040 for bp in this.get_breakpoints() {
1041 bp.enable(false);
1042 }
1043 for &tid in this.threads.read().iter() {
1044 ptrace::detach(Pid::from_raw(tid as _), None)
1045 .log_error_with(|err| format!("ptrace_detach({tid}) failed: {err:?}"));
1046 }
1047 self.targets.retain(|t| !Arc::ptr_eq(&this, t));
1048 this.base.status.set(UDbgStatus::Detached);
1049 } else if buf.regs_dirty {
1050 buf.regs_dirty = false;
1051 buf.write_regs(self.tid);
1052 }
1053
1054 ptrace::cont(tid, sig);
1055 }
1056}
1057
1058pub struct DefaultEngine {
1059 pub targets: Vec<Arc<ProcessTarget>>,
1060 pub status: WaitStatus,
1061 pub inited: bool,
1062 pub cloned_tids: HashSet<tid_t>,
1063 pub tid: tid_t,
1064}
1065
1066impl Default for DefaultEngine {
1067 fn default() -> Self {
1068 Self {
1069 targets: Default::default(),
1070 status: WaitStatus::StillAlive,
1071 inited: false,
1072 tid: 0,
1073 cloned_tids: Default::default(),
1074 }
1075 }
1076}
1077
1078impl UDbgEngine for DefaultEngine {
1079 fn open(&mut self, pid: pid_t) -> UDbgResult<Arc<dyn UDbgTarget>> {
1080 Ok(ProcessTarget::open(pid)?)
1081 }
1082
1083 fn attach(&mut self, pid: pid_t) -> UDbgResult<Arc<dyn UDbgTarget>> {
1084 let this = ProcessTarget::open(pid)?;
1085 for tid in this.process.tasks()?.filter_map(|t| t.ok().map(|t| t.tid)) {
1087 ptrace::attach(Pid::from_raw(tid)).with_context(|| format!("attach {tid}"))?;
1088 }
1089 waitpid(Pid::from_raw(pid), Some(WaitPidFlag::WUNTRACED))
1091 .with_context(|| format!("waitpid({pid})"))?;
1092 self.targets.push(this.clone());
1093 Ok(this)
1094 }
1095
1096 fn create(
1097 &mut self,
1098 path: &str,
1099 cwd: Option<&str>,
1100 args: &[&str],
1101 ) -> UDbgResult<Arc<dyn UDbgTarget>> {
1102 match unsafe { libc::fork() } {
1103 0 => unsafe {
1104 use std::ffi::CString;
1105 ptrace::traceme();
1106 let path = CString::new(path).unwrap();
1107 let args = args
1108 .iter()
1109 .map(|&arg| CString::new(arg).unwrap())
1110 .collect::<Vec<_>>();
1111 let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
1112 argv.insert(0, path.as_ptr());
1113 argv.push(core::ptr::null());
1114 libc::execvp(path.as_ptr().cast(), argv.as_ptr());
1115 unreachable!();
1116 },
1117 -1 => Err(UDbgError::system()),
1118 pid => {
1119 waitpid(Pid::from_raw(pid), Some(WaitPidFlag::WUNTRACED))
1120 .with_context(|| format!("waitpid({pid})"))?;
1121 let ps = Process::from_pid(pid).context("open")?;
1122 let this = Arc::new(ProcessTarget(TargetCommon::new(ps)));
1123 self.targets.push(this.clone());
1124 Ok(this)
1125 }
1126 }
1127 }
1128
1129 fn event_loop<'a>(&mut self, callback: &mut UDbgCallback<'a>) -> UDbgResult<()> {
1130 self.targets.iter().for_each(|t| {
1131 t.update_module();
1132 t.update_memory_page();
1133 });
1134
1135 let target = self
1136 .targets
1137 .iter()
1138 .next()
1139 .map(Clone::clone)
1140 .context("no attached target")?;
1141
1142 self.tid = target.process.pid;
1143 let buf = &mut TraceBuf {
1144 callback,
1145 user: unsafe { core::mem::zeroed() },
1146 si: unsafe { core::mem::zeroed() },
1147 regs_dirty: false,
1148 target,
1149 };
1150
1151 buf.target.base.event_tid.set(self.tid);
1152 buf.target.insert_thread(self.tid);
1153
1154 if buf.target.base.status.get() == UDbgStatus::Detaching {
1155 self.cont(None, buf);
1156 return Ok(());
1157 }
1158 buf.target.base.status.set(UDbgStatus::Attached);
1159
1160 buf.call(UEvent::InitBp);
1161 buf.call(UEvent::ProcessCreate);
1162 buf.call(UEvent::ThreadCreate(self.tid));
1163 self.cont(None, buf);
1164
1165 while let Some(s) = self.fetch(buf).and_then(|_| self.handle(buf)) {
1166 self.cont(s, buf);
1167 if self.targets.is_empty() {
1168 break;
1169 }
1170 }
1171
1172 Ok(())
1173 }
1174}