0

Trying to capture two characters and new line from user input.

The program prints the 3 helloworlds to screen and then users can type in some characters. Everything seems to work, but it doesn't print the input

I suspect it is due to the way I operate on the X1 register in the _read function, or the way the buffer is allocated

No errors are reported when running the code.

The code is compiled using the following command. It should run on a Mac M1

as HelloWorld.s -o HelloWorld.o && ld -macosx_version_min 12.0.0 -o HelloWorld HelloWorld.o -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch arm64 && ./HelloWorld

//HelloWorld.s

.equ SYS_WRITE, 4
.equ SYS_READ, 3
.equ NEWLN, 10


.global _start             // Provide program starting address to linker
.align 2

// Setup the parameters to print hello world
// and then call Linux to do it.

_start: 
        adr X4, helloworld1
        mov X1, X4
        bl _sizeof
        bl _print

        adr X4, helloworld2
        mov X1, X4
        bl _sizeof                
        bl _print


        adr X4, helloworld3
        mov X1, X4
        bl _sizeof
        bl _print


        bl _read
        //mov X2, 4
       // bl _sizeof
        bl _print


_exit:
        mov     X0,  X2     // Use 0 return code
        mov     X16, #1     // Service command code 1 terminates this program
        svc     0           // Call MacOS to terminate the program





_sizeof: //X1 = address, X2 = out length, string must terminate with \n
        str LR, [SP, #-16]!     //Store registers
        //str W0, [SP, #-16]!


        mov X2, #0
        __loop:
             
                ldrb W0, [X1, X2]       //load a byte into W0 (32 bit)
                add X2, X2, #1          //Add 1 offset  
                cmp W0, NEWLN             //Compare byte with \n return
                bne __loop

        //ldr W0, [SP], #16
        ldr LR, [SP], #16       //Load registers
        ret




_print: //X2 = length, X1 = address
        str LR, [SP, #-16]!     //Store registers

        mov X0, #1     // 1 = StdOut
       // mov X1, X1      // string to print
       // mov X2, X2     // length of string
        mov X16, SYS_WRITE     // MacOS write system call
        svc 0     // Call kernel to output the string
        
        ldr LR, [SP], #16       //Load registers
        ret  

_read:
//3 AUE_NULL    ALL { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } 

        str LR, [SP, #-16]!     //Store registers
        
        adr X1, msg

        mov X0, #0    // 0 = StdIn
        ldr X1, [x1]    // address to store string

        mov X2, #4     // length 
        mov X16, SYS_READ     // MacOS read system call
        svc 0     // Call system

        ldr LR, [SP], #16       //Load registers
        ret

msg: .ds 4     //memory buffer for keyboard input


helloworld1:      .ascii  "Hello World\n"
helloworld2:      .ascii  "Happy new year for 2022\n"
helloworld3:      .ascii  "Welcome to AARCH64 assembly on Mac Silicon\n"

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
siwix
  • 91
  • 1
  • 7
  • I don't think you need the `ldr X1, [x1] `. – Jester Jan 05 '22 at 12:13
  • 3
    Your `msg:` buffer is in the same section as your code, so it won't be writeable. A read system-call on it will error with `EFAULT` when checking permissions before a write from kernel space; a `scanf` library function will segfault when copying to it in user-space. – Peter Cordes Jan 05 '22 at 13:42
  • Looks like you're using Linux system call numbers; MacOS uses different numbering. The numbers for x86 are discussed [here](https://stackoverflow.com/questions/48845697/macos-64-bit-system-call-table/53905561); I don't know if ARM64 is the same or different. – Nate Eldredge Jan 05 '22 at 18:14
  • @NateEldredge arm64 Darwin uses positive numbers for unix syscalls and negative ones for mach traps (so `0x100001c` on x86_64 would become `-0x1c`, or `0xffffffe4` on arm64). Given this, it just so happens that syscalls 1, 3 and 4 correspond to `exit`, `read` and `write` . So that part should work fine. – Siguza Jan 05 '22 at 19:31

2 Answers2

1

First you need to move msg to a writeable segment:

.data
msg: .ds 4     //memory buffer for keyboard input

.text // keep everything else in __TEXT

Then, because segments may be moved around arbitrarily at link-time, Apple's toolchain will no longer allow you to use adr to get the address of that buffer - you will have to use adrp and add:

adrp x1, msg@page
add x1, x1, msg@pageoff

If you want, you can tell the linker to please optimise this back to an adr if possible:

Lloh0:
    adrp x1, msg@page
Lloh1:
    add x1, x1, msg@pageoff
.loh AdrpAdd Lloh0, Lloh1

Then you need to remove this line:

ldr X1, [x1]

That would load the contents of the buffer, which would just be null bytes.

And finally, you should change the x0 value to exit to a constant:

mov x0, 0

The value in x2 will have been clobbered at this point, and you don't need it anyway.

Siguza
  • 21,155
  • 6
  • 52
  • 89
  • Thanks so much for this answer. I didn't get it to work at first. After adding in your suggestions, it still didn't print the input. But I added in the adrp and add again after the syscall in the_read function and then it worked. – siwix Jan 06 '22 at 00:37
1

As a reference for anyone in the future looking for an example to read from Standard In on AppleSilicon (M1), this code (based on the above information) works. It takes in a string up to 20 characters and prints it back out to the Standard Output.

.global _start
.align 2

_start:
                                // READ IN FROM KEYBOARD
    mov X16, 3                  // Tell system we want to read from StdIn (#3)
    mov X0, 0                   // Focus on the keyboard (#0)
    mov X2, 20                  // Define length of string to read in

    adrp    x1, msg@page        // Load the address of the message
    add x1, x1, msg@pageoff     // Store the address to x1

    svc 0                       // Call kernel to perform the action

_write:
    mov X16, 4                  // Tell system we want to write to StdOut (#4)
    mov X0, 1                   // Focus on the screen (#1)

    adrp    x1, msg@page        // Load the address of the message
    add x1, x1, msg@pageoff     // Store the address to x1

    svc 0                       // Call kernel to perform the action

_end:
    mov X0, 0                   // Return 0 (get a run error without this)
    mov X16, 1                  // System call to terminate this program
    svc 0                       // Call kernel to perform the action

.data
msg:
    .ds 20                      // 20 bytes of memory for keyboard input

Your makefile should look like this:

temp: temp.o
    ld -o temp temp.o -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch arm64 

temp.o: temp.s
    as -arch arm64 -o temp.o temp.s