1
0
mirror of https://github.com/golang/go synced 2024-11-19 21:04:43 -07:00

linux/386: use Xen-friendly ELF TLS instruction sequence

Fixes #465.

R=iant
CC=golang-dev
https://golang.org/cl/1665051
This commit is contained in:
Russ Cox 2010-07-17 16:54:03 -07:00
parent bc2ee6b8c6
commit 2d6ae385e1
5 changed files with 76 additions and 12 deletions

View File

@ -102,7 +102,7 @@ Dconv(Fmt *fp)
i = a->type;
if(i >= D_INDIR && i < 2*D_INDIR) {
if(a->offset)
sprint(str, "%ld(%R)", a->offset, i-D_INDIR);
sprint(str, "%ld(%R)", (long)a->offset, i-D_INDIR);
else
sprint(str, "(%R)", i-D_INDIR);
goto brk;

View File

@ -378,7 +378,7 @@ patch(void)
s = lookup("exit", 0);
vexit = s->value;
for(p = firstp; p != P; p = p->link) {
if(HEADTYPE == 10) {
if(HEADTYPE == 10) { // Windows
// Convert
// op n(GS), reg
// to
@ -391,7 +391,7 @@ patch(void)
&& p->to.type >= D_AX && p->to.type <= D_DI) {
q = appendp(p);
q->from = p->from;
q->from.type += p->to.type-D_GS;
q->from.type = D_INDIR + p->to.type;
q->to = p->to;
q->as = p->as;
p->as = AMOVL;
@ -399,6 +399,23 @@ patch(void)
p->from.offset = 0x2C;
}
}
if(HEADTYPE == 7) { // Linux
// Running binaries under Xen requires using
// MOVL 0(GS), reg
// and then off(reg) instead of saying off(GS) directly
// when the offset is negative.
if(p->from.type == D_INDIR+D_GS && p->from.offset < 0
&& p->to.type >= D_AX && p->to.type <= D_DI) {
q = appendp(p);
q->from = p->from;
q->from.type = D_INDIR + p->to.type;
q->to = p->to;
q->as = p->as;
p->as = AMOVL;
p->from.type = D_INDIR+D_GS;
p->from.offset = 0;
}
}
if(p->as == ATEXT)
curtext = p;
if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) {
@ -616,7 +633,8 @@ dostkoff(void)
if(pmorestack != P)
if(!(p->from.scale & NOSPLIT)) {
p = appendp(p); // load g into CX
if(HEADTYPE == 10) {
switch(HEADTYPE) {
case 10: // Windows
p->as = AMOVL;
p->from.type = D_INDIR+D_FS;
p->from.offset = 0x2c;
@ -627,7 +645,22 @@ dostkoff(void)
p->from.type = D_INDIR+D_CX;
p->from.offset = 0;
p->to.type = D_CX;
} else {
break;
case 7: // Linux
p->as = AMOVL;
p->from.type = D_INDIR+D_GS;
p->from.offset = 0;
p->to.type = D_CX;
p = appendp(p);
p->as = AMOVL;
p->from.type = D_INDIR+D_CX;
p->from.offset = tlsoffset + 0;
p->to.type = D_CX;
break;
default:
p->as = AMOVL;
p->from.type = D_INDIR+D_GS;
p->from.offset = tlsoffset + 0;

View File

@ -45,11 +45,14 @@ threadentry(void *v)
* Set specific keys. On Linux/ELF, the thread local storage
* is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
* for the two words g and m at %gs:-8 and %gs:-4.
* Xen requires us to access those words indirect from %gs:0
* which points at itself.
*/
asm volatile (
"movl %0, %%gs:-8\n" // MOVL g, -8(GS)
"movl %1, %%gs:-4\n" // MOVL m, -4(GS)
:: "r"(ts.g), "r"(ts.m)
"movl %%gs:0, %%eax\n" // MOVL 0(GS), tmp
"movl %0, -8(%%eax)\n" // MOVL g, -8(GS)
"movl %1, -4(%%eax)\n" // MOVL m, -4(GS)
:: "r"(ts.g), "r"(ts.m) : "%eax"
);
crosscall_386(ts.fn);

View File

@ -161,12 +161,12 @@ TEXT clone(SB),7,$0
// In child on new stack. Reload registers (paranoia).
MOVL 0(SP), BX // m
MOVL 4(SP), DX // g
MOVL 8(SP), CX // fn
MOVL 8(SP), SI // fn
MOVL AX, m_procid(BX) // save tid as m->procid
// set up ldt 7+id to point at m->tls.
// m->tls is at m+40. newosproc left the id in tls[0].
// newosproc left the id in tls[0].
LEAL m_tls(BX), BP
MOVL 0(BP), DI
ADDL $7, DI // m0 is LDT#7. count up.
@ -186,7 +186,7 @@ TEXT clone(SB),7,$0
MOVL DX, g(AX)
MOVL BX, m(AX)
CALL stackcheck(SB) // smashes AX
CALL stackcheck(SB) // smashes AX, CX
MOVL 0(DX), DX // paranoia; check they are not nil
MOVL 0(BX), BX
@ -195,7 +195,7 @@ TEXT clone(SB),7,$0
CALL emptyfunc(SB)
POPAL
CALL CX // fn()
CALL SI // fn()
CALL exit1(SB)
MOVL $0x1234, 0x1005
RET
@ -247,8 +247,11 @@ TEXT setldt(SB),7,$32
* To accommodate that rewrite, we translate
* the address here and bump the limit to 0xffffffff (no limit)
* so that -8(GS) maps to 0(address).
* Also, the final 0(GS) (current 8(CX)) has to point
* to itself, to mimic ELF.
*/
ADDL $0x8, CX // address
MOVL CX, 0(CX)
// set up user_desc
LEAL 16(SP), AX // struct user_desc

View File

@ -24,6 +24,31 @@ case "$GOARCH" in
echo '#define g(r) 0(r)'
echo '#define m(r) 4(r)'
;;
linux)
# On Linux systems, what we call 0(GS) and 4(GS) for g and m
# turn into %gs:-8 and %gs:-4 (using gcc syntax to denote
# what the machine sees as opposed to 8l input).
# 8l rewrites 0(GS) and 4(GS) into these.
#
# On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4
# directly. Instead, we have to store %gs:0 into a temporary
# register and then use -8(%reg) and -4(%reg). This kind
# of addressing is correct even when not running Xen.
#
# 8l can rewrite MOVL 0(GS), CX into the appropriate pair
# of mov instructions, using CX as the intermediate register
# (safe because CX is about to be written to anyway).
# But 8l cannot handle other instructions, like storing into 0(GS),
# which is where these macros come into play.
# get_tls sets up the temporary and then g and r use it.
#
# The final wrinkle is that get_tls needs to read from %gs:0,
# but in 8l input it's called 8(GS), because 8l is going to
# subtract 8 from all the offsets, as described above.
echo '#define get_tls(r) MOVL 8(GS), r'
echo '#define g(r) -8(r)'
echo '#define m(r) -4(r)'
;;
*)
echo '#define get_tls(r)'
echo '#define g(r) 0(GS)'