adp162
adp162

Reputation: 71

go with networking on Android

I've been trying to get a Go application running on Android 10 and hit a snag. I'm pretty sure I now know why I'm hitting this snag, but not sure how to fix it. First, simple things work with cross compilation. For example,

package main
import "fmt"
func main() {
    fmt.Println("Hello, Android!")
}

will run just fine compiled with GOARM=arm64 GOOS=linux go build

But now if I add some networking code:

package main

import (
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {
    fmt.Println("Hello, Android!")
    client := &http.Client{}
    resp, err := client.Get("https://www.google.com")
    if err != nil {
        fmt.Println(err)
        return
    }
    bodyBytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("%s", bodyBytes)
}

And compile the same way it will run, but I'll get an error like:

Get "https://www.google.com": dial tcp: lookup www.google.com on [::1]:53: read udp [::1]:47618->[::1]:53: read: connection refused

Initially I thought this was some networking config on my Android device, but after lots of messing around I'm pretty sure it has to do with the fact that Android networking is "different". Maybe this is a bionic vs libc thing (?) but at least one other person seems to have hit the same snag and solved by building using the toolchain that comes with the NDK.

Does anyone know how to actually get this to work though?

Initially I was hoping it was as easy as GOOS=android GOARCH=arm64 go build but that fails with:

# command-line-arguments
/usr/lib/golang/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/ld: /tmp/go-link-384615804/go.o: Relocations in generic ELF (EM: 183)
<bunch of these>
/usr/bin/ld: /tmp/go-link-384615804/go.o: error adding symbols: file in wrong format
collect2: error: ld returned 1 exit status

Some Googling turned up https://github.com/golang/go/issues/30216 - which says you need to explicitly enable CGO. But GOARCH=arm64 GOOS=android CGO_ENABLED=1 go build still fails, this time with:

# runtime/cgo
gcc_android.c:6:10: fatal error: android/log.h: No such file or directory
    6 | #include <android/log.h>
      |          ^~~~~~~~~~~~~~~
compilation terminated.

I messed around with a lot of other combinations and the closest I got was pointing Go to compilers in my NDK installation. Basically something like GOARCH=arm64 GOOS=android CC=$NDKROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc CGO_ENABLED=1 CGO_CFLAGS="--sysroot $NDKROOT/sysroot -I$NDKROOT/sysroot/usr/include/arm-linux-androideabi/" go build. But this failed with:

# runtime/cgo
In file included from _cgo_export.c:4:0:
cgo-gcc-export-header-prolog:25:14: error: size of array '_check_for_64_bit_pointer_matching_GoInt' is negative

There's something broken in the way I'm pointing to the compiler - for example I won't find any header files (stdlib.h) without specifying sysroot, and even then it won't find the architecture specific includes (asm/types.h) without specifying the additional include path. So the error is probably a mismatch between architecture and compiler I'm just not sure where or how to fix it.

If you've read all this, thanks! Some additional info:

I've also looked at things like gomobile but that seems more geared towards building Android applications (like apks) although maybe I'm missing something here.

Upvotes: 3

Views: 1533

Answers (1)

adp162
adp162

Reputation: 71

Figured it out! Well, got my specific example working but I'm not really sure why this works. The answer came basically from https://github.com/golang/go/issues/20755 and you have to:

  • Build a standalone version of the NDK (the comment in the Python script that does this says it packages it in a more convenient way for other tools to use)
  • Rebuild Go's standard library using the cross compiler in your NDK
  • Build your application using the NDK cross compiler.

So for my example:

$ $(NDKROOT)/build/tools/make_standalone_toolchain.py --arch arm64 --api 24 --install-dir $(HOME)/ndk_standalone
$ CC=$(HOME)/ndk_standalone/bin/clang CXX=$(HOME)/ndk_standalone/bin/clang GOOS=android GOARCH=arm64 go install std
$ CC=$(HOME)/ndk_standalone/bin/clang CXX=$(HOME)/ndk_standalone/bin/clang GOOS=android GOARCH=arm64 CGO_ENABLED=1 go build

And networking works!

I'm not really sure why (or if) clang makes a difference as opposed to invoking the compiler directly. Maybe I'll play around with this more. Also, I had to still enabled CGO explicitly when compiling my application (but not for the go std library) otherwise linking failed.

Upvotes: 3

Related Questions