Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
Arm Community blogs
Arm Community blogs
Architectures and Processors blog "Hello World" in Assembly
  • Blogs
  • Mentions
  • Sub-Groups
  • Tags
  • Jump...
  • Cancel
More blogs in Arm Community blogs
  • AI blog

  • Announcements

  • Architectures and Processors blog

  • Automotive blog

  • Embedded and Microcontrollers blog

  • Internet of Things (IoT) blog

  • Laptops and Desktops blog

  • Mobile, Graphics, and Gaming blog

  • Operating Systems blog

  • Servers and Cloud Computing blog

  • SoC Design and Simulation blog

  • Tools, Software and IDEs blog

Tell us what you think
Tags
  • Assembly
  • GCC
  • Arm Assembly
Actions
  • RSS
  • More
  • Cancel
Related blog posts
Related forum threads

"Hello World" in Assembly

Jacob Bramley
Jacob Bramley
September 11, 2013
4 minute read time.

Chinese Version: 汇编 "Hello World"

Assembly language can be fairly daunting, even for experienced software engineers. The lists of strange instructions and squiggles can be hard to read at the best of times; indeed, that is why we use languages such as C, where the compiler worries about such things so you don't have to. However, understanding the instruction set of your processor can make C-level optimizations easier to spot and implement, and will help you to gain an understanding of what your program is really doing. In addition, it can enable you to create some finely-tuned code for specific tasks that are hard to implement in C. If nothing else, it's fun!

This post aims to provide a simple introduction to Arm assembly language. The code will be presented in such a way that you can understand what's going on without having to understand the nuances and specifics of each instruction. Future posts will explain the mechanisms in more detail.

Tools

In order to actually do anything interesting, you'll need an Arm device and a suitable tool-chain. If you have a reasonably powerful device with a desktop-like operating system (such as Ubuntu), you can work directly on the board; this is native development. On Ubuntu, you can use the built-in apt-get utility to get a tool-chain; just enter apt-get install build-essential (as root) and you'll get a moderately recent version of GCC. In this case, development becomes pretty much identical to development on your PC, except that you'll be writing Arm assembly code rather than x86 assembly code.

If you don't have a particularly powerful Arm device or you don't have a platform that allows you to easily build natively, you'll want to use a cross-compiler. You can get Code Sourcery's free Arm cross-compiler from their website; this is essentially a pre-built Arm cross-compiler (and assembler), so you don't have to worry about building one yourself. In this case, you would have to compile your code on a PC, then move the binary to your platform before executing it there.

Assembly Files

Assembly is essentially a human-readable form of machine code. Each assembly instruction maps more-or-less onto one machine instruction  so you can very finely control what the processor is doing. The syntax is much simpler than C; you can't form complex compound statements without explicitly listing the instructions required to calculate the statement. For example, the C expression a=(b+c)*d might look like this in Arm assembly:

  add r0, r1, r2
  mul r0, r3, r0
 

The expression must be split into a=(b+c) and a=a*d.

It's important to note at this stage that most assemblers use a different syntax, even though they essentially do the same job. Arm's RVCT includes an assembler that uses a different syntax to the GNU assembler in GCC, for example. Here, we use GCC syntax by default because the GCC tool-chain is readily available for free and for multiple platforms. In addition, the GNU assembler uses a different line-comment delimiter for each platform. On Arm, it is @. The GNU assembler also allows the use of C-style multi-line comments (such as "/* ... */").

Hello World

Standard C Implementation

A traditional introduction to many languages is the "Hello World" program. In C, this looks something like this:

#include <stdio.h>

int main(void) {
  printf("Hello, world.\n");
  return 0;
}
 

That's all very well and good, but what does it actually mean to the processor? How does it execute that? The assembly version of the same program is remarkably similar. I won't explain the details of each instruction here, but I'll present the code and we'll discuss the various mechanisms in future posts.

Assembly Implementation

For convenience, the full example program is attached to this page, but it is also listed below:

    .syntax unified

    @ --------------------------------
.global main
main:
    @ Stack the return address (lr) in addition to a dummy register (ip) to
    @ keep the stack 8-byte aligned.
    push    {ip, lr}

    @ Load the argument and perform the call. This is like 'printf("...")' in C.
    ldr     r0, =message
    bl      printf

    @ Exit from 'main'. This is like 'return 0' in C.
    mov     r0, #0    @ Return 0.

    @ Pop the dummy ip to reverse our alignment fix, and pop the original lr
    @ value directly into pc — the Program Counter — to return.
    pop     {ip, pc}

    @ --------------------------------
    @ Data for the printf calls. The GNU assembler's ".asciz" directive
    @ automatically adds a NULL character termination.
message:
    .asciz "Hello, world.\n"
 

We can assemble and run this program using the following (on an Arm Linux-like platform):

gcc -o hello_world hello_world.s
$ ./hello_world
 

You should then see the text "Hello, world." on the console.

If you're using a cross-compiler (such as RVCT or the Code Sourcery edition of GCC) you'll need to run the first step on your PC — probably substituting gcc with something like arm-none-linux-gnueabi-gcc — and then copy the output binary to an Arm target before running the program itself.

If you're curious about how this relates to what the C compiler would do, try compiling the C version using gcc -S hello_world.c -O2 instead of your usual compile command. You can also examine existing objects or binaries using objdump to disassemble the output. There will be a few differences, and you'll see different results if you provide different -O flags to the compiler. The compiler output will also vary between compiler versions.

The details of each mechanism and instruction will be discussed in other posts, but the approximate mapping between the C and assembly implementation should be evident from the example.

hello_world.tar.gz
Anonymous
Architectures and Processors blog
  • Introducing GICv5: Scalable and secure interrupt management for Arm

    Christoffer Dall
    Christoffer Dall
    Introducing Arm GICv5: a scalable, hypervisor-free interrupt controller for modern multi-core systems with improved virtualization and real-time support.
    • April 28, 2025
  • Getting started with AARCHMRS Features.json using Python

    Joh
    Joh
    A high-level introduction to the Arm Architecture Machine Readable Specification (AARCHMRS) Features.json with some examples to interpret and start to work with the available data using Python.
    • April 8, 2025
  • Advancing server manageability on Arm Neoverse Compute Subsystem (CSS) with OpenBMC

    Samer El-Haj-Mahmoud
    Samer El-Haj-Mahmoud
    Arm and 9elements Cyber Security have brought a prototype of OpenBMC to the Arm Neoverse Compute Subsystem (CSS) to advancing server manageability.
    • January 28, 2025