How to use golang c-shared library in go - go

I wrote c-shared library using go.
package main
import "C"
import "log"
//export RunLib
func RunLib() {
log.Println("Call RunLib")
}
func init() {
log.Println("Call init")
}
func main() {
log.Println("Call main")
}
I created the library using this command:
go build -buildmode=c-shared -o lib.so lib.go
To use the library, I wrote this golang code.
package main
/*
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
static void callFromLib() {
void (*fn)();
void *h = dlopen("lib.so", RTLD_LAZY);
if (!h) {
fprintf(stderr, "Error: %s\n", dlerror());
return;
}
*(void**)(&fn) = dlsym(h, "RunLib");
if (!fn) {
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(h);
return;
}
fn();
dlclose(h);
}
*/
import "C"
func main() {
C.callFromLib()
}
If I run the last code it throws this error (go run call.go):
fatal error: bad sweepgen in refill
goroutine 1 [running, locked to thread]:
runtime.throw({0xb990782, 0xc000042a38})
/usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000042a18 sp=0xc0000429e8 pc=0xb930691
runtime.(*mcache).refill(0x41215b8, 0x2)
/usr/local/go/src/runtime/mcache.go:156 +0x24e fp=0xc000042a68 sp=0xc000042a18 pc=0xb91434e
runtime.(*mcache).nextFree(0x41215b8, 0x2)
/usr/local/go/src/runtime/malloc.go:880 +0x85 fp=0xc000042ab0 sp=0xc000042a68 pc=0xb90ba85
runtime.mallocgc(0x8, 0xb9ba300, 0x1)
/usr/local/go/src/runtime/malloc.go:1071 +0x4e8 fp=0xc000042b30 sp=0xc000042ab0 pc=0xb90c108
runtime.growslice(0xb9ba300, {0x0, 0x41a1910, 0x2}, 0xc00009c000)
/usr/local/go/src/runtime/slice.go:267 +0x4ea fp=0xc000042b98 sp=0xc000042b30 pc=0xb94586a
sync.(*Pool).pinSlow(0xba394e0)
/usr/local/go/src/sync/pool.go:223 +0x105 fp=0xc000042c30 sp=0xc000042b98 pc=0xb960ec5
sync.(*Pool).pin(0xba394e0)
/usr/local/go/src/sync/pool.go:206 +0x4e fp=0xc000042c48 sp=0xc000042c30 pc=0xb960d8e
sync.(*Pool).Get(0xba394e0)
/usr/local/go/src/sync/pool.go:128 +0x25 fp=0xc000042c80 sp=0xc000042c48 pc=0xb960ac5
fmt.newPrinter()
/usr/local/go/src/fmt/print.go:137 +0x25 fp=0xc000042ca8 sp=0xc000042c80 pc=0xb985d45
fmt.Sprintln({0xc000042d38, 0x1, 0x1})
/usr/local/go/src/fmt/print.go:280 +0x28 fp=0xc000042cf0 sp=0xc000042ca8 pc=0xb986008
log.Println({0xc000042d38, 0x24, 0x0})
/usr/local/go/src/log/log.go:329 +0x1e fp=0xc000042d20 sp=0xc000042cf0 pc=0xb98cd5e
main.RunLib(...)
/Users/.../demo/lib.go:8
_cgoexp_6b951f94a90e_RunLib(0xc000042d90)
_cgo_gotypes.go:36 +0x45 fp=0xc000042d58 sp=0xc000042d20 pc=0xb98cf85
runtime.cgocallbackg1(0xb98cf40, 0xc000042e60, 0x0)
/usr/local/go/src/runtime/cgocall.go:306 +0x29a fp=0xc000042e28 sp=0xc000042d58 pc=0xb903d1a
runtime.cgocallbackg(0xc0000001a0, 0x300000002, 0xc0000001a0)
/usr/local/go/src/runtime/cgocall.go:232 +0x109 fp=0xc000042eb8 sp=0xc000042e28 pc=0xb9039e9
runtime.cgocallbackg(0xb98cf40, 0x7ffeefbff737, 0x0)
<autogenerated>:1 +0x2f fp=0xc000042ee0 sp=0xc000042eb8 pc=0xb95e32f
runtime: unexpected return pc for runtime.cgocallback called from 0x4053e00
stack: frame={sp:0xc000042ee0, fp:0xc000042f08} stack=[0xc000042000,0xc000043000)
0x000000c000042de0: 0x000000c000042d9d 0x000000c000042e18
0x000000c000042df0: 0x000000000b95843b <runtime.exitsyscall+0x00000000000000fb> 0x000000c0000001a0
0x000000c000042e00: 0x000000c000042dd8 0x0000000000000000
0x000000c000042e10: 0x000000000b9c3dc0 0x000000c000042ea8
0x000000c000042e20: 0x000000000b9039e9 <runtime.cgocallbackg+0x0000000000000109> 0x000000000b98cf40 <_cgoexp_6b951f94a90e_RunLib+0x0000000000000000>
...
<_cgoexp_6b951f94a90e_RunLib+0x0000000000000000>
0x000000c000042ea0: 0x00007ffeefbff737 0x000000c000042ed0
0x000000c000042eb0: 0x000000000b95e32f <runtime.cgocallbackg+0x000000000000002f> 0x000000c0000001a0
...
<runtime.cgocallback+0x00000000000000b4>
0x000000c000042ee0: <0x000000000b98cf40 <_cgoexp_6b951f94a90e_RunLib+0x0000000000000000> 0x00007ffeefbff737
...
runtime.cgocallback(0x4004165, 0x4058340, 0xc000042f70)
/usr/local/go/src/runtime/asm_amd64.s:915 +0xb4 fp=0xc000042f08 sp=0xc000042ee0 pc=0xb95c134
goroutine 1 [runnable, locked to thread]:
unicode.init()
/usr/local/go/src/unicode/tables.go:9 +0x79
exit status 2
But if I use python everything works correctly!
>>> import ctypes
>>> lib = ctypes.cdll.LoadLibrary("lib.so")
>>> 2022/03/02 01:08:17 Call init
>>> lib.RunLib()
2022/03/02 01:08:22 Call RunLib
0
>>>
Informations
OS: macOs Big Sur 11.6.3 (20G415)
>>> clang --version
Apple clang version 13.0.0 (clang-1300.0.29.30)
>>> nm lib.so| grep RunLib
000000000008cfa0 T _RunLib
000000000008cf40 t __cgoexp_6b951f94a90e_RunLib
00000000000c4200 s __cgoexp_6b951f94a90e_RunLib.stkobj
>>> go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="~/Library/Caches/go-build"
GOENV="~/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="~/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="~/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.17"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="~/projects/go/.../go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/wl/9rtfdx8x7jvgyn6t8hpq7yh00000gn/T/go-build401121298=/tmp/go-build -gno-record-gcc-switches -fno-common"
I googled and found this article. But there is no golang in the article.
https://medium.com/learning-the-go-programming-language/calling-go-functions-from-other-languages-4c7d8bcc69bf

