Skip to content

cross compile

Frank Bauernöppel edited this page Apr 9, 2017 · 6 revisions

cross-compilation == software is compiled on machine (architecture) A for running on machine architecture B.

  • A is called the build host which is typically a Linux x86 or amd64 machine, and
  • B the target which is here the RasPi, having an armv7-a architecture.

Cross-compilation is by no means restricted to Yocto Project, but we focus here on it.

Prerequisites

Yocto Project installed and already successfully bitbaked something, e.g. an image. A 64-bit Linux build host is assumed, otherwise the paths are slightly different.

cross-compilation tools

The ${BUILDDIR}/tmp/sysroots/x86_64-linux folder contains tools for cross-compilation like the compiler ${BUILDDIR}/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc which you may start with the -v option to see its configuration:

frank@FrankBuntuSSD:~/rpi/build$ ${BUILDDIR}/tmp/sysroots/x86_64-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc -v

In the (lengthy) output look for strings like Target: arm-poky-linux-gnueabi and --build=x86_64-linux --host=x86_64-linux --target=arm-poky-linux-gnueabi.

Although the compiler could be used directly, we prefer using it form a devshell. This is the typical Yocto Project workflow which not only provides the compiler but a cross-compilation environment.

Note for advanced users: It is possible to extract a complete cross-compilation environment (called SDK) for use on a different build host. Those SDKs can be integrated into an Eclipse or Qt Creator IDE, and, if properly installed, support automatic deployment to the target, cross-debugging, and so on.

preparing a helloworld example

There is a hello-single example in ${BUILDDIR}/../recipes/poky/documentation/ref-manual/examples/.

Create a new folder recipes-examples in the ${BUILDDIR}/../recipes/meta-raspberrypi/ folder.

From a bitbake shell copy the whole hello-single folder structure to the meta-raspberrypi layer like this:

frank@FrankBuntuSSD:~/rpi/build$ cp -r ${BUILDDIR}/../recipes/poky/documentation/ref-manual/examples/hello-single ${BUILDDIR}/../recipes/meta-raspberrypi/recipes-examples

frank@FrankBuntuSSD:~/rpi/build$ tree ${BUILDDIR}/../recipes/meta-raspberrypi/recipes-examples
/home/frank/rpi/build/../recipes/meta-raspberrypi/recipes-examples
└── hello-single
    ├── files
    │   └── helloworld.c
    └── hello.bb

Note: In general, it is not recommended to copy files into someone else's layer. Before doing so, you should fork that layer, create a working branch, or create your own layer. We do it here for simplicity.

The recipe needs a little fix: the compile command should be:

do_compile() {
	${CC} helloworld.c -o helloworld ${LDFLAGS}
}

Do not bitbake hello, because this would create to much files for starting. If you really want to test the recipe, make a dry-run: bitbake -n hello.

open a devshell

On the build host open a bitbake shell and, from there, a devshell for hello:

frank@FrankBuntuSSD:~/rpi/build$ bitbake -c devshell hello

A new window opens, execute the tree command in it to look around:

root@FrankBuntuSSD:~/rpi/build/tmp/work/cortexa7hf-neon-vfpv4-poky-linux-gnueabi/hello/1.0-r0# tree
.
├── helloworld.c
├── patches
├── pseudo
│   ├── files.db
│   ├── logs.db
│   ├── pseudo.lock
│   ├── pseudo.log
│   ├── pseudo.pid
│   └── pseudo.socket
└── temp
    ├── log.do_devshell -> log.do_devshell.30728
    ├── log.do_devshell.30728
    ├── log.do_fetch -> log.do_fetch.30721
    ├── log.do_fetch.30721
    ├── log.do_patch -> log.do_patch.30726
    ├── log.do_patch.30726
    ├── log.do_unpack -> log.do_unpack.30722
    ├── log.do_unpack.30722
    ├── log.task_order
    ├── run.base_do_fetch.30721
    ├── run.base_do_unpack.30722
    ├── run.do_devshell -> run.do_devshell.30728
    ├── run.do_devshell.30728
    ├── run.do_fetch -> run.do_fetch.30721
    ├── run.do_fetch.30721
    ├── run.do_patch -> run.do_patch.30726
    ├── run.do_patch.30726
    ├── run.do_qa_unpack.30722
    ├── run.do_terminal.30728
    ├── run.do_unpack -> run.do_unpack.30722
    ├── run.do_unpack.30722
    └── run.patch_do_patch.30726

You see the source code and some auxiliary files, mostly scripts and logfiles for the bitbake phases executed to open that devshell. The cross compiler can be used to build the result. The file command is used to check that we have built an ARM executable:

root@FrankBuntuSSD:~/rpi/build/tmp/work/cortexa7hf-neon-vfpv4-poky-linux-gnueabi/hello/1.0-r0# arm-poky-linux-gnueabi-gcc  -march=armv7ve -marm -mfpu=neon-vfpv4  -mfloat-abi=hard -mcpu=cortex-a7 -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed --sysroot=/home/frank/rpi/build/tmp/sysroots/raspberrypi3 helloworld.c -o helloworld

root@FrankBuntuSSD:~/rpi/build/tmp/work/cortexa7hf-neon-vfpv4-poky-linux-gnueabi/hello/1.0-r0# file helloworld
helloworld: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=c5fd3620b555c21de821324d19bfdd9b8b079649, not stripped

Note that quite a few options are necessary to make the cross compiler happy. We could have achieved the same result much simpler from a bitbake shell by executing:

frank@FrankBuntuSSD:~/rpi/build$ bitbake -c compile hello

Note: compile is not the last phase of bitbake. A complete bitbake hello will also build a package and put it in the deployment folder for the package server.

Note: hello would not be included in any image unless specifically included in the image recipe or required by some other recipe in the image.

Makefiles and more advanced make mechanisms

Makefiles are popular to build larger software projects by a simple make, but a Makefile contains architecture specific flags and commands. There are several preprocessing steps which are better suited for cross compilation.

cmake

https://cmake.org/

autotools

Further reading