diff --git a/Cargo.toml b/Cargo.toml index 83c0e33..9402586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "ax-x86" -version = "0.3.1" +version = "0.3.2" authors = ["xarantolus "] edition = "2018" -license = "GPL-3.0" +license = "AGPL-3.0" homepage = "https://ax.010.one" repository = "https://github.com/xarantolus/ax" description = "x86 emulator for running simple binaries in your browser" diff --git a/LICENSE b/LICENSE index f288702..0ad25db 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies @@ -7,17 +7,15 @@ Preamble - The GNU General Public License is a free, copyleft license for -software and other kinds of works. + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to +our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. +software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you @@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. The precise terms and conditions for copying, distribution and modification follow. @@ -72,7 +60,7 @@ modification follow. 0. Definitions. - "This License" refers to version 3 of the GNU General Public License. + "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. @@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - 13. Use with the GNU Affero General Public License. + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single +under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General +Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published +GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's +versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. @@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see +For more information on this, and how to apply and follow the GNU AGPL, see . - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.md b/README.md index 335776d..86025a6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,11 @@ Additionally, this repository contains scripts that should be interesting for an ## Try it out! You can try out the emulator right now by visiting [the website](https://ax.010.one), selecting a suitable ELF binary and clicking "Run". The emulator will then execute the binary and show the output. Note that currently support for ELF binaries is limited/buggy (there are some problems getting libc to work), you can however use binaries from the [`examples/programs`](examples/programs) directory. The source code for this site is in the [`examples/web`](examples/web) directory. -Other than that, you can also find it in use on the [MemeAssembly Playground](https://memeasm.010.one). MemeAssembly is a meme programming language that compiles to x86-64 instructions, which are executed by this emulator. The site also emulates some syscalls like `read`, `write` and `exit` to make the programs work. + +### Usage on the web +* The [demo site](https://ax.010.one) uses `ax` to run ELF binaries in the browser +* [MemeAssembly](https://kammt.github.io/MemeAssembly/) is a meme programming language that compiles to x86-64 instructions. My [MemeAssembly Playground](https://memeasm.010.one) uses `ax` to run this language right in the browser, emulating some syscalls like `read`, `write` and `exit` to make the programs work. +* Maybe you? If you use `ax` in a project, please let me know and I'll add it here! :) ## How to use The emulator is compiled to WebAssembly and can be used as a JavaScript Module. This works in modern browsers. @@ -136,10 +140,10 @@ let syscallHandler = async function (ax: Axecutor) { throw new Error(`WRITE syscall: cannot write non-std{out,err} (!= 1,2) fds, but tried ${rdi}`); } - // Read whatever was + // Read data we should write from memory let result_buf = ax.mem_read_bytes(rsi, rdx); - // Convert to string + // Decode to string let result_str = new TextDecoder().decode(result_buf); // Do something with the string @@ -271,4 +275,4 @@ See [issue 1](https://github.com/xarantolus/ax/issues/1) for some ideas for futu ## [License](LICENSE) -GPL-3.0 +AGPL v3 diff --git a/examples/web/package-lock.json b/examples/web/package-lock.json index 2e45a38..9f4aa68 100644 --- a/examples/web/package-lock.json +++ b/examples/web/package-lock.json @@ -25,8 +25,8 @@ }, "../../pkg": { "name": "ax-x86", - "version": "0.3.0", - "license": "GPL-3.0" + "version": "0.3.1", + "license": "AGPL-3.0" }, "../pkg": { "version": "0.1.0", diff --git a/examples/web/src/components/Demo.vue b/examples/web/src/components/Demo.vue index 6f75ed8..7a599fb 100644 --- a/examples/web/src/components/Demo.vue +++ b/examples/web/src/components/Demo.vue @@ -249,6 +249,7 @@ export default defineComponent({ ax.reg_write_64(Register.RAX, this.toTwosComplement(-1n)); return ax.commit(); } + // TODO: make sure that all syscalls return something sensible, the following might not case 102n: // getuid case 104n: // getgid case 107n: // geteuid diff --git a/generate.py b/generate.py index 1e16e4b..83cf363 100644 --- a/generate.py +++ b/generate.py @@ -280,6 +280,7 @@ def generate_all_switch(): code += """ #[wasm_bindgen(js_name = Mnemonic)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +/// All mnemonics supported by the emulator pub enum SupportedMnemonic { """ for (mnemonic, num) in mnems: diff --git a/src/instructions/axecutor.rs b/src/instructions/axecutor.rs index b6c5dfe..7761ac7 100644 --- a/src/instructions/axecutor.rs +++ b/src/instructions/axecutor.rs @@ -15,6 +15,15 @@ extern crate console_error_panic_hook; #[wasm_bindgen] #[derive(Debug, Clone, Serialize, Deserialize)] +/// Axecutor is the main struct that is used for emulation. +/// +/// It can be instantiated in JavaScript using one of the following methods: +/// - `let ax = new Axecutor(code: Uint8Array, code_start_addr: bigint, initial_rip: bigint)` +/// - `let ax = Axecutor.fromBinary(elf_binary: Uint8Array)`. +/// +/// Afterwards, one can register hooks before/after instructions: +/// - `ax.hook_before_mnemonic(Mnemonic.Syscall, (axInstance: Axecutor) => {...});` +/// - `ax.hook_after_mnemonic(Mnemonic.Syscall, (axInstance: Axecutor) => {...});` pub struct Axecutor { // code_start_address is the memory address/RIP of the first instruction, which isn't necessarily the entrypoint (can be set by writing to RIP) pub(crate) code_start_address: u64, @@ -123,6 +132,7 @@ impl MachineState { #[wasm_bindgen] impl Axecutor { #[wasm_bindgen(constructor)] + /// Creates a new Axecutor instance from the given x86-64 instruction bytes, writing the code to memory at `code_start_addr` and setting the initial RIP to `initial_rip`. pub fn new(code: &[u8], code_start_addr: u64, initial_rip: u64) -> Result { debug_log!("Calling Axecutor::new"); @@ -186,14 +196,15 @@ impl Axecutor { result } - pub fn commit(&self) -> Result { + /// This function should be returned from a hook to indicate the state should be kept and execution should continue. + pub fn commit(&self) -> Result { debug_log!( "Calling Axecutor::commit, finished: {}, hooks_running: {}", self.state.finished, self.hooks.running ); if !self.hooks.running { - return Err(JsError::new("Cannot call commit() outside of a hook")); + return Err(AxError::from("Cannot call commit() outside of a hook")); } debug_log!("FS value in commit: {:#x}", self.state.fs); @@ -202,17 +213,18 @@ impl Axecutor { self.state .serialize(&s) - .map_err(|e| JsError::new(&*format!("Failed to serialize: {}", e))) + .map_err(|e| AxError::from(&*format!("Failed to serialize: {}", e))) } - pub fn stop(&mut self) -> Result { + /// This function should be returned from a hook to indicate the state should be kept and execution should stop. + pub fn stop(&mut self) -> Result { debug_log!( "Calling Axecutor::stop, finished: {}, hooks_running: {}", self.state.finished, self.hooks.running ); if !self.hooks.running { - return Err(JsError::new("Cannot call stop() outside of a hook")); + return Err(AxError::from("Cannot call stop() outside of a hook")); } self.state.finished = true; @@ -220,6 +232,7 @@ impl Axecutor { self.commit() } + /// This function should be returned from a hook to indicate the state should *not* be kept, but execution should continue. pub fn unchanged(&self) -> JsValue { debug_log!( "Calling Axecutor::unchanged, finished: {}, hooks_running: {}", @@ -229,7 +242,7 @@ impl Axecutor { JsValue::NULL } - pub(crate) fn state_from_committed(&mut self, value: JsValue) -> Result<(), JsError> { + pub(crate) fn state_from_committed(&mut self, value: JsValue) -> Result<(), AxError> { debug_log!( "Calling Axecutor::state_from_committed, finished: {}, hooks_running: {}", self.state.finished, @@ -237,17 +250,17 @@ impl Axecutor { ); if !self.hooks.running { - return Err(JsError::new( + return Err(AxError::from( "Cannot call state_from_committed() outside of a hook", )); } if value.is_falsy() { - return Err(JsError::new("Cannot call state_from_committed() with falsy value. Note that you *must* return either null or Axecutor.commit() from your hook")); + return Err(AxError::from("Cannot call state_from_committed() with falsy value. Note that you *must* return either null or Axecutor.commit() from your hook")); } let state: MachineState = serde_wasm_bindgen::from_value(value).map_err(|e| { - JsError::new(&*format!( + AxError::from(&*format!( "state_from_committed: failed to deserialize state: {}\nNote that you *must* return either Axecutor.unchanged(), Axecutor.stop() or Axecutor.commit() from your hook", e, )) @@ -271,7 +284,7 @@ mod tests { let instruction = ax.decode_next().expect("Failed to get instruction"); assert_eq!(instruction.ip(), 0x1000); assert_eq!(instruction.next_ip(), 0x1000 + code.len() as u64); - assert_eq!(ax.reg_read_64(Register::RIP.into()), 0x1000); + assert_eq!(ax.reg_read_64(Register::RIP.into()).unwrap(), 0x1000); } test_async![top_lvl_return_with_stack_setup; async { diff --git a/src/instructions/call.rs b/src/instructions/call.rs index be844f8..14123b3 100644 --- a/src/instructions/call.rs +++ b/src/instructions/call.rs @@ -12,10 +12,10 @@ use crate::{fatal_error, opcode_unimplemented}; macro_rules! push_rip { ($self:ident) => {{ - let rip = $self.reg_read_64(RIP); - let rsp = $self.reg_read_64(RSP); + let rip = $self.reg_read_64(RIP)?; + let rsp = $self.reg_read_64(RSP)?; $self.mem_write_64(rsp, rip)?; - $self.reg_write_64(RSP, rsp - 8); + $self.reg_write_64(RSP, rsp - 8)?; }}; } @@ -85,13 +85,13 @@ impl Axecutor { OpKind::NearBranch64 => { // push_rip!(self); - let rip = self.reg_read_64(RIP); - let rsp = self.reg_read_64(RSP); + let rip = self.reg_read_64(RIP)?; + let rsp = self.reg_read_64(RSP)?; self.mem_write_64(rsp, rip)?; - self.reg_write_64(RSP, rsp - 8); + self.reg_write_64(RSP, rsp - 8)?; let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP.into(), offset); + self.reg_write_64(RIP.into(), offset)?; Ok(()) } _ => fatal_error!("Invalid op0_kind for CALL rel32: {:?}", i.op0_kind()), @@ -127,12 +127,12 @@ impl Axecutor { let addr = self.mem_addr(m); self.mem_read_64(addr)? } - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, _ => fatal_error!("Invalid operand for CALL r/m64: {:?}", i.op0_kind()), }; push_rip!(self); - self.reg_write_64(RIP.into(), target); + self.reg_write_64(RIP.into(), target)?; Ok(()) } @@ -188,7 +188,7 @@ mod tests { let mut ax = Axecutor::new(code, rip, rip).expect("Failed to create axecutor"); // Setup stack - ax.reg_write_64(RSP.into(), 0x1000 - 8); + ax.reg_write_64(RSP.into(), 0x1000 - 8).expect("Failed to write to register"); ax.mem_init_zero(0x1000 - 8, 8) .expect("Failed to init memory"); @@ -217,7 +217,7 @@ mod tests { 50000; // 50000 bytes of 0x90 (nop) as padding 0x48, 0xc7, 0xc0, 0x32, 0x0, 0x0, 0x0, 0xe8, 0x9c, 0x3c, 0xff, 0xff, 0x90; // Lcall: mov rax, 50; call func; nop |a: &mut Axecutor| { - a.reg_write_64(RSP.into(), 0x8000); + a.reg_write_64(RSP.into(), 0x8000).unwrap(); a.mem_init_zero(0x8000, 8).expect("Failed to init memory"); }; |a: Axecutor| { @@ -232,7 +232,7 @@ mod tests { 50; // 50 bytes of 0x90 (nop) as padding 0x48, 0x8d, 0x5, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xd0, 0x90; // Lcall: lea rax, [rip+func]; call rax; nop |a: &mut Axecutor| { - a.reg_write_64(RSP.into(), 0x8000); + a.reg_write_64(RSP.into(), 0x8000).unwrap(); a.mem_init_zero(0x8000, 8).expect("Failed to init memory"); }; |a: Axecutor| { diff --git a/src/instructions/cdq.rs b/src/instructions/cdq.rs index 84cb4e4..9e8d8db 100644 --- a/src/instructions/cdq.rs +++ b/src/instructions/cdq.rs @@ -23,14 +23,14 @@ impl Axecutor { fn instr_cdq(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), iced_x86::Code::Cdq); - let eax = self.reg_read_32(EAX); + let eax = self.reg_read_32(EAX)?; let edx = if eax & 0x8000_0000 == 0 { 0 } else { 0xFFFF_FFFF }; - self.reg_write_32(EDX, edx); + self.reg_write_32(EDX, edx)?; Ok(()) } diff --git a/src/instructions/cpuid.rs b/src/instructions/cpuid.rs index 20fbcc5..ff9de2d 100644 --- a/src/instructions/cpuid.rs +++ b/src/instructions/cpuid.rs @@ -26,14 +26,14 @@ impl Axecutor { fn instr_cpuid(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), Code::Cpuid); - // let eax = self.reg_read_32(SupportedRegister::EAX); - // let ecx = self.reg_read_32(SupportedRegister::ECX); + // let eax = self.reg_read_32(SupportedRegister::EAX)?; + // let ecx = self.reg_read_32(SupportedRegister::ECX)?; // just write dummy values for now. Not sure if this even makes sense - self.reg_write_32(SupportedRegister::EAX, 0); - self.reg_write_32(SupportedRegister::EBX, 0); - self.reg_write_32(SupportedRegister::ECX, 0); - self.reg_write_32(SupportedRegister::EDX, 0); + self.reg_write_32(SupportedRegister::EAX, 0)?; + self.reg_write_32(SupportedRegister::EBX, 0)?; + self.reg_write_32(SupportedRegister::ECX, 0)?; + self.reg_write_32(SupportedRegister::EDX, 0)?; Ok(()) } diff --git a/src/instructions/cqo.rs b/src/instructions/cqo.rs index 9f5595e..e4543a7 100644 --- a/src/instructions/cqo.rs +++ b/src/instructions/cqo.rs @@ -23,14 +23,14 @@ impl Axecutor { fn instr_cqo(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), iced_x86::Code::Cqo); - let rax = self.reg_read_64(RAX); + let rax = self.reg_read_64(RAX)?; let rdx = if rax & 0x8000_0000_0000_0000 == 0 { 0 } else { 0xFFFF_FFFF_FFFF_FFFF }; - self.reg_write_64(RDX, rdx); + self.reg_write_64(RDX, rdx)?; Ok(()) } diff --git a/src/instructions/cwd.rs b/src/instructions/cwd.rs index c69f982..c6dc498 100644 --- a/src/instructions/cwd.rs +++ b/src/instructions/cwd.rs @@ -23,14 +23,14 @@ impl Axecutor { fn instr_cwd(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), iced_x86::Code::Cwd); - let ax_value = self.reg_read_16(AX); + let ax_value = self.reg_read_16(AX)?; let dx_value = if ax_value & 0x8000 == 0x8000 { 0xFFFF } else { 0x0000 }; - self.reg_write_16(DX, dx_value); + self.reg_write_16(DX, dx_value)?; Ok(()) } diff --git a/src/instructions/div.rs b/src/instructions/div.rs index d7841ab..0b8b2ba 100644 --- a/src/instructions/div.rs +++ b/src/instructions/div.rs @@ -28,11 +28,11 @@ impl Axecutor { fn instr_div_rm8(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), Div_rm8); - let ax = self.reg_read_16(AX) as u16; + let ax = self.reg_read_16(AX)? as u16; let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Div_rm8", op), } as u16; @@ -46,8 +46,8 @@ impl Axecutor { let (quotient, remainder) = (ax / src_val, ax % src_val); - self.reg_write_8(AL, quotient as u8 as u64); - self.reg_write_8(AH, remainder as u8 as u64); + self.reg_write_8(AL, quotient as u8 as u64)?; + self.reg_write_8(AH, remainder as u8 as u64)?; Ok(()) } @@ -61,7 +61,7 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Div_rm16", op), } as u32; @@ -73,12 +73,12 @@ impl Axecutor { ))); } - let dst_val = self.reg_read_16(AX) as u32 | ((self.reg_read_16(DX) as u32) << 16); + let dst_val = self.reg_read_16(AX)? as u32 | ((self.reg_read_16(DX)? as u32) << 16); let (quotient, remainder) = (dst_val / src_val, dst_val % src_val); - self.reg_write_16(AX, quotient as u16 as u64); - self.reg_write_16(DX, remainder as u16 as u64); + self.reg_write_16(AX, quotient as u16 as u64)?; + self.reg_write_16(DX, remainder as u16 as u64)?; Ok(()) } @@ -92,7 +92,7 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Div_rm32", op), } as u64; @@ -104,12 +104,12 @@ impl Axecutor { ))); } - let dst_val = self.reg_read_32(EAX) as u64 | ((self.reg_read_32(EDX) as u64) << 32); + let dst_val = self.reg_read_32(EAX)? as u64 | ((self.reg_read_32(EDX)? as u64) << 32); let (quotient, remainder) = (dst_val / src_val, dst_val % src_val); - self.reg_write_32(EAX, quotient as u32 as u64); - self.reg_write_32(EDX, remainder as u32 as u64); + self.reg_write_32(EAX, quotient as u32 as u64)?; + self.reg_write_32(EDX, remainder as u32 as u64)?; Ok(()) } @@ -123,7 +123,7 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Div_rm64", op), } as u128; @@ -135,12 +135,12 @@ impl Axecutor { ))); } - let dst_val = (self.reg_read_64(RAX) as u128) | ((self.reg_read_64(RDX) as u128) << 64); + let dst_val = (self.reg_read_64(RAX)? as u128) | ((self.reg_read_64(RDX)? as u128) << 64); let (quotient, remainder) = (dst_val / src_val, dst_val % src_val); - self.reg_write_64(RAX, quotient as u64); - self.reg_write_64(RDX, remainder as u64); + self.reg_write_64(RAX, quotient as u64)?; + self.reg_write_64(RDX, remainder as u64)?; Ok(()) } @@ -156,7 +156,7 @@ mod tests { async fn assert_divide_by_zero_error(code: &[u8]) { let mut ax = Axecutor::new(code, 0x1000, 0x1000).expect("Failed to create Axecutor"); - ax.reg_write_64(SupportedRegister::RAX, 0); + ax.reg_write_64(SupportedRegister::RAX, 0).unwrap(); let _ = ax.execute().await; } diff --git a/src/instructions/execute.rs b/src/instructions/execute.rs index 8606487..972f6f0 100644 --- a/src/instructions/execute.rs +++ b/src/instructions/execute.rs @@ -2,7 +2,6 @@ use std::{cmp::min, convert::TryInto}; use iced_x86::{Decoder, DecoderOptions, Instruction, Register}; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsError; use crate::{ debug_log, @@ -14,7 +13,7 @@ use super::{axecutor::Axecutor, errors::AxError}; #[wasm_bindgen] impl Axecutor { pub(crate) fn decode_next(&self) -> Result { - let rip = self.reg_read_64(SupportedRegister::RIP); + let rip = self.reg_read_64(SupportedRegister::RIP)?; if rip < self.code_start_address || rip >= self.code_start_address + self.code.len() as u64 { @@ -51,19 +50,18 @@ impl Axecutor { Ok(instr) } - // step executes the next instruction, returning if the execution can continue running - #[wasm_bindgen] - pub async fn step(&mut self) -> Result { + /// Execute the next instruction (including all registered hooks), returning if the execution has stopped + pub async fn step(&mut self) -> Result { debug_log!( "Calling Axecutor::step, finished={}, rip={:#x}", self.state.finished, - self.reg_read_64(Register::RIP.into()) + self.reg_read_64(Register::RIP.into())? ); if self.state.finished { - return Err( - AxError::from("Cannot advance after execution has already finished").into(), - ); + return Err(AxError::from( + "Cannot advance after execution has already finished", + )); } // Fetch the next instruction @@ -72,7 +70,7 @@ impl Axecutor { .map_err(|e| AxError::from(format!("decoding instruction: {}", e)))?; debug_log!("Fetched instruction {}", instr); - self.reg_write_64(SupportedRegister::RIP, instr.next_ip()); + self.reg_write_64(SupportedRegister::RIP, instr.next_ip())?; let mnem: SupportedMnemonic = instr.mnemonic().try_into()?; @@ -124,7 +122,7 @@ impl Axecutor { self.state.executed_instructions_count += 1; // If we reached the last instruction (and no jump has been performed etc.), we're done - if self.reg_read_64(Register::RIP.into()) + if self.reg_read_64(Register::RIP.into())? == self.code_start_address + self.code.len() as u64 { self.state.finished = true; @@ -143,7 +141,9 @@ impl Axecutor { Ok(!self.state.finished) } - pub async fn execute(&mut self) -> Result<(), JsError> { + /// Execute all instructions until the execution has stopped. + /// This is the same as calling `step` in a loop, but staying in WASM should be more efficient. + pub async fn execute(&mut self) -> Result<(), AxError> { debug_log!("Calling Axecutor::execute"); while self.step().await? {} Ok(()) diff --git a/src/instructions/generated.rs b/src/instructions/generated.rs index 06bef65..4a02fa1 100644 --- a/src/instructions/generated.rs +++ b/src/instructions/generated.rs @@ -82,6 +82,7 @@ impl Axecutor { #[wasm_bindgen(js_name = Mnemonic)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +/// All mnemonics supported by the emulator pub enum SupportedMnemonic { Add = 7, And = 21, diff --git a/src/instructions/hooks.rs b/src/instructions/hooks.rs index 94f0ba7..2f2bfd6 100644 --- a/src/instructions/hooks.rs +++ b/src/instructions/hooks.rs @@ -2,7 +2,7 @@ extern crate lazy_static; use js_sys::{self, Array, Function}; use std::{collections::HashMap, fmt::Formatter}; -use wasm_bindgen::{prelude::*, JsCast}; +use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use std::fmt::Debug; @@ -124,25 +124,31 @@ impl HookProcessor { #[wasm_bindgen] impl Axecutor { + /// Register a function to be called before a mnemonic is executed. The function will be called with the Axecutor object as first argument. + /// The function may be sync or async and *MUST* return the result of one of the following functions: + /// - instance.commit(): Continue execution, keep data + /// - instance.stop(): Stop execution, keep data + /// - instance.unchanged(): Continue execution, but discard data changed in the hook + /// You can register multiple functions for the same mnemonic, the order of execution is however not defined. pub fn hook_before_mnemonic( &mut self, mnemonic: SupportedMnemonic, cb: JsValue, - ) -> Result<(), JsError> { + ) -> Result<(), AxError> { debug_log!( "Calling Axecutor::hook_before_mnemonic, hooks_running={}", self.hooks.running ); if self.hooks.running { - return Err(JsError::new( + return Err(AxError::from( "Cannot add hooks while another hook is running", )); } if cb.has_type::() { let function = cb.dyn_into::().map_err(|_| { - JsError::new("The provided callback is not a function. Please provide a function.") + AxError::from("The provided callback is not a function. Please provide a function.") })?; debug_log!( @@ -165,31 +171,37 @@ impl Axecutor { Ok(()) } else { debug_log!("hook_before_mnemonic: Provided callback is not a function"); - Err(JsError::new(&*format!( + Err(AxError::from(&*format!( "hook_before_mnemonic: expected function or async function argument, but got {:?}", cb ))) } } + /// Register a function to be called after a mnemonic is executed. The function will be called with the Axecutor object as first argument. + /// The function may be sync or async and *MUST* return the result of one of the following functions: + /// - instance.commit(): Continue execution, keep data + /// - instance.stop(): Stop execution, keep data + /// - instance.unchanged(): Continue execution, but discard data changed in the hook + /// You can register multiple functions for the same mnemonic, the order of execution is however not defined. pub fn hook_after_mnemonic( &mut self, mnemonic: SupportedMnemonic, cb: JsValue, - ) -> Result<(), JsError> { + ) -> Result<(), AxError> { debug_log!( "Calling Axecutor::hook_after_mnemonic, hooks_running={}", self.hooks.running ); if self.hooks.running { - return Err(JsError::new( + return Err(AxError::from( "Cannot add hooks while another hook is running", )); } if cb.has_type::() { let function = cb.dyn_into::().map_err(|_| { - JsError::new("The provided callback is not a function. Please provide a function.") + AxError::from("The provided callback is not a function. Please provide a function.") })?; debug_log!( @@ -209,7 +221,7 @@ impl Axecutor { ); Ok(()) } else { - Err(JsError::new(&*format!( + Err(AxError::from(&*format!( "hook_after_mnemonic: expected function or async function argument, but got {:?}", cb ))) diff --git a/src/instructions/idiv.rs b/src/instructions/idiv.rs index 2a8f154..b1a84f4 100644 --- a/src/instructions/idiv.rs +++ b/src/instructions/idiv.rs @@ -28,11 +28,11 @@ impl Axecutor { fn instr_idiv_rm8(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), Idiv_rm8); - let ax = self.reg_read_16(AX) as i16; + let ax = self.reg_read_16(AX)? as i16; let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Idiv_rm8", op), } as i16; @@ -46,8 +46,8 @@ impl Axecutor { let (quotient, remainder) = (ax / src_val, ax % src_val); - self.reg_write_8(AL, quotient as u8 as u64); - self.reg_write_8(AH, remainder as u8 as u64); + self.reg_write_8(AL, quotient as u8 as u64)?; + self.reg_write_8(AH, remainder as u8 as u64)?; Ok(()) } @@ -61,7 +61,7 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Idiv_rm16", op), } as i32; @@ -73,12 +73,13 @@ impl Axecutor { ))); } - let dst_val = (self.reg_read_16(AX) as u32 | ((self.reg_read_16(DX) as u32) << 16)) as i32; + let dst_val = + (self.reg_read_16(AX)? as u32 | ((self.reg_read_16(DX)? as u32) << 16)) as i32; let (quotient, remainder) = (dst_val / src_val, dst_val % src_val); - self.reg_write_16(AX, quotient as u16 as u64); - self.reg_write_16(DX, remainder as u16 as u64); + self.reg_write_16(AX, quotient as u16 as u64)?; + self.reg_write_16(DX, remainder as u16 as u64)?; Ok(()) } @@ -92,7 +93,7 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Idiv_rm32", op), } as i64; @@ -105,12 +106,12 @@ impl Axecutor { } let dst_val = - (self.reg_read_32(EAX) as u64 | ((self.reg_read_32(EDX) as u64) << 32)) as i64; + (self.reg_read_32(EAX)? as u64 | ((self.reg_read_32(EDX)? as u64) << 32)) as i64; let (quotient, remainder) = (dst_val / src_val, dst_val % src_val); - self.reg_write_32(EAX, quotient as u32 as u64); - self.reg_write_32(EDX, remainder as u32 as u64); + self.reg_write_32(EAX, quotient as u32 as u64)?; + self.reg_write_32(EDX, remainder as u32 as u64)?; Ok(()) } @@ -124,7 +125,7 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Idiv_rm64", op), } as u128 as i128; @@ -137,12 +138,12 @@ impl Axecutor { } let dst_val = - (self.reg_read_64(RAX) as u128 | ((self.reg_read_64(RDX) as u128) << 64)) as i128; + (self.reg_read_64(RAX)? as u128 | ((self.reg_read_64(RDX)? as u128) << 64)) as i128; let (quotient, remainder) = (dst_val / src_val, dst_val % src_val); - self.reg_write_64(RAX, quotient as u64); - self.reg_write_64(RDX, remainder as u64); + self.reg_write_64(RAX, quotient as u64)?; + self.reg_write_64(RDX, remainder as u64)?; Ok(()) } @@ -158,7 +159,7 @@ mod tests { async fn assert_divide_by_zero_error(code: &[u8]) { let mut ax = Axecutor::new(code, 0x1000, 0x1000).expect("Failed to create Axecutor"); - ax.reg_write_64(SupportedRegister::RAX, 0); + ax.reg_write_64(SupportedRegister::RAX, 0).unwrap(); let _ = ax.execute().await; } diff --git a/src/instructions/imul.rs b/src/instructions/imul.rs index fbb3787..b991fe1 100644 --- a/src/instructions/imul.rs +++ b/src/instructions/imul.rs @@ -41,7 +41,7 @@ impl Axecutor { let imm_op = self.instruction_operand(i, 2)?; let src_value = match src_op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand for IMUL r16, r/m16, imm16"), } as i16; @@ -55,7 +55,7 @@ impl Axecutor { let (result, overflow) = src_value.overflowing_mul(imm_value); - self.reg_write_16(dest_op.into(), result as u16 as u64); + self.reg_write_16(dest_op.into(), result as u16 as u64)?; if overflow { self.set_flags_u8(FLAG_CF | FLAG_OF, 0, 0); @@ -76,7 +76,7 @@ impl Axecutor { let imm_op = self.instruction_operand(i, 2)?; let src_value = match src_op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand for IMUL r32, r/m32, imm32"), } as i32; @@ -90,7 +90,7 @@ impl Axecutor { let (result, overflow) = src_value.overflowing_mul(imm_value); - self.reg_write_32(dest_op.into(), result as u32 as u64); + self.reg_write_32(dest_op.into(), result as u32 as u64)?; if overflow { self.set_flags_u8(FLAG_CF | FLAG_OF, 0, 0); @@ -111,7 +111,7 @@ impl Axecutor { let imm_op = self.instruction_operand(i, 2)?; let src_value = match src_op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand for IMUL r64, r/m64, imm32"), } as i64; @@ -125,7 +125,7 @@ impl Axecutor { let (result, overflow) = src_value.overflowing_mul(imm_value); - self.reg_write_64(dest_op.into(), result as u64); + self.reg_write_64(dest_op.into(), result as u64)?; if overflow { self.set_flags_u8(FLAG_CF | FLAG_OF, 0, 0); @@ -146,7 +146,7 @@ impl Axecutor { let imm_op = self.instruction_operand(i, 2)?; let src_value = match src_op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand for IMUL r16, r/m16, imm8"), } as i16; @@ -160,7 +160,7 @@ impl Axecutor { let (result, overflow) = src_value.overflowing_mul(imm_value); - self.reg_write_16(dest_op.into(), result as u16 as u64); + self.reg_write_16(dest_op.into(), result as u16 as u64)?; if overflow { self.set_flags_u8(FLAG_CF | FLAG_OF, 0, 0); @@ -181,7 +181,7 @@ impl Axecutor { let imm_op = self.instruction_operand(i, 2)?; let src_value = match src_op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand for IMUL r32, r/m32, imm8"), } as i32; @@ -195,7 +195,7 @@ impl Axecutor { let (result, overflow) = src_value.overflowing_mul(imm_value); - self.reg_write_32(dest_op.into(), result as u32 as u64); + self.reg_write_32(dest_op.into(), result as u32 as u64)?; if overflow { self.set_flags_u8(FLAG_CF | FLAG_OF, 0, 0); @@ -216,7 +216,7 @@ impl Axecutor { let imm_op = self.instruction_operand(i, 2)?; let src_value = match src_op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand for IMUL r64, r/m64, imm8"), } as i64; @@ -230,7 +230,7 @@ impl Axecutor { let (result, overflow) = src_value.overflowing_mul(imm_value); - self.reg_write_64(dest_op.into(), result as u64); + self.reg_write_64(dest_op.into(), result as u64)?; if overflow { self.set_flags_u8(FLAG_CF | FLAG_OF, 0, 0); @@ -250,16 +250,16 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_rm8", op), } as i8; - let dst_val = self.reg_read_8(AL) as i8; + let dst_val = self.reg_read_8(AL)? as i8; let result = (dst_val as i16).wrapping_mul(src_val as i16); - self.reg_write_16(AX, result as u16 as u64); + self.reg_write_16(AX, result as u16 as u64)?; self.set_flags_u8( if result >> 7 != 0 && result >> 7 != -1 { @@ -287,17 +287,17 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_rm16", op), } as i16; - let dst_val = self.reg_read_16(AX) as i16; + let dst_val = self.reg_read_16(AX)? as i16; let result = (dst_val as i32).wrapping_mul(src_val as i32); - self.reg_write_16(AX, result as u16 as u64); - self.reg_write_16(DX, (result >> 16) as u16 as u64); + self.reg_write_16(AX, result as u16 as u64)?; + self.reg_write_16(DX, (result >> 16) as u16 as u64)?; self.set_flags_u16( if result >> 15 != 0 && result >> 15 != -1 { @@ -325,17 +325,17 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_rm32", op), } as i32; - let dst_val = self.reg_read_32(EAX) as i32; + let dst_val = self.reg_read_32(EAX)? as i32; let result = (dst_val as i64).wrapping_mul(src_val as i64); - self.reg_write_32(EAX, result as u32 as u64); - self.reg_write_32(EDX, (result >> 32) as u32 as u64); + self.reg_write_32(EAX, result as u32 as u64)?; + self.reg_write_32(EDX, (result >> 32) as u32 as u64)?; self.set_flags_u32( if result >> 31 != 0 && result >> 31 != -1 { @@ -363,17 +363,17 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_rm64", op), } as i64; - let dst_val = self.reg_read_64(RAX) as i64; + let dst_val = self.reg_read_64(RAX)? as i64; let result = (dst_val as i128).wrapping_mul(src_val as i128); - self.reg_write_64(RAX, result as u64); - self.reg_write_64(RDX, (result >> 64) as u64); + self.reg_write_64(RAX, result as u64)?; + self.reg_write_64(RDX, (result >> 64) as u64)?; self.set_flags_u64( if result >> 63 != 0 && result >> 63 != -1 { @@ -401,19 +401,19 @@ impl Axecutor { let (dest_op, src_op) = self.instruction_operands_2(i)?; let src_val = match src_op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_r16_rm16", src_op), } as i16; let dst_val = match dest_op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, _ => fatal_error!("Invalid operand {:?} for Imul_r16_rm16", dest_op), } as i16; let result = (dst_val as i32).wrapping_mul(src_val as i32); - self.reg_write_16(dest_op.into(), result as u16 as u64); + self.reg_write_16(dest_op.into(), result as u16 as u64)?; self.set_flags_u16( if result >> 15 != 0 && result >> 15 != -1 { @@ -441,19 +441,19 @@ impl Axecutor { let (dest_op, src_op) = self.instruction_operands_2(i)?; let src_val = match src_op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_r32_rm32", src_op), } as i32; let dst_val = match dest_op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, _ => fatal_error!("Invalid operand {:?} for Imul_r32_rm32", dest_op), } as i32; let result = (dst_val as i64).wrapping_mul(src_val as i64); - self.reg_write_32(dest_op.into(), result as u32 as u64); + self.reg_write_32(dest_op.into(), result as u32 as u64)?; self.set_flags_u32( if result >> 31 != 0 && result >> 31 != -1 { @@ -481,19 +481,19 @@ impl Axecutor { let (dest_op, src_op) = self.instruction_operands_2(i)?; let src_val = match src_op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Imul_r64_rm64", src_op), } as i64; let dst_val = match dest_op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, _ => fatal_error!("Invalid operand {:?} for Imul_r64_rm64", dest_op), } as i64; let result = (dst_val as i128).wrapping_mul(src_val as i128); - self.reg_write_64(dest_op.into(), result as u64); + self.reg_write_64(dest_op.into(), result as u64)?; self.set_flags_u64( if result >> 63 != 0 && result >> 63 != -1 { diff --git a/src/instructions/integration_tests.rs b/src/instructions/integration_tests.rs index cddd92f..ec09331 100644 --- a/src/instructions/integration_tests.rs +++ b/src/instructions/integration_tests.rs @@ -12,14 +12,14 @@ mod test { write_reg_value!(q; a; RBX; 0x031591385913u64); // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { assert_reg_value!(q; a; RAX; 0); assert_reg_value!(q; a; RBX; 0x1234567890ABCDEFu64); - let rsp = a.reg_read_64(RSP.into()); + let rsp = a.reg_read_64(RSP.into()).unwrap(); assert_eq!(rsp, 0x1000); assert_eq!(a.mem_read_64(rsp).unwrap(), 0x1234567890ABCDEFu64); @@ -127,7 +127,7 @@ mod test { write_reg_value!(q; a; RAX; 0x50f01be8d7485109u64); // Setup stack for address - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).expect("Failed to initialize memory"); }; |a: Axecutor| { diff --git a/src/instructions/ja.rs b/src/instructions/ja.rs index 12efe55..07037f8 100644 --- a/src/instructions/ja.rs +++ b/src/instructions/ja.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JA rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JA rel32_64", i.op0_kind()), } diff --git a/src/instructions/jae.rs b/src/instructions/jae.rs index c7cd843..b38a0a5 100644 --- a/src/instructions/jae.rs +++ b/src/instructions/jae.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JAE rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JAE rel32_64", i.op0_kind()), } diff --git a/src/instructions/jb.rs b/src/instructions/jb.rs index 7b3adcb..6adeb6b 100644 --- a/src/instructions/jb.rs +++ b/src/instructions/jb.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JB rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JB rel32_64", i.op0_kind()), } diff --git a/src/instructions/jbe.rs b/src/instructions/jbe.rs index f69a6dd..f4bf352 100644 --- a/src/instructions/jbe.rs +++ b/src/instructions/jbe.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JBE rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JBE rel32_64", i.op0_kind()), } diff --git a/src/instructions/je.rs b/src/instructions/je.rs index 09cfccf..a74db1c 100644 --- a/src/instructions/je.rs +++ b/src/instructions/je.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP.into(), offset); + self.reg_write_64(RIP.into(), offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JE rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP.into(), offset); + self.reg_write_64(RIP.into(), offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JE rel32_64", i.op0_kind()), } diff --git a/src/instructions/jecxz.rs b/src/instructions/jecxz.rs index 7eac8ee..0a05a33 100644 --- a/src/instructions/jecxz.rs +++ b/src/instructions/jecxz.rs @@ -46,11 +46,11 @@ impl Axecutor { fn instr_jecxz_rel8_64(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), Jecxz_rel8_64); - if self.reg_read_32(SupportedRegister::ECX) == 0 { + if self.reg_read_32(SupportedRegister::ECX)? == 0 { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(SupportedRegister::RIP, offset); + self.reg_write_64(SupportedRegister::RIP, offset)?; return Ok(()); } diff --git a/src/instructions/jg.rs b/src/instructions/jg.rs index d5764b8..1d9a1ff 100644 --- a/src/instructions/jg.rs +++ b/src/instructions/jg.rs @@ -54,7 +54,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JG rel8_64", i.op0_kind()), } @@ -93,7 +93,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP.into(), offset); + self.reg_write_64(RIP.into(), offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JG rel32_64", i.op0_kind()), } diff --git a/src/instructions/jge.rs b/src/instructions/jge.rs index dc179a4..7fbb455 100644 --- a/src/instructions/jge.rs +++ b/src/instructions/jge.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JGE rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JGE rel32_64", i.op0_kind()), } diff --git a/src/instructions/jl.rs b/src/instructions/jl.rs index fd3274a..f45da43 100644 --- a/src/instructions/jl.rs +++ b/src/instructions/jl.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JL rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JL rel32_64", i.op0_kind()), } diff --git a/src/instructions/jle.rs b/src/instructions/jle.rs index 974000c..ad6f5b6 100644 --- a/src/instructions/jle.rs +++ b/src/instructions/jle.rs @@ -54,7 +54,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JLE rel8_64", i.op0_kind()), } @@ -93,7 +93,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JLE rel32_64", i.op0_kind()), } diff --git a/src/instructions/jmp.rs b/src/instructions/jmp.rs index 69c050e..c2eaf8c 100644 --- a/src/instructions/jmp.rs +++ b/src/instructions/jmp.rs @@ -58,7 +58,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP.into(), offset); + self.reg_write_64(RIP.into(), offset)?; Ok(()) } _ => fatal_error!("Invalid op0_kind for JMP rel32: {:?}", i.op0_kind()), @@ -91,8 +91,8 @@ impl Axecutor { // imm8 sign-extended let offset = i.immediate8() as i8 as u64; - let rip = self.reg_read_64(RIP.into()) as i64 as u64; - self.reg_write_64(RIP.into(), rip + offset); + let rip = self.reg_read_64(RIP.into())? as i64 as u64; + self.reg_write_64(RIP.into(), rip + offset)?; Ok(()) } @@ -114,7 +114,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP.into(), offset); + self.reg_write_64(RIP.into(), offset)?; Ok(()) } _ => fatal_error!("Invalid op0_kind {:?} for JMP rel8", i.op0_kind()), diff --git a/src/instructions/jne.rs b/src/instructions/jne.rs index 211f490..db20856 100644 --- a/src/instructions/jne.rs +++ b/src/instructions/jne.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNE rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNE rel32_64", i.op0_kind()), } diff --git a/src/instructions/jno.rs b/src/instructions/jno.rs index de3bd26..cd007a7 100644 --- a/src/instructions/jno.rs +++ b/src/instructions/jno.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNO rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNO rel32_64", i.op0_kind()), } diff --git a/src/instructions/jnp.rs b/src/instructions/jnp.rs index 4e8aaaf..6965d49 100644 --- a/src/instructions/jnp.rs +++ b/src/instructions/jnp.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNP rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNP rel32_64", i.op0_kind()), } diff --git a/src/instructions/jns.rs b/src/instructions/jns.rs index a8d1dee..a3e9701 100644 --- a/src/instructions/jns.rs +++ b/src/instructions/jns.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNS rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JNS rel32_64", i.op0_kind()), } diff --git a/src/instructions/jo.rs b/src/instructions/jo.rs index bcf28d1..0db2ad3 100644 --- a/src/instructions/jo.rs +++ b/src/instructions/jo.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JO rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JO rel32_64", i.op0_kind()), } diff --git a/src/instructions/jp.rs b/src/instructions/jp.rs index 4a4436d..ace62d4 100644 --- a/src/instructions/jp.rs +++ b/src/instructions/jp.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JP rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JP rel32_64", i.op0_kind()), } diff --git a/src/instructions/jrcxz.rs b/src/instructions/jrcxz.rs index 7c53858..e2099dc 100644 --- a/src/instructions/jrcxz.rs +++ b/src/instructions/jrcxz.rs @@ -35,11 +35,11 @@ impl Axecutor { fn instr_jrcxz_rel8_64(&mut self, i: Instruction) -> Result<(), AxError> { debug_assert_eq!(i.code(), Jrcxz_rel8_64); - if self.reg_read_64(SupportedRegister::RCX) == 0 { + if self.reg_read_64(SupportedRegister::RCX)? == 0 { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(SupportedRegister::RIP, offset); + self.reg_write_64(SupportedRegister::RIP, offset)?; return Ok(()); } diff --git a/src/instructions/js.rs b/src/instructions/js.rs index f9b2e89..76cee24 100644 --- a/src/instructions/js.rs +++ b/src/instructions/js.rs @@ -52,7 +52,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JS rel8_64", i.op0_kind()), } @@ -89,7 +89,7 @@ impl Axecutor { match i.op0_kind() { OpKind::NearBranch64 => { let offset = i.near_branch64() as i64 as u64; - self.reg_write_64(RIP, offset); + self.reg_write_64(RIP, offset)?; } _ => fatal_error!("Invalid op0_kind {:?} for JS rel32_64", i.op0_kind()), } diff --git a/src/instructions/lea.rs b/src/instructions/lea.rs index 1ab9ec0..e4a881a 100644 --- a/src/instructions/lea.rs +++ b/src/instructions/lea.rs @@ -35,7 +35,7 @@ impl Axecutor { let dest = dest.into(); - self.reg_write_16(dest, src_addr as u16 as u64); + self.reg_write_16(dest, src_addr as u16 as u64)?; Ok(()) } @@ -54,7 +54,7 @@ impl Axecutor { let dest = dest.into(); - self.reg_write_32(dest, src_addr as u32 as u64); + self.reg_write_32(dest, src_addr as u32 as u64)?; Ok(()) } @@ -73,7 +73,7 @@ impl Axecutor { let dest = dest.into(); - self.reg_write_64(dest, src_addr as u64); + self.reg_write_64(dest, src_addr as u64)?; Ok(()) } } diff --git a/src/instructions/macros.rs b/src/instructions/macros.rs index a9f817d..8b6958a 100644 --- a/src/instructions/macros.rs +++ b/src/instructions/macros.rs @@ -16,7 +16,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_8(src.into()); + let src_val = self.reg_read_8(src.into())?; match dest { Operand::Memory(m) => { @@ -30,12 +30,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_8(r); + let dest_val = self.reg_read_8(r)?; let (result, flags) = op(dest_val as u8, src_val as u8); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u8(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(r, result as u64); + self.reg_write_8(r, result as u64)?; } Ok(()) } @@ -57,7 +57,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_16(src.into()); + let src_val = self.reg_read_16(src.into())?; match dest { Operand::Memory(m) => { @@ -71,12 +71,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_16(r); + let dest_val = self.reg_read_16(r)?; let (result, flags) = op(dest_val as u16, src_val as u16); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -98,7 +98,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_32(src.into()); + let src_val = self.reg_read_32(src.into())?; match dest { Operand::Memory(m) => { @@ -112,12 +112,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_32(r); + let dest_val = self.reg_read_32(r)?; let (result, flags) = op(dest_val as u32, src_val as u32); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -139,7 +139,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_64(src.into()); + let src_val = self.reg_read_64(src.into())?; match dest { Operand::Memory(m) => { @@ -153,12 +153,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_64(r); + let dest_val = self.reg_read_64(r)?; let (result, flags) = op(dest_val, src_val); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } @@ -180,7 +180,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_8(src.into()); + let src_val = self.reg_read_8(src.into())?; match dest { Operand::Memory(m) => { @@ -194,12 +194,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_16(r); + let dest_val = self.reg_read_16(r)?; let (result, flags) = op(dest_val as u16, src_val as u8); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -221,7 +221,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_8(src.into()); + let src_val = self.reg_read_8(src.into())?; match dest { Operand::Memory(m) => { @@ -235,12 +235,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_32(r); + let dest_val = self.reg_read_32(r)?; let (result, flags) = op(dest_val as u32, src_val as u8); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -262,7 +262,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_8(src.into()); + let src_val = self.reg_read_8(src.into())?; match dest { Operand::Memory(m) => { @@ -276,12 +276,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_64(r); + let dest_val = self.reg_read_64(r)?; let (result, flags) = op(dest_val, src_val as u8); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } @@ -303,7 +303,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_8(src.into()); + let src_val = self.reg_read_8(src.into())?; match dest { Operand::Memory(m) => { @@ -316,11 +316,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_8(r); + let dest_val = self.reg_read_8(r)?; let result = op(dest_val as u8, src_val as u8); self.set_flags_u8(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(r, result as u64); + self.reg_write_8(r, result as u64)?; } Ok(()) } @@ -342,7 +342,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_16(src.into()); + let src_val = self.reg_read_16(src.into())?; match dest { Operand::Memory(m) => { @@ -355,11 +355,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_16(r); + let dest_val = self.reg_read_16(r)?; let result = op(dest_val as u16, src_val as u16); self.set_flags_u16(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -381,7 +381,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_32(src.into()); + let src_val = self.reg_read_32(src.into())?; match dest { Operand::Memory(m) => { @@ -394,11 +394,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_32(r); + let dest_val = self.reg_read_32(r)?; let result = op(dest_val as u32, src_val as u32); self.set_flags_u32(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -420,7 +420,7 @@ impl Axecutor { flags_to_clear: u64, ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; - let src_val = self.reg_read_64(src.into()); + let src_val = self.reg_read_64(src.into())?; match dest { Operand::Memory(m) => { @@ -433,11 +433,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_64(r); + let dest_val = self.reg_read_64(r)?; let result = op(dest_val, src_val); self.set_flags_u64(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } @@ -512,7 +512,7 @@ impl Axecutor { ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; let dest_reg: SupportedRegister = dest.into(); - let dest_val = self.reg_read_8(dest_reg); + let dest_val = self.reg_read_8(dest_reg)?; match src { Operand::Memory(m) => { @@ -521,17 +521,17 @@ impl Axecutor { debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u8(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(dest_reg, result as u64); + self.reg_write_8(dest_reg, result as u64)?; } Ok(()) } Operand::Register(r) => { - let src_val = self.reg_read_8(r); + let src_val = self.reg_read_8(r)?; let (result, flags) = op(dest_val as u8, src_val as u8); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u8(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(dest_reg, result as u64); + self.reg_write_8(dest_reg, result as u64)?; } Ok(()) } @@ -552,7 +552,7 @@ impl Axecutor { ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; let dest_reg: SupportedRegister = dest.into(); - let dest_val = self.reg_read_16(dest_reg); + let dest_val = self.reg_read_16(dest_reg)?; match src { Operand::Memory(m) => { @@ -561,17 +561,17 @@ impl Axecutor { debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(dest_reg, result as u64); + self.reg_write_16(dest_reg, result as u64)?; } Ok(()) } Operand::Register(r) => { - let src_val = self.reg_read_16(r); + let src_val = self.reg_read_16(r)?; let (result, flags) = op(dest_val as u16, src_val as u16); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(dest_reg, result as u64); + self.reg_write_16(dest_reg, result as u64)?; } Ok(()) } @@ -592,7 +592,7 @@ impl Axecutor { ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; let dest_reg: SupportedRegister = dest.into(); - let dest_val = self.reg_read_32(dest_reg); + let dest_val = self.reg_read_32(dest_reg)?; match src { Operand::Memory(m) => { @@ -601,17 +601,17 @@ impl Axecutor { debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(dest_reg, result as u64); + self.reg_write_32(dest_reg, result as u64)?; } Ok(()) } Operand::Register(r) => { - let src_val = self.reg_read_32(r); + let src_val = self.reg_read_32(r)?; let (result, flags) = op(dest_val as u32, src_val as u32); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(dest_reg, result as u64); + self.reg_write_32(dest_reg, result as u64)?; } Ok(()) } @@ -632,7 +632,7 @@ impl Axecutor { ) -> Result<(), AxError> { let (dest, src) = self.instruction_operands_2(i)?; let dest_reg: SupportedRegister = dest.into(); - let dest_val = self.reg_read_64(dest_reg); + let dest_val = self.reg_read_64(dest_reg)?; match src { Operand::Memory(m) => { @@ -641,17 +641,17 @@ impl Axecutor { debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(dest_reg, result); + self.reg_write_64(dest_reg, result)?; } Ok(()) } Operand::Register(r) => { - let src_val = self.reg_read_64(r); + let src_val = self.reg_read_64(r)?; let (result, flags) = op(dest_val, src_val); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(dest_reg, result); + self.reg_write_64(dest_reg, result)?; } Ok(()) } @@ -673,7 +673,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -682,11 +682,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_8(dest); + let dest_val = self.reg_read_8(dest)?; let result = op(dest_val as u8, src_val as u8); self.set_flags_u8(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(dest, result as u64); + self.reg_write_8(dest, result as u64)?; } Ok(()) } @@ -701,7 +701,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -710,11 +710,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_16(dest); + let dest_val = self.reg_read_16(dest)?; let result = op(dest_val as u16, src_val as u16); self.set_flags_u16(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(dest, result as u64); + self.reg_write_16(dest, result as u64)?; } Ok(()) } @@ -729,7 +729,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -738,11 +738,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_32(dest); + let dest_val = self.reg_read_32(dest)?; let result = op(dest_val as u32, src_val as u32); self.set_flags_u32(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(dest, result as u64); + self.reg_write_32(dest, result as u64)?; } Ok(()) } @@ -757,7 +757,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -766,11 +766,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_64(dest); + let dest_val = self.reg_read_64(dest)?; let result = op(dest_val, src_val); self.set_flags_u64(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(dest, result); + self.reg_write_64(dest, result)?; } Ok(()) } @@ -785,7 +785,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -794,11 +794,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_64(dest); + let dest_val = self.reg_read_64(dest)?; let result = op(dest_val, src_val as u32); self.set_flags_u64(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(dest, result); + self.reg_write_64(dest, result)?; } Ok(()) } @@ -813,7 +813,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -822,11 +822,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_32(dest); + let dest_val = self.reg_read_32(dest)?; let result = op(dest_val as u32, src_val as u16); self.set_flags_u32(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(dest, result as u64); + self.reg_write_32(dest, result as u64)?; } Ok(()) } @@ -841,7 +841,7 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, _ => fatal_error!( "Invalid source operand {:?} for {:?} instruction", dest, @@ -850,11 +850,11 @@ impl Axecutor { }; let dest = dest.into(); - let dest_val = self.reg_read_64(dest); + let dest_val = self.reg_read_64(dest)?; let result = op(dest_val, src_val as u16); self.set_flags_u64(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(dest, result); + self.reg_write_64(dest, result)?; } Ok(()) } @@ -948,12 +948,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_8(r); + let dest_val = self.reg_read_8(r)?; let (result, flags) = op(dest_val as u8, src_val); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u8(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(r, result as u64); + self.reg_write_8(r, result as u64)?; } Ok(()) } @@ -1002,12 +1002,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_16(r); + let dest_val = self.reg_read_16(r)?; let (result, flags) = op(dest_val as u16, src_val); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -1056,12 +1056,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_32(r); + let dest_val = self.reg_read_32(r)?; let (result, flags) = op(dest_val as u32, src_val); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -1110,12 +1110,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_64(r); + let dest_val = self.reg_read_64(r)?; let (result, flags) = op(dest_val, src_val); debug_assert!(flags & NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } @@ -1163,11 +1163,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_8(r); + let dest_val = self.reg_read_8(r)?; let result = op(dest_val as u8, src_val); self.set_flags_u8(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_8(r, result as u64); + self.reg_write_8(r, result as u64)?; } Ok(()) } @@ -1215,11 +1215,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_16(r); + let dest_val = self.reg_read_16(r)?; let result = op(dest_val as u16, src_val); self.set_flags_u16(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -1267,11 +1267,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_32(r); + let dest_val = self.reg_read_32(r)?; let result = op(dest_val as u32, src_val); self.set_flags_u32(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -1319,11 +1319,11 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_64(r); + let dest_val = self.reg_read_64(r)?; let result = op(dest_val, src_val); self.set_flags_u64(flags_to_set, flags_to_clear, result); if (flags_to_set & NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } @@ -1372,12 +1372,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_16(r); + let dest_val = self.reg_read_16(r)?; let (result, flags) = op(dest_val as u16, src_val); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -1426,12 +1426,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_32(r); + let dest_val = self.reg_read_32(r)?; let (result, flags) = op(dest_val as u32, src_val); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -1480,12 +1480,12 @@ impl Axecutor { Ok(()) } Operand::Register(r) => { - let dest_val = self.reg_read_64(r); + let dest_val = self.reg_read_64(r)?; let (result, flags) = op(dest_val, src_val); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } @@ -1571,12 +1571,12 @@ impl Axecutor { let dest = self.instruction_operand(i, 0)?; match dest { Operand::Register(r) => { - let src_val = self.reg_read_8(r); + let src_val = self.reg_read_8(r)?; let (result, flags) = op(src_val as u8); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u8(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_8(r, result as u64); + self.reg_write_8(r, result as u64)?; } Ok(()) } @@ -1610,12 +1610,12 @@ impl Axecutor { let dest = self.instruction_operand(i, 0)?; match dest { Operand::Register(r) => { - let src_val = self.reg_read_16(r); + let src_val = self.reg_read_16(r)?; let (result, flags) = op(src_val as u16); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u16(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_16(r, result as u64); + self.reg_write_16(r, result as u64)?; } Ok(()) } @@ -1649,12 +1649,12 @@ impl Axecutor { let dest = self.instruction_operand(i, 0)?; match dest { Operand::Register(r) => { - let src_val = self.reg_read_32(r); + let src_val = self.reg_read_32(r)?; let (result, flags) = op(src_val as u32); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u32(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_32(r, result as u64); + self.reg_write_32(r, result as u64)?; } Ok(()) } @@ -1688,12 +1688,12 @@ impl Axecutor { let dest = self.instruction_operand(i, 0)?; match dest { Operand::Register(r) => { - let src_val = self.reg_read_64(r); + let src_val = self.reg_read_64(r)?; let (result, flags) = op(src_val); debug_assert!(flags & crate::instructions::macros::NO_WRITEBACK == 0, "NO_WRITEBACK flag must not be returned by operation lambda, set it as $flags_to_set"); self.set_flags_u64(flags_to_set | flags, flags_to_clear, result); if (flags_to_set & crate::instructions::macros::NO_WRITEBACK) == 0 { - self.reg_write_64(r, result); + self.reg_write_64(r, result)?; } Ok(()) } diff --git a/src/instructions/memory.rs b/src/instructions/memory.rs index da01095..bc601c7 100644 --- a/src/instructions/memory.rs +++ b/src/instructions/memory.rs @@ -60,6 +60,7 @@ impl MemoryArea { impl Axecutor { // TODO: Currently cannot read consecutive sections of memory #[must_use] + /// Reads `length` bytes from memory at `address` pub fn mem_read_bytes(&self, address: u64, length: u64) -> Result, AxError> { debug_log!( "Calling Axecutor::mem_read_bytes, address={:#x}, length={}", @@ -137,24 +138,28 @@ impl Axecutor { ))) } + /// Reads a 64-bit value from memory at `address` pub fn mem_read_64(&self, address: u64) -> Result { let bytes = self.mem_read_bytes(address, 8)?; Ok(u64::from_le_bytes(bytes.try_into().unwrap())) } + /// Reads a 32-bit value from memory at `address` pub fn mem_read_32(&self, address: u64) -> Result { let bytes = self.mem_read_bytes(address, 4)?; Ok(u32::from_le_bytes(bytes.try_into().unwrap()) as u64) } + /// Reads a 16-bit value from memory at `address` pub fn mem_read_16(&self, address: u64) -> Result { let bytes = self.mem_read_bytes(address, 2)?; Ok(u16::from_le_bytes(bytes.try_into().unwrap()) as u64) } + /// Reads an 8-bit value from memory at `address` pub fn mem_read_8(&self, address: u64) -> Result { let bytes = self.mem_read_bytes(address, 1)?; @@ -164,6 +169,7 @@ impl Axecutor { // TODO: Currently cannot write consecutive sections of memory // It would also make sense to give better error messages, e.g. if the write start address is within an area, but the data is too long #[must_use] + /// Writes bytes of `data` to memory at `address` pub fn mem_write_bytes(&mut self, address: u64, data: &[u8]) -> Result<(), AxError> { debug_log!( "Calling Axecutor::mem_write_bytes, address={:#x}, data_len={:?}", @@ -221,10 +227,12 @@ impl Axecutor { ))) } + /// Writes a 64-bit value to memory at `address` pub fn mem_write_64(&mut self, address: u64, data: u64) -> Result<(), AxError> { self.mem_write_bytes(address, &data.to_le_bytes()) } + /// Writes a 32-bit value to memory at `address` pub fn mem_write_32(&mut self, address: u64, data: u64) -> Result<(), AxError> { crate::assert_fatal!( data <= u32::MAX as u64, @@ -235,6 +243,7 @@ impl Axecutor { self.mem_write_bytes(address, &(data as u32).to_le_bytes()) } + /// Writes a 16-bit value to memory at `address` pub fn mem_write_16(&mut self, address: u64, data: u64) -> Result<(), AxError> { crate::assert_fatal!( data <= u16::MAX as u64, @@ -244,6 +253,7 @@ impl Axecutor { self.mem_write_bytes(address, &(data as u16).to_le_bytes()) } + /// Writes an 8-bit value to memory at `address` pub fn mem_write_8(&mut self, address: u64, data: u64) -> Result<(), AxError> { crate::assert_fatal!( data <= u8::MAX as u64, @@ -254,6 +264,9 @@ impl Axecutor { self.mem_write_bytes(address, &[data as u8]) } + /// Resize the already existing section of memory with start address `start_addr` to `new_size` + /// It is not possible the reduce the size of a section. + /// The code section cannot be resized. pub fn resize_section(&mut self, start_addr: u64, new_size: u64) -> Result<(), AxError> { debug_log!( "Calling Axecutor::resize_section, start_addr={:#x}, new_size={}", @@ -305,6 +318,8 @@ impl Axecutor { } #[must_use] + /// Initialize a memory area with the given data and name. + /// The name is used for logging and debugging purposes. pub fn mem_init_area_named( &mut self, start: u64, @@ -354,13 +369,16 @@ impl Axecutor { Ok(()) } + /// Initialize a memory area with the given data. pub fn mem_init_area(&mut self, start: u64, data: Vec) -> Result<(), AxError> { self.mem_init_area_named(start, data, None) } + /// Initialize a memory area with the given length. pub fn mem_init_zero(&mut self, start: u64, length: u64) -> Result<(), AxError> { self.mem_init_area_named(start, vec![0; length as usize], None) } + /// Initialize a memory area with the given length and name. pub fn mem_init_zero_named( &mut self, start: u64, @@ -370,6 +388,8 @@ impl Axecutor { self.mem_init_area_named(start, vec![0; length as usize], Some(name)) } + /// Initialize a memory area of the given length at a random address. + /// The start address is returned. pub fn init_zero_anywhere(&mut self, length: u64) -> Result { let mut start: u64 = 0x1000; @@ -389,6 +409,8 @@ impl Axecutor { Ok(start) } + /// Initialize a memory area with the given data at a random address. + /// The start address is returned. pub fn init_anywhere(&mut self, data: Vec) -> Result { let mut start: u64 = 0x1000; @@ -408,6 +430,7 @@ impl Axecutor { Ok(start) } + /// Initializes the stack at a random location with the given length. pub fn init_stack(&mut self, length: u64) -> Result { let mut stack_start: u64 = 0x1000; @@ -428,12 +451,14 @@ impl Axecutor { } let initial_rsp = stack_start + length - 8; - self.reg_write_64(SupportedRegister::RSP, initial_rsp); + self.reg_write_64(SupportedRegister::RSP, initial_rsp)?; self.stack_top = stack_start + length; Ok(stack_start) } + /// Initializes the stack with the given length, command-line arguments and environment variables according to the System V ABI. + /// This is useful for emulating ELF binaries. pub fn init_stack_program_start( &mut self, length: u64, @@ -509,7 +534,7 @@ impl Axecutor { stack_top -= 8; } - self.reg_write_64(SupportedRegister::RSP, stack_top); + self.reg_write_64(SupportedRegister::RSP, stack_top)?; debug_log!( "Initialized stack, stack_top={:#x}, self.stack_top={:#x}", diff --git a/src/instructions/mul.rs b/src/instructions/mul.rs index 8c2e6e7..4161b70 100644 --- a/src/instructions/mul.rs +++ b/src/instructions/mul.rs @@ -31,18 +31,18 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Mul_rm8", op), }; - let dst_val = self.reg_read_8(AL); + let dst_val = self.reg_read_8(AL)?; let result = (dst_val as u16).wrapping_mul(src_val as u16); let upper = (result >> 8) as u8; - self.reg_write_16(AX, result as u64); + self.reg_write_16(AX, result as u64)?; self.set_flags_u8( if upper == 0 { 0 } else { FLAG_CF | FLAG_OF }, @@ -62,19 +62,19 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Mul_rm16", op), }; - let dst_val = self.reg_read_16(AX); + let dst_val = self.reg_read_16(AX)?; let result = (dst_val as u32).wrapping_mul(src_val as u32); let upper = (result >> 16) as u16; - self.reg_write_16(AX, result as u16 as u64); - self.reg_write_16(DX, upper as u64); + self.reg_write_16(AX, result as u16 as u64)?; + self.reg_write_16(DX, upper as u64)?; self.set_flags_u8( if upper == 0 { 0 } else { FLAG_CF | FLAG_OF }, @@ -94,19 +94,19 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Mul_rm32", op), }; - let dst_val = self.reg_read_32(EAX); + let dst_val = self.reg_read_32(EAX)?; let result = (dst_val as u64).wrapping_mul(src_val as u64); let upper = (result >> 32) as u32; - self.reg_write_32(EAX, result as u32 as u64); - self.reg_write_32(EDX, upper as u64); + self.reg_write_32(EAX, result as u32 as u64)?; + self.reg_write_32(EDX, upper as u64)?; self.set_flags_u8( if upper == 0 { 0 } else { FLAG_CF | FLAG_OF }, @@ -126,19 +126,19 @@ impl Axecutor { let op = self.instruction_operand(i, 0)?; let src_val = match op { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for Mul_rm64", op), }; - let dst_val = self.reg_read_64(RAX); + let dst_val = self.reg_read_64(RAX)?; let result = (dst_val as u128).wrapping_mul(src_val as u128); let upper = (result >> 64) as u64; - self.reg_write_64(RAX, result as u64); - self.reg_write_64(RDX, upper); + self.reg_write_64(RAX, result as u64)?; + self.reg_write_64(RDX, upper)?; self.set_flags_u8( if upper == 0 { 0 } else { FLAG_CF | FLAG_OF }, diff --git a/src/instructions/operand.rs b/src/instructions/operand.rs index ed2b171..a121804 100644 --- a/src/instructions/operand.rs +++ b/src/instructions/operand.rs @@ -131,10 +131,15 @@ impl Axecutor { } = o; let mut addr: u64 = 0; if let Some(base) = base { - addr += self.reg_read_64(base) as u64; + addr += self + .reg_read_64(base) + .expect("reading memory operand base register") as u64; } if let Some(index) = index { - addr += self.reg_read_64(index) * (scale as u64); + addr += self + .reg_read_64(index) + .expect("reading memory operand index register") + * (scale as u64); } // This overflow is explicitly allowed, as x86-64 encodes negative values as signed integers @@ -339,7 +344,7 @@ mod tests { ]; |a: &mut Axecutor| { use iced_x86::Register::*; - a.reg_write_64(RSP.into(), 0x1000) + a.reg_write_64(RSP.into(), 0x1000).unwrap(); }; vec![ 0x1000 @@ -361,7 +366,7 @@ mod tests { ]; |a: &mut Axecutor| { use iced_x86::Register::*; - a.reg_write_64(RSP.into(), 0x1000) + a.reg_write_64(RSP.into(), 0x1000).unwrap(); }; vec![ 0x1000 @@ -383,7 +388,7 @@ mod tests { ]; |a: &mut Axecutor| { use iced_x86::Register::*; - a.reg_write_64(RSP.into(), 0x1000) + a.reg_write_64(RSP.into(), 0x1000).unwrap(); }; vec![ 0x1001 @@ -406,7 +411,7 @@ mod tests { ]; |a: &mut Axecutor| { use iced_x86::Register::*; - a.reg_write_64(RSP.into(), 0x1000) + a.reg_write_64(RSP.into(), 0x1000).unwrap(); }; vec![ 0x0fff @@ -428,8 +433,8 @@ mod tests { ]; |a: &mut Axecutor| { use iced_x86::Register::*; - a.reg_write_64(R11.into(), 0x8001); - a.reg_write_64(RCX.into(), 5); + a.reg_write_64(R11.into(), 0x8001).unwrap(); + a.reg_write_64(RCX.into(), 5).unwrap(); }; vec![ 0x8015 diff --git a/src/instructions/pop.rs b/src/instructions/pop.rs index c08e25d..8c9b04d 100644 --- a/src/instructions/pop.rs +++ b/src/instructions/pop.rs @@ -32,12 +32,12 @@ impl Axecutor { debug_assert_eq!(i.code(), Pop_r16); let reg: SupportedRegister = i.op0_register().into(); - let rsp = self.reg_read_64(Register::RSP.into()) + 2; + let rsp = self.reg_read_64(Register::RSP.into())? + 2; let value = self.mem_read_16(rsp)?; - self.reg_write_16(reg, value as u64); + self.reg_write_16(reg, value as u64)?; - self.reg_write_64(Register::RSP.into(), rsp); + self.reg_write_64(Register::RSP.into(), rsp)?; Ok(()) } @@ -58,12 +58,12 @@ impl Axecutor { debug_assert_eq!(i.code(), Pop_r64); let reg: SupportedRegister = i.op0_register().into(); - let rsp = self.reg_read_64(Register::RSP.into()) + 8; + let rsp = self.reg_read_64(Register::RSP.into())? + 8; let value = self.mem_read_64(rsp)?; - self.reg_write_64(reg, value); + self.reg_write_64(reg, value)?; - self.reg_write_64(Register::RSP.into(), rsp); + self.reg_write_64(Register::RSP.into(), rsp)?; Ok(()) } diff --git a/src/instructions/push.rs b/src/instructions/push.rs index 45e235b..bbfd9f1 100644 --- a/src/instructions/push.rs +++ b/src/instructions/push.rs @@ -37,11 +37,11 @@ impl Axecutor { let reg: SupportedRegister = i.op0_register().into(); - let value = self.reg_read_16(reg); - let rsp = self.reg_read_64(Register::RSP.into()); + let value = self.reg_read_16(reg)?; + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_16(rsp, value)?; - self.reg_write_64(Register::RSP.into(), rsp - 2); + self.reg_write_64(Register::RSP.into(), rsp - 2)?; Ok(()) } @@ -63,11 +63,11 @@ impl Axecutor { let reg: SupportedRegister = i.op0_register().into(); - let value = self.reg_read_64(reg); - let rsp = self.reg_read_64(Register::RSP.into()); + let value = self.reg_read_64(reg)?; + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_64(rsp, value)?; - self.reg_write_64(Register::RSP.into(), rsp - 8); + self.reg_write_64(Register::RSP.into(), rsp - 8)?; Ok(()) } @@ -79,10 +79,10 @@ impl Axecutor { debug_assert_eq!(i.code(), Push_imm16); let value = i.immediate16() as u64; - let rsp = self.reg_read_64(Register::RSP.into()); + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_16(rsp, value)?; - self.reg_write_64(Register::RSP.into(), rsp - 2); + self.reg_write_64(Register::RSP.into(), rsp - 2)?; Ok(()) } @@ -94,15 +94,15 @@ impl Axecutor { debug_assert_eq!(i.code(), Push_rm16); let src = match self.instruction_operand(i, 0)? { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid operand {:?} for PUSH r/m16", i.op0_kind()), }; - let rsp = self.reg_read_64(Register::RSP.into()); + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_16(rsp, src)?; - self.reg_write_64(Register::RSP.into(), rsp - 2); + self.reg_write_64(Register::RSP.into(), rsp - 2)?; Ok(()) } @@ -136,24 +136,24 @@ impl Axecutor { // TODO: not sure if 16 and 32-bit are required here, but AMD manual says so OpKind::Immediate8to16 => { let value = i.immediate8to16(); - let rsp = self.reg_read_64(Register::RSP.into()); + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_16(rsp, value as u16 as u64)?; - self.reg_write_64(Register::RSP.into(), rsp - 2); + self.reg_write_64(Register::RSP.into(), rsp - 2)?; } OpKind::Immediate8to32 => { let value = i.immediate8to32(); - let rsp = self.reg_read_64(Register::RSP.into()); + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_32(rsp, value as u32 as u64)?; - self.reg_write_64(Register::RSP.into(), rsp - 4); + self.reg_write_64(Register::RSP.into(), rsp - 4)?; } OpKind::Immediate8to64 => { let value = i.immediate8to64(); - let rsp = self.reg_read_64(Register::RSP.into()); + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_64(rsp, value as u64)?; - self.reg_write_64(Register::RSP.into(), rsp - 8); + self.reg_write_64(Register::RSP.into(), rsp - 8)?; } _ => fatal_error!("Invalid operand {:?} for PUSH imm8", i.op0_kind()), } @@ -172,10 +172,10 @@ impl Axecutor { OpKind::Immediate32to64 => { // Sign-extend the 32-bit immediate to 64-bit let value = i.immediate32to64() as u64; - let rsp = self.reg_read_64(Register::RSP.into()); + let rsp = self.reg_read_64(Register::RSP.into())?; self.mem_write_64(rsp, value)?; - self.reg_write_64(Register::RSP.into(), rsp - 8); + self.reg_write_64(Register::RSP.into(), rsp - 8)?; } _ => fatal_error!("Invalid operand {:?} for PUSH imm64", i.op0_kind()), } @@ -196,13 +196,13 @@ mod tests { write_reg_value!(w; a; AX; 0x1234); // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 2).unwrap(); }; |a: Axecutor| { assert_reg_value!(w; a; AX; 0x1234); - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-2); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-2); assert_eq!(a.mem_read_16(0x1000).unwrap(), 0x1234); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) @@ -214,13 +214,13 @@ mod tests { write_reg_value!(q; a; RBX; 0x1234567890ABCDEF); // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { assert_reg_value!(q; a; RBX; 0x1234567890ABCDEFu64); - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-8); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-8); assert_eq!(a.mem_read_64(0x1000).unwrap(), 0x1234567890ABCDEF); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) @@ -230,11 +230,11 @@ mod tests { ax_test![push_0x1234; 0x68, 0x34, 0x12, 0x0, 0x0; |a: &mut Axecutor| { // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-8); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-8); assert_eq!(a.mem_read_64(0x1000).unwrap(), 0x1234); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) @@ -244,11 +244,11 @@ mod tests { ax_test![push_0xff; 0x6a, 0xff; |a: &mut Axecutor| { // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-8); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-8); assert_eq!(a.mem_read_64(0x1000).unwrap(), 0xffffffffffffffff); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) @@ -258,11 +258,11 @@ mod tests { ax_test![push_0x7f; 0x6a, 0x7f; |a: &mut Axecutor| { // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-8); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-8); assert_eq!(a.mem_read_64(0x1000).unwrap(), 0x7f); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) @@ -272,11 +272,11 @@ mod tests { ax_test![push_0xfffffff; 0x68, 0xff, 0xff, 0xff, 0x0; |a: &mut Axecutor| { // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-8); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-8); assert_eq!(a.mem_read_64(0x1000).unwrap(), 0xffffff); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) @@ -286,11 +286,11 @@ mod tests { ax_test![push_0x7fffffff; 0x68, 0xff, 0xff, 0xff, 0x7f; |a: &mut Axecutor| { // Setup stack - a.reg_write_64(RSP.into(), 0x1000); + a.reg_write_64(RSP.into(), 0x1000).unwrap(); a.mem_init_zero(0x1000, 8).unwrap(); }; |a: Axecutor| { - assert_eq!(a.reg_read_64(RSP.into()), 0x1000-8); + assert_eq!(a.reg_read_64(RSP.into()).unwrap(), 0x1000-8); assert_eq!(a.mem_read_64(0x1000).unwrap(), 0x7fffffff); }; (0; FLAG_CF | FLAG_PF | FLAG_ZF | FLAG_SF | FLAG_OF) diff --git a/src/instructions/registers.rs b/src/instructions/registers.rs index 57ed9c6..18a0a6a 100644 --- a/src/instructions/registers.rs +++ b/src/instructions/registers.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use wasm_bindgen::prelude::wasm_bindgen; -use crate::debug_log; +use crate::{assert_fatal, debug_log, instructions::errors::AxError}; use super::axecutor::Axecutor; @@ -119,6 +119,7 @@ pub(crate) fn randomized_register_set(rip_value: u64) -> HashMap Result<(), AxError> { + assert_fatal!( value <= 0xFF, "reg_write_8: value {:x} is too large to fit in 8 bits", value ); let r: Register = reg.into(); - assert!(r.is_gpr8(), "{:?} is not a valid 8-bit register", r); + assert_fatal!(r.is_gpr8(), "{:?} is not a valid 8-bit register", r); // Map 8-bit register to 64-bit register that it is part of let qword_register = REGISTER_TO_QWORD.get(®).unwrap(); @@ -396,17 +399,21 @@ impl Axecutor { result_value, reg_value ); + + Ok(()) } - pub fn reg_write_16(&mut self, reg: SupportedRegister, value: u64) { - assert!( + #[must_use] + /// Writes a 16-bit value to a 16-bit wide register. Out-of-range values or invalid registers lead to exceptions. + pub fn reg_write_16(&mut self, reg: SupportedRegister, value: u64) -> Result<(), AxError> { + assert_fatal!( value <= 0xFFFF, "reg_write_16: value {:x} is too large to fit in 16 bits", value ); let r: Register = reg.into(); - assert!(r.is_gpr16(), "{:?} is not a valid 16-bit register", r); + assert_fatal!(r.is_gpr16(), "{:?} is not a valid 16-bit register", r); // Map 16-bit register to 64-bit register that it is part of let qword_register = REGISTER_TO_QWORD.get(®).unwrap(); @@ -424,17 +431,22 @@ impl Axecutor { result_value, reg_value ); + + Ok(()) } - pub fn reg_write_32(&mut self, reg: SupportedRegister, value: u64) { - assert!( + #[must_use] + /// Writes a 32-bit value to a 32-bit wide register. Out-of-range values or invalid registers lead to exceptions. + /// Note that on x86-64 writes to 32-bit registers clear the upper 32 bits of the corresponding 64-bit register. + pub fn reg_write_32(&mut self, reg: SupportedRegister, value: u64) -> Result<(), AxError> { + assert_fatal!( value <= 0xFFFF_FFFF, "reg_write_32: value {:x} is too large to fit in 32 bits", value ); let r: Register = reg.into(); - assert!(r.is_gpr32(), "{:?} is not a valid 32-bit register", r); + assert_fatal!(r.is_gpr32(), "{:?} is not a valid 32-bit register", r); // Map 32-bit register to 64-bit register that it is part of let qword_register = REGISTER_TO_QWORD.get(®).unwrap(); @@ -455,11 +467,15 @@ impl Axecutor { Option::None => "".to_string(), } ); + + Ok(()) } - pub fn reg_write_64(&mut self, reg: SupportedRegister, value: u64) { + #[must_use] + /// Writes a 64-bit value to a 64-bit wide register. Out-of-range values or invalid registers lead to exceptions. + pub fn reg_write_64(&mut self, reg: SupportedRegister, value: u64) -> Result<(), AxError> { let r: Register = reg.into(); - assert!( + assert_fatal!( r.is_gpr64() || r.is_ip(), "{:?} is not a valid 64-bit register", r @@ -477,11 +493,15 @@ impl Axecutor { Option::None => "".to_string(), } ); + + Ok(()) } - pub fn reg_read_8(&self, reg: SupportedRegister) -> u64 { + #[must_use] + /// Reads an 8-bit value from a 8-bit wide register. Invalid registers lead to exceptions. + pub fn reg_read_8(&self, reg: SupportedRegister) -> Result { let r: Register = reg.into(); - assert!(r.is_gpr8(), "{:?} is not a valid 8-bit register", r); + assert_fatal!(r.is_gpr8(), "{:?} is not a valid 8-bit register", r); // Map 8-bit register to 64-bit register that it is part of let qword_register = REGISTER_TO_QWORD.get(®).unwrap(); @@ -498,12 +518,14 @@ impl Axecutor { debug_log!("Read value 0x{:x} from {:?}", result_value, reg); - return result_value as u64; + Ok(result_value as u64) } - pub fn reg_read_16(&self, reg: SupportedRegister) -> u64 { + #[must_use] + /// Reads a 16-bit value from a 16-bit wide register. Invalid registers lead to exceptions. + pub fn reg_read_16(&self, reg: SupportedRegister) -> Result { let r: Register = reg.into(); - assert!(r.is_gpr16(), "{:?} is not a valid 16-bit register", r); + assert_fatal!(r.is_gpr16(), "{:?} is not a valid 16-bit register", r); // Map 16-bit register to 64-bit register that it is part of let qword_register = REGISTER_TO_QWORD.get(®).unwrap(); @@ -514,12 +536,14 @@ impl Axecutor { debug_log!("Read value 0x{:x} from {:?}", result_value, reg); - return result_value; + Ok(result_value) } - pub fn reg_read_32(&self, reg: SupportedRegister) -> u64 { + #[must_use] + /// Reads a 32-bit value from a 32-bit wide register. Invalid registers lead to exceptions. + pub fn reg_read_32(&self, reg: SupportedRegister) -> Result { let r: Register = reg.into(); - assert!(r.is_gpr32(), "{:?} is not a valid 32-bit register", r); + assert_fatal!(r.is_gpr32(), "{:?} is not a valid 32-bit register", r); // Map 32-bit register to 64-bit register that it is part of let qword_register = REGISTER_TO_QWORD.get(®).unwrap(); @@ -530,12 +554,14 @@ impl Axecutor { debug_log!("Read value 0x{:x} from {:?}", result_value, reg); - return result_value; + Ok(result_value) } - pub fn reg_read_64(&self, reg: SupportedRegister) -> u64 { + #[must_use] + /// Reads a 64-bit value from a 64-bit wide register. Invalid registers lead to exceptions. + pub fn reg_read_64(&self, reg: SupportedRegister) -> Result { let r: Register = reg.into(); - assert!( + assert_fatal!( r.is_gpr64() || r.is_ip(), "{:?} is not a valid 64-bit register", r @@ -545,9 +571,10 @@ impl Axecutor { debug_log!("Read value 0x{:x} from {:?}", reg_value, reg); - return reg_value; + Ok(reg_value) } + /// Reads the value of the FS segment register. pub fn read_fs(&self) -> u64 { let value = self.state.fs; @@ -556,6 +583,7 @@ impl Axecutor { return value; } + /// Writes a value to the FS segment register. pub fn write_fs(&mut self, value: u64) { self.state.fs = value; diff --git a/src/instructions/ret.rs b/src/instructions/ret.rs index 65a095c..58a3cdd 100644 --- a/src/instructions/ret.rs +++ b/src/instructions/ret.rs @@ -10,13 +10,13 @@ use crate::{fatal_error, opcode_unimplemented}; macro_rules! pop_rip { ($self:ident) => {{ - let rsp = $self.reg_read_64(RSP) + 8; + let rsp = $self.reg_read_64(RSP)? + 8; if rsp == $self.stack_top { return Err(AxError::from("Cannot pop from empty stack").end_execution()); } let rip = $self.mem_read_64(rsp)?; - $self.reg_write_64(RIP, rip); - $self.reg_write_64(RSP, rsp); + $self.reg_write_64(RIP, rip)?; + $self.reg_write_64(RSP, rsp)?; }}; } @@ -163,7 +163,7 @@ mod tests { 50000; // 50000 bytes of 0x90 (nop) as padding 0x48, 0xc7, 0xc0, 0x32, 0x0, 0x0, 0x0, 0xe8, 0x9c, 0x3c, 0xff, 0xff, 0x90; // Lcall: mov rax, 50; call func; nop |a: &mut Axecutor| { - a.reg_write_64(RSP.into(), 0x8000); + a.reg_write_64(RSP.into(), 0x8000).unwrap(); a.mem_init_zero(0x8000, 8).expect("Failed to init memory"); }; |a: Axecutor| { diff --git a/src/instructions/test.rs b/src/instructions/test.rs index 1b2888b..924bb55 100644 --- a/src/instructions/test.rs +++ b/src/instructions/test.rs @@ -40,12 +40,12 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, _ => fatal_error!("Invalid source operand {:?} for TEST r/m8, r8", src), } as u8; let dest_val = match dest { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, _ => fatal_error!("Invalid destination operand {:?} for TEST r/m8, r8", dest), } as u8; @@ -66,12 +66,12 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, _ => fatal_error!("Invalid source operand {:?} for TEST r/m16, r16", src), } as u16; let dest_val = match dest { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!("Invalid destination operand {:?} for TEST r/m16, r16", dest), } as u16; @@ -92,12 +92,12 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, _ => fatal_error!("Invalid source operand {:?} for TEST r/m32, r32", src), } as u32; let dest_val = match dest { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!("Invalid destination operand {:?} for TEST r/m32, r32", dest), } as u32; @@ -118,12 +118,12 @@ impl Axecutor { let (dest, src) = self.instruction_operands_2(i)?; let src_val = match src { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, _ => fatal_error!("Invalid source operand {:?} for TEST r/m64, r64", src), }; let dest_val = match dest { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!("Invalid destination operand {:?} for TEST r/m64, r64", dest), }; @@ -187,7 +187,7 @@ impl Axecutor { }; let dest_val = match dest { - Operand::Register(r) => self.reg_read_8(r), + Operand::Register(r) => self.reg_read_8(r)?, Operand::Memory(m) => self.mem_read_8(self.mem_addr(m))?, _ => fatal_error!("Invalid destination operand {:?} for TEST r/m8, imm8", dest), } as u8; @@ -225,7 +225,7 @@ impl Axecutor { }; let dest_val = match dest { - Operand::Register(r) => self.reg_read_16(r), + Operand::Register(r) => self.reg_read_16(r)?, Operand::Memory(m) => self.mem_read_16(self.mem_addr(m))?, _ => fatal_error!( "Invalid destination operand {:?} for TEST r/m16, imm16", @@ -256,7 +256,7 @@ impl Axecutor { }; let dest_val = match dest { - Operand::Register(r) => self.reg_read_32(r), + Operand::Register(r) => self.reg_read_32(r)?, Operand::Memory(m) => self.mem_read_32(self.mem_addr(m))?, _ => fatal_error!( "Invalid destination operand {:?} for TEST r/m32, imm32", @@ -287,7 +287,7 @@ impl Axecutor { }; let dest_val = match dest { - Operand::Register(r) => self.reg_read_64(r), + Operand::Register(r) => self.reg_read_64(r)?, Operand::Memory(m) => self.mem_read_64(self.mem_addr(m))?, _ => fatal_error!( "Invalid destination operand {:?} for TEST r/m64, imm32", diff --git a/src/instructions/tests.rs b/src/instructions/tests.rs index 7b34679..c438f32 100644 --- a/src/instructions/tests.rs +++ b/src/instructions/tests.rs @@ -125,7 +125,7 @@ macro_rules! assert_reg_value { [b; $axecutor:expr; $reg:expr; $value:expr] => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr8(), "Register must be 8 bit wide"); - let val = $axecutor.reg_read_8(wrap) as u8; + let val = $axecutor.reg_read_8(wrap).expect("could not read 8-bit register") as u8; assert_eq!( &val, &$value, "expected register {:?} to have value {:?}, but got {}", @@ -135,7 +135,7 @@ macro_rules! assert_reg_value { [w; $axecutor:expr; $reg:expr; $value:expr] => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr16(), "Register must be 16 bit wide"); - let val = $axecutor.reg_read_16(wrap) as u16; + let val = $axecutor.reg_read_16(wrap).expect("could not read 16-bit register") as u16; assert_eq!( &val, &$value, "expected register {:?} to have value {:?}, but got {}", @@ -145,7 +145,7 @@ macro_rules! assert_reg_value { [d; $axecutor:expr; $reg:expr; $value:expr] => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr32(), "Register must be 32 bit wide"); - let val = $axecutor.reg_read_32(wrap) as u32; + let val = $axecutor.reg_read_32(wrap).expect("could not read 32-bit register") as u32; assert_eq!( &val, &$value, "expected register {:?} to have value {:?}, but got {}", @@ -155,7 +155,7 @@ macro_rules! assert_reg_value { [q; $axecutor:expr; $reg:expr; $value:expr] => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr64() || $reg.is_ip(), "Register must be 64 bit wide"); - let val = $axecutor.reg_read_64(wrap); + let val = $axecutor.reg_read_64(wrap).expect("could not read 64-bit register"); assert_eq!( &val, &$value, "expected register {:?} to have value {:?}, but got {}", @@ -170,22 +170,30 @@ macro_rules! write_reg_value { (b; $axecutor:expr; $reg:expr; $value:expr) => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr8(), "Register must be 8 bit wide"); - $axecutor.reg_write_8(wrap, $value as u64); + $axecutor + .reg_write_8(wrap, $value as u64) + .expect("could not write 8-bit register"); }; (w; $axecutor:expr; $reg:expr; $value:expr) => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr16(), "Register must be 16 bit wide"); - $axecutor.reg_write_16(wrap, $value as u64); + $axecutor + .reg_write_16(wrap, $value as u64) + .expect("could not write 16-bit register"); }; (d; $axecutor:expr; $reg:expr; $value:expr) => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr32(), "Register must be 32 bit wide"); - $axecutor.reg_write_32(wrap, $value as u64); + $axecutor + .reg_write_32(wrap, $value as u64) + .expect("could not write 32-bit register"); }; (q; $axecutor:expr; $reg:expr; $value:expr) => { let wrap = crate::instructions::registers::SupportedRegister::from($reg); assert!($reg.is_gpr64(), "Register must be 64 bit wide"); - $axecutor.reg_write_64(wrap, $value as u64); + $axecutor + .reg_write_64(wrap, $value as u64) + .expect("could not write 64-bit register"); }; } diff --git a/src/instructions/xor.rs b/src/instructions/xor.rs index 34877f7..9a3c196 100644 --- a/src/instructions/xor.rs +++ b/src/instructions/xor.rs @@ -619,7 +619,7 @@ mod tests { |a: &mut Axecutor| { write_reg_value!(d; a; EDX; 0x10); - let rip = a.reg_read_64(RIP.into()); + let rip = a.reg_read_64(RIP.into()).unwrap(); a.mem_init_zero(rip+0x35353 + 6, 4).unwrap(); a.mem_write_32(rip+0x35353 +6, 0x12345678).unwrap(); }; @@ -627,7 +627,7 @@ mod tests { assert_reg_value!(d; a; EDX; 0x12345678^0x10); // Note that rip has advanced by instruction length 6 - let rip = a.reg_read_64(RIP.into()); + let rip = a.reg_read_64(RIP.into()).unwrap(); assert_eq!(a.mem_read_32(rip+0x35353).unwrap(), 0x12345678); }; (0; FLAG_PF | FLAG_SF | FLAG_OF | FLAG_CF | FLAG_ZF) @@ -638,7 +638,7 @@ mod tests { |a: &mut Axecutor| { write_reg_value!(q; a; RAX; 0x10); - let rip = a.reg_read_64(RIP.into()); + let rip = a.reg_read_64(RIP.into()).unwrap(); a.mem_init_zero(rip+0x35353 + 7, 8).unwrap(); a.mem_write_64(rip+0x35353 +7, 0x12345678).unwrap(); }; @@ -646,7 +646,7 @@ mod tests { assert_reg_value!(q; a; RAX; 0x12345678^0x10); // Note that rip has advanced by instruction length 7 - let rip = a.reg_read_64(RIP.into()); + let rip = a.reg_read_64(RIP.into()).unwrap(); assert_eq!(a.mem_read_64(rip+0x35353).unwrap(), 0x12345678); }; (0; FLAG_PF | FLAG_SF | FLAG_OF | FLAG_CF | FLAG_ZF) diff --git a/src/version_info.rs b/src/version_info.rs index d9fad6e..d48ce04 100644 --- a/src/version_info.rs +++ b/src/version_info.rs @@ -2,6 +2,7 @@ use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen] #[allow(dead_code)] +/// Returns the version of `ax` pub fn version() -> String { const VERSION: &'static str = env!("CARGO_PKG_VERSION"); VERSION.to_string() @@ -9,6 +10,7 @@ pub fn version() -> String { #[wasm_bindgen] #[allow(dead_code)] +/// Returns the commit hash of this `ax` version pub fn commit() -> String { const COMMIT: &'static str = env!("GIT_COMMIT"); COMMIT.to_string()