New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/cgo: signal arrived during external code execution that calls setjmp on Windows #13672

Open
redstorm-fyy opened this Issue Dec 18, 2015 · 29 comments

Comments

Projects
None yet
@redstorm-fyy

redstorm-fyy commented Dec 18, 2015

Processor Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
OS: Windows 7 64
golang:1.5.2
gcc:mingw-w64 version 5.2.0 x86_64-posix-seh-rt_v4-rev1

When I compile golang with some external c code which using the setjmp and longjmp,the program crash.

My c code is here (It's in a c file in a directory which implements package lua)

#include <stdio.h>
#include <setjmp.h>
void Print(){
    jmp_buf c;
    if(setjmp(c)==0){
        longjmp(c,1);
    }
    else{
        printf("receive an exception\n");
    }
}

My go code

package main
import "github.com/redstorm-fyy/go-lua/lua"
func main() {
    lua.Print()
}

Important!
It crashes when the c code is in an external file.When the c code is just up the .go file's import "C",It does not crash.

Crash information
Exception 0xc0000028 0x0 0x0 0x77b096f8
PC=0x77b096f8
signal arrived during external code execution

github.com/redstorm-fyy/go-lua/lua._Cfunc_Print()
??:0 +0x38
github.com/redstorm-fyy/go-lua/lua.Print()
E:/GolangRoot/src/github.com/redstorm-fyy/go-lua/lua/luago.go:587 +0x1b
main.main()
E:/GolangRoot/src/github.com/redstorm-fyy/go-lua/example/exam.go:6 +0x1b

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
c:/go/src/runtime/asm_amd64.s:1721 +0x1
rax 0xf
rbx 0xc0000028
rcx 0x22ebd0
rdi 0x22c000
rsi 0x22f7b0
rbp 0x22f230
rsp 0x22eb10
r8 0x0
r9 0x400000
r10 0x0
r11 0x22f330
r12 0x22fe60
r13 0x22fdb0
r14 0x4013e8
r15 0x584024
rip 0x77b096f8
rflags 0x202
cs 0x33
fs 0x53
gs 0x2b

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Dec 18, 2015

Looks a lot like #9754.

Any chance you can write a standalone test case that does not involve using Lua?

@ianlancetaylor ianlancetaylor changed the title from signal arrived during external code execution to cmd/cgo: signal arrived during external code execution Dec 18, 2015

@ianlancetaylor ianlancetaylor changed the title from cmd/cgo: signal arrived during external code execution to cmd/cgo: signal arrived during external code execution that calls setjmp on Windows Dec 18, 2015

@ianlancetaylor ianlancetaylor added this to the Go1.6Maybe milestone Dec 18, 2015

@minux

This comment has been minimized.

Member

minux commented Dec 20, 2015

I assumed the OP meant this code:

package main
/*
#include <stdio.h>
#include <setjmp.h>
void Print() {
jmp_buf c;
if (setjmp(c) == 0)
longjmp(c,1);
else
printf("receive an exception\n");
}
*/
import "C"
func main() {
C.Print()
}

will panic on windows.

I tried to reproduce it on windows/amd64, external linking works,
but internal linker fails.

C:\go\src>..\bin\go run -ldflags "-linkmode internal" x.go
Exception 0xc0000005 0x8 0xfffffffff9058d48 0xfffffffff9058d48
PC=0xfffffffff9058d48
signal arrived during external code execution

main._Cfunc_Print()
command-line-arguments/_obj/_cgo_gotypes.go:43 +0x38
main.main()
x.go:19 +0x1b

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
C:/go/src/runtime/asm_amd64.s:1998 +0x1

rax 0x459466
rbx 0xc082027f38
rcx 0x22fdd0
rdi 0xc082027f38
rsi 0x4c4c00
rbp 0x22fed0
rsp 0x22fda8
r8 0x45941a
r9 0xc082027ee0
r10 0x11
r11 0x0
r12 0x5
r13 0x4957a5
r14 0x1
r15 0x8
rip 0xfffffffff9058d48
rflags 0x10246
cs 0x33
fs 0x53
gs 0x2b
exit status 2

I'm not convinced that we need to fix internal linking as external linking
is the default now.

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Dec 21, 2015

If I'm reading this right, the problem is that cmd/link is not generating the correct exception information for the Windows OS. Is that correct?

@minux

This comment has been minimized.

Member

minux commented Dec 21, 2015

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Dec 21, 2015

It seems pretty clear from the error message that some exception is occurring.

This MSDN page implies that longjmp is exception aware: https://msdn.microsoft.com/en-us/library/yz2ez4as.aspx .

@minux

This comment has been minimized.

Member

minux commented Dec 21, 2015

@rsc

This comment has been minimized.

Contributor

rsc commented Jan 14, 2016

cmd/link/internal/amd64/asm.go seems to want to reject this case:

case 256 + ld.R_X86_64_PC32:
    if targ.Type == obj.SDYNIMPORT {
        ld.Diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name)
    }
    if targ.Type == 0 || targ.Type == obj.SXREF {
        ld.Diag("unknown symbol %s in pcrel", targ.Name)
    }
    r.Type = obj.R_PCREL
    r.Add += 4
    return

