HomeAboutArticles

ARM64 — Using the stack

Start page1 page2 page3 page4 page5 page6 page7 page8 page9 page10

It is usual for functions to call other functions, even recursively, and then you need to take care of parameters and local variables, and return addresses — and to handle that we introduce the stack.

Programs running have received a piece of memory for stack space, and an initialized stackpointer, sp.

We will see two strange things. First a drawing:

This means that the stack pointer points to a 16 bytes stack element. The x-registers are 8 bytes, so you can store two x-registers at a time. And that's exactly what we a going to do.

At the start of any function we will push two important registers on the stack, and at the end we will pop them off again — just before issuing the ret-instruction.

The two registers are:

So functions will mostly look like this, pushing the return address and the frame pointer on the stack:

func1:    stp x29,x30,[sp, #-16]!   /* push 2 registers */
          ...
          ldp x29, x30, [sp], #16   /* pop 2 registers */
          ret

The syntax for push and pop is strange. For now we will accept it with no explanation.

Modified lib3.s

The library we made earlier worked without the stack, but let's introduce it for the _phex function. There is no need in the _exit function.

In addition let's prove that the registers are restored by moving some arbitrary values into the two registers — and remove the two lines later on:

        .text
        .global _phex
        .global _exit

_phex:  stp x29,x30,[sp, #-16]!
        movz x3,16
        adr x2,message

again:  ror x0,x0,60
        and x1,x0,0xF
        cmp x1,10
        bge isAtoF
        add x1,x1,48
        b  done
isAtoF: add x1,x1,55
done:   strb w1,[x2]
        add x2,x2,1

        subs x3,x3,1
        bne again

        mov x0,1
        adr x1,message
        mov x2,17
        mov x8,0x40
        svc 0
        movz x29,123  /* to be removed */
        movz x30,456  /* to be removed */
        ldp x29, x30, [sp], #16
        ret

_exit:  mov x0,0
        mov x8, 93
        svc 0


        .data
message: .ascii "****************\n"

Everything should still work fine.

But actually the variable message is local to phex. It doesn't need to be known elsewhere, there is no need for it to exist unless we are in _phex, and if the function were recursive we would need more than one variable, one for each called function.

This is where we will use the stack: allocate space for local variables, and use the frame pointer to access these variables.

Confused? You won't be after the next episode of ARM64 Assembly Programming.

Start page1 page2 page3 page4 page5 page6 page7 page8 page9 page10

2025-06-16