]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go, cmd/ld, runtime, os/user: TLS emulation for android
authorDavid Crawshaw <david.crawshaw@zentus.com>
Thu, 3 Jul 2014 20:14:34 +0000 (16:14 -0400)
committerDavid Crawshaw <david.crawshaw@zentus.com>
Thu, 3 Jul 2014 20:14:34 +0000 (16:14 -0400)
Based on cl/69170045 by Elias Naur.

There are currently several schemes for acquiring a TLS
slot to save the g register. None of them appear to work
for android. The closest are linux and darwin.

Linux uses a linker TLS relocation. This is not supported
by the android linker.

Darwin uses a fixed offset, and calls pthread_key_create
until it gets the slot it wants. As the runtime loads
late in the android process lifecycle, after an
arbitrary number of other libraries, we cannot rely on
any particular slot being available.

So we call pthread_key_create, take the first slot we are
given, and put it in runtime.tlsg, which we turn into a
regular variable in cmd/ld.

Makes android/arm cgo binaries work.

LGTM=minux
R=elias.naur, minux, dave, josharian
CC=golang-codereviews
https://golang.org/cl/106380043

13 files changed:
src/cmd/go/build.go
src/cmd/ld/data.c
src/cmd/ld/elf.c
src/cmd/ld/lib.c
src/cmd/ld/pobj.c
src/cmd/ld/symtab.c
src/pkg/os/user/lookup_stubs.go
src/pkg/os/user/lookup_unix.go
src/pkg/runtime/asm_arm.s
src/pkg/runtime/cgo/cgo.go
src/pkg/runtime/cgo/gcc_android_arm.c [new file with mode: 0644]
src/pkg/runtime/cgo/gcc_linux_arm.c
src/pkg/runtime/tls_arm.s [new file with mode: 0644]

index 1dc13cf068dc0c04104fe966ac09977d04de9838..152806f876b217ff8a768a78156a4dff63bf26a0 100644 (file)
@@ -2262,13 +2262,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
 
        linkobj = append(linkobj, p.SysoFiles...)
        dynobj := obj + "_cgo_.o"
-       if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym
+       pie := goarch == "arm" && (goos == "linux" || goos == "android")
+       if pie { // we need to use -pie for Linux/ARM to get accurate imported sym
                cgoLDFLAGS = append(cgoLDFLAGS, "-pie")
        }
        if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil {
                return nil, nil, err
        }
-       if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs
+       if pie { // but we don't need -pie for normal cgo programs
                cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1]
        }
 
index 03b93c77ce91bc1a7bf523cfb1df1b57f40ea602..b2075f2d66db9f8545159e70e4f8181f73f947e0 100644 (file)
@@ -160,6 +160,10 @@ relocsym(LSym *s)
                if(r->sym != S && r->sym->type != STLSBSS && !r->sym->reachable)
                        diag("unreachable sym in relocation: %s %s", s->name, r->sym->name);
 