The problem described by the original poster looks weird. I could not reproduce it on Linux.
My setup: Ubuntu 18, Go 1.19.2, gcc 7.5.0
I made only one change: added pragma #cgo LDFLAGS: -ldl to the C snippet. It is required by GCC to enable dlopen & Co.
package main
/*
#cgo LDFLAGS: -ldl
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
static void callFromLib() {
void (*fn)();
void *h = dlopen("lib.so", RTLD_LAZY);
if (!h) {
fprintf(stderr, "Error: %s\n", dlerror());
return;
}
*(void**)(&fn) = dlsym(h, "RunLib");
if (!fn) {
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(h);
return;
}
fn();
dlclose(h);
}
*/
import "C"
func main() {
C.callFromLib()
}
Launched as:
LD_LIBRARY_PATH=/home/user/tmp/try-go/ go run call.go
LD_LIBRARY_PATH variable points to the directory with lib.so
Output:
2022/10/26 09:39:10 Call init
2022/10/26 09:39:10 Call RunLib
As expected, no errors.
The original error bad sweepgen in refill is raised by the garbage collector. The cache of objects became incoherent. I suspect the issue is that there are two runtimes and two garbage collectors in the program - one comes from the calling program and the other is from the loaded lib.so. It looks like two garbage collectors clashed while running over the same heap.
Anyway, in go 1.19 the code works. Still I wouldn't load Go shared object through C dlopen - who knows how two Go runtimes could interact, especially if they are from different compilers versions.

