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}