cgo wrapper function not working in buildmode=c-shared with exported function

196 views
Skip to first unread message

Dan Kortschak

unread,
Dec 5, 2019, 10:47:59 PM12/5/19
to golang-nuts
I am trying to write a shared module that will be called from C, but I
have run into a problem in using the work-around in
https://github.com/golang/go/wiki/cgo#the-basics for calling variadic C
functions.

The case that I have is more complex, but altering the example at the
wiki demonstrates the problem; the function definition that is used to
call on to printf appears more than once in the C code generated by
Cgo.

```
~/src/github.com/kortschak/cgo $ cat cgo.go
package main

/*
#include <stdio.h>
#include <stdlib.h>

void myprint(char* s) {
printf("%s\n", s);
}
*/
import "C"

import "unsafe"

//export Example
func Example() {
cs := C.CString("Hello from stdio\n")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}

func main() {}
~/src/github.com/kortschak/cgo $ go build -o cgo.so -buildmode=c-shared
.
# github.com/kortschak/cgo
/tmp/go-build899365101/b001/_x002.o: In function `printf':
/usr/include/x86_64-linux-gnu/bits/stdio2.h:104: multiple definition of
`myprint'
/tmp/go-build899365101/b001/_x001.o:/usr/include/x86_64-linux-
gnu/bits/stdio2.h:104: first defined here
collect2: error: ld returned 1 exit status
```

Removing the "//export Example" comment prevents this failure, but then
obviously also loses the exported function. I have tried protecting the
function in a #ifndef/#endif, to no avail.

Is it reasonable for me to expect this to work? If so, what am I doing
wrong?

thanks
Dan

andrey mirtchovski

unread,
Dec 5, 2019, 11:04:41 PM12/5/19
to Dan Kortschak, golang-nuts
you just need to split it in two files. the cfuncs go into another
(sorry for lack of playground link):

$ go build cgo.go cfunc.go
$ ./cgo
Hello from stdio

$ cat cgo.go
package main

/*
#include <stdlib.h>
extern void myprint(char *s);
*/
import "C"

import "unsafe"

//export Example
func Example() {
cs := C.CString("Hello from stdio\n")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}

func main() {
Example()
}
$ cat cfunc.go
package main

/*
#include <stdio.h>
#include <stdlib.h>

void myprint(char* s) {
printf("%s\n", s);
}
*/
import "C"

> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/ab825669afe753c505952f18fb6c61bc8e2dd24d.camel%40kortschak.io.

Dan Kortschak

unread,
Dec 5, 2019, 11:53:14 PM12/5/19
to andrey mirtchovski, golang-nuts
Thanks. Can you explain the reason for this so it sticks in my head?

andrey mirtchovski

unread,
Dec 6, 2019, 12:02:52 AM12/6/19
to Dan Kortschak, golang-nuts
i think cgo does some magic with defining functions called via
C.funcname. if you have the same func defined in the C preamble as
well as call it from the same Go file you get the same func defined
twice. putting it elsewhere as an extern seems to work.

to be honest i never dug into it. i did it once for callbacks in a
library in 2013 and forgot about it :) it just works.

Ian Lance Taylor

unread,
Dec 6, 2019, 12:06:44 AM12/6/19
to andrey mirtchovski, Dan Kortschak, golang-nuts
On Thu, Dec 5, 2019 at 9:02 PM andrey mirtchovski <mirtc...@gmail.com> wrote:
>
> i think cgo does some magic with defining functions called via
> C.funcname. if you have the same func defined in the C preamble as
> well as call it from the same Go file you get the same func defined
> twice. putting it elsewhere as an extern seems to work.
>
> to be honest i never dug into it. i did it once for callbacks in a
> library in 2013 and forgot about it :) it just works.

That is basically right.

This is documented at https://golang.org/cmd/cgo/#hdr-C_references_to_Go:

Using //export in a file places a restriction on the preamble: since
it is copied into two different C output files, it must not contain
any definitions, only declarations. If a file contains both
definitions and declarations, then the two output files will produce
duplicate symbols and the linker will fail. To avoid this, definitions
must be placed in preambles in other files, or in C source files.

Ian
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAK4xykW1EiNZrXaytkrO%2BoiE4PQmF5ccDoc0b9aJgtMzXig8Bg%40mail.gmail.com.

Dan Kortschak

unread,
Dec 6, 2019, 2:43:58 AM12/6/19
to Ian Lance Taylor, andrey mirtchovski, golang-nuts
Thanks, Ian and Andrey.
Reply all
Reply to author
Forward
0 new messages