patchouly_core/
relocation.rs1use bitfields::bitfield;
2
3#[bitfield(u64)]
8#[derive(Copy, Clone, PartialEq, Eq, Hash)]
9pub struct Relocation {
10 offset: u16,
15 #[bits(7)]
17 encoding: RelocationEncoding,
18 relative: bool,
23 size: u8,
29 #[bits(12)]
33 addend: i16,
34 #[bits(4)]
36 patch_kind: PatchKind,
37 patch_id: u16,
39}
40
41#[derive(Copy, Clone)]
42#[repr(u8)]
43pub enum RelocationEncoding {
44 Invalid = 0,
45 Generic,
47 X86Signed,
49 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 Next,
158 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 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}