]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[release-branch.go1.20] runtime: fallback to TEB arbitrary pointer when TLS slots...
authorqmuntal <quimmuntal@gmail.com>
Thu, 20 Apr 2023 15:30:38 +0000 (17:30 +0200)
committerGopher Robot <gobot@golang.org>
Thu, 22 Jun 2023 15:27:07 +0000 (15:27 +0000)
Note: This CL is cherry-picked from CL 486816 omitting the changes
in sys_windows_386.s, which don't apply to go1.20 release branch
because windows/386 started using the TEB TLS slot in go1.21 (CL 454675).

The Go runtime allocates the TLS slot in the TEB TLS slots instead of
using the TEB arbitrary pointer. See CL 431775 for more context.

The problem is that the TEB TLS slots array only has capacity for 64
indices, allocating more requires some complex logic that we don't
support yet.

Although the Go runtime only allocates one index, a Go DLL can be
loaded in a process with more than 64 TLS slots allocated,
in which case it abort.

This CL avoids aborting by falling back to the older behavior, that
is to use the TEB arbitrary pointer.

Fixes #60535
Updates #59213

Change-Id: I39c73286fe2da95aa9c5ec5657ee0979ecbec533
Reviewed-on: https://go-review.googlesource.com/c/go/+/486816
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Quim Muntal <quimmuntal@gmail.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit 715d53c090ea02dbd73c301684ecbd09b476989e)
Reviewed-on: https://go-review.googlesource.com/c/go/+/504475
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>

src/runtime/signal_windows_test.go
src/runtime/sys_windows_amd64.s
src/runtime/sys_windows_arm64.s
src/runtime/testdata/testwintls/main.c [new file with mode: 0644]
src/runtime/testdata/testwintls/main.go [new file with mode: 0644]

index c9b8e901189f9912265746736322816138e191f2..4c7a476bfb0b390dbe36442c39247e053c980ab4 100644 (file)
@@ -254,3 +254,59 @@ func TestLibraryCtrlHandler(t *testing.T) {
                t.Fatalf("Program exited with error: %v\n%s", err, &stderr)
        }
 }
+
+func TestIssue59213(t *testing.T) {
+       if runtime.GOOS != "windows" {
+               t.Skip("skipping windows only test")
+       }
+       if *flagQuick {
+               t.Skip("-quick")
+       }
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+
+       goEnv := func(arg string) string {
+               cmd := testenv.Command(t, testenv.GoToolPath(t), "env", arg)
+               cmd.Stderr = new(bytes.Buffer)
+
+               line, err := cmd.Output()
+               if err != nil {
+                       t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
+               }
+               out := string(bytes.TrimSpace(line))
+               t.Logf("%v: %q", cmd, out)
+               return out
+       }
+
+       cc := goEnv("CC")
+       cgoCflags := goEnv("CGO_CFLAGS")
+
+       t.Parallel()
+
+       tmpdir := t.TempDir()
+       dllfile := filepath.Join(tmpdir, "test.dll")
+       exefile := filepath.Join(tmpdir, "gotest.exe")
+
+       // build go dll
+       cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", dllfile, "-buildmode", "c-shared", "testdata/testwintls/main.go")
+       out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
+       if err != nil {
+               t.Fatalf("failed to build go library: %s\n%s", err, out)
+       }
+
+       // build c program
+       cmd = testenv.Command(t, cc, "-o", exefile, "testdata/testwintls/main.c")
+       testenv.CleanCmdEnv(cmd)
+       cmd.Env = append(cmd.Env, "CGO_CFLAGS="+cgoCflags)
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("failed to build c exe: %s\n%s", err, out)
+       }
+
+       // run test program
+       cmd = testenv.Command(t, exefile, dllfile, "GoFunc")
+       out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
+       if err != nil {
+               t.Fatalf("failed: %s\n%s", err, out)
+       }
+}
index 777726f7c11fc57b5a726fff7341ad72127f699c..4027770087778db47ac92b0b39d9cac44498b23d 100644 (file)
@@ -10,6 +10,7 @@
 
 // Offsets into Thread Environment Block (pointer in GS)
 #define TEB_TlsSlots 0x1480
+#define TEB_ArbitraryPtr 0x28
 
 // void runtime·asmstdcall(void *c);
 TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0
@@ -427,7 +428,11 @@ TEXT runtime·wintls(SB),NOSPLIT|NOFRAME,$0
        // Assert that slot is less than 64 so we can use _TEB->TlsSlots
        CMPQ    CX, $64
        JB      ok
-       CALL    runtime·abort(SB)
+
+       // Fallback to the TEB arbitrary pointer.
+       // TODO: don't use the arbitrary pointer (see go.dev/issue/59824)
+       MOVQ    $TEB_ArbitraryPtr, CX
+       JMP     settls
 ok:
        // Convert the TLS index at CX into
        // an offset from TEB_TlsSlots.
@@ -435,5 +440,6 @@ ok:
 
        // Save offset from TLS into tls_g.
        ADDQ    $TEB_TlsSlots, CX
+settls:
        MOVQ    CX, runtime·tls_g(SB)
        RET
index 4702a4d7d254f742fc58fa12a1464a775e149ab7..e3082a1f55e3bd68e9eb02d8ce5f312c02b55323 100644 (file)
@@ -12,6 +12,7 @@
 // Offsets into Thread Environment Block (pointer in R18)
 #define TEB_error 0x68
 #define TEB_TlsSlots 0x1480
+#define TEB_ArbitraryPtr 0x28
 
 // Note: R0-R7 are args, R8 is indirect return value address,
 // R9-R15 are caller-save, R19-R29 are callee-save.
@@ -415,12 +416,15 @@ TEXT runtime·wintls(SB),NOSPLIT,$0
        // Assert that slot is less than 64 so we can use _TEB->TlsSlots
        CMP     $64, R0
        BLT     ok
-       MOVD    $runtime·abort(SB), R1
-       BL      (R1)
+       // Fallback to the TEB arbitrary pointer.
+       // TODO: don't use the arbitrary pointer (see go.dev/issue/59824)
+       MOVD    $TEB_ArbitraryPtr, R0
+       B       settls
 ok:
 
        // Save offset from R18 into tls_g.
        LSL     $3, R0
        ADD     $TEB_TlsSlots, R0
+settls:
        MOVD    R0, runtime·tls_g(SB)
        RET
diff --git a/src/runtime/testdata/testwintls/main.c b/src/runtime/testdata/testwintls/main.c
new file mode 100644 (file)
index 0000000..6061828
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2023 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 <windows.h>
+
+int main(int argc, char **argv) {
+    if (argc < 3) {
+        return 1;
+    }
+    // Allocate more than 64 TLS indices
+    // so the Go runtime doesn't find
+    // enough space in the TEB TLS slots.
+    for (int i = 0; i < 65; i++) {
+        TlsAlloc();
+    }
+    HMODULE hlib = LoadLibrary(argv[1]);
+    if (hlib == NULL) {
+        return 2;
+    }
+    FARPROC proc = GetProcAddress(hlib, argv[2]);
+    if (proc == NULL) {
+        return 3;
+    }
+    if (proc() != 42) {
+        return 4;
+    }
+    return 0;
+}
\ No newline at end of file
diff --git a/src/runtime/testdata/testwintls/main.go b/src/runtime/testdata/testwintls/main.go
new file mode 100644 (file)
index 0000000..1cf296c
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2023 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.
+
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() int { return 42 }
+
+func main() {}