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