A Go contributor writes that it is not possible to have two Go runtimes in one image. The comment refers to Windows, but seems to apply to macOS as well, since I can reliably reproduce the crash mentioned by the OP:
If I am understanding correctly, you are using a Go program to open a C++ DLL and the C++ DLL opens a Go DLL. I'm sorry, this won't work on Windows. There can only be one copy of the Go runtime in the program image on Windows, but you are trying to have two.
see https://github.com/golang/go/issues/50304#issuecomment-999302888
However since the OP uses macOS there is an alternative: the use of plugins, see https://pkg.go.dev/plugin:
Package plugin implements loading and symbol resolution of Go plugins.
The command to create the library would change as follows:
go build -buildmode=plugin -o lib.so lib.go
Accordingly, your call to the function defined in your library would then look something like this in Go:
package main
import (
"plugin"
)
func main() {
plug, err := plugin.Open("lib.so")
if err != nil {
panic(err)
}
runLib, err := plug.Lookup("RunLib")
if err != nil {
panic(err)
}
runLib.(func())()
}
A test with this program generates the following logging outputs:
stephan#mac golang-c-lib-in-go % go run call.go
2022/10/29 23:23:01 Call init
2022/10/29 23:23:01 Call RunLib
If the library is also to be used for calling from C and/or Python, then one possibility is to create the library in two variants: one that is called from C and/or Python, as shown in the question, and one in the plugin variant that can then be used by Go programs.
In your example, you don't need to make any changes in lib.go, the only difference is the command line to create the library.
Finally, an important note from the documentation:
Currently plugins are only supported on Linux, FreeBSD, and macOS.

I suggest this blog which discusses linking go programs with c libraries in detail.
Your code in the go program seems to be missing the compiler and linker instructions:
#cgo CFLAGS: -I./src
#cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib

Related

import c-shared library that generated by cython to go with cgo

