Open corhere opened 1 year ago
I can reproduce this issue in tip:
> go version
go version devel go1.22-c9c885f92f Mon Oct 2 15:18:39 2023 +0000 windows/amd64
> gcc --version
gcc.exe (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> go test -ldflags=-linkmode=internal cmd/cgo/internal/test -run TestMultipleAssign
exit status 0xc0000409
FAIL cmd/cgo/internal/test 2.018
This is a smaller reproducer:
package main
/*
#include <windows.h>
*/
import "C"
import "unsafe"
func main() {
p := C.CString("234")
n := C.strtol(p, nil, 37)
C.free(unsafe.Pointer(p))
println("done", n)
}
The ofender is C.strtol(p, nil, 37)
. The base parameter (37
) is not valid, so strtol
tries to return a EINVAL
error. My current theory is that when a EINVAL
happens, Windows performs some security checks that depend on the stack being correct and unwindable, which is not the case here because the Go internal linker does not copy the SEH information generated by the C compiler into the final binary. As the security checks don't pass, the program crashed.
Notices that windows/amd64 supports SEH unwinding since #57302, but I missed this case when I was implementing that.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
No. The cgo test suite (moved to package path
cmd/cgo/internal/test
) passes with Go 1.21 when built with-linkmode=internal
using the same C toolchain.What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
What did you expect to see?
What did you see instead?
go test -v
``` PS C:\Program Files\Go\misc\cgo\test> go test -v -ldflags=-linkmode=internal -tags=internal === RUN Test1328 --- PASS: Test1328 (0.00s) === RUN Test1560 --- PASS: Test1560 (0.00s) === RUN Test1635 scatter = 00000000008522E0 --- PASS: Test1635 (0.00s) === RUN Test3250 test.go:1277: not applicable on windows --- SKIP: Test3250 (0.00s) === RUN Test3729 test.go:1353: skipping on windows --- SKIP: Test3729 (0.00s) === RUN Test3775 --- PASS: Test3775 (0.00s) === RUN Test4029 --- PASS: Test4029 (0.00s) === RUN Test4339 --- PASS: Test4339 (0.00s) === RUN Test5227 --- PASS: Test5227 (0.00s) === RUN Test5242 --- PASS: Test5242 (0.00s) === RUN Test5337 --- PASS: Test5337 (0.00s) === RUN Test5548 --- PASS: Test5548 (0.00s) === RUN Test5603 --- PASS: Test5603 (0.00s) === RUN Test5986 sqrt is: 0 --- PASS: Test5986 (0.00s) === RUN Test6390 --- PASS: Test6390 (0.00s) === RUN Test6833 --- PASS: Test6833 (0.00s) === RUN Test6907 --- PASS: Test6907 (0.00s) === RUN Test6907Go --- PASS: Test6907Go (0.00s) === RUN Test7560 --- PASS: Test7560 (0.00s) === RUN Test7665 --- PASS: Test7665 (0.00s) === RUN Test7978 --- PASS: Test7978 (0.67s) === RUN Test8092 --- PASS: Test8092 (0.00s) === RUN Test8517 --- PASS: Test8517 (0.00s) === RUN Test8694 --- PASS: Test8694 (0.00s) === RUN Test8756 --- PASS: Test8756 (0.00s) === RUN Test8811 --- PASS: Test8811 (0.00s) === RUN Test9026 --- PASS: Test9026 (0.00s) === RUN Test9510 --- PASS: Test9510 (0.00s) === RUN Test9557 --- PASS: Test9557 (0.00s) === RUN Test10303 --- PASS: Test10303 (0.00s) === RUN Test11925 --- PASS: Test11925 (0.00s) === RUN Test12030 --- PASS: Test12030 (0.00s) === RUN Test14838 --- PASS: Test14838 (0.00s) === RUN Test17065 --- PASS: Test17065 (0.00s) === RUN Test17537 --- PASS: Test17537 (0.00s) === RUN Test18126 --- PASS: Test18126 (0.00s) === RUN Test18720 --- PASS: Test18720 (0.00s) === RUN Test20129 --- PASS: Test20129 (0.00s) === RUN Test20266 --- PASS: Test20266 (0.00s) === RUN Test20369 --- PASS: Test20369 (0.00s) === RUN Test20910 --- PASS: Test20910 (0.00s) === RUN Test21708 --- PASS: Test21708 (0.00s) === RUN Test21809 --- PASS: Test21809 (0.00s) === RUN Test21897 issue21897b.go:13: test runs only on darwin+cgo --- SKIP: Test21897 (0.00s) === RUN Test22906 --- PASS: Test22906 (0.00s) === RUN Test23356 --- PASS: Test23356 (0.00s) === RUN Test24206 test.go:2028: skipping on windows/amd64 --- SKIP: Test24206 (0.00s) === RUN Test25143 --- PASS: Test25143 (0.00s) === RUN Test26066 --- PASS: Test26066 (0.00s) === RUN Test26213 --- PASS: Test26213 (0.00s) === RUN Test27660 --- PASS: Test27660 (0.63s) === RUN Test28896 --- PASS: Test28896 (0.00s) === RUN Test30065 --- PASS: Test30065 (0.00s) === RUN Test32579 --- PASS: Test32579 (0.00s) === RUN Test31891 --- PASS: Test31891 (0.00s) === RUN Test42018 --- PASS: Test42018 (0.00s) === RUN Test45451 --- PASS: Test45451 (0.00s) === RUN Test49633 --- PASS: Test49633 (0.00s) === RUN TestAlign --- PASS: TestAlign (0.00s) === RUN TestAtol --- PASS: TestAtol (0.00s) === RUN TestBlocking --- PASS: TestBlocking (0.00s) === RUN TestBoolAlign --- PASS: TestBoolAlign (0.00s) === RUN TestCallGoWithString --- PASS: TestCallGoWithString (0.00s) === RUN TestCallback --- PASS: TestCallback (0.00s) === RUN TestCallbackCallers --- PASS: TestCallbackCallers (0.00s) === RUN TestCallbackGC --- PASS: TestCallbackGC (0.00s) === RUN TestCallbackPanic --- PASS: TestCallbackPanic (0.00s) === RUN TestCallbackPanicLocked --- PASS: TestCallbackPanicLocked (0.00s) === RUN TestCallbackPanicLoop --- PASS: TestCallbackPanicLoop (0.36s) === RUN TestCallbackStack --- PASS: TestCallbackStack (0.01s) === RUN TestCflags --- PASS: TestCflags (0.00s) === RUN TestCheckConst --- PASS: TestCheckConst (0.00s) === RUN TestConst --- PASS: TestConst (0.00s) === RUN TestCthread --- PASS: TestCthread (0.00s) === RUN TestEnum --- PASS: TestEnum (0.00s) === RUN TestNamedEnum --- PASS: TestNamedEnum (0.00s) === RUN TestCastToEnum --- PASS: TestCastToEnum (0.00s) === RUN TestErrno --- PASS: TestErrno (0.00s) === RUN TestFpVar --- PASS: TestFpVar (0.00s) === RUN TestGCC68255 --- PASS: TestGCC68255 (0.00s) === RUN TestHandle --- PASS: TestHandle (0.00s) === RUN TestHelpers --- PASS: TestHelpers (0.00s) === RUN TestLibgcc --- PASS: TestLibgcc (0.00s) === RUN TestMultipleAssign exit status 0xc0000409 FAIL misc/cgo/test 3.012s ```
gdb
session``` PS C:\Program Files\Go\misc\cgo\test> go test -ldflags=-linkmode=internal -tags=internal -c PS C:\Program Files\Go\misc\cgo\test> gdb -iex 'set auto-load safe-path /' test.test.exe GNU gdb (GDB for MinGW-W64 x86_64, built by Brecht Sanders) 13.2 Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test.test.exe...
Loading Go Runtime support.
(gdb) r -test.run TestMultipleAssign
Starting program: C:\Program Files\Go\misc\cgo\test\test.test.exe -test.run TestMultipleAssign
[New Thread 6928.0x21b4]
[New Thread 6928.0x1828]
[New Thread 6928.0x7f8]
[New Thread 6928.0x1188]
[New Thread 6928.0x1624]
[New Thread 6928.0x21e0]
[New Thread 6928.0x17c0]
gdb: unknown target exception 0xc0000409 at 0x7ffad467b3a8
Thread 1 received signal ?, Unknown signal.
0x00007ffad467b3a8 in ucrtbase!_invoke_watson () from C:\Windows\System32\ucrtbase.dll
(gdb) bt
#0 0x00007ffad467b3a8 in ucrtbase!_invoke_watson () from C:\Windows\System32\ucrtbase.dll
#1 0x00007ffad4660854 in ucrtbase!log2f () from C:\Windows\System32\ucrtbase.dll
#2 0x00007ffad4652e20 in ucrtbase!.intrinsic_setjmpex () from C:\Windows\System32\ucrtbase.dll
#3 0x00007ffad461ff0c in ucrtbase!strtol () from C:\Windows\System32\ucrtbase.dll
#4 0x0000000000c03822 in _cgo_0b47cc75cb3f_Cfunc_strtol ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
```
The cgo test suite passes with
-linkmode=internal
when the MSVCRT runtime variant of the same MinGW-W64 toolchain is used.The impact of this issue is minimal. External linking would be a viable workaround for those who need to match up C runtime versions for compatibility with third-party DLLs. And for those of us (like myself) who are building Go from source and are needing to get
go tool dist test
to pass, switching to a MinGW that links against MSVCRT when building the Go toolchain is a viable workaround as no pre-compiled native code is required in binary distributions of Go 1.20.