# Bare metal environment support

A bare metal environment is a software execution environment where
the software image is executed directly on the hardware with no
operating system support. All the required software mechanisms for
performing operation system functionality such as system calls, image
loading, and relocation are handled by code that is part of the
software image rather than through an operation system.

Bare metal environments are typically used for boot images as well as
firmware that must execute as soon as the system boots up, well
before a full-fledged HLOS operating system (such as Linux, Android,
or Windows) is brought up. Hence, bare metal images must include all
the functionality such as startup code and memory management API as
part of the image. In addition, the image layout is typically
finalized at link time because runtime relocation is not common in
bare metal environments.

This Snapdragon LLVM Arm Toolchain contains full support for the
following popular Arm processors:

- Cortex-M0 processor (Armv6-M architecture version)
- Cortex-M3 processor (Armv7-M architecture version)
- Cortex-A family of processors (Armv7 architecture version)
- 64-bit cores (AARCH64 architecture version)

For each target group, the Snapdragon LLVM toolchain includes a set
of runtime C and C++ libraries specifically built for the target. In
addition, there are several features (as explained in customization
hooks, [Customization hooks for bare metal images](https://docs.qualcomm.com/doc/80-VB419-99/topic/bare_metal_environment_support.html#sec-customization-hooks-for-bare-metal-images))
available in the toolchain to facilitate bare metal image programmers
to enable end-to-end development.

## Compiler options specific to bare metal

The following options are introduced to support automatically adding
the correct bare metal includes and libraries. You are not required
to explicitly specify paths to bare metal includes or libraries if
you use these options.

**-target {&lt;arch&gt;}-none-{&lt;ABI&gt;}**

> 
> 
> Set the target architecture and ABI for code generation. We recommend
> the following values for the bare metal environment:
> 
> - *&lt;arch&gt;* can be armv5, armv6m, armv7m, armv7, or aarch64.
> - For 32-bit targets, *&lt;ABI&gt;* can be eabi if libc is not needed, or
> musleabi if SDLLVM libc is needed.
> - For 64-bit targets, *&lt;ABI&gt;* can be elf if libc is not needed, or
> musleabi if SDLLVM libc is needed.

**-fuse-baremetal-inc**

> 
> 
> Adds the correct bare metal includes for the target during
> compilation.
> 
> 
> Note
> 
> 
> This option is OFF by default. It will not be enabled if
> `-nostdinc` or `-nobuiltininc` are specified on the command line.
> 
> 
> Use the option as follows:
> 
> 
> clang -target armv7m-none-eabi -fuse-baremetal-inc hello.c -c
>     Copy to clipboard
> 
> 
> This option adds the following on the compilation line:
> 
> 
> -isystem <llvm_install_dir>/armv7m-none-eabi/libc/include
>     Copy to clipboard

**-fuse-baremetal-libs**

> 
> 
> Adds the correct bare metal libraries for the target during compilation.
> 
> 
> Note
> 
> 
> This option is OFF by default. It will not be enabled if `-nostdlib` or
> `-nodefaultlibs` are specified on the command line.
> 
> 
> Use the option as follows:
> 
> 
> clang -target armv7m-none-eabi -fuse-baremetal-libs hello.c
>     Copy to clipboard
> 
> 
> This option adds the following libraries:
> 
> - -L&lt;llvm\_inEopstall\_dir&gt;/armv7m-none-eabi/libc/lib
> - -L&lt;llvm\_install\_dir&gt;/armv7m-none-eabi/lib
> - -lunwind
> 
> 
> 
> If the code being linked is C++, the clang++ driver is invoked. Thus,
> the following libraries will be added to the previous list of
> libraries:
> 
> - -lc++
> - -lc++abi

**-fuse-baremetal-rtlib**

> 
> 
> Add the correct built-ins (compiler-rt) library for the target during linking.
> 
> 
> Note
> 
> 
> This option is OFF by default. It will not be enabled if
> `-nostdlib` or `-nodefaultlibs` are specified on the command line.
> 
> 
> Use the option as follows:
> 
> 
> clang -target armv7m-none-eabi -fuse-baremetal-rtlib hello.c
>     Copy to clipboard
> 
> 
> The following libraries are added:
> 
> 
> -L<llvm_install_dir>/lib/clang/<version>/lib/baremetal
>     --start-group -lc -lclang_rt.builtins-armv7m --end-group
>     -mfloat-abi=soft and -mfpu=none
>     Copy to clipboard
> 
> 
> Both `-mfloat-abi=soft` and `-mfpu=none` turn off floating point
> instruction generation. As a result, and only for the Armv7-M
> architecture, if either of these flags are present on the command
> line, link with the nofp (no floating point) version of the
> compiler-rt library.
> 
> 
> Use the options as follows:
> 
> 
> clang -target armv7m-none-eabi -fuse-baremetal-rtlib -mfloat-abi=soft hello.c
>     clang -target armv7m-none-eabi -fuse-baremetal-rtlib -mfpu=none hello.c
>     -L<llvm_install_dir>/lib/clang/<version>/lib/baremetal --start-group -lc -lclang_rt.builtins-armv7m-nofp --end-group
>     Copy to clipboard

**-fuse-baremetal-crt**

> 
> 
> Add the correct entry point and initialization routines library
> (crt1.o) for the target during linking.
> 
> 
> Note
> 
> 
> This option is OFF by default. It is not enabled if
> `-nostdlib` or `-nodefaultlibs` is specified on the command line.
> 
> 
> Use the option as follows:
> 
> 
> clang -target armv7m-none-eabi -fuse-baremetal-crt hello.c
>     Copy to clipboard
> 
> 
> This adds the following object file on the command line:
> 
> - &lt;llvm\_install\_dir&gt;\*/armv7m-none-eabi/libc/lib/crt1.o

**-fuse-baremetal-sysroot**

> 
> 
> Instead of specifying the -fuse-baremetal-inc, -fuse-baremetal-libs,
> -fuse-baremetal-rtlib, or -fuse-baremetal-crt options separately, you
> can specify the -fuse-baremetal-sysroot option to get the correct
> includes and libraries for the target.
> 
> 
> Note
> 
> 
> This option is OFF by default. It is not enabled if
> `-nostdlib` or `-nodefaultlibs` are specified on the command line.
> 
> 
> Use the option as follows:
> 
> 
> clang++ -target armv7m-none-eabi -fuse-baremetal-sysroot hello.cpp
>     Copy to clipboard
> 
> 
> This option adds the following includes and libraries:
> 
> 
> -isystem <llvm_install_dir>/armv7m-none-eabi/libc/include
>     -L<llvm_install_dir>/armv7m-none-eabi/libc/lib
>     -L<llvm_install_dir>/armv7m-none-eabi/lib
>     -lunwind
>     -lc++
>     -lc++abi
>     -L<llvm_install_dir>/lib/clang/<version>/lib/baremetal
>     --start-group -lc -lclang_rt.builtins-armv7m --end-group
>     armv7m-none-eabi/libc/lib/crt1.o
>     Copy to clipboard

**-baremetal-lib-variant=&lt;variant&gt;**

> 
> 
> Specify the variant of libraries to be used.
> 
> 
> For example, for the Armv6-M architecture, -baremetal-lib-variant=
> nofltprint\_nopthread\_zeroinbss allows the use of a special variant of
> libc that has no thread support and no floating number formatting,
> and zero-initialized data is placed in BSS sections.
> 
> 
> Note
> 
> 
> This option is OFF by default. It is only effective if
> `-fuse-baremetal-libs` or `fuse-baremetal-sysroot` is used, and the
> corresponding architecture has the specified variant available.

### Notes

- Each option listed in [Compiler options specific to bare metal](https://docs.qualcomm.com/doc/80-VB419-99/topic/bare_metal_environment_support.html#sec-compiler-options-specific-to-bare-metal) can be explicitly
turned OFF using the `-fno-baremetal-<flagname>` option. For example:

clang++ -target armv7m-none-eabi -fuse-baremetal-sysroot -fno-use-baremetal-inc
        Copy to clipboard

    This example adds all the required bare metal libraries except the
bare metal includes.

clang++ -target armv7m-none-eabi -fuse-baremetal-sysroot -fno-use-baremetal-libs
        Copy to clipboard

    This example adds all the required bare metal includes, rtlib, and
crt. It will not include any of the bare metal C or C++ libraries.

clang++ -target armv7m-none-eabi -fuse-baremetal-sysroot -fno-use-baremetal-rtlib
        Copy to clipboard

    This example adds all the required bare metal includes and libraries
except the bare metal built-ins (compiler-rt) library.
- If multiple conflicting versions of an option are specified, the
rightmost option prevails. For example:

clang++ -target armv7m-none-eabi -fuse-baremetal-sysroot -fno-use-baremetal-sysroot
        Copy to clipboard

    In this example, no bare metal include or library is added because
the right-most option (-fno-baremetal-sysroot) prevails.

### Bare metal linker option

**-fuse-ld=qcld**

> 
> 
> The linker used for bare metal is ld.qcld. Specify this -fuse-ld=qcld
> option to invoke this linker.
> 
> 
> Use the option as follows:
> 
> 
> clang -target armv7m-none-eabi -fuse-ld=qcld -fuse-baremetal-sysroot hello.c
>     Copy to clipboard
> 
> 
> This option invokes &lt;llvm\_install\_dir&gt;/bin/ld.qcld for linking.

### Commonly used options

We recommend the following options for reducing the code size of a
bare metal application:

- `-fomit-frame-pointers`
- `-fno-exceptions` **(for c++)**
- `-fshort-enums`
- `-mno-interrupt-stack-align`

    Disables the stack alignment for interrupts (IRQ, FIQ, and SWI) if
the function does not have any other user-defined attribute. By
default, stack alignment is enabled for all functions.

#### Other options

**-m{no-}unaligned-access**

> 
> 
> Enables/disables accessing 16-bit or larger data from a memory
> address that might be unaligned. By default, unaligned access is
> enabled except for all pre-Armv6 and Armv6-M architectures.

**-fno-zero-initialized-in-bss**

> 
> 
> Normally, zero-initialized values are placed in BSS (ZI) sections.
> This option disables this behavior and that data will be placed in
> regular data sections. This is useful when the BSS sections have not
> been zero-initialized when the data is used.

**-mgeneral-regs-only**

> 
> 
> Prevents the compiler from using floating-point and advanced SIMD
> registers for AArch64.

## Toolchain components specific to bare metal

The toolchain comes with the following libraries built for the Armv5,
Armv6-M, Armv7-M, Armv7, and AArch64 architectures:

- libc - An implementation of the ISO and POSIX C standard library
based on the MUSL C library.
- libc++ - An implementation of the C++ standard library that targets
C++11. libc++ is part of the LLVM family of projects.
- libc++abi - An implementation of low-level support for a standard C++
library. This project is part of the LLVM family of projects.
- libunwind - An implementation of stack unwinder used for C++
exception handling. This project is part of the LLVM family of
projects.
- libclang - An implementation runtime library calls generated by the
LLVM compiler. This project (compiler-rt) is part of the LLVM family
of projects.

### Location of bare metal libraries

#### Naming conventions:

- The {arch} part in the locations should be one of the following
options: armv5, armv6m, armv7m, armv7, aarch64.
- The {abi} part in the locations should be eabi for 32-bit and elf for
64-bit (AArch64).

#### Locations:

- libc

    - Location is *&lt;llvm\_install\_dir&gt;*/{*arch*}-none-{*abi*}/lib/libc
    - Header files are in the include subdirectory
    - Libraries are in the lib subdirectory:

        - libc.a for the C library
        - crt1.o for the C runtime library
- libc++, libc++abi, libunwind

    - Location is *&lt;llvm\_install\_dir&gt;*/{*arch*}-none-{*abi*}
    - Header files are in the include subdirectory
    - Libraries are in the lib subdirectory:

        - libc++.a for libc++
        - libc++abi.a for libc++abi
        - libunwind.a for libunwind
- libclang\_rt.builtins

    - Location is *&lt;llvm\_install\_dir&gt;*/lib/clang/&lt;*version*&gt;/lib/baremetal

        - For example, lib/clang/*&lt;version&gt;*/lib/baremetal
        - libclang\_rt.builtins-{*arch*}.a
- libclang\_rt.builtins-armv7-nofp.a

    - For Armv7 targets without hardware floating-point
- libclang\_rt.builtins.Ospace-armv7m.a

    - For Armv7-M targets that prioritize for code size

### C library for bare metal

#### Implemented functions and headers

The C library for bare metal implements functions specified by
ISO/IEC 9899. Each library function is declared with a prototype in a
header. The header declares a set of related functions, necessary
types, and macros. Following are the headers provided by the C
library:

<assert.h>
    <complex.h>
    <ctype.h>
    <errorno.h>
    <fenv.h>
    <float.h>
    <inttypes.h>
    <iso646.h>
    <limits.h>
    <locale.h>
    <math.h>
    <signal.h>
    <stdarg.h>
    <stdbool.h>
    <stddef.h>
    <stdint.h>
    <stdio.h>
    <setjmp.h>
    <stdlib.h>
    <string.h>
    <tgmath.h>
    <time.h>
    <wchar.h>
    <wctype.h>
    Copy to clipboard

Some functions specified by the ISO/IEC 9899 standard require
syscalls. These functions are not supported unless they are
explicitly documented as supported (such as semihosting functions).
In addition, some non-standard functions and headers are implemented
for convenience.

##### Non-standard functions

`strlcpy(char *dst, const char *src, size_t size)`

> 
> 
> Size-bounded string copying.

`strlcat(char *dst, const char *src, size_t size)`

> 
> 
> Size-bounded string concatenating.

`strnlen(const char \*str, size_t size)`

> 
> 
> Size-bounded string length calculation.

`strtok_r (char *str, const char *delim, char **saveptr)`

> 
> 
> Parses a string into a sequence of tokens.

`memalign(size_t boundary, size_t size)`

> 
> 
> Allocates a block of size bytes whose address is a multiple of boundary.

##### Non-standard headers

- *elf.h*
- *pthread.h*

#### Bare metal-specific features

The C library is optimized for code size by default. More
specifically, the default C library is built as follows:

- Use smallest data types for enum types (-fshort-enums)
- Hidden symbol visibility by default (-fvisibility=hidden)
- Limited formatted printing (printf, vfprint) support:
- No wide-character formatting; that is, %S and %C modifiers are not
supported
- Non-standard modifiers like %m are not supported
- Some library variants have no floating-point number formatting;
that is, %f, %F,

    %e, %E, %g, %G, %a, %A modifiers are not supported

## Compiler built-ins for bare metal

Built-ins (also called *intrinsics*) are functions built into the
compiler. They are syntactically similar to regular functions. The
compiler lowers them into the appropriate instructions.

Syntax:

void builtin_arm_breakpoint (innnnt n)
    Copy to clipboard

Example:

void foo() {
       builtin_arm_breakpoint(0x2A);
    }
    Copy to clipboard

This call to builtin\_arm\_breakpoint generates the BKPT instruction
with immediate operand 0x2A.

Following are the built-ins supported by LLVM for bare metal. The
built-ins are shown with their signature (return types and parameter
types).

**Table 6-1 Built-ins supported by LLVM for bare metal**

| **Built-in** | **Description** |
| --- | --- |
| void<br>builtin\_arm\_breakpoint(unsigned<br>int) | Generates a BKPT instruction. |
| unsigned int<br>builtin\_arm\_clz(unsigned int) | (Count Leading Zeros) Returns the<br>number of leading zero bits in a<br>value. |
| unsigned int<br>builtin\_arm\_current\_pc() | Returns the current value of the<br>program counter. |
| unsigned int<br>builtin\_arm\_current\_sp() | Returns the current value of the<br>stack pointer. |
| void builtin\_arm\_dbg(unsigned<br>int) | Generates a DBG instruction. |
| void builtin\_arm\_dmb(unsigned<br>int) | Generates a DMB (data memory<br>barrier) instruction. |
| void builtin\_arm\_dsb(unsigned<br>int) | Generates a DSB (data<br>synchronization barrier)<br>instruction. |
| void builtin\_arm\_isb(unsigned<br>int) | Generates an ISB (instruction<br>synchronization barrier)<br>instruction. |
| void builtin\_arm\_nop() | Generates a NOP instruction. |
| void builtin\_arm\_prefetch(const<br>int \*, unsigned int, unsigned<br>int) | Generates data prefetch<br>instructions to minimize<br><br><br>cache-miss latency by moving data<br>into the cache before it is<br>accessed. |
| int builtin\_arm\_rsr(const char<br>\*) | Reads a 32-bit system register<br>(special register). |
| unsigned long long int<br><br><br>builtin\_arm\_rsr64(const char \*) | Reads a 64-bit system register<br>(special register). |
| void \* builtin\_arm\_rsrp(const<br>char \*) | Reads a system register<br>containing an address. |
| void builtin\_arm\_wfe() | Generates a WFE (wait for event)<br>instruction. |
| void builtin\_arm\_wfi() | Generates a WFI (wait for<br>interrupt) instruction. |
| void builtin\_arm\_wsr(const char<br>\*, unsigned int) | Writes a 32-bit system register<br>(special register). |
| void builtin\_arm\_wsr64(const char<br>\*, unsigned long long int) | Writes a 64-bit system register<br>(special register). |
| void builtin\_arm\_wsrp(const char<br>\*, void \*) | Writes a system register<br>containing an address. |
| void \*<br>builtin\_return\_address(unsigned<br>int) | Returns the return address of a<br>function on the call stack.<br><br><br>The input parameter is the call<br>depth; 0 means the current<br>function. |
| void disable\_irq() | Disables IRQ interrupts. |
| void enable\_irq() | Enables IRQ interrupts and clears<br>the I-bit in the current program<br>status register (CPSR).<br><br><br>For Cortex-M profile processors,<br>clears the exception mask<br>register (PRIMASK). |
| unsigned int ror(unsigned int,<br>unsigned int) | Generates an ROR (rotate operand<br>right) instruction.<br><br><br>It right rotates a value by the<br>specified number of places. |

## Customization hooks for bare metal images

### attribute ((at(address, [prefix])))

This attribute specifies the absolute address of the variable, where
address is the expected address, and the optional prefix can be used
to customize the section name.

The compiler places the variable in its own section named
[prefix._AT_@address](mailto:prefix&#46;_AT_&#37;&#52;&#48;address). The linker automatically places the section in
the specified address.

For example:

#define BASE 0x4000 #define OFFSET 0x100
    #define ADDR (BASE + OFFSET)
    int v attribute ((at(ADDR))) = 4;
    Copy to clipboard

The compiler will place variable v in section .\_AT\_@0x4100, and the
linker will then place the section at address 0x4100.

For an uninitialized variable, using the at attribute will make it a
regular data typed section rather than a BSS typed section. To
generate a BSS section, the .bss prefix is required. For example:

int v0 attribute ((at(0xc0ffee00, “.bss”))) = 0;

The compiler will place variable v0 in section .bss.\_AT\_@0xC0FFEE00.

### Entry point and initialization

The entry point and initialization routines are defined in crt1.o of
the C library. Passing

-fuse-baremetal-crt to Clang will link the object into image; see
[-fuse-baremetal-crt](https://docs.qualcomm.com/doc/80-VB419-99/topic/bare_metal_environment_support.html#fuse-baremetal-crt).

#### Entry point

The entry point is main(). It first performs initialization and
eventually calls user-defined main().

#### Initializate heap and stack

If dynamic memory allocation routines such as malloc()/calloc() are
used, the initial heap starting and ending address must be set up.
The C library uses two variables,

heap\_base and heap\_limit, for heap base and heap end, respectively.
To set the initial SP to some specific value, either assign the
initial stack address to initial\_sp or change the SP register
directly.

Heap space can also be defined in the linker script by directly
assigning addresses to

libc\_heap\_start and libc\_heap\_end. The values are used only if both

heap\_base and heap\_limit are not defined.

There are two ways to set the variables:

- Assign the address to the variables directly before calling main. For
example:

extern uintptr_t __heap_base, __heap_limit, __initial_sp;
        void boot() {
           __heap_base = 0x1000;
           __heap_limit = __heap_base + heap_size;
           __initial_sp = 0x8000;
           __main();
        }
        Copy to clipboard

    The default user\_setup\_stackheap() defined in crt1.o will initialize
the SP register to the value defined by initial\_sp.
- Re-implement user\_setup\_stackheap and set the variable there. For
example:

> 
> 
> .global __user_setup_stackheap
>     .type __user_setup_stackheap, %function
>     __user_setup_stackheap:
>     ldr r0, =__heap_base
>     ldr r1, #0x1000
>     str r1, [r0]
>     ldr r0, =__heap_limit
>     ldr r1, #0x2000
>     str r1, [r0]
>     mov sp, #4000
>     bx lr
>     Copy to clipboard

### Override library functions

It is possible to redefine functions of the C library. For example,
you can provide your own memory allocation routines to replace the
standard malloc(). However, other functions of the C library such as
snprintf() might call malloc() as well. To re-route all calls to a
user- defined version, two methods are available:

- Implement the functionality with the same function name. For example:

mymalloc.c:
        void * malloc() { /* user-defined implementation */ }
        Copy to clipboard

Note

During linking, the object file that defines the function
(such as mymalloc.o) must appear after all other user object files
that use the function, but before -lc.
- Implement the functionality with the wrap\_ prefix. For example:

void * wrap_malloc() { /* user-defined implementation* / }
        Copy to clipboard

    Then during link step, pass the linker flag –wrap *&lt;function&gt;* (such
as –wrap malloc). Object files can be listed in any order on the
command line.

### I/O functions in C library

High-level APIs (such as printf, putc) will eventually call low-level
input/output functions. The default implementation is for
semi-hosting. You can re-implement those low-level functions to
re-target the input/output.

- Read from a file or STDIO:

size_t __stdio_read(FILE *f, unsigned char *buf, size_t len)
        Copy to clipboard
- Write to a file or STDOUT/STDERR:

size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
        Copy to clipboard
- File seek:

off_t __stdio_seek(FILE *f, off_t off, int whence)
        Copy to clipboard
- File open:

FILE *fopen(const char *restrict filename, const char *restrict mode)
        Copy to clipboard
- File close:

int __fclose_ca(FILE *f)
        Copy to clipboard

### Semihosting support

The C library implements the following semihosting sys calls that
enable code running on an Arm target to use the I/O facilities on a
host computer that is running a compliant debugger:

SYS_OPEN (0x01)
    SYS_CLOSE (0x02)
    SYS_WRITE (0x05)
    SYS_READ (0x06)
    SYS_SEEK (0x0A)
    SYS_FLEN (0x0C)
    Copy to clipboard

The following C functions are supported for semihosting:

fopen
    fclose
    fseek
    fread
    fwrite
    printf/fprintf
    putc/fputc
    Copy to clipboard

## Examples

This section provides several examples. The first example shows how
to set up an interrupt vector table. An interrupt vector table (also
called exception vectors) contains the addresses of all exception
handlers. The Reset handler is usually the entry point of the bare
metal image. For Cortex-M3, the first entry of the interrupt vector
table is the initial stack pointer.

The example that shows how to set up stack space has a corresponding
linker script example.

### Set up interrupt vector table

In vector\_table.c:

typedef void (*FPtr)(void) __attribute__((interrupt("IRQ")));
    #define STACK_START_ADDR 0x0E00
    #define STACK_SIZE 0x400
    static const FPtr vector_table[] __attribute__((at(0x0), used)) =
       { (FPtr)Stack_Start_Addr /* the initial SP value */,
         __main, /* handler for RESET*/
        abort_isr, HardFault_Handler,
        ...
       }
    Copy to clipboard

- at(0x0)instructs the tool chain to place vector\_table at address 0
- used attribute informs the compiler to keep the static variable even
if it is unreferenced in the source code

### Set up stack space

In main.c:

int main() {
    
    /* The loader or the image itself should zero-initialize BSS sections */
    
    extern char DRAM_BSS_START, DRAM_BSS_SIZE;
    memset((void\*)&DRAM_BSS_START, 0, (int)(&DRAM_BSS_SIZE));
    
    start_my_image();
    }
    
    int stack_base_address = STACK_START_ADDR;
    
    // Re-implement user_setup_stackheap to set the initial value of SP.
    // The "naked" attribute prevents the compiler to generate code for prologue/epilogue that could
    // clobber the value of SP.
    
    __attribute__((naked)) void user_setup_stackheap() {
       asm("ldr r0,=stack_base_address");
       asm("ldr sp, [r0]");
       asm("bx lr");
    }
    Copy to clipboard

Following is the corresponding linker script:

ENTRY(__main) /* Specify entry point as __main */
    PHDRS {
      /* Here we create two load regions / segments */
      CODE_LOAD PT_LOAD;
      DATA_LOAD PT_LOAD;
    }
    SECTIONS {
      /* Here we place all code and RO data into section "EXEC" and assign it to CODE_LOAD segment */
    EXEC 0x0 : {
      *.o (.text*)
      foo\bar\init.o (MyCode) /* Pleasese back-slash as path separator for both Windows and Linux build environment */
     *.o(.rodata*)
    } : CODE_LOAD
    /* Here we place all data into to DRAM_DATA section and assign it to
    DATA_LOAD segment */
    DRAM_DATA : {
    *(.data*)
    } : DATA_LOAD
    /* Here we place all BSS data into DRAM_BSS section and it will go to
       the same segment as previous section. Usually, BSS sections should be
       placed at end of a segment */
    DRAM_BSS : {
    DRAM_BSS_START = .;
    *.o (.bss*)
    }
    DRAM_BSS_SIZE = SIZEOF(DRAM_BSS);
    /* Here we create the space to be used for stack. The section is
    defined from a lower address */
    STACK (STACK_START_ADDR - STACK_SIZE) : { . += STACK_SIZE; }
    /DISCARD/ : { *(.ARM.exidx*) } /* Unneeded sections go here */
    Copy to clipboard

## Port from Arm Compiler toolchain

The Snapdragon LLVM toolchain provides features and capabilities
comparable to the Arm Compiler toolchain. Following are the
corresponding components of both toolchains.

**Table 6-2 Toolchain components mapping**

| **Components** | **Arm Compilertoolchain** | **SnapdragonLLVMtoolchain** | **Notes** |
| --- | --- | --- | --- |
| C and C++<br>compiler | > <br>> <br>> armcc<br>(version 4<br>and 5)<br><br><br><br>armclang<br>(version 6) | clang |  |
| Arm and Thumb<br>assembler | armasm | clang | Clang only<br>supports GNU<br>assembly<br>language |
| Arm librarian | armar | arm-ar |  |
| Arm linker | armlink | clang or<br>ld.qcld | Clang can be<br>used as driver<br>for linking |
| Arm image<br>conversion<br>utility | fromelf | llvm-elf-to-hex.py |  |
| Supporting<br>libraries | Built into<br>toolchain | libc, libc++,<br>libc++abi,<br>libunwid,<br>libcl<br>ang\_rt.builtins |  |

There are some differences in various language syntax between Arm
Compiler toolchains and Snapdragon LLVM toolchain. Following are some
of the differences.

### Assembly files

The syntax of most instructions is the same for armasm and clang, but
directives are different. Clang supports GNU assembly language syntax
and requires UAL (Unified Assembly Language) syntax as dictated by
the Arm architecture specification. The following table lists the
mapping of some of the directives.

**Table 6-3 Mapping directives**

| **armasm supported syntax** | **clang supported syntax** |
| --- | --- |
| @ (comment) | ; |
| IMPORT | .extern |
| EXPORT | .global |
| PRESERVE8 | .eabi\_attribute<br>Tag\_ABI\_align8\_preserved, 1 |
| AREA *&lt;name&gt;*, *&lt;attribute&gt;* | section *&lt;name&gt;*, *&lt;flags&gt;* |
| MACRO | .macro |
| MEND | .endm |
| xxx | .Lxxx |
| *&lt;name&gt;* FUNCTION | .type *&lt;name&gt;*, %function |
| *&lt;name&gt;*: |  |
| ENDFUNCTION | (not needed) |
| SPACE | .space |
| DCD | .word |
| *&lt;sym&gt;* EQU *&lt;val&gt;* | .set *&lt;sym&gt;*, *&lt;val&gt;* |
| CODE32 | .code 32 |
| LTORG | .ltorg |
| ALIGN | .balign |
| INCLUDE | .include |
| - OR:<br><br>    - | | |
| - AND:<br><br>    - | & |

### C/C++ source code

No change is required for C/C++ code that conforms to the C/C++
standards. Compiler- specific syntax like inline assembly,
attributes, intrinsics, and built-in functions must be ported.

**Table 6-4 Commonly used attributes, intrinsics, and built-infunctions**

| **armcc** | **clang** |
| --- | --- |
| weak | attribute ((weak)) |
| irq | attribute ((interrupt(“IRQ”))) |
| inline | inline |
| CLZ | builtin\_clz |
| dsb | builtin\_arm\_dsb |
| dmb | builtin\_arm\_dmb |
| return\_address | builtin\_return\_address(0) |

### Mapping of commonly used compiler flags

**Table 6-5 Commonly used compiler flags**

| **armcc** | **clang** |
| --- | --- |
| –protect\_stack | -fstack-protector |
| –loose\_implicit\_case | -Wno-int-conversion |
| –cpu *&lt;CPU&gt;* | -mcpu=*&lt;CPU&gt;* |
| –C99 | -std=c99 |
| –diag\_error=warning | -Werror |
| –debug | -g |
| –dwarf3 | -gdwarf-3 |

### Linker script

The syntax of the linker script (scatter loading files) is very
different. This is no simple mapping between the two. For details,
see the *Qualcomm Snapdragon LLVM ARM Linker User Guide* (80-VB419-102).

## Code coverage for bare metal environments

The source-based code coverage helps you to understand which part of
the code is used during execution. This can help to identify if a
test case effectively covers the code, discover potential dead code,
and so on

### Requirements for bare metal images

To retrieve the coverage data, semihosting is required. The resulting
raw data is stored on host via semihosting.

Extra image size and run-time memory is expected to accommodate the
instrumentation code and data.

### Code coverage workflow

1. Build the source code with instrumentation.
2. Link the objects against the run-time library.
3. Run the code for one or more times. For each run, export the coverage
data.
4. Combine the coverage data from multiple runs, and generate the
report.

### Build a program with code coverage support

#### Compile

Compile each object with the following flags:

`-fcoverage-mapping`

For bare metal images, you must explicitly dump the result data at
the intended stop point. This is because bare metal images do not
call the exit() function, so the run-time library will not capture
the exiting point.

#include "qc_coverage.h" void my_job_done() {
    qc_llvm_profile_write_file("my_code_coverage.dat");
    }
    Copy to clipboard

#### Link

If clang is invoked to drive the linking, and assuming
-fuse-baremetal-sysroot is passed and the -target flag is set
properly, add -fcoverage-mapping in the linking command. The linker
automatically searches for the required library.

If linking is done by invoking the linker directly, you must
explicitly specify the run-time library for profiling, such as
-lclang\_rt.profile-armv5.

For bare metal images, the linker script must be updated to place
some specific sections:

1. Place the following read only sections somewhere in memory:

llvm_prf_names : { KEEP(\*( llvm_prf_names)) }
        Copy to clipboard
2. Place the following data sections somewhere in memory:

llvm_prf_data : { KEEP(*( llvm_prf_data)) }
        llvm_prf_cnts : { KEEP(*( llvm_prf_cnts)) }
        Copy to clipboard
3. The following section is not required to be in memory (like debug info):

llvm_covmap : { KEEP(\*( llvm_covmap)) }
        Copy to clipboard

#### Example

PHDRS {
       CORE_REGION PT_LOAD;
    }
    
    SECTIONS { CORE_RO : {
       *\init*.o (entry)
       *  (.text*)
       *  (.rodata*)
     } : CORE_REGION
    
    __llvm_prf_names : { KEEP(*( llvm_prf_names)) }
    
    CORE_RW : ALIGN(0x100000) {
       *  (.data*)
       *  (stacks)
    }
    llvm_prf_data : { KEEP(*( llvm_prf_data)) }
    llvm_prf_cnts : { KEEP(*( llvm_prf_cnts)) }
    
    CORE_ZI: {
    *  (.bss*)
    }
    
    llvm_covmap : { KEEP(*( llvm_covmap)) }
    /DISCARD/ : {
          *(.ARM.exidx*)
       }
    }
    Copy to clipboard

### Export code coverage data

The code can be run multiple times. For bare metal images, the data
is stored on the host machine via semi-hosting, and the filename is
specified in the source code in \_\_qc\_llvm\_profile\_write\_file. The data
file might need to be renamed before the next run.

### Generate code coverage reports

You can consolidate the code coverage data from multiple runs into a
single report. The report is in HTML format and is stored in a
specified directory.

mkdir code_coverage_report
    *<llvm_root>*/bin/llvm-profdata merge data_from_run1 data_from_run2 ... -output=merged.data
    *<llvm_root>*/bin/llvm-cov show -format=html -instr-profile=merged.data
    -show-region-summary=false image.elf -o code_coverage_report
    Copy to clipboard

Using a browser, open the index.html under code\_coverage\_report.

`llvm-cov export` converts the coverage data to JSON format, which can
be used for writing tools on top of the LLVM coverage infrastructure.

### Interpret code coverage reports

The index page of the coverage report shows coverage by file.

- **Function Coverage** is the percentage of functions that were
executed during the coverage runs.
- **Line coverage** is the percentage of lines.

These percentages only count code compiled into the executable, so
they ignore code that is disabled using an ifdef. Cells that show
coverage less than 80% are highlighted in red.

If you click on the link to one of the individual files, you can see
on a line-by-line basis which lines are covered.

- Code that is not executed is highlighted in red.
- The number in blue on the left (after the line number) is the number
of times a line was executed.
- Source code lines that are not code, like global variables or code
disabled using an ifdef, do not show any execution count.

Last Published: May 10, 2024

[Previous Topic
Code optimization](https://docs.qualcomm.com/bundle/publicresource/80-VB419-99/topics/code_optimization.md) [Next Topic
Resource analyzer](https://docs.qualcomm.com/bundle/publicresource/80-VB419-99/topics/resource_analyzer.md)