Search code examples
assemblyarmreturnprintf

How to let the program exit with a 0 after printf finished?


This little program just prints the 2 argv written in shell.

.global main

main:
    ldr r2, [r1,#8]
    ldr r1, [r1,#4]
    ldr r0, =message_format
    bl  printf
    mov r0, #0

message_format:
    .asciz "argv: %s %s\n"

I want it to exit with a 0 code so I put #0 inside r0, but:

root@kali-pi:~/Desktop/arm_test# ./arggggg 1 2
argv: 1 2
Segmentation fault
root@kali-pi:~/Desktop/arm_test# echo $?
139

How could I fix this?


Solution

  • How could I fix this?

    Assembly programs do not have an "end":

    Because the memory of your computer is typically longer than the assembly program, there is some data following the assembly program. And because the CPU does not destinguish between data and code, it will interpret the bytes following the assembly program as code.

    Example:

    Let's say you have Thumb code and the RAM after your program contains the bytes 1, 2, 3, and 4. Then the CPU will interpret your program as:

    ldr r2, [r1,#8]
    ldr r1, [r1,#4]
    ldr r0, =message_format
    bl  printf
    mov r0, #0
    lsls r1, r0, #8
    lsls r3, r0, #16
    ...
    

    The CPU will run until some instruction is found that cases an error.

    You have two possibilities that the other users already wrote in a comment:

    1) Explicitly use the exit() function or similar

    You may call the exit() function to exit from your program immediately.

    This is done like calling the printf() function.

    2) Return from the main() function

    You may use bx lr to return from the main() function.

    Unfortunately, the printf() function will overwrite the value of lr, so a simple bx lr would cause an endless loop.

    You'll have to save the lr register before calling printf() and to restore it later on.

    Example:

    push {lr}
    ldr r2, [r1,#8]
    ldr r1, [r1,#4]
    ldr r0, =message_format
    bl  printf
    mov r0, #0
    pop {lr}
    bx lr
    

    If you are compiling Thumb code, pop {lr} won't work. Use pop {r1} and bx r1 instead.

    If you are sure that both the main() function and the function that called the main() function are of the same code type (ARM or Thumb), you can also use only one instruction at the end: pop {pc}.

    This is the case when working on a Cortex M CPU that only supports Thumb code or when working on an old ARM CPU that did not support Thumb.