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

cmd/cgo: link error when using pointers to static C functions #19836

Open
bcmills opened this issue Apr 4, 2017 · 5 comments
Open

cmd/cgo: link error when using pointers to static C functions #19836

bcmills opened this issue Apr 4, 2017 · 5 comments

Comments

@bcmills
Copy link
Member

@bcmills bcmills commented Apr 4, 2017

If I try to pass a pointer to a static C function, I get a link error.

cgocallback/main.go:

package main

/*
#include <stdio.h>

static void invoke(void (*f)()) {
	f();
}

static void print_hello() {
	printf("Hello, !");
}

typedef void (*closure)();  // https://golang.org/issue/19835
*/
import "C"

func main() {
	C.invoke(C.closure(C.print_hello))
}
bcmills:~$ go build cgocallback
# cgocallback
/tmp/go-build059026917/cgocallback/_obj/_cgo_main.o:(.data.rel+0x0): undefined reference to `print_hello'
collect2: error: ld returned 1 exit status

The workaround is to use external linkage for functions used as function pointers, but in some cases that means I have to use more verbose names to avoid collisions.

(Possibly related to #19835.)

bcmills:~$ go version
go version devel +2bbfa6f746 Thu Mar 9 15:36:43 2017 -0500 linux/amd64
bcmills:~$ $(go env CC) --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
@cherrymui
Copy link
Contributor

@cherrymui cherrymui commented Apr 4, 2017

Isn't it that C static function is only visible to the same file? The C code and Go code are not in the same file after code generation. Did I miss something?

Loading

@bcmills
Copy link
Member Author

@bcmills bcmills commented Apr 4, 2017

static is only visible to the same "compilation unit", but everything in the cgo preamble is in the same compilation unit as the Go code.

(In the example above, you can invoke C.print_hello() directly from Go.)

Loading

@cherrymui
Copy link
Contributor

@cherrymui cherrymui commented Apr 4, 2017

Ok. I thought they are in different compilation units... Thanks.

Loading

@qmuntal
Copy link
Contributor

@qmuntal qmuntal commented Jan 11, 2021

static is only visible to the same "compilation unit", but everything in the cgo preamble is in the same compilation unit as the Go code.

(In the example above, you can invoke C.print_hello() directly from Go.)

@bcmills Looking at the cgo intermediate outputs @cherrymui might be right, the Go code is not in the same compilation unit as the static function. In the example above C.print_hello() can be invoked directly because that call does not point directly to the static function defined in the preamble but to a trampoline autogenerated by cgo that is not static and is defined in the same file as the static one (main.cgo2.c), therefore exported by the object file.

This is how main.cgo2.c looks like:

...
static void print_hello() { ... }
...

CGO_NO_SANITIZE_THREAD
void
_cgo_66eac9955d07_Cfunc_print_hello(void *v)
{
  struct {
    char unused;
  } __attribute__((__packed__, __gcc_struct__)) *_cgo_a = v;
  _cgo_tsan_acquire();
  print_hello();
  _cgo_tsan_release();
}

And this is the go wrapper in _cgo_gotypes.go:

//go:cgo_unsafe_args
func _Cfunc_f1() (r1 _Ctype_void) {
  _cgo_runtime_cgocall(_cgo_66eac9955d07_Cfunc_f1, uintptr(unsafe.Pointer(&r1)))
  ...
}

This does not happen when taking the address of a C function because the non-static trampoline is not used and C.print_hello points directly to the static function, therefore producing the linker error.

Loading

@qmuntal
Copy link
Contributor

@qmuntal qmuntal commented Jan 11, 2021

Taking as background my last comment, this issue could be fixed by:

  • Create trampoline functions for all static C functions that are either directly invoked (current behavior) or whose address is retrieved (new addition).
  • When the address of a static C function is retrieved return the address of the non-static trampoline function instead of the original static function.

This approach has the disadvantage that the address retrieved in Go will be different from the address retrieved in C, but I can't think on any other easy way to solve this issue.

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants