user12613899
user12613899

Reputation:

Two functions/subroutines in ARM assembly language

I am stuck with an exercise of ARM. The following program should calculate the result of 2((x-1)^2 + 1) but there is a mistake in the program that leads it into an infinite loop. I think that I still don't understand completely subroutines and for this reason I am not seeing where the mistake is.

_start:
 mov r0, #4 
 bl g
 mov r7, #1
 swi #0

f:
 mul r1, r0, r0
 add r0, r1, #1
 mov pc, lr

g: 
 sub r0, r0, #1
 bl f
 add r0, r0, r0
 mov pc, lr

The infinite loop starts in subroutine g: in the line of mov pc, lr and instead of returning to _start it goes to the previous line add r0, r0, r0 and then again to the last line of subroutine g:. So I guess that the problem is the last line of subroutine g: but I can't find the way to return to _start without using mov pc, lr. I mean, this should be the command used when we have a branch with link.

Also, in this case r0 = 4, so the result of the program should be 20.

Upvotes: 2

Views: 1518

Answers (1)

Frant
Frant

Reputation: 5925

This is because you don't save lr on the stack prior to calling f, and the initial return address was therefore lost: if you only have one level of subroutine calls, using lr without saving it is fine, but if you have more then one, you need to preserve the previous value of lr.

For example, when compiling this C example using Compiler Explorer with ARM gcc 4.56.4 (Linux), and options -mthumb -O0,

void f()
{
}

void g()
{
    f();
}

void start()
{
    g();
}

The generated code will be:

f():
        push    {r7, lr}
        add     r7, sp, #0
        mov     sp, r7
        pop     {r7, pc}
g():
        push    {r7, lr}
        add     r7, sp, #0
        bl      f()
        mov     sp, r7
        pop     {r7, pc}
start():
        push    {r7, lr}
        add     r7, sp, #0
        bl      g()
        mov     sp, r7
        pop     {r7, pc}

If you were running this on bare metal, not under Linux, you'd need your stack pointer to be initialized a correct value. Assuming you are running from RAM on a bare-metal system/simulator, you could setup a minimal stack of 128 bytes:

       .text
       .balign 8
_start:
        adr r0, . + 128  // set top of stack at _start + 128
        mov sp, r0
        ...

But it looks like you're writing a Linux executable that exits with a swi/r7=1 exit system call. So don't do that, it would make your program crash when it tries to write to the stack.

Upvotes: 3

Related Questions