about summary refs log tree commit diff
path: root/src/thread
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2015-05-16 01:15:40 -0400
committerRich Felker <dalias@aerifal.cx>2015-05-16 01:15:40 -0400
commit707d7c30f3379441de9b320536ddfd354f4c2143 (patch)
treef2252cc09c6ae67ac14a025be2314ca8079280f5 /src/thread
parentc0f10cf06725bd0de37f3ced7954a653bf9f1049 (diff)
downloadmusl-707d7c30f3379441de9b320536ddfd354f4c2143.tar.gz
musl-707d7c30f3379441de9b320536ddfd354f4c2143.tar.xz
musl-707d7c30f3379441de9b320536ddfd354f4c2143.zip
in i386 __set_thread_area, don't assume %gs register is initially zero
commit f630df09b1fd954eda16e2f779da0b5ecc9d80d3 added logic to handle
the case where __set_thread_area is called more than once by reusing
the GDT slot already in the %gs register, and only setting up a new
GDT slot when %gs is zero. this created a hidden assumption that %gs
is zero when a new process image starts, which is true in practice on
Linux, but does not seem to be documented ABI, and fails to hold under
qemu app-level emulation.

while it would in theory be possible to zero %gs in the entry point
code, this code is shared between static and dynamic binaries, and
dynamic binaries must not clobber the value of %gs already setup by
the dynamic linker.

the alternative solution implemented in this commit simply uses global
data to store the GDT index that's selected. __set_thread_area should
only be called in the initial thread anyway (subsequent threads get
their thread pointer setup by __clone), but even if it were called by
another thread, it would simply read and write back the same GDT index
that was already assigned to the initial thread, and thus (in the x86
memory model) there is no data race.
Diffstat (limited to 'src/thread')
-rw-r--r--src/thread/i386/__set_thread_area.s13
1 files changed, 9 insertions, 4 deletions
diff --git a/src/thread/i386/__set_thread_area.s b/src/thread/i386/__set_thread_area.s
index 1d852689..3a558fb0 100644
--- a/src/thread/i386/__set_thread_area.s
+++ b/src/thread/i386/__set_thread_area.s
@@ -6,10 +6,10 @@ __set_thread_area:
 	push $0x51
 	push $0xfffff
 	push 16(%esp)
-	xor %edx,%edx
-	mov %gs,%dx
-	sub $3,%edx
-	sar $3,%edx
+	call 1f
+1:	addl $4f-1b,(%esp)
+	pop %ecx
+	mov (%ecx),%edx
 	push %edx
 	mov %esp,%ebx
 	xor %eax,%eax
@@ -18,6 +18,7 @@ __set_thread_area:
 	testl %eax,%eax
 	jnz 2f
 	movl (%esp),%edx
+	movl %edx,(%ecx)
 	leal 3(,%edx,8),%edx
 3:	movw %dx,%gs
 1:
@@ -38,3 +39,7 @@ __set_thread_area:
 	mov $7,%dl
 	inc %al
 	jmp 3b
+
+.data
+	.align 4
+4:	.long -1