I wanna import a c-shared-library to go that generated by Cython in python 3.7, try do it by cgo.
in this case:
go version go1.12.7 linux/amd64
Python 3.7.3
Cython version 0.29.12
os: Manjaro 18.0.4
Kernel: x86_64 Linux 5.1.19-1
I will continue:
make a python file vim pylib.pyx:
#!python
cdef public void hello():
print("hello world!")
and run python -m cython pylib.pyx for generate the c-shared-library, I have two files, pylib.c and pylib.h.
now, try import these to golang, so make a go file vim test.go:
package main
/*
#include </usr/include/python3.7m/Python.h>
#include "pylib.h"
*/
import "C"
import "fmt"
func main() {
C.hello()
fmt.Println("done")
}
finaly, I run go run test.go:
I have the following output:
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_51159acd5c8e_Cfunc_hello':
/tmp/go-build/cgo-gcc-prolog:48: undefined reference to `hello'
collect2: error: ld returned 1 exit status
I try import it to c too but I encountered a similar output like this:
undefined reference to `hello'
ld returned 1 exit status
I don't know what to do, help me, please.
:(
I run go run test.go: I have the following output:
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_51159acd5c8e_Cfunc_hello':
/tmp/go-build/cgo-gcc-prolog:48: undefined reference to `hello'
collect2: error: ld returned 1 exit status
We can generate an equivalent error message with the following code.
package main
/*
#include <math.h>
*/
import "C"
import "fmt"
func main() {
cube2 := C.pow(2.0, 3.0)
fmt.Println(cube2)
}
Output:
$ go run cube2.go
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_f6c6fa139eda_Cfunc_pow':
/tmp/go-build/cgo-gcc-prolog:53: undefined reference to `pow'
collect2: error: ld returned 1 exit status
$
In both cases, ld (the linker) can't find a C function after looking in the usual places: undefined reference to 'pow' or undefined reference to 'hello'.
Let's tell cgo where to find the C pow function in the C math library: m.
For cgo, using ld flags,
#cgo LDFLAGS: -lm
GCC: 3.14 Options for Linking
-llibrary
Search the library named library when linking.
Updating the previous code,
package main
/*
#cgo LDFLAGS: -lm
#include <math.h>
*/
import "C"
import "fmt"
func main() {
cube2 := C.pow(2.0, 3.0)
fmt.Println(cube2)
}
Output:
$ go run cube2.go
8
$
This illustrates a basic cgo principle: include a C header file for your C library and point to the location of the C library.
References:
Cgo and Python : Embedding CPython: a primer

CGO linking error: undefined reference to `MemoryFreeLibrary'

I am trying to wrap around a C library with Go. The library is built with CMake and produces a static library file. I added the #cgo stuff at the beginning of the .go file with the proper CFLAGS and LDFLAGS, however, I keep getting undefined reference errors when running go build memorymodule.go.
Here is my code:
package main
/*
#cgo CFLAGS: -IMemoryModule
#cgo LDFLAGS: MemoryModule/build/MemoryModule.a
#include "MemoryModule/MemoryModule.h"
*/
import "C"
import (
"fmt"
"io/ioutil"
"os"
"unsafe"
)
const SIZE int = 1024
func end(msg string) {
fmt.Println(msg)
os.Exit(1)
}
func check(err error, msg string) {
if err != nil {
end(msg)
}
}
func main() {
bin, err := ioutil.ReadFile(os.Args[0])
check(err, "error reading file")
// Convert the args passed to this program into a C array of C strings
var cArgs []*C.char
for _, goString := range os.Args {
cArgs = append(cArgs, C.CString(goString))
}
// Load the reconstructed binary from memory
handle := C.MemoryLoadLibraryEx(
unsafe.Pointer(&bin[0]), // void *data
(C.size_t)(len(bin)), // size_t
(*[0]byte)(C.MemoryDefaultAlloc), // Alloc func ptr
(*[0]byte)(C.MemoryDefaultFree), // Free func ptr
(*[0]byte)(C.MemoryDefaultLoadLibrary), // loadLibrary func ptr
(*[0]byte)(C.MemoryDefaultGetProcAddress), // getProcAddress func ptr
(*[0]byte)(C.MemoryDefaultFreeLibrary), // freeLibrary func ptr
unsafe.Pointer(&cArgs[0]), // void *userdata
)
// Execute binary
C.MemoryCallEntryPoint(handle)
// Cleanup
C.MemoryFreeLibrary(handle)
}
Here is the results from the go build ... with -x to help with debugging:
cd /home/wlaw/go-memory-module
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -o $WORK/command-line-arguments/_obj/_cgo_.o $WORK/command-line-arguments/_obj/_cgo_main.o $WORK/command-line-arguments/_obj/_cgo_export.o $WORK/command-line-arguments/_obj/memorymodule.cgo2.o -g -O2 MemoryModule/build/MemoryModule.a
# command-line-arguments
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x0): undefined reference to `MemoryDefaultLoadLibrary'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x4): undefined reference to `MemoryDefaultGetProcAddress'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x8): undefined reference to `MemoryDefaultFreeLibrary'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0xc): undefined reference to `MemoryDefaultFree'
/tmp/go-build590803163/command-line-arguments/_obj/_cgo_main.o:_cgo_main.c:(.data+0x10): undefined reference to `MemoryDefaultAlloc'
/tmp/go-build590803163/command-line-arguments/_obj/memorymodule.cgo2.o: In function `cgo_50ced23471ff_Cfunc_MemoryCallEntryPoint':
/tmp/go-build/command-line-arguments/_obj/cgo-gcc-prolog:40: undefined reference to `MemoryCallEntryPoint'
/tmp/go-build590803163/command-line-arguments/_obj/memorymodule.cgo2.o: In function `cgo_50ced23471ff_Cfunc_MemoryLoadLibraryEx':
/tmp/go-build/command-line-arguments/_obj/cgo-gcc-prolog:76: undefined reference to `MemoryLoadLibraryEx'
/tmp/go-build590803163/command-line-arguments/_obj/memorymodule.cgo2.o: In function `cgo_50ced23471ff_Cfunc_MemoryFreeLibrary':
/tmp/go-build/command-line-arguments/_obj/cgo-gcc-prolog:54: undefined reference to `MemoryFreeLibrary'
collect2: error: ld returned 1 exit status
Makefile:9: recipe for target 'all' failed
make: *** [all] Error 2
Whats interesting is if I compile the MemoryModule dependency using make instead of CMake (by editing my Makefile) such that it produces an object file instead of a static library file, and modify my source code to link with that instead, I get no problems:
package main
/*
#cgo CFLAGS: -IMemoryModule
#cgo LDFLAGS: MemoryModule/MemoryModule.o
#include "MemoryModule/MemoryModule.h"
*/
import "C"
...
My Makefile for reference:
ifneq ("$(shell which i686-w64-mingw32-gcc)","")
compiler = i686-w64-mingw32-gcc
else
compiler = i586-mingw32msvc-gcc
endif
# Build the dependencies first (subdirs), then move onto the meat and potatoes.
all: MemoryModule
CC=$(compiler) CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -x memorymodule.go
# Dependency build.
SUBDIRS = MemoryModule
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
# Override default subdir build behavior (make) with cmake.
MemoryModule:
[ "`ls -A MemoryModule`" ] || git submodule update --init
# $(MAKE) -C $#
cmake -HMemoryModule -BMemoryModule/build
cmake --build MemoryModule/build --target MemoryModule
# Clean targed.
CLEANDIRS = $(SUBDIRS:%=clean-%)
clean: $(CLEANDIRS)
rm -f memorymodule.exe
$(CLEANDIRS):
$(MAKE) -C $(#:clean-%=%) clean
test:
$(MAKE) -C tests test
.PHONY: subdirs $(INSTALLDIRS) $(SUBDIRS) clean test
Any help would be appreciated!
Git project here: https://github.com/wheelerlaw/go-memory-module
Update:
Okay, so I stumbled across this SO question. So if I change my go build command to:
GOOS="windows" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-w64-mingw32-gcc" go build -x
from:
GOOS="windows" GOARCH="386" CGO_ENABLED="1" CC="i686-w64-mingw32-gcc" go build -x
... it works. So it looks like an incompatibility between the different archs. I'm going to download a 32 bit version of Go to see if that helps.
But what I don't understand is that the program compiles just fine even with the mis-matched archs when I compile C library into an object instead of a static library. Any ideas?

go run throes error " undefined reference to `dlopen'"

