]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use set_thread_area instead of modify_ldt on linux/386
authorShinji Tanaka <shinji.tanaka@gmail.com>
Sat, 26 Mar 2016 11:14:15 +0000 (07:14 -0400)
committerMinux Ma <minux@golang.org>
Mon, 28 Mar 2016 16:56:38 +0000 (16:56 +0000)
linux/386 depends on modify_ldt system call, but recent Linux kernels
can disable this system call. Any Go programs built as linux/386
crash with the message 'Trace/breakpoint trap'.

The kernel config CONFIG_MODIFY_LDT_SYSCALL, which control
enable/disable modify_ldt, is disabled on Amazon Linux 2016.03.

This fixes this problem by using set_thread_area instead of modify_ldt
on linux/386.

Fixes #14795.

Change-Id: I0cc5139e40e9e5591945164156a77b6bdff2c7f1
Reviewed-on: https://go-review.googlesource.com/21190
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Minux Ma <minux@golang.org>
src/runtime/sys_linux_386.s

index 3c7b17f927582b3f6c9116eae60d8bfe0ab5bbb5..3bf5eb0df47cce87da7dc6f3a91ff0675f457a02 100644 (file)
@@ -406,9 +406,18 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
 #define SEG_NOT_PRESENT 0x20
 #define USEABLE 0x40
 
+// `-1` means the kernel will pick a TLS entry on the first setldt call,
+// which happens during runtime init, and that we'll store back the saved
+// entry and reuse that on subsequent calls when creating new threads.
+DATA  runtime·tls_entry_number+0(SB)/4, $-1
+GLOBL runtime·tls_entry_number(SB), NOPTR, $4
+
 // setldt(int entry, int address, int limit)
+// We use set_thread_area, which mucks with the GDT, instead of modify_ldt,
+// which would modify the LDT, but is disabled on some kernels.
+// The name, setldt, is a misnomer, although we leave this name as it is for
+// the compatibility with other platforms.
 TEXT runtime·setldt(SB),NOSPLIT,$32
-       MOVL    entry+0(FP), BX // entry
        MOVL    address+4(FP), DX       // base address
 
 #ifdef GOOS_android
@@ -437,18 +446,19 @@ TEXT runtime·setldt(SB),NOSPLIT,$32
        MOVL    DX, 0(DX)
 #endif
 
+       // get entry number
+       MOVL    runtime·tls_entry_number(SB), CX
+
        // set up user_desc
        LEAL    16(SP), AX      // struct user_desc
-       MOVL    BX, 0(AX)
-       MOVL    DX, 4(AX)
-       MOVL    $0xfffff, 8(AX)
+       MOVL    CX, 0(AX)       // unsigned int entry_number
+       MOVL    DX, 4(AX)       // unsigned long base_addr
+       MOVL    $0xfffff, 8(AX) // unsigned int limit
        MOVL    $(SEG_32BIT|LIMIT_IN_PAGES|USEABLE|CONTENTS_DATA), 12(AX)       // flag bits
 
-       // call modify_ldt
-       MOVL    $1, BX  // func = 1 (write)
-       MOVL    AX, CX  // user_desc
-       MOVL    $16, DX // sizeof(user_desc)
-       MOVL    $123, AX        // syscall - modify_ldt
+       // call set_thread_area
+       MOVL    AX, BX  // user_desc
+       MOVL    $243, AX        // syscall - set_thread_area
        // We can't call this via 0x10(GS) because this is called from setldt0 to set that up.
        INT     $0x80
 
@@ -457,10 +467,18 @@ TEXT runtime·setldt(SB),NOSPLIT,$32
        JLS 2(PC)
        INT $3
 
-       // compute segment selector - (entry*8+7)
-       MOVL    entry+0(FP), AX
+       // read allocated entry number back out of user_desc
+       LEAL    16(SP), AX      // get our user_desc back
+       MOVL    0(AX), AX
+
+       // store entry number if the kernel allocated it
+       CMPL    CX, $-1
+       JNE     2(PC)
+       MOVL    AX, runtime·tls_entry_number(SB)
+
+       // compute segment selector - (entry*8+3)
        SHLL    $3, AX
-       ADDL    $7, AX
+       ADDL    $3, AX
        MOVW    AX, GS
 
        RET