+               // Android emulates runtime.tlsg as a regular variable.
+               if (r->type == R_TLS && strcmp(goos, "android") == 0)
+                       r->type = R_ADDR;
+
                switch(r->type) {
                default:
                        o = 0;
index 0555cf46aa2525d16804a65aa9171d39a362d8be..3196961f35c2124ecb6fe162bdbc092632082e99 100644 (file)
@@ -776,7 +776,8 @@ elfshbits(Section *sect)
        if(sect->rwx & 2)
                sh->flags |= SHF_WRITE;
        if(strcmp(sect->name, ".tbss") == 0) {
-               sh->flags |= SHF_TLS;
+               if(strcmp(goos, "android") != 0)
+                       sh->flags |= SHF_TLS; // no TLS on android
                sh->type = SHT_NOBITS;
        }
        if(linkmode != LinkExternal)
index ef638a66a6b21e8d49d0cc16a33b90bfef7cdab0..0a5d8d99f2da4685d3e09d4015b91473f7143a2d 100644 (file)
@@ -226,6 +226,10 @@ loadlib(void)
                        linkmode = LinkExternal;
                else
                        linkmode = LinkInternal;
+
+               // Force external linking for android.
+               if(strcmp(goos, "android") == 0)
+                       linkmode = LinkExternal;
        }
 
        if(linkmode == LinkInternal) {
index 819c37954a6b6a77ec91c00898dee3365004568b..d3a15a77057e79d30432613408910611e48f40bc 100644 (file)
@@ -139,7 +139,7 @@ main(int argc, char *argv[])
        if(HEADTYPE == -1)
                HEADTYPE = headtype(goos);
        ctxt->headtype = HEADTYPE;
-       if (headstring == nil)
+       if(headstring == nil)
                headstring = headstr(HEADTYPE);
 
        archinit();
index 1bc384e805bf01459a9924c5dcb5ff38413986d8..1805f97d3a31872c42892ecd9bd9f56c9774c835 100644 (file)
@@ -204,7 +204,12 @@ asmelfsym(void)
                        diag("missing section for %s", s->name);
                        errorexit();
                }
