Skip to main content

asmkit/
lib.rs

1//! # asmkit
2//!
3//! ### Overview
4//! asmkit is a portable assembler toolkit designed for decoding and encoding various assembly architectures. It aims to provide a small, efficient, and cross-platform library that can be used to build and manipulate assembly code without being tied to a specific platform or architecture. The library is written in Rust and supports several architectures including X64, RISC-V, and ARM (work-in-progress). Key features include:
5//! - **Multi-Architecture Support**: Supports multiple architectures such as X64, RISC-V, ARM (WIP), PPC (WIP), and plans to support PPC64 and OpenPOWER in the future.
6//! - **Minimal Dependencies**: Relies on a minimal set of dependencies to ensure portability and efficiency:
7//! - - `libc` and `intrusive-collections`` - For JIT support.
8//! - - `paste` and `derive-more` - Utility crates that simplify repetitive code.
9//! - - `smallvec` - A crate used to manage collections that avoid too frequent heap allocations during code generation.
10//!
11//! - **Code Relocations**: Provides a CodeBuffer interface to handle relocations, allowing the insertion of symbols into the API seamlessly.
12//! - **Auto-Generated Assemblers**: The goal is to support a wide range of platforms and provide auto-generated assemblers for as many architectures as possible.
13//! - **Portability**: Built to run on any platform, with the architecture-specific parts of the library being independent of the platform on which asmkit is built.
14//!
15//!
16//!
17//! ### Usage
18//!
19//! To use the library simply import a module for architecture you want to emit code for e.g `use asmkit::x86::*;`; This would
20//! include all the required code to generate code for platform.
21//!
22//! Example:
23//!
24//! ```rust
25//! use asmkit::core::buffer::CodeBuffer;
26//! use asmkit::core::jit_allocator::JitAllocator;
27//! use asmkit::x86::*;
28//!
29//!
30//!     let mut buf = CodeBuffer::new();
31//!     let mut asm = Assembler::new(&mut buf);
32//!
33//!     let dst = RDI;
34//!     let arg0 = RSI;
35//!     let arg1 = RDX;
36//!
37//!     asm.sse_movdqu(XMM0, ptr64(arg0, 0)); // load 4 ints from [arg0] to XMM0
38//!     asm.sse_movdqu(XMM1, ptr64(arg1, 0)); // load 4 ints from [arg1] to XMM1
39//!     asm.sse_paddw(XMM0, XMM1); // add 4 ints
40//!     asm.sse_movdqu(ptr64(dst, 0), XMM0); // store result in [dst]
41//!     asm.ret(); // return from function
42//!
43//!     let result = buf.finish();
44//!     let mut jit = JitAllocator::new(Default::default());
45//!     // you can also use jit.alloc + jit.write manually.
46//!     let span = result
47//!         .allocate(&mut jit)
48//!         .expect("failed to allocate JIT-code");
49//!
50//!     // JIT Allocator uses dual-mapping: it allocates two pages which map to same physical space
51//!     // and you write to executable code through `span.rw()` pointer while you can execute `span.rx()`.
52//!     let f: extern "C" fn(*mut i32, *const i32, *const i32) = unsafe { std::mem::transmute(span.rx()) };
53//!     #[cfg(all(unix, target_arch="x86_64"))] // can run only on x64 and on SystemV platforms.
54//!     {
55//!         let mut res = [0; 4];
56//!         f(res.as_mut_ptr(), [4, 3, 2, 1].as_ptr(), [1, 5, 2, 8].as_ptr());
57//!
58//!         println!("{:?}", res);
59//!     }
60//!
61//!```
62
63#![cfg_attr(not(test), no_std)]
64
65extern crate alloc;
66
67#[cfg(feature = "aarch64")]
68pub mod aarch64;
69pub mod core;
70pub mod masm;
71pub mod ppc;
72#[cfg(feature = "riscv")]
73pub mod riscv;
74pub mod util;
75
76pub mod x86;
77
78use ::core::fmt;
79
80#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
81pub enum AsmError {
82    InvalidPrefix,
83    InvalidOperand,
84    InvalidImmediate,
85    InvalidInstruction,
86    OutOfMemory,
87    InvalidState,
88    TooManyHandles,
89    InvalidArgument,
90    FailedToOpenAnonymousMemory,
91    TooLarge,
92    X86(X86Error),
93    UnsupportedInstruction { reason: &'static str },
94}
95
96impl fmt::Display for AsmError {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        match self {
99            AsmError::InvalidPrefix => write!(f, "invalid prefix"),
100            AsmError::InvalidOperand => write!(f, "invalid operand"),
101            AsmError::InvalidInstruction => write!(f, "invalid instruction"),
102            AsmError::OutOfMemory => write!(f, "out of memory"),
103            AsmError::InvalidState => write!(f, "invalid state"),
104            AsmError::TooManyHandles => write!(f, "too many handles"),
105            AsmError::InvalidArgument => write!(f, "invalid argument"),
106            AsmError::InvalidImmediate => write!(f, "invalid immediate"),
107            AsmError::FailedToOpenAnonymousMemory => {
108                write!(f, "failed to open anonymous memory")
109            }
110            AsmError::TooLarge => write!(f, "too large"),
111            AsmError::X86(e) => write!(f, "x86 error: {}", e),
112            AsmError::UnsupportedInstruction { reason } => {
113                write!(f, "unsupported instruction: {}", reason)
114            }
115        }
116    }
117}
118
119#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
120pub enum X86Error {
121    InvalidPrefix {
122        prefix: u64,
123        reason: &'static str,
124    },
125    InvalidOperand {
126        operand_index: usize,
127        reason: &'static str,
128    },
129    InvalidInstruction {
130        opcode: u64,
131        reason: &'static str,
132    },
133    InvalidEncoding {
134        encoding: u8,
135        reason: &'static str,
136    },
137    InvalidModRM {
138        modrm: u8,
139        reason: &'static str,
140    },
141    InvalidSIB {
142        sib: u8,
143        reason: &'static str,
144    },
145    InvalidDisplacement {
146        value: i64,
147        size: usize,
148        reason: &'static str,
149    },
150    InvalidImmediate {
151        value: i64,
152        size: usize,
153        reason: &'static str,
154    },
155    InvalidRegister {
156        reg_id: u32,
157        reg_type: &'static str,
158        reason: &'static str,
159    },
160    InvalidMemoryOperand {
161        base: Option<u32>,
162        index: Option<u32>,
163        scale: u8,
164        offset: i64,
165        reason: &'static str,
166    },
167    InvalidVSIB {
168        index_reg: u32,
169        reason: &'static str,
170    },
171    InvalidMasking {
172        mask_reg: u32,
173        reason: &'static str,
174    },
175    InvalidBroadcast {
176        reason: &'static str,
177    },
178    InvalidRoundingControl {
179        rc: u64,
180        reason: &'static str,
181    },
182    InvalidEVEX {
183        field: &'static str,
184        reason: &'static str,
185    },
186    InvalidVEX {
187        field: &'static str,
188        reason: &'static str,
189    },
190    TooLongInstruction {
191        length: usize,
192        max_length: usize,
193    },
194    SegmentOverrideNotAllowed {
195        segment: u8,
196        reason: &'static str,
197    },
198    AddressSizeMismatch {
199        expected: usize,
200        actual: usize,
201    },
202    OperandSizeMismatch {
203        expected: usize,
204        actual: usize,
205    },
206    InvalidRIPRelative {
207        offset: i64,
208        reason: &'static str,
209    },
210    InvalidLabel {
211        label_id: u32,
212        reason: &'static str,
213    },
214    InvalidSymbol {
215        symbol_id: u32,
216        reason: &'static str,
217    },
218    InvalidRelocation {
219        reloc_type: &'static str,
220        reason: &'static str,
221    },
222    InvalidOperandCombination {
223        mnemonic: &'static str,
224    },
225}
226
227impl fmt::Display for X86Error {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        match self {
230            X86Error::InvalidPrefix { prefix, reason } => {
231                write!(f, "invalid prefix 0x{:x}: {}", prefix, reason)
232            }
233            X86Error::InvalidOperand {
234                operand_index,
235                reason,
236            } => write!(f, "invalid operand {}: {}", operand_index, reason),
237            X86Error::InvalidInstruction { opcode, reason } => {
238                write!(f, "invalid instruction 0x{:x}: {}", opcode, reason)
239            }
240            X86Error::InvalidEncoding { encoding, reason } => {
241                write!(f, "invalid encoding {}: {}", encoding, reason)
242            }
243            X86Error::InvalidModRM { modrm, reason } => {
244                write!(f, "invalid ModRM byte 0x{:02x}: {}", modrm, reason)
245            }
246            X86Error::InvalidSIB { sib, reason } => {
247                write!(f, "invalid SIB byte 0x{:02x}: {}", sib, reason)
248            }
249            X86Error::InvalidDisplacement {
250                value,
251                size,
252                reason,
253            } => write!(
254                f,
255                "invalid displacement 0x{:x} (size {}): {}",
256                value, size, reason
257            ),
258            X86Error::InvalidImmediate {
259                value,
260                size,
261                reason,
262            } => {
263                write!(
264                    f,
265                    "invalid immediate 0x{:x} (size {}): {}",
266                    value, size, reason
267                )
268            }
269            X86Error::InvalidRegister {
270                reg_id,
271                reg_type,
272                reason,
273            } => write!(
274                f,
275                "invalid register {} (type {}): {}",
276                reg_id, reg_type, reason
277            ),
278            X86Error::InvalidMemoryOperand {
279                base,
280                index,
281                scale,
282                offset,
283                reason,
284            } => write!(
285                f,
286                "invalid memory operand [base={:?}, index={:?}, scale={}, offset={}]: {}",
287                base, index, scale, offset, reason
288            ),
289            X86Error::InvalidVSIB { index_reg, reason } => {
290                write!(f, "invalid VSIB index register {}: {}", index_reg, reason)
291            }
292            X86Error::InvalidMasking { mask_reg, reason } => {
293                write!(f, "invalid mask register {}: {}", mask_reg, reason)
294            }
295            X86Error::InvalidBroadcast { reason } => {
296                write!(f, "invalid broadcast: {}", reason)
297            }
298            X86Error::InvalidRoundingControl { rc, reason } => {
299                write!(f, "invalid rounding control 0x{:x}: {}", rc, reason)
300            }
301            X86Error::InvalidEVEX { field, reason } => {
302                write!(f, "invalid EVEX field '{}': {}", field, reason)
303            }
304            X86Error::InvalidVEX { field, reason } => {
305                write!(f, "invalid VEX field '{}': {}", field, reason)
306            }
307            X86Error::TooLongInstruction { length, max_length } => write!(
308                f,
309                "instruction too long: {} bytes (max {})",
310                length, max_length
311            ),
312            X86Error::SegmentOverrideNotAllowed { segment, reason } => {
313                write!(f, "segment override {} not allowed: {}", segment, reason)
314            }
315            X86Error::AddressSizeMismatch { expected, actual } => write!(
316                f,
317                "address size mismatch: expected {} bytes, got {}",
318                expected, actual
319            ),
320            X86Error::OperandSizeMismatch { expected, actual } => write!(
321                f,
322                "operand size mismatch: expected {} bytes, got {}",
323                expected, actual
324            ),
325            X86Error::InvalidRIPRelative { offset, reason } => {
326                write!(f, "invalid RIP-relative offset {}: {}", offset, reason)
327            }
328            X86Error::InvalidLabel { label_id, reason } => {
329                write!(f, "invalid label {}: {}", label_id, reason)
330            }
331            X86Error::InvalidSymbol { symbol_id, reason } => {
332                write!(f, "invalid symbol {}: {}", symbol_id, reason)
333            }
334            X86Error::InvalidRelocation { reloc_type, reason } => {
335                write!(f, "invalid relocation {}: {}", reloc_type, reason)
336            }
337            X86Error::InvalidOperandCombination { mnemonic } => {
338                write!(
339                    f,
340                    "invalid operand combination for instruction `{}`",
341                    mnemonic
342                )
343            }
344        }
345    }
346}