Cross-Compilation With Buildroot
Cross-compiling for embedded Linux? Use the easy button. Designing software that will run in QEMU doesn't have to be a challenge if you're using buildroot.
Join the DZone community and get the full member experience.
Join For FreeIn one of my last articles, I showed you how to build an ARM image that you can then run in QEMU. We configured SSH so that you can connect to the image, and transfer files to it, too.
But how do you create software that runs on the new image?
Okay, so let's look at a simple example. This example uses libcurl to post to a URL. We'll build this so it runs on x86, then we'll migrate it to ARM using the buildroot toolchain.
First, the program itself (requestor.c):
#include <stdio.h>
#include <curl/curl.h>
#define OK 0
#define INIT_ERR 1
#define REQ_ERR 2
#define URL "http://192.168.120.152:8000"
int main(void) {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
return REQ_ERR;
}
curl_easy_cleanup(curl);
} else {
return INIT_ERR;
}
return OK;
}
This is a simple program that will submit an HTTP GET request to a specified IP address on port 8000. I run this with pPython's SimpleHTTPServer to test connectivity. The IP address is the address of my QEMU host, and can be reached either from a program on the QEMU host or from the QEMU guest. Python SimpleHTTPServer (run with $ python -m SimpleHTTPServer) runs by default on port 8000.
The makefile for the x86 host is pretty straightforward:
CC=gcc
CCFLAGS=
INCLUDES=
LFLAGS=-L/usr/lib/x86_64-linux-gnu
LIBS=-lcurl -lpthread
SRC=requestor.c
OBJ=$(SRC:.c=.o)
MAIN=test
RM=rm -rf
.c.o:
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(MAIN): $(OBJ)
$(CC) $(CCFLAGS) $(INCLUDES) -o $(MAIN) $(OBJ) $(LFLAGS) $(LIBS)
all: $(MAIN)
clean:
$(RM) $(MAIN) *.o *~
Replace the IP address in reqeustor.c with your host address. Then, you can run make with this makefile, and you'll build the test program. In another window, run SimpleHTTPServer, and then run the new test program. When you do, you'll see a GET request recorded in the SimpleHTTPServer window, and the test program will return a directory listing of the directory you ran SimpleHTTPServer in.
I usually use this kind of workflow because testing and debugging on a full workstation host is easier than doing it on an embedded Linux device. Using your workstation allows for faster build/test/refactor loops, allowing you to be more efficient. You still need to test on the embedded device, but you don't need to develop on it.
Now, one of the really cool things about Buildroot is that it'll download and make available the correct toolchains you need to build software for any image you use it to create. You could install the cross-compilation toolchains by hand, but it is ever-so-much easier to just let Buildroot do it.
Check out the ARM makefile:
BUILDROOT_HOME=/home/cclamb/buildroot-2016.11.1
CC=$(BUILDROOT_HOME)/output/host/usr/bin/arm-linux-gcc
CFLAGS=--sysroot=$(BUILDROOT_HOME)/output/staging
INCLUDES=
LFLAGS=
LIBS=-lcurl -lc
SRC=requestor.c
OBJ=$(SRC:.c=.o)
MAIN=test
RM=rm -rf
.c.o:
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(MAIN): $(OBJ)
$(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJ) $(LFLAGS) $(LIBS)
all: $(MAIN)
clean:
$(RM) $(MAIN) *.o *~
Pretty similar to the x86 buildfile, but it has some small, key differences. First, look at the CC variable — we're pointing to a cross-compiler in the buildroot file hierarchy, downloaded when you built your QEMU image. CFLAGS has some important differences too — take a look at the --sysroot flag. This lets the compiler know where to look for things like libraries when building. Finally, look at the libraries we're linking to - we're now explicitly linking to libc.so.
Run make and build — this'll create a new test program. We'll move it up to the QEMU guest and test next time.
Opinions expressed by DZone contributors are their own.
Comments