-               putelfsyment(putelfstr(s->name), 0, s->size, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
+               if (strcmp(goos, "android") == 0) {
+                       // Android emulates runtime.tlsg as a regular variable.
+                       putelfsyment(putelfstr(s->name), 0, s->size, (STB_LOCAL<<4)|STT_OBJECT, s->sect->elfsect->shnum, 0);
+               } else {
+                       putelfsyment(putelfstr(s->name), 0, s->size, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
+               }
                s->elfsym = numelfsym++;
        }
 
index 86f0e6e645ae64ee61cf6065fac5263a76798f9d..4fb0e3c6edd865f90dc81d141480c960a2e7cfbe 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !cgo,!windows,!plan9
+// +build !cgo,!windows,!plan9 android
 
 package user
 
index f2baf05bbff1cacdf58b00eafe551b986e2e0614..0871473df1d4e7402218e12ebd0ccbd61f6507e6 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
 // +build cgo
 
 package user
index 4f029c850afcb7677383bb8c510ee3810733ed9f..6711d5105be3813818985b262be3e31b0b5917d7 100644 (file)
@@ -40,10 +40,12 @@ TEXT _rt0_go(SB),NOSPLIT,$-4
        MOVW    _cgo_init(SB), R4
        CMP     $0, R4
        B.EQ    nocgo
-       BL              runtime·save_g(SB);
-       MOVW    g, R0 // first argument of _cgo_init is g
-       MOVW    $setg_gcc<>(SB), R1 // second argument is address of save_g
-       BL              (R4) // will clobber R0-R3
+       MRC     15, 0, R0, C13, C0, 3   // load TLS base pointer
+       MOVW    R0, R3                  // arg 3: TLS base pointer
+       MOVW    $runtime·tlsg(SB), R2  // arg 2: tlsg
+       MOVW    $setg_gcc<>(SB), R1     // arg 1: setg
+       MOVW    g, R0                   // arg 0: G
+       BL      (R4) // will clobber R0-R3
 
 nocgo:
        // update stackguard after _cgo_init
@@ -688,42 +690,6 @@ _eqnext:
        MOVB    R7, v+16(FP)
        RET
 
-// We have to resort to TLS variable to save g(R10).
-// One reason is that external code might trigger
-// SIGSEGV, and our runtime.sigtramp don't even know we
-// are in external code, and will continue to use R10,
-// this might as well result in another SIGSEGV.
-// Note: all three functions will clobber R0, and the last
-// two can be called from 5c ABI code.
-
-// save_g saves the g register into pthread-provided
-// thread-local memory, so that we can call externally compiled
-// ARM code that will overwrite those registers.
-// NOTE: runtime.gogo assumes that R1 is preserved by this function.
-//       runtime.mcall assumes this function only clobbers R0 and R11.
-TEXT runtime·save_g(SB),NOSPLIT,$0
-       MRC             15, 0, R0, C13, C0, 3 // fetch TLS base pointer
-       // $runtime.tlsg(SB) is a special linker symbol.
-       // It is the offset from the TLS base pointer to our
-       // thread-local storage for g.
-       MOVW    $runtime·tlsg(SB), R11
-       ADD     R11, R0
-       MOVW    g, 0(R0)
-       RET
-
-// load_g loads the g register from pthread-provided
-// thread-local memory, for use after calling externally compiled
-// ARM code that overwrote those registers.
-TEXT runtime·load_g(SB),NOSPLIT,$0
-       MRC             15, 0, R0, C13, C0, 3 // fetch TLS base pointer
-       // $runtime.tlsg(SB) is a special linker symbol.
-       // It is the offset from the TLS base pointer to our
-       // thread-local storage for g.
-       MOVW    $runtime·tlsg(SB), R11
-       ADD     R11, R0
-       MOVW    0(R0), g
-       RET
-
 // void setg_gcc(G*); set g called from gcc.
 TEXT setg_gcc<>(SB),NOSPLIT,$0
        MOVW    R0, g
index 258b6fba103b0713758661e4899e69a96c859419..786ae515c85d36a498929fbbbdf0f744344904df 100644 (file)
@@ -14,7 +14,8 @@ package cgo
 #cgo darwin LDFLAGS: -lpthread
 #cgo dragonfly LDFLAGS: -lpthread
 #cgo freebsd LDFLAGS: -lpthread
-#cgo linux LDFLAGS: -lpthread
+#cgo android LDFLAGS: -llog
+#cgo !android,linux LDFLAGS: -lpthread
 #cgo netbsd LDFLAGS: -lpthread
 #cgo openbsd LDFLAGS: -lpthread
 #cgo windows LDFLAGS: -lm -mthreads
diff --git a/src/pkg/runtime/cgo/gcc_android_arm.c b/src/pkg/runtime/cgo/gcc_android_arm.c
new file mode 100644 (file)
index 0000000..58b5fc4
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <android/log.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/limits.h>
+#include "libcgo.h"
+
+#define magic1 (0x23581321U)
+
+// PTHREAD_KEYS_MAX has been added to sys/limits.h at head in bionic:
+// https://android.googlesource.com/platform/bionic/+/master/libc/include/sys/limits.h
+// TODO(crawshaw): remove this definition when a new NDK is released.
+#define PTHREAD_KEYS_MAX 128
+
+// inittls allocates a thread-local storage slot for g.
+//
+// It finds the first available slot using pthread_key_create and uses
+// it as the offset value for runtime.tlsg.
+static void
+inittls(void **tlsg, void **tlsbase)
+{
+       pthread_key_t k;
+       int i, err;
+
+       err = pthread_key_create(&k, nil);
+       if(err != 0) {
+               fprintf(stderr, "runtime/cgo: pthread_key_create failed: %d\n", err);
+               __android_log_print(ANDROID_LOG_FATAL, "runtime/cgo", "pthread_key_create failed: %d", err);
+               abort();
+       }
+       pthread_setspecific(k, (void*)magic1);
+       for (i=0; i<PTHREAD_KEYS_MAX; i++) {
+               if (*(tlsbase+i) == (void*)magic1) {
+                       *tlsg = (void*)(i*sizeof(void *));
+                       pthread_setspecific(k, 0);
+                       return;
+               }
+       }
+       fprintf(stderr, "runtime/cgo: could not find pthread key\n");
+       __android_log_print(ANDROID_LOG_FATAL, "runtime/cgo", "could not find pthread key");
+       abort();
+}
+
+void (*x_cgo_inittls)(void **tlsg, void **tlsbase) = inittls;
index 3b108fec2247b536bfc2131ea910d480a68a19d3..a746ca5f5aec8c87f05c0938484c10a5d78a8dcb 100644 (file)
@@ -9,21 +9,7 @@
 
 static void *threadentry(void*);
 
-static void (*setg_gcc)(void*);
-
-void
-x_cgo_init(G *g, void (*setg)(void*))
-{
-       pthread_attr_t attr;
-       size_t size;
-
-       setg_gcc = setg;
-       pthread_attr_init(&attr);
-       pthread_attr_getstacksize(&attr, &size);
-       g->stackguard = (uintptr)&attr - size + 4096;
-       pthread_attr_destroy(&attr);
-}
-
+void (*setg_gcc)(void*);
 
 void
 _cgo_sys_thread_start(ThreadStart *ts)
@@ -50,8 +36,7 @@ _cgo_sys_thread_start(ThreadStart *ts)
        pthread_sigmask(SIG_SETMASK, &oset, nil);
 
        if (err != 0) {
-               fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
-               abort();
+               fatalf("pthread_create failed: %s", strerror(err));
        }
 }
 
