Linux下调用xxx.so动态库
go 项目中有时会以来与C语言的动态库,因此需要在go语言中封装C语言的接口。
Linux下cgo刚好满足此需求,因此可以使用cgo调用xxx.so库。
本文的编译环境为:Centos7,go1.14.4 gcc-4.8.3-9 。
源代码位置: https://download.csdn.net/download/ok532655221/12517746
源代码结构:
inc 头文件路径
src 源代码路径
test 测试程序路径,包括c语言测试程序和go语言测试程序
1 、使用源代码之前请先配置好所有环境头文件
2、编译 make -f makefile 生成可执行文件(如果环境完全相同可以不编译)
注意:
报错 ./test: error while loading shared libraries: libVeSdkAli.so: cannot open shared object file: No such file or directory 是由于缺少配置环境变量配置,
需要手动在test目录执行:
export LD_LIBRARY_PATH={LD_LIBRARY_PATH}:./
3、GO语言调用
package main
//
// 引用的C头文件需要在注释中声明,紧接着注释需要有import "C",且这一行和注释之间不能有空格
//
/*
//包含header的目录
#cgo CFLAGS: -I .
//动态库编译方式
#cgo LDFLAGS: -L./ -lVeSdkAli -Wl,-rpath=./
#include "vesdk.h"
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println("=====================start====================== ")
var ret C.int = 0
var key string = "1234567811111111"
var inData string = "1111111122222222"
var outData string = ""
//var byteOutBuffer []byte
//byteOutBuffer = make( []byte, 2048 )
//对应C语言中的void*
var pContext unsafe.Pointer
var nKeynum C.int = 1
var pKey = (*C.uchar)(unsafe.Pointer(C.CString(key)))
var nKeyLen = C.uint(len(key))
var pInData = (*C.uchar)(unsafe.Pointer(C.CString(inData)))
var nInDataLen = C.uint(len(inData))
var pOutData = (*C.uchar)(unsafe.Pointer(C.CString(outData)))
//var pOutData = (*C.uchar)(&byteOutBuffer[0])
var pOutDataLen = C.uint(len(outData))
ret = C.VESDK_SM4ECBEncrypt(pContext,nKeynum,pKey,nKeyLen,pInData,nInDataLen,pOutData,&pOutDataLen)
if ret != 0 {
fmt.Println("VESDK_SM4ECBEncrypt Error, ret = %d ",ret);
}
var tmpOutData = (*C.char)(unsafe.Pointer(pOutData))
var cipher = C.GoString(tmpOutData);
fmt.Println("pOutData = ", *pOutData)
fmt.Println("pOutDataLen = ", pOutDataLen)
fmt.Println("cipher = ", cipher)
//fmt.Println("&byteOutBuffer[0] = ", byteOutBuffer[0])
fmt.Println();
fmt.Println("=====================end====================== ")
retur
4、测试结果:
============start==================
nKeynum = 1
pKey[16]:
00000000 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
pInData[16]:
00000000 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
============end==================
pOutDataLen = 16
pOutData[16]:
00000000 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
VESDK_SM4ECBEncryp success
}
5、go执行结构
执行 go run t.go
结果:
=====================start======================
============start==================
nKeynum = 1
pKey[16]:
00000000 31 32 33 34 35 36 37 38 31 31 31 31 31 31 31 31
pInData[16]:
00000000 31 31 31 31 31 31 31 31 32 32 32 32 32 32 32 32
============end==================
pOutData = 51
pOutDataLen = 16
cipher = 3333333333333333
=====================end======================
梳理与总结:
1.C语言和go测试结果
此DEMO中,C语言返回的结果为16个字节的33,然而go中返回的是16个字符的3,由于数字3的十六进制是33,因此正好相差一倍。但是结果完全正确。如果需要返回的是字节数据,可以考虑使用数组。此演示DEMO中仅演示传递字符串。
2.C语言、cgo、go语言中变量对应关系
C语言 - > cgo -> go语言
char --> C.char --> byte
signed char --> C.schar --> int8
unsigned char --> C.uchar --> uint8
short int --> C.short --> int16
short unsigned int --> C.ushort --> uint16
int --> C.int --> int
unsigned int --> C.uint --> uint32
long int --> C.long --> int32 or int64
long unsigned int --> C.ulong --> uint32 or uint64
long long int --> C.longlong --> int64
long long unsigned int --> C.ulonglong --> uint64
float --> C.float --> float32
double --> C.double --> float64
wchar_t --> C.wchar_t -->
void * -> unsafe.Pointer
注意unsafe.Pointer用来表示的为void*,同时unsafe.Pointer可以用来进行指针类型的强制转换(具体参考例子)
3 .go的例子中t.go开头部分注意事项
开头部分为cgo的编译方式以及链接路径,链接的动态库等信息,不能省略
注意动态库编译和静态库编译的区别,具体可以网上查询例子,本文采用动态库编译
注释和import "C"之间不能有空行
/*
//包含header的目录
#cgo CFLAGS: -I .
//动态库编译方式
#cgo LDFLAGS: -L./ -lVeSdkAli -Wl,-rpath=./
#include "vesdk.h"
*/
import "C"
4 .cgo和go常用的转换函数
// 需要调用 C.free 来释放内存
// Golang 的字符串转为 C 字符串
func C.CString(string) *C.char
// 转换 C 字符串到 Golang 字符串
func C.GoString(*C.char) string
// 转换一定长度的 C 字符串到 Golang 字符串
func C.GoStringN(*C.char, C.int) string
// 转换一块 C 内存区域到 Golang 的字节数组中去
func C.GoBytes(unsafe.Pointer, C.int) []byte