Skip to content

Commit

Permalink
Split up DynasmApi into two traits, DynasmApi and DynasmLabelApi and …
Browse files Browse the repository at this point in the history
…introduce Assembler.alter_uncommitted which uses the new UncommittedModifier which does not support labels and therefore only implements DynasmApi
  • Loading branch information
CensoredUsername committed Sep 7, 2016
1 parent f6df982 commit 8d63448
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 19 deletions.
2 changes: 1 addition & 1 deletion doc/examples/bf-jit/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#[macro_use]
extern crate dynasmrt;
use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};

extern crate itertools;
use itertools::Itertools;
Expand Down
2 changes: 1 addition & 1 deletion doc/examples/hello-world/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#[macro_use]
extern crate dynasmrt;

use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};

use std::{io, slice, mem};
use std::io::Write;
Expand Down
16 changes: 8 additions & 8 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Dynasm-rs is a library and syntax extension for assembling code at runtime. For
#[macro_use]
extern crate dynasmrt;
use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};
use std::{io, slice, mem};
use std::io::Write;
Expand Down Expand Up @@ -67,15 +67,15 @@ To use the `dynasm!` procedural macro, first the dynasm plugin has to be loaded.
#[macro_use]
extern crate dynasmrt;
use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};
```
We then link to the dynasm runtime crate. Although they are not used here, it also contains various utility macros which we load here.
Furthermore, the `DynasmApi` trait is loaded. This trait defines the interface used by the `dynasm!` procedural macro to produce assembled code.
Furthermore, the `DynasmApi` and `DynasmLabelApi` traits are loaded. These traits defines the interfaces used by the `dynasm!` procedural macro to produce assembled code.

```
let mut ops = dynasmrt::Assembler::new();
```
Of course, the machine code that will be generated will need to live somewhere. `dynasmrt::Assembler` is a struct that implements the `DynasmApi` trait, provides storage for the generated machine code, handles memory permissions and provides various utilities for dynamically assembling code. It even allows assembling code in one thread while several other threads execute said code. For this example though, we will use it in the most simple usecase, just assembling everything in advance and then executing it.
Of course, the machine code that will be generated will need to live somewhere. `dynasmrt::Assembler` is a struct that implements the `DynasmApi` and `DynasmLabelApi` traits, provides storage for the generated machine code, handles memory permissions and provides various utilities for dynamically assembling code. It even allows assembling code in one thread while several other threads execute said code. For this example though, we will use it in the most simple use case, just assembling everything in advance and then executing it.

```
dynasm!(ops
Expand Down Expand Up @@ -157,7 +157,7 @@ And for the people interested in the behind-the-scenes, here's what the `dynasm!
#[macro_use]
extern crate dynasmrt;
use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};
use std::{io, slice, mem};
use std::io::Write;
Expand Down Expand Up @@ -360,15 +360,15 @@ fn main() {

## Basics

To kickstart this process, we'll first add the `dynasm` plugin and `dynasmrt` crate to our project, and `use` the `DynasmApi` trait:
To kickstart this process, we'll first add the `dynasm` plugin and `dynasmrt` crate to our project, and `use` the `DynasmApi` and `DynasmLabelApi` traits:

```diffnew
+ #![feature(plugin)]
+ #![plugin(dynasm)]
+
+ #[macro_use]
+ extern crate dynasmrt;
+ use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};
```

Then, we'll define the following aliases to make the code more readable. As this code is specific to `x86_64` we'll add a `cfg` attribute here so the code will fail to compile on other architectures. Note that `ops` is purely a placeholder here for the parser, it isn't actually used.
Expand Down Expand Up @@ -785,7 +785,7 @@ With these changes, adding the necessary `use` statements and removing unused fu
#[macro_use]
extern crate dynasmrt;
use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};
extern crate itertools;
use itertools::Itertools;
Expand Down
104 changes: 96 additions & 8 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ macro_rules! MutPointer {
($e:expr) => {$e as *mut _ as _};
}

/// A struct representing an offset into the assembling buffer of a `DynasmApi` struct.
/// A struct representing an offset into the assembling buffer of a `DynasmLabelApi` struct.
/// The wrapped `usize` is the offset from the start of the assembling buffer in bytes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AssemblyOffset(pub usize);
Expand All @@ -48,7 +48,7 @@ pub struct ExecutableBuffer {
/// A structure wrapping some executable memory. It dereferences into a &[u8] slice.
impl ExecutableBuffer {
/// Obtain a pointer into the executable memory from an offset into it.
/// When an offset returned from `DynasmApi::offset` is used, the resulting pointer
/// When an offset returned from `DynasmLabelApi::offset` is used, the resulting pointer
/// will point to the start of the first instruction after the offset call,
/// which can then be jumped or called to divert control flow into the executable
/// buffer. Note that if this buffer is accessed through an Executor, these pointers
Expand Down Expand Up @@ -123,6 +123,15 @@ pub trait DynasmApi<'a> : Extend<u8> + Extend<&'a u8> {
mem::transmute::<_, [u8; 8]>(value.to_le())
}.iter().cloned());
}
/// This function is called in when a runtime error has to be generated. It panics.
#[inline]
fn runtime_error(&self, msg: &'static str) -> ! {
panic!(msg);
}
}

