Commit 1d10b175 authored by Elias Naur's avatar Elias Naur

cmd/link/ld,cmd/internal/obj,runtime: make the Android TLS offset dynamic

We're going to need a different TLS offset for Android Q, so the static
offsets used for 386 and amd64 are no longer viable on Android.

Introduce runtime·tls_g and use that for indexing into TLS storage. As
an added benefit, we can then merge the TLS setup code for all android
GOARCHs.

While we're at it, remove a bunch of android special cases no longer
needed.

Updates #29674
Updates #29249 (perhaps fixes it)

Change-Id: I77c7385aec7de8f1f6a4da7c9c79999157e39572
Reviewed-on: https://go-review.googlesource.com/c/go/+/169817
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
parent 2cc34738
...@@ -41,13 +41,8 @@ import ( ...@@ -41,13 +41,8 @@ import (
func CanUse1InsnTLS(ctxt *obj.Link) bool { func CanUse1InsnTLS(ctxt *obj.Link) bool {
if isAndroid { if isAndroid {
// For android, we use a disgusting hack that assumes // Android uses a global variable for the tls offset.
// the thread-local storage slot for g is allocated return false
// using pthread_key_create with a fixed offset
// (see src/runtime/cgo/gcc_android_amd64.c).
// This makes access to the TLS storage (for g) doable
// with 1 instruction.
return true
} }
if ctxt.Arch.Family == sys.I386 { if ctxt.Arch.Family == sys.I386 {
...@@ -162,6 +157,18 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { ...@@ -162,6 +157,18 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
} }
} }
// Android uses a tls offset determined at runtime. Rewrite
// MOVQ TLS, BX
// to
// MOVQ runtime.tls_g(SB), BX
if isAndroid && (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_EXTERN
p.From.Reg = REG_NONE
p.From.Sym = ctxt.Lookup("runtime.tls_g")
p.From.Index = REG_NONE
}
// TODO: Remove. // TODO: Remove.
if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 || ctxt.Headtype == objabi.Hplan9 { if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 || ctxt.Headtype == objabi.Hplan9 {
if p.From.Scale == 1 && p.From.Index == REG_TLS { if p.From.Scale == 1 && p.From.Index == REG_TLS {
...@@ -1007,6 +1014,7 @@ func load_g_cx(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) *obj.Prog { ...@@ -1007,6 +1014,7 @@ func load_g_cx(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) *obj.Prog {
progedit(ctxt, p, newprog) progedit(ctxt, p, newprog)
for p.Link != next { for p.Link != next {
p = p.Link p = p.Link
progedit(ctxt, p, newprog)
} }
if p.From.Index == REG_TLS { if p.From.Index == REG_TLS {
......
...@@ -218,9 +218,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -218,9 +218,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type)) Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type))
} }
case objabi.R_TLS_LE: case objabi.R_TLS_LE:
isAndroidX86 := objabi.GOOS == "android" && (ctxt.Arch.InFamily(sys.AMD64, sys.I386)) if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
if ctxt.LinkMode == LinkExternal && ctxt.IsELF && !isAndroidX86 {
r.Done = false r.Done = false
if r.Sym == nil { if r.Sym == nil {
r.Sym = ctxt.Tlsg r.Sym = ctxt.Tlsg
...@@ -243,7 +241,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -243,7 +241,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// related to the fact that our own TLS storage happens // related to the fact that our own TLS storage happens
// to take up 8 bytes. // to take up 8 bytes.
o = 8 + r.Sym.Value o = 8 + r.Sym.Value
} else if ctxt.IsELF || ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hdarwin || isAndroidX86 { } else if ctxt.IsELF || ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hdarwin {
o = int64(ctxt.Tlsoffset) + r.Add o = int64(ctxt.Tlsoffset) + r.Add
} else if ctxt.HeadType == objabi.Hwindows { } else if ctxt.HeadType == objabi.Hwindows {
o = r.Add o = r.Add
...@@ -251,9 +249,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -251,9 +249,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType) log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType)
} }
case objabi.R_TLS_IE: case objabi.R_TLS_IE:
isAndroidX86 := objabi.GOOS == "android" && (ctxt.Arch.InFamily(sys.AMD64, sys.I386)) if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
if ctxt.LinkMode == LinkExternal && ctxt.IsELF && !isAndroidX86 {
r.Done = false r.Done = false
if r.Sym == nil { if r.Sym == nil {
r.Sym = ctxt.Tlsg r.Sym = ctxt.Tlsg
......
...@@ -61,6 +61,7 @@ func linknew(arch *sys.Arch) *Link { ...@@ -61,6 +61,7 @@ func linknew(arch *sys.Arch) *Link {
} }
// computeTLSOffset records the thread-local storage offset. // computeTLSOffset records the thread-local storage offset.
// Not used for Android where the TLS offset is determined at runtime.
func (ctxt *Link) computeTLSOffset() { func (ctxt *Link) computeTLSOffset() {
switch ctxt.HeadType { switch ctxt.HeadType {
default: default:
...@@ -80,21 +81,7 @@ func (ctxt *Link) computeTLSOffset() { ...@@ -80,21 +81,7 @@ func (ctxt *Link) computeTLSOffset() {
objabi.Hopenbsd, objabi.Hopenbsd,
objabi.Hdragonfly, objabi.Hdragonfly,
objabi.Hsolaris: objabi.Hsolaris:
if objabi.GOOS == "android" { ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
switch ctxt.Arch.Family {
case sys.AMD64:
// Android/amd64 constant - offset from 0(FS) to our TLS slot.
// Explained in src/runtime/cgo/gcc_android_*.c
ctxt.Tlsoffset = 0x1d0
case sys.I386:
// Android/386 constant - offset from 0(GS) to our TLS slot.
ctxt.Tlsoffset = 0xf8
default:
ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
}
} else {
ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
}
case objabi.Hnacl: case objabi.Hnacl:
switch ctxt.Arch.Family { switch ctxt.Arch.Family {
......
...@@ -171,9 +171,18 @@ nocpuinfo: ...@@ -171,9 +171,18 @@ nocpuinfo:
MOVL _cgo_init(SB), AX MOVL _cgo_init(SB), AX
TESTL AX, AX TESTL AX, AX
JZ needtls JZ needtls
#ifdef GOOS_android
MOVL 0(TLS), BX
MOVL BX, 12(SP) // arg 4: TLS base, stored in the first slot (TLS_SLOT_SELF).
MOVL $runtime·tls_g(SB), 8(SP) // arg 3: &tls_g
#else
MOVL $0, BX
MOVL BX, 12(SP) // arg 3,4: not used when using platform's TLS
MOVL BX, 8(SP)
#endif
MOVL $setg_gcc<>(SB), BX MOVL $setg_gcc<>(SB), BX
MOVL BX, 4(SP) MOVL BX, 4(SP) // arg 2: setg_gcc
MOVL BP, 0(SP) MOVL BP, 0(SP) // arg 1: g0
CALL AX CALL AX
// update stackguard after _cgo_init // update stackguard after _cgo_init
...@@ -1553,3 +1562,7 @@ TEXT runtime·panicExtendSlice3CU(SB),NOSPLIT,$0-12 ...@@ -1553,3 +1562,7 @@ TEXT runtime·panicExtendSlice3CU(SB),NOSPLIT,$0-12
MOVL AX, lo+4(FP) MOVL AX, lo+4(FP)
MOVL CX, y+8(FP) MOVL CX, y+8(FP)
JMP runtime·goPanicExtendSlice3CU(SB) JMP runtime·goPanicExtendSlice3CU(SB)
#ifdef GOOS_android
GLOBL runtime·tls_g+0(SB), NOPTR, $4
#endif
...@@ -132,9 +132,22 @@ nocpuinfo: ...@@ -132,9 +132,22 @@ nocpuinfo:
MOVQ _cgo_init(SB), AX MOVQ _cgo_init(SB), AX
TESTQ AX, AX TESTQ AX, AX
JZ needtls JZ needtls
// g0 already in DI // arg 1: g0, already in DI
MOVQ DI, CX // Win64 uses CX for first parameter MOVQ $setg_gcc<>(SB), SI // arg 2: setg_gcc
MOVQ $setg_gcc<>(SB), SI #ifdef GOOS_android
MOVQ $runtime·tls_g(SB), DX // arg 3: &tls_g
MOVQ 0(TLS), CX // arg 4: TLS base, stored in the first slot (TLS_SLOT_SELF).
#else
MOVQ $0, DX // arg 3, 4: not used when using platform's TLS
MOVQ $0, CX
#endif
#ifdef GOOS_windows
// Adjust for the Win64 calling convention.
MOVQ CX, R9 // arg 4
MOVQ DX, R8 // arg 3
MOVQ SI, DX // arg 2
MOVQ DI, CX // arg 1
#endif
CALL AX CALL AX
// update stackguard after _cgo_init // update stackguard after _cgo_init
...@@ -1698,3 +1711,7 @@ TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 ...@@ -1698,3 +1711,7 @@ TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP) MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP) MOVQ CX, y+8(FP)
JMP runtime·goPanicSlice3CU(SB) JMP runtime·goPanicSlice3CU(SB)
#ifdef GOOS_android
GLOBL runtime·tls_g+0(SB), NOPTR, $8
#endif
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <android/log.h> #include <android/log.h>
#include <pthread.h>
#include "libcgo.h" #include "libcgo.h"
void void
...@@ -29,3 +30,37 @@ fatalf(const char* format, ...) ...@@ -29,3 +30,37 @@ fatalf(const char* format, ...)
abort(); abort();
} }
// Truncated to a different magic value on 32-bit; that's ok.
#define magic1 (0x23581321345589ULL)
// 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.tls_g.
static void
inittls(void **tlsg, void **tlsbase)
{
pthread_key_t k;
int i, err;
err = pthread_key_create(&k, nil);
if(err != 0) {
fatalf("pthread_key_create failed: %d", err);
}
pthread_setspecific(k, (void*)magic1);
// If thread local slots are laid out as we expect, our magic word will
// be located at some low offset from tlsbase. However, just in case something went
// wrong, the search is limited to sensible offsets. PTHREAD_KEYS_MAX was the
// original limit, but issue 19472 made a higher limit necessary.
for (i=0; i<384; i++) {
if (*(tlsbase+i) == (void*)magic1) {
*tlsg = (void*)(i*sizeof(void *));
pthread_setspecific(k, 0);
return;
}
}
fatalf("could not find pthread key");
}
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) = inittls;
// Copyright 2015 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 <string.h> /* for strerror */
#include <pthread.h>
#include <signal.h>
#include "libcgo.h"
#define magic1 (0x23581321U)
static void
inittls(void)
{
uint32 x;
pthread_key_t tofree[128], k;
int i, ntofree;
/*
* Same logic, code as gcc_android_amd64.c:/inittls.
* Note that this is a temporary hack that should be fixed soon.
*
* TODO: fix this.
*
* The linker and runtime hard-code this constant offset
* from %gs where we expect to find g. Disgusting.
*
* Known to src/cmd/link/internal/ld/sym.go:/0xf8
* and to src/runtime/sys_linux_386.s:/0xf8 or /GOOS_android.
* TODO(hyangah): check 0xb0 works with API23+
*
* As disgusting as on the darwin/386, darwin/amd64.
*/
ntofree = 0;
for(;;) {
if(pthread_key_create(&k, nil) != 0) {
fprintf(stderr, "runtime/cgo: pthread_key_create failed\n");
abort();
}
pthread_setspecific(k, (void*)magic1);
asm volatile("movl %%gs:0xf8, %0" : "=r"(x));
pthread_setspecific(k, 0);
if (x == magic1) {
break;
}
if(ntofree >= nelem(tofree)) {
fprintf(stderr, "runtime/cgo: could not obtain pthread_keys\n");
fprintf(stderr, "\ttried");
for(i=0; i<ntofree; i++)
fprintf(stderr, " %#x", (unsigned)tofree[i]);
fprintf(stderr, "\n");
abort();
}
tofree[ntofree++] = k;
}
// TODO: output to stderr is not useful for apps.
// Can we fall back to Android's log library?
/*
* We got the key we wanted. Free the others.
*/
for(i=0; i<ntofree; i++) {
pthread_key_delete(tofree[i]);
}
}
void (*x_cgo_inittls)(void) = inittls;
// Copyright 2015 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 <string.h> /* for strerror */
#include <pthread.h>
#include <signal.h>
#include "libcgo.h"
#define magic1 (0x23581321345589ULL)
static void
inittls(void)
{
uint64 x;
pthread_key_t tofree[128], k;
int i, ntofree;
/*
* Same logic, code as gcc_darwin_386.c:/inittls.
* Note that this is a temporary hack that should be fixed soon.
* Android-L and M bionic's pthread implementation differ
* significantly, and can change any time.
* https://android-review.googlesource.com/#/c/134202
*
* We chose %fs:0x1d0 which seems to work in testing with Android
* emulators (API22, API23) but it may break any time.
*
* TODO: fix this.
*
* The linker and runtime hard-code this constant offset
* from %fs where we expect to find g. Disgusting.
*
* Known to src/cmd/link/internal/ld/sym.go:/0x1d0
* and to src/runtime/sys_linux_amd64.s:/0x1d0 or /GOOS_android.
*
* As disgusting as on the darwin/386, darwin/amd64.
*/
ntofree = 0;
for(;;) {
if(pthread_key_create(&k, nil) != 0) {
fprintf(stderr, "runtime/cgo: pthread_key_create failed\n");
abort();
}
pthread_setspecific(k, (void*)magic1);
asm volatile("movq %%fs:0x1d0, %0" : "=r"(x));
pthread_setspecific(k, 0);
if(x == magic1) {
break;
}
if(ntofree >= nelem(tofree)) {
fprintf(stderr, "runtime/cgo: could not obtain pthread_keys\n");
fprintf(stderr, "\ttried");
for(i=0; i<ntofree; i++)
fprintf(stderr, " %#x", (unsigned)tofree[i]);
fprintf(stderr, "\n");
abort();
}
tofree[ntofree++] = k;
}
// TODO: output to stderr is not useful for apps.
// Can we fall back to Android's log library?
/*
* We got the key we wanted. Free the others.
*/
for(i=0; i<ntofree; i++) {
pthread_key_delete(tofree[i]);
}
}
void (*x_cgo_inittls)(void) = inittls;
// 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 <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <sys/limits.h>
#include "libcgo.h"
#define magic1 (0x23581321U)
// 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) {
fatalf("pthread_key_create failed: %d", err);
}
pthread_setspecific(k, (void*)magic1);
// If thread local slots are laid out as we expect, our magic word will
// be located at some low offset from tlsbase. However, just in case something went
// wrong, the search is limited to sensible offsets. PTHREAD_KEYS_MAX was the
// original limit, but issue 19472 made a higher limit necessary.
for (i=0; i<384; i++) {
if (*(tlsbase+i) == (void*)magic1) {
*tlsg = (void*)(i*sizeof(void *));
pthread_setspecific(k, 0);
return;
}
}
fatalf("could not find pthread key");
}
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) = inittls;
// Copyright 2015 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 <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <sys/limits.h>
#include "libcgo.h"
#define magic1 (0x23581321345589ULL)
// 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) {
fatalf("pthread_key_create failed: %d", err);
}
pthread_setspecific(k, (void*)magic1);
// If thread local slots are laid out as we expect, our magic word will
// be located at some low offset from tlsbase. However, just in case something went
// wrong, the search is limited to sensible offsets. PTHREAD_KEYS_MAX was the
// original limit, but issue 19472 made a higher limit necessary.
for (i=0; i<384; i++) {
if (*(tlsbase+i) == (void*)magic1) {
*tlsg = (void*)(i*sizeof(void *));
pthread_setspecific(k, 0);
return;
}
}
fatalf("could not find pthread key");
}
void (*x_cgo_inittls)(void **tlsg, void **tlsbase) = inittls;
...@@ -11,11 +11,11 @@ ...@@ -11,11 +11,11 @@
static void *threadentry(void*); static void *threadentry(void*);
static void (*setg_gcc)(void*); static void (*setg_gcc)(void*);
// This will be set in gcc_android_386.c for android-specific customization. // This will be set in gcc_android.c for android-specific customization.
void (*x_cgo_inittls)(void); void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
void void
x_cgo_init(G *g, void (*setg)(void*)) x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{ {
pthread_attr_t attr; pthread_attr_t attr;
size_t size; size_t size;
...@@ -27,7 +27,7 @@ x_cgo_init(G *g, void (*setg)(void*)) ...@@ -27,7 +27,7 @@ x_cgo_init(G *g, void (*setg)(void*))
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
if (x_cgo_inittls) { if (x_cgo_inittls) {
x_cgo_inittls(); x_cgo_inittls(tlsg, tlsbase);
} }
} }
......
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
static void* threadentry(void*); static void* threadentry(void*);
static void (*setg_gcc)(void*); static void (*setg_gcc)(void*);
// This will be set in gcc_android_amd64.c for android-specific customization. // This will be set in gcc_android.c for android-specific customization.
void (*x_cgo_inittls)(void); void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
void void
x_cgo_init(G* g, void (*setg)(void*)) x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{ {
pthread_attr_t *attr; pthread_attr_t *attr;
size_t size; size_t size;
...@@ -49,7 +49,7 @@ x_cgo_init(G* g, void (*setg)(void*)) ...@@ -49,7 +49,7 @@ x_cgo_init(G* g, void (*setg)(void*))
free(attr); free(attr);
if (x_cgo_inittls) { if (x_cgo_inittls) {
x_cgo_inittls(); x_cgo_inittls(tlsg, tlsbase);
} }
} }
......
...@@ -575,12 +575,8 @@ TEXT runtime·setldt(SB),NOSPLIT,$32 ...@@ -575,12 +575,8 @@ TEXT runtime·setldt(SB),NOSPLIT,$32
MOVL address+4(FP), DX // base address MOVL address+4(FP), DX // base address
#ifdef GOOS_android #ifdef GOOS_android
/* // Android stores the TLS offset in runtime·tls_g.
* Same as in sys_darwin_386.s:/ugliness, different constant. SUBL runtime·tls_g(SB), DX
* address currently holds m->tls, which must be %gs:0xf8.
* See cgo/gcc_android_386.c for the derivation of the constant.
*/
SUBL $0xf8, DX
MOVL DX, 0(DX) MOVL DX, 0(DX)
#else #else
/* /*
......
...@@ -605,10 +605,8 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$-8 ...@@ -605,10 +605,8 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
// set tls base to DI // set tls base to DI
TEXT runtime·settls(SB),NOSPLIT,$32 TEXT runtime·settls(SB),NOSPLIT,$32
#ifdef GOOS_android #ifdef GOOS_android
// Same as in sys_darwin_386.s:/ugliness, different constant. // Android stores the TLS offset in runtime·tls_g.
// DI currently holds m->tls, which must be fs:0x1d0. SUBQ runtime·tls_g(SB), DI
// See cgo/gcc_android_amd64.c for the derivation of the constant.
SUBQ $0x1d0, DI // In android, the tls base
#else #else
ADDQ $8, DI // ELF wants to use -8(FS) ADDQ $8, DI // ELF wants to use -8(FS)
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment