From 66b8d9293404251c7c4201cb2e486801c46f6e11 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Tue, 17 Aug 2021 00:40:13 +0200 Subject: [PATCH] add Python module --- .gitignore | 2 + Cargo.lock | 260 ++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + disasm-py/Cargo.toml | 16 +++ disasm-py/src/lib.rs | 137 +++++++++++++++++++++++ disasm/Cargo.toml | 4 +- disasm/src/iter.rs | 34 ++++++ disasm/src/lib.rs | 20 ++-- fuzz/Cargo.toml | 4 +- macros/Cargo.toml | 2 +- rand/Cargo.toml | 4 +- 11 files changed, 466 insertions(+), 18 deletions(-) create mode 100644 disasm-py/Cargo.toml create mode 100644 disasm-py/src/lib.rs create mode 100644 disasm/src/iter.rs diff --git a/.gitignore b/.gitignore index 9e0c6e5..48ca60a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .idea/ .DS_Store *.profraw +env/ +lib/ diff --git a/Cargo.lock b/Cargo.lock index b25a613..c8b38c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,28 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "ctor" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -25,6 +41,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghost" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5bcf1bbeab73aa4cf2fde60a846858dc036163c7c33bec309f8d17de785479" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -34,12 +61,75 @@ dependencies = [ "libc", ] +[[package]] +name = "indoc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "inventory" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0f7efb804ec95e33db9ad49e4252f049e37e8b0a4652e3cd61f7999f2eff7f" +dependencies = [ + "ctor", + "ghost", + "inventory-impl", +] + +[[package]] +name = "inventory-impl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c094e94816723ab936484666968f5b58060492e880f3c8d00489a1e244fa51" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "libc" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -59,9 +149,59 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + [[package]] name = "ppc750cl" -version = "0.1.0" +version = "0.1.1" dependencies = [ "num-traits", "ppc750cl-macros", @@ -69,7 +209,7 @@ dependencies = [ [[package]] name = "ppc750cl-fuzz" -version = "0.1.0" +version = "0.1.1" dependencies = [ "num_cpus", "ppc750cl", @@ -77,16 +217,24 @@ dependencies = [ [[package]] name = "ppc750cl-macros" -version = "0.1.0" +version = "0.1.1" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "ppc750cl-py" +version = "0.1.1" +dependencies = [ + "ppc750cl", + "pyo3", +] + [[package]] name = "ppc750cl-rand" -version = "0.1.0" +version = "0.1.1" dependencies = [ "ppc750cl", "rand_core", @@ -99,6 +247,12 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.28" @@ -108,6 +262,55 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "pyo3" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af205762ba65eec9f27a2fa1a57a40644e8e3368784b8c8b2f2de48f6e8ddd96" +dependencies = [ + "cfg-if", + "indoc", + "inventory", + "libc", + "parking_lot", + "paste", + "pyo3-build-config", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755944027ce803c7238e59c5a18e59c1d0a4553db50b23e9ba209a568353028d" +dependencies = [ + "once_cell", +] + +[[package]] +name = "pyo3-macros" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd31b36bccfd902c78804bd96c28ea93eac6fa0ca311f9d21ef2230b6665b29a" +dependencies = [ + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c21c59ba36db9c823e931c662766b0dd01a030b1d96585b67d8857a96a56b972" +dependencies = [ + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.9" @@ -158,6 +361,21 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sfmt" version = "0.6.0" @@ -168,6 +386,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "syn" version = "1.0.74" @@ -185,8 +409,36 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index adfa56b..fafcda0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "disasm", + "disasm-py", "macros", "fuzz", "rand", diff --git a/disasm-py/Cargo.toml b/disasm-py/Cargo.toml new file mode 100644 index 0000000..4f64f31 --- /dev/null +++ b/disasm-py/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ppc750cl-py" +version = "0.1.1" +edition = "2018" +authors = ["Richard Patel "] +license = "GPL-3.0-or-later" +description = "Python bindings for PowerPC 750CL Disassembler" +repository = "https://github.com/terorie/ppc750cl" + +[lib] +name = "ppc750cl" +crate-type = ["cdylib"] + +[dependencies] +pyo3 = { version = "0.14", features = ["extension-module", "multiple-pymethods"] } +ppc750cl = { version = "0.1.1", path = "../disasm" } diff --git a/disasm-py/src/lib.rs b/disasm-py/src/lib.rs new file mode 100644 index 0000000..53b5847 --- /dev/null +++ b/disasm-py/src/lib.rs @@ -0,0 +1,137 @@ +use pyo3::prelude::*; +use pyo3::{PyIterProtocol, PyObjectProtocol}; + +#[pyclass] +struct Ins(ppc750cl::Ins); + +macro_rules! ins_ufield { + ($name:ident) => { + #[pymethods] + impl Ins { + #[getter] + fn $name(&self) -> PyResult { + Ok(self.0.$name() as u32) + } + } + }; +} + +macro_rules! ins_ifield { + ($name:ident) => { + #[pymethods] + impl Ins { + #[getter] + fn $name(&self) -> PyResult { + Ok(self.0.$name() as i32) + } + } + }; +} + +#[pymethods] +impl Ins { + #[new] + fn new(code: u32, addr: u32) -> Self { + Ins(ppc750cl::Ins::new(code, addr)) + } + + #[getter] + fn code(&self) -> PyResult { + Ok(self.0.code) + } + + #[getter] + fn addr(&self) -> PyResult { + Ok(self.0.addr) + } +} + +ins_ufield!(rc); +ins_ufield!(aa); +ins_ufield!(lk); +ins_ufield!(l); +ins_ufield!(oe); +ins_ufield!(w); +ins_ufield!(s); +ins_ufield!(d); +ins_ufield!(a); +ins_ufield!(b); +ins_ufield!(c); +ins_ufield!(crb_d); +ins_ufield!(crb_a); +ins_ufield!(crb_b); +ins_ufield!(crm); +ins_ufield!(sr); +ins_ufield!(spr); +ins_ufield!(fm); +ins_ufield!(crf_d); +ins_ufield!(crf_s); +ins_ifield!(simm); +ins_ufield!(uimm); +ins_ufield!(bo); +ins_ufield!(bi); +ins_ufield!(sh); +ins_ufield!(mb); +ins_ufield!(me); +ins_ufield!(me_31sub); +ins_ifield!(bd); +ins_ifield!(li); +ins_ufield!(to); +ins_ufield!(ps_l); +ins_ifield!(ps_d); + +impl From for Ins { + fn from(ins: ppc750cl::Ins) -> Self { + Self(ins) + } +} + +#[pyproto] +impl<'a> PyObjectProtocol<'a> for Ins { + fn __str__(&self) -> String { + self.0.to_string() + } +} + +#[pyclass] +struct DisasmIterator { + bytes: Vec, + addr: u32, + offset: u32, +} + +#[pyproto] +impl PyIterProtocol for DisasmIterator { + fn __iter__(slf: PyRef) -> PyRef { + slf + } + fn __next__(mut slf: PyRefMut) -> PyResult> { + if (slf.bytes.len() as u32) - slf.offset < 4 { + return Ok(None); + } + let code = ((slf.bytes[(slf.offset + 0) as usize] as u32) << 24) + | ((slf.bytes[(slf.offset + 1) as usize] as u32) << 16) + | ((slf.bytes[(slf.offset + 2) as usize] as u32) << 8) + | (slf.bytes[(slf.offset + 3) as usize] as u32); + slf.offset += 4; + let ins = Ins::new(code, slf.addr); + slf.addr += 4; + Ok(Some(ins)) + } +} + +#[pyfunction] +fn disasm_iter<'a>(code: &[u8], addr: u32) -> PyResult { + Ok(DisasmIterator { + bytes: code.to_vec(), + addr, + offset: 0, + }) +} + +#[pymodule] +fn ppc750cl(_: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_wrapped(wrap_pyfunction!(disasm_iter))?; + Ok(()) +} diff --git a/disasm/Cargo.toml b/disasm/Cargo.toml index ce7582d..1f139ba 100644 --- a/disasm/Cargo.toml +++ b/disasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ppc750cl" -version = "0.1.0" +version = "0.1.1" edition = "2018" authors = ["Richard Patel "] license = "GPL-3.0-or-later" @@ -10,4 +10,4 @@ repository = "https://github.com/terorie/ppc750cl" [dependencies] num-traits = "0.2.14" -ppc750cl-macros = { path = "../macros", version = "0.1.0" } +ppc750cl-macros = { path = "../macros", version = "0.1.1" } diff --git a/disasm/src/iter.rs b/disasm/src/iter.rs new file mode 100644 index 0000000..5a3065b --- /dev/null +++ b/disasm/src/iter.rs @@ -0,0 +1,34 @@ +use crate::Ins; + +/// Returns an iterator of instructions in the given byte slice. +pub fn disasm_iter(code: &[u8], addr: u32) -> DisasmIterator { + DisasmIterator { code, addr } +} + +pub struct DisasmIterator<'a> { + code: &'a [u8], + addr: u32, +} + +impl<'a> Iterator for DisasmIterator<'a> { + type Item = Ins; + + fn next(&mut self) -> Option { + if self.code.len() < 4 { + return None; + } + let code = ((self.code[0] as u32) << 24) + | ((self.code[1] as u32) << 16) + | ((self.code[2] as u32) << 8) + | (self.code[3] as u32); + self.code = &self.code[4..]; + let addr = self.addr; + self.addr += 4; + Some(Ins::new(code, addr)) + } + + fn size_hint(&self) -> (usize, Option) { + let count = self.code.len() / 4; + (count, Some(count)) + } +} diff --git a/disasm/src/lib.rs b/disasm/src/lib.rs index 9171bfb..7020ac9 100644 --- a/disasm/src/lib.rs +++ b/disasm/src/lib.rs @@ -11,10 +11,12 @@ use ppc750cl_macros::write_asm; mod macros; pub mod formatter; mod isa; +mod iter; pub use crate::formatter::AsmFormatter; use crate::formatter::SimpleFormatter; pub use crate::isa::Opcode; +pub use crate::iter::{disasm_iter, DisasmIterator}; #[derive(Default, Clone)] pub struct Ins { @@ -40,7 +42,8 @@ where macro_rules! ins_bit { ($func:ident, $idx:expr) => { - fn $func(&self) -> u8 { + #[inline(always)] + pub fn $func(&self) -> u8 { bit(self.code, $idx) } }; @@ -48,7 +51,8 @@ macro_rules! ins_bit { macro_rules! ins_ufield { ($func:ident, $return_type:ident, $range:expr) => { - fn $func(&self) -> $return_type { + #[inline(always)] + pub fn $func(&self) -> $return_type { debug_assert!( ($range).len() / 8 <= (std::mem::size_of::<$return_type>()), "{:?} does not fit in {}", @@ -62,7 +66,8 @@ macro_rules! ins_ufield { macro_rules! ins_ifield { ($func:ident, $range:expr) => { - fn $func(&self) -> i32 { + #[inline(always)] + pub fn $func(&self) -> i32 { debug_assert!( ($range).len() / 8 <= (std::mem::size_of::()), "{:?} does not fit in {}", @@ -78,7 +83,8 @@ macro_rules! ins_ifield { } }; ($func:ident, $range:expr, $shift:literal) => { - fn $func(&self) -> i32 { + #[inline(always)] + pub fn $func(&self) -> i32 { debug_assert!( ($range).len() / 8 <= (std::mem::size_of::()), "{:?} does not fit in {}", @@ -123,7 +129,7 @@ impl Ins { ins_ufield!(crm, u8, 12..20); ins_ufield!(sr, u8, 12..16); - fn spr(&self) -> u16 { + pub fn spr(&self) -> u16 { bits::(self.code, 11..16) | (bits::(self.code, 16..21) << 5) } ins_ufield!(fm, u16, 7..15); @@ -136,7 +142,7 @@ impl Ins { ins_ufield!(sh, u8, 16..21); ins_ufield!(mb, u8, 21..26); ins_ufield!(me, u8, 26..31); - fn me_31sub(&self) -> u8 { + pub fn me_31sub(&self) -> u8 { 31 - self.me() } ins_ifield!(bd, 16..30, 2); @@ -161,7 +167,7 @@ impl Ins { // Ported from // https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/Common/GekkoDisassembler.cpp - #[inline] + #[inline(always)] fn write_asm_branch(&self, out: &mut F, bname: &str) -> std::io::Result<()> where F: AsmFormatter, diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f8d0f92..fdcb8d7 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ppc750cl-fuzz" -version = "0.1.0" +version = "0.1.1" edition = "2018" authors = ["Richard Patel "] license = "GPL-3.0-or-later" @@ -9,4 +9,4 @@ repository = "https://github.com/terorie/ppc750cl" [dependencies] num_cpus = "1.13" -ppc750cl = { path = "../disasm", version = "0.1.0" } +ppc750cl = { path = "../disasm", version = "0.1.1" } diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 6594b19..51288b3 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ppc750cl-macros" -version = "0.1.0" +version = "0.1.1" edition = "2018" authors = ["Richard Patel "] license = "GPL-3.0-or-later" diff --git a/rand/Cargo.toml b/rand/Cargo.toml index 66cdce2..1002969 100644 --- a/rand/Cargo.toml +++ b/rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ppc750cl-rand" -version = "0.1.0" +version = "0.1.1" edition = "2018" authors = ["Richard Patel "] license = "GPL-3.0-or-later" @@ -8,6 +8,6 @@ description = "Generate random PowerPC 750CL instructions" repository = "https://github.com/terorie/ppc750cl" [dependencies] -ppc750cl = { path = "../disasm", version = "0.1.0" } +ppc750cl = { path = "../disasm", version = "0.1.1" } rand_core = "0.5" sfmt = "0.6"