Skip to content
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

runtime: .NET / Golang Interop - Go catching .NET exceptions #30417

Closed
kfreezen opened this issue Feb 26, 2019 · 4 comments
Closed

runtime: .NET / Golang Interop - Go catching .NET exceptions #30417

kfreezen opened this issue Feb 26, 2019 · 4 comments

Comments

@kfreezen
Copy link

kfreezen commented Feb 26, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12 windows/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Kent\AppData\Local\go-build
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=C:\Users\Kent\go
set GOPROXY=
set GORACE=
set GOROOT=C:\Go
set GOTMPDIR=
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\Kent\AppData\Local\Temp\go-build993497478=/tmp/go-build -gno-record-gcc-switches

What did you do?

Ran a C# console application that interacts with a CGO DLL. The code of the .NET program is below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SampleGoInterop
{
    class Program
    {
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void CallbackDelegate(long handle);

        [DllImport("sample-dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        public static extern void RunCallback();

        [DllImport("sample-dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        public static extern void SetCallback(CallbackDelegate func);

        static void Callback(long handle)
        {
            try
            {
                Console.WriteLine("Callback() is called");

                int i = 0;

                i /= 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine("We've recovered!");
            }
        }

        static void Main(string[] args)
        {
            SetCallback(new CallbackDelegate(Callback));

            RunCallback();
        }
    }
}

SampleGoInterop.zip

What did you expect to see?

Callback() is called
We've recovered!

What did you see instead?

Callback() is called
Exception 0xc0000094 0x0 0x0 0x7fff54d811f0
PC=0x7fff54d811f0
signal arrived during external code execution

main._Cfunc_FireCallback(0x267d37e08cc, 0x13371337deadbeef)
        _cgo_gotypes.go:43 +0x4c
main.RunCallback.func1()
        E:/code/sample-dll/main.go:31 +0x73
main.RunCallback()
        E:/code/sample-dll/main.go:31 +0x3a
main._cgoexpwrap_ef0ddd7f92d8_RunCallback()
        _cgo_gotypes.go:74 +0x27
rax     0x0
rbx     0xc00003fe48
rcx     0x7ffef5780825
rdi     0x91e85be4e0
rsi     0x13371337deadbeef
rbp     0x91e85be4f0
rsp     0x91e85be4a0
r8      0x267bae45bec
r9      0x0
r10     0x91e85be448
r11     0x267b94be840
r12     0x267b94be840
r13     0x91e85be9c0
r14     0x91e85bea38
r15     0x4
rip     0x7fff54d811f0
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b

I know this is a really rare case, but is there any way we can make .NET exception handling work without having to make local changes to the Golang runtime?

Local changes are in signal_windows.go -- func lastcontinuehandler()

if testingWER {
    return _EXCEPTION_CONTINUE_SEARCH
}

return _EXCEPTION_CONTINUE_SEARCH

// ... rest of function ...
@agnivade
Copy link
Contributor

/cc @ianlancetaylor

Btw, did you intentionally change the value of rsi ?

@ianlancetaylor ianlancetaylor changed the title .NET / Golang Interop - Go catching .NET exceptions runtime: .NET / Golang Interop - Go catching .NET exceptions Feb 27, 2019
@ianlancetaylor
Copy link
Contributor

The last time I looked into this, which was three years ago, I couldn't get anything to work. See #12516. It ought to be possible, but I didn't have the patience to figure it out.

We should probably close this as a dup of #12516, unless anybody sees a relevant difference.

@kfreezen
Copy link
Author

@agnivade, no I did not. RunCallback() in the go portion of the code passes 0x13371337deadbeef as the handle. I hadn't noticed that value in rsi, which makes me wonder: What in the world is the right calling convention to use in my P/Invoke statements? I haven't noticed any differences between cdecl and stdcall.

@ianlancetaylor Yes this issue looks like a dup to me. The environment's a bit different, but it's still the same issue.

What would an acceptable fix be? I've thought about adding a simple flag that people using Go in a DLL can set to disable Go panics on exceptions like these, but it probably isn't the right fix.

@ianlancetaylor
Copy link
Contributor

I think the right fix is for Go's exception handler to pass the exception along to C code, rather than panic. It tries to do that today but apparently it doesn't work.

Closing this issue as a dup.

@golang golang locked and limited conversation to collaborators Feb 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants