lmoe

Last Man On Earth : Github Thoughts

View the Project on GitHub heltonmarx/lmoe

Cross compile Go using Buildroot

This a simple tutorial to describe step-by-step how to create an environment to cross compile Go applications to run on a BeagleBone Black using Buildroot toolchain.

Requirements

Environment

Install essential things first

apt-get update
apt-get install -y build-essential libncurses5-dev \
        bzr cvs git mercurial rsync subversion \
        libc6 libstdc++6 libncurses5 zlib1g lzop u-boot-tools \
        tftpd-hpa nfs-kernel-server sqlite3 picocom

Buildroot

Download and extract the Buildroot release (e.g: 2015.02) on /opt/toolchain/ directory:

mkdir -p /opt/toolchain
cd /opt/toolchain
wget http://buildroot.uclibc.org/downloads/buildroot-2015.02.tar.gz
tar -xzvf buildroot-2015.02.tar.gz
cd buildroot-2015.02

It's already have a previous configuration to BeagleBone Black describe in configs/beaglebone_defconfig, so, it's a nice way to follow.

Invoke those commands bellow to generate a Makefile and configure the system:

make beaglebone_defconfig
make menuconfig

Here are some settings you should change on buildroot config (yeah!...I'll use sqlite...)

Target Options --->
    ARM instruction set (Thumb2)
Toolchain --->
    Toolchain type (Buildroot toolchain) --->
    Kernel Headers (Linux 3.12x kernel headers) --->
    C library (eglibc) --->
    [*] Enable C++ support
    [*] Build cross gdb for the host
    [*] Purge unwanted locales
    (C en_US de fr pt_br) Locales to keep
    (en_US) Generate locale data
    [*] Enable MMU support
    [*] Register toolchain within Eclipse Buildroot plug-in
System configuration --->
    [*] Install timezone info
    (default) timezone list
    (America/Sao_Paulo) default local time
Kernel --->
    [] Linux Kernel
Target packages --->
    Libraries --->
        Database --->
            [*] sqlite
                [*] Command-line editing
                [*] Additional query optmizations (stat3)
                [*] Enable version 3 of the full-text search engine
    Network applications --->
        [*] dhcpd
        [*] iptables
        [*] iputils
        [*] openssh
Filesystem images --->
    [*]tar the root filesystem
        Compression method (no compression)

Save, exit and initiate a full system build (This will take a while...):

make all

OK, get the u-boot files and move them to a more visible directory (e.g.:/opt/toolchain/output/):

mkdir -p /opt/toolchain/output
cp -a /opt/toolchain/buildroot-2015.02/output/images/MLO /opt/toolchain/output/
cp -a /opt/toolchain/buildroot-2015.02/output/images/u-boot.img /opt/toolchain/output/
cp -a /opt/toolchain/buildroot-2015.02/output/images/Env.txt /opt/toolchain/output/

Now, run to the hills...

Linux kernel 3.12

Download the Linux kernel 3.12 directly from BeagleBone Github repository:

mkdir -p /opt/toolchain/linux
git clone -b 3.12 git://github.com/beagleboard/kernel.git /opt/toolchain/linux
cd /opt/toolchain/linux
bash ./patch.sh
cp configs/beaglebone kernel/arch/arm/configs/beaglebone_defconfig
wget http://arago-project.org/git/projects/?p=am33x-cm3.git\;a=blob_plain\;f=bin/am335x-pm-firmware.bin\;hb=HEAD -O kernel/firmware/am335x-pm-firmware.bin

Setting the arm-linux-gcc to your $PATH:

export PATH=$PATH:/opt/toolchain/buildroot-2015.02/output/host/usr/bin/

Compile and generate an uImage file with a DTB blob, and kernel modules (This will take a while,again...)

cd /opt/toolchain/linux/kernel
make ARCH=arm CROSS_COMPILE=arm-linux- beaglebone_defconfig -j2
make ARCH=arm CROSS_COMPILE=arm-linux- uImage dtbs LOADADDR=0x80008000 -j2
make ARCH=arm CROSS_COMPILE=arm-linux- modules -j2

Copy uImage and DTB file to /opt/toolchain/output directory:

cp -a /opt/toolchain/linux/kernel/arch/arm/boot/uImage /opt/toolchain/output
cp -a /opt/toolchain/linux/kernel/arch/arm/boot/dts/am335x-boneblack.dtb /opt/toolchain/output/

Rootfs

Untar rootfs generated by buildroot to install kernel modules:

mkdir -p /opt/toolchain/rootfs
tar -xvf /opt/toolchain/buildroot-2015.02/output/images/rootfs.tar -C /opt/toolchain/rootfs
make ARCH=arm CROSS_COMPILE=arm-linux- INSTALL_MOD_PATH=/opt/toolchain/rootfs modules_install
make ARCH=arm CROSS_COMPILE=arm-linux- INSTALL_MOD_PATH=/opt/toolchain/rootfs firmware_install

NFS

The great advantage of using NFS is that you compile binaries on your development environment, install binaries and export them, have instant response on your target system.

Edit /etc/exports file, including the following line:

/opt/toolchain/rootfs   *(rw,sync,fsid=0,no_root_squash,crossmnt,no_subtree_check,no_acl)

So, restart the NFS service: /etc/init.d/nfs-kernel-server restart.

Copy uboot files and kernel uImage to tftpboot directory:

mkdir -p /var/lib/tftpboot/boot/
cp -a /opt/toolchain/output/uImage /var/lib/tftpboot/boot/
cp -a /opt/toolchain/output/MLO /var/lib/tftpboot/boot/
cp -a /opt/toolchain/output/u-boot.img /var/lib/tftpboot/boot/
cp -a /opt/toolchain/output/am335x-boneblack.dtb /var/lib/tftpboot/boot/

Connect the BeagleBone to your serial port via uart1 (Debug Serial Header), and set commands in u-boot, to load your linux kernel from tftp and rootfs over NFS:

set ipaddr 192.168.0.15
set serverip 192.168.0.50
set gateway_ip 192.168.0.1
set rootpath '/opt/toolchain/rootfs/production'

set loadtftp 'tftpboot 0x80200000 /boot/uImage; tftpboot 0x815f0000 /boot/am335x-boneblack.dtb'
set netargs 'setenv bootargs console=${console} ${optargs} root=/dev/nfs nfsroot=${serverip}:${rootpath},${nfsopts} rw ip=dhcp'
set uenvcmd 'setenv autoload no;run loadtftp; run netargs; bootm 0x80200000 - 0x815f0000'

run uenvcmd

Go

1.4.2 it's a good start, I'll check go 1.5 later.

Install go

wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz
tar -xzvf go1.4.2.linux-amd64.tar.gz -C /opt/
cd /opt/go/src
GOOS=linux GOARCH=arm ./make.bash --no-clean
rm go1.4.2.linux-amd64.tar.gz

Feel more confortable using a local GOPATH like:

cd $HOME
mkdir -p go

Set my $USER profile (.bashrc):

export GOROOT="/opt/go"
export GOPATH="$HOME/go"
export PATH=$PATH:/opt/go/bin

Cross compilation

Create a simple go app:

package main.go

import "fmt"

func main() {
    fmt.Printf("Hello BeagleBone Black\n")
}

Using Makefile to the dirty job:

TARGET := hello

all:
    go build -o $(TARGET) $^

beagle:
    CC=arm-linux-gcc CGO_ENABLED="1" GOARM=7 GOARCH=arm GOOS=linux go build -o $(TARGET) $^

clean:
    go clean

install:
    cp -a $(TARGET) /opt/toolchain/rootfs/root/

Compile and install (copy TARGET to rootfs directory):

make beagle ; make install

VoilĂ , we have a cross-compiled go binary ready to running on a BeagleBone Black.