/// This trait extends DynasmApi to not only allow assembling, but also labels and various directives
pub trait DynasmLabelApi<'a> : DynasmApi<'a> {
/// Push nops until the assembling target end is aligned to the given alignment
fn align(&mut self, alignment: usize);
/// Record the definition of a local label
Expand All @@ -140,12 +149,6 @@ pub trait DynasmApi<'a> : Extend<u8> + Extend<&'a u8> {
fn global_reloc( &mut self, name: &'static str, size: u8);
/// Record a relocation spot for a reference to a dynamic label
fn dynamic_reloc( &mut self, id: DynamicLabel, size: u8);

/// This function is called in when a runtime error has to be generated. It panics.
#[inline]
fn runtime_error(&self, msg: &'static str) -> ! {
panic!(msg);
}
}

/// This struct is an implementation of a dynasm runtime. It supports incremental
Expand Down Expand Up @@ -239,6 +242,13 @@ impl Assembler {
// no commit is required as we directly modified the buffer.
}

pub fn alter_uncommitted<F>(&mut self, f: F) where F: FnOnce(&mut UncommittedModifier) -> () {
f(&mut UncommittedModifier {
offset: self.asmoffset,
assembler: self
});
}

#[inline]
fn patch_loc(&mut self, loc: PatchLoc, target: usize) {
let buf_loc = loc.0 - self.asmoffset;
Expand Down Expand Up @@ -376,7 +386,9 @@ impl<'a> DynasmApi<'a> for Assembler {
fn push(&mut self, value: u8) {
self.ops.push(value);
}
}

impl<'a> DynasmLabelApi<'a> for Assembler {
#[inline]
fn align(&mut self, alignment: usize) {
let offset = self.offset().0 % alignment;
Expand Down Expand Up @@ -494,6 +506,15 @@ impl<'a, 'b> AssemblyModifier<'a, 'b> {
}
}

/// Checks that the current modification offset is exactly the specified offset.
/// If this is violated, it panics.
#[inline]
pub fn check_exact(&mut self, offset: AssemblyOffset) {
if self.assembler.asmoffset != offset.0 {
panic!("specified offset to check is smaller than the actual offset");
}
}

#[inline]
fn patch_loc(&mut self, loc: PatchLoc, target: usize) {
let buf = &mut self.buffer.as_mut_slice()[loc.0 - loc.1 as usize .. loc.0];
Expand Down Expand Up @@ -546,7 +567,9 @@ impl<'a, 'b, 'c> DynasmApi<'c> for AssemblyModifier<'a, 'b> {
self.buffer.as_mut_slice()[self.assembler.asmoffset] = value;
self.assembler.asmoffset += 1;
}
}

impl<'a, 'b, 'c> DynasmLabelApi<'c> for AssemblyModifier<'a, 'b> {
#[inline]
fn align(&mut self, alignment: usize) {
self.assembler.align(alignment);
Expand Down Expand Up @@ -614,3 +637,68 @@ impl<'a, 'b, 'c> Extend<&'c u8> for AssemblyModifier<'a, 'b> {
self.extend(iter.into_iter().cloned())
}
}

/// This struct is a wrapper around an `Assembler` normally created using the
/// `Assembler.alter_uncommitted` method. It allows the user to edit parts
/// of the assembling buffer that cannot be determined easily or efficiently
/// in advance. Due to limitations of the label resolution algorithms, this
/// assembler does not allow labels to be used.
pub struct UncommittedModifier<'a> {
assembler: &'a mut Assembler,
offset: usize
}

impl<'a> UncommittedModifier<'a> {
/// Sets the current modification offset to the given value
#[inline]
pub fn goto(&mut self, offset: AssemblyOffset) {
self.offset = offset.0;
}

/// Checks that the current modification offset is not larger than the specified offset.
/// If this is violated, it panics.
#[inline]
pub fn check(&mut self, offset: AssemblyOffset) {
if self.offset > offset.0 {
panic!("specified offset to check is smaller than the actual offset");
}
}

/// Checks that the current modification offset is exactly the specified offset.
/// If this is violated, it panics.
#[inline]
pub fn check_exact(&mut self, offset: AssemblyOffset) {
if self.offset != offset.0 {
panic!("specified offset to check is smaller than the actual offset");
}
}
}

impl<'a, 'b> DynasmApi<'b> for UncommittedModifier<'a> {
#[inline]
fn offset(&self) -> AssemblyOffset {
AssemblyOffset(self.offset)
}

#[inline]
fn push(&mut self, value: u8) {
self.assembler.ops[self.offset - self.assembler.asmoffset] = value;
self.offset += 1;
}
}

impl<'a> Extend<u8> for UncommittedModifier<'a> {
#[inline]
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> {
for i in iter {
self.push(i)
}
}
}

impl<'a, 'b> Extend<&'b u8> for UncommittedModifier<'a> {
#[inline]
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=&'b u8> {
self.extend(iter.into_iter().cloned())
}
}
2 changes: 1 addition & 1 deletion testing/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#[macro_use]
extern crate dynasmrt;
use dynasmrt::DynasmApi;
use dynasmrt::{DynasmApi, DynasmLabelApi};

// aliases, and dynasm! in item position
dynasm!(ops
Expand Down

0 comments on commit 8d63448

Please sign in to comment.