Skip to main content

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}