* Made Capstone an optional dependency (fixes #382). * Introduced feature 'disas' for disassembly (related to #382). * Made 'disas' a default feature in cretonne-tools. * Fixed errors in src/compile.rs introduced by get_disassembler changes. - Moves `use` statements before the function declaration. - Returns an error if the disassembler cannot be found created.
185 lines
5.5 KiB
Rust
185 lines
5.5 KiB
Rust
//! CLI tool to read Cretonne IR files and compile them into native code.
|
|
|
|
use cretonne_codegen::isa::TargetIsa;
|
|
use cretonne_codegen::print_errors::pretty_error;
|
|
use cretonne_codegen::settings::FlagsOrIsa;
|
|
use cretonne_codegen::Context;
|
|
use cretonne_codegen::{binemit, ir};
|
|
use cretonne_reader::parse_test;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use utils::{parse_sets_and_triple, read_to_string};
|
|
|
|
struct PrintRelocs {
|
|
flag_print: bool,
|
|
}
|
|
|
|
impl binemit::RelocSink for PrintRelocs {
|
|
fn reloc_ebb(
|
|
&mut self,
|
|
where_: binemit::CodeOffset,
|
|
r: binemit::Reloc,
|
|
offset: binemit::CodeOffset,
|
|
) {
|
|
if self.flag_print {
|
|
println!("reloc_ebb: {} {} at {}", r, offset, where_);
|
|
}
|
|
}
|
|
|
|
fn reloc_external(
|
|
&mut self,
|
|
where_: binemit::CodeOffset,
|
|
r: binemit::Reloc,
|
|
name: &ir::ExternalName,
|
|
addend: binemit::Addend,
|
|
) {
|
|
if self.flag_print {
|
|
println!("reloc_ebb: {} {} {} at {}", r, name, addend, where_);
|
|
}
|
|
}
|
|
|
|
fn reloc_jt(&mut self, where_: binemit::CodeOffset, r: binemit::Reloc, jt: ir::JumpTable) {
|
|
if self.flag_print {
|
|
println!("reloc_ebb: {} {} at {}", r, jt, where_);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PrintTraps {
|
|
flag_print: bool,
|
|
}
|
|
|
|
impl binemit::TrapSink for PrintTraps {
|
|
fn trap(&mut self, offset: binemit::CodeOffset, _srcloc: ir::SourceLoc, code: ir::TrapCode) {
|
|
if self.flag_print {
|
|
println!("trap: {} at {}", code, offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn run(
|
|
files: Vec<String>,
|
|
flag_print: bool,
|
|
flag_set: &[String],
|
|
flag_isa: &str,
|
|
) -> Result<(), String> {
|
|
let parsed = parse_sets_and_triple(flag_set, flag_isa)?;
|
|
|
|
for filename in files {
|
|
let path = Path::new(&filename);
|
|
let name = String::from(path.as_os_str().to_string_lossy());
|
|
handle_module(flag_print, &path.to_path_buf(), &name, parsed.as_fisa())?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_module(
|
|
flag_print: bool,
|
|
path: &PathBuf,
|
|
name: &str,
|
|
fisa: FlagsOrIsa,
|
|
) -> Result<(), String> {
|
|
let buffer = read_to_string(&path).map_err(|e| format!("{}: {}", name, e))?;
|
|
let test_file = parse_test(&buffer).map_err(|e| format!("{}: {}", name, e))?;
|
|
|
|
// If we have an isa from the command-line, use that. Otherwise if the
|
|
// file contains a unique isa, use that.
|
|
let isa = if let Some(isa) = fisa.isa {
|
|
isa
|
|
} else if let Some(isa) = test_file.isa_spec.unique_isa() {
|
|
isa
|
|
} else {
|
|
return Err(String::from("compilation requires a target isa"));
|
|
};
|
|
|
|
for (func, _) in test_file.functions {
|
|
let mut context = Context::new();
|
|
context.func = func;
|
|
|
|
// Compile and encode the result to machine code.
|
|
let mut mem = Vec::new();
|
|
let mut relocs = PrintRelocs { flag_print };
|
|
let mut traps = PrintTraps { flag_print };
|
|
context
|
|
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
|
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
|
|
|
if flag_print {
|
|
println!("{}", context.func.display(isa));
|
|
}
|
|
|
|
if flag_print {
|
|
print!(".byte ");
|
|
let mut first = true;
|
|
for byte in &mem {
|
|
if first {
|
|
first = false;
|
|
} else {
|
|
print!(", ");
|
|
}
|
|
print!("{}", byte);
|
|
}
|
|
|
|
println!();
|
|
print_disassembly(isa, &mem)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
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())
|
|
}
|
|
|
|
fn print_disassembly(isa: &TargetIsa, mem: &[u8]) -> Result<(), String> {
|
|
let mut cs = get_disassembler(isa)?;
|
|
|
|
println!("\nDisassembly:");
|
|
let insns = cs.disasm_all(&mem, 0x0).unwrap();
|
|
for i in insns.iter() {
|
|
println!("{}", i);
|
|
}
|
|
Ok(())
|
|
}
|
|
} else {
|
|
fn print_disassembly(_: &TargetIsa, _: &[u8]) -> Result<(), String> {
|
|
println!("\nNo disassembly available.");
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|