From 39f183cb5ea8f5514a0010b9b9a43f2d449567cd Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 2 Jan 2022 00:01:55 -0800 Subject: document the crate --- CHANGELOG | 7 ++ src/armv7.rs | 141 ++++++++++++++++++++- src/armv8/a64.rs | 356 +++++++++++++++++++++++++++++++++++++++++------------- src/armv8/mod.rs | 1 + src/lib.rs | 50 ++++++++ test/armv8/a64.rs | 14 +-- 6 files changed, 478 insertions(+), 91 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fb1addc..6054a16 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +## 0.2.0 +* added documentation to all public members. +* fix tests incorrectly expecting `lsl #0` in memory operands where it would have no effect +* remove some redundant `a64` operand kinds: + - `RegOffset` is now `RegPreIndex` with no writeback. + - `Offset` was a duplicate of `PCOffset`, and all uses have been so-renamed. + ## 0.1.4 * add or fix many a64 instructions, extend support up through armv8.5. - some disassembly and optional elements of instruction text does not match diff --git a/src/armv7.rs b/src/armv7.rs index 5f826ac..c2254a5 100644 --- a/src/armv7.rs +++ b/src/armv7.rs @@ -12,6 +12,10 @@ use yaxpeax_arch::{Arch, AddressDiff, Colorize, Decoder, LengthedInstruction, Re mod thumb; // opcode, s, w, cond +/// a struct for the combined display of an opcode and possible suffixes. +/// +/// this includes the opcode, its optional `.s` suffix, optional `.w` suffix, and condition code, +/// if any. pub struct ConditionedOpcode(pub Opcode, pub bool, pub bool, pub ConditionCode); impl Display for ConditionedOpcode { @@ -20,6 +24,9 @@ impl Display for ConditionedOpcode { } } +/// a context impl to display `arm` instructions with no additional context (no symbol name +/// information, offset names, etc). this impl results in `some_instruction.contextualize(...)` +/// displaying an instruction the same way its `Display` impl would. pub struct NoContext; fn reg_name_colorize(reg: Reg, colors: &Y) -> impl fmt::Display { @@ -850,6 +857,7 @@ impl Display for Opcode { #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(non_camel_case_types)] +#[allow(missing_docs)] pub enum Opcode { Invalid, /* @@ -1088,6 +1096,8 @@ static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ Opcode::MVN ]; +/// a struct describiing a shifted register operand. this is primarily interesting in that it can +/// be translated to a `RegShiftStyle` for further interpretation. #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[repr(transparent)] pub struct RegShift { @@ -1103,26 +1113,37 @@ impl RegShift { } } + /// don't use this. it's for armv7 testing only. + #[doc(hidden)] pub fn from_raw(data: u16) -> Self { RegShift { data } } } +/// an enum describing one of two ways a shifted register operand may be shifted. pub enum RegShiftStyle { + /// a register shifted by an immediate. RegImm(RegImmShift), + /// a register shifted by a register. RegReg(RegRegShift), } +/// a register shifted by a register. #[repr(transparent)] pub struct RegRegShift { data: u16 } +/// the way a shift operation is carried out. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ShiftStyle { + /// left-shift the value, filling in zeroes. LSL = 0, + /// right-shift the value, filling in zeroes. LSR = 1, + /// arithmetic shift right, filling with the top bit of the value (sign-extending). ASR = 2, + /// rotate-right, filling with bits shifted out of the value. ROR = 3, } @@ -1150,34 +1171,42 @@ impl ShiftStyle { } impl RegRegShift { + /// the general-purpose register, an amount to shift the shiftee. pub fn shifter(&self) -> Reg { Reg::from_u8((self.data >> 8) as u8 & 0b1111) } + /// the way in which this register is shifted. pub fn stype(&self) -> ShiftStyle { ShiftStyle::from((self.data >> 5) as u8 & 0b11) } + /// the general-purpose register to be shifted. pub fn shiftee(&self) -> Reg { Reg::from_u8(self.data as u8 & 0b1111) } } +/// a register shifted by an immediate. #[repr(transparent)] pub struct RegImmShift { data: u16 } impl RegImmShift { + /// the immediate this register is shifted by. pub fn imm(&self) -> u8 { (self.data >> 7) as u8 & 0b11111 } + /// the way in which this register is shifted. pub fn stype(&self) -> ShiftStyle { ShiftStyle::from((self.data >> 5) as u8 & 0b11) } + /// the general-purpose register to be shifted. pub fn shiftee(&self) -> Reg { Reg::from_u8(self.data as u8 & 0b1111) } } +/// a struct describing an `arm` register. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(transparent)] pub struct Reg { @@ -1252,6 +1281,9 @@ impl Reg { } } + /// create a new `Reg` with the specified number. + /// + /// panics if `bits` is out of range (16 or above). pub fn from_u8(bits: u8) -> Reg { if bits > 0b1111 { panic!("register number out of range"); @@ -1260,11 +1292,13 @@ impl Reg { Reg { bits } } + /// get the number of this register. the returned value will be between 0 and 15. pub fn number(&self) -> u8 { self.bits } } +/// a control register. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(transparent)] pub struct CReg { @@ -1278,6 +1312,9 @@ impl Display for CReg { } impl CReg { + /// create a new `CReg` with the specified number. + /// + /// panics if `bits` is out of range (16 or above). pub fn from_u8(bits: u8) -> CReg { if bits > 0b1111 { panic!("register number out of range"); @@ -1286,6 +1323,7 @@ impl CReg { CReg { bits } } + /// get the number of this register. the returned value will be between 0 and 15. pub fn number(&self) -> u8 { self.bits } @@ -1331,6 +1369,7 @@ impl Display for StatusRegMask { #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] +#[allow(missing_docs)] pub enum StatusRegMask { // Note 0b0000 is unused (as is 0b10000) CPSR_C = 0b0001, @@ -1409,36 +1448,82 @@ impl StatusRegMask { } } +/// an operand in an `arm` instruction. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Operand { + /// a general-purpose register. Reg(Reg), + /// a general-purpose register, with writeback. these generally imply an increment by width of + /// a memort operand, depending on the instruction. RegWBack(Reg, bool), + /// a list of registers specified as a bitmask from bits 0 to 15. RegList(u16), + /// a memory access, dereferencing a general-purpose register. RegDeref(Reg), + /// a memory access, dereferencing a shifted general-purpose register with register or + /// immediate offset. RegShift(RegShift), + /// a memory access of a register, post-indexed a register shifted by register or immediate. + /// the first bool indicates if the shifted-register is added or subtracted ot the base + /// register, while the second bool indicates if the resulting address is written back to the + /// base register. RegDerefPostindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback + /// a memory access of a register, pre-indexed with a register shifted by register or + /// immediate. the first bool indicates if the shifted-register is added or subtracted ot the + /// base register, while the second bool indicates if the resulting address is written back to + /// the base register. RegDerefPreindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback + /// a memory access of a register, post-indexed with an immediate. the first bool indicates if + /// the shifted-register is added or subtracted ot the base register, while the second bool + /// indicates if the resulting address is written back to the base register. RegDerefPostindexOffset(Reg, u16, bool, bool), // add/sub, wback + /// a memory access of a register, pre-indexed with an immediate. the first bool indicates if + /// the shifted-register is added or subtracted ot the base register, while the second bool + /// indicates if the resulting address is written back to the base register. RegDerefPreindexOffset(Reg, u16, bool, bool), // add/sub, wback + /// a memory access of a register, post-indexed with a register. the first bool indicates if the + /// shifted-register is added or subtracted ot the base register, while the second bool + /// indicates if the resulting address is written back to the base register. RegDerefPostindexReg(Reg, Reg, bool, bool), // add/sub, wback + /// a memory access of a register, pre-indexed with a register. the first bool indicates if the + /// shifted-register is added or subtracted ot the base register, while the second bool + /// indicates if the resulting address is written back to the base register. RegDerefPreindexReg(Reg, Reg, bool, bool), // add/sub, wback + /// a 12-bit immediate, stored in a `u16`. Imm12(u16), + /// a 32-bit immediate, stored in a `u32`. Imm32(u32), + /// a pc-relative branch, with 32-bit signed offset, left-shifted by 2. BranchOffset(i32), + /// a pc-relative branch, with 32-bit signed offset, left-shifted by 1. BranchThumbOffset(i32), + /// a coprocessor index. Coprocessor(u8), + /// a coprocessor option number. CoprocOption(u8), + /// an `arm` control register. CReg(CReg), + /// an `arm` banked register, either `usr` (general-purpose) bank or one of the alternate sets + /// of `arm` registers. BankedReg(Bank, Reg), + /// `spsr` in some `arm` register bank. BankedSPSR(Bank), + /// a mask of bits for the `spsr` register. StatusRegMask(StatusRegMask), + /// the `apsr` register. APSR, + /// the `spsr` register. SPSR, + /// the `cpsr` register. CPSR, + /// "no operand". since an instruction's `operands` array is always four entries, this is used + /// to fill space, if any, after recording an instruction's extant operands. Nothing, } +/// a register bank for a register in `armv7` or below. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[allow(missing_docs)] pub enum Bank { Usr, Fiq, @@ -1551,10 +1636,16 @@ impl Colorize for Operand { } } +/// a `armv7` or below instruction. #[derive(Debug, PartialEq, Eq)] pub struct Instruction { + /// the condition code for this instruction, defaults to `AL` if the instruction is + /// unconditional. pub condition: ConditionCode, + /// the opcode of this instruction. pub opcode: Opcode, + /// operands for the decoded instruction. operands are populated from index 0, to 1, 2, and 3. + /// operands from the instruction are non-`Operand::Nothing`. pub operands: [Operand; 4], /// does this instruction update flags, while variants that do not update flags exist? pub s: bool, @@ -1566,14 +1657,28 @@ pub struct Instruction { pub thumb: bool, } +/// the kinds of errors possibly encountered in trying to decode an `armv7` or below instruction. #[derive(Debug, PartialEq, Copy, Clone)] pub enum DecodeError { + /// the input was insufficient to decode a full instruction. for non-thumb instructions, this means + /// the input was not at least four bytes long. for thumb instructions, the input was either + /// not two bytes, or not four bytes, depending on how much the instruction would need. ExhaustedInput, + /// the instruction encodes an opcode that is not valid. InvalidOpcode, + /// the instruction encodes an operand that is not valid. InvalidOperand, + /// `yaxpeax-arm` doesn't know how to decode this, but it may be a valid instruction. the + /// instruction decoder is not complete, sorry. :( + /// + /// in practice this typically indicates some kinds of coprocessor instruction, or `ARMv7` SIMD + /// instruction. Incomplete, + /// the instruction includes reserved bits that were not set as required. Nonconforming, + /// the input encodes an instruction that is explicitly undefined. Undefined, + /// the input encodes an instruction with unpredictable behavior. Unpredictable, } @@ -1641,18 +1746,23 @@ impl Instruction { fn set_s(&mut self, value: bool) { self.s = value; } + /// does this instruction set status flags? pub fn s(&self) -> bool { self.s } pub(crate) fn set_w(&mut self, value: bool) { self.thumb_w = value; } + /// was this instruction encoded in `thumb` mode and still 4 bytes, *and* requires a `.w` + /// suffix on the opcode? pub fn w(&self) -> bool { self.thumb_w } pub(crate) fn set_wide(&mut self, value: bool) { self.wide = value; } + /// was this instruction encoded in `thumb` mode and still 4 bytes? pub fn wide(&self) -> bool { self.wide } pub(crate) fn set_thumb(&mut self, value: bool) { self.thumb = value; } + /// was this instruction encoded in `thumb` mode? pub fn thumb(&self) -> bool { self.thumb } } @@ -1769,7 +1879,9 @@ impl LengthedInstruction for Instruction { } } +/// a condition code for am `armv7` or below instruction. #[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[allow(missing_docs)] pub enum ConditionCode { EQ, NE, @@ -1811,7 +1923,7 @@ impl Display for ConditionCode { } impl ConditionCode { - pub fn build(value: u8) -> ConditionCode { + fn build(value: u8) -> ConditionCode { match value { 0b0000 => ConditionCode::EQ, 0b0001 => ConditionCode::NE, @@ -1918,6 +2030,14 @@ impl Default for ARMVersion { } // nothing checks/rejects by arm version yet, but.. soon.... +/// a struct with decode configuration for `ARMv7` and below. the same decoder is used for `thumb` +/// and non-`thumb` modes, and the same instruction struct is used for decoded instructions in +/// either mode. +/// +/// NOTE: helper functions here create `InstDecoder` for specific revisions, extensions, or lack +/// thereof, in the supported instruction set. `yaxpeax-arm` does not actually honor these settings +/// yet. this means any `InstDecoder` will decode all known instructions through the latest `ARMv7` +/// extensions. #[allow(unused)] #[derive(Debug)] pub struct InstDecoder { @@ -1939,19 +2059,28 @@ impl Default for InstDecoder { } impl InstDecoder { + /// set the decoder to decoding in thumb mode as the specified bool provides; `true` means + /// "yes, decode in `thumb` mode", where `false` means to decode as a normal `arm` instruction. pub fn set_thumb_mode(&mut self, thumb: bool) { self.thumb = thumb; } + /// set the decoder to decoding in thumb mode as the specified bool provides; `true` means + /// "yes, decode in `thumb` mode", where `false` means to decode as a normal `arm` instruction. + /// + /// (this consumes and returns the `InstDecoder` to support use in chained calls.)` pub fn with_thumb_mode(mut self, thumb: bool) -> Self { self.set_thumb_mode(thumb); self } + /// initialize a new `arm` `InstDecoder` with default ("everything") support, but in `thumb` + /// mode. pub fn default_thumb() -> Self { Self::default().with_thumb_mode(true) } + /// create an `InstDecoder` that supports only instructions through to `ARMv4`. pub fn armv4() -> Self { Self { mode: DecodeMode::Any, @@ -1961,6 +2090,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv5`. pub fn armv5() -> Self { Self { mode: DecodeMode::Any, @@ -1970,6 +2100,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv6`. pub fn armv6() -> Self { Self { mode: DecodeMode::Any, @@ -1979,6 +2110,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv6t2`. pub fn armv6t2() -> Self { Self { mode: DecodeMode::Any, @@ -1988,6 +2120,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv6t2` in thumb mode. pub fn armv6t2_thumb() -> Self { Self { mode: DecodeMode::Any, @@ -1997,6 +2130,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv7`. pub fn armv7() -> Self { Self { mode: DecodeMode::Any, @@ -2006,6 +2140,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv7` in thumb mode. pub fn armv7_thumb() -> Self { Self { mode: DecodeMode::Any, @@ -2015,6 +2150,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv7ve`. pub fn armv7ve() -> Self { Self { mode: DecodeMode::Any, @@ -2024,6 +2160,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv7ve` in thumb mode. pub fn armv7ve_thumb() -> Self { Self { mode: DecodeMode::Any, @@ -2033,6 +2170,7 @@ impl InstDecoder { } } + /// create an `InstDecoder` that supports only instructions through to `ARMv7vese`. pub fn armv7vese() -> Self { Self { mode: DecodeMode::Any, @@ -3709,6 +3847,7 @@ impl Decoder for InstDecoder { } } +/// a struct with a summary of the `ARMv7` instruction set in an associated `impl Arch for ARMv7`. #[cfg_attr(feature="use-serde", derive(Serialize, Deserialize))] #[derive(Debug)] pub struct ARMv7; diff --git a/src/armv8/a64.rs b/src/armv8/a64.rs index 0ca7bff..dc7d399 100644 --- a/src/armv8/a64.rs +++ b/src/armv8/a64.rs @@ -161,11 +161,21 @@ mod docs { } } +/// the kinds of errors possibly encountered in trying to decode an `aarch64` instruction #[derive(Debug, PartialEq, Copy, Clone)] pub enum DecodeError { + /// the input was insufficient to decode a full instruction. for `a64` instructions, this means + /// the input was not at least four bytes long. ExhaustedInput, + /// the instruction encodes an opcode that is not valid. InvalidOpcode, + /// the instruction encodes an operand that is not valid. InvalidOperand, + /// `yaxpeax-arm` doesn't know how to decode this, but it may be a valid instruction. the + /// instruction decoder is not complete, sorry. :( + /// + /// in practice this means that the instruction is likely either in the `SVE` or `SVE2` + /// additions. IncompleteDecoder, } @@ -210,6 +220,9 @@ impl yaxpeax_arch::Instruction for Instruction { fn well_defined(&self) -> bool { true } } +/// a context impl to display `a64` instructions with no additional context (no symbol name +/// information, offset names, etc). this impl results in `some_instruction.contextualize(...)` +/// displaying an instruction the same way its `Display` impl would. pub struct NoContext; impl ShowContextual for Instruction { @@ -218,10 +231,14 @@ impl ShowContextual for Inst } } +/// a struct with a summary of the `ARMv8`/`aarch64` instruction set in an associated `impl Arch +/// for ARMv8`. #[cfg(feature="use-serde")] #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct ARMv8 { } +/// a struct with a summary of the `ARMv8`/`aarch64` instruction set in an associated `impl Arch +/// for ARMv8`. #[cfg(not(feature="use-serde"))] #[derive(Copy, Clone, Debug)] pub struct ARMv8 { } @@ -235,13 +252,31 @@ impl Arch for ARMv8 { type Operand = Operand; } +/// a size marker for a register. #[derive(Copy, Clone, Debug, PartialEq)] #[repr(u8)] -pub enum SizeCode { X, W } +pub enum SizeCode { + /// an x-size (64-bits) register. + X, + /// a w-size (32-bits) register. + W +} +/// a size marker for a SIMD register, a full scalar size, or vector element size. #[derive(Copy, Clone, Debug, PartialEq)] #[repr(u8)] -pub enum SIMDSizeCode { B, H, S, D, Q } +pub enum SIMDSizeCode { + /// a byte (8-bits) scalar or element. + B, + /// a halfword (16-bits) scalar or element. + H, + /// a single(?) word (32-bits) scalar or element. + S, + /// a dword (64-bits) scalar or element. + D, + /// a qword (128-bits) scalar or element. + Q +} impl SIMDSizeCode { fn width(&self) -> u16 { @@ -265,10 +300,14 @@ impl SIMDSizeCode { } } +/// an `aarch64` instruction. #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Instruction { + /// the operation of the decoded instruction. pub opcode: Opcode, + /// operands for the decoded instruction. operands are populated from index 0, to 1, 2, and 3. + /// operands from the instruction are non-`Operand::Nothing`. pub operands: [Operand; 4], } @@ -1078,6 +1117,10 @@ impl Default for Instruction { } } +/// a descriptor for the operation in a `sys` or `sysl` instruction. +/// +/// there are two fields of interest, `op1` and `op2`. for a description of how to interpret these +/// fields of the `sys{,l}` opcodes, consult the `ARM Reference Manual`. #[derive(Copy, Clone, Debug, PartialEq)] #[repr(transparent)] pub struct SysOps { @@ -1091,10 +1134,12 @@ impl SysOps { } } + /// retrieve the `op1` field encoded in this `SysOps`. #[inline] pub fn op1(&self) -> u8 { self.data & 0b1111 } + /// retrieve the `op2` field encoded in this `SysOps`. #[inline] pub fn op2(&self) -> u8 { (self.data >> 4) & 0b1111 @@ -1103,6 +1148,7 @@ impl SysOps { #[derive(Copy, Clone, Debug, PartialEq)] #[repr(u16)] +#[allow(missing_docs)] pub enum Opcode { Invalid, MOVN, @@ -2609,19 +2655,32 @@ impl Display for Opcode { } } +/// the way a shift operation is carried out. #[derive(Copy, Clone, Debug, PartialEq)] pub enum ShiftStyle { + /// left-shift the value, filling in zeroes. LSL, + /// right-shift the value, filling in zeroes. LSR, + /// arithmetic shift right, filling with the top bit of the value (sign-extending). ASR, + /// rotate-right, filling with bits shifted out of the value. ROR, + /// unsigned extend byte to 64-bit word. UXTB, + /// unsigned extend half-word (16-bit) to 64-bit word. UXTH, + /// unsigned extend word (32-bit) to 64-bit word. UXTW, + /// unsigned extend 64-bit word to 64-bit word (functionally equivalent to left-shift). UXTX, + /// signed extend byte to 64-bit word. SXTB, + /// signed extend half-word (16-bit) to 64-bit word. SXTH, + /// signed extend word (32-bit) to 64-bit word. SXTW, + /// signed extend 64-bit word to 64-bit word (functionally equivalent to left-shift). SXTX, } @@ -2644,37 +2703,179 @@ impl Display for ShiftStyle { } } +/// an operand for an `aarch64` instruction. +/// +/// an instruction's `operands` array allows many more combination of `Operand` than are possible +/// in practice; no `aarch64` instruction has multiple `Operand::PCOffset` entries, for example. #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub enum Operand { + /// "no operand". since an instruction's `operands` array is always four entries, this is used + /// to fill space, if any, after recording an instruction's extant operands. Nothing, + /// a general-purpose register. + /// + /// for example, `x5` would be represented as `Operand::Register(SizeCode::X, 5)`. the register + /// number currently does not extend beyond 31. `Operand::Register(_, 31)` is the zero + /// register, either `wzr` or `xzr`. Register(SizeCode, u16), + /// a general-purpose register. + /// + /// for example, `x5` would be represented as `Operand::Register(SizeCode::X, 5)`. the register + /// number ranges between 0 and 31. unlike `Operand::Register`, `Operand::Register(_, 31)` is + /// the stack pointer, either `wsp` or `xsp`. RegisterPair(SizeCode, u16), + /// a SIMD register, used as a scalar element. + /// + /// for example, `d3` would be represented as `Operand::SIMDRegister(SIMDSizeCode::D, 3)`. the + /// register number ranges between 0 and 31. SIMDRegister(SIMDSizeCode, u16), + /// a SIMD register, used as a vector of elements. + /// + /// for example, `v10.4h` would be represented as + /// `Operand::SIMDRegisterElements(SIMDSizeCode::D, 10, SIMDSizeCode::H)`. + /// + /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have + /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31. SIMDRegisterElements(SIMDSizeCode, u16, SIMDSizeCode), + /// one lane in a SIMD vector of elements. + /// + /// for example, `v10.h[3]` would be represented as + /// `Operand::SIMDRegisterElementsLane(SIMDSizeCode::D, 10, SIMDSizeCode::H, 3)`. + /// + /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have + /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31. + /// the lane number can range between 0 and `element.width() / vector.width()`. SIMDRegisterElementsLane(SIMDSizeCode, u16, SIMDSizeCode, u8), + /// multiple lanes in a SIMD vector of elements. + /// + /// for example, `v10.4h[3]` would be represented as + /// `Operand::SIMDRegisterElementsLane(SIMDSizeCode::Q, 10, SIMDSizeCode::H, 3, 4)`. + /// + /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have + /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31. + /// the lane number can range between 0 and `element.width() / vector.width()`. the number of + /// elements is, in practice, always 4. SIMDRegisterElementsMultipleLane(SIMDSizeCode, u16, SIMDSizeCode, u8, u8), + /// multiple full SIMD vectors. + /// + /// for example, `{v2.2d, v3.2d, v4.2d}` would be represented as + /// `Operand::SIMDRegisterGroup(SIMDSizeCode::Q, 2, SIMDSizeCode::D, 3)`. + /// + /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have + /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31. + /// the number of SIMD registers ranges from 1 to 4. SIMDRegisterGroup(SIMDSizeCode, u16, SIMDSizeCode, u8), + /// a lane in multiple SIMD vectors. + /// + /// for example, `{v2.2d, v3.2d, v4.2d}[1]` would be represented as + /// `Operand::SIMDRegisterGroup(SIMDSizeCode::Q, 2, SIMDSizeCode::D, 3, 1)`. + /// + /// SIMD registers are either `SIMDSizeCode::Q` or `SIMDSizeCode::D` wide, and can have + /// elements of width `SIMDSizeCode::{B, H, S, D}`. the register number ranges between 0 and 31. + /// the number of SIMD registers ranges from 1 to 4. the lane number can range between 0 and + /// `element.width() / vector.width()`. SIMDRegisterGroupLane(u16, SIMDSizeCode, u8, u8), + /// a general-purpose register, where register 31 is `sp`, rather than `zr`. + /// + /// for example, `x5` would be represented as `Operand::Register(SizeCode::X, 5)`. the register + /// number currently does not extend beyond 31. `Operand::Register(_, 31)` is the stack pointer + /// register, either `wsp` or `xsp`. RegisterOrSP(SizeCode, u16), + /// a condition code describing some comparison. + /// + /// there are 16 condition codes with number 0 to 15: `eq, hs, mi, vs, hi, ge, gt, al, ne, lo, + /// pl, vc, ls, lt, le, nv`. the `nv` condition code is identical to `al`, and only has a + /// different condition code number in encoded instructions. ConditionCode(u8), - Offset(i64), + /// a pc-relative offset. + /// + /// this is the addressing mode for instructions like, `bl $+0x1234`, or `adrp $-0x123000`. PCOffset(i64), + /// a 32-bit immediate. + /// + /// this immediate may be signed or unsigned, depending on the instruction in question. if the + /// immediate is signed, it was sign-extended to 32-bits and that extended value is reported + /// here. Immediate(u32), - ImmediateDouble(f64), + /// a 64-bit immediate. + /// + /// this immediate may be signed or unsigned, depending on the instruction in question. if the + /// immediate is signed, it was sign-extended to 64-bits and that extended value is reported + /// here. Imm64(u64), + /// a 16-bit immediate. + /// + /// this immediate may be signed or unsigned, depending on the instruction in question. if the + /// immediate is signed, it was sign-extended to 16-bits and that extended value is reported + /// here. Imm16(u16), + /// a 64-bit floating-point immediate. + /// + /// the immediate as recorded in the instruction may be `f16`, `f32`, or `f64`, but is + /// converted to an `f64` for this operand. + ImmediateDouble(f64), + /// an immediate, a base number left-shifted by some amount. + /// + /// this is displayed as, for example, `#0x1234, lsl #12`. the base number may be any 16-bit + /// value, and can be shifted by up to 64 bits. ImmShift(u16, u8), + /// an immediate, a base number left-shifted by some amount, filling with 1's. + /// + /// this is displayed as, for example, `#0x1234, msl #12`. the base number may be any 16-bit + /// value, and can be shifted by up to 64 bits. ImmShiftMSL(u16, u8), + /// a register, shifted by some constant number of bits. + /// + /// this is displayed as, for example, `x15, lsl #3`. the operand would be represented by + /// `Operand::RegShift(ShiftStyle::LSL, 3, Sizecode::X, 15)`. RegShift(ShiftStyle, u8, SizeCode, u16), - RegOffset(u16, i16), + /// a memory access, with base register offset by another register shifted by some amount. + /// + /// this is displayed as, for example, `[x6, w9, lsl #5]`, and would be represented by + /// `Operand::RegRegOffset(6, 9, SizeCode::W, ShiftStyle::LSL, 5)`. RegRegOffset(u16, u16, SizeCode, ShiftStyle, u8), + /// a memory access, with a register base address and constant offset, and optional writeback. + /// + /// this is displayed as, for example: + /// * `[x13]` (no writeback, offset of 0) + /// - Offset::RegPreIndex(13, 0, false)` + /// * `[x13, #0x80]` (no writeback, offset of 0x80) + /// - Offset::RegPreIndex(13, 0x80, false)` + /// * `[x13, #0x80]!` (writeback, offset of 0x80) + /// - Offset::RegPreIndex(13, 0x80, true)` RegPreIndex(u16, i32, bool), + /// a memory access, with register base address and constant offset, base+offset written back + /// to the base register after access. + /// + /// this is displayed as, for example, `[x5], #-0x40`. the operand would be represented by + /// `Operand::RegPostIndex(5, -0x40)`. RegPostIndex(u16, i32), + /// a memory access, with register base address and register offset, base+offset written back + /// to the base register after access. + /// + /// this is displayed as, for example, `[x5], x9`. the operand would be represented by + /// `Operand::RegPostIndex(5, 9)`. RegPostIndexReg(u16, u16), + /// some kind of operation for a `prfm` or `prfum` operation. + /// + /// if the operation has a specified name, it will be `{pld,pli,pst}{l1,l2,l3}{keep,strm}`. + /// otherwise, the prefetch operation will be shown as the underlying integer from the encoded + /// instruction. PrefetchOp(u16), + /// a system register for an `msr` or `mrs` operation. + /// + /// if the register is a standard system register, and `yaxpeax-arm` knows about it, its name + /// will be used instead. otherwise, this will display as, for example, `sysreg:a6f4`. SystemReg(u16), + /// a control register for an `sys` or `sysl` operation. + /// + /// this operand will display as, for example, `cr5`. ControlReg(u16), + /// a selector for a field of the `pstate` control register. + /// + /// `yaxpeax-arm` does not name specific fields of `pstate` yet, so this operand displays as + /// `pstate.0x50`. PstateField(u8), } @@ -2738,7 +2939,7 @@ impl Display for Operand { Operand::PstateField(reg) => { // `MSR (immediate)` writes to the `PSTATE` register, setting a few bit patterns as // selected by `reg`. - write!(fmt, "pstate.{:x}", reg) + write!(fmt, "pstate.{:#x}", reg) } Operand::SIMDRegister(size, reg) => { match size { @@ -2834,13 +3035,6 @@ impl Display for Operand { _ => { unreachable!(); } } } - Operand::Offset(offs) => { - if *offs < 0 { - write!(fmt, "$-{:#x}", offs.wrapping_neg()) - } else { - write!(fmt, "$+{:#x}", offs) - } - } Operand::PCOffset(offs) => { if *offs < 0 { write!(fmt, "$-{:#x}", offs.wrapping_neg()) @@ -2906,17 +3100,6 @@ impl Display for Operand { } } } - Operand::RegOffset(reg, offset) => { - if *offset != 0 { - if *offset < 0 { - write!(fmt, "[{}, #-{:#x}]", Operand::RegisterOrSP(SizeCode::X, *reg), -*offset) - } else { - write!(fmt, "[{}, #{:#x}]", Operand::RegisterOrSP(SizeCode::X, *reg), offset) - } - } else { - write!(fmt, "[{}]", Operand::RegisterOrSP(SizeCode::X, *reg)) - } - } Operand::RegRegOffset(reg, index_reg, index_size, extend, amount) => { if extend == &ShiftStyle::LSL && *amount == 0 { write!(fmt, "[{}, {}]", Operand::RegisterOrSP(SizeCode::X, *reg), Operand::Register(*index_size, *index_reg)) @@ -2958,7 +3141,14 @@ impl Display for Operand { } } -#[derive(Default, Debug)] +/// an `aarch64` instruction decoder. +/// +/// there are no options or levels of decoding support, yet. as a result, any +/// `armv8::a64::InstDecoder` will decode as much of the a64 instruction set as is implemented. +/// +/// `InstDecoder` is currently zero-size, but users should not rely on this being the case in the +/// future. +#[derive(Default, Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] pub struct InstDecoder {} #[allow(non_snake_case)] @@ -7684,7 +7874,7 @@ impl Decoder for InstDecoder { let extended_offset = (offset << 11) >> 11; inst.operands = [ Operand::Register(SizeCode::X, (word & 0x1f) as u16), - Operand::Offset((extended_offset as i64) << 12), + Operand::PCOffset((extended_offset as i64) << 12), Operand::Nothing, Operand::Nothing ]; @@ -7694,7 +7884,7 @@ impl Decoder for InstDecoder { let extended_offset = (offset << 11) >> 11; inst.operands = [ Operand::Register(SizeCode::X, (word & 0x1f) as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing ]; @@ -8022,7 +8212,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::RegisterPair(size_code, Rs), Operand::RegisterPair(size_code, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; return Ok(()); @@ -8035,14 +8225,14 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::W, Rs), Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; } else if Lo1 == 0b10 { // load ops inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -8088,7 +8278,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::W, Rs), Operand::Register(size_code, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; match o0 { @@ -8102,7 +8292,7 @@ impl Decoder for InstDecoder { Operand::Register(SizeCode::W, Rs), Operand::Register(size_code, Rt), Operand::Register(size_code, Rt2), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), ]; match o0 { 0b0 => Opcode::STXP, @@ -8113,7 +8303,7 @@ impl Decoder for InstDecoder { 0b10 => { inst.operands = [ Operand::Register(size_code, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -8127,7 +8317,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(size_code, Rt), Operand::Register(size_code, Rt2), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; match o0 { @@ -8176,7 +8366,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(size_code, Rs), Operand::Register(size_code, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; return Ok(()); @@ -8219,7 +8409,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(size_code, Rt), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -8283,7 +8473,7 @@ impl Decoder for InstDecoder { let Rn = ((word >> 5) & 0b11111) as u16; let Rt = ((word >> 0) & 0b11111) as u16; - let simm = ((imm9 as i16) << 7) >> 7; + let simm = (((imm9 as i16) << 7) >> 7) as i32; let index = (size << 2) | opc; if index == 0b1011 || index >= 0b1110 { @@ -8306,7 +8496,7 @@ impl Decoder for InstDecoder { inst.opcode = *opcode; inst.operands = [ Operand::Register(size, Rt), - Operand::RegOffset(Rn, simm), + Operand::RegPreIndex(Rn, simm, false), Operand::Nothing, Operand::Nothing, ]; @@ -8321,7 +8511,7 @@ impl Decoder for InstDecoder { let Rn = ((word >> 5) & 0b11111) as u16; let Rt = ((word >> 0) & 0b11111) as u16; - let simm = ((imm9 as i16) << 7) >> 7; + let simm = (((imm9 as i16) << 7) >> 7) as i32; let opcode = &[ Opcode::STZGM, Opcode::STG, Opcode::STG, Opcode::STG, @@ -8337,7 +8527,7 @@ impl Decoder for InstDecoder { // ldg inst.operands = [ Operand::Register(SizeCode::X, Rt), - Operand::RegOffset(Rn, simm), + Operand::RegPreIndex(Rn, simm, false), Operand::Nothing, Operand::Nothing, ]; @@ -8354,13 +8544,13 @@ impl Decoder for InstDecoder { Operand::RegisterOrSP(SizeCode::X, Rt) }, if op2 == 0b00 { - Operand::RegOffset(Rn, 0) + Operand::RegPreIndex(Rn, 0, false) } else if op2 == 0b01 { - Operand::RegPostIndex(Rn, simm as i32) + Operand::RegPostIndex(Rn, simm) } else if op2 == 0b10 { - Operand::RegOffset(Rn, simm) + Operand::RegPreIndex(Rn, simm, false) } else { - Operand::RegPreIndex(Rn, simm as i32, true) + Operand::RegPreIndex(Rn, simm, true) }, Operand::Nothing, Operand::Nothing, @@ -8585,7 +8775,7 @@ impl Decoder for InstDecoder { let Rt = (word & 0x1f) as u16; let Rn = ((word >> 5) & 0x1f) as u16; let Rt2 = ((word >> 10) & 0x1f) as u16; - let mut imm7 = ((((word >> 15) & 0x7f) as i16) << 9) >> 9; + let mut imm7 = (((((word >> 15) & 0x7f) as i16) << 9) >> 9) as i32; let opc_L = ((word >> 22) & 1) | ((word >> 29) & 0x6); let size = match opc_L { 0b000 => { @@ -8627,7 +8817,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(size, Rt), Operand::Register(size, Rt2), - Operand::RegOffset(Rn, imm7), + Operand::RegPreIndex(Rn, imm7, false), Operand::Nothing ]; }, @@ -8863,7 +9053,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(size, Rs as u16), Operand::Register(size, Rt as u16), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; } else { @@ -8881,7 +9071,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(size, Rs as u16), Operand::Register(size, Rt as u16), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, ]; } else if opcode == 0b100 { @@ -8900,7 +9090,7 @@ impl Decoder for InstDecoder { // TOOD: should_is_must, Rs = 11111 inst.operands = [ Operand::Register(size, Rt as u16), - Operand::RegOffset(Rn, 0), + Operand::RegPreIndex(Rn, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -8915,7 +9105,7 @@ impl Decoder for InstDecoder { // V==0 let w = (word >> 11) & 1; let imm9 = ((word >> 12) & 0b1_1111_1111) as i16; - let imm9 = (imm9 << 7) >> 7; + let imm9 = ((imm9 << 7) >> 7) as i32; let imm9 = imm9 << 3; // `simm` is stored as a multiple of 8 let m = (word >> 23) & 1; let size = (word >> 30) & 0b11; @@ -8932,9 +9122,9 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::X, Rt), if w == 0 { - Operand::RegOffset(Rn, imm9) + Operand::RegPreIndex(Rn, imm9, false) } else { - Operand::RegPreIndex(Rn, imm9 as i32, true) + Operand::RegPreIndex(Rn, imm9, true) }, Operand::Nothing, Operand::Nothing, @@ -9008,7 +9198,7 @@ impl Decoder for InstDecoder { } } } else { - let imm9 = ((((word >> 12) & 0x1ff) as i16) << 7) >> 7; + let imm9 = (((((word >> 12) & 0x1ff) as i16) << 7) >> 7) as i32; match category { 0b00 => { // Load/store register (unscaled immediate) @@ -9040,7 +9230,7 @@ impl Decoder for InstDecoder { } else { Operand::Register(size, Rt) }, - Operand::RegOffset(Rn, imm9), + Operand::RegPreIndex(Rn, imm9, false), Operand::Nothing, Operand::Nothing, ]; @@ -9320,14 +9510,14 @@ impl Decoder for InstDecoder { // V == 0 let Rt = (word & 0x1f) as u16; let Rn = ((word >> 5) & 0x1f) as u16; - let imm12 = ((word >> 10) & 0x0fff) as i16; + let imm12 = ((word >> 10) & 0x0fff) as i16 as i32; let size_opc = ((word >> 22) & 0x3) | ((word >> 28) & 0xc); match size_opc { 0b0000 => { inst.opcode = Opcode::STRB; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12), + Operand::RegPreIndex(Rn, imm12, false), Operand::Nothing, Operand::Nothing, ]; @@ -9336,7 +9526,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRB; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12), + Operand::RegPreIndex(Rn, imm12, false), Operand::Nothing, Operand::Nothing, ]; @@ -9345,7 +9535,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRSB; inst.operands = [ Operand::Register(SizeCode::X, Rt), - Operand::RegOffset(Rn, imm12), + Operand::RegPreIndex(Rn, imm12, false), Operand::Nothing, Operand::Nothing, ]; @@ -9354,7 +9544,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRSB; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12), + Operand::RegPreIndex(Rn, imm12, false), Operand::Nothing, Operand::Nothing, ]; @@ -9363,7 +9553,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::STRH; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12 << 1), + Operand::RegPreIndex(Rn, imm12 << 1, false), Operand::Nothing, Operand::Nothing, ]; @@ -9372,7 +9562,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRH; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12 << 1), + Operand::RegPreIndex(Rn, imm12 << 1, false), Operand::Nothing, Operand::Nothing, ]; @@ -9381,7 +9571,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRSH; inst.operands = [ Operand::Register(SizeCode::X, Rt), - Operand::RegOffset(Rn, imm12 << 1), + Operand::RegPreIndex(Rn, imm12 << 1, false), Operand::Nothing, Operand::Nothing, ]; @@ -9390,7 +9580,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRSH; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12 << 1), + Operand::RegPreIndex(Rn, imm12 << 1, false), Operand::Nothing, Operand::Nothing, ]; @@ -9399,7 +9589,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::STR; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12 << 2), + Operand::RegPreIndex(Rn, imm12 << 2, false), Operand::Nothing, Operand::Nothing, ]; @@ -9408,7 +9598,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDR; inst.operands = [ Operand::Register(SizeCode::W, Rt), - Operand::RegOffset(Rn, imm12 << 2), + Operand::RegPreIndex(Rn, imm12 << 2, false), Operand::Nothing, Operand::Nothing, ]; @@ -9417,7 +9607,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDRSW; inst.operands = [ Operand::Register(SizeCode::X, Rt), - Operand::RegOffset(Rn, imm12 << 2), + Operand::RegPreIndex(Rn, imm12 << 2, false), Operand::Nothing, Operand::Nothing, ]; @@ -9427,7 +9617,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::STR; inst.operands = [ Operand::Register(SizeCode::X, Rt), - Operand::RegOffset(Rn, imm12 << 3), + Operand::RegPreIndex(Rn, imm12 << 3, false), Operand::Nothing, Operand::Nothing, ]; @@ -9436,7 +9626,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::LDR; inst.operands = [ Operand::Register(SizeCode::X, Rt), - Operand::RegOffset(Rn, imm12 << 3), + Operand::RegPreIndex(Rn, imm12 << 3, false), Operand::Nothing, Operand::Nothing, ]; @@ -9445,7 +9635,7 @@ impl Decoder for InstDecoder { inst.opcode = Opcode::PRFM; inst.operands = [ Operand::PrefetchOp(Rt), - Operand::RegOffset(Rn, imm12 << 3), + Operand::RegPreIndex(Rn, imm12 << 3, false), Operand::Nothing, Operand::Nothing, ]; @@ -9571,7 +9761,7 @@ impl Decoder for InstDecoder { ]; inst.operands = [ Operand::SIMDRegisterGroup(datasize, Rt as u16, SIZES[size as usize], num_regs), - Operand::RegOffset(Rn as u16, 0), + Operand::RegPreIndex(Rn as u16, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -9702,7 +9892,7 @@ impl Decoder for InstDecoder { ]; inst.operands = [ Operand::SIMDRegisterGroup(datasize, Rt as u16, SIZES[size as usize], opc_idx as u8 + 1), - Operand::RegOffset(Rn as u16, 0), + Operand::RegPreIndex(Rn as u16, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -9766,7 +9956,7 @@ impl Decoder for InstDecoder { }; inst.operands = [ Operand::SIMDRegisterGroupLane(Rt as u16, item_size, group_size, index as u8), - Operand::RegOffset(Rn as u16, 0), + Operand::RegPreIndex(Rn as u16, 0, false), Operand::Nothing, Operand::Nothing, ]; @@ -9922,7 +10112,7 @@ impl Decoder for InstDecoder { let extended_offset = (offset << 4) >> 4; inst.opcode = Opcode::B; inst.operands = [ - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing, Operand::Nothing @@ -9935,7 +10125,7 @@ impl Decoder for InstDecoder { let Rt = word & 0x1f; inst.operands = [ Operand::Register(SizeCode::W, Rt as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing ]; @@ -9947,7 +10137,7 @@ impl Decoder for InstDecoder { let Rt = word & 0x1f; inst.operands = [ Operand::Register(SizeCode::W, Rt as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing ]; @@ -9961,7 +10151,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::W, Rt as u16), Operand::Imm16(b as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing ]; }, @@ -9974,7 +10164,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::W, Rt as u16), Operand::Imm16(b as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing ]; }, @@ -9984,7 +10174,7 @@ impl Decoder for InstDecoder { let cond = word & 0x0f; inst.opcode = Opcode::Bcc(cond as u8); inst.operands = [ - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing, Operand::Nothing @@ -10003,7 +10193,7 @@ impl Decoder for InstDecoder { let extended_offset = (offset << 4) >> 4; inst.opcode = Opcode::BL; inst.operands = [ - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing, Operand::Nothing @@ -10016,7 +10206,7 @@ impl Decoder for InstDecoder { let Rt = word & 0x1f; inst.operands = [ Operand::Register(SizeCode::X, Rt as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing ]; @@ -10028,7 +10218,7 @@ impl Decoder for InstDecoder { let Rt = word & 0x1f; inst.operands = [ Operand::Register(SizeCode::X, Rt as u16), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing, Operand::Nothing ]; @@ -10042,7 +10232,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::X, Rt as u16), Operand::Imm16((b as u16) | 0x20), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing ]; }, @@ -10055,7 +10245,7 @@ impl Decoder for InstDecoder { inst.operands = [ Operand::Register(SizeCode::X, Rt as u16), Operand::Imm16((b as u16) | 0x20), - Operand::Offset(extended_offset as i64), + Operand::PCOffset(extended_offset as i64), Operand::Nothing ]; }, diff --git a/src/armv8/mod.rs b/src/armv8/mod.rs index e3fc54a..3d46f33 100644 --- a/src/armv8/mod.rs +++ b/src/armv8/mod.rs @@ -1 +1,2 @@ +/// `yaxpeax-arm`'s `ARMv8/aarch64` decoder and `Arch` implementation. pub mod a64; diff --git a/src/lib.rs b/src/lib.rs index ad2eb01..58a7667 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,52 @@ +//! # `yaxpeax-arm`, a decoder for `arm` instruction sets. +//! +//! `yaxpeax-arm` provides `armv7` (and below) decoders, including `thumb` support, as well a +//! decoder for `armv8`/`a64`. +//! +//! ## usage +//! +//! `yaxpeax-arm` is currently only usable through `yaxpeax-arch` traits: +//! ``` +//! mod decoder { +//! use yaxpeax_arch::{Arch, AddressDisplay, Decoder, Reader, ReaderBuilder}; +//! +//! pub fn decode_stream< +//! 'data, +//! A: yaxpeax_arch::Arch, +//! U: ReaderBuilder, +//! >(data: U) where +//! A::Instruction: std::fmt::Display, +//! { +//! let mut reader = ReaderBuilder::read_from(data); +//! let mut address: A::Address = reader.total_offset(); +//! +//! let decoder = A::Decoder::default(); +//! let mut decode_res = decoder.decode(&mut reader); +//! loop { +//! match decode_res { +//! Ok(ref inst) => { +//! println!("{}: {}", address.show(), inst); +//! decode_res = decoder.decode(&mut reader); +//! address = reader.total_offset(); +//! } +//! Err(e) => { +//! println!("{}: decode error: {}", address.show(), e); +//! break; +//! } +//! } +//! } +//! } +//! } +//! +//! use yaxpeax_arm::armv8::a64::Arch; +//! use yaxpeax_arch::{ReaderBuilder, U8Reader}; +//! let data: &[u8] = &[0x94, 0x02, 0x1e, 0x32]; +//! // would display `orr w20, w20, #0x4`. +//! decoder::decode_stream::(data); +//! ``` + #![no_std] +#![deny(missing_docs)] #[cfg(feature="use-serde")] #[macro_use] extern crate serde_derive; @@ -7,5 +55,7 @@ extern crate serde; extern crate yaxpeax_arch; extern crate bitvec; +/// `yaxpeax-arm`'s `ARMv7` decoder and `Arch` implementation. pub mod armv7; +/// `yaxpeax-arm`'s `ARMv8` decoder and `Arch` implementation. pub mod armv8; diff --git a/test/armv8/a64.rs b/test/armv8/a64.rs index 19256d1..cdb1265 100644 --- a/test/armv8/a64.rs +++ b/test/armv8/a64.rs @@ -77,7 +77,7 @@ fn test_decode_str_ldr() { opcode: Opcode::LDR, operands: [ Operand::Register(SizeCode::X, 17), - Operand::RegOffset(25, 0), + Operand::RegPreIndex(25, 0, false), Operand::Nothing, Operand::Nothing, ] @@ -89,7 +89,7 @@ fn test_decode_str_ldr() { opcode: Opcode::STR, operands: [ Operand::Register(SizeCode::X, 21), - Operand::RegOffset(31, 0xb0), + Operand::RegPreIndex(31, 0xb0, false), Operand::Nothing, Operand::Nothing, ] @@ -101,7 +101,7 @@ fn test_decode_str_ldr() { opcode: Opcode::STRB, operands: [ Operand::Register(SizeCode::W, 0), - Operand::RegOffset(19, 0x280), + Operand::RegPreIndex(19, 0x280, false), Operand::Nothing, Operand::Nothing, ] @@ -129,7 +129,7 @@ fn test_decode_misc() { opcode: Opcode::ADRP, operands: [ Operand::Register(SizeCode::X, 2), - Operand::Offset(0x8725000), + Operand::PCOffset(0x8725000), Operand::Nothing, Operand::Nothing ] @@ -214,7 +214,7 @@ fn test_decode_branch() { opcode: Opcode::CBZ, operands: [ Operand::Register(SizeCode::X, 1), - Operand::Offset(8), + Operand::PCOffset(8), Operand::Nothing, Operand::Nothing ] @@ -226,7 +226,7 @@ fn test_decode_branch() { opcode: Opcode::CBZ, operands: [ Operand::Register(SizeCode::X, 2), - Operand::Offset(12), + Operand::PCOffset(12), Operand::Nothing, Operand::Nothing ] @@ -4866,7 +4866,7 @@ fn test_misc() { ([0x00, 0x30, 0xc0, 0x9a], "pacga x0, x0, x0"), ([0x00, 0x00, 0xae, 0x9e], "fmov x0, v0.d[1]"), ([0x00, 0x00, 0xe6, 0x9e], "fmov x0, h0"), - ([0x7f, 0x41, 0x00, 0xd5], "msr pstate.58, #0x1"), + ([0x7f, 0x41, 0x00, 0xd5], "msr pstate.0x58, #0x1"), ([0x00, 0x68, 0x20, 0x38], "strb w0, [x0, x0]"), ]; let errs = run_tests(TESTS); -- cgit v1.1