@@ -75,3 +60,22 @@ threadentry(void *v)
        crosscall_arm1(ts.fn, setg_gcc, (void*)ts.g);
        return nil;
 }
+
+void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
+
+void
+x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
+{
+       pthread_attr_t attr;
+       size_t size;
+
+       setg_gcc = setg;
+       pthread_attr_init(&attr);
+       pthread_attr_getstacksize(&attr, &size);
+       g->stackguard = (uintptr)&attr - size + 4096;
+       pthread_attr_destroy(&attr);
+
+       if (x_cgo_inittls) {
+               x_cgo_inittls(tlsg, tlsbase);
+       }
+}
diff --git a/src/pkg/runtime/tls_arm.s b/src/pkg/runtime/tls_arm.s
new file mode 100644 (file)
index 0000000..040ce7d
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "zasm_GOOS_GOARCH.h"
+#include "funcdata.h"
+#include "../../cmd/ld/textflag.h"
+
+// We have to resort to TLS variable to save g(R10).
+// One reason is that external code might trigger
+// SIGSEGV, and our runtime.sigtramp don't even know we
+// are in external code, and will continue to use R10,
+// this might as well result in another SIGSEGV.
+// Note: both functions will clobber R0 and R11 and
+// can be called from 5c ABI code.
+
+// On android, runtime.tlsg is a normal variable.
+// TLS offset is computed in x_cgo_inittls.
+
+// save_g saves the g register into pthread-provided
+// thread-local memory, so that we can call externally compiled
+// ARM code that will overwrite those registers.
+// NOTE: runtime.gogo assumes that R1 is preserved by this function.
+//       runtime.mcall assumes this function only clobbers R0 and R11.
+TEXT runtime·save_g(SB),NOSPLIT,$0
+       MRC             15, 0, R0, C13, C0, 3 // fetch TLS base pointer
+       // $runtime.tlsg(SB) is a special linker symbol.
+       // It is the offset from the TLS base pointer to our
+       // thread-local storage for g.
+#ifdef GOOS_android
+       MOVW    runtime·tlsg(SB), R11
+#else
+       MOVW    $runtime·tlsg(SB), R11
+#endif
+       ADD     R11, R0
+       MOVW    g, 0(R0)
+       RET
+
+// load_g loads the g register from pthread-provided
+// thread-local memory, for use after calling externally compiled
+// ARM code that overwrote those registers.
+TEXT runtime·load_g(SB),NOSPLIT,$0
+       MRC             15, 0, R0, C13, C0, 3 // fetch TLS base pointer
+       // $runtime.tlsg(SB) is a special linker symbol.
+       // It is the offset from the TLS base pointer to our
+       // thread-local storage for g.
+#ifdef GOOS_android
+       MOVW    runtime·tlsg(SB), R11
+#else
+       MOVW    $runtime·tlsg(SB), R11
+#endif
+       ADD     R11, R0
+       MOVW    0(R0), g
+       RET