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