diff --git a/build_docs.sh b/build_docs.sh index 0fc041f157..24c80cfc63 100644 --- a/build_docs.sh +++ b/build_docs.sh @@ -18,7 +18,7 @@ mkdir ./build_docs/plugin mkdir ./build_docs/runtime # create instruction reference markdown file -(cd doc/insref && cargo run > ../instructionref.md) +(cd doc/insref && cargo update && cargo run > ../instructionref.md) # build plugin docs for f in ./doc/*.md; do diff --git a/doc/examples/bf-jit/src/main.rs b/doc/examples/bf-jit/src/main.rs index cefb6d37d3..ef667aef29 100644 --- a/doc/examples/bf-jit/src/main.rs +++ b/doc/examples/bf-jit/src/main.rs @@ -122,15 +122,15 @@ impl Program { ); }, b',' => { - call_extern!(ops, State::getchar); dynasm!(ops + ;; call_extern!(ops, State::getchar) ; cmp al, 0 ; jnz ->io_failure ); }, b'.' => { - call_extern!(ops, State::putchar); dynasm!(ops + ;; call_extern!(ops, State::putchar) ; cmp al, 0 ; jnz ->io_failure ); @@ -172,17 +172,13 @@ impl Program { return Err("[ without matching ]"); } - epilogue!(ops, 0); - dynasm!(ops + ;; epilogue!(ops, 0) ;->overflow: - ); - epilogue!(ops, 1); - - dynasm!(ops + ;; epilogue!(ops, 1) ;->io_failure: + ;; epilogue!(ops, 2) ); - epilogue!(ops, 2); let code = ops.finalize().unwrap(); Ok(Program { diff --git a/doc/langref.md b/doc/langref.md index ae2b5cbfe6..f0f3f7c4fd 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -10,6 +10,8 @@ The following syntax units used in dynasm syntax are defined by the [rust gramma - `ident` - `expr_path` - `expr` +- `stmt` + Dynasm-rs defines the following base syntax units: @@ -26,7 +28,7 @@ The entry point of dynasm-rs is the dynasm! macro. It is structured as following Where line can be one of the following: -`line : directive | label | instruction ;` +`line : (";" stmt) | directive | label | instruction ;` ## Directives @@ -98,13 +100,13 @@ macro_rules! fma { } ``` -An important thing to notice here is which matchers are used for which parts of dynasm! syntax. The following table lists the correct matchers to be used for expanding to dynasm syntax elements. Note that `$a:expr` means that anything that parses to an expression like `$a:ident` and just raw token trees are allowed. +An important thing to notice here is which matchers are used for which parts of `dynasm!` syntax. The following table lists the correct matchers to be used for expanding to dynasm syntax elements. Note that `$a:expr` means that anything that parses to an expression like `$a:ident` and just raw token trees are allowed. Table 2: dynasm-rs macro expansion rules Syntax element | Matchers :----------------------|:------------------------ -Assembling buffer | `$ops:ident` +Assembling buffer | `$ops:expr` Register reference | `$reg:expr` Memory reference | `$mem:expr` Any element inside a memory reference | `$elem:expr, $reg:ident` @@ -113,6 +115,26 @@ Local or global label name | `$label:ident` Dynamic label | `$label:expr` Type map | `$reg:expr => $type:path[$reg:expr].$attr:ident` +## statements + +To make code that uses a lot of macros less verbose, dynasm-rs allows bare rust statements to be inserted inside `dynasm!` invocations. This can be done by using a double semicolon instead of a single semicolon at the start of the line as displayed in the following equivalent examples: + +``` +dynasm!(ops + ; mov rcx, rax +); +call_extern!(ops, extern_func); +dynasm!(ops + ; mov rcx, rax +); + +dynasm!(ops + ; mov rcx, rax + ;; call_extern!(ops, extern_func) + ; mov rcx, rax +); +``` + ## Labels In order to describe flow control effectively, dynasm-rs supports labels. However, since the assembly templates can be combined in a variety of ways at the mercy of the program using dynasm-rs, the semantics of these labels are somewhat different from how labels work in a static assembler. @@ -209,7 +231,7 @@ Syntax | Equivalent expression :------|:----------- `rax => Type.attr` | `(rax as *mut Type).attr` `rax => Type[expr]` | `(rax as *mut [Type])[expr]` -`rax => Type[rbx + expr]` | `(rax as *mut [Type])[rbx + expr]` +`rax => Type[rbx]` | `(rax as *mut [Type])[rbx]` `rax => Type[rbx + expr].attr` | `(rax as *mut [Type])[rbx + expr].attr ` #### Immediates diff --git a/doc/tutorial.md b/doc/tutorial.md index 305f86a82f..be41281da6 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -593,8 +593,8 @@ We can then simply call these methods directly from the compiled code. If the I/ - } ``` ```diffnew -+ call_extern!(ops, State::getchar); + dynasm!(ops ++ ;; call_extern!(ops, State::getchar) + ; cmp al, 0 + ; jnz ->io_failure + ); @@ -606,8 +606,8 @@ We can then simply call these methods directly from the compiled code. If the I/ - } ``` ```diffnew -+ call_extern!(ops, State::putchar); + dynasm!(ops ++ ;; call_extern!(ops, State::putchar) + ; cmp al, 0 + ; jnz ->io_failure + ); @@ -679,17 +679,13 @@ The `[` and `]` commands have the most complex implementation. When a `[` is enc With the end of the parsing reached, we must now handle the return and possible error conditions. This is done by returning 0 if the executionw as successful, or an error code when an error happened at runtime. ```diffnew -+ epilogue!(ops, 0); -+ + dynasm!(ops ++ ;; epilogue!(ops, 0) + ;->overflow: -+ ); -+ epilogue!(ops, 1); -+ -+ dynasm!(ops ++ ;; epilogue!(ops, 1) + ;->io_failure: ++ ;; epilogue!(ops, 2) + ); -+ epilogue!(ops, 2); ``` Now we can finalize the assembler and construct a Program from the resulting buffer: @@ -904,15 +900,15 @@ impl Program { ); }, b',' => { - call_extern!(ops, State::getchar); dynasm!(ops + ;; call_extern!(ops, State::getchar) ; cmp al, 0 ; jnz ->io_failure ); }, b'.' => { - call_extern!(ops, State::putchar); dynasm!(ops + ;; call_extern!(ops, State::putchar) ; cmp al, 0 ; jnz ->io_failure ); @@ -954,17 +950,13 @@ impl Program { return Err("[ without matching ]"); } - epilogue!(ops, 0); - dynasm!(ops + ;; epilogue!(ops, 0) ;->overflow: - ); - epilogue!(ops, 1); - - dynasm!(ops + ;; epilogue!(ops, 1) ;->io_failure: + ;; epilogue!(ops, 2) ); - epilogue!(ops, 2); let code = ops.finalize().unwrap(); Ok(Program { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 73dc94a25a..2aa51c6764 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -18,7 +18,7 @@ macro_rules! Pointer { ($e:expr) => {$e as *const _ as _}; } -/// Preforms the same action as the Pointer! macro, but casts to a *mut pointer. +/// Preforms the same action as the `Pointer!` macro, but casts to a *mut pointer. #[macro_export] macro_rules! MutPointer { ($e:expr) => {$e as *mut _ as _}; @@ -191,7 +191,7 @@ impl Assembler { Assembler { execbuffer: Arc::new(RwLock::new(ExecutableBuffer { length: 0, - buffer: Mmap::anonymous(MMAP_INIT_SIZE, Protection::ReadExecute).unwrap() + buffer: Mmap::anonymous(MMAP_INIT_SIZE, Protection::ReadExecute).expect("Failed to allocate executable memory") })), asmoffset: 0, map_len: MMAP_INIT_SIZE, @@ -227,7 +227,7 @@ impl Assembler { let lock = self.execbuffer.clone(); let mut lock = lock.write().unwrap(); let buf = lock.deref_mut(); - buf.buffer.set_protection(Protection::ReadWrite).unwrap(); + buf.buffer.set_protection(Protection::ReadWrite).expect("Failed to change memory protection mode"); { let mut m = AssemblyModifier { @@ -238,11 +238,15 @@ impl Assembler { m.encode_relocs(); } - buf.buffer.set_protection(Protection::ReadExecute).unwrap(); + buf.buffer.set_protection(Protection::ReadExecute).expect("Failed to change memory protection mode"); self.asmoffset = asmoffset; // no commit is required as we directly modified the buffer. } + /// Similar to `Assembler::alter`, this method allows modification of the yet to be + /// committed assembing buffer. Note that it is not possible to use labels in this + /// context, and overriding labels will cause corruption when the assembler tries to + /// resolve the labels at commit time. pub fn alter_uncommitted(&mut self, f: F) where F: FnOnce(&mut UncommittedModifier) -> () { f(&mut UncommittedModifier { offset: self.asmoffset, @@ -317,7 +321,7 @@ impl Assembler { if buf_end > self.map_len { // create a new buffer of the necessary size max(current_buf_len * 2, wanted_len) let map_len = cmp::max(buf_end, self.map_len * 2); - let mut new_buf = Mmap::anonymous(map_len, Protection::ReadWrite).unwrap(); + let mut new_buf = Mmap::anonymous(map_len, Protection::ReadWrite).expect("Failed to change memory protection mode"); self.map_len = new_buf.len(); // copy over from the old buffer and the asm buffer (unsafe is completely safe due to use of anonymous mappings) @@ -325,7 +329,7 @@ impl Assembler { new_buf.as_mut_slice()[same].copy_from_slice(&self.execbuffer.read().unwrap().buffer.as_slice()[same]); new_buf.as_mut_slice()[changed].copy_from_slice(&self.ops); } - new_buf.set_protection(Protection::ReadExecute).unwrap(); + new_buf.set_protection(Protection::ReadExecute).expect("Failed to change memory protection mode"); // swap the buffers and the initialized length let mut data = ExecutableBuffer { @@ -337,11 +341,11 @@ impl Assembler { } else { // make the buffer writeable and copy things over. let mut data = self.execbuffer.write().unwrap(); - data.buffer.set_protection(Protection::ReadWrite).unwrap(); + data.buffer.set_protection(Protection::ReadWrite).expect("Failed to change memory protection mode"); unsafe { data.buffer.as_mut_slice()[changed].copy_from_slice(&self.ops); } - data.buffer.set_protection(Protection::ReadExecute).unwrap(); + data.buffer.set_protection(Protection::ReadExecute).expect("Failed to change memory protection mode"); // update the length of the initialized part of the buffer, if this commit adds length if buf_end > data.length { data.length = buf_end;