Skip to main content

ras/
lib.rs

1//! ras - drop-in replacement for as / GAS
2//!
3//! Assembly source (.s) to relocatable object files (.o).
4//! Supported: x86_64 and AArch64; ELF on Linux/BSD/Redox.
5
6mod aarch64_ldst_imm64;
7
8pub mod assembler;
9pub mod encoder;
10pub mod error;
11pub mod object;
12pub mod parser;
13pub mod runtime;
14pub mod target;
15
16pub use assembler::RasAssembler;
17pub use error::RasError;
18pub use object::ObjectWriteOptions;
19pub use runtime::{ExecutableMemory, RasRuntime};
20pub use target::{
21    is_assembly_target_supported, is_jit_arch_supported, is_object_file_supported,
22    supported_jit_targets_hint,
23};
24
25use lamina_platform::{TargetArchitecture, TargetOperatingSystem};
26
27/// Main assembler interface
28pub struct Ras {
29    assembler: RasAssembler,
30}
31
32impl Ras {
33    pub fn new(
34        target_arch: TargetArchitecture,
35        target_os: TargetOperatingSystem,
36    ) -> Result<Self, RasError> {
37        Self::with_object_write_options(target_arch, target_os, ObjectWriteOptions::default())
38    }
39
40    pub fn with_object_write_options(
41        target_arch: TargetArchitecture,
42        target_os: TargetOperatingSystem,
43        object_write_options: ObjectWriteOptions,
44    ) -> Result<Self, RasError> {
45        Ok(Self {
46            assembler: RasAssembler::with_object_write_options(
47                target_arch,
48                target_os,
49                object_write_options,
50            )?,
51        })
52    }
53
54    pub fn assemble(
55        &mut self,
56        asm_text: &str,
57        output_path: &std::path::Path,
58    ) -> Result<(), RasError> {
59        self.assembler
60            .assemble_text_to_object(asm_text, output_path)
61    }
62
63    pub fn assemble_file(
64        &mut self,
65        input_path: &std::path::Path,
66        output_path: &std::path::Path,
67    ) -> Result<(), RasError> {
68        let asm_text = std::fs::read_to_string(input_path)
69            .map_err(|e| RasError::IoError(format!("Failed to read input: {}", e)))?;
70        self.assemble(&asm_text, output_path)
71    }
72
73    #[cfg(feature = "encoder")]
74    pub fn compile_mir_to_binary(
75        &mut self,
76        module: &lamina_mir::Module,
77    ) -> Result<Vec<u8>, RasError> {
78        self.assembler.compile_mir_to_binary(module)
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_assemble_gas_style_to_elf() {
88        let asm = r#"
89.text
90.globl main
91main:
92    movq $42, %rax
93    ret
94"#;
95        let mut ras =
96            Ras::new(TargetArchitecture::X86_64, TargetOperatingSystem::Linux).expect("ras new");
97        let path = std::env::temp_dir().join("ras_gas_style_test.o");
98        ras.assemble(asm, &path).expect("assemble");
99        let buf = std::fs::read(&path).expect("read");
100        let _ = std::fs::remove_file(&path);
101        assert!(buf.len() >= 64);
102        assert_eq!(&buf[0..4], &[0x7f, b'E', b'L', b'F']);
103    }
104
105    #[test]
106    fn test_assemble_memory_operands() {
107        let asm = r#"
108.text
109.globl main
110main:
111    pushq %rbp
112    movq %rsp, %rbp
113    subq $16, %rsp
114    movq $42, %rax
115    movq %rax, -8(%rbp)
116    movq -8(%rbp), %rax
117    addq $16, %rsp
118    popq %rbp
119    ret
120"#;
121        let mut ras =
122            Ras::new(TargetArchitecture::X86_64, TargetOperatingSystem::Linux).expect("ras new");
123        let path = std::env::temp_dir().join("ras_mem_test.o");
124        ras.assemble(asm, &path).expect("assemble");
125        let buf = std::fs::read(&path).expect("read");
126        let _ = std::fs::remove_file(&path);
127        assert!(buf.len() >= 64);
128        assert_eq!(&buf[0..4], &[0x7f, b'E', b'L', b'F']);
129    }
130
131    #[test]
132    fn test_assemble_jmp_label_resolution() {
133        let asm = r#"
134.text
135.globl main
136main:
137    pushq %rbp
138    movq %rsp, %rbp
139    subq $16, %rsp
140    movq $0, %rax
141    jmp .L_exit
142.L_exit:
143    addq $16, %rsp
144    popq %rbp
145    ret
146"#;
147        let mut ras =
148            Ras::new(TargetArchitecture::X86_64, TargetOperatingSystem::Linux).expect("ras new");
149        let path = std::env::temp_dir().join("ras_jmp_test.o");
150        ras.assemble(asm, &path).expect("assemble");
151        let buf = std::fs::read(&path).expect("read");
152        let _ = std::fs::remove_file(&path);
153        assert!(buf.len() >= 64);
154        assert_eq!(&buf[0..4], &[0x7f, b'E', b'L', b'F']);
155    }
156
157    #[test]
158    fn test_assemble_rip_relative_lea_label() {
159        // Verify that `leaq label(%rip), %reg` with an .asciz data label assembles
160        // without error and that the RIP-relative displacement is correct.
161        let asm = r#"
162.section .rodata
163.L_fmt: .asciz "%lld\n"
164.text
165.globl get_fmt
166get_fmt:
167    leaq .L_fmt(%rip), %rax
168    ret
169"#;
170        let mut ras =
171            Ras::new(TargetArchitecture::X86_64, TargetOperatingSystem::Linux).expect("ras new");
172        let path = std::env::temp_dir().join("ras_rip_rel_test.o");
173        ras.assemble(asm, &path).expect("assemble should succeed");
174        let buf = std::fs::read(&path).expect("read");
175        let _ = std::fs::remove_file(&path);
176        // Must be a valid ELF file
177        assert_eq!(&buf[0..4], &[0x7f, b'E', b'L', b'F']);
178    }
179
180    #[test]
181    fn test_assemble_arx64_label_resolution_to_elf() {
182        let asm = r#"
183.text
184.globl main
185main:
186    addi r5, r0, 1
187    j done
188    addi r5, r0, 2
189done:
190    ret
191"#;
192        let mut ras =
193            Ras::new(TargetArchitecture::Arx64, TargetOperatingSystem::Linux).expect("ras new");
194        let path = std::env::temp_dir().join("ras_arx64_label_test.o");
195        ras.assemble(asm, &path).expect("assemble");
196        let buf = std::fs::read(&path).expect("read");
197        let _ = std::fs::remove_file(&path);
198        assert!(buf.len() >= 64);
199        assert_eq!(&buf[0..4], &[0x7f, b'E', b'L', b'F']);
200    }
201}