├── .gitignore ├── Makefile └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | main.o 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | main: main.o 2 | ld -o main main.o -static --omagic 3 | strip -s main 4 | 5 | main.o: main.rs 6 | rustc main.rs --emit=obj \ 7 | -C opt-level=z \ 8 | -C panic="abort" \ 9 | -C strip="debuginfo" 10 | 11 | clean: 12 | rm main.o main 13 | -------------------------------------------------------------------------------- /main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::panic::PanicInfo; 5 | use core::arch::asm; 6 | 7 | #[panic_handler] 8 | fn panic(_info: &PanicInfo) -> ! { 9 | loop {} 10 | } 11 | 12 | #[cfg(target_arch="aarch64")] 13 | fn exit(exit_code: usize) { 14 | unsafe { 15 | asm! ( 16 | "mov x0, {exit_code}", 17 | "mov w8, 93", 18 | "svc 0", 19 | exit_code = in(reg) exit_code 20 | ); 21 | } 22 | } 23 | 24 | #[cfg(target_arch="x86_64")] 25 | fn exit(exit_code: usize) { 26 | unsafe { 27 | asm! ( 28 | "mov rdi, {exit_code}", 29 | "mov rax, 60", 30 | "syscall", 31 | exit_code = in(reg) exit_code 32 | ); 33 | } 34 | } 35 | 36 | 37 | #[cfg(target_arch="aarch64")] 38 | fn print(text: &str) { 39 | let ptr = text.as_ptr(); 40 | let size: usize = text.len(); 41 | unsafe { 42 | asm! ( 43 | "mov x0, 1", 44 | "mov x1, {ptr}", 45 | "mov x2, {size}", 46 | "mov w8, 64", 47 | "svc 0", 48 | ptr = in(reg) ptr, 49 | size = in(reg) size 50 | ); 51 | } 52 | } 53 | 54 | #[cfg(target_arch="x86_64")] 55 | fn print(text: &str) { 56 | let ptr = text.as_ptr(); 57 | let size: usize = text.len(); 58 | unsafe { 59 | asm! ( 60 | "mov rdi, 1", 61 | "mov rsi, {ptr}", 62 | "mov rdx, {size}", 63 | "mov rax, 1", 64 | "syscall", 65 | ptr = in(reg) ptr, 66 | size = in(reg) size, 67 | // Mark all registers which are not preserved by the "C" calling 68 | // convention as clobbered. 69 | // https://doc.rust-lang.org/rust-by-example/unsafe/asm.html#symbol-operands-and-abi-clobbers 70 | // 71 | // Without this the program just starts printing garbage 72 | // TODO: we may want to do something similar for aarch64 73 | clobber_abi("C") 74 | ); 75 | } 76 | } 77 | 78 | #[no_mangle] 79 | unsafe fn memset(dest: *mut u8, c: u8, n: usize) { 80 | for i in 0..n { 81 | *dest.wrapping_add(i.into()) = c; 82 | } 83 | } 84 | 85 | #[cfg(target_arch="aarch64")] 86 | fn input(buffer: &mut [u8; 1024]) -> &str { 87 | let ptr = (*buffer).as_mut_ptr(); 88 | 89 | unsafe { 90 | asm!( 91 | "mov x0, 0", 92 | "mov x1, {buf}", 93 | "mov x2, 1024", 94 | "mov w8, 63", 95 | "svc 0", 96 | buf = in(reg) ptr 97 | ); 98 | 99 | // TODO: strip off newlines in aarch64 (see x86_64 implementation) 100 | return core::str::from_utf8_unchecked(buffer); 101 | } 102 | } 103 | 104 | #[cfg(target_arch="x86_64")] 105 | fn input(buffer: &mut [u8; 1024]) -> &str { 106 | let ptr = (*buffer).as_mut_ptr(); 107 | let mut n: usize; 108 | 109 | unsafe { 110 | asm!( 111 | "mov rdi, 0", 112 | "mov rsi, {buf}", 113 | "mov rdx, 1024", 114 | "mov rax, 0", 115 | "syscall", 116 | buf = in(reg) ptr, 117 | out("rax") n, 118 | clobber_abi("C"), 119 | ); 120 | // Trim the trailing newlines 121 | while n > 0 && *buffer.get_unchecked(n-1) == b'\n' { 122 | n -= 1; 123 | } 124 | return core::str::from_utf8_unchecked(buffer.get_unchecked(0..n)); 125 | } 126 | } 127 | 128 | #[no_mangle] 129 | pub extern fn _start() { 130 | let mut buffer = [0u8; 1024]; 131 | print("What is your name? "); 132 | let name = input(&mut buffer); 133 | print("Hello, "); 134 | print(name); 135 | print("!"); 136 | exit(0); 137 | } 138 | --------------------------------------------------------------------------------