This page looks best with JavaScript enabled

ARM Assembly - Hello World!

 ·  ☕ 3 min read  ·  ✍️ devoalda

Installation

I’m using WSL to build and run the ARM assembly code, these are the installation packages I used:

1
sudo apt install gcc-arm-linux-gnueabi make git-core ncurses-dev qemu qemu-user

{:file="Installation on Windows WSL”}

Code

Simple program to return a number to shell

.global _start
.section .text

_start:
    mov r7, #0x1    @ System call number for exit (0x1)
    mov r0, #32     @ Exit code 32 (Returns 32 to the shell)
    swi 0           @ Software interrupt

.section .data

{:file="test.s”}

This returns

1
2
echo $?
32

{:file="Shell output”}

The echo $? command returns the exit code of the last command.

The ARM systemcall table is available here. I’ve used the exit system call, which is 0x1.

Hello World

.global _start
.section .text

_start:
    mov r7, #0x4        @ System call number for write (0x4)
    mov r0, #0x1        @ File descriptor (stdout)

    ldr r1, =message    @ Load address of message into r1
    ldr r2, =len        @ Load length of message into r2
    swi 0               @ Software interrupt

    mov r7, #0x1        @ System call number for exit (0x1)
    swi 0               @ Software interrupt

.section .data
message:
    .ascii "Welcome to DevBlog!\n"

len = . - message       @ Length of message

{:file="armasm.s”}

This returns

1
2
$ qemu-arm ./armasm.elf
Welcome to DevBlog!

{:file="Shell output”}

The swi instruction is a software interrupt instruction that allows the program to call the kernel, where it will use register r7 to determine which system call to use and registers r0 to r4 to pass parameters to the system call.

Using the system call table, loading 0x4 into r7 calls the write system call.

The write system call takes 3 arguments:

  • r0 is the file descriptor for STDOUT (1)
  • r1 is the address of the message
  • r2 is the length of the message.

Loading the address of the message into r1 and the length of the message into r2 is done using the ldr instruction and r0 is loaded with the file descriptor 1 for STDOUT. This directly outputs the message to STDOUT.

File Descriptors

These are the file descriptors for the standard input, output and error:

File Descriptor Description
0 STDIN
1 STDOUT
2 STDERR

Compilation

To compile the code(s), I’m using the following command:

1
2
arm-linux-gnueabi-as armasm.s -o armasm.o
arm-linux-gnueabi-gcc-9 armasm.o -o armasm.elf -nostdlib

{:file="Compilation”}

The arm-linux-gnueabi-as command compiles the assembly code into an object file, which is then linked with the arm-linux-gnueabi-gcc-9 command to create an executable file.

The -nostdlib flag is used to prevent the compiler from linking the standard libraries, which is not needed for this program.

Makefile

Alternatively, you can use a Makefile to compile the code(s):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CFLAGS  = -nostdlib
NAME = armasm

$(NAME): $(NAME).o
	arm-linux-gnueabi-gcc-9 $(NAME).o -o $(NAME).elf $(CFLAGS)

$(NAME).o: $(NAME).s
	arm-linux-gnueabi-as $(NAME).s -o $(NAME).o

clean:
	rm -f *.o

This allows you to compile the code(s) using the command make and clean the object files using the command make clean.

Running

To run the code, I’m using the following command:

1
qemu-arm ./armasm.elf

Using the qemu emulator, you can run the code on your computer without having to use a Raspberry Pi or other ARM device.

References

Share on

Devoalda
WRITTEN BY
devoalda
Technophile