I'm porting some server code I wrote in C over to Go and it uses an encryption library I really don't want to rewrite. Instead I'm trying to use Cgo to write a wrapper so that the rest of my code can call it more easily. Here's part of the header for the lib:
// encryption/encryption.h
#define CRYPT_BBCFG 1
typedef struct {
// ...bunch of fields...
uint32_t bb_posn;
} CRYPT_SETUP;
int CRYPT_CreateKeys(CRYPT_SETUP* cs, void* key, unsigned char type);
And here's the proof-of-concept snippet I'm trying to get to work:
package goserv
//#include "encryption/encryption.h"
import "C"
func main() {
cdata := new(C.struct_CRYPT_SETUP)
key := make([]byte, 48)
C.CRYPT_CreateKeys(cdata, &key, C.CRYPT_BLUEBURST)
}
I defined a test function (int test() { return 1; }
) in the header and have no problem calling that from my code (via C.test()
) nor referencing any of the #defined'd constants (C.CRYPT_BBCFG
) but get the following error when I attempt to run go install goserv:
Undefined symbols for architecture x86_64:
"_CRYPT_CreateKeys", referenced from:
__cgo_e89359206bf1_Cfunc_CRYPT_CreateKeys in goserv.cgo2.o
(maybe you meant: __cgo_e89359206bf1_Cfunc_CRYPT_CreateKeys)
ld: symbol(s) not found for architecture x86_64
At this point I'm assuming I'm just not calling the function with the correct arguments. I was under the impression that cdata is of type *C.struct_CRYPT_SETUP
, key should be *byte
(though it doesn't work without the & either) and C.CRYPT_BLUEBURST of type...something. Trying C.uchar(CRYPT_BLURBURST)
also doesn't change anything.
Any suggestions on getting this code to compile?
Edit: Forgot my platform, I'm running Mac OS X 10.10
Edit2 (SOLVED): Jsor's point about using unsafe.Pointer with the address of the first element of key helped but I also had to move my C source files into the same directory as my Go file. There was another type error resulting from using C.struct_CRYPT_DATA instead of C.CRYPT_DATA, so if anyone else runs into errors like this:
./goserv.go:18: cannot use cdata (type *C.struct_CRYPT_SETUP) as type *C.struct___0 in argument to _Cfunc_CRYPT_CreateKeys
Then remove the struct_ prefix (though the cgo docs say that's how to directly reference C struct types)
You probably want &key[0]
, otherwise you're pointing to the Slice Header, which is a struct of 3 values. You end up getting a pointer to a pointer to your buffer, which is probably not what you want.
In general, the pointer to the backing buffer of a slice is &slice[0]
, not &slice
.
As a standard disclaimer: be warned about passing Go-allocated buffers into C. Almost everybody does it, (including me) but it may not be a good idea, and may stop being supported at some point. It's probably fine as long as you keep a reference to the data for the entire duration it's in C, and don't store the pointer for later use on the C side, but it's plausible you'll end up with weirdness due to the Go 1.3 stack moving GC.
Update: Something I just remembered -- void*
in C is analogous to Go's unsafe.Pointer
. So your call should be:
C.CRYPT_CreateKeys(cdata, unsafe.Pointer(&key[0]), C.CRYPT_BLUEBURST)
Another issue may be if you usually link an external library with -l
. For this, use
// #cgo LDFLAGS: -llibname
In the area right above import "C"
where you do your includes.
Edit2: Also, looking at it more, it looks like the function CRYPT_CreateKeys
is not defined in the header file -- only the function prototype. Make sure if it's defined in a separate .c file to declare it extern
. Not doing so will trip up Go.