dynasmrt/
relocations.rs

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