1#![cfg(target_os = "linux")]
2#![allow(dead_code)]
3#[cfg(not(target_arch = "x86_64"))]
4compile_error!("Not x86_64 arch");
5
6use breakpoint_site::IdType;
7use disassembler::print_disassembly;
8use ffi::sig_abbrev;
9use gimli::{DW_AT_location, DW_AT_type, DW_TAG_formal_parameter, DW_TAG_variable};
10use indoc::indoc;
11use nix::libc::SIGTRAP;
12use nix::sys::signal::Signal;
13use nix::unistd::Pid;
14use parse::{parse_register_value, parse_vector};
15use process::{Process, ProcessState, StopReason, StoppointId, SyscallCatchPolicy, TrapType};
16use register_info::{GRegisterInfos, RegisterType, register_info_by_name};
17use sdb_error::SdbError;
18use std::any::{Any, TypeId};
19use std::cell::Ref;
20use std::cmp::min;
21use std::collections::HashSet;
22use std::fs::File;
23use std::io::{BufRead, BufReader};
24use std::path::Path;
25use std::rc::Rc;
26use syscalls::{syscall_id_to_name, syscall_name_to_id};
27use target::Target;
28use traits::FromLowerHexStr;
29use traits::StoppointTrait;
30use types::{StoppointMode, VirtualAddress};
31mod breakpoint;
32mod breakpoint_site;
33mod disassembler;
34mod ffi;
35mod parse;
36mod stack;
37mod stoppoint_collection;
38mod utils;
39mod watchpoint;
40
41pub mod dwarf;
42pub mod elf;
43pub mod syscalls;
44pub mod target;
45pub mod traits;
46pub use utils::ResultLogExt;
47
48use watchpoint::WatchPoint;
49
50use breakpoint::AddressBreakpoint;
51use breakpoint::FunctionBreakpoint;
52use breakpoint::LineBreakpoint;
53
54use register_info::RegisterInfo;
55
56use types::TypedData;
57
58use dwarf::DwarfExpressionSimpleLocation;
59use register_info::register_info_by_dwarf;
60
61use dwarf::DwarfExpressionResult;
62
63pub mod bit;
64pub mod pipe;
65pub mod process;
66pub mod register_info;
67pub mod registers;
68pub(crate) mod sdb_error;
69pub mod types;
70
71pub fn attach(args: &[&str]) -> Result<Rc<Target>, SdbError> {
74 if args.len() == 3 && args[1] == "-p" {
75 let pid = Pid::from_raw(args[2].parse().unwrap());
76 return Target::attach(pid);
77 } else {
78 let program_path = args[1];
79 let target = Target::launch(Path::new(program_path), None)?;
80 let pid = target.get_process().pid();
81 println!("Launched process with PID {pid}");
82 return Ok(target);
83 }
84}
85
86fn print_stop_reason(target: &Target, reason: StopReason) -> Result<(), SdbError> {
87 let process = &target.get_process();
88 let pid = process.pid();
89 match reason.reason {
90 ProcessState::Exited => {
91 let info = reason.info;
92 println!("Process {pid} exited with status {info}");
93 }
94 ProcessState::Terminated => {
95 let sig: Signal = reason.info.try_into().unwrap();
96 println!("Process {pid} terminated with signal {}", sig.as_str());
97 }
98 ProcessState::Stopped => {
99 get_signal_stop_reason(target, reason)?;
100 println!(
101 "Thread {} {}",
102 reason.tid,
103 get_signal_stop_reason(target, reason)?
104 );
105 }
106 ProcessState::Running => {
107 eprintln!("Incorrect state");
108 }
109 };
110 Ok(())
111}
112
113fn get_signal_stop_reason(target: &Target, reason: StopReason) -> Result<String, SdbError> {
114 let process = target.get_process();
115 let pc = process.get_pc(Some(reason.tid));
116 let mut msg = format!(
117 "stopped with signal {} at {:#x}\n",
118 sig_abbrev(reason.info),
119 pc.addr()
120 );
121 let line = target.line_entry_at_pc(Some(reason.tid))?;
122 if !line.is_end() {
123 let file = line
124 .get_current()
125 .file_entry
126 .as_ref()
127 .unwrap()
128 .path
129 .file_name()
130 .unwrap()
131 .to_str()
132 .unwrap();
133 msg += &format!(" at {}:{}\n", file, line.get_current().line);
134 }
135 let func_name = target.function_name_at_address(pc)?;
136 if !func_name.is_empty() {
137 msg += &format!(" in {func_name}\n");
138 }
139 if reason.info == SIGTRAP {
140 msg += &get_sigtrap_info(&process, reason)?;
141 }
142 Ok(msg)
143}
144
145fn get_sigtrap_info(process: &Process, reason: StopReason) -> Result<String, SdbError> {
146 if reason.trap_reason == Some(TrapType::SoftwareBreak) {
147 let site = process
148 .breakpoint_sites()
149 .borrow()
150 .get_by_address(process.get_pc(Some(reason.tid)))?;
151 return Ok(format!(" (breakpoint {})", site.borrow().id()));
152 }
153 if reason.trap_reason == Some(TrapType::HardwareBreak) {
154 let id = process.get_current_hardware_stoppoint(Some(reason.tid))?;
155
156 match id {
157 StoppointId::BreakpointSite(id) => return Ok(format!(" (breakpoint {id})")),
158 StoppointId::Watchpoint(id) => {
159 let point = process.watchpoints().borrow().get_by_id(id)?;
160 let point = point.borrow() as Ref<dyn Any>;
161 let point = point.downcast_ref::<WatchPoint>().unwrap();
162 let mut msg = format!(" (watchpoint {})", point.id());
163 if point.data() == point.previous_data() {
164 msg += &format!("\nValue: {:#x}", point.data());
165 } else {
166 msg += &format!(
167 "\nOld value: {:#x}\nNew value: {:#x}",
168 point.previous_data(),
169 point.data()
170 );
171 }
172 return Ok(msg);
173 }
174 }
175 }
176
177 if reason.trap_reason == Some(TrapType::SingleStep) {
178 return Ok(" (single step)".to_string());
179 }
180 if reason.trap_reason == Some(TrapType::Syscall) {
181 let info = reason.syscall_info.as_ref().unwrap();
182 return match info.data {
183 process::SyscallData::Args(data) => Ok(format!(
184 " (syscall entry)\nsyscall: {}({})",
185 syscall_id_to_name(info.id as i64)?,
186 data.iter()
187 .map(|d| { format!("{d:#x}") })
188 .collect::<Vec<_>>()
189 .join(",")
190 )),
191 process::SyscallData::Ret(data) => {
192 Ok(format!(" (syscall exit)\nsyscall returned {data:#x}"))
193 }
194 };
195 }
196
197 return Ok("".to_string());
198}
199
200pub fn handle_command(target: &Rc<Target>, line: &str) -> Result<(), SdbError> {
201 let args: Vec<&str> = line.split(" ").filter(|s| !s.is_empty()).collect();
202 let process = &target.get_process();
203 let cmd = args[0];
204 if cmd == "continue" {
205 process.resume_all_threads()?;
206 let reason = process.wait_on_signal(Pid::from_raw(-1))?;
207 handle_stop(target, reason)?;
208 } else if cmd == "help" {
209 print_help(&args);
210 } else if cmd == "register" {
211 handle_register_command(target, &args)?;
212 } else if cmd == "breakpoint" {
213 handle_breakpoint_command(target, &args)?;
214 } else if cmd == "memory" {
215 handle_memory_command(process, &args)?;
216 } else if cmd == "disassemble" {
217 handle_disassemble_command(process, &args)?;
218 } else if cmd == "watchpoint" {
219 handle_watchpoint_command(process, &args)?;
220 } else if cmd == "catchpoint" {
221 handle_catchpoint_command(process, &args)?;
222 } else if cmd == "next" {
223 let reason = target.step_over(None)?;
224 handle_stop(target, reason)?;
225 } else if cmd == "finish" {
226 let reason = target.step_out(None)?;
227 handle_stop(target, reason)?;
228 } else if cmd == "step" {
229 let reason = target.step_in(None)?;
230 handle_stop(target, reason)?;
231 } else if cmd == "stepi" {
232 let reason = process.step_instruction(None)?;
233 handle_stop(target, reason)?;
234 } else if cmd == "up" {
235 target.get_stack(None).borrow_mut().up();
236 print_code_location(target)?;
237 } else if cmd == "down" {
238 target.get_stack(None).borrow_mut().down();
239 print_code_location(target)?;
240 } else if cmd == "backtrace" {
241 print_backtrace(target)?;
242 } else if cmd == "thread" {
243 handle_thread_command(target, &args)?;
244 } else if cmd == "variable" {
245 handle_variable_command(target, &args)?;
246 } else if cmd == "expression" {
247 let expr = &line[line.find(' ').unwrap() + 1..];
248 let ret = target.evaluate_expression(expr, None)?;
249 if let Some(ret) = ret {
250 let str = ret.return_value.visualize(&target.get_process(), 0)?;
251 println!("${}: {}", ret.id, str);
252 }
253 } else {
254 eprintln!("Unknown command");
255 }
256 Ok(())
257}
258
259fn handle_variable_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
260 if args.len() < 2 {
261 print_help(&["help", "variable"]);
262 return Ok(());
263 }
264 if args[1] == "locals" {
265 handle_variable_locals_command(target)?;
266 return Ok(());
267 }
268
269 if args.len() < 3 {
270 print_help(&["help", "variable"]);
271 return Ok(());
272 }
273 if args[1] == "read" {
274 handle_variable_read_command(target, args)?;
275 } else if args[1] == "location" {
276 handle_variable_location_command(target, args)?;
277 }
278 Ok(())
279}
280
281fn handle_variable_location_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
282 let name = args[2];
283 let pc = target.get_pc_file_address(None);
284 let var = target.find_variable(name, &pc)?;
285 if var.is_none() {
286 eprintln!("Variable not found");
287 return Ok(());
288 }
289 let var = var.unwrap();
290 let loc = var.index(DW_AT_location.0 as u64)?.as_evaluated_location(
291 &target.get_process(),
292 &target.get_stack(None).borrow().current_frame().registers,
293 false,
294 )?;
295 let print_simple_location = |loc: &DwarfExpressionSimpleLocation| -> Result<(), SdbError> {
296 if let DwarfExpressionSimpleLocation::Register { reg_num } = loc {
297 let name = register_info_by_dwarf(*reg_num as i32)?.name;
298 println!("Register: {name}");
299 } else if let DwarfExpressionSimpleLocation::Address { address } = loc {
300 println!("Address: {:#x}", address.addr());
301 } else {
302 println!("None");
303 }
304 Ok(())
305 };
306
307 if let DwarfExpressionResult::SimpleLocation(loc) = loc {
308 print_simple_location(&loc)?;
309 } else if let DwarfExpressionResult::Pieces(pieces) = loc {
310 for piece in pieces.pieces {
311 print!(
312 "Piece: offset = {}, bit size = {}, location = ",
313 piece.offset, piece.bit_size
314 );
315 print_simple_location(&piece.location)?;
316 }
317 }
318 Ok(())
319}
320
321fn handle_variable_read_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
322 let name = args[2];
323 let pc = target.get_pc_file_address(None);
324 let data = target.resolve_indirect_name(name, &pc)?;
325 let str = data.variable.unwrap().visualize(&target.get_process(), 0)?;
326 println!("Value: {str}");
327 Ok(())
328}
329
330fn handle_variable_locals_command(target: &Rc<Target>) -> Result<(), SdbError> {
331 let pc = target.get_pc_file_address(None);
332 let scopes = pc.rc_elf_file().get_dwarf().scopes_at_address(&pc)?;
333 let mut seen = HashSet::new();
334 for scope in scopes {
335 for var in scope.children() {
336 let name = var.name()?.unwrap_or("".to_string());
337 let tag = var.abbrev_entry().tag;
338 if tag as u16 == DW_TAG_variable.0
339 || tag as u16 == DW_TAG_formal_parameter.0
340 && !name.is_empty()
341 && !seen.contains(&name)
342 {
343 let loc = var.index(DW_AT_location.0 as u64)?.as_evaluated_location(
344 &target.get_process(),
345 &target.get_stack(None).borrow().current_frame().registers,
346 false,
347 )?;
348 let type_ = var.index(DW_AT_type.0 as u64)?.as_type();
349 let value = target.read_location_data(&loc, type_.byte_size()?, None)?;
350 let str = TypedData::builder()
351 .data(value)
352 .type_(type_)
353 .build()
354 .visualize(&target.get_process(), 0)?;
355 println!("{name}: {str}");
356 seen.insert(name);
357 }
358 }
359 }
360 Ok(())
361}
362
363fn handle_thread_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
364 if args.len() < 2 {
365 print_help(&["help", "thread"]);
366 return Ok(());
367 }
368 if args[1] == "list" {
369 for (tid, thread) in target.threads().borrow().iter() {
370 let prefix = if *tid == target.get_process().current_thread() {
371 "*"
372 } else {
373 " "
374 };
375 println!(
376 "{prefix}Thread {tid}: {}",
377 get_signal_stop_reason(target, thread.state.upgrade().unwrap().borrow().reason)?
378 );
379 }
380 } else if args[1] == "select" {
381 if args.len() != 3 {
382 print_help(&["help", "thread"]);
383 return Ok(());
384 }
385 let tid = i32::from_integral(args[2])?;
386 target.get_process().set_current_thread(Pid::from_raw(tid));
387 }
388 Ok(())
389}
390
391fn print_backtrace(target: &Rc<Target>) -> Result<(), SdbError> {
392 let stack = target.get_stack(None);
393 for (i, frame) in stack.borrow().frames().iter().enumerate() {
394 let pc = frame.backtrace_report_address;
395 let func_name = target.function_name_at_address(pc)?;
396
397 let mut message = format!(
398 "{}[{}]: {:#x} {}",
399 if i == stack.borrow().current_frame_index() {
400 "*"
401 } else {
402 " "
403 },
404 i,
405 pc.addr(),
406 func_name
407 );
408 if frame.inlined {
409 message += &format!(
410 " [inlined] {}",
411 frame.func_die.name()?.unwrap_or("".to_string())
412 );
413 }
414 println!("{message}");
415 }
416 Ok(())
417}
418
419fn handle_catchpoint_command(process: &Process, args: &[&str]) -> Result<(), SdbError> {
420 if args.len() < 2 {
421 print_help(&["help", "catchpoint"]);
422 return Ok(());
423 }
424 if args[1] == "syscall" {
425 handle_syscall_catchpoint_command(process, args)?;
426 }
427 Ok(())
428}
429
430fn handle_syscall_catchpoint_command(process: &Process, args: &[&str]) -> Result<(), SdbError> {
431 let mut policy = SyscallCatchPolicy::All;
432 if args.len() == 3 && args[2] == "none" {
433 policy = SyscallCatchPolicy::None;
434 } else if args.len() >= 3 {
435 let syscalls: Vec<_> = args[2]
436 .split(",")
437 .map(|s| s.trim())
438 .map(|s| {
439 if is_digits(s) {
440 i32::from_integral(s)
441 } else {
442 syscall_name_to_id(s).map(|d| d as i32)
443 }
444 })
445 .collect();
446 let to_catch: Result<Vec<_>, _> = syscalls.into_iter().collect();
447 policy = SyscallCatchPolicy::Some(to_catch?);
448 }
449 process.set_syscall_catch_policy(policy);
450 Ok(())
451}
452
453fn is_digits(s: &str) -> bool {
454 !s.is_empty() && s.chars().all(|c| c.is_ascii_digit())
455}
456
457fn handle_watchpoint_command(process: &Rc<Process>, args: &[&str]) -> Result<(), SdbError> {
458 if args.len() < 2 {
459 print_help(&["help", "watchpoint"]);
460 return Ok(());
461 }
462 let command = args[1];
463 if command == "list" {
464 handle_watchpoint_list(process)?;
465 return Ok(());
466 }
467
468 if command == "set" {
469 handle_watchpoint_set(process, args)?;
470 return Ok(());
471 }
472
473 if args.len() < 3 {
474 print_help(&["help", "watchpoint"]);
475 return Ok(());
476 }
477
478 let id = args[2]
479 .parse::<IdType>()
480 .map_err(|_| SdbError::new_err("Command expects watchpoint id"))?;
481 if command == "enable" {
482 process
483 .watchpoints()
484 .borrow()
485 .get_by_id(id)?
486 .borrow_mut()
487 .enable()?;
488 } else if command == "disable" {
489 process
490 .watchpoints()
491 .borrow()
492 .get_by_id(id)?
493 .borrow_mut()
494 .disable()?;
495 } else if command == "delete" {
496 process.watchpoints().borrow_mut().remove_by_id(id)?;
497 }
498 Ok(())
499}
500
501fn handle_watchpoint_list(process: &Process) -> Result<(), SdbError> {
502 let watchpoints = process.watchpoints();
503 let watchpoints = watchpoints.borrow();
504 if watchpoints.empty() {
505 println!("No watchpoints set");
506 } else {
507 println!("Current watchpoints:");
508 watchpoints.for_each(|w| {
509 let w = w.borrow() as Ref<dyn Any>;
510 let w = w.downcast_ref::<WatchPoint>().unwrap();
511 println!(
512 "{}: address = {:#x}, mode = {}, size = {}, {}",
513 w.id(),
514 w.address().addr(),
515 w.mode(),
516 w.size(),
517 if w.is_enabled() {
518 "enabled"
519 } else {
520 "disabled"
521 }
522 )
523 });
524 }
525 Ok(())
526}
527
528fn handle_watchpoint_set(process: &Rc<Process>, args: &[&str]) -> Result<(), SdbError> {
529 if args.len() != 5 {
530 print_help(&["help", "watchpoint"]);
531 return Ok(());
532 }
533 let address = u64::from_integral_lower_hex_radix(args[2], 16)?;
534 let mode_text = args[3];
535 let size = usize::from_integral(args[4])?;
536 if !(mode_text == "write" || mode_text == "rw" || mode_text == "execute") {
537 print_help(&["help", "watchpoint"]);
538 return Ok(());
539 }
540 let mode: StoppointMode = match mode_text {
541 "write" => StoppointMode::Write,
542 "rw" => StoppointMode::ReadWrite,
543 "execute" => StoppointMode::Execute,
544 _ => panic!(),
545 };
546 process
547 .create_watchpoint(address.into(), mode, size)?
548 .upgrade()
549 .unwrap()
550 .borrow_mut()
551 .enable()?;
552 Ok(())
553}
554
555fn handle_disassemble_command(process: &Process, args: &[&str]) -> Result<(), SdbError> {
556 let mut address = process.get_pc(None);
557 let mut n_instructions = 5usize;
558 let mut args_iter = args.iter();
559 args_iter.next();
560 while let Some(data) = args_iter.next() {
561 match *data {
562 "-a" => {
563 let opt_addr = args_iter
564 .next()
565 .ok_or(SdbError::new_err("Invalid address format"))?;
566 address = u64::from_integral_lower_hex_radix(opt_addr, 16)?.into();
567 }
568 "-c" => {
569 let instruction_count = args_iter
570 .next()
571 .ok_or(SdbError::new_err("Invalid instruction count"))?;
572 n_instructions = usize::from_integral(instruction_count)?;
573 }
574 _ => {
575 print_help(&["help", "disassemble"]);
576 return Ok(());
577 }
578 }
579 }
580
581 print_disassembly(process, address, n_instructions)?;
582 Ok(())
583}
584
585fn handle_stop(target: &Rc<Target>, reason: StopReason) -> Result<(), SdbError> {
586 print_stop_reason(target, reason)?;
587 if reason.reason == ProcessState::Stopped {
588 print_code_location(target)?;
589 }
590 Ok(())
591}
592
593fn print_code_location(target: &Rc<Target>) -> Result<(), SdbError> {
594 if target.get_stack(None).borrow().has_frames() {
595 let stack = target.get_stack(None);
596 let stack = stack.borrow();
597 let frame = stack.current_frame();
598 print_source(&frame.location.file.path, frame.location.line, 3)?;
599 } else {
600 print_disassembly(&target.get_process(), target.get_process().get_pc(None), 5)?;
601 }
602
603 Ok(())
604}
605
606fn print_source(path: &Path, line: u64, n_lines_context: u64) -> Result<(), SdbError> {
607 let file = File::open(path)
608 .map_err(|e| SdbError::new_err(&format!("Could not open source file, {e}")))?;
609 let reader = BufReader::new(file);
610
611 let start_line = if line <= n_lines_context {
612 1
613 } else {
614 line - n_lines_context
615 };
616 let end_line = line + n_lines_context + 1;
617 let fill_width = ((end_line as f64).log10().floor() as usize) + 1;
618
619 for (idx, line_text) in reader.lines().enumerate() {
620 let current_line = (idx + 1) as u64;
621 if current_line < start_line {
622 continue;
623 }
624 if current_line > end_line {
625 break;
626 }
627 let text = line_text.map_err(|_| SdbError::new_err("Could not read source file"))?;
628 let arrow = if current_line == line { ">" } else { " " };
629 println!("{arrow} {current_line:>fill_width$} {text}");
630 }
631
632 Ok(())
633}
634
635fn handle_memory_command(process: &Process, args: &[&str]) -> Result<(), SdbError> {
636 if args.len() < 3 {
637 print_help(&["help", "memory"]);
638 return Ok(());
639 }
640 if args[1] == "read" {
641 handle_memory_read_command(process, args)?;
642 } else if args[1] == "write" {
643 handle_memory_write_command(process, args)?;
644 } else {
645 print_help(&["help", "memory"]);
646 }
647
648 Ok(())
649}
650
651fn handle_memory_read_command(process: &Process, args: &[&str]) -> Result<(), SdbError> {
652 let address = u64::from_integral_lower_hex_radix(args[2], 16)?;
653 let mut n_bytes = 32usize;
654 if args.len() == 4 {
655 let bytes_args = usize::from_integral_lower_hex_radix(args[3], 16)?;
656 n_bytes = bytes_args;
657 }
658 let data = process.read_memory(address.into(), n_bytes)?;
659 for i in (0..data.len()).step_by(16) {
660 let bytes = &data[i..min(i + 16, data.len())];
661 let addr = VirtualAddress::from(address) + i as i64;
662 let data_msg = bytes
663 .iter()
664 .map(|b| format!("{b:02x}"))
665 .collect::<Vec<_>>()
666 .join(" ");
667 let msg = format!("{addr:#016x}: {data_msg}");
668 println!("{msg}");
669 }
670 Ok(())
671}
672
673fn handle_memory_write_command(process: &Process, args: &[&str]) -> Result<(), SdbError> {
674 if args.len() != 4 {
675 print_help(&["help", "memory"]);
676 return Ok(());
677 }
678 let address = u64::from_integral_lower_hex_radix(args[2], 16)?;
679 let data = parse_vector(args[3])?;
680 process.write_memory(address.into(), &data)?;
681 Ok(())
682}
683
684fn handle_breakpoint_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
685 if args.len() < 2 {
686 print_help(&["help", "register"]);
687 return Ok(());
688 }
689 let command = args[1];
690 if command == "list" {
691 handle_breakpoint_list_command(target)?;
692 return Ok(());
693 }
694
695 if args.len() < 3 {
696 print_help(&["help", "breakpoint"]);
697 return Ok(());
698 }
699
700 if command == "set" {
701 handle_breakpoint_set_command(target, args)?;
702 return Ok(());
703 }
704
705 handle_breakpoint_toggle(target, args)?;
706 Ok(())
707}
708
709fn handle_breakpoint_toggle(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
710 let command = args[1];
711 let dot_pos = args[2].find('.').unwrap_or(args[2].len());
712 let id_str = &args[2][..dot_pos];
713 let id = IdType::from_integral(id_str)
714 .map_err(|_| SdbError::new_err("Command expects breakpoint id"))?;
715 let bp = target.breakpoints().borrow().get_by_id(id)?;
716 if dot_pos != args[2].len() {
717 let site_id_str = &args[2][dot_pos + 1..];
718 let site_id = IdType::from_integral(site_id_str)
719 .map_err(|_| SdbError::new_err("Command expects breakpoint site id"))?;
720 let site = bp.borrow().breakpoint_sites().get_by_id(site_id)?;
721 if command == "enable" {
722 site.borrow_mut().enable()?;
723 } else if command == "disable" {
724 site.borrow_mut().disable()?;
725 }
726 } else if command == "enable" {
727 bp.borrow_mut().enable()?;
728 } else if command == "disable" {
729 bp.borrow_mut().disable()?;
730 } else if command == "delete" {
731 for site in bp.borrow_mut().breakpoint_sites().iter() {
732 target
733 .get_process()
734 .breakpoint_sites()
735 .borrow_mut()
736 .remove_by_address(site.borrow().address())?;
737 }
738 target.breakpoints().borrow_mut().remove_by_id(id)?;
739 }
740 Ok(())
741}
742
743fn handle_breakpoint_list_command(target: &Rc<Target>) -> Result<(), SdbError> {
744 let breakpoints = target.breakpoints().borrow();
745 if breakpoints.empty() {
746 println!("No breakpoints set");
747 } else {
748 println!("Current breakpoints:");
749 for bp in breakpoints.iter() {
750 if bp.borrow().is_internal() {
751 continue;
752 }
753 print!("{}: ", bp.borrow().id());
754
755 match (&*bp.borrow() as &dyn Any).type_id() {
756 id if id == TypeId::of::<AddressBreakpoint>() => {
757 print!(
758 "address = {:#x}",
759 (bp.borrow() as Ref<dyn Any>)
760 .downcast_ref::<AddressBreakpoint>()
761 .unwrap()
762 .address()
763 );
764 }
765 id if id == TypeId::of::<FunctionBreakpoint>() => {
766 print!(
767 "function = {}",
768 (bp.borrow() as Ref<dyn Any>)
769 .downcast_ref::<FunctionBreakpoint>()
770 .unwrap()
771 .function_name()
772 );
773 }
774 id if id == TypeId::of::<LineBreakpoint>() => {
775 print!(
776 "file = {}, line = {}",
777 (bp.borrow() as Ref<dyn Any>)
778 .downcast_ref::<LineBreakpoint>()
779 .unwrap()
780 .file()
781 .to_str()
782 .unwrap(),
783 (bp.borrow() as Ref<dyn Any>)
784 .downcast_ref::<LineBreakpoint>()
785 .unwrap()
786 .line()
787 );
788 }
789 _ => {}
790 }
791 println!(
792 ", {}",
793 if bp.borrow().is_enabled() {
794 "enabled"
795 } else {
796 "disabled"
797 }
798 );
799 bp.borrow().breakpoint_sites().for_each(|site| {
800 println!(
801 " .{}: address = {:#x}, {}",
802 site.borrow().id(),
803 site.borrow().address().addr(),
804 if site.borrow().is_enabled() {
805 "enabled"
806 } else {
807 "disabled"
808 }
809 );
810 });
811 }
812 }
813 Ok(())
814}
815
816fn handle_breakpoint_set_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
817 let mut hardware = false;
818 if args.len() == 4 {
819 if args[3] == "-h" {
820 hardware = true;
821 } else {
822 return SdbError::err("Invalid breakpoint command argument");
823 }
824 }
825 if args[2].starts_with("0x") {
826 let address = u64::from_integral_lower_hex_radix(args[2], 16);
827 match address {
828 Ok(address) => {
829 target
830 .create_address_breakpoint(address.into(), hardware, false)?
831 .upgrade()
832 .unwrap()
833 .borrow_mut()
834 .enable()?;
835 }
836 Err(_) => {
837 return SdbError::err(
838 "Breakpoint command expects address in hexadecimal, prefixed with '0x'",
839 );
840 }
841 }
842 } else if args[2].contains(':') {
843 let mut data = args[2].split(':');
844 let path = data.next().unwrap();
845 let line = u64::from_integral(data.next().unwrap());
846 match line {
847 Ok(line) => {
848 target
849 .create_line_breakpoint(Path::new(path), line as usize, hardware, false)?
850 .upgrade()
851 .unwrap()
852 .borrow_mut()
853 .enable()?;
854 }
855 Err(_) => {
856 return SdbError::err("Line number should be an integer");
857 }
858 }
859 } else {
860 target
861 .create_function_breakpoint(args[2], false, false)?
862 .upgrade()
863 .unwrap()
864 .borrow_mut()
865 .enable()?;
866 }
867 Ok(())
868}
869
870fn handle_register_command(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
871 if args.len() < 2 {
872 print_help(&["help", "register"]);
873 return Ok(());
874 }
875
876 if args[1] == "read" {
877 handle_register_read(target, args)?;
878 } else if args[1] == "write" {
879 handle_register_write(&target.get_process(), args);
880 } else {
881 print_help(&["help", "register"]);
882 }
883 Ok(())
884}
885
886fn handle_register_read(target: &Rc<Target>, args: &[&str]) -> Result<(), SdbError> {
887 let stack = target.get_stack(None);
888 let stack = stack.borrow();
889 let regs = stack.regs();
890
891 let print_reg_info = |info: &RegisterInfo| -> Result<(), SdbError> {
892 if regs.is_undefined(info.id)? {
893 println!("{}:\tundefined", info.name);
894 } else {
895 let value = regs.read(info)?;
896 println!("{}:\t{}", info.name, value);
897 }
898 Ok(())
899 };
900 if args.len() == 2 || (args.len() == 3 && args[2] == "all") {
901 for info in GRegisterInfos {
902 if args.len() == 3 || info.type_ == RegisterType::Gpr {
903 print_reg_info(info)?;
904 }
905 }
906 } else if args.len() == 3 {
907 let info_res = register_info_by_name(args[2]);
908 match info_res {
909 Ok(info) => {
910 print_reg_info(&info)?;
911 }
912 Err(_) => {
913 eprintln!("No such register")
914 }
915 }
916 } else {
917 print_help(&["help", "register"]);
918 }
919 Ok(())
920}
921
922fn handle_register_write(process: &Process, args: &[&str]) {
923 if args.len() != 4 {
924 print_help(&["help", "register"]);
925 return;
926 }
927 if let Err(e) = (|| -> Result<(), SdbError> {
928 let info = register_info_by_name(args[2])?;
929 let value = parse_register_value(&info, args[3])?;
930 process
931 .get_registers(None)
932 .borrow_mut()
933 .write(&info, value, true)?;
934 Ok(())
935 })() {
936 eprintln!("{e}");
937 }
938}
939
940fn print_help(args: &[&str]) {
941 if args.len() == 1 {
942 eprintln!(indoc! {"
943 Available commands:
944 breakpoint - Commands for operating on breakpoints
945 catchpoint - Commands for operating on catchpoints
946 continue - Resume the process
947 disassemble - Disassemble machine code to assembly
948 finish - Step-out
949 memory - Commands for operating on memory
950 next - Step-over
951 register - Commands for operating on registers
952 step - Step-in
953 stepi - Single instruction step
954 watchpoint - Commands for operating on watchpoints
955 down - Select the stack frame below the current one
956 up - Select the stack frame above the current one
957 thread - Commands for operating on threads
958 variable - Commands for operating on variables
959 "
960 });
961 } else if args[1] == "register" {
962 eprintln!(indoc! {"
963 Available commands:
964 read
965 read <register>
966 read all
967 write <register> <value>
968 "});
969 } else if args[1] == "breakpoint" {
970 eprintln!(indoc! {"
971 Available commands:
972 list
973 delete <id>
974 disable <id>
975 enable <id>
976 set <address>
977 set <address> -h
978 "});
979 } else if args[1] == "memory" {
980 eprintln!(indoc! {"
981 Available commands:
982 read <address>
983 read <address> <number of bytes>
984 write <address> <bytes>
985 "});
986 } else if args[1] == "disassemble" {
987 eprintln!(indoc! {"
988 Available options:
989 -c <number of instructions>
990 -a <start address>
991 "});
992 } else if args[1] == "watchpoint" {
993 eprintln!(indoc! {"
994 Available commands:
995 list
996 delete <id>
997 disable <id>
998 enable <id>
999 set <address> <write|rw|execute> <size>
1000 "})
1001 } else if args[1] == "catchpoint" {
1002 eprintln!(indoc! {"
1003 Available commands:
1004 syscall
1005 syscall none
1006 syscall <list of syscall IDs or names>
1007 "})
1008 } else if args[1] == "thread" {
1009 eprintln!(indoc! {"
1010 Available commands:
1011 list
1012 select <thread ID>
1013 "})
1014 } else if args[1] == "variable" {
1015 eprintln!(indoc! {"
1016 Available commands:
1017 read <variable>
1018 "})
1019 }
1020}