Skip to main content

isa_lang/x86_64/
error.rs

1//! Encoding and decoding errors.
2
3use core::fmt;
4
5use super::inst::Mnemonic;
6use super::reg::Reg;
7
8/// Why an instruction could not be encoded.
9///
10/// The encoder tries every form of the mnemonic and reports the failure of
11/// the form that came closest: an operand-kind mismatch only when no form
12/// took the operand kinds at all.
13///
14/// # Examples
15///
16/// ```
17/// use isa_lang::x86_64::{encode, EncodeError, Inst, Mem, Mnemonic, Operand, Reg, Size};
18///
19/// // Memory to memory has no form.
20/// let m = Mem::new(Size::Qword, Reg::RAX);
21/// let err = encode(&Inst::new(Mnemonic::Mov, [m.into(), m.into()])).unwrap_err();
22/// assert_eq!(err, EncodeError::NoForm { mnemonic: Mnemonic::Mov });
23///
24/// // A 64-bit add takes a sign-extended 32-bit immediate at most.
25/// let err = encode(&Inst::new(Mnemonic::Add, [Reg::RAX.into(), Operand::Imm(1 << 40)])).unwrap_err();
26/// assert_eq!(err, EncodeError::ImmediateOutOfRange { value: 1 << 40 });
27/// assert_eq!(err.to_string(), "immediate 0x10000000000 does not fit any form for these operands");
28/// ```
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30#[non_exhaustive]
31pub enum EncodeError {
32    /// No form of the mnemonic takes operands of these kinds, sizes, and
33    /// count. Check the operand order (destination first) and widths.
34    NoForm {
35        /// The mnemonic that was asked for.
36        mnemonic: Mnemonic,
37    },
38    /// Forms take these operand kinds, but the immediate is outside every
39    /// one's range. Immediates are signed values of the operation's width;
40    /// write `-1`, not `0xFFFF_FFFF`, for a 32-bit all-ones value, and load
41    /// wider constants into a register first.
42    ImmediateOutOfRange {
43        /// The immediate as given.
44        value: i64,
45    },
46    /// The branch target is beyond the reach of a 32-bit displacement.
47    TargetOutOfRange {
48        /// The target, relative to the start of the instruction.
49        offset: i64,
50    },
51    /// `AH`, `CH`, `DH`, or `BH` appears in an instruction that needs a REX
52    /// prefix (64-bit operation, `R8`-`R15`, or `SPL`/`BPL`/`SIL`/`DIL`),
53    /// where those encodings mean `SPL`-`DIL` instead. Use another register.
54    HighByteWithRex,
55    /// A memory operand's base register is not a 64-bit register.
56    BaseNotQword {
57        /// The offending base.
58        base: Reg,
59    },
60    /// A memory operand's index register is not a 64-bit register.
61    IndexNotQword {
62        /// The offending index.
63        index: Reg,
64    },
65    /// `RSP` is used as an index register, which the SIB byte cannot express.
66    /// Swap base and index if the scale is 1.
67    RspIndex,
68    /// A RIP-relative memory operand has an index register.
69    RipWithIndex,
70}
71
72impl fmt::Display for EncodeError {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            EncodeError::NoForm { mnemonic } => {
76                write!(f, "no `{mnemonic}` form takes these operands")
77            }
78            EncodeError::ImmediateOutOfRange { value } => {
79                f.write_str("immediate ")?;
80                super::fmt::hex(f, *value)?;
81                f.write_str(" does not fit any form for these operands")
82            }
83            EncodeError::TargetOutOfRange { offset } => {
84                f.write_str("branch target $")?;
85                super::fmt::signed_hex(f, *offset)?;
86                f.write_str(" is out of reach of a 32-bit displacement")
87            }
88            EncodeError::HighByteWithRex => f.write_str(
89                "ah, ch, dh, and bh cannot be used in an instruction that needs a REX prefix",
90            ),
91            EncodeError::BaseNotQword { base } => {
92                write!(f, "memory base `{base}` is not a 64-bit register")
93            }
94            EncodeError::IndexNotQword { index } => {
95                write!(f, "memory index `{index}` is not a 64-bit register")
96            }
97            EncodeError::RspIndex => f.write_str("rsp cannot be an index register"),
98            EncodeError::RipWithIndex => {
99                f.write_str("a rip-relative memory operand cannot have an index register")
100            }
101        }
102    }
103}
104
105impl core::error::Error for EncodeError {}
106
107/// Why bytes could not be decoded.
108///
109/// Every variant carries `at`, the offset in the input of the byte at fault.
110/// The decoder accepts exactly the encodings the form table describes, plus
111/// a redundant empty REX prefix (`40`) and a redundant REX.W on 64-bit-default
112/// forms; anything else is an error, never a guess.
113///
114/// # Examples
115///
116/// ```
117/// use isa_lang::x86_64::{decode, DecodeError};
118///
119/// assert_eq!(decode(&[0x48, 0x83]), Err(DecodeError::Truncated { at: 2 }));
120/// assert_eq!(decode(&[0xF0, 0x01, 0xC0]), Err(DecodeError::UnsupportedPrefix { at: 0, byte: 0xF0 }));
121/// assert_eq!(decode(&[0x0F, 0x05]), Err(DecodeError::UnknownOpcode { at: 1, escaped: true, opcode: 0x05 }));
122/// assert_eq!(
123///     decode(&[0x0F, 0x05]).unwrap_err().to_string(),
124///     "unknown opcode 0f 05 at byte 1",
125/// );
126/// ```
127#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
128#[non_exhaustive]
129pub enum DecodeError {
130    /// The input ends before the instruction does.
131    Truncated {
132        /// The offset at which another byte was needed.
133        at: usize,
134    },
135    /// A legacy prefix outside the supported set (only `66` is supported:
136    /// no LOCK, REP, segment, or address-size prefixes).
137    UnsupportedPrefix {
138        /// Offset of the prefix.
139        at: usize,
140        /// The prefix byte.
141        byte: u8,
142    },
143    /// The operand-size prefix `66` appears twice.
144    RepeatedPrefix {
145        /// Offset of the second `66`.
146        at: usize,
147    },
148    /// A REX prefix is followed by another prefix instead of the opcode.
149    MisplacedRex {
150        /// Offset of the REX prefix.
151        at: usize,
152    },
153    /// No form has this opcode.
154    UnknownOpcode {
155        /// Offset of the opcode byte.
156        at: usize,
157        /// Whether the opcode follows the `0F` escape.
158        escaped: bool,
159        /// The opcode byte.
160        opcode: u8,
161    },
162    /// The opcode exists, but not with this operand size (`66` / REX.W).
163    InvalidOperandSize {
164        /// Offset of the opcode byte.
165        at: usize,
166    },
167    /// The opcode exists, but not with this ModRM `reg` extension.
168    UnknownExtension {
169        /// Offset of the ModRM byte.
170        at: usize,
171        /// The `/digit` found.
172        ext: u8,
173    },
174    /// The form needs a memory operand but ModRM names a register.
175    RegisterOperand {
176        /// Offset of the ModRM byte.
177        at: usize,
178    },
179    /// The REX prefix sets R, X, or B, but no operand of the form uses it.
180    UnusedRex {
181        /// Offset of the REX prefix.
182        at: usize,
183    },
184}
185
186impl DecodeError {
187    /// The offset in the input of the byte at fault.
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// use isa_lang::x86_64::decode;
193    ///
194    /// assert_eq!(decode(&[0x66, 0x66, 0x90]).unwrap_err().at(), 1);
195    /// ```
196    #[must_use]
197    pub const fn at(&self) -> usize {
198        match *self {
199            DecodeError::Truncated { at }
200            | DecodeError::UnsupportedPrefix { at, .. }
201            | DecodeError::RepeatedPrefix { at }
202            | DecodeError::MisplacedRex { at }
203            | DecodeError::UnknownOpcode { at, .. }
204            | DecodeError::InvalidOperandSize { at }
205            | DecodeError::UnknownExtension { at, .. }
206            | DecodeError::RegisterOperand { at }
207            | DecodeError::UnusedRex { at } => at,
208        }
209    }
210}
211
212impl fmt::Display for DecodeError {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        match *self {
215            DecodeError::Truncated { at } => {
216                write!(
217                    f,
218                    "instruction is cut off: byte {at} is past the end of the input"
219                )
220            }
221            DecodeError::UnsupportedPrefix { at, byte } => {
222                write!(f, "unsupported prefix {byte:02x} at byte {at}")
223            }
224            DecodeError::RepeatedPrefix { at } => {
225                write!(f, "repeated operand-size prefix at byte {at}")
226            }
227            DecodeError::MisplacedRex { at } => {
228                write!(
229                    f,
230                    "REX prefix at byte {at} is not immediately before the opcode"
231                )
232            }
233            DecodeError::UnknownOpcode {
234                at,
235                escaped,
236                opcode,
237            } => {
238                let escape = if escaped { "0f " } else { "" };
239                write!(f, "unknown opcode {escape}{opcode:02x} at byte {at}")
240            }
241            DecodeError::InvalidOperandSize { at } => {
242                write!(f, "opcode at byte {at} has no form with this operand size")
243            }
244            DecodeError::UnknownExtension { at, ext } => {
245                write!(
246                    f,
247                    "opcode extension /{ext} in the ModRM byte at {at} is not defined"
248                )
249            }
250            DecodeError::RegisterOperand { at } => {
251                write!(
252                    f,
253                    "ModRM byte at {at} names a register where memory is required"
254                )
255            }
256            DecodeError::UnusedRex { at } => {
257                write!(f, "REX prefix at byte {at} sets a bit no operand uses")
258            }
259        }
260    }
261}
262
263impl core::error::Error for DecodeError {}