|
| 1 | +// Copyright (c) Microsoft Corporation. |
| 2 | +// Licensed under the MIT License. |
| 3 | +#![allow(clippy::all)] |
| 4 | + |
| 5 | +use std::rc::Rc; |
| 6 | + |
| 7 | +use anyhow::Result; |
| 8 | +use clap::Parser; |
| 9 | + |
| 10 | +use debuggable_module::{ |
| 11 | + block, |
| 12 | + debuginfo::{DebugInfo, Function}, |
| 13 | + load_module::LoadModule, |
| 14 | + loader::Loader, |
| 15 | + path::FilePath, |
| 16 | + {Module, Offset}, |
| 17 | +}; |
| 18 | +use iced_x86::{Decoder, Formatter, Instruction, NasmFormatter, SymbolResolver, SymbolResult}; |
| 19 | +use regex::Regex; |
| 20 | + |
| 21 | +#[derive(Parser, Debug)] |
| 22 | +struct Args { |
| 23 | + #[arg(short, long)] |
| 24 | + module: String, |
| 25 | + |
| 26 | + #[arg(short, long)] |
| 27 | + function: Option<String>, |
| 28 | +} |
| 29 | + |
| 30 | +fn main() -> Result<()> { |
| 31 | + let args = Args::parse(); |
| 32 | + |
| 33 | + let loader = Loader::new(); |
| 34 | + let path = FilePath::new(&args.module)?; |
| 35 | + let module = Box::<dyn Module>::load(&loader, path)?; |
| 36 | + let debuginfo = Rc::new(module.debuginfo()?); |
| 37 | + |
| 38 | + let glob = args.function.unwrap_or(".*".to_owned()); |
| 39 | + let regex = glob_to_regex(&glob)?; |
| 40 | + |
| 41 | + for function in debuginfo.functions() { |
| 42 | + if regex.is_match(&function.name) { |
| 43 | + dump_function(&*module, debuginfo.clone(), function)?; |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + Ok(()) |
| 48 | +} |
| 49 | + |
| 50 | +fn glob_to_regex(expr: &str) -> Result<Regex> { |
| 51 | + // Don't make users escape Windows path separators. |
| 52 | + let expr = expr.replace(r"\", r"\\"); |
| 53 | + |
| 54 | + // Translate glob wildcards into quantified regexes. |
| 55 | + let expr = expr.replace("*", ".*"); |
| 56 | + |
| 57 | + // Anchor to line start. |
| 58 | + let expr = format!("^{expr}"); |
| 59 | + |
| 60 | + Ok(Regex::new(&expr)?) |
| 61 | +} |
| 62 | + |
| 63 | +fn dump_function(module: &dyn Module, debuginfo: Rc<DebugInfo>, function: &Function) -> Result<()> { |
| 64 | + println!("{}", function.name); |
| 65 | + |
| 66 | + let mut fmt = formatter(debuginfo.clone()); |
| 67 | + |
| 68 | + let blocks = block::sweep_region(module, &*debuginfo, function.offset, function.size)?; |
| 69 | + |
| 70 | + for block in &blocks { |
| 71 | + let data = module.read(block.offset, block.size)?; |
| 72 | + dump(block.offset.0, data, &mut *fmt)?; |
| 73 | + println!() |
| 74 | + } |
| 75 | + |
| 76 | + Ok(()) |
| 77 | +} |
| 78 | + |
| 79 | +fn dump(pc: u64, data: &[u8], fmt: &mut dyn Formatter) -> Result<()> { |
| 80 | + let mut decoder = Decoder::new(64, data, 0); |
| 81 | + decoder.set_ip(pc); |
| 82 | + |
| 83 | + while decoder.can_decode() { |
| 84 | + let inst = decoder.decode(); |
| 85 | + |
| 86 | + let mut display = String::new(); |
| 87 | + fmt.format(&inst, &mut display); |
| 88 | + println!("{:>12x} {}", inst.ip(), display); |
| 89 | + } |
| 90 | + |
| 91 | + Ok(()) |
| 92 | +} |
| 93 | + |
| 94 | +fn formatter(debuginfo: Rc<DebugInfo>) -> Box<dyn Formatter> { |
| 95 | + let resolver = Box::new(Resolver(debuginfo)); |
| 96 | + let mut fmt = NasmFormatter::with_options(Some(resolver), None); |
| 97 | + |
| 98 | + let opts = fmt.options_mut(); |
| 99 | + opts.set_add_leading_zero_to_hex_numbers(false); |
| 100 | + opts.set_branch_leading_zeros(false); |
| 101 | + opts.set_displacement_leading_zeros(false); |
| 102 | + opts.set_hex_prefix("0x"); |
| 103 | + opts.set_hex_suffix(""); |
| 104 | + opts.set_leading_zeros(false); |
| 105 | + opts.set_uppercase_all(false); |
| 106 | + opts.set_uppercase_hex(false); |
| 107 | + opts.set_space_after_operand_separator(true); |
| 108 | + opts.set_space_between_memory_add_operators(true); |
| 109 | + |
| 110 | + Box::new(fmt) |
| 111 | +} |
| 112 | + |
| 113 | +struct Resolver(Rc<DebugInfo>); |
| 114 | + |
| 115 | +impl SymbolResolver for Resolver { |
| 116 | + fn symbol( |
| 117 | + &mut self, |
| 118 | + _instruction: &Instruction, |
| 119 | + _operand: u32, |
| 120 | + _instruction_operand: Option<u32>, |
| 121 | + address: u64, |
| 122 | + _address_size: u32, |
| 123 | + ) -> Option<SymbolResult> { |
| 124 | + let f = self.0.find_function(Offset(address))?; |
| 125 | + |
| 126 | + Some(SymbolResult::with_str(f.offset.0, &f.name)) |
| 127 | + } |
| 128 | +} |
0 commit comments