Calling a Function in (ARM64) Assembly

In my last post, I reversed a string. I want to build on that to make reusable code; I want to reverse multiple strings. I do this by turning the reverse code into a function and then call it.

The first step is to reorder the code so that the logic to reverse the string is at the end, and is called using the BL (Branch with link) instruction. We also need to add a return at the end of our code so that we can continue processing. We make sure that the code to exit the program sits in between the calling point and the function. The body of the code now looks like this:

    mov  x0, #1   /* fd := STDOUT_FILENO */
    ldr  x1, =msg  /* buf := msg */
    ldr  x2, =len  /* count = lent */
 
    bl reverse
 
    /* syscall exit (int status) */
    mov    x0, #0    /* status  := 0 */
    mov    w8,  #93  /* exit is syscall #1 */
    svc    #0
 
reverse:
    add  X4, X1, 0    
    add X6, x1, x2
    sub X6, X6, #1
 
loop:
    sub X6, X6, #1
 
    ldrb w5, [x4] 
    ldrb w7, [x6] 
 
    strb w5, [X6]
    strb w7, [X4]
 
    add X4, X4, #1
    cmp X4, X6
    b.lt loop
 
    mov  w8, #64
    svc  #0
 
   ret

Running this code looks like this:

$ make reversed ; ./reversed 
as -g -o reversed.o reversed.s
ld -o reversed reversed.o
CBA

Now I can add a couple other strings and call them, too.

And…I just learned something. The first time I added the strings, I added them like this:

msg1:
    .ascii        "ABC\n"
msg2:
    .ascii        "Hello, World!\n"
msg3:
   .ascii        "1234567890\n"
len1 = . - msg1
len2 = . - msg2
len3 = . - msg3

This is obviously wrong, and I understand why. Now. What was interesting was how it manifested itself. This was the output:

$ make reversed ; ./reversed 
as -g -o reversed.o reversed.s
ld -o reversed reversed.o
0987654321
!dlroW ,olleH
CBA

Yes! It worked! Um, wait…I didn’t actuially call the function multiple times? What is going on here?

The error, of course, is due to the calculation of the length of the string. The macro len1 = . – msg1 is based on their locations in code. This calculated the length of the entire string buffer, multiple lines, including the \n characters, and reversed the entire buffer in one pass.

How are we going to write a test for this case into our code? Call them in different orders. Instead of calling reverse for msg1, then 2, then 3, I’ll call 2 first, then 1, then 3. Like this:

    mov  x0, #1   /* fd := STDOUT_FILENO */
    ldr  x1, =msg2  /* buf := msg */
    ldr  x2, =len2  /* count = lent */
    bl reverse
 
 
    mov  x0, #1   /* fd := STDOUT_FILENO */
    ldr  x1, =msg1  /* buf := msg */
    ldr  x2, =len1  /* count = lent */
    bl reverse
 
 
    mov  x0, #1   /* fd := STDOUT_FILENO */
    ldr  x1, =msg3  /* buf := msg */
    ldr  x2, =len3  /* count = lent */
    bl reverse
$ make reversed ; ./reversed 
as -g -o reversed.o reversed.s
ld -o reversed reversed.o
!dlroW ,olleH
CBA
0987654321


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.