打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
shared libraries

I am trying to create a static object written in Go to interface with a C program (say, a kernel module or something).

I have found documentation on calling C functions from Go, but I haven't found much on how to go the other way. What I've found is that it's possible, but complicated.

Here is what I found:

Blog post about callbacks between C and Go

Cgo documentation

Golang mailing list post

Does anyone have experience with this? In short, I'm trying to create a PAM module written entirely in Go.

asked May 25 '11 at 14:02
tjameson
10.9k105898

9  
You can't, at least from threads that were not created from Go. I've raged about this numerous times, and ceased developing in Go until this is fixed. – Matt Joiner May 25 '11 at 16:08
    
I heard that it is possible. Is there no solution? – tjameson May 25 '11 at 16:56
    
Go uses a different calling convention and segmented stacks. You might be able to link Go code compiled with gccgo with C code, but I have not tried this since I haven't gotten gccgo to build on my system. – mkb May 25 '11 at 18:15
    
I'm trying it using SWIG now, and I'm hopeful... I haven't gotten anything to work yet though... ='( I posted on the mailing list. Hopefully someone has mercy on me. – tjameson May 26 '11 at 4:48
2  
You can call Go code from C, but at the moment you can't embed the Go runtime into a C app, which is an important, but subtle, difference. – tylerl May 27 '11 at 18:39
up vote 94 down vote accepted

You can call Go code from C. it is a confusing proposition though.

The process is outlined in the blog post you linked to. But I can see how that isn't very helpful. Here is a short snippet without any unnecessary bits. It should make things a little clearer.

package foo// extern int goCallbackHandler(int, int);//// static int doAdd(int a, int b) {//     return goCallbackHandler(a, b);// }import "C"//export goCallbackHandlerfunc goCallbackHandler(a, b C.int) C.int {    return a + b}// This is the public function, callable from outside this package.// It forwards the parameters to C.doAdd(), which in turn forwards// them back to goCallbackHandler(). This one performs the addition// and yields the result.func MyAdd(a, b int) int {   return int( C.doAdd( C.int(a), C.int(b)) )}

The order in which everything is called is as follows:

foo.MyAdd(a, b) ->  C.doAdd(a, b) ->    C.goCallbackHandler(a, b) ->      foo.goCallbackHandler(a, b)

The key to remember here is that a callback function must be marked with the //export comment on the Go side and as extern on the C side. This means that any callback you wish to use, must be defined inside your package.

In order to allow a user of your package to supply a custom callback function, we use the exact same approach as above, but we supply the user's custom handler (which is just a regular Go function) as a parameter that is passed onto the C side as void*. It is then received by the callbackhandler in our package and called.

Let's use a more advanced example I am currently working with. In this case, we have a C function that performs a pretty heavy task: It reads a list of files from a USB device. This can take a while, so we want our app to be notified of its progress. We can do this by passing in a function pointer that we defined in our program. It simply displays some progress info to the user whenever it gets called. Since it has a well known signature, we can assign it its own type:

type ProgressHandler func(current, total uint64, userdata interface{}) int

This handler takes some progress info (current number of files received and total number of files) along with an interface{} value which can hold anything the user needs it to hold.

Now we need to write the C and Go plumbing to allow us to use this handler. Luckily the C function I wish to call from the library allows us to pass in a userdata struct of type void*. This means it can hold whatever we want it to hold, no questions asked and we will get it back into the Go world as-is. To make all this work, we do not call the library function from Go directly, but we create a C wrapper for it which we will name goGetFiles(). It is this wrapper that actually supplies our Go callback to the C library, along with a userdata object.

package foo// #include <somelib.h>// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);// // static int goGetFiles(some_t* handle, void* userdata) {//    return somelib_get_files(handle, goProgressCB, userdata);// }import "C"import "unsafe"

Note that the goGetFiles() function does not take any function pointers for callbacks as parameters. Instead, the callback that our user has supplied is packed in a custom struct that holds both that handler and the user's own userdata value. We pass this into goGetFiles() as the userdata parameter.

// This defines the signature of our user's progress handler,type ProgressHandler func(current, total uint64, userdata interface{}) int // This is an internal type which will pack the users callback function and userdata.// It is an instance of this type that we will actually be sending to the C code.type progressRequest struct {   f ProgressHandler  // The user's function pointer   d interface{}      // The user's userdata.}//export goProgressCBfunc goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {    // This is the function called from the C world by our expensive     // C.somelib_get_files() function. The userdata value contains an instance    // of *progressRequest, We unpack it and use it's values to call the    // actual function that our user supplied.    req := (*progressRequest)(userdata)    // Call req.f with our parameters and the user's own userdata value.    return C.int( req.f( uint64(current), uint64(total), req.d ) )}// This is our public function, which is called by the user and// takes a handle to something our C lib needs, a function pointer// and optionally some user defined data structure. Whatever it may be.func GetFiles(h *Handle, pf ProgressFunc, userdata interface{}) int {   // Instead of calling the external C library directly, we call our C wrapper.   // We pass it the handle and an instance of progressRequest.   req := unsafe.Pointer(&progressequest{ pf, userdata })   return int(C.goGetFiles( (*C.some_t)(h), req ))}

That's it for our C bindings. The user's code is now very straight forward:

package mainimport (    "foo"    "fmt")func main() {    handle := SomeInitStuff()    // We call GetFiles. Pass it our progress handler and some    // arbitrary userdata (could just as well be nil).    ret := foo.GetFiles( handle, myProgress, "Callbacks rock!" )    ....}// This is our progress handler. Do something useful like display.// progress percentage.func myProgress(current, total uint64, userdata interface{}) int {    fc := float64(current)    ft := float64(total) * 0.01    // print how far along we are.    // eg: 500 / 1000 (50.00%)    // For good measure, prefix it with our userdata value, which    // we supplied as "Callbacks rock!".    fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc / ft)    return 0}

This all looks a lot more complicated than it is. The call order has not changed as opposed to our previous example, but we get two extra calls at the end of the chain:

The order is as follows:

foo.GetFiles(....) ->  C.goGetFiles(...) ->    C.somelib_get_files(..) ->      C.goProgressCB(...) ->        foo.goProgressCB(...) ->           main.myProgress(...)
answered May 27 '11 at 2:02
jimt
11.5k33946

    
Yes, I do realize that all this may blow up in our faces when separate threads come into play. Specifically those not created by Go. Unfortunately that is the way things are at this point. – jimt May 27 '11 at 2:22
13  
This is a really good answer, and thorough. It doesn't directly answer the question, but that is because there is no answer. According to several sources, the entry point has to be Go, and cannot be C. I'm marking this as correct because this really cleared stuff up for me. Thanks! – tjameson May 27 '11 at 2:45
    
@jimt How does that integrate with the garbage collector? Specifically, when is the private progressRequest instance collected, if ever? (New to Go, and thus to unsafe.Pointer). Also, what about API like SQLite3 that take a void* userdata, but also an optional "deleter" function for userdata? Can that be used to interact with the GC, to tell it "now it's OK to reclaim that userdata, provided the Go side doesn't reference it anymore?". – ddevienne Jun 2 '13 at 18:06
4  
As of Go 1.5 there is better support for calling Go from C. See this question if you are looking for an answer that demonstrates a simpler technique: stackoverflow.com/questions/32215509/… – Gabriel Southern Aug 27 '15 at 21:59
1  
As of Go 1.6 this approach doesn't work, it breaks the "C code may not keep a copy of a Go pointer after the call returns." rule and issues a "panic: runtime error: cgo argument has Go pointer to Go pointer" error at runtime – gg.kaspersky Jun 24 at 22:48

It is not a confusing proposition if you use gccgo. This works here:

foo.go

package mainfunc Add(a, b int) int {    return a + b}

bar.c

#include <stdio.h>extern int go_add(int, int) __asm__ ("example.main.Add");int main() {  int x = go_add(2, 3);  printf("Result: %d\n", x);}

Makefile

all: mainmain: foo.o bar.c    gcc foo.o bar.c -o mainfoo.o: foo.go    gccgo -c foo.go -o foo.o -fgo-prefix=exampleclean:    rm -f main *.o
answered Apr 2 '13 at 9:47
Alexander
3,24312332

    
when I have go code do sth with string go package main func Add(a, b string) int { return a + b } I get error "undefined _go_string_plus" – TruongSinh Jul 28 '14 at 9:09
2  
TruongSinh, you probably want to use cgo and go instead of gccgo. See golang.org/cmd/cgo. When that's said, it is fully possible to use the "string" type in the .go file and change your .c file to include a __go_string_plus function. This works: ix.io/dZB – Alexander Aug 22 '14 at 13:25

The answer has changed with the release of Go 1.5

This SO question that I asked some time ago addresses the issue again in light of the 1.5 added capabilities

Using Go code in an existing C project

answered May 11 at 20:50
dreadiscool
492318

As far as I am concerned it isn't possible:

Note: you can't define any C functions in preamble if you're using exports.

source: https://github.com/golang/go/wiki/cgo

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Golang语法、技巧和窍门
编程语言 Google Go 的初级读本 - 开源中国社区
Go与C语言的互操作
对比学习:Golang VS Python3
Golang学习之路(六):Go+体验分享及字面量讲解
【Go语言学习笔记一】基础语法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服