Правильный тип Go для перехода к функции C?

Я переношу серверный код, написанный на C, на Go, и он использует библиотеку шифрования, которую я действительно не хочу переписывать. Вместо этого я пытаюсь использовать Cgo для написания оболочки, чтобы остальная часть моего кода могла легко ее вызывать. Вот часть заголовка библиотеки:

// 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);

И вот фрагмент доказательства концепции, который я пытаюсь заставить работать:

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)
}

Я определил тестовую функцию (int test() { return 1; }) в заголовке, и у меня нет проблем с ее вызовом из моего кода (через C.test()) и ссылкой на какую-либо из констант #defined (C.CRYPT_BBCFG), но при попытке запустить возникает следующая ошибка. установить 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

На данный момент я предполагаю, что просто не вызываю функцию с правильными аргументами. У меня сложилось впечатление, что cdata имеет тип *C.struct_CRYPT_SETUP, ключ должен быть *byte (хотя без & тоже не работает) и C.CRYPT_BLUEBURST типа... что-то. Попытка C.uchar(CRYPT_BLURBURST) тоже ничего не меняет.

Любые предложения по компиляции этого кода?

Редактировать: забыл свою платформу, я использую Mac OS X 10.10

Edit2 (РЕШЕНО): точка Jsor об использовании unsafe.Pointer с адресом первого элемента ключа помогла, но мне также пришлось переместить исходные файлы C в тот же каталог, что и мой файл Go. Возникла еще одна ошибка типа в результате использования C.struct_CRYPT_DATA вместо C.CRYPT_DATA, поэтому, если кто-то еще столкнется с такими ошибками:

./goserv.go:18: cannot use cdata (type *C.struct_CRYPT_SETUP) as type *C.struct___0 in argument to _Cfunc_CRYPT_CreateKeys

Затем удалите префикс struct_ (хотя в документах cgo говорится, что именно так можно напрямую ссылаться на типы структур C)


person drodman    schedule 06.11.2014    source источник


Ответы (2)


Вероятно, вам нужен &key[0], иначе вы указываете на заголовок фрагмента, который является структурой 3 значения. В итоге вы получаете указатель на указатель на ваш буфер, что, вероятно, не то, что вам нужно.

Как правило, указатель на резервный буфер слайса равен &slice[0], а не &slice.

Стандартный отказ от ответственности: будьте осторожны с передачей выделенных Go буферов в C. Почти все так делают (включая меня), но это может быть плохой идеей, и в какой-то момент поддержка может прекратиться. Это вероятно нормально, если вы сохраняете ссылку на данные в течение всего времени, пока они находятся в C, и не сохраняете указатель для последующего использования на стороне C, но вполне вероятно, что вы закончите со странностями из-за того, что стек Go 1.3 перемещает сборщик мусора.

Обновление: кое-что, что я только что вспомнил - void* в C аналогичен unsafe.Pointer в Go. Итак, ваш звонок должен быть:

C.CRYPT_CreateKeys(cdata, unsafe.Pointer(&key[0]), C.CRYPT_BLUEBURST)

Другая проблема может возникнуть, если вы обычно связываете внешнюю библиотеку с -l. Для этого используйте

// #cgo LDFLAGS: -llibname

В области прямо над import "C", где вы делаете свои включения.

Edit2: Кроме того, если посмотреть на это подробнее, похоже, что функция CRYPT_CreateKeys не определена в заголовочном файле - только прототип функции. Убедитесь, что он определен в отдельном файле .c, чтобы объявить его extern. В противном случае Go будет спотыкаться.

person LinearZoetrope    schedule 06.11.2014
comment
Спасибо за ваше предложение, я уверен, что оно приближает меня к тому, чтобы заставить это работать, но, к сожалению, я все еще получаю ту же ошибку. - person drodman; 06.11.2014
comment
@drodman Когда вы компилируете свою программу на C, у вас обычно есть флаг -l? Потому что вам нужно сделать это и в Go. Я обновлю свой ответ, если это так. - person LinearZoetrope; 08.11.2014
comment
Код C не использует ничего, кроме стандартной библиотеки C, поэтому мне нечего указывать с помощью флага -l. Раньше я пробовал unsafe.Pointer, но не с адресом первого элемента массива, это хорошая идея, но я не могу избежать этих ошибок компоновщика. Нужно ли что-то делать помимо запуска go install? - person drodman; 09.11.2014
comment
@drodman - Кроме того, не забудьте объявить все функции и переменные, не полностью определенные в вашем заголовочном файле, как extern. - person LinearZoetrope; 09.11.2014
comment
Это работает, большое спасибо! Мне пришлось переместить исходные файлы C в тот же каталог, что и мой файл go (который мне нужно выяснить, как обойти), а также воспользоваться вашим советом по использованию unsafe.Pointer для передачи ключа. Столкнулся с незначительной ошибкой типа, но я редактирую свой вопрос, чтобы указать, что - person drodman; 09.11.2014

Еще один момент, если вы передаете буферы между go и C. Это хороший вариант написать код C таким образом, чтобы он локально копировал буфер, переданный GO. Это поможет, когда вы приводите свой буфер к типу Int* или другому типу указателя. Таким образом, вы будете независимы от буфера, предоставленного Cgo.

person Community    schedule 01.08.2018