iced_x86/code_asm.rs
1// SPDX-License-Identifier: MIT
2// Copyright (C) 2018-present iced project and contributors
3
4//! Easier creating of instructions (eg. `a.mov(eax, ecx)`) than using `Instruction::with*()` functions.
5//!
6//! This requires the `code_asm` feature to use (not enabled by default). Add it to your `Cargo.toml`:
7//!
8//! ```toml
9//! [dependencies.iced-x86]
10//! version = "1.21.0"
11//! features = ["code_asm"]
12//! ```
13//!
14//! See [`CodeAssembler`] docs for usage.
15//!
16//! [`CodeAssembler`]: struct.CodeAssembler.html
17
18pub mod asm_traits;
19mod code_asm_methods;
20mod fn_asm_impl;
21mod fn_asm_pub;
22mod mem;
23mod op_state;
24mod reg;
25pub mod registers;
26#[cfg(test)]
27mod tests;
28
29pub use crate::code_asm::mem::*;
30pub use crate::code_asm::reg::*;
31pub use crate::code_asm::registers::*;
32pub use crate::IcedError;
33use crate::{BlockEncoderResult, Instruction};
34use alloc::vec::Vec;
35use core::hash::{Hash, Hasher};
36
37struct PrefixFlags;
38impl PrefixFlags {
39 const NONE: u8 = 0x00;
40 const LOCK: u8 = 0x01;
41 const REPE: u8 = 0x02;
42 const REPNE: u8 = 0x04;
43 const NOTRACK: u8 = 0x08;
44 const PREFER_VEX: u8 = 0x10;
45 const PREFER_EVEX: u8 = 0x20;
46}
47
48struct CodeAssemblerOptions;
49impl CodeAssemblerOptions {
50 const PREFER_VEX: u8 = 0x01;
51 const PREFER_SHORT_BRANCH: u8 = 0x02;
52}
53
54/// Creates and encodes instructions. It's easier to use this struct than to call `Instruction::with*()` functions.
55///
56/// This requires the `code_asm` feature to use (not enabled by default). Add it to your `Cargo.toml`:
57///
58/// ```toml
59/// [dependencies.iced-x86]
60/// version = "1.21.0"
61/// features = ["code_asm"]
62/// ```
63///
64/// # Examples
65///
66/// ```
67/// use iced_x86::code_asm::*;
68///
69/// # fn main() -> Result<(), IcedError> {
70/// let mut a = CodeAssembler::new(64)?;
71///
72/// // Anytime you add something to a register (or subtract from it), you create a
73/// // memory operand. You can also call word_ptr(), dword_bcst() etc to create memory
74/// // operands.
75/// let _ = rax; // register
76/// let _ = rax + 0; // memory with no size hint
77/// let _ = ptr(rax); // memory with no size hint
78/// let _ = rax + rcx * 4 - 123; // memory with no size hint
79/// // To create a memory operand with only a displacement or only a base register,
80/// // you can call one of the memory fns:
81/// let _ = qword_ptr(123); // memory with a qword size hint
82/// let _ = dword_bcst(rcx); // memory (broadcast) with a dword size hint
83/// // To add a segment override, call the segment methods:
84/// let _ = ptr(rax).fs(); // fs:[rax]
85///
86/// // Each mnemonic is a method
87/// a.push(rcx)?;
88/// // There are a few exceptions where you must append `_<opcount>` to the mnemonic to
89/// // get the instruction you need:
90/// a.ret()?;
91/// a.ret_1(123)?;
92/// // Use byte_ptr(), word_bcst(), etc to force the arg to a memory operand and to add a
93/// // size hint
94/// a.xor(byte_ptr(rdx+r14*4+123), 0x10)?;
95/// // Prefixes are also methods
96/// a.rep().stosd()?;
97/// // Sometimes, you must add an integer suffix to help the compiler:
98/// a.mov(rax, 0x1234_5678_9ABC_DEF0u64)?;
99///
100/// // Create labels that can be referenced by code
101/// let mut loop_lbl1 = a.create_label();
102/// let mut after_loop1 = a.create_label();
103/// a.mov(ecx, 10)?;
104/// a.set_label(&mut loop_lbl1)?;
105/// a.dec(ecx)?;
106/// a.jp(after_loop1)?;
107/// a.jne(loop_lbl1)?;
108/// a.set_label(&mut after_loop1)?;
109///
110/// // It's possible to reference labels with RIP-relative addressing
111/// let mut skip_data = a.create_label();
112/// let mut data = a.create_label();
113/// a.jmp(skip_data)?;
114/// a.set_label(&mut data)?;
115/// a.db(b"\x90\xCC\xF1\x90")?;
116/// a.set_label(&mut skip_data)?;
117/// a.lea(rax, ptr(data))?;
118///
119/// // AVX512 opmasks, {z}, {sae}, {er} and broadcasting are also supported:
120/// a.vsqrtps(zmm16.k2().z(), dword_bcst(rcx))?;
121/// a.vsqrtps(zmm1.k2().z(), zmm23.rd_sae())?;
122/// // Sometimes, the encoder doesn't know if you want VEX or EVEX encoding.
123/// // You can force EVEX globally like so:
124/// a.set_prefer_vex(false);
125/// a.vucomiss(xmm31, xmm15.sae())?;
126/// a.vucomiss(xmm31, ptr(rcx))?;
127/// // or call vex()/evex() to override the encoding option:
128/// a.evex().vucomiss(xmm31, xmm15.sae())?;
129/// a.vex().vucomiss(xmm15, xmm14)?;
130///
131/// // Encode all added instructions
132/// let bytes = a.assemble(0x1234_5678)?;
133/// assert_eq!(bytes.len(), 82);
134/// // If you don't want to encode them, you can get all instructions by calling
135/// // one of these methods:
136/// let instrs = a.instructions(); // Get a reference to the internal vec
137/// assert_eq!(instrs.len(), 19);
138/// let instrs = a.take_instructions(); // Take ownership of the vec with all instructions
139/// assert_eq!(instrs.len(), 19);
140/// assert_eq!(a.instructions().len(), 0);
141/// # Ok(())
142/// # }
143/// ```
144#[allow(missing_debug_implementations)]
145pub struct CodeAssembler {
146 bitness: u32,
147 instructions: Vec<Instruction>,
148 current_label_id: u64,
149 current_label: CodeLabel,
150 current_anon_label: CodeLabel,
151 next_anon_label: CodeLabel,
152 defined_anon_label: bool,
153 prefix_flags: u8,
154 options: u8,
155}
156
157/// A label created by [`CodeAssembler`]
158///
159/// [`CodeAssembler`]: struct.CodeAssembler.html
160#[derive(Debug, Default, Copy, Clone)]
161pub struct CodeLabel {
162 id: u64,
163 instruction_index: usize,
164}
165
166impl Eq for CodeLabel {}
167
168impl PartialEq for CodeLabel {
169 #[inline]
170 fn eq(&self, other: &CodeLabel) -> bool {
171 self.id == other.id
172 }
173}
174
175impl Hash for CodeLabel {
176 #[inline]
177 fn hash<H: Hasher>(&self, state: &mut H) {
178 self.id.hash(state);
179 }
180}
181
182impl CodeLabel {
183 #[must_use]
184 #[inline]
185 pub(crate) fn new(id: u64) -> Self {
186 Self { id, instruction_index: usize::MAX }
187 }
188
189 #[must_use]
190 #[inline]
191 pub(crate) fn is_empty(&self) -> bool {
192 self.id == 0
193 }
194
195 #[must_use]
196 #[inline]
197 pub(crate) fn has_instruction_index(&self) -> bool {
198 self.instruction_index != usize::MAX
199 }
200
201 #[must_use]
202 #[inline]
203 pub(crate) fn id(&self) -> u64 {
204 self.id
205 }
206}
207
208/// Result of assembling the instructions
209#[derive(Debug)]
210#[cfg_attr(not(feature = "exhaustive_enums"), non_exhaustive)]
211pub struct CodeAssemblerResult {
212 /// Inner `BlockEncoder` result
213 pub inner: BlockEncoderResult,
214}
215
216impl CodeAssemblerResult {
217 /// Gets the address of a label
218 ///
219 /// # Notes
220 ///
221 /// You should pass [`BlockEncoderOptions::RETURN_NEW_INSTRUCTION_OFFSETS`] to [`CodeAssembler::assemble_options()`] or this method will fail.
222 ///
223 /// # Arguments
224 ///
225 /// * `label`: The label
226 ///
227 /// # Errors
228 ///
229 /// Fails if the label is invalid
230 ///
231 /// [`BlockEncoderOptions::RETURN_NEW_INSTRUCTION_OFFSETS`]: ../struct.BlockEncoderOptions.html#associatedconstant.RETURN_NEW_INSTRUCTION_OFFSETS
232 /// [`CodeAssembler::assemble_options()`]: struct.CodeAssembler.html#method.assemble_options
233 #[allow(clippy::missing_inline_in_public_items)]
234 pub fn label_ip(&self, label: &CodeLabel) -> Result<u64, IcedError> {
235 if label.is_empty() {
236 return Err(IcedError::new("Invalid label. Must be created via `CodeAssembler::create_label()`."));
237 }
238 if !label.has_instruction_index() {
239 return Err(IcedError::new(
240 "The label is not associated with an instruction index. It must be emitted via `CodeAssembler::set_label()`.",
241 ));
242 }
243 let new_offset = if let Some(new_offset) = self.inner.new_instruction_offsets.get(label.instruction_index) {
244 *new_offset
245 } else {
246 return Err(IcedError::new(
247 "Invalid label instruction index or `BlockEncoderOptions::RETURN_NEW_INSTRUCTION_OFFSETS` option was not enabled when calling `assemble_options()`.",
248 ));
249 };
250 if new_offset == u32::MAX {
251 Err(IcedError::new("The instruction was re-written to a longer instruction (eg. JE NEAR -> JE FAR) and there's no instruction offset. Consider using a `zero_bytes()` instruction as a label instead of a normal instruction or disable branch optimizations."))
252 } else {
253 Ok(self.inner.rip.wrapping_add(new_offset as u64))
254 }
255 }
256}