Add a --disasm option to clif-util wasm and compile (#713)
- Both the `wasm` and `compile` commands get this new subcommand, and it defaults to false. This means that test runs with `wasm` can request disassembly (the main reason I am doing this) while test runs with `compile` now must request it, this changes current behavior. - Switch to using context.compile_and_emit directly, and make the reloc and trap printers just accumulate output, not print it. This allows us to factor the printing code into the disasm module.
This commit is contained in:
committed by
Benjamin Bouvier
parent
82c6867155
commit
141ccb9e9d
215
cranelift/src/disasm.rs
Normal file
215
cranelift/src/disasm.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
use cfg_if::cfg_if;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::{binemit, ir};
|
||||
use std::fmt::Write;
|
||||
|
||||
pub struct PrintRelocs {
|
||||
pub flag_print: bool,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl PrintRelocs {
|
||||
pub fn new(flag_print: bool) -> PrintRelocs {
|
||||
Self {
|
||||
flag_print,
|
||||
text: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl binemit::RelocSink for PrintRelocs {
|
||||
fn reloc_ebb(
|
||||
&mut self,
|
||||
where_: binemit::CodeOffset,
|
||||
r: binemit::Reloc,
|
||||
offset: binemit::CodeOffset,
|
||||
) {
|
||||
if self.flag_print {
|
||||
write!(
|
||||
&mut self.text,
|
||||
"reloc_ebb: {} {} at {}\n",
|
||||
r, offset, where_
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
where_: binemit::CodeOffset,
|
||||
r: binemit::Reloc,
|
||||
name: &ir::ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
if self.flag_print {
|
||||
write!(
|
||||
&mut self.text,
|
||||
"reloc_external: {} {} {} at {}\n",
|
||||
r, name, addend, where_
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn reloc_jt(&mut self, where_: binemit::CodeOffset, r: binemit::Reloc, jt: ir::JumpTable) {
|
||||
if self.flag_print {
|
||||
write!(&mut self.text, "reloc_jt: {} {} at {}\n", r, jt, where_).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrintTraps {
|
||||
pub flag_print: bool,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl PrintTraps {
|
||||
pub fn new(flag_print: bool) -> PrintTraps {
|
||||
Self {
|
||||
flag_print,
|
||||
text: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl binemit::TrapSink for PrintTraps {
|
||||
fn trap(&mut self, offset: binemit::CodeOffset, _srcloc: ir::SourceLoc, code: ir::TrapCode) {
|
||||
if self.flag_print {
|
||||
write!(&mut self.text, "trap: {} at {}\n", code, offset).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "disas")] {
|
||||
use capstone::prelude::*;
|
||||
use target_lexicon::Architecture;
|
||||
|
||||
fn get_disassembler(isa: &TargetIsa) -> Result<Capstone, String> {
|
||||
let cs = match isa.triple().architecture {
|
||||
Architecture::Riscv32 | Architecture::Riscv64 => {
|
||||
return Err(String::from("No disassembler for RiscV"))
|
||||
}
|
||||
Architecture::I386 | Architecture::I586 | Architecture::I686 => Capstone::new()
|
||||
.x86()
|
||||
.mode(arch::x86::ArchMode::Mode32)
|
||||
.build(),
|
||||
Architecture::X86_64 => Capstone::new()
|
||||
.x86()
|
||||
.mode(arch::x86::ArchMode::Mode64)
|
||||
.build(),
|
||||
Architecture::Arm
|
||||
| Architecture::Armv4t
|
||||
| Architecture::Armv5te
|
||||
| Architecture::Armv7
|
||||
| Architecture::Armv7s => Capstone::new().arm().mode(arch::arm::ArchMode::Arm).build(),
|
||||
Architecture::Thumbv6m | Architecture::Thumbv7em | Architecture::Thumbv7m => Capstone::new(
|
||||
).arm()
|
||||
.mode(arch::arm::ArchMode::Thumb)
|
||||
.build(),
|
||||
Architecture::Aarch64 => Capstone::new()
|
||||
.arm64()
|
||||
.mode(arch::arm64::ArchMode::Arm)
|
||||
.build(),
|
||||
_ => return Err(String::from("Unknown ISA")),
|
||||
};
|
||||
|
||||
cs.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
pub fn print_disassembly(isa: &TargetIsa, mem: &[u8]) -> Result<(), String> {
|
||||
let mut 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() {
|
||||
write!(&mut bytes_str, "{:02x}", b).unwrap();
|
||||
if !first {
|
||||
write!(&mut bytes_str, " ").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(())
|
||||
}
|
||||
} else {
|
||||
pub fn print_disassembly(_: &TargetIsa, _: &[u8]) -> Result<(), String> {
|
||||
println!("\nNo disassembly available.");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_all(
|
||||
isa: &TargetIsa,
|
||||
mem: &[u8],
|
||||
code_size: u32,
|
||||
rodata_size: u32,
|
||||
relocs: &PrintRelocs,
|
||||
traps: &PrintTraps,
|
||||
) -> Result<(), String> {
|
||||
print_bytes(&mem);
|
||||
print_disassembly(isa, &mem[0..code_size as usize])?;
|
||||
print_readonly_data(&mem[code_size as usize..(code_size + rodata_size) as usize]);
|
||||
println!("\n{}\n{}", &relocs.text, &traps.text);
|
||||
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!();
|
||||
}
|
||||
|
||||
pub fn print_readonly_data(mem: &[u8]) {
|
||||
if mem.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("\nFollowed by {} bytes of read-only data:", mem.len());
|
||||
|
||||
for (i, byte) in mem.iter().enumerate() {
|
||||
if i % 16 == 0 {
|
||||
if i != 0 {
|
||||
println!();
|
||||
}
|
||||
print!("{:4}: ", i);
|
||||
}
|
||||
if i % 4 == 0 {
|
||||
print!(" ");
|
||||
}
|
||||
print!("{:02x} ", byte);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
Reference in New Issue
Block a user