215 lines
6.5 KiB
Rust
215 lines
6.5 KiB
Rust
|
|
//! Disassembly calling function for most targets.
|
||
|
|
|
||
|
|
use ::*;
|
||
|
|
use std::process::Command;
|
||
|
|
|
||
|
|
pub(crate) fn disassemble_myself() -> HashMap<String, Vec<Function>> {
|
||
|
|
let me = env::current_exe().expect("failed to get current exe");
|
||
|
|
|
||
|
|
if cfg!(target_arch = "x86_64")
|
||
|
|
&& cfg!(target_os = "windows")
|
||
|
|
&& cfg!(target_env = "msvc")
|
||
|
|
{
|
||
|
|
let mut cmd = cc::windows_registry::find(
|
||
|
|
"x86_64-pc-windows-msvc",
|
||
|
|
"dumpbin.exe",
|
||
|
|
).expect("failed to find `dumpbin` tool");
|
||
|
|
let output = cmd
|
||
|
|
.arg("/DISASM")
|
||
|
|
.arg(&me)
|
||
|
|
.output()
|
||
|
|
.expect("failed to execute dumpbin");
|
||
|
|
println!(
|
||
|
|
"{}\n{}",
|
||
|
|
output.status,
|
||
|
|
String::from_utf8_lossy(&output.stderr)
|
||
|
|
);
|
||
|
|
assert!(output.status.success());
|
||
|
|
parse_dumpbin(&String::from_utf8_lossy(&output.stdout))
|
||
|
|
} else if cfg!(target_os = "windows") {
|
||
|
|
panic!("disassembly unimplemented")
|
||
|
|
} else if cfg!(target_os = "macos") {
|
||
|
|
let output = Command::new("otool")
|
||
|
|
.arg("-vt")
|
||
|
|
.arg(&me)
|
||
|
|
.output()
|
||
|
|
.expect("failed to execute otool");
|
||
|
|
println!(
|
||
|
|
"{}\n{}",
|
||
|
|
output.status,
|
||
|
|
String::from_utf8_lossy(&output.stderr)
|
||
|
|
);
|
||
|
|
assert!(output.status.success());
|
||
|
|
|
||
|
|
parse_otool(str::from_utf8(&output.stdout).expect("stdout not utf8"))
|
||
|
|
} else {
|
||
|
|
let objdump =
|
||
|
|
env::var("OBJDUMP").unwrap_or_else(|_| "objdump".to_string());
|
||
|
|
let output = Command::new(objdump.clone())
|
||
|
|
.arg("--disassemble")
|
||
|
|
.arg(&me)
|
||
|
|
.output()
|
||
|
|
.expect(&format!(
|
||
|
|
"failed to execute objdump. OBJDUMP={}",
|
||
|
|
objdump
|
||
|
|
));
|
||
|
|
println!(
|
||
|
|
"{}\n{}",
|
||
|
|
output.status,
|
||
|
|
String::from_utf8_lossy(&output.stderr)
|
||
|
|
);
|
||
|
|
assert!(output.status.success());
|
||
|
|
|
||
|
|
parse_objdump(str::from_utf8(&output.stdout).expect("stdout not utf8"))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn parse_objdump(output: &str) -> HashMap<String, Vec<Function>> {
|
||
|
|
let mut lines = output.lines();
|
||
|
|
let expected_len =
|
||
|
|
if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
|
||
|
|
8
|
||
|
|
} else {
|
||
|
|
2
|
||
|
|
};
|
||
|
|
|
||
|
|
for line in output.lines().take(100) {
|
||
|
|
println!("{}", line);
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut ret = HashMap::new();
|
||
|
|
while let Some(header) = lines.next() {
|
||
|
|
// symbols should start with `$hex_addr <$name>:`
|
||
|
|
if !header.ends_with(">:") {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
let start = header.find('<')
|
||
|
|
.expect(&format!("\"<\" not found in symbol pattern of the form \"$hex_addr <$name>\": {}", header));
|
||
|
|
let symbol = &header[start + 1..header.len() - 2];
|
||
|
|
|
||
|
|
let mut instructions = Vec::new();
|
||
|
|
while let Some(instruction) = lines.next() {
|
||
|
|
if instruction.is_empty() {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// Each line of instructions should look like:
|
||
|
|
//
|
||
|
|
// $rel_offset: ab cd ef 00 $instruction...
|
||
|
|
let parts = instruction
|
||
|
|
.split_whitespace()
|
||
|
|
.skip(1)
|
||
|
|
.skip_while(|s| {
|
||
|
|
s.len() == expected_len
|
||
|
|
&& usize::from_str_radix(s, 16).is_ok()
|
||
|
|
}).map(|s| s.to_string())
|
||
|
|
.collect::<Vec<String>>();
|
||
|
|
instructions.push(Instruction { parts });
|
||
|
|
}
|
||
|
|
|
||
|
|
ret.entry(normalize(symbol))
|
||
|
|
.or_insert_with(Vec::new)
|
||
|
|
.push(Function {
|
||
|
|
addr: None,
|
||
|
|
instrs: instructions,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
ret
|
||
|
|
}
|
||
|
|
|
||
|
|
fn parse_otool(output: &str) -> HashMap<String, Vec<Function>> {
|
||
|
|
let mut lines = output.lines();
|
||
|
|
|
||
|
|
for line in output.lines().take(100) {
|
||
|
|
println!("{}", line);
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut ret = HashMap::new();
|
||
|
|
let mut cached_header = None;
|
||
|
|
while let Some(header) = cached_header.take().or_else(|| lines.next()) {
|
||
|
|
// symbols should start with `$symbol:`
|
||
|
|
if !header.ends_with(':') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
// strip the leading underscore and the trailing colon
|
||
|
|
let symbol = &header[1..header.len() - 1];
|
||
|
|
|
||
|
|
let mut instructions = Vec::new();
|
||
|
|
while let Some(instruction) = lines.next() {
|
||
|
|
if instruction.ends_with(':') {
|
||
|
|
cached_header = Some(instruction);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// Each line of instructions should look like:
|
||
|
|
//
|
||
|
|
// $addr $instruction...
|
||
|
|
let parts = instruction
|
||
|
|
.split_whitespace()
|
||
|
|
.skip(1)
|
||
|
|
.map(|s| s.to_string())
|
||
|
|
.collect::<Vec<String>>();
|
||
|
|
instructions.push(Instruction { parts });
|
||
|
|
}
|
||
|
|
|
||
|
|
ret.entry(normalize(symbol))
|
||
|
|
.or_insert_with(Vec::new)
|
||
|
|
.push(Function {
|
||
|
|
addr: None,
|
||
|
|
instrs: instructions,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
ret
|
||
|
|
}
|
||
|
|
|
||
|
|
fn parse_dumpbin(output: &str) -> HashMap<String, Vec<Function>> {
|
||
|
|
let mut lines = output.lines();
|
||
|
|
|
||
|
|
for line in output.lines().take(100) {
|
||
|
|
println!("{}", line);
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut ret = HashMap::new();
|
||
|
|
let mut cached_header = None;
|
||
|
|
while let Some(header) = cached_header.take().or_else(|| lines.next()) {
|
||
|
|
// symbols should start with `$symbol:`
|
||
|
|
if !header.ends_with(':') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
// strip the trailing colon
|
||
|
|
let symbol = &header[..header.len() - 1];
|
||
|
|
|
||
|
|
let mut instructions = Vec::new();
|
||
|
|
while let Some(instruction) = lines.next() {
|
||
|
|
if !instruction.starts_with(" ") {
|
||
|
|
cached_header = Some(instruction);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// Each line looks like:
|
||
|
|
//
|
||
|
|
// > $addr: ab cd ef $instr..
|
||
|
|
// > 00 12 # this line os optional
|
||
|
|
if instruction.starts_with(" ") {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
let parts = instruction
|
||
|
|
.split_whitespace()
|
||
|
|
.skip(1)
|
||
|
|
.skip_while(|s| {
|
||
|
|
s.len() == 2 && usize::from_str_radix(s, 16).is_ok()
|
||
|
|
}).map(|s| s.to_string())
|
||
|
|
.collect::<Vec<String>>();
|
||
|
|
instructions.push(Instruction { parts });
|
||
|
|
}
|
||
|
|
|
||
|
|
ret.entry(normalize(symbol))
|
||
|
|
.or_insert_with(Vec::new)
|
||
|
|
.push(Function {
|
||
|
|
addr: None,
|
||
|
|
instrs: instructions,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
ret
|
||
|
|
}
|