Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit e2dc560

Browse files
authored
Add debuggable-module (#2700)
1 parent f6a7060 commit e2dc560

File tree

12 files changed

+1784
-104
lines changed

12 files changed

+1784
-104
lines changed

src/agent/Cargo.lock

Lines changed: 412 additions & 104 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agent/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
members = [
33
"atexit",
44
"coverage-legacy",
5+
"debuggable-module",
56
"debugger",
67
"dynamic-library",
78
"input-tester",
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "debuggable-module"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "MIT"
6+
7+
[dependencies]
8+
anyhow = "1.0"
9+
gimli = "0.26.2"
10+
goblin = "0.6.0"
11+
iced-x86 = "1.17"
12+
log = "0.4.17"
13+
pdb = "0.8.0"
14+
regex = "1.0"
15+
symbolic = { version = "10.1", features = ["debuginfo", "demangle", "symcache"] }
16+
thiserror = "1.0"
17+
18+
[dev-dependencies]
19+
clap = { version = "4.0", features = ["derive"] }
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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

Comments
 (0)