extern crate memmap;
extern crate byteorder;
pub mod mmap;
pub mod components;
pub mod relocations;
pub mod x64;
pub mod x86;
pub mod aarch64;
pub use crate::mmap::ExecutableBuffer;
use crate::components::{MemoryManager, LabelRegistry, RelocRegistry, ManagedRelocs, PatchLoc};
use crate::relocations::Relocation;
use std::iter::Extend;
use std::sync::{Arc, RwLock, RwLockReadGuard};
use std::io;
use std::error;
use std::fmt;
use std::mem;
#[macro_export]
macro_rules! Pointer {
($e:expr) => {$e as *const _ as _};
}
#[macro_export]
macro_rules! MutPointer {
($e:expr) => {$e as *mut _ as _};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AssemblyOffset(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DynamicLabel(usize);
impl DynamicLabel {
pub fn get_id(self) -> usize {
self.0
}
}
#[derive(Debug, Clone)]
pub struct Executor {
execbuffer: Arc<RwLock<ExecutableBuffer>>
}
impl Executor {
#[inline]
pub fn lock(&self) -> RwLockReadGuard<ExecutableBuffer> {
self.execbuffer.read().unwrap()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LabelKind {
Local(&'static str),
Global(&'static str),
Dynamic(DynamicLabel)
}
impl fmt::Display for LabelKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Local(s) => write!(f, "label {}", s),
Self::Global(s) => write!(f, "label ->{}", s),
Self::Dynamic(id) => write!(f, "label =>{}", id.get_id())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TargetKind {
Forward(&'static str),
Backward(&'static str),
Global(&'static str),
Dynamic(DynamicLabel),
Extern(usize),
Managed,
}
impl fmt::Display for TargetKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Forward(s) => write!(f, "target >{}", s),
Self::Backward(s) => write!(f, "target <{}", s),
Self::Global(s) => write!(f, "target ->{}", s),
Self::Dynamic(id) => write!(f, "target =>{}", id.get_id()),
Self::Extern(value) => write!(f, "target extern {}", value),
Self::Managed => write!(f, "while adjusting managed relocation"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DynasmError {
CheckFailed,
DuplicateLabel(LabelKind),
UnknownLabel(LabelKind),
ImpossibleRelocation(TargetKind),
}
impl fmt::Display for DynasmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DynasmError::CheckFailed => write!(f, "An assembly modification check failed"),
DynasmError::DuplicateLabel(l) => write!(f, "Duplicate label defined: '{}'", l),
DynasmError::UnknownLabel(l) => write!(f, "Unknown label: '{}'", l),
DynasmError::ImpossibleRelocation(s) => write!(f, "Impossible relocation: '{}'", s),
}
}
}
impl error::Error for DynasmError {
fn description(&self) -> &str {
match self {
DynasmError::CheckFailed => "An assembly modification offset check failed",
DynasmError::DuplicateLabel(_) => "Duplicate label defined",
DynasmError::UnknownLabel(_) => "Unknown label",
DynasmError::ImpossibleRelocation(_) => "Impossible relocation",
}
}
}
pub trait DynasmApi: Extend<u8> + for<'a> Extend<&'a u8> {
fn offset(&self) -> AssemblyOffset;
fn push(&mut self, byte: u8);
fn align(&mut self, alignment: usize, with: u8);
#[inline]
fn push_i8(&mut self, value: i8) {
self.push(value as u8);
}
#[inline]
fn push_i16(&mut self, value: i16) {
self.extend(&value.to_le_bytes());
}
#[inline]
fn push_i32(&mut self, value: i32) {
self.extend(&value.to_le_bytes());
}
#[inline]
fn push_i64(&mut self, value: i64) {
self.extend(&value.to_le_bytes());
}
#[inline]
fn push_u16(&mut self, value: u16) {
self.extend(&value.to_le_bytes());
}
#[inline]
fn push_u32(&mut self, value: u32) {
self.extend(&value.to_le_bytes());
}
#[inline]
fn push_u64(&mut self, value: u64) {
self.extend(&value.to_le_bytes());
}
#[inline]
fn runtime_error(&self, msg: &'static str) -> ! {
panic!(msg);
}
}
pub trait DynasmLabelApi : DynasmApi {
type Relocation: Relocation;
fn local_label( &mut self, name: &'static str);
fn global_label( &mut self, name: &'static str);
fn dynamic_label(&mut self, id: DynamicLabel);
fn forward_reloc( &mut self, name: &'static str, offset: isize, kind: <Self::Relocation as Relocation>::Encoding) {
self.forward_relocation(name, offset, Self::Relocation::from_encoding(kind))
}
fn backward_reloc(&mut self, name: &'static str, offset: isize, kind: <Self::Relocation as Relocation>::Encoding) {
self.backward_relocation(name, offset, Self::Relocation::from_encoding(kind))
}
fn global_reloc( &mut self, name: &'static str, offset: isize, kind: <Self::Relocation as Relocation>::Encoding) {
self.global_relocation(name, offset, Self::Relocation::from_encoding(kind))
}
fn dynamic_reloc( &mut self, id: DynamicLabel, offset: isize, kind: <Self::Relocation as Relocation>::Encoding) {
self.dynamic_relocation(id, offset, Self::Relocation::from_encoding(kind))
}
fn bare_reloc(&mut self, target: usize, kind: <Self::Relocation as Relocation>::Encoding) {
self.bare_relocation(target, Self::Relocation::from_encoding(kind))
}
fn forward_relocation( &mut self, name: &'static str, offset: isize, kind: Self::Relocation);
fn backward_relocation(&mut self, name: &'static str, offset: isize, kind: Self::Relocation);
fn global_relocation( &mut self, name: &'static str, offset: isize, kind: Self::Relocation);
fn dynamic_relocation( &mut self, id: DynamicLabel, offset: isize, kind: Self::Relocation);
fn bare_relocation(&mut self, target: usize, kind: Self::Relocation);
}
pub struct VecAssembler(Vec<u8>);
impl Extend<u8> for VecAssembler {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> {
self.0.extend(iter)
}
}
impl<'a> Extend<&'a u8> for VecAssembler {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=&'a u8> {
self.0.extend(iter)
}
}
impl DynasmApi for VecAssembler {
fn offset(&self) -> AssemblyOffset {
AssemblyOffset(self.0.len())
}
fn push(&mut self, byte: u8) {
self.0.push(byte);
}
fn align(&mut self, alignment: usize, with: u8) {
let offset = self.offset().0 % alignment;
if offset != 0 {
for _ in offset .. alignment {
self.push(with);
}
}
}
}
#[derive(Debug)]
pub struct Assembler<R: Relocation> {
ops: Vec<u8>,
memory: MemoryManager,
labels: LabelRegistry,
relocs: RelocRegistry<R>,
managed: ManagedRelocs<R>,
error: Option<DynasmError>,
}
impl<R: Relocation> Assembler<R> {
pub fn new() -> io::Result<Self> {
Ok(Self {
ops: Vec::new(),
memory: MemoryManager::new(R::page_size())?,
labels: LabelRegistry::new(),
relocs: RelocRegistry::new(),
managed: ManagedRelocs::new(),
error: None
})
}
pub fn new_dynamic_label(&mut self) -> DynamicLabel {
self.labels.new_dynamic_label()
}
pub fn alter_uncommitted(&mut self) -> UncommittedModifier {
let offset = self.memory.committed();
UncommittedModifier::new(&mut self.ops, AssemblyOffset(offset))
}
pub fn alter<F, O>(&mut self, f: F) -> Result<O, DynasmError>
where F: FnOnce(&mut Modifier<R>) -> O {
self.commit()?;
let mut lock = self.memory.write();
let buffer = mem::replace(&mut *lock, ExecutableBuffer::default());
let mut buffer = buffer.make_mut().expect("Could not swap buffer protection modes");
let mut modifier = Modifier {
asmoffset: 0,
previous_asmoffset: 0,
buffer: &mut *buffer,
labels: &mut self.labels,
relocs: &mut self.relocs,
old_managed: &mut self.managed,
new_managed: ManagedRelocs::new(),
error: None
};
let output = f(&mut modifier);
modifier.encode_relocs()?;
let buffer = buffer.make_exec().expect("Could not swap buffer protection modes");
mem::replace(&mut *lock, buffer);
Ok(output)
}
pub fn commit(&mut self) -> Result<(), DynasmError> {
self.encode_relocs()?;
let managed = &self.managed;
let error = &mut self.error;
self.memory.commit(&mut self.ops, |buffer, old_addr, new_addr| {
let change = new_addr.wrapping_sub(old_addr) as isize;
for reloc in managed.iter() {
if let Err(_) = reloc.adjust(0, buffer, change) {
*error = Some(DynasmError::ImpossibleRelocation(TargetKind::Managed))
}
}
});
if let Some(e) = self.error.take() {
return Err(e);
}
Ok(())
}
pub fn finalize(mut self) -> Result<ExecutableBuffer, Self> {
self.commit().expect("Errors were encountered when committing before finalization");
match self.memory.finalize() {
Ok(execbuffer) => Ok(execbuffer),
Err(memory) => Err(Self {
memory,
..self
})
}
}
pub fn reader(&self) -> Executor {
Executor {
execbuffer: self.memory.reader()
}
}
pub fn labels(&self) -> &LabelRegistry {
&self.labels
}
pub fn labels_mut(&mut self) -> &mut LabelRegistry {
&mut self.labels
}
fn encode_relocs(&mut self) -> Result<(), DynasmError> {
let buf_offset = self.memory.committed();
let buf_addr = self.memory.execbuffer_addr();
let buf = &mut self.ops;
if let Some(e) = self.error.take() {
return Err(e);
}
for (loc, name) in self.relocs.take_globals() {
let target = self.labels.resolve_global(name)?;
if let Err(_) = loc.patch(buf_offset, buf_addr, buf, target.0) {
return Err(DynasmError::ImpossibleRelocation(TargetKind::Global(name)));
}
if loc.needs_adjustment() {
self.managed.add(loc)
}
}
for (loc, id) in self.relocs.take_dynamics() {
let target = self.labels.resolve_dynamic(id)?;
if let Err(_) = loc.patch(buf_offset, buf_addr, buf, target.0) {
return Err(DynasmError::ImpossibleRelocation(TargetKind::Dynamic(id)));
}
if loc.needs_adjustment() {
self.managed.add(loc)
}
}
for (_, name) in self.relocs.take_locals() {
return Err(DynasmError::UnknownLabel(LabelKind::Local(name)));
}
Ok(())
}
}
impl<R: Relocation> Extend<u8> for Assembler<R> {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> {
self.ops.extend(iter)
}
}
impl<'a, R: Relocation> Extend<&'a u8> for Assembler<R> {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=&'a u8> {
self.ops.extend(iter)
}
}
impl<R: Relocation> DynasmApi for Assembler<R> {
fn offset(&self) -> AssemblyOffset {
AssemblyOffset(self.memory.committed() + self.ops.len())
}
fn push(&mut self, value: u8) {
self.ops.push(value);
}
fn align(&mut self, alignment: usize, with: u8) {
let misalign = self.offset().0 % alignment;
if misalign != 0 {
for _ in misalign .. alignment {
self.push(with);
}
}
}
}
impl<R: Relocation> DynasmLabelApi for Assembler<R> {
type Relocation = R;
fn local_label(&mut self, name: &'static str) {
let offset = self.offset();
for loc in self.relocs.take_locals_named(name) {
if let Err(_) = loc.patch(self.memory.committed(), self.memory.execbuffer_addr(), &mut self.ops, offset.0) {
self.error = Some(DynasmError::ImpossibleRelocation(TargetKind::Forward(name)))
} else if loc.needs_adjustment() {
self.managed.add(loc)
}
}
self.labels.define_local(name, offset);
}
fn global_label( &mut self, name: &'static str) {
let offset = self.offset();
if let Err(e) = self.labels.define_global(name, offset) {
self.error = Some(e)
}
}
fn dynamic_label(&mut self, id: DynamicLabel) {
let offset = self.offset();
if let Err(e) = self.labels.define_dynamic(id, offset) {
self.error = Some(e)
}
}
fn global_relocation(&mut self, name: &'static str, offset: isize, kind: R) {
let location = self.offset();
self.relocs.add_global(name, PatchLoc::new(location, offset, kind));
}
fn dynamic_relocation(&mut self, id: DynamicLabel, offset: isize, kind: R) {
let location = self.offset();
self.relocs.add_dynamic(id, PatchLoc::new(location, offset, kind));
}
fn forward_relocation(&mut self, name: &'static str, offset: isize, kind: R) {
let location = self.offset();
self.relocs.add_local(name, PatchLoc::new(location, offset, kind));
}
fn backward_relocation(&mut self, name: &'static str, offset: isize, kind: R) {
let target = match self.labels.resolve_local(name) {
Ok(target) => target.0,
Err(e) => {
self.error = Some(e);
return;
}
};
let location = self.offset();
let loc = PatchLoc::new(location, offset, kind);
if let Err(_) = loc.patch(self.memory.committed(), self.memory.execbuffer_addr(), &mut self.ops, target) {
self.error = Some(DynasmError::ImpossibleRelocation(TargetKind::Backward(name)))
} else if loc.needs_adjustment() {
self.managed.add(loc)
}
}
fn bare_relocation(&mut self, target: usize, kind: R) {
let location = self.offset();
let loc = PatchLoc::new(location, 0, kind);
if let Err(_) = loc.patch(self.memory.committed(), self.memory.execbuffer_addr(), &mut self.ops, target) {
self.error = Some(DynasmError::ImpossibleRelocation(TargetKind::Extern(target)))
} else if loc.needs_adjustment() {
self.managed.add(loc)
}
}
}
#[derive(Debug)]
pub struct Modifier<'a, R: Relocation> {
asmoffset: usize,
previous_asmoffset: usize,
buffer: &'a mut [u8],
labels: &'a mut LabelRegistry,
relocs: &'a mut RelocRegistry<R>,
old_managed: &'a mut ManagedRelocs<R>,
new_managed: ManagedRelocs<R>,
error: Option<DynasmError>
}
impl<'a, R: Relocation> Modifier<'a, R> {
pub fn goto(&mut self, offset: AssemblyOffset) {
self.old_managed.remove_between(self.previous_asmoffset, self.asmoffset);
self.asmoffset = offset.0;
self.previous_asmoffset = offset.0;
}
pub fn check(&self, offset: AssemblyOffset) -> Result<(), DynasmError> {
if self.asmoffset > offset.0 {
Err(DynasmError::CheckFailed)
} else {
Ok(())
}
}
pub fn check_exact(&self, offset: AssemblyOffset) -> Result<(), DynasmError> {
if self.asmoffset != offset.0 {
Err(DynasmError::CheckFailed)
} else {
Ok(())
}
}
fn encode_relocs(&mut self) -> Result<(), DynasmError> {
let buf_addr = self.buffer.as_ptr() as usize;
if let Some(e) = self.error.take() {
return Err(e);
}
for (loc, name) in self.relocs.take_globals() {
let target = self.labels.resolve_global(name)?;
if let Err(_) = loc.patch(0, buf_addr, self.buffer, target.0) {
return Err(DynasmError::ImpossibleRelocation(TargetKind::Global(name)));
}
if loc.needs_adjustment() {
self.new_managed.add(loc);
}
}
for (loc, id) in self.relocs.take_dynamics() {
let target = self.labels.resolve_dynamic(id)?;
if let Err(_) = loc.patch(0, buf_addr, self.buffer, target.0) {
return Err(DynasmError::ImpossibleRelocation(TargetKind::Dynamic(id)));
}
if loc.needs_adjustment() {
self.new_managed.add(loc);
}
}
for (_, name) in self.relocs.take_locals() {
return Err(DynasmError::UnknownLabel(LabelKind::Local(name)));
}
self.old_managed.remove_between(self.previous_asmoffset, self.asmoffset);
self.previous_asmoffset = self.asmoffset;
self.old_managed.append(&mut self.new_managed);
Ok(())
}
}
impl<'a, R: Relocation> Extend<u8> for Modifier<'a,R> {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=u8> {
for (src, dst) in iter.into_iter().zip(self.buffer[self.asmoffset ..].iter_mut()) {
*dst = src;
}
}
}
impl<'a, 'b, R: Relocation> Extend<&'b u8> for Modifier<'a, R> {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=&'b u8> {
for (src, dst) in iter.into_iter().zip(self.buffer[self.asmoffset ..].iter_mut()) {
*dst = *src;
}
}
}
impl<'a, R: Relocation> DynasmApi for Modifier<'a, R> {
fn offset(&self) -> AssemblyOffset {
AssemblyOffset(self.asmoffset)
}
fn push(&mut self, value: u8) {
self.buffer[self.asmoffset] = value;
self.asmoffset += 1
}
fn align(&mut self, alignment: usize, with: u8) {
let mismatch = self.asmoffset % alignment;
if mismatch != 0 {
for _ in mismatch .. alignment {
self.push(with)
}
}
}
}
impl<'a, R: Relocation> DynasmLabelApi for Modifier<'a, R> {
type Relocation = R;
fn local_label(&mut self, name: &'static str) {
let offset = self.offset();
for loc in self.relocs.take_locals_named(name) {
if let Err(_) = loc.patch(0, self.buffer.as_ptr() as usize, self.buffer, offset.0) {
self.error = Some(DynasmError::ImpossibleRelocation(TargetKind::Forward(name)));
} else if loc.needs_adjustment() {
self.new_managed.add(loc);
}
}
self.labels.define_local(name, offset);
}
fn global_label( &mut self, name: &'static str) {
let offset = self.offset();
if let Err(e) = self.labels.define_global(name, offset) {
self.error = Some(e);
}
}
fn dynamic_label(&mut self, id: DynamicLabel) {
let offset = self.offset();
if let Err(e) = self.labels.define_dynamic(id, offset) {
self.error = Some(e);
}
}
fn global_relocation(&mut self, name: &'static str, offset: isize, kind: R) {
let location = self.offset();
self.relocs.add_global(name, PatchLoc::new(location, offset, kind));
}
fn dynamic_relocation(&mut self, id: DynamicLabel, offset: isize, kind: R) {
let location = self.offset();
self.relocs.add_dynamic(id, PatchLoc::new(location, offset, kind));
}
fn forward_relocation(&mut self, name: &'static str, offset: isize, kind: R) {
let location = self.offset();
self.relocs.add_local(name, PatchLoc::new(location, offset, kind));
}
fn backward_relocation(&mut self, name: &'static str, offset: isize, kind: R) {
let target = match self.labels.resolve_local(name) {
Ok(target) => target.0,
Err(e) => {
self.error = Some(e);
return;
}
};
let location = self.offset();
let loc = PatchLoc::new(location, offset, kind);
if let Err(_) = loc.patch(0, self.buffer.as_ptr() as usize, self.buffer, target) {
self.error = Some(DynasmError::ImpossibleRelocation(TargetKind::Backward(name)));
} else if loc.needs_adjustment() {
self.new_managed.add(loc)
}
}
fn bare_relocation(&mut self, target: usize, kind: R) {
let location = self.offset();
let loc = PatchLoc::new(location, 0, kind);
if let Err(_) = loc.patch(0, self.buffer.as_ptr() as usize, self.buffer, target) {
self.error = Some(DynasmError::ImpossibleRelocation(TargetKind::Extern(target)));
} else if loc.needs_adjustment() {
self.new_managed.add(loc)
}
}
}
#[derive(Debug)]
pub struct UncommittedModifier<'a> {
buffer: &'a mut Vec<u8>,
base_offset: usize,
offset: usize
}
impl<'a> UncommittedModifier<'a> {
pub fn new(buffer: &mut Vec<u8>, base_offset: AssemblyOffset) -> UncommittedModifier {
UncommittedModifier {
buffer,
base_offset: base_offset.0,
offset: base_offset.0
}
}
pub fn goto(&mut self, offset: AssemblyOffset) {
self.offset = offset.0;
}
pub fn check(&mut self, offset: AssemblyOffset) -> Result<(), DynasmError> {
if self.offset > offset.0 {
Err(DynasmError::CheckFailed)
} else {
Ok(())
}
}
pub fn check_exact(&mut self, offset: AssemblyOffset) -> Result<(), DynasmError> {
if self.offset != offset.0 {
Err(DynasmError::CheckFailed)
} else {
Ok(())
}
}
}
impl<'a> DynasmApi for UncommittedModifier<'a> {
fn offset(&self) -> AssemblyOffset {
AssemblyOffset(self.offset)
}
fn push(&mut self, value: u8) {
self.buffer[self.offset - self.base_offset] = value;
self.offset += 1;
}
fn align(&mut self, alignment: usize, with: u8) {
let mismatch = self.offset % alignment;
if mismatch != 0 {
for _ in mismatch .. alignment {
self.push(with)
}
}
}
}
impl<'a> Extend<u8> for UncommittedModifier<'a> {
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> {
fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=&'b u8> {
self.extend(iter.into_iter().cloned())
}
}