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 ppc;
71#[cfg(feature = "riscv")]
72pub mod riscv;
73pub mod util;
74#[cfg(feature = "x86")]
75pub mod x86;
76
77use ::core::fmt;
78
79#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
80pub enum AsmError {
81    InvalidPrefix,
82    InvalidOperand,
83    InvalidImmediate,
84    InvalidInstruction,
85    OutOfMemory,
86    InvalidState,
87    TooManyHandles,
88    InvalidArgument,
89    FailedToOpenAnonymousMemory,
90    TooLarge,
91    X86(X86Error),
92    UnsupportedInstruction { reason: &'static str },
93}
94
95impl fmt::Display for AsmError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            AsmError::InvalidPrefix => write!(f, "invalid prefix"),
99            AsmError::InvalidOperand => write!(f, "invalid operand"),
100            AsmError::InvalidInstruction => write!(f, "invalid instruction"),
101            AsmError::OutOfMemory => write!(f, "out of memory"),
102            AsmError::InvalidState => write!(f, "invalid state"),
103            AsmError::TooManyHandles => write!(f, "too many handles"),
104            AsmError::InvalidArgument => write!(f, "invalid argument"),
105            AsmError::InvalidImmediate => write!(f, "invalid immediate"),
106            AsmError::FailedToOpenAnonymousMemory => {
107                write!(f, "failed to open anonymous memory")
108            }
109            AsmError::TooLarge => write!(f, "too large"),
110            AsmError::X86(e) => write!(f, "x86 error: {}", e),
111            AsmError::UnsupportedInstruction { reason } => {
112                write!(f, "unsupported instruction: {}", reason)
113            }
114        }
115    }
116}
117
118#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
119pub enum X86Error {
120    InvalidPrefix {
121        prefix: u64,
122        reason: &'static str,
123    },
124    InvalidOperand {
125        operand_index: usize,
126        reason: &'static str,
127    },
128    InvalidInstruction {
129        opcode: u64,
130        reason: &'static str,
131    },
132    InvalidEncoding {
133        encoding: u8,
134        reason: &'static str,
135    },
136    InvalidModRM {
137        modrm: u8,
138        reason: &'static str,
139    },
140    InvalidSIB {
141        sib: u8,
142        reason: &'static str,
143    },
144    InvalidDisplacement {
145        value: i64,
146        size: usize,
147        reason: &'static str,
148    },
149    InvalidImmediate {
150        value: i64,
151        size: usize,
152        reason: &'static str,
153    },
154    InvalidRegister {
155        reg_id: u32,
156        reg_type: &'static str,
157        reason: &'static str,
158    },
159    InvalidMemoryOperand {
160        base: Option<u32>,
161        index: Option<u32>,
162        scale: u8,
163        offset: i64,
164        reason: &'static str,
165    },
166    InvalidVSIB {
167        index_reg: u32,
168        reason: &'static str,
169    },
170    InvalidMasking {
171        mask_reg: u32,
172        reason: &'static str,
173    },
174    InvalidBroadcast {
175        reason: &'static str,
176    },
177    InvalidRoundingControl {
178        rc: u64,
179        reason: &'static str,
180    },
181    InvalidEVEX {
182        field: &'static str,
183        reason: &'static str,
184    },
185    InvalidVEX {
186        field: &'static str,
187        reason: &'static str,
188    },
189    TooLongInstruction {
190        length: usize,
191        max_length: usize,
192    },
193    SegmentOverrideNotAllowed {
194        segment: u8,
195        reason: &'static str,
196    },
197    AddressSizeMismatch {
198        expected: usize,
199        actual: usize,
200    },
201    OperandSizeMismatch {
202        expected: usize,
203        actual: usize,
204    },
205    InvalidRIPRelative {
206        offset: i64,
207        reason: &'static str,
208    },
209    InvalidLabel {
210        label_id: u32,
211        reason: &'static str,
212    },
213    InvalidSymbol {
214        symbol_id: u32,
215        reason: &'static str,
216    },
217    InvalidRelocation {
218        reloc_type: &'static str,
219        reason: &'static str,
220    },
221    InvalidOperandCombination {
222        mnemonic: &'static str,
223    },
224}
225
226impl fmt::Display for X86Error {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        match self {
229            X86Error::InvalidPrefix { prefix, reason } => {
230                write!(f, "invalid prefix 0x{:x}: {}", prefix, reason)
231            }
232            X86Error::InvalidOperand {
233                operand_index,
234                reason,
235            } => write!(f, "invalid operand {}: {}", operand_index, reason),
236            X86Error::InvalidInstruction { opcode, reason } => {
237                write!(f, "invalid instruction 0x{:x}: {}", opcode, reason)
238            }
239            X86Error::InvalidEncoding { encoding, reason } => {
240                write!(f, "invalid encoding {}: {}", encoding, reason)
241            }
242            X86Error::InvalidModRM { modrm, reason } => {
243                write!(f, "invalid ModRM byte 0x{:02x}: {}", modrm, reason)
244            }
245            X86Error::InvalidSIB { sib, reason } => {
246                write!(f, "invalid SIB byte 0x{:02x}: {}", sib, reason)
247            }
248            X86Error::InvalidDisplacement {
249                value,
250                size,
251                reason,
252            } => write!(
253                f,
254                "invalid displacement 0x{:x} (size {}): {}",
255                value, size, reason
256            ),
257            X86Error::InvalidImmediate {
258                value,
259                size,
260                reason,
261            } => {
262                write!(
263                    f,
264                    "invalid immediate 0x{:x} (size {}): {}",
265                    value, size, reason
266                )
267            }
268            X86Error::InvalidRegister {
269                reg_id,
270                reg_type,
271                reason,
272            } => write!(
273                f,
274                "invalid register {} (type {}): {}",
275                reg_id, reg_type, reason
276            ),
277            X86Error::InvalidMemoryOperand {
278                base,
279                index,
280                scale,
281                offset,
282                reason,
283            } => write!(
284                f,
285                "invalid memory operand [base={:?}, index={:?}, scale={}, offset={}]: {}",
286                base, index, scale, offset, reason
287            ),
288            X86Error::InvalidVSIB { index_reg, reason } => {
289                write!(f, "invalid VSIB index register {}: {}", index_reg, reason)
290            }
291            X86Error::InvalidMasking { mask_reg, reason } => {
292                write!(f, "invalid mask register {}: {}", mask_reg, reason)
293            }
294            X86Error::InvalidBroadcast { reason } => {
295                write!(f, "invalid broadcast: {}", reason)
296            }
297            X86Error::InvalidRoundingControl { rc, reason } => {
298                write!(f, "invalid rounding control 0x{:x}: {}", rc, reason)
299            }
300            X86Error::InvalidEVEX { field, reason } => {
301                write!(f, "invalid EVEX field '{}': {}", field, reason)
302            }
303            X86Error::InvalidVEX { field, reason } => {
304                write!(f, "invalid VEX field '{}': {}", field, reason)
305            }
306            X86Error::TooLongInstruction { length, max_length } => write!(
307                f,
308                "instruction too long: {} bytes (max {})",
309                length, max_length
310            ),
311            X86Error::SegmentOverrideNotAllowed { segment, reason } => {
312                write!(f, "segment override {} not allowed: {}", segment, reason)
313            }
314            X86Error::AddressSizeMismatch { expected, actual } => write!(
315                f,
316                "address size mismatch: expected {} bytes, got {}",
317                expected, actual
318            ),
319            X86Error::OperandSizeMismatch { expected, actual } => write!(
320                f,
321                "operand size mismatch: expected {} bytes, got {}",
322                expected, actual
323            ),
324            X86Error::InvalidRIPRelative { offset, reason } => {
325                write!(f, "invalid RIP-relative offset {}: {}", offset, reason)
326            }
327            X86Error::InvalidLabel { label_id, reason } => {
328                write!(f, "invalid label {}: {}", label_id, reason)
329            }
330            X86Error::InvalidSymbol { symbol_id, reason } => {
331                write!(f, "invalid symbol {}: {}", symbol_id, reason)
332            }
333            X86Error::InvalidRelocation { reloc_type, reason } => {
334                write!(f, "invalid relocation {}: {}", reloc_type, reason)
335            }
336            X86Error::InvalidOperandCombination { mnemonic } => {
337                write!(
338                    f,
339                    "invalid operand combination for instruction `{}`",
340                    mnemonic
341                )
342            }
343        }
344    }
345}