March 25, 2013

Cross-compiling applications for ARM (part 2)

In my previous post I wrote about how to setup a cross-compiling environment. In this post I'll show you how to use that environment to cross-compile everything, from simple Hello World examples to big applications with external dependencies (libraries).

You'll be able to compile whatever you want the way you already do it, except that you should prepend the command sb2 in front of every command you run. That way you're letting ScratchBox2 know what it should run with the session you setup.

Let's start with a small Hello World.

As you can see I'm able to compile and run my code with sb2 and qemu. Notice that I'm using -L in qemu so it knows where to search for I told it to look inside my toolchain, which buildroot setup for me (see previous post if you haven't).

Compiling software that depends on external libraries isn't that different from what I just did, but there are some things that you must do. First you should know that all the libraries that your application depends on must be cross-compiled too. You can't cross-compile your application for ARM linking against a library that is compiled for a different architecture. You must compile all those libraries and install them in a custom prefix to avoid overriding your system's libraries and also to be able to move them to your device and use them.

As I worked with GStreamer I'll show how to cross-compile all GStreamer's dependencies, GStreamer itself and a simple one-liner example.

First let's see a working example of what we want to cross-compile:

Now we have a small working code that requires GStreamer. We know that it works and we know that it depends on GStreamer so we just need to cross-compile it for the target device. Anyways, we can't just prepend sb2 in front of the the gcc line because, as I said, we need every single library to be cross-compiled.

Note: I'm using pkg-config to tell gcc where are GStreamer's libs. It's important to be aware of that as I'll use sb2 and pkg-config when I'm cross-compiling.

We need to get the sources of GStreamer and all it's dependencies and compile/install them somewhere. For that we'll use PKG_CONFIG_PATH and --prefix. That way we're telling our environment where to look for our libraries and where to install them.

I'll create two folders: deps and libs then I'll download the sources of all the dependencies in the first one and I'll install them in the second one.

As I said, I'll be using PKG_CONFIG_PATH (set it just once) and --prefix.

We're getting closer! Just note where I'm setting PKG_CONFIG_PATH's path and where I'm setting --prefix's path.

Note: I'm redirecting to /dev/null because I know that it will compile fine (as I run each command on a second shell just to be sure I'm not making a mistake) and because there is no need to show here the output of a typical compile process.

After compiling and installing all other dependencies, that's how everything looks like:

Now the environment is ready to cross-compile the code I wrote earlier. We just need to tell gcc and pkg-config where they should look at (with sb2's help).

Please note a couple of things!

  • I'm using sb2 pkg-config to tell gcc where to look for the libraries that I cross-compiled
  • I manually appended libffi path to LDLIBS (can't say if that's a bug or I screwed something, but I'm more prone to think that's a bug in libffi as the other libs work just fine and all them are detected properly by pkg-config)

As you can see we successfully cross-compiled the code. Now we can copy the whole libs folder to the target device, set PATH to something like

set PATH=/path/to/copied/libs:$PATH

and we're ready to run the binary we cross-compiled.

PS: I'm not sure how can I run the ARM binary in my machine without screwing my PATH, but if somebody knows please share your knowledge in the comments and I'll update the post.