User Tools

Site Tools


building

ARM GCC

Different build options

Bare metal applications do not use a C library. linux applications are built using *-linux* toolchains, while kernel and u-boot are bare-metal applications, built with *-eabi* toolchains.

Example with Sourcery G++ Lite

Sourcery G++ version toolchain gcc binutils library
2011.03-42 for ARM EABI arm-none-eabi- 4.5.2 2.20.51 newlib 1.18.0
2011.03-46 for ARM uClinux arm-uclinuxeabi- 4.5.2 2.20.51 uclibc 0.9.30
2011.03-41 for ARM GNU/Linux arm-none-linux-gnueabi- 4.5.2 2.20.51 glibc 2.13

QEMU emulation

Most of what follows has been copied from balau82.wordpress.com, a great blog, very much recommended.

Bare metal programs

The QEMU emulator is written especially to emulate Linux guest systems; for this reason its startup procedure is implemented specifically: the -kernel option loads a binary file (usually a Linux kernel) inside the system memory starting at address 0x00010000. The emulator starts the execution at address 0x00000000, where few instructions (already in place) are used to jump at the beginning of the kernel image. The interrupt table of ARM cores, usually placed at address 0x00000000, is not present, and the peripheral interrupts are disabled at startup, as needed to boot a Linux kernel.

The ARM9 architecture begins to execute code at a determined address, that could be 0 (usually allocated to RAM) or 0xFFFF0000 (usually allocated to Read Only Memory). We must put some special code at that particular address: the interrupt vector table.

  • Interrupt vector table (startup.s)
.section INTERRUPT_VECTOR, "x"
.global _Reset
_Reset:
 B Reset_Handler /* Reset */
 B . /* Undefined */
 B . /* SWI */
 B . /* Prefetch Abort */
 B . /* Data Abort */
 B . /* reserved */
 B . /* IRQ */
 B . /* FIQ */

Reset_Handler:
LDR sp, =stack_top
 BL c_entry
 B .
  • A C file containing a function c_entry (test.c)
  • A linker script (test.ld)
ENTRY(_Reset)
SECTIONS
{
 . = 0x10000;
 .startup . : { startup.o(.text) }
 .text : { *(.text) }
 .data : { *(.data) }
 .bss : { *(.bss) }
 . = . + 0x1000; /* 4kB of stack memory */
 stack_top = .;
}

Our resulting test.bin program is passed to QEMU using this command:

qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin

Using Newlib

arm-none-eabi-gcc -mcpu=cortex-a8 -I ~/embedded_linux_newlib/newlib-1.18.0/newlib/libc/include   -c -o test.o test.c
arm-none-eabi-gcc -mcpu=cortex-a8 -I ~/embedded_linux_newlib/newlib-1.18.0/newlib/libc/include   -c -o syscalls.o syscalls.c
arm-none-eabi-as -mcpu=cortex-a8  -o startup.o startup.s
arm-none-eabi-gcc -mcpu=cortex-a8 -I ~/embedded_linux_newlib/newlib-1.18.0/newlib/libc/include   -c -o syscalls.o syscalls.c
arm-none-eabi-gcc -nostdlib  -T test.ld  test.o startup.o syscalls.o ~/embedded_linux_newlib/newlib-1.18.0/arm-none-eabi/newlib/libc.a ~/CodeSourcery/Sourcery_G++_Lite/lib/gcc/arm-none-eabi/4.5.2/libgcc.a -o test
arm-none-eabi-objcopy -O binary test test.bin
qemu-system-arm -M realview-pb-a8 -serial stdio -kernel test.bin

Making use of CS3

The “arm-none-eabi” toolchain also contains a particular library that is linked by default in compiled programs, which is called CS3™: the CodeSourcery Common Startup Code Sequence. This library is associated with linker scripts for generic and specific platforms, chosen through a compiler switch:

arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -T lm3s6965.ld main.c reset.S syscalls.c -o main

CodeSourcery provides a standard linked script that needs to be modified for our specific Qemu simulated board …/Sourcery_G++_Lite/arm-none-eabi/lib/thumb2/generic-m.ld. In addition to that, we need to write the reset code and the syscalls.c function as previously done.

arm-none-eabi-objcopy -O binary main main.bin
qemu-system-arm -M lm3s6965evb --kernel main.bin --serial stdio

U-Boot with bare metal program

We can create a single binary that contains the U-Boot binary and our bare metal program. The initial address of our bare metal program must be changed with respect to the instructions present in the previous section, because at 0x10000 (our last initial address) QEMU places the beginning of the U-Boot binary. Since the U-Boot binary is about 100KB, we can place our binary at 0x100000 (that is 1MB) to be safe, by changing test.ld:

...
ENTRY(_Reset)
SECTIONS
{
 . = 0x100000;
...

Now create the U-Boot image test.uimg using the following command, where we affirm that the image is for ARM architecture, is not compressed, is meant to be loaded at address 0x100000 and the entry point is at the same address. I use “linux” as operating system and “kernel” as image type because in this way U-Boot cleans the environment before passing the control to our image: this means disabling interrupts, caches and MMU:

mkimage -A arm -C none -O linux -T kernel -d test.bin -a 0x00100000 -e 0x00100000 test.uimg

Now we can create a single binary simply with:

cat u-boot.bin test.uimg > flash.bin

Linux programs

QEMU passes the filesystem binary image to the kernel using the initrd parameter; the kernel must also know that the root filesystem will be located in RAM (because that’s where QEMU writes the initrd binary) and that the program to launch is our test executable contained within the root filesystem ramdisk:

qemu-system-arm -M versatilepb -m 128M -kernel zImage -initrd rootfs -append "root=/dev/ram rdinit=/test"

U-Boot with linux programs

On real HW, U-Boot loads a linux kernel from flash memory. QEMU has the possibility to emulate flash memory on many platforms, but not on the VersatilePB. There are patches ad procedures available that can add flash support. One feature of U-Boot is self-relocation, which means that on execution the code copies itself into another address, which by default is 0x1000000 (16MiB).

QEMU can load a Linux kernel using the -kernel and -initrd options; at a low level, these options have the effect of loading two binary files into the emulated memory: the kernel binary at address 0x10000 (64KiB) and the ramdisk binary at address 0x800000 (8MiB). Then QEMU prepares the kernel arguments and jumps at 0x10000 (64KiB) to execute Linux. The -kernel option in QEMU will be used to load the Flash binary into the emulated memory, and this means the starting address of the binary image will be 0x10000 (64KiB). See the original article.

building.txt · Last modified: by admin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki