1.5 Compiling a Hello World example

These examples show how to use the ARM® Compiler toolchain to build and inspect an executable image from C/C++ source files.

The source code

The source code that is used in the examples is a single C source file, hello.c, to display a greeting message:

#include <stdio.h>

int main() {
	printf("Hello World\n");
	return 0;

Compiling in a single step

When compiling code, you must first decide which target the executable is to run on. An ARMv8-A target can run in different states:

  • AArch64 state targets execute A64 instructions using 64-bit and 32-bit general-purpose registers.
  • AArch32 state targets execute A32 or T32 instructions using 32-bit general-purpose registers.

The --target option determines which target state to compile for. This option is a mandatory option.

Compiling for an AArch64 target

To create an executable for an AArch64 target in a single step:

armclang --target=aarch64-arm-none-eabi hello.c

This command creates an executable, a.out.

This example compiles for an AArch64 state target. Because only --target is specified, the compiler defaults to generating code that runs on any ARMv8-A target. You can also use -mcpu to target a specific processor.

Compiling for an AArch32 target

To create an executable for an AArch32 target in a single step:

armclang --target=arm-arm-none-eabi -mcpu=cortex-a53 hello.c

There is no default target for AArch32 state. You must specify either -march to target an architecture or -mcpu to target a processor. This example uses -mcpu to target the Cortex®-A53 processor. The compiler generates code that is optimized specifically for the Cortex-A53, but might not run on other processors.

Use -mcpu=list or -march=list to see all available processor or architecture options.

Beyond the defaults

Compiler options let you specify precisely how the compiler behaves when generating code.

The armclang Reference Guide describes all the supported options, but here are some of the most common:

  • Including debug information. The -g option tells the compiler to produce DWARF debug information. You can then use a compatible debugger, such as ARM DS-5 Debugger, to load, run, and debug images.

  • Optimization. The -Olevel option specifies the level of optimization to use when compiling source files. The default is -O0, with no optimization. Different optimization levels let you control what type of optimization the compiler performs. For example, -Os aims to reduce code size by balancing code size against code speed, whereas -Omax uses aggressive optimizations to target performance optimization.

  • Instruction set. AArch32 targets support two instruction sets that you specify with the -m option. The -marm option specifies A32, that is 32-bit instructions, to emphasize performance. The -mthumb option specifies T32, that is mixed 32-bit and 16-bit instructions, to emphasize code density.

Examining the executable

The fromelf tool lets you examine a compiled binary, extract information about it, or convert it.

For example, you can:

  • Disassemble the code that is contained in the executable:

    fromelf --text -c a.out
    	0x000081a0:    e92d4800    .H-.    PUSH     {r11,lr}
    	0x000081a4:    e1a0b00d    ....    MOV      r11,sp
    	0x000081a8:    e24dd010    ..M.    SUB      sp,sp,#0x10
    	0x000081ac:    e3a00000    ....    MOV      r0,#0
    	0x000081b0:    e50b0004    ....    STR      r0,[r11,#-4]
    	0x000081b4:    e30a19cc    ....    MOV      r1,#0xa9cc
  • Examine the size of code and data in the executable:

    fromelf --text -z a.out
    	Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name
    	10436        492        596         16        348       3468   a.out
    	10436        492        596         16          0          0   ROM Totals for a.out
  • Convert the ELF executable image to another format, for example a plain binary file:

    fromelf --bin --output=outfile.bin a.out

See fromelf Command-line Options for the options from the fromelf tool.

Compiling and linking as separate steps

For simple projects with small numbers of source files, compiling and linking in a single step might be the simplest option:

armclang --target=aarch64-arm-none-eabi file1.c file2.c -o image.axf

This example compiles the two source files file1.c and file2.c for an AArch64 state target. The -o option specifies that the filename of the generated executable is image.axf.

More complex projects might have many more source files. It is not efficient to compile every source file at every compilation, because most source files are unchanged. To avoid compiling unchanged source files, you can compile and link as separate steps. In this way, you can then use a build system (such as make) to compile only those source files that have changed, then link the object code together. The armclang -c option tells the compiler to compile to object code and stop before calling the linker:

armclang -c --target=aarch64-arm-none-eabi file1.c 
armclang -c --target=aarch64-arm-none-eabi file2.c
armlink file1.o file2.o -o image.axf

These commands do the following:

  • Compile file1.c to object code, and save using the default name file1.o.
  • Compile file2.c to object code, and save using the default name file2.o.
  • Link the object files file1.o and file2.o to produce an executable that is called image.axf.

In the future, if you modify file2.c, you can rebuild the executable by recompiling only file2.c then linking the new file2.o with the existing file1.o to produce a new executable:

armclang -c --target=aarch64-arm-none-eabi file2.c
armlink file1.o file2.o -o image.axf
Non-ConfidentialPDF file icon PDF versionARM 100748_0607_00_en
Copyright © 2016, 2017 ARM Limited or its affiliates. All rights reserved.