Files
wasmtime/cranelift/src/disasm.rs
Afonso Bordado ba7b874ca3 cranelift: Add RISC-V disassembly capabilities to clif-util (#5117)
This just correctly maps our RISC-V ISA to capstone.
2022-10-25 10:03:04 -07:00

225 lines
7.0 KiB
Rust

use anyhow::Result;
use cfg_if::cfg_if;
use cranelift_codegen::ir::function::FunctionParameters;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{MachReloc, MachStackMap, MachTrap};
use std::fmt::Write;
fn print_relocs(func_params: &FunctionParameters, relocs: &[MachReloc]) -> String {
let mut text = String::new();
for &MachReloc {
kind,
offset,
ref name,
addend,
} in relocs
{
writeln!(
text,
"reloc_external: {} {} {} at {}",
kind,
name.display(Some(func_params)),
addend,
offset
)
.unwrap();
}
text
}
pub fn print_traps(traps: &[MachTrap]) -> String {
let mut text = String::new();
for &MachTrap { offset, code } in traps {
writeln!(text, "trap: {code} at {offset:#x}").unwrap();
}
text
}
pub fn print_stack_maps(traps: &[MachStackMap]) -> String {
let mut text = String::new();
for MachStackMap {
offset,
offset_end,
stack_map,
} in traps
{
writeln!(
text,
"add_stack_map at {offset:#x}-{offset_end:#x} mapped_words={}",
stack_map.mapped_words()
)
.unwrap();
write!(text, " entries: ").unwrap();
let mut first = true;
for i in 0..stack_map.mapped_words() {
if !stack_map.get_bit(i as usize) {
continue;
}
if !first {
write!(text, ", ").unwrap();
} else {
first = false;
}
write!(text, "{i}").unwrap();
}
}
text
}
cfg_if! {
if #[cfg(feature = "disas")] {
use capstone::prelude::*;
use target_lexicon::Architecture;
fn get_disassembler(isa: &dyn TargetIsa) -> Result<Capstone> {
let cs = match isa.triple().architecture {
Architecture::X86_32(_) => Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.build()
.map_err(map_caperr)?,
Architecture::X86_64 => Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.build()
.map_err(map_caperr)?,
Architecture::Arm(arm) => {
if arm.is_thumb() {
Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Thumb)
.build()
.map_err(map_caperr)?
} else {
Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Arm)
.build()
.map_err(map_caperr)?
}
}
Architecture::Aarch64 {..} => {
let mut cs = Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.build()
.map_err(map_caperr)?;
// AArch64 uses inline constants rather than a separate constant pool right now.
// Without this option, Capstone will stop disassembling as soon as it sees
// an inline constant that is not also a valid instruction. With this option,
// Capstone will print a `.byte` directive with the bytes of the inline constant
// and continue to the next instruction.
cs.set_skipdata(true).map_err(map_caperr)?;
cs
}
Architecture::S390x {..} => Capstone::new()
.sysz()
.mode(arch::sysz::ArchMode::Default)
.build()
.map_err(map_caperr)?,
Architecture::Riscv64 {..} => {
let mut cs = Capstone::new()
.riscv()
.mode(arch::riscv::ArchMode::RiscV64)
.build()
.map_err(map_caperr)?;
// Similar to AArch64, RISC-V uses inline constants rather than a separate
// constant pool. We want to skip dissasembly over inline constants instead
// of stopping on invalid bytes.
cs.set_skipdata(true).map_err(map_caperr)?;
cs
}
_ => anyhow::bail!("Unknown ISA"),
};
Ok(cs)
}
pub fn print_disassembly(isa: &dyn TargetIsa, mem: &[u8]) -> Result<()> {
let cs = get_disassembler(isa)?;
println!("\nDisassembly of {} bytes:", mem.len());
let insns = cs.disasm_all(&mem, 0x0).unwrap();
for i in insns.iter() {
let mut line = String::new();
write!(&mut line, "{:4x}:\t", i.address()).unwrap();
let mut bytes_str = String::new();
let mut len = 0;
let mut first = true;
for b in i.bytes() {
if !first {
write!(&mut bytes_str, " ").unwrap();
}
write!(&mut bytes_str, "{:02x}", b).unwrap();
len += 1;
first = false;
}
write!(&mut line, "{:21}\t", bytes_str).unwrap();
if len > 8 {
write!(&mut line, "\n\t\t\t\t").unwrap();
}
if let Some(s) = i.mnemonic() {
write!(&mut line, "{}\t", s).unwrap();
}
if let Some(s) = i.op_str() {
write!(&mut line, "{}", s).unwrap();
}
println!("{}", line);
}
Ok(())
}
fn map_caperr(err: capstone::Error) -> anyhow::Error{
anyhow::format_err!("{}", err)
}
} else {
pub fn print_disassembly(_: &dyn TargetIsa, _: &[u8]) -> Result<()> {
println!("\nNo disassembly available.");
Ok(())
}
}
}
pub fn print_all(
isa: &dyn TargetIsa,
func_params: &FunctionParameters,
mem: &[u8],
code_size: u32,
print: bool,
relocs: &[MachReloc],
traps: &[MachTrap],
stack_maps: &[MachStackMap],
) -> Result<()> {
print_bytes(&mem);
print_disassembly(isa, &mem[0..code_size as usize])?;
if print {
println!(
"\n{}\n{}\n{}",
print_relocs(func_params, relocs),
print_traps(traps),
print_stack_maps(stack_maps)
);
}
Ok(())
}
pub fn print_bytes(mem: &[u8]) {
print!(".byte ");
let mut first = true;
for byte in mem.iter() {
if first {
first = false;
} else {
print!(", ");
}
print!("{}", byte);
}
println!();
}