mirror of
https://github.com/golang/go
synced 2024-11-25 08:47:56 -07:00
runtime: work around kernel bug in Snow Leopard signal handling
Could not take a signal on threads other than the main thread. If you look at the spinning binary with dtrace, you can see a fault happening over and over: $ dtrace -n ' fbt::user_trap:entry /execname=="boot32" && self->count < 10/ { self->count++; printf("%s %x %x %x %x", probefunc, arg1, arg2, arg3, arg4); stack(); tracemem(arg4, 256); }' dtrace: description 'fbt::user_trap:entry ' matched 1 probe CPU ID FUNCTION:NAME 1 17015 user_trap:entry user_trap 0 10 79af0a0 79af0a0 mach_kernel`lo_alltraps+0x12a 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 0: 0e 00 00 00 37 00 00 00 00 00 00 00 1f 00 00 00 ....7........... 10: 1f 00 00 00 a8 33 00 00 00 00 00 01 00 00 00 00 .....3.......... 20: 98 ba dc fe 07 09 00 00 00 00 00 00 98 ba dc fe ................ 30: 06 00 00 00 0d 00 00 00 34 00 00 00 9e 1c 00 00 ........4....... 40: 17 00 00 00 00 02 00 00 ac 30 00 00 1f 00 00 00 .........0...... 50: 00 00 00 00 00 00 00 00 0d 00 00 00 e0 e6 29 00 ..............). 60: 34 00 00 00 00 00 00 00 9e 1c 00 00 00 00 00 00 4............... 70: 17 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 ................ 80: ac 30 00 00 00 00 00 00 1f 00 00 00 00 00 00 00 .0.............. 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ a0: 48 00 00 00 10 00 00 00 85 00 00 00 a0 f2 29 00 H.............). b0: 69 01 00 02 00 00 00 00 e6 93 04 82 ff 7f 00 00 i............... c0: 2f 00 00 00 00 00 00 00 06 02 00 00 00 00 00 00 /............... d0: 78 ee 42 01 01 00 00 00 1f 00 00 00 00 00 00 00 x.B............. e0: 00 ed 9a 07 00 00 00 00 00 00 00 00 00 00 00 00 ................ f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ... The memory dump shows a 32-bit exception frame: x86_saved_state32 gs = 0x37 fs = 0 es = 0x1f ds = 0x1f edi = 0x33a8 esi = 0x01000000 ebp = 0 cr2 = 0xfedcba98 ebx = 0x0907 edx = 0 ecx = 0xfedcba98 eax = 0x06 trapno = 0x0d err = 0x34 eip = 0x1c9e cs = 0x17 efl = 0x0200 uesp = 0x30ac ss = 0x1f The cr2 of 0xfedcba98 is the address that the new thread read to cause the fault, but note that the trap is now a GP fault with error code 0x34, meaning it's moved past the cr2 problem and on to an invaild segment selector. The 0x34 is suspiciously similar to the 0x37 in gs, and sure enough, OS X forces gs to have that value in the signal handler, and if your thread hasn't set up that segment (known as USER_CTHREAD), you'll fault on the IRET into the signal handler and never be able to handle a signal. The kernel bug is that it forces segment 0x37 without making sure it is a valid segment. Leopard also forced 0x37 but had the courtesy to set it up first. Since OS X requires us to set up that segment (using the thread_fast_set_cthread_self system call), we might as well use it instead of the more complicated i386_set_ldt call to set up our per-OS thread storage. Also add some more zeros to bsdthread_register for new arguments in Snow Leopard (apparently unnecessary, but being careful). Fixes #510. R=r CC=golang-dev https://golang.org/cl/824046
This commit is contained in:
parent
9aa8f95ba8
commit
2f0cae46d8
@ -74,11 +74,6 @@ TEXT sigaction(SB),7,$0
|
|||||||
// 16(FP) siginfo
|
// 16(FP) siginfo
|
||||||
// 20(FP) context
|
// 20(FP) context
|
||||||
TEXT sigtramp(SB),7,$40
|
TEXT sigtramp(SB),7,$40
|
||||||
// Darwin sets GS to 0x37 on entry.
|
|
||||||
// The original GS is at 0x70(FP).
|
|
||||||
MOVL oldgs+0x70(FP), BX
|
|
||||||
MOVW BX, GS
|
|
||||||
|
|
||||||
// g = m->gsignal
|
// g = m->gsignal
|
||||||
get_tls(CX)
|
get_tls(CX)
|
||||||
MOVL m(CX), BP
|
MOVL m(CX), BP
|
||||||
@ -186,9 +181,9 @@ TEXT bsdthread_register(SB),7,$40
|
|||||||
MOVL $bsdthread_start(SB), 4(SP) // threadstart
|
MOVL $bsdthread_start(SB), 4(SP) // threadstart
|
||||||
MOVL $0, 8(SP) // wqthread, not used by us
|
MOVL $0, 8(SP) // wqthread, not used by us
|
||||||
MOVL $0, 12(SP) // pthsize, not used by us
|
MOVL $0, 12(SP) // pthsize, not used by us
|
||||||
MOVL $0, 16(SP) // paranoia
|
MOVL $0, 16(SP) // dummy_value [sic]
|
||||||
MOVL $0, 20(SP)
|
MOVL $0, 20(SP) // targetconc_ptr
|
||||||
MOVL $0, 24(SP)
|
MOVL $0, 24(SP) // dispatchqueue_offset
|
||||||
INT $0x80
|
INT $0x80
|
||||||
JAE 2(PC)
|
JAE 2(PC)
|
||||||
CALL notok(SB)
|
CALL notok(SB)
|
||||||
@ -253,26 +248,10 @@ TEXT mach_semaphore_signal_all(SB),7,$0
|
|||||||
CALL sysenter(SB)
|
CALL sysenter(SB)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
/*
|
|
||||||
descriptor entry format for system call
|
|
||||||
is the native machine format, ugly as it is:
|
|
||||||
|
|
||||||
2-byte limit
|
|
||||||
3-byte base
|
|
||||||
1-byte: 0x80=present, 0x60=dpl<<5, 0x1F=type
|
|
||||||
1-byte: 0x80=limit is *4k, 0x40=32-bit operand size,
|
|
||||||
0x0F=4 more bits of limit
|
|
||||||
1 byte: 8 more bits of base
|
|
||||||
|
|
||||||
int i386_get_ldt(int, union ldt_entry *, int);
|
|
||||||
int i386_set_ldt(int, const union ldt_entry *, int);
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// setldt(int entry, int address, int limit)
|
// setldt(int entry, int address, int limit)
|
||||||
|
// entry and limit are ignored.
|
||||||
TEXT setldt(SB),7,$32
|
TEXT setldt(SB),7,$32
|
||||||
MOVL address+4(FP), BX // aka base
|
MOVL address+4(FP), BX // aka base
|
||||||
MOVL limit+8(FP), CX
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When linking against the system libraries,
|
* When linking against the system libraries,
|
||||||
@ -288,44 +267,20 @@ TEXT setldt(SB),7,$32
|
|||||||
* of the constant.
|
* of the constant.
|
||||||
*/
|
*/
|
||||||
SUBL $0x468, BX
|
SUBL $0x468, BX
|
||||||
ADDL $0x468, CX
|
|
||||||
|
|
||||||
// set up data_desc
|
/*
|
||||||
LEAL 16(SP), AX // struct data_desc
|
* Must set up as USER_CTHREAD segment because
|
||||||
MOVL $0, 0(AX)
|
* Darwin forces that value into %gs for signal handlers,
|
||||||
MOVL $0, 4(AX)
|
* and if we don't set one up, we'll get a recursive
|
||||||
|
* fault trying to get into the signal handler.
|
||||||
|
* Since we have to set one up anyway, it might as
|
||||||
|
* well be the value we want. So don't bother with
|
||||||
|
* i386_set_ldt.
|
||||||
|
*/
|
||||||
|
MOVL BX, 4(SP)
|
||||||
|
MOVL $3, AX // thread_fast_set_cthread_self - machdep call #3
|
||||||
|
INT $0x82 // sic: 0x82, not 0x80, for machdep call
|
||||||
|
|
||||||
MOVW BX, 2(AX)
|
XORL AX, AX
|
||||||
SHRL $16, BX
|
MOVW GS, AX
|
||||||
MOVB BX, 4(AX)
|
|
||||||
SHRL $8, BX
|
|
||||||
MOVB BX, 7(AX)
|
|
||||||
|
|
||||||
MOVW CX, 0(AX)
|
|
||||||
SHRL $16, CX
|
|
||||||
ANDL $0x0F, CX
|
|
||||||
ORL $0x40, CX // 32-bit operand size
|
|
||||||
MOVB CX, 6(AX)
|
|
||||||
|
|
||||||
MOVB $0xF2, 5(AX) // r/w data descriptor, dpl=3, present
|
|
||||||
|
|
||||||
// call i386_set_ldt(entry, desc, 1)
|
|
||||||
MOVL $0xffffffff, 0(SP) // auto-allocate entry and return in AX
|
|
||||||
MOVL AX, 4(SP)
|
|
||||||
MOVL $1, 8(SP)
|
|
||||||
CALL i386_set_ldt(SB)
|
|
||||||
|
|
||||||
// compute segment selector - (entry*8+7)
|
|
||||||
SHLL $3, AX
|
|
||||||
ADDL $7, AX
|
|
||||||
MOVW AX, GS
|
|
||||||
RET
|
RET
|
||||||
|
|
||||||
TEXT i386_set_ldt(SB),7,$0
|
|
||||||
MOVL $5, AX
|
|
||||||
INT $0x82 // sic
|
|
||||||
JAE 2(PC)
|
|
||||||
CALL notok(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
GLOBL tlsoffset(SB),$4
|
|
||||||
|
@ -113,6 +113,7 @@ TEXT bsdthread_create(SB),7,$0
|
|||||||
MOVQ gg+24(SP), R10 // "pthread"
|
MOVQ gg+24(SP), R10 // "pthread"
|
||||||
// TODO(rsc): why do we get away with 0 flags here but not on 386?
|
// TODO(rsc): why do we get away with 0 flags here but not on 386?
|
||||||
MOVQ $0, R8 // flags
|
MOVQ $0, R8 // flags
|
||||||
|
MOVQ $0, R9 // paranoia
|
||||||
MOVQ $(0x2000000+360), AX // bsdthread_create
|
MOVQ $(0x2000000+360), AX // bsdthread_create
|
||||||
SYSCALL
|
SYSCALL
|
||||||
JCC 2(PC)
|
JCC 2(PC)
|
||||||
@ -146,6 +147,9 @@ TEXT bsdthread_register(SB),7,$0
|
|||||||
MOVQ $bsdthread_start(SB), DI // threadstart
|
MOVQ $bsdthread_start(SB), DI // threadstart
|
||||||
MOVQ $0, SI // wqthread, not used by us
|
MOVQ $0, SI // wqthread, not used by us
|
||||||
MOVQ $0, DX // pthsize, not used by us
|
MOVQ $0, DX // pthsize, not used by us
|
||||||
|
MOVQ $0, R10 // dummy_value [sic]
|
||||||
|
MOVQ $0, R8 // targetconc_ptr
|
||||||
|
MOVQ $0, R9 // dispatchqueue_offset
|
||||||
MOVQ $(0x2000000+366), AX // bsdthread_register
|
MOVQ $(0x2000000+366), AX // bsdthread_register
|
||||||
SYSCALL
|
SYSCALL
|
||||||
JCC 2(PC)
|
JCC 2(PC)
|
||||||
|
Loading…
Reference in New Issue
Block a user