Initial experiment.

This commit is contained in:
Dan Gohman
2018-10-29 12:00:12 -07:00
commit 154b35ecc1
15 changed files with 781 additions and 0 deletions

109
src/backend.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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(())
}