I am trying to integrate Golang script with existing C shared library.
The C shared library in turn loads other shared libraries at run time.
While I am trying to run
go run gotest.go
it throws error
./libtest.so: undefined reference to `dlopen'
./libtest.so: undefined reference to `dlclose'
./libtest.so: undefined reference to `dlerror'
./libtest.so: undefined reference to `dlsym'
collect2: error: ld returned 1 exit status
I have created separate C executable to load this libtest.so at run time and that is working properly.
Any idea what should be done to fix the issue?
I have tried following commands
sudo go build -compiler=gccgo -gccgoflags="-ltest" gotest.go
also tried the command
sudo go build -compiler=gccgo -gccgoflags="-ltest -ldl" gotest.go
Also tried the command
go run gotest.go
===============================================
Here is the code I am using
testapi.h
typedef Students{
char name[40];
int id;
} Student;
Student* getStudent();
test.c
#include <stdlib.h>
#include <stdio.h>
#include "mylib.h"
#include "testapi.h"
int rc = 0;
Student *s = 0;
Student* getStudent(){
rc = loadmylib(); //This function loads another shared library
if (!rc)
{
rc = initlib(); //This calls dlsym to get the necessary function from opened shared library
if (!rc)
{
s= (student *)malloc(sizeof(Student));
//use the function pointer received from dlsym to populate the Student struct 's'
}
}
return s;
}
I have following entries in my gotest.go file
package main
/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -ltest -ldl
#include "testapi.h"
*/
import "C"
import (
"fmt"
)
type Student struct{
name string
id int
}
func main() {
st := C.getStudent()
fmt.Println("Name: ", string(st.name))
}
I have copied testapi.h, gotest.go, mylib.h, libmylib.so, libtest.so in the same directory.
Then run the command
go run gotest.go
It has thrown the error " error: undefined reference to 'getStudent' "
What am I doing wrong here?
Am I supposed to run command
sudo go build -compiler=gccgo -gccgoflags="-ltest" gotest.go

Building Go with C interface to Gtk+

I'm trying to build a Go programm which uses external C code as an interface for Gtk+.
That's the basic Go code I've got (ui.h.go):
package main
//#cgo pkg-config: gtk+-3.0
//#include "ui.h"
import "C"
func CInit() {
C.Init(nil, 0)
}
func CMain() {
C.Main()
}
func CShowWindow() {
C.ShowWindow()
}
func main() {
CInit()
CShowWindow()
CMain()
}
C code is compiled from vala into an object file (ui.o) and a header file (ui.h):
#ifndef __UI_H__
#define __UI_H__
#include <glib.h>
#include <stdlib.h>
#include <string.h>
G_BEGIN_DECLS
void ShowWindow (void);
void Init (gchar** args, int args_length1);
void Main (void);
G_END_DECLS
#endif
When I try go build ui.h.go I get:
# command-line-arguments
/tmp/go-build916459533/command-line-arguments/_obj/ui.h.cgo2.o: In function `_cgo_80fc53cbf347_Cfunc_Init':
./ui.h.go:37: undefined reference to `Init'
/tmp/go-build916459533/command-line-arguments/_obj/ui.h.cgo2.o: In function `_cgo_80fc53cbf347_Cfunc_Main':
./ui.h.go:46: undefined reference to `Main'
/tmp/go-build916459533/command-line-arguments/_obj/ui.h.cgo2.o: In function `_cgo_80fc53cbf347_Cfunc_ShowWindow':
./ui.h.go:55: undefined reference to `ShowWindow'
collect2: error: ld returned 1 exit status
Which is logical, I haven't provided my object file. But if I specify it in cgo header of ui.h.go like that...
//#cgo LDFLAGS: ui.o
//#cgo pkg-config: gtk+-3.0
//#include "ui.h"
import "C"
I get multiple definition error, as if it's being linked twice.
# command-line-arguments
/usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
ui.o:(.bss+0x0): multiple definition of `window'
/tmp/go-link-461834384/000000.o:/home/oleg/Документы/Projects/rasp/ui.h.go:38: first defined here
ui.o: In function `ShowWindow':
ui.c:(.text+0x0): multiple definition of `ShowWindow'
/tmp/go-link-461834384/000000.o:(.text+0x25): first defined here
ui.o: In function `Init':
ui.c:(.text+0x29): multiple definition of `Init'
/tmp/go-link-461834384/000000.o:(.text+0x4e): first defined here
ui.o: In function `Main':
ui.c:(.text+0x116): multiple definition of `Main'
/tmp/go-link-461834384/000000.o:(.text+0x13b): first defined here
collect2: error: ld returned 1 exit status
How do I link my ui.o file to the Go program correctly?
Thank you.
Well, I figured out that cgo does link well with static libraries. So I decided to archive my ui.o into libui.a and link it using #cgo LDFLAGS: -L. -lui and it worked correctly.

Golang debug using GDB?

I got 2 questions about GDB + golang?
1) Go build GCC flags
when I run "go build" , which gcc flags do the Go builder use to build a program? The build value is same as the "GOGCCFLAGS" set in the go envionment?
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
because I don't see any "-g" or "-g3" flags for adding code symbol. If yes, how could the symbol table be compiled ?
2) How to print a value in GDB
I followed the tutorial here GDB debug go tutorial, but it seems the value is not what I set.
By the way, I noticed there is a post about it gdb debug go
However, doesn't work for me either.
Golang now works well with GDB
Here is an example golang app gdbtest
- gdbtest/
- main.go
Take the following example main.go
package main
import "fmt"
type MyStruct struct {
x string
i int
f float64
}
func main() {
x := "abc"
i := 3
fmt.Println(i)
fmt.Println(x)
ms := &MyStruct{
x: "cba",
i: 10,
f: 11.10335,
}
fmt.Println(ms)
}
Save that to main.go. Then compile with the follwing gcflag flag.
go build -gcflags "-N"
Open gdb with your newly built golang app
gdb gdbtest
# or
gdb <PROJECT_NAME>
You now have full control of gdb. For example, add a breakpoint with br <linenumber> command, then execute the app with run
(gdb) br 22
Breakpoint 1 at 0x2311: file /go/src/github.com/cevaris/gdbtest/main.go, line 22.
(gdb) run
Starting program: /go/src/github.com/cevaris/gdbtest/gdbtest
3
abc
Breakpoint 1, main.main () at /go/src/github.com/cevaris/gdbtest/main.go:22
22 fmt.Println(ms)
(gdb)
Now you can print all the local variables
(gdb) info locals
i = 3
ms = 0x20819e020
x = 0xdb1d0 "abc"
Even get access to the pointers
(gdb) p ms
$1 = (struct main.MyStruct *) 0x20819e020
(gdb) p *ms
$2 = {x = 0xdb870 "cba", i = 10, f = 11.103350000000001}
Go does not work well with GDB and one of the known problems is the printing of values.
More details can be found here.
The accepted answer is outdated.
Golang currently works with GDB (including locals) if you build with the flags -gcflags=all="-N -l", as described on the official documentation

Resources