But this only applies to the symbols that the linker knows are being imported. I believe here the generated .o file has inside it a reference to __imp_longjmp, and the linker knows nothing about that, and in particular does not have it marked as SDYNIMPORT, so no error is produced.

I don't believe the linker is set up to handle this case. It's not just a trivial fix. Given that, we should probably leave this for Go 1.7.

@rsc rsc modified the milestones: Go1.7Early, Go1.6Maybe Jan 14, 2016

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Jan 21, 2016

I did some debuging here:

diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go
index 8439c06..d8788c9 100644
--- a/src/cmd/link/internal/ld/ldpe.go
+++ b/src/cmd/link/internal/ld/ldpe.go
@@ -128,6 +128,8 @@ type PeObj struct {
 }

 func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
+   fmt.Printf("\nprocessing file %s\n\n", pn)
+   defer fmt.Printf("\ndone with %s\n", pn)
    if Debug['v'] != 0 {
        fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn)
    }
@@ -199,6 +201,7 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
    // read symbols
    peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols)

+   fmt.Printf("\npe symbols for %v:\n", pn)
    peobj.npesym = uint(peobj.fh.NumberOfSymbols)
    obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0)
    for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 {
@@ -223,6 +226,9 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
        if numaux < 0 {
            numaux = 0
        }
+       if strings.Contains(pn, "issue13672.a") {
+           fmt.Printf("%d: %v\t\tsection=%d value=0x%x\n", i, peobj.pesym[i].name, peobj.pesym[i].sectnum, peobj.pesym[i].value)
+       }
    }

    // create symbols for mapped sections
@@ -287,6 +293,7 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
            continue
        }

+       fmt.Printf("\npe relocations for section %v:\n", rsect.name)
        r = make([]Reloc, rsect.sh.NumberOfRelocations)
        obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0)
        for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ {
@@ -297,6 +304,10 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
            rva := Le32(symbuf[0:])
            symindex := Le32(symbuf[4:])
            type_ := Le16(symbuf[8:])
+           if rsect.name == ".text" && strings.Contains(pn, "issue13672.a") {
+               fmt.Printf("%d: type=%v rva=0x%x symindex=%v\n", j, type_, rva, symindex)
+           }
+
            if err = readpesym(peobj, int(symindex), &sym); err != nil {
                goto bad
            }
@@ -348,6 +359,13 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) {
        s = rsect.sym
        s.R = r
        s.R = s.R[:rsect.sh.NumberOfRelocations]
+
+       if rsect.name == ".text" && strings.Contains(pn, "issue13672.a") {
+           fmt.Printf("\ngo relocations for section %v:\n", s.Name)
+           for i, r := range s.R {
+               fmt.Printf("%d: Off=0x%x Sym(addr=%p name=%s type=%d dynimplib=%s extname=%s value=0x%x size=0x%x)\n", i, r.Off, r.Sym, r.Sym.Name, r.Sym.Type, r.Sym.Dynimplib, r.Sym.Extname, r.Sym.Value, r.Sym.Size)
+           }
+       }
    }

    // enter sub-symbols into symbol table.

and I get this output

C:\dev\src\issues\issue13672>go build -ldflags="-linkmode=internal"
# issues/issue13672

processing file $WORK\issues\issue13672.a(_all.o)


pe symbols for $WORK\issues\issue13672.a(_all.o):
0: .file        section=65534 value=0x12
2: .text        section=1 value=0x0
4: .data        section=3 value=0x0
6: .bss     section=8 value=0x0
8: .debug_info      section=10 value=0x0
10: .debug_abbrev       section=11 value=0x0
12: .debug_aranges      section=9 value=0x0
14: .debug_line     section=12 value=0x0
16: .rdata$zzz      section=5 value=0x20
18: .file       section=65534 value=0x31
20: Print       section=1 value=0x0
22: _cgo_7d1619ce4bb5_Cfunc_Print       section=1 value=0x50
23: .text       section=1 value=0x0
25: .data       section=3 value=0x0
27: .bss        section=8 value=0x0
29: .rdata      section=4 value=0x0
31: .xdata      section=7 value=0x0
33: .pdata      section=6 value=0x0
35: .debug_info     section=10 value=0x18f
37: .debug_abbrev       section=11 value=0x19
39: .debug_loc      section=15 value=0x0
41: .debug_aranges      section=9 value=0x20
43: .debug_line     section=12 value=0x1d
45: .rdata$zzz      section=5 value=0x0
47: .debug_frame        section=13 value=0x0
49: .file       section=65534 value=0x44
51: mingw_getsp     section=1 value=0x60
53: longjmp     section=1 value=0x66
54: .debug_info     section=10 value=0x4e5
56: .debug_abbrev       section=11 value=0x16c
58: .debug_line     section=12 value=0xc8
60: .text       section=1 value=0x60
62: .data       section=3 value=0x0
64: .bss        section=8 value=0x0
66: .debug_aranges      section=9 value=0x50
68: __imp_longjmp       section=0 value=0x0
69: puts        section=0 value=0x0
70: _setjmp     section=0 value=0x0

pe relocations for section .text:
0: type=4 rva=0x16 symindex=70
1: type=4 rva=0x21 symindex=29
2: type=4 rva=0x26 symindex=69
3: type=4 rva=0x41 symindex=53
4: type=4 rva=0x69 symindex=68

go relocations for section main(.text):
0: Off=0x16 Sym(addr=0xc0820697a0 name=_setjmp type=41 dynimplib=msvcrt.dll extname=_setjmp value=0x0 size=0x0)
1: Off=0x21 Sym(addr=0xc082f190e0 name=main(.rdata) type=8 dynimplib= extname=main(.rdata) value=0x0 size=0x20)
2: Off=0x26 Sym(addr=0xc0820705a0 name=puts type=41 dynimplib=msvcrt.dll extname=puts value=0x0 size=0x0)
3: Off=0x41 Sym(addr=0xc082070240 name=longjmp type=41 dynimplib=msvcrt.dll extname=longjmp value=0x0 size=0x0)
4: Off=0x69 Sym(addr=0xc082070240 name=longjmp type=41 dynimplib=msvcrt.dll extname=longjmp value=0x0 size=0x0)

pe relocations for section .pdata:

done with $WORK\issues\issue13672.a(_all.o)

processing file C:\dev\go/pkg/windows_amd64/runtime/cgo.a(_all.o)


pe symbols for C:\dev\go/pkg/windows_amd64/runtime/cgo.a(_all.o):

pe relocations for section .text:

pe relocations for section .pdata:

done with C:\dev\go/pkg/windows_amd64/runtime/cgo.a(_all.o)

Note that _all.o .text section has two interesting pe relocations: 3 and 4. 3 has symindex=53 which points to symbol

53: longjmp     section=1 value=0x66

that points to small longjmp function in .text. While 4 has symindex=68 which points to symbol

68: __imp_longjmp       section=0 value=0x0

which is just a place for external function (longjmp that gcc hoping to find in msvcrt.dll).

You can also see those relocations converted into "go relocations". But "go relocatoins" 3 and 4 points to the same symbol. And this symbol settings match neither original longjmp nor __imp_longjmp settings. So I am not surprised that linker generates invalid code here.

I think this is all to do with symbol renaming we do in readpesym.

Alex

@actgardner

This comment has been minimized.

actgardner commented Feb 20, 2016

I'm experiencing the same issue with the external linker - I was able to reproduce the issue as redstorm described it: when Print() is defined in an external C file, the longjmp() call results in a stack trace.

It's a bit tough to follow here: are there any leads on why externally linked binaries would still have this issue?

@actgardner

This comment has been minimized.

actgardner commented Feb 22, 2016

@redstorm-fyy If this is still impacting you, I was able to work around it by using __builtin_setjmp and __builtin_longjmp. They aren't affected by this issue. This appears to be a MinGW bug on 64-bit platforms: https://sourceforge.net/p/mingw-w64/bugs/406/

@bradfitz

This comment has been minimized.

Member

bradfitz commented Jul 20, 2017

Is there still a problem here? @redstorm-fyy, @alexbrainman ?

@BorisKozo

This comment has been minimized.

BorisKozo commented Jul 20, 2017

I just got this error, google brought me here, saw you modified the milestone an hour ago :(

@bradfitz

This comment has been minimized.

Member

bradfitz commented Jul 20, 2017

@BorisKozo please provide details then. That would help.

@BorisKozo

This comment has been minimized.

BorisKozo commented Jul 20, 2017

I am on
go version go1.8.1 windows/amd64
I am writing a simple unit test against sqlite3 driver compiled in CGo.
My unit test opens a DB, and closes it (does nothing on the DB)
I am building with the flags -i -ldflags="-linkmode internal" via Gogland (JetBrains) because otherwise the debugger fails with another error.

When I call db.Close() (I verified there is an open connection) I get the following:

Exception 0xc0000005 0x8 0xaa92f8 0xaa92f8
PC=0xaa92f8
signal arrived during external code execution

github.com/mattn/go-sqlite3._Cfunc_sqlite3_close_v2(0x10f7018, 0x0)
	github.com/mattn/go-sqlite3/_obj/_cgo_gotypes.go:495 +0x50
github.com/mattn/go-sqlite3.(*SQLiteConn).Close.func1(0x10f7018, 0x0)
	C:/projects/Phoenix/go/src/github.com/mattn/go-sqlite3/sqlite3.go:759 +0x67
github.com/mattn/go-sqlite3.(*SQLiteConn).Close(0xc04215c960, 0x69e53e, 0xc04219dce8)
	C:/projects/Phoenix/go/src/github.com/mattn/go-sqlite3/sqlite3.go:759 +0x3a
database/sql.(*driverConn).finalClose.func2()
	C:/Go/src/database/sql/sql.go:440 +0x50
database/sql.withLock(0xaad500, 0xc04216e3f0, 0xc04219dd60)
	C:/Go/src/database/sql/sql.go:2545 +0x6c
database/sql.(*driverConn).finalClose(0xc04216e3f0, 0xc04219de78, 0x4219de70)
	C:/Go/src/database/sql/sql.go:442 +0x160
database/sql.(finalCloser).(database/sql.finalClose)-fm(0xc04216a348, 0xc0420d0cf0)
	C:/Go/src/database/sql/sql.go:537 +0x36
database/sql.(*DB).Close(0xc04216a320, 0xc04215e000, 0xc04219df00)
	C:/Go/src/database/sql/sql.go:644 +0x304
<project name>/DataMonitor.(*Database).Finalize(0xc04219df58, 0x0, 0x0)
	<project name>/db_wrapper.go:78 +0x36
<project name>/DataMonitor.TestDatabase_Initialize(0xc0421589c0)
	<project name>/db_wrapper_test.go:26 +0x1d0
testing.tRunner(0xc0421589c0, 0x8e4858)
	C:/Go/src/testing/testing.go:657 +0x9d
created by testing.(*T).Run
	C:/Go/src/testing/testing.go:697 +0x2d1

@bradfitz

This comment has been minimized.

Member

bradfitz commented Jul 20, 2017

@aclements

This comment has been minimized.

Member

aclements commented Jul 20, 2017

@BorisKozo, it's not obvious to me that your crash involves setjmp/longjmp. Do you have evidence that it does? Basically anything going wrong in C code will cause "signal arrived during external code execution", and the exception code you got (0xc0000005, access violation) is different from the original report (0xc0000028, bad stack). Based on the exception code you got, it looks like the SQLite3 C code attempted to jump to a bad program counter (0xaa92f8).

@BorisKozo

This comment has been minimized.

BorisKozo commented Jul 20, 2017

@aclements No evidence whatsoever. This is the first time I am doing something with CGo so I am not sure how to read those error messages or what they mean.

@redstorm-fyy

This comment has been minimized.

redstorm-fyy commented Aug 4, 2017

@bradfitz
This problem is still exist in go 1.8 windows/amd64
To solve the problem,I move the c code to the .go file.
The following code is ok.
As @alanctgardner said,this is the external linker's bug.

package main
/*
#include <stdio.h>
#include <setjmp.h>
void Print(){
printf("begin\n");
jmp_buf c;
if(setjmp(c)==0){
longjmp(c,1);
}
else{
printf("receive an exception\n");
}
printf("end\n");
}
*/
import "C"

func main(){
C.Print()
}

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Aug 4, 2017

Is there still a problem here? @redstorm-fyy, @alexbrainman ?

Nothing changed since #13672 (comment)

This works with external linker, but broken with internal linker.

c:\Users\Alex\dev\src\issue\go\13672>go version
go version devel +16422078ab Thu Aug 3 04:24:28 2017 +0000 windows/amd64

c:\Users\Alex\dev\src\issue\go\13672>gcc --version
gcc (GCC) 4.9.1
Copyright (C) 2014 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.


c:\Users\Alex\dev\src\issue\go\13672>type main.go
package main

/*
#include <stdio.h>
#include <setjmp.h>
void Print() {
jmp_buf c;
if (setjmp(c) == 0)
longjmp(c,1);
else
printf("receive an exception\n");
}
*/
import "C"

func main() {
        C.Print()
}

c:\Users\Alex\dev\src\issue\go\13672>go run main.go
receive an exception

c:\Users\Alex\dev\src\issue\go\13672>go run -ldflags "-linkmode internal" main.go
Exception 0xc0000028 0x3100000200 0xff 0x7ff965564b90
PC=0x7ff965564b90
signal arrived during external code execution

main._Cfunc_Print()
        command-line-arguments/_obj/_cgo_gotypes.go:41 +0x48
main.main()
        c:/Users/Alex/dev/src/issue/go/13672/main.go:17 +0x27
rax     0x0
rbx     0xc0000028
rcx     0x0
rdi     0x7ff962dc61dc
rsi     0x6df320
rbp     0x6decb0
rsp     0x6debb0
r8      0x0
r9      0x0
r10     0x0
r11     0x76b8b2da85fb
r12     0x7ff962d40000
r13     0x7ff962d52774
r14     0x6dff48
r15     0x14
rip     0x7ff965564b90
rflags  0x202
cs      0x33
fs      0x53
gs      0x2b
exit status 2

c:\Users\Alex\dev\src\issue\go\13672>

I doubt many people use internal linker nowadays.
But I will try to investigate this again when I have time.

Alex

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Aug 4, 2017

Reopening because the problem still exists.

@ianlancetaylor ianlancetaylor reopened this Aug 4, 2017

@rsc rsc modified the milestones: Go1.10, Go1.11 Nov 22, 2017

@spieglt

This comment has been minimized.

spieglt commented Dec 7, 2017

Would this also be happening when using a DLL built with Visual Studio via the Syscall package? I believe I'm having similar symptoms.

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Dec 7, 2017

Would this also be happening when using a DLL built with Visual Studio via the Syscall package?

The problem discussed in this issue can only happens with Go program that uses Cgo. If your program does not uses Cgo, it cannot have this problem.

Alex

@deadprogram

This comment has been minimized.

deadprogram commented Jan 12, 2018

Here is an example of what appears to be this same issue happening in a recent PR to GoCV: https://ci.appveyor.com/project/deadprogram/gocv/build/216#L141

This code works as expected on Linux, but fails same test on Windows using Go 1.9.2, seemingly for attempting to catch the exception:
https://github.com/hybridgroup/gocv/pull/55/files#diff-32d3fe6df51960dca7c273d37844190fR24

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Jan 12, 2018

@deadprogram I suspect that that problem you mention is actually #12516. Doesn't help much as that one is not fixed either.

@deadprogram

This comment has been minimized.

deadprogram commented Jan 12, 2018

Hi @ianlancetaylor thanks for the tip. I will try out and exception handler that does not allocate any new memory and see if that helps.

@Akito13

This comment has been minimized.

Akito13 commented Feb 16, 2018

2 years and still no fix?

@mattn

This comment has been minimized.

Member

mattn commented Feb 16, 2018

I tested this on Windows10 64bit.

setjmp

Does not reproduce.

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Feb 16, 2018

@mattn As far as I know the bug only occurs when you explicitly use the internal linker via -ldflags=-linkmode=internal.

@Akito13 Why do you want to use the internal linker for code like this?

@mattn

This comment has been minimized.

Member

mattn commented Feb 17, 2018

Sorry, I could reproduce it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment