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
Does anyone have experience with this? In short, I’m trying to create a PAM module written entirely in Go.
You can call the 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.
The order in which everything is called is as follows:
The key to remember here is that a callback function must be marked with the
//exportcomment on the Go side and asexternon 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:
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 namegoGetFiles(). It is this wrapper that actually supplies our Go callback to the C library, along with a userdata object.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 intogoGetFiles()as the userdata parameter.That’s it for our C bindings. The user’s code is now very straight forward:
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: