Skip to main content

patchouly_core/
relocation.rs

1use bitfields::bitfield;
2
3/// A relocation entry
4///
5/// It roughly follows the `Relocation` struct from the `object` crate,
6/// with some simplifications.
7#[bitfield(u64)]
8#[derive(Copy, Clone, PartialEq, Eq, Hash)]
9pub struct Relocation {
10    /// The address where relocation is required
11    ///
12    /// Note that we think `u16` is good enough for a stencil:
13    /// if your stencils go beyond 10k, you're probably doing something wrong.
14    offset: u16,
15    /// Encoding, how a relocation should be written in the place.
16    #[bits(7)]
17    encoding: RelocationEncoding,
18    /// Whether the relocation is relative
19    ///
20    /// `RelocationKind::Absolute/Relative/PltRelative` in the `object` crate.
21    /// Most often, this is `true` for jump target, and `false` for values.
22    relative: bool,
23    /// Size in bits
24    ///
25    /// Usually one just can't fit a `usize` into a relocation. Users might
26    /// need to use multiple holes for that and we will warn them using this
27    /// field.
28    size: u8,
29    /// Addend, extra value to add to the target before putting it in place.
30    ///
31    /// Again, we assume `i12` is enough here.
32    #[bits(12)]
33    addend: i16,
34    /// What this relocation is for
35    #[bits(4)]
36    patch_kind: PatchKind,
37    /// See [PatchInfo]
38    patch_id: u16,
39}
40
41#[derive(Copy, Clone)]
42#[repr(u8)]
43pub enum RelocationEncoding {
44    Invalid = 0,
45    /// Plain value
46    Generic,
47    /// Sign extended
48    X86Signed,
49    /// Upper limit
50    Unknown,
51}
52impl RelocationEncoding {
53    const fn from_bits(bits: u8) -> Self {
54        if bits >= Self::Unknown as u8 {
55            Self::Invalid
56        } else {
57            unsafe { core::mem::transmute::<u8, Self>(bits) }
58        }
59    }
60    const fn into_bits(self) -> u8 {
61        self as u8
62    }
63}
64
65#[derive(Copy, Clone)]
66#[repr(u8)]
67pub enum PatchKind {
68    Hole = 0,
69    Stack,
70    Target,
71    Unknown,
72}
73impl PatchKind {
74    const fn from_bits(bits: u8) -> Self {
75        if bits >= Self::Unknown as u8 {
76            Self::Unknown
77        } else {
78            unsafe { core::mem::transmute::<u8, Self>(bits) }
79        }
80    }
81    const fn into_bits(self) -> u8 {
82        self as u8
83    }
84}
85
86pub enum PatchInfo {
87    Hole(u16),
88    Stack(u16),
89    Target(u16),
90}
91
92impl Relocation {
93    pub fn is_invalid(&self) -> bool {
94        matches!(self.encoding(), RelocationEncoding::Invalid)
95    }
96
97    pub fn patch_info(&self) -> Option<PatchInfo> {
98        Some(match self.patch_kind() {
99            PatchKind::Hole => PatchInfo::Hole(self.patch_id()),
100            PatchKind::Stack => PatchInfo::Stack(self.patch_id()),
101            PatchKind::Target => PatchInfo::Target(self.patch_id()),
102            _ => return None,
103        })
104    }
105
106    pub fn supports_value(&self, value: usize) -> bool {
107        if self.relative() {
108            return false;
109        }
110        match self.encoding() {
111            RelocationEncoding::Generic => fits_unsigned(value, self.size()),
112            RelocationEncoding::X86Signed => fits_signed(value as isize, self.size()),
113            _ => false,
114        }
115    }
116
117    pub fn apply_raw(&self, dest: &mut [u8], value: usize) {
118        let value = value.wrapping_add_signed(self.addend() as isize);
119        let offset = self.offset();
120        let size = self.size();
121        match self.encoding() {
122            RelocationEncoding::Generic => {
123                let size = (size / 8) as usize;
124                dest[offset as usize..][..size].copy_from_slice(&value.to_le_bytes()[..size]);
125            }
126            RelocationEncoding::X86Signed => {
127                let size = (size / 8) as usize;
128                dest[offset as usize..][..size].copy_from_slice(&value.to_le_bytes()[..size]);
129            }
130            _ => unreachable!(),
131        }
132    }
133}
134
135fn fits_unsigned(value: usize, bits: u8) -> bool {
136    match bits {
137        0 => value == 0,
138        bits if bits as u32 >= usize::BITS => true,
139        bits => value < (1usize << bits),
140    }
141}
142
143fn fits_signed(value: isize, bits: u8) -> bool {
144    match bits {
145        0 => value == 0,
146        bits if bits as u32 >= isize::BITS => true,
147        bits => {
148            let shift = isize::BITS - bits as u32;
149            (value << shift >> shift) == value
150        }
151    }
152}
153
154#[derive(Copy, Clone)]
155pub enum JumpTarget {
156    /// To the next stencil
157    Next,
158    /// TODO: To a specific block id
159    Target(u16),
160}
161
162#[derive(Copy, Clone)]
163pub enum DelayedTarget {
164    Block(u16),
165    Constant(u16),
166    Next,
167}
168
169#[derive(Clone)]
170pub struct DelayedRelocation {
171    offset: usize,
172    relocation: Relocation,
173    target: DelayedTarget,
174}
175impl DelayedRelocation {
176    pub fn try_apply(
177        dest: &mut [u8],
178        offset: usize,
179        relocation: Relocation,
180        stack_vars: &[usize],
181        holes: &[usize],
182        jumps: &[JumpTarget],
183    ) -> Option<Self> {
184        // TODO: unwrap safety?
185        let mut value = match relocation.patch_info().unwrap() {
186            PatchInfo::Hole(i) => holes[i as usize],
187            PatchInfo::Stack(i) => stack_vars[i as usize],
188            PatchInfo::Target(i) => {
189                return Some(DelayedRelocation {
190                    offset,
191                    relocation,
192                    target: match jumps[i as usize] {
193                        JumpTarget::Next => DelayedTarget::Next,
194                        JumpTarget::Target(target) => DelayedTarget::Block(target),
195                    },
196                });
197            }
198        };
199
200        if relocation.relative() {
201            value -= offset + relocation.offset() as usize
202        }
203
204        relocation.apply_raw(&mut dest[offset..], value);
205        None
206    }
207
208    pub fn constant(offset: usize, relocation: Relocation, constant: u16) -> Self {
209        Self {
210            offset,
211            relocation,
212            target: DelayedTarget::Constant(constant),
213        }
214    }
215
216    pub fn next(offset: usize, relocation: Relocation) -> Self {
217        Self {
218            offset,
219            relocation,
220            target: DelayedTarget::Next,
221        }
222    }
223
224    pub fn target(&self) -> DelayedTarget {
225        self.target
226    }
227
228    pub fn offset(&self) -> usize {
229        self.offset
230    }
231
232    pub fn location(&self) -> usize {
233        self.offset + self.relocation.offset() as usize
234    }
235
236    pub fn resolve(&self, base: usize, value: usize) -> usize {
237        if self.relocation.relative() {
238            value.wrapping_sub(base + self.location())
239        } else {
240            value
241        }
242    }
243
244    pub fn apply(&self, dest: &mut [u8], value: usize) {
245        self.relocation.apply_raw(&mut dest[self.offset..], value);
246    }
247}