Overview

The Building "hello world" using Arm Compiler 6 tutorial shows you how to build a simple C program with the Arm Compiler 6 toolchain.

This tutorial explores some of the more advanced features of the Arm Compiler 6 toolchain.

Before you begin

Install and license Arm DS Development Studio. For more information on installation and licensing, see Getting started with Arm Development Studio.

Note: Arm Compiler 6 adopts the LLVM integrated assembler as default because it aligns more closely with GNU assembler syntax, improving portability between GNU and Arm Compiler toolchains. The LLVM integrated assembler is called by default by armclang. A side effect is that Arm Compiler does not compile C/C++ source files which contain legacy armcc inline or embedded assembler. 


Compiling mixed C and assembly source files

The Arm assembler armclang is an integrated assembler that is based on LLVM using GNU syntax and reads assembly language source code and outputs object code. The Arm compiler armclang compiles C and C++ source code to object code. 

The Arm linker armlink combines the contents of one or more object files with any required libraries to produce an executable program.

Arm compiler, assembler, and linker are all part of the Arm Compiler 6 toolchain which is inbuilt with Arm DS.  

The following example shows how to use armclang integrated assembler, armclang and armlink from Arm DS to build a project containing both C and assembly source files. 

  1. Create a new C project and add a new source file my_strcopy.s containing the following assembly code:

    #include "my_strcpy.h"
        .section   StringCopy, "ax"
        .balign    8
        .global    mystrcopy
        .type      mystrcopy, "function"
        mystrcopy:
        ldrb       r2, [r1], #1                 ; Load byte and update address
        strb       r2, [r0], #1                 ; Store byte and update address
        cmp        r2, #0                       ; Check for null terminator
        bne        mystrcopy                    ; Keep going if not
        bx         lr                           ; Return
        .end

    The function my_strcopy() is exported so that it is available to be used from C, see the following code screenshot:

  2. Add a new source file to the project with the name test.c containing the following C code:

    #include <stdio.h>
    #include <stdlib.h>
    
    /* Declare the assembly function */
    extern void mystrcopy(char *d, const char *s);
    
    int main()
    {
      const char *srcstr = "First string - source ";
      char *dststr = "Second string - dest ";
      puts("Before copying:\n");
      printf("  %s\n  %s\n",srcstr,dststr);
    
      mystrcopy(dststr,srcstr);
      puts("\nAfter copying:\n");
      printf("  %s\n  %s\n",srcstr,dststr);
      return (0);
    }
    
  3. Build the project.
    The Arm Compiler toolchain does the following:
    • Assembles my_strcopy.s with armclang my_strcopy.o.
    • Compiles test.c with armclang to produce the object file test.o.
    • Links the object files with armlink to produce an executable image.
    • When you run the executable image, it produces the following output:

      Before copying:
      First string - source
      Second string - dest
      After copying:
      First string - source
      First string - source


Sharing header files between C and assembly code

The usual way to define constants in C code is to use #define, or in assembly code to use CMP directives. If your project contains a mixture of C and assembly code, there might be some constant definitions that are common to both. If so, to avoid maintaining two separate lists, you can create one list of common definitions and include them in both your C and assembly code.

To make common definitions, you can use C-style #include and #define directives directly in your assembly source code. You can pass this source code through the armclang C preprocessor. It outputs a preprocessed version of your assembly code which armclang can then assemble.

The following example shows how to do this.

  1. Add a header file called my_strcopy.h to the project, containing the following line:
    #define ONE_CONSTANT 1
  2. Add this line to the top of my_strcopy.s, created in the previous example:
    #include "my_strcopy.h"
  3. In my_strcopy.s, replace the occurrences of #1 with #ONE_CONSTANT, for example:
    LDRB R2, [R1], #ONE_CONSTANT

  4. Pass my_strcopy.s through the C preprocessor. If you tried to build the project without first doing this, Arm assembler 6 would report a syntax error for the #include statement you added to my_strcopy.s.

  5. Open the Project Settings dialog. Then, under C/C++ build, select Settings.
    In the Tool Settings tab, under Arm Assembler 6, select Preprocessor, then tick the box marked Preprocess input before assembling (- x assembler-with-cpp), as shown in the following image:
  6. The -x assembler-with-cpp option tells armclang that the assembly source file requires preprocessing. Now if you try to build the project, it will be able to successfully build it.
  7. If you need to pass other simple command-line options to the C preprocessor, for example –D,-U or –E, specify them in the field shown in Preprocessor window. Deatils of these options can be found in the Arm compiler 6 documentation.