1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
//! This module defines the `Relocation` trait and several utilities for implementing relocations.
use byteorder::{ByteOrder, LittleEndian};
use std::convert::TryFrom;
/// Error returned when encoding a relocation failed
#[derive(Debug)]
pub struct ImpossibleRelocation { }
/// Used to inform assemblers on how to implement relocations for each architecture.
/// When implementing a new architecture, one simply has to implement this trait for
/// the architecture's relocation definition.
pub trait Relocation {
/// The encoded representation for this relocation that is emitted by the dynasm! macro.
type Encoding;
/// construct this relocation from an encoded representation.
fn from_encoding(encoding: Self::Encoding) -> Self;
/// construct this relocation from a simple size. This is used to implement relocations in directives and literal pools.
fn from_size(size: RelocationSize) -> Self;
/// The size of the slice of bytes affected by this relocation
fn size(&self) -> usize;
/// Write a value into a buffer of size `self.size()` in the format of this relocation.
/// Any bits not part of the relocation should be preserved.
fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation>;
/// Read a value from a buffer of size `self.size()` in the format of this relocation.
fn read_value(&self, buf: &[u8]) -> isize;
/// Specifies what kind of relocation this relocation instance is.
fn kind(&self) -> RelocationKind;
/// Specifies the default page size on this platform.
fn page_size() -> usize;
}
/// Specifies what kind of relocation a relocation is.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RelocationKind {
/// A simple, PC-relative relocation. These can be encoded once and do not need
/// to be adjusted when the executable buffer is moved.
Relative = 0,
/// An absolute relocation to a relative address,
/// i.e. trying to put the address of a dynasm x86 function in a register
/// This means adjustment is necessary when the executable buffer is moved
AbsToRel = 1,
/// A relative relocation to an absolute address,
/// i.e. trying to call a Rust function with a dynasm x86 call.
/// This means adjustment is necessary when the executable buffer is moved
RelToAbs = 2,
}
impl RelocationKind {
/// Converts back from numeric value to RelocationKind
pub fn from_encoding(encoding: u8) -> Self {
match encoding {
0 => Self::Relative,
1 => Self::AbsToRel,
2 => Self::RelToAbs,
x => panic!("Unsupported relocation kind {}", x)
}
}
}
/// A descriptor for the size of a relocation. This also doubles as a relocation itself
/// for relocations in data directives. Can be converted to relocations of any kind of architecture
/// using `Relocation::from_size`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RelocationSize {
/// A byte-sized relocation
Byte = 1,
/// A two-byte relocation
Word = 2,
/// A four-byte sized relocation
DWord = 4,
/// An 8-byte sized relocation
QWord = 8,
}
impl Relocation for RelocationSize {
type Encoding = u8;
fn from_encoding(encoding: Self::Encoding) -> Self {
match encoding {
1 => RelocationSize::Byte,
2 => RelocationSize::Word,
4 => RelocationSize::DWord,
8 => RelocationSize::QWord,
x => panic!("Unsupported relocation size {}", x)
}
}
fn from_size(size: RelocationSize) -> Self {
size
}
fn size(&self) -> usize {
*self as usize
}
fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> {
match self {
RelocationSize::Byte => buf[0] =
i8::try_from(value).map_err(|_| ImpossibleRelocation { } )?
as u8,
RelocationSize::Word => LittleEndian::write_i16(buf,
i16::try_from(value).map_err(|_| ImpossibleRelocation { } )?
),
RelocationSize::DWord => LittleEndian::write_i32(buf,
i32::try_from(value).map_err(|_| ImpossibleRelocation { } )?
),
RelocationSize::QWord => LittleEndian::write_i64(buf,
i64::try_from(value).map_err(|_| ImpossibleRelocation { } )?
),
}
Ok(())
}
fn read_value(&self, buf: &[u8]) -> isize {
match self {
RelocationSize::Byte => buf[0] as i8 as isize,
RelocationSize::Word => LittleEndian::read_i16(buf) as isize,
RelocationSize::DWord => LittleEndian::read_i32(buf) as isize,
RelocationSize::QWord => LittleEndian::read_i64(buf) as isize,
}
}
fn kind(&self) -> RelocationKind {
RelocationKind::Relative
}
fn page_size() -> usize {
4096
}
}
pub(crate) fn fits_signed_bitfield(value: i64, bits: u8) -> bool {
if bits >= 64 {
return true;
}
let half = 1i64 << (bits - 1);
value < half && value >= -half
}