mixed C / Go debugging

1,469 views
Skip to first unread message

Alain Mellan

unread,
Jun 6, 2017, 8:41:01 PM6/6/17
to golang-nuts
Hi,

I need to have a Go application wrapped in a DLL with a thin C layer so that I can load the .dll (I'm on Windows) from a C/C++ application. What are my options for debugging the Go code?

When I load my app from gdb, it seems the Go code is a black box, and dlv does not seem to be able to attach properly to the process that is running app + go dll.

Am I missing something? Any suggestions?

-- alain.

brainman

unread,
Jun 6, 2017, 10:05:00 PM6/6/17
to golang-nuts
It would be useful if you provide complete instructions of what you did, so we could at least be able to reproduce it here. Including complete source code and description of environment and tools you use. Thank you.

Alex

Alain Mellan

unread,
Jun 7, 2017, 12:57:14 PM6/7/17
to golang-nuts
Sure, this is what I did. I'm working on Windows with MinGW.

sayhello.go:

// package name: hello
package main

import "C"
import "fmt"

//export SayHello
func SayHello() {
fmt.Printf("Hello!\n")
fmt.Scanln()
fmt.Printf("Goodbye\n")
}

func main() {
// We need the main function to make possible
// CGO compiler to compile the package as C shared library
}

Then hello.c:

#include "hello.h"
#include <stdio.h>

void __stdcall __declspec(dllexport) hello()
{
   SayHello();
}

int main() {
  printf("Hello from C\n");
  SayHello();
  return 0;
}

I build with the following commands:

$ go build -gcflags "-N -l" -buildmode=c-archive -o hello.a sayhello.go
$ gcc -o hello.dll -shared hello.c hello.a -lWinMM -lntdll -lWS2_32

Then I have a dload.cpp which will load the dll and call hello() (most code scraped from the Internet)

#include <windows.h>
#include <iostream>

typedef void (__stdcall *f_funci)();

int main()
{
   HINSTANCE hGetProcIDDLL = LoadLibrary("hello.dll");

   if (!hGetProcIDDLL) {
      std::cout << "could not load the dynamic library" << std::endl;
      return EXIT_FAILURE;
   }

  //  resolve function address here
  f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "hello");
  if (!funci) {
    std::cout << "could not locate the function" << std::endl;
    return EXIT_FAILURE;
  }

  funci();
  // std::cout << "sayhello() returned " << << std::endl;

  return EXIT_SUCCESS;
}

I build it with g++ -o dload dload.cpp, and it runs properly: load the .dll, run hello() that says "Hello", waits for the user to hit Enter, and says "Goodbye".

Now, if I debug with gdb:

(gdb) break main
Breakpoint 1 at 0x4015bd
(gdb) run
Starting program: C:\Users\amellan\src\ngm\examples\sharedlib\dload.exe
[New Thread 17956.0x2454]
[New Thread 17956.0x2be0]
[New Thread 17956.0x3ec0]
[New Thread 17956.0x4420]

Thread 1 hit Breakpoint 1, 0x00000000004015bd in main ()
(gdb) s
Single stepping until exit from function main,
which has no line number information.
[New Thread 17956.0x1c1c]
[New Thread 17956.0x1480]
[New Thread 17956.0x41d0]
[New Thread 17956.0x46a0]
[New Thread 17956.0x3074]
Hello!

Goodbye
__tmainCRTStartup ()
    at C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:336
336     C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c: No such file or directory.
(gdb)

I hit "Enter" to continue through the Go code and get "Goodbye".

Trying to attach with dlv gives:

$ dlv attach 18336
could not get Go symbols no runtime.pclntab symbol found

Which kind of makes sense, since this is not a pure Go executable.

I don't really care about debugging the C code, which will be just a shim layer, I would like to debug Go. What am I missing?

-- alain.

brainman

unread,
Jun 11, 2017, 11:27:21 PM6/11/17
to golang-nuts
Thank you very much for instructions. I could reproduce what you see.

Unfortunately I do not have much to suggest.

dlv expects executable to be written in Go. dlv searches executable for particular symbols (among many other things). That is why you see "could not get Go symbols no runtime.pclntab symbol found" message.

So that leaves you with gdb. If you add -g flag to your both gcc and g++ commands, that will include dwarf info. Then you can use gdb:

c:\Users\Alex\dev\src\alain_mellan>gdb dload.exe
GNU gdb (GDB) 7.7.50.20140303-cvs
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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".

This binary was built by Equation Solution <http://www.Equation.com>...
Reading symbols from dload.exe...done.
(gdb) b dload.cpp:22
Breakpoint 1 at 0x40159c: file dload.cpp, line 22.
(gdb) r
Starting program: c:\Users\Alex\dev\src\alain_mellan\dload.exe
[New Thread 4020.0x868]

Breakpoint 1, main () at dload.cpp:22
22 funci();
(gdb) br 'main.SayHello'
Breakpoint 2 at 0x6249e050: file c:/Users/Alex/dev/src/alain_mellan/sayhello.go, line 8.
(gdb) c
Continuing.
[New Thread 4020.0x7bc]
[New Thread 4020.0xee8]
[New Thread 4020.0x7dc]
[New Thread 4020.0xd04]

Breakpoint 2, main.SayHello () at c:/Users/Alex/dev/src/alain_mellan/sayhello.go:8
8 func SayHello() {
(gdb) l
3
4 import "C"
5 import "fmt"
6
7 //export SayHello
8 func SayHello() {
9 fmt.Printf("Hello!\n")
10 fmt.Scanln()
11 fmt.Printf("Goodbye\n")
12 }
(gdb) bt
#0 main.SayHello () at c:/Users/Alex/dev/src/alain_mellan/sayhello.go:8
#1 0x000000006249e037 in main._cgoexpwrap_961c5413714f_SayHello ()
at command-line-arguments/_obj/_cgo_gotypes.go:45
#2 0x000000006244f6f2 in runtime.call32 () at c:/users/alex/dev/go/src/runtime/asm_amd64.s:509
#3 0x000000006240320a in runtime.cgocallbackg1 (ctxt=0)
at c:/users/alex/dev/go/src/runtime/cgocall.go:305
#4 0x0000000062402fba in runtime.cgocallbackg (ctxt=0)
at c:/users/alex/dev/go/src/runtime/cgocall.go:187
#5 0x0000000062450d4f in runtime.cgocallback_gofunc ()
at c:/users/alex/dev/go/src/runtime/asm_amd64.s:762
#6 0x0000000062451ab1 in runtime.goexit () at c:/users/alex/dev/go/src/runtime/asm_amd64.s:2337
#7 0x0000000000000000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)

I doubt whether gdb is very useful.

Maybe create an issue at https://github.com/derekparker/delve/issues. Maybe Delve Team might have suggestions.

Sorry it took me a while to get back to you.

Alex

Alain Mellan

unread,
Jun 12, 2017, 1:38:46 PM6/12/17
to golang-nuts
Hi Alex,

Thanks for spending some time on this!
Duh, I missed the gcc/g++ -g flag. It helps a little bit. I also tried to load the go extension for gdb with "source 
C:/utils/go/src/runtime/runtime-gdb.py", but it doesn't do much good, Go still executes as a black box. What you describe is exactly what I was afraid of :-)
It would be nice to have a dbg_break() call in Go that would wait for dlv to connect, irrespective of whether Go is embedded in C/C++ or not.

-- alain.

brainman

unread,
Jun 12, 2017, 8:40:30 PM6/12/17
to golang-nuts
> ... I also tried to load the go extension for gdb with "source C:/utils/go/src/runtime/runtime-gdb.py", but it doesn't do much good, 

Yes, I tried that recently. It didn't work for me either. From what I remember, you had to have "python support" with gdb, and that, apparently, is hard. I didn't have real need for runtime-gdb.py, so I gave up.

> It would be nice to have a dbg_break() call in Go that would wait for dlv to connect, irrespective of whether Go is embedded in C/C++ or not.

I do not know what you are proposing. But, sure, there are other ways to debug code. For example, I use fmt.Printf or println.

Alex

Alain Mellan

unread,
Jun 13, 2017, 12:49:29 PM6/13/17
to golang-nuts
Haha, I agree, Printf is my debugging tool of choice, but some in my team like debuggers ...
Reply all
Reply to author
Forward
0 new messages