asm_rs/lib.rs
1//! # asm-rs — Pure Rust Multi-Architecture Assembly Engine
2//!
3//! `asm-rs` is a pure Rust, zero-C-dependency, multi-architecture runtime assembler
4//! that turns human-readable assembly text into machine-code bytes.
5//!
6//! ## Quick Start
7//!
8//! ```rust
9//! use asm_rs::{assemble, Arch};
10//!
11//! let code = assemble("nop", Arch::X86_64).unwrap();
12//! assert_eq!(code, vec![0x90]);
13//! ```
14//!
15//! ## Features
16//!
17//! - **Pure Rust** — no C/C++ FFI, no LLVM, no system assembler at runtime.
18//! - **Multi-arch** — x86, x86-64, ARM, AArch64, RISC-V (feature-gated).
19//! - **Runtime text parsing** — assemble from strings at runtime.
20//! - **`no_std` + `alloc`** — embeddable in firmware, kernels, WASM.
21//! - **Labels & branch relaxation** — automatic forward/backward label resolution.
22//!
23//! ## Where to look next
24//!
25//! - The [guide and ISA reference](https://hupe1980.github.io/asm-rs/) cover
26//! syntax dialects, directives, resource limits and per-architecture
27//! instruction tables.
28//! - [`Assembler`] is the builder API for labels, base addresses, listings and
29//! resource limits; [`assemble`] is the one-shot shortcut.
30
31#![cfg_attr(not(feature = "std"), no_std)]
32#![forbid(unsafe_code)]
33// ── Pedantic lint policy ─────────────────────────────────────────────────
34// An assembler intentionally performs many narrowing / sign-changing casts
35// between integer widths (i128→u8, u8→u32, etc.) and uses dense hex literals
36// without separators (0xFFD0, 0x0F38F6). The lints below are expected and
37// acceptable in this context.
38#![allow(
39 clippy::cast_possible_truncation,
40 clippy::cast_sign_loss,
41 clippy::cast_lossless,
42 clippy::cast_possible_wrap,
43 clippy::unreadable_literal,
44 clippy::match_same_arms,
45 clippy::redundant_closure_for_method_calls,
46 clippy::bool_to_int_with_if,
47 clippy::wildcard_imports,
48 clippy::enum_glob_use,
49 clippy::needless_raw_string_hashes,
50 clippy::semicolon_if_nothing_returned,
51 clippy::must_use_candidate,
52 clippy::module_name_repetitions,
53 clippy::uninlined_format_args,
54 clippy::doc_markdown,
55 clippy::similar_names,
56 clippy::case_sensitive_file_extension_comparisons,
57 clippy::fn_params_excessive_bools,
58 clippy::too_many_lines,
59 clippy::single_match_else,
60 clippy::manual_let_else,
61 clippy::unnecessary_wraps,
62 clippy::unused_self,
63 clippy::map_unwrap_or,
64 clippy::many_single_char_names,
65 clippy::redundant_else,
66 clippy::return_self_not_must_use,
67 clippy::missing_errors_doc,
68 clippy::needless_continue
69)]
70
71extern crate alloc;
72
73#[cfg(feature = "aarch64")]
74pub(crate) mod aarch64;
75#[cfg(feature = "arm")]
76pub(crate) mod arm;
77/// Public assembler API — builder pattern, one-shot assembly, and `AssemblyResult`.
78pub mod assembler;
79/// x86-64 instruction encoder (REX, ModR/M, SIB, immediate, relocation).
80pub mod encoder;
81/// Error types and source-span diagnostics.
82pub mod error;
83/// Intermediate representation: registers, operands, instructions, directives.
84pub mod ir;
85/// Zero-copy lexer (tokenizer) with span tracking.
86pub mod lexer;
87/// Fragment-based linker: label resolution, branch relaxation, patching.
88pub mod linker;
89/// Peephole optimizations for instruction encoding.
90pub mod optimize;
91/// Intel-syntax parser producing IR statements.
92pub mod parser;
93/// Preprocessor: macros, repeat loops, and conditional assembly.
94pub mod preprocessor;
95#[cfg(feature = "riscv")]
96pub(crate) mod riscv;
97#[cfg(any(feature = "x86", feature = "x86_64"))]
98pub(crate) mod x86;
99
100// Re-exports
101pub use assembler::{Assembler, AssemblyResult, ResourceLimits};
102pub use encoder::RelocKind;
103pub use error::{ArchName, AsmError, Span};
104pub use ir::{
105 AddrMode, AlignDirective, Arch, BroadcastMode, ConstDef, DataDecl, DataSize, DataValue, Expr,
106 FillDirective, Instruction, MemoryOperand, Mnemonic, Operand, OperandList, OperandSize,
107 OptLevel, OrgDirective, Prefix, PrefixList, Register, ShiftAmount, ShiftOp, SpaceDirective,
108 Statement, SvePredQual, Syntax, VectorArrangement, X86Mode,
109};
110pub use linker::AppliedRelocation;
111pub use preprocessor::Preprocessor;
112
113use alloc::vec::Vec;
114
115/// Assemble a string of assembly into machine code bytes.
116///
117/// Semicolons or newlines separate instructions.
118/// Labels are defined with a trailing colon: `loop:`
119///
120/// # Errors
121///
122/// Returns [`AsmError`] if the input contains syntax errors, unknown
123/// mnemonics, invalid operand combinations, undefined labels, or any
124/// other encoding issue.
125///
126/// # Examples
127///
128/// ```rust
129/// use asm_rs::{assemble, Arch};
130///
131/// let code = assemble("nop", Arch::X86_64).unwrap();
132/// assert_eq!(code, vec![0x90]);
133/// ```
134pub fn assemble(source: &str, arch: Arch) -> Result<Vec<u8>, AsmError> {
135 assemble_at(source, arch, 0)
136}
137
138/// Assemble with an explicit base virtual address.
139///
140/// # Errors
141///
142/// Returns [`AsmError`] on assembly failure (see [`assemble`] for details).
143///
144/// # Examples
145///
146/// ```rust
147/// use asm_rs::{assemble_at, Arch};
148///
149/// let code = assemble_at("nop", Arch::X86_64, 0x1000).unwrap();
150/// assert_eq!(code, vec![0x90]);
151/// ```
152pub fn assemble_at(source: &str, arch: Arch, base_addr: u64) -> Result<Vec<u8>, AsmError> {
153 let mut asm = Assembler::new(arch);
154 asm.base_address(base_addr);
155 asm.emit(source)?;
156 let result = asm.finish()?;
157 Ok(result.into_bytes())
158}
159
160/// Assemble with external labels pre-defined at known addresses.
161///
162/// # Errors
163///
164/// Returns [`AsmError`] on assembly failure (see [`assemble`] for details).
165///
166/// # Examples
167///
168/// ```rust
169/// use asm_rs::{assemble_with, Arch};
170///
171/// let code = assemble_with("nop", Arch::X86_64, 0x0, &[]).unwrap();
172/// assert_eq!(code, vec![0x90]);
173/// ```
174pub fn assemble_with(
175 source: &str,
176 arch: Arch,
177 base_addr: u64,
178 external_labels: &[(&str, u64)],
179) -> Result<Vec<u8>, AsmError> {
180 let mut asm = Assembler::new(arch);
181 asm.base_address(base_addr);
182 for &(name, addr) in external_labels {
183 asm.define_external(name, addr);
184 }
185 asm.emit(source)?;
186 let result = asm.finish()?;
187 Ok(result.into_bytes())
188}