dynasmrt/
aarch64.rs

1//! Runtime support for the aarch64 architecture assembling target.
2//!
3//! The aarch64 instruction set features fixed-width 32-bit instructions and relative relocations up to 28 bits in size.
4//!
5//! The core relocation behaviour for this architecture is provided by the [`Aarch64Relocation`] type.
6//!
7//! Next to that, this module contains the following:
8//!
9//! ## Type aliases
10//!
11//! Several specialized type aliases of the generic [`Assembler`] are provided as these are by far the most common usecase.
12//!
13//! ## Enums
14//!
15//! There are enumerations of every logically distinct register family usable in aarch64.
16//! These enums implement the [`Register`] trait and their discriminant values match their numeric encoding in dynamic register literals.
17//!
18//! *Note: The presence of some registers listed here is purely what is encodable. Check the relevant architecture documentation to find what is architecturally valid.*
19//!
20//! ## Functions
21//!
22//! The aarch64 architecture allows encoding several special types of immediates. The encoding implementations for these immediate types have been exposed to assist the user
23//! in correctly using these instructions. They will return `Some(encoding)` only if the given value can be encoded losslessly in that immediate type.
24
25use crate::Register;
26use crate::relocations::{Relocation, RelocationSize, RelocationKind, ImpossibleRelocation, fits_signed_bitfield};
27use byteorder::{ByteOrder, LittleEndian};
28use std::convert::TryFrom;
29
30/// Relocation implementation for the aarch64 architecture.
31#[derive(Debug, Clone)]
32#[allow(missing_docs)]
33pub enum Aarch64Relocation {
34    // b, bl 26 bits, dword aligned
35    B,
36    // b.cond, cbnz, cbz, ldr, ldrsw, prfm: 19 bits, dword aligned
37    BCOND,
38    // adr split 21 bit, byte aligned
39    ADR,
40    // adrp split 21 bit, 4096-byte aligned
41    ADRP,
42    // tbnz, tbz: 14 bits, dword aligned
43    TBZ,
44    // Anything in directives
45    Plain(RelocationSize),
46}
47
48impl Aarch64Relocation {
49    fn op_mask(&self) -> u32 {
50        match self {
51            Self::B => 0xFC00_0000,
52            Self::BCOND => 0xFF00_001F,
53            Self::ADR => 0x9F00_001F,
54            Self::ADRP => 0x9F00_001F,
55            Self::TBZ => 0xFFF8_001F,
56            Self::Plain(_) => 0
57        }
58    }
59
60    fn encode(&self, value: isize) -> Result<u32, ImpossibleRelocation> {
61        let value = i64::try_from(value).map_err(|_| ImpossibleRelocation { } )?;
62        Ok(match self {
63            Self::B => {
64                if value & 3 != 0 || !fits_signed_bitfield(value >> 2, 26) {
65                    return Err(ImpossibleRelocation { } );
66                }
67                let value = (value >> 2) as u32;
68                value & 0x3FF_FFFF
69            },
70            Self::BCOND => {
71                if value & 3 != 0 || !fits_signed_bitfield(value >> 2, 19) {
72                    return Err(ImpossibleRelocation { } );
73                }
74                let value = (value >> 2) as u32;
75                (value & 0x7FFFF) << 5
76            },
77            Self::ADR => {
78                if !fits_signed_bitfield(value, 21) {
79                    return Err(ImpossibleRelocation { } );
80                }
81                let low = (value) as u32;
82                let high = (value >> 2) as u32;
83                ((high & 0x7FFFF) << 5) | ((low & 3) << 29)
84            },
85            Self::ADRP => {
86                let value = value + 0xFFF;
87                if !fits_signed_bitfield(value >> 12, 21) {
88                    return Err(ImpossibleRelocation { } );
89                }
90                let low = (value >> 12) as u32;
91                let high = (value >> 14) as u32;
92                ((high & 0x7FFFF) << 5) | ((low & 3) << 29)
93            },
94            Self::TBZ => {
95                if value & 3 != 0 || !fits_signed_bitfield(value >> 2, 14) {
96                    return Err(ImpossibleRelocation { } );
97                }
98                let value = (value >> 2) as u32;
99                (value & 0x3FFF) << 5
100            },
101            Self::Plain(_) => return Err(ImpossibleRelocation { } )
102        })
103    }
104}
105
106impl Relocation for Aarch64Relocation {
107    type Encoding = (u8,);
108    fn from_encoding(encoding: Self::Encoding) -> Self {
109        match encoding.0 {
110            0 => Self::B,
111            1 => Self::BCOND,
112            2 => Self::ADR,
113            3 => Self::ADRP,
114            4 => Self::TBZ,
115            x  => Self::Plain(RelocationSize::from_encoding(x - 4))
116        }
117    }
118    fn from_size(size: RelocationSize) -> Self {
119        Self::Plain(size)
120    }
121    fn size(&self) -> usize {
122        match self {
123            Self::Plain(s) => s.size(),
124            _ => RelocationSize::DWord.size(),
125        }
126    }
127    fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> {
128        if let Self::Plain(s) = self {
129            return s.write_value(buf, value);
130        };
131
132        let mask = self.op_mask();
133        let template = LittleEndian::read_u32(buf) & mask;
134
135        let packed = self.encode(value)?;
136
137        LittleEndian::write_u32(buf, template | packed);
138        Ok(())
139    }
140    fn read_value(&self, buf: &[u8]) -> isize {
141        if let Self::Plain(s) = self {
142            return s.read_value(buf);
143        };
144
145        let mask = !self.op_mask();
146        let value = LittleEndian::read_u32(buf);
147        let unpacked = match self {
148            Self::B => u64::from(
149                value & mask
150            ) << 2,
151            Self::BCOND => u64::from(
152                (value & mask) >> 5
153            ) << 2,
154            Self::ADR  => u64::from(
155                (((value >> 5 ) & 0x7FFFF) << 2) |
156                ((value >> 29) & 3 )
157            ),
158            Self::ADRP => u64::from(
159                (((value >> 5 ) & 0x7FFFF) << 2) |
160                ((value >> 29) & 3 )
161            ) << 12,
162            Self::TBZ => u64::from(
163                (value & mask) >> 5
164            ) << 2,
165            Self::Plain(_) => unreachable!()
166        };
167
168        // Sign extend.
169        let bits = match self {
170            Self::B => 26,
171            Self::BCOND => 19,
172            Self::ADR => 21,
173            Self::ADRP => 33,
174            Self::TBZ => 14,
175            Self::Plain(_) => unreachable!()
176        };
177        let offset = 1u64 << (bits - 1);
178        let value: u64 = (unpacked ^ offset).wrapping_sub(offset);
179
180        value as i64 as isize
181    }
182    fn kind(&self) -> RelocationKind {
183        RelocationKind::Relative
184    }
185    fn page_size() -> usize {
186        4096
187    }
188}
189
190/// An aarch64 Assembler. This is aliased here for backwards compatability.
191pub type Assembler = crate::Assembler<Aarch64Relocation>;
192/// An aarch64 AssemblyModifier. This is aliased here for backwards compatability.
193pub type AssemblyModifier<'a> = crate::Modifier<'a, Aarch64Relocation>;
194/// An aarch64 UncommittedModifier. This is aliased here for backwards compatability.
195pub type UncommittedModifier<'a> = crate::UncommittedModifier<'a>;
196
197
198// these should explicitly never be inlined, as this is the slow path.
199// that's also why these aren't made generic.
200
201/// Handler for `f32` out-of-range aarch64 immediates.
202#[inline(never)]
203pub fn immediate_out_of_range_unsigned_f32(immediate: f32) -> ! {
204    panic!("Cannot assemble this Aarch64 instruction. Immediate {immediate} is out of range.")
205}
206
207/// Handler for `u64` out-of-range aarch64 immediates.
208#[inline(never)]
209pub fn immediate_out_of_range_unsigned_64(immediate: u64) -> ! {
210    panic!("Cannot assemble this Aarch64 instruction. Immediate {immediate} is out of range.")
211}
212
213/// Handler for `u32` out-of-range aarch64 immediates.
214#[inline(never)]
215pub fn immediate_out_of_range_unsigned_32(immediate: u32) -> ! {
216    panic!("Cannot assemble this Aarch64 instruction. Immediate {immediate} is out of range.")
217}
218
219/// Handler for `i32` out-of-range aarch64 immediates.
220#[inline(never)]
221pub fn immediate_out_of_range_signed_32(immediate: i32) -> ! {
222    panic!("Cannot assemble this Aarch64 instruction. Immediate {immediate} is out of range.")
223}
224
225
226/// Helper function for validating that a given value can be encoded as a 32-bit logical immediate
227pub fn encode_logical_immediate_32bit(value: u32) -> Option<u16> {
228    let transitions = value ^ value.rotate_right(1);
229    let element_size = (64u32).checked_div(transitions.count_ones())?;
230
231    // confirm that the elements are identical
232    if value != value.rotate_left(element_size) {
233        return None;
234    }
235
236    let element = value & 1u32.checked_shl(element_size).unwrap_or(0).wrapping_sub(1);
237    let ones = element.count_ones();
238    let imms = (!((element_size << 1) - 1) & 0x3F) | (ones - 1);
239
240    let immr = if (element & 1) != 0 {
241        ones - (!element).trailing_zeros()
242    } else {
243        element_size - element.trailing_zeros()
244    };
245
246    Some(((immr as u16) << 6) | (imms as u16))
247}
248
249/// Helper function for validating that a given value can be encoded as a 64-bit logical immediate
250pub fn encode_logical_immediate_64bit(value: u64) -> Option<u16> {
251    let transitions = value ^ value.rotate_right(1);
252    let element_size = (128u32).checked_div(transitions.count_ones())?;
253
254    // confirm that the elements are identical
255    if value != value.rotate_left(element_size) {
256        return None;
257    }
258
259    let element = value & 1u64.checked_shl(element_size).unwrap_or(0).wrapping_sub(1);
260    let ones = element.count_ones();
261    let imms = (!((element_size << 1) - 1) & 0x7F) | (ones - 1);
262
263    let immr = if (element & 1) != 0 {
264        ones - (!element).trailing_zeros()
265    } else {
266        element_size - element.trailing_zeros()
267    };
268
269    let n = imms & 0x40 == 0;
270    let imms = imms & 0x3F;
271
272    Some(((n as u16) << 12) | ((immr as u16) << 6) | (imms as u16))
273}
274
275/// Helper function for validating that a given value can be encoded as a floating point immediate
276pub fn encode_floating_point_immediate(value: f32) -> Option<u8> {
277    // floating point ARM immediates are encoded as
278    // abcdefgh => aBbbbbbc defgh000 00000000 00000000
279    // where B = !b
280    // which means we can just slice out "a" and "bcdefgh" and assume the rest was correct
281
282    let bits = value.to_bits();
283
284    let check = (bits >> 25) & 0x3F;
285    if (check == 0b10_0000 || check == 0b01_1111) && (bits & 0x7_FFFF) == 0 {
286        Some((((bits >> 24) & 0x80) | ((bits >> 19) & 0x7F)) as u8)
287    } else {
288        None
289    }
290}
291
292
293/// 4 or 8-byte general purpopse registers, where X31 is the zero register.
294#[allow(missing_docs)]
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
296pub enum RX {
297    X0 = 0x00, X1 = 0x01, X2 = 0x02, X3 = 0x03,
298    X4 = 0x04, X5 = 0x05, X6 = 0x06, X7 = 0x07,
299    X8 = 0x08, X9 = 0x09, X10= 0x0A, X11= 0x0B,
300    X12= 0x0C, X13= 0x0D, X14= 0x0E, X15= 0x0F,
301    X16= 0x10, X17= 0x11, X18= 0x12, X19= 0x13,
302    X20= 0x14, X21= 0x15, X22= 0x16, X23= 0x17,
303    X24= 0x18, X25= 0x19, X26= 0x1A, X27= 0x1B,
304    X28= 0x1C, X29= 0x1D, X30= 0x1E, XZR= 0x1F,
305}
306reg_impls!(RX);
307
308/// 0x1F addresses both XZR and SP (disambiguated by context). This enum is a mirror of RX just
309/// with the SP in place of XZR.
310#[allow(missing_docs)]
311#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
312pub enum RXSP {
313    X0 = 0x00, X1 = 0x01, X2 = 0x02, X3 = 0x03,
314    X4 = 0x04, X5 = 0x05, X6 = 0x06, X7 = 0x07,
315    X8 = 0x08, X9 = 0x09, X10= 0x0A, X11= 0x0B,
316    X12= 0x0C, X13= 0x0D, X14= 0x0E, X15= 0x0F,
317    X16= 0x10, X17= 0x11, X18= 0x12, X19= 0x13,
318    X20= 0x14, X21= 0x15, X22= 0x16, X23= 0x17,
319    X24= 0x18, X25= 0x19, X26= 0x1A, X27= 0x1B,
320    X28= 0x1C, X29= 0x1D, X30= 0x1E, SP = 0x1F,
321}
322reg_impls!(RXSP);
323
324/// 1, 2, 4, 8 or 16-bytes scalar FP / vector SIMD registers. 
325#[allow(missing_docs)]
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
327pub enum RV {
328    V0 = 0x00, V1 = 0x01, V2 = 0x02, V3 = 0x03,
329    V4 = 0x04, V5 = 0x05, V6 = 0x06, V7 = 0x07,
330    V8 = 0x08, V9 = 0x09, V10= 0x0A, V11= 0x0B,
331    V12= 0x0C, V13= 0x0D, V14= 0x0E, V15= 0x0F,
332    V16= 0x10, V17= 0x11, V18= 0x12, V19= 0x13,
333    V20= 0x14, V21= 0x15, V22= 0x16, V23= 0x17,
334    V24= 0x18, V25= 0x19, V26= 0x1A, V27= 0x1B,
335    V28= 0x1C, V29= 0x1D, V30= 0x1E, V31= 0x1F,
336}
337reg_impls!(RV);
338
339#[cfg(test)]
340mod tests {
341    use super::RX::*;
342    use crate::Register;
343
344    #[test]
345    fn reg_code() {
346        assert_eq!(X2.code(), 2);
347    }
348
349    #[test]
350    fn reg_code_from() {
351        assert_eq!(u8::from(X24), 0x18);
352    }
353}