blob: 1aee27a9e4fffd30fba1277602c3dbc12e89c7d1 [file] [log] [blame]
//! 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()
.unwrap_or_else(|_| panic!(
"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('<')
.unwrap_or_else(|| panic!("\"<\" 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()
})
.skip_while(|s| *s == "lock") // skip x86-specific prefix
.map(std::string::ToString::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(std::string::ToString::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(std::string::ToString::to_string)
.skip_while(|s| *s == "lock") // skip x86-specific prefix
.collect::<Vec<String>>();
instructions.push(Instruction { parts });
}
ret.entry(normalize(symbol))
.or_insert_with(Vec::new)
.push(Function {
addr: None,
instrs: instructions,
});
}
ret
}