Static Console Binaries für Android mit GO

Diesmal mache ich es richtig!

Wie man an den letzten Beiträgen wohl mitbekommen hat, beschäftige ich mich immer mal wieder damit, mit Go Dinge™ für Android zu kompilieren.
Dabei zielen die von Google und der Community bereitgestellten Tools hauptsächlich darauf ab, Apps zu schreiben.

Mein Interesse ist es dabei eher, das darunter liegende Linux für mich nutzbar zu machen. Detailierter habe ich mich dazu schon im letzten Beitrag ausgelassen, weshalb ich das hier jetzt auch nicht unnötig in die Länge ziehen will.

War es mir bis jetzt nicht möglich, wirklich statische Binaries zu bauen, so lag die Lösung des Problems mit dem Position Independend Code (siehe auch dazu den letzten Beitrag) näher, als ich dachte.

Entgegen dem nativen Fall scheint der Go Compiler beim erstellen der Binaries für Android immer den externen Linker auf zu rufen.
Diesen Sachverhalt kann man sich zu Nutze machen, wenn man es schafft, die nötigen Flags für den Linker durch zu reichen.

Ich hebe das deshalb so hervor, weil es echt ein Krampf ist, die passenden Flags zu finden und dann auch noch so zu escapen, dass die eigentlichen Linker Flags dort auch ankommen.

Im Netz findet man relativ wenig dazu und die Hilfe der Go Tools – sagen wir mal … sie könnte besser sein.

Hier gehts los:

Für Ungeduldige, die vielleicht gerade auf der 2 Seite der Suche angekommen sind und schon etwas genervt sind, versuche ich es so kurz wie möglich zu halten.

Benötigt wird für das folgende Skript nur ein lokaler gcc und wget.
Der gcc wird benötigt, um die Go Tools um einige Tools für Android zu erweitern, wget nutze ich (Überraschung) um Sachen umkompliziert aus dem Netz zu ziehen.
Diesmal zerhacke ich die Befehle absichtlich nicht in lauter kleine Brocken, die keiner mehr copypasten will. Dafür gibts aber auch keine weiteren Erklärungen. Alles nötige sollte in den Kommentaren stehen.
Der komplette Blob kann so wie er ist in die Shell geworfen werden, alle weiter nötigen Tools werden direkt aus dem Netz bezogen.
Kein sudo erforderlich!

# we need Go1.4 to bootstrap GO1.5 for Android
wget https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz
wget https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz

# unpack
tar xvzf go1.4.3.linux-amd64.tar.gz
# bootstrap process will search for $HOME/go1.4
mv go go1.4
tar xvzf go1.5.1.linux-amd64.tar.gz

# get & unpack native development kit for Android
wget http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin
chmod 744 android-ndk-r10e-linux-x86_64.bin 
./android-ndk-r10e-linux-x86_64.bin 

#build toolchain
android-ndk-r10e/build/tools/make-standalone-toolchain.sh \
--arch=arm --platform=android-21 --install-dir=toolchain
#export Android gcc
export CC_FOR_TARGET=$HOME/toolchain/arm-linux-androideabi/bin/gcc

#prepare Go1.5 to build Android compatible binaries
cd go/src/
GOOS=android GOARCH=arm GOARM=7 ./make.bash

# burp a hello world to disk
cd
cat > hello.go << EOF
package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}
EOF

#include go1.5 bin path to searchpath
export PATH=$PATH:$HOME/go/bin
#magic
GOOS=android GOARCH=arm GOARM=7 go build \
-ldflags '-extldflags "-Wl,-pie"' hello.go

Das Skript konnte ich ohne Modifikation in eine
docker run -it ubuntu bash -l
Shell werfen (nachdem ich dort wget und gcc installiert hatte).
Es sollte also keine Probleme geben.

Achso: Der Schnipsel ist offensichtlich (wie immer) für irgendein Linux/x64.
Für MAC oder auch Linux/i386 muss offensichtlich ein bisschen was angepasst werden.