Initial experiment.
This commit is contained in:
109
src/backend.rs
Normal file
109
src/backend.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
#![allow(dead_code)] // for now
|
||||
|
||||
use dynasmrt::x64::Assembler;
|
||||
use dynasmrt::DynasmApi;
|
||||
|
||||
type GPR = u8;
|
||||
|
||||
struct GPRs {
|
||||
bits: u16,
|
||||
}
|
||||
|
||||
impl GPRs {
|
||||
fn new() -> Self {
|
||||
Self { bits: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
static RAX: u8 = 0;
|
||||
static RCX: u8 = 1;
|
||||
static RDX: u8 = 2;
|
||||
static RBX: u8 = 3;
|
||||
static RSP: u8 = 4;
|
||||
static RBP: u8 = 5;
|
||||
static RSI: u8 = 6;
|
||||
static RDI: u8 = 7;
|
||||
static R8: u8 = 8;
|
||||
static R9: u8 = 9;
|
||||
static R10: u8 = 10;
|
||||
static R11: u8 = 11;
|
||||
static R12: u8 = 12;
|
||||
static R13: u8 = 13;
|
||||
static R14: u8 = 14;
|
||||
static R15: u8 = 15;
|
||||
|
||||
impl GPRs {
|
||||
fn take(&mut self) -> GPR {
|
||||
let lz = self.bits.trailing_zeros();
|
||||
assert!(lz < 32, "ran out of free GPRs");
|
||||
self.bits &= !(1 << lz);
|
||||
lz as GPR
|
||||
}
|
||||
|
||||
fn release(&mut self, gpr: GPR) {
|
||||
assert_eq!(
|
||||
self.bits & (1 << gpr),
|
||||
0,
|
||||
"released register was already free"
|
||||
);
|
||||
self.bits |= 1 << gpr;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Registers {
|
||||
scratch_gprs: GPRs,
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
pub fn new() -> Self {
|
||||
let mut result = Self {
|
||||
scratch_gprs: GPRs::new(),
|
||||
};
|
||||
// Give ourselves a few scratch registers to work with, for now.
|
||||
result.release_scratch_gpr(RAX);
|
||||
result.release_scratch_gpr(RCX);
|
||||
result.release_scratch_gpr(RDX);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn take_scratch_gpr(&mut self) -> GPR {
|
||||
self.scratch_gprs.take()
|
||||
}
|
||||
|
||||
pub fn release_scratch_gpr(&mut self, gpr: GPR) {
|
||||
self.scratch_gprs.release(gpr);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_i32(ops: &mut Assembler, regs: &mut Registers, gpr: GPR) {
|
||||
// For now, do an actual push (and pop below). In the future, we could
|
||||
// do on-the-fly register allocation here.
|
||||
dynasm!(ops
|
||||
; push Rq(gpr)
|
||||
);
|
||||
regs.release_scratch_gpr(gpr);
|
||||
}
|
||||
|
||||
fn pop_i32(ops: &mut Assembler, regs: &mut Registers) -> GPR {
|
||||
let gpr = regs.take_scratch_gpr();
|
||||
dynasm!(ops
|
||||
; pop Rq(gpr)
|
||||
);
|
||||
gpr
|
||||
}
|
||||
|
||||
pub fn add_i32(ops: &mut Assembler, regs: &mut Registers) {
|
||||
let op0 = pop_i32(ops, regs);
|
||||
let op1 = pop_i32(ops, regs);
|
||||
dynasm!(ops
|
||||
; add Rq(op0), Rq(op1)
|
||||
);
|
||||
push_i32(ops, regs, op0);
|
||||
regs.release_scratch_gpr(op1);
|
||||
}
|
||||
|
||||
pub fn unsupported_opcode(ops: &mut Assembler) {
|
||||
dynasm!(ops
|
||||
; ud2
|
||||
);
|
||||
}
|
||||
36
src/disassemble.rs
Normal file
36
src/disassemble.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use capstone::prelude::*;
|
||||
use error::Error;
|
||||
use std::fmt::Write;
|
||||
|
||||
pub fn disassemble(mem: &[u8]) -> Result<(), Error> {
|
||||
let mut cs = Capstone::new()
|
||||
.x86()
|
||||
.mode(arch::x86::ArchMode::Mode64)
|
||||
.build()?;
|
||||
|
||||
println!("{} 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();
|
||||
for b in i.bytes() {
|
||||
write!(&mut bytes_str, "{:02x} ", b).unwrap();
|
||||
}
|
||||
write!(&mut line, "{:21}\t", bytes_str).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(())
|
||||
}
|
||||
27
src/error.rs
Normal file
27
src/error.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use capstone;
|
||||
use wasmparser::BinaryReaderError;
|
||||
|
||||
#[derive(Fail, PartialEq, Eq, Clone, Debug)]
|
||||
pub enum Error {
|
||||
#[fail(display = "Disassembler error: {}", _0)]
|
||||
Disassembler(String),
|
||||
|
||||
#[fail(display = "Assembler error: {}", _0)]
|
||||
Assembler(String),
|
||||
|
||||
#[fail(display = "Input error: {}", _0)]
|
||||
Input(String),
|
||||
}
|
||||
|
||||
impl From<BinaryReaderError> for Error {
|
||||
fn from(e: BinaryReaderError) -> Self {
|
||||
let BinaryReaderError { message, offset } = e;
|
||||
Error::Input(format!("At wasm offset {}: {}", offset, message))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<capstone::Error> for Error {
|
||||
fn from(e: capstone::Error) -> Self {
|
||||
Error::Disassembler(e.to_string())
|
||||
}
|
||||
}
|
||||
34
src/function_body.rs
Normal file
34
src/function_body.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use backend::*;
|
||||
use disassemble::disassemble;
|
||||
use error::Error;
|
||||
use wasmparser::{FunctionBody, Operator};
|
||||
|
||||
pub fn translate(body: &FunctionBody) -> Result<(), Error> {
|
||||
let locals = body.get_locals_reader()?;
|
||||
for local in locals {
|
||||
local?; // TODO
|
||||
}
|
||||
|
||||
let mut ops = dynasmrt::x64::Assembler::new().unwrap();
|
||||
let operators = body.get_operators_reader()?;
|
||||
let mut regs = Registers::new();
|
||||
for op in operators {
|
||||
match op? {
|
||||
Operator::I32Add => {
|
||||
add_i32(&mut ops, &mut regs);
|
||||
}
|
||||
_ => {
|
||||
unsupported_opcode(&mut ops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let output = ops
|
||||
.finalize()
|
||||
.map_err(|_asm| Error::Assembler("assembler error".to_owned()))?;
|
||||
|
||||
// TODO: Do something with the output.
|
||||
disassemble(&output)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
18
src/lib.rs
Normal file
18
src/lib.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
#![feature(plugin)]
|
||||
#![plugin(dynasm)]
|
||||
|
||||
extern crate capstone;
|
||||
extern crate failure;
|
||||
extern crate wasmparser;
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
extern crate dynasmrt;
|
||||
|
||||
mod backend;
|
||||
mod disassemble;
|
||||
mod error;
|
||||
mod function_body;
|
||||
mod module;
|
||||
mod translate_sections;
|
||||
|
||||
pub use module::translate;
|
||||
131
src/module.rs
Normal file
131
src/module.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use error::Error;
|
||||
use translate_sections;
|
||||
use wasmparser::{ModuleReader, SectionCode};
|
||||
|
||||
/// Translate from a slice of bytes holding a wasm module.
|
||||
pub fn translate(data: &[u8]) -> Result<(), Error> {
|
||||
let mut reader = ModuleReader::new(data)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut section = reader.read()?;
|
||||
|
||||
if let SectionCode::Type = section.code {
|
||||
let types = section.get_type_section_reader()?;
|
||||
translate_sections::type_(types)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Import = section.code {
|
||||
let imports = section.get_import_section_reader()?;
|
||||
translate_sections::import(imports)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Function = section.code {
|
||||
let functions = section.get_function_section_reader()?;
|
||||
translate_sections::function(functions)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Table = section.code {
|
||||
let tables = section.get_table_section_reader()?;
|
||||
translate_sections::table(tables)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Memory = section.code {
|
||||
let memories = section.get_memory_section_reader()?;
|
||||
translate_sections::memory(memories)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Global = section.code {
|
||||
let globals = section.get_global_section_reader()?;
|
||||
translate_sections::global(globals)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Export = section.code {
|
||||
let exports = section.get_export_section_reader()?;
|
||||
translate_sections::export(exports)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Start = section.code {
|
||||
let start = section.get_start_section_content()?;
|
||||
translate_sections::start(start)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Element = section.code {
|
||||
let elements = section.get_element_section_reader()?;
|
||||
translate_sections::element(elements)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Code = section.code {
|
||||
let code = section.get_code_section_reader()?;
|
||||
translate_sections::code(code)?;
|
||||
|
||||
reader.skip_custom_sections()?;
|
||||
if reader.eof() {
|
||||
return Ok(());
|
||||
}
|
||||
section = reader.read()?;
|
||||
}
|
||||
|
||||
if let SectionCode::Data = section.code {
|
||||
let data = section.get_data_section_reader()?;
|
||||
translate_sections::data(data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
95
src/translate_sections.rs
Normal file
95
src/translate_sections.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use error::Error;
|
||||
use function_body;
|
||||
#[allow(unused_imports)] // for now
|
||||
use wasmparser::{
|
||||
CodeSectionReader, Data, DataSectionReader, Element, ElementSectionReader, Export,
|
||||
ExportSectionReader, ExternalKind, FuncType, FunctionSectionReader, Global,
|
||||
GlobalSectionReader, GlobalType, Import, ImportSectionEntryType, ImportSectionReader,
|
||||
MemorySectionReader, MemoryType, Operator, TableSectionReader, Type, TypeSectionReader,
|
||||
};
|
||||
|
||||
/// Parses the Type section of the wasm module.
|
||||
pub fn type_(types: TypeSectionReader) -> Result<(), Error> {
|
||||
for entry in types {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Import section of the wasm module.
|
||||
pub fn import(imports: ImportSectionReader) -> Result<(), Error> {
|
||||
for entry in imports {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Function section of the wasm module.
|
||||
pub fn function(functions: FunctionSectionReader) -> Result<(), Error> {
|
||||
for entry in functions {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Table section of the wasm module.
|
||||
pub fn table(tables: TableSectionReader) -> Result<(), Error> {
|
||||
for entry in tables {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Memory section of the wasm module.
|
||||
pub fn memory(memories: MemorySectionReader) -> Result<(), Error> {
|
||||
for entry in memories {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Global section of the wasm module.
|
||||
pub fn global(globals: GlobalSectionReader) -> Result<(), Error> {
|
||||
for entry in globals {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Export section of the wasm module.
|
||||
pub fn export(exports: ExportSectionReader) -> Result<(), Error> {
|
||||
for entry in exports {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Start section of the wasm module.
|
||||
pub fn start(_index: u32) -> Result<(), Error> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Element section of the wasm module.
|
||||
pub fn element(elements: ElementSectionReader) -> Result<(), Error> {
|
||||
for entry in elements {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Code section of the wasm module.
|
||||
pub fn code(code: CodeSectionReader) -> Result<(), Error> {
|
||||
for body in code {
|
||||
function_body::translate(&body?)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Data section of the wasm module.
|
||||
pub fn data(data: DataSectionReader) -> Result<(), Error> {
|
||||
for entry in data {
|
||||
entry?; // TODO
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user