assemblyrustinline-assemblyarm64

Using string constants in Rust inline assembly


I am trying to execute a write syscall using inline assembly in Rust. Basically I'm trying to reproduce this hello world example. I'm trying to pass in a reference to a string slice as my message, but I'm getting the following error which seems to contradict its self:

cannot use value of type `*const str` for inline assembly
only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly

The code is below, but my questions are:

  1. Is there a more suitable sort of pointer than a *const str?
  2. I'm still in the early days of working with assembly, are there other glaring problems that I'm missing?
use std::arch::asm;

fn main() {
    let msg = "Hello World!\n";
    let len = msg.len();
    unsafe {
        asm!(
            "mov X0,  #1",     // specify stdout
            "adr X1,  {msg}",  // point to message
            "mov X2,  {len}",  // specify message length
            "mov X16, #4",     // specify syscall number for 'write'
            "svc #0x80",       // syscall
            msg = in(reg) msg,
            len = in(reg) len,
       );
    }
}

This is running on MacOS v13.6, on an M1 chip (Armv8.5-a).


Solution

  • Well there is a slight difference between &str which is a wide reference, *const str a wide pointer and a thin pointer as required by the assembly. Though the error message could make the requirement for a thin pointer clearer. Fortunately there is a method to turn a &str into an apropriate pointer unsurprisingly called as_ptr

    use std::arch::asm;
    
    fn main() {
        let msg_str = "Hello World!\n";
        let len = msg_str.len();
        let msg = msg_str.as_ptr();
        unsafe {
            asm!(
                "mov X0,  #1",     // specify stdout
                "adr X1,  {msg}",  // point to message
                "mov X2,  {len}",  // specify message length
                "mov X16, #4",     // specify syscall number for 'write'
                "svc #0x80",       // syscall
                msg = in(reg) msg,
                len = in(reg) len,
           );
        }
    }
    

    Does get further, but I can't test the arm assembly so it still fails to compile for me.