cgo+gSoap+onvif学习总结:8、arm平台交叉编译运行及常见问题总结


文章目录

1. 前言

此前我们都是将相关cgo程序代码在Windows上运行的,所以基本是x86或者amd64,在wsl上编译c或者go都是比较简单容易的,没有太大的问题,但是当我们需要将cgo程序应用于arm平台时(不管是Android、openwrt、纯linux或者现在的macOS很多都使用arm架构芯片了),就需要关注到交叉编译的问题,所以这里以arm-openwrt为例,总结使用gSoap的cgo程序如何在交叉编译之后正常运行于arm平台,一些常见问题我们是如何解决的,这对于使用gSoap+cgo框架的onvif协议程序应用于Android、openwrt、Linux、新的macOS都是很有帮助的。

2. 交叉编译工具链选择及安装

首先我们需要安装交叉编译工具链,需要找到设备厂商获取编译系统时生成的交叉编译工具链,系统可能会有一些裁剪之类的,交叉编译工具链需要的一些c动态库可能会有一些变化,如果没有特殊之处一般也有标准的arm-linux交叉编译工具链可以直接通过命令安装,你需要根据你的设备或者系统情况选择正确的交叉编译工具链,这是交叉编译C/C++程序的前提条件。

tar

可以将交叉编译工具链路径加到环境变量中或者编译时直接指明绝对路径。

3. 交叉编译openssl

之前gSoap的c程序中需要依赖openssl来进行鉴权,而openssl不是系统自带的库,所以我们需要先交叉编译一下openssl生成相关库。

3.1 下载openssl源码

最好根据系统已有的openssl的版本相同,编译后的程序依赖不会出问题,比如目标机上已经有了libssl.1.1.so那么就下载1.1.1版本的openssl进行交叉编译。

openssl下载地址:

​https://www.openssl.org/source/old/​

可以使用wget下载:

wget

3.2 wsl下交叉编译openssl

tar zxvf openssl-1.1.1f.tar.gz -C ./
cd openssl-1.1.1f/
mkdir arm64-openwrt
./config no-asm shared --prefix=/home/zy/work/openssl-1.1.1f/arm64-openwrt/ CROSS_COMPILE=/home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/bin/aarch64-openwrt-linux-musl- CC=gcc
make
make install

4. 交叉编译c程序测试及生成静态库

根据gSoap框架生成c代码的过程就不再多说了,不清楚的看一下之前的文章。

此外,为了减少go和c之间传递参数,创建了统一的结构体进行处理。

4.1 c程序交叉编译测试

//
// Created by admin on 2022/3/3.
//

#include <string.h>
#include "soap/soapStub.h"
#include "soap/wsdd.nsmap"
#include "soap/soapH.h"
#include "soap/wsseapi.h"
#include "onvif_cgo_client.h"

struct soap *new_soap(struct soap *soap) {
//soap初始化,申请空间
soap = soap_new();
if (soap == NULL) {
printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
return NULL;
}

soap_set_namespaces(soap, namespaces);
soap->recv_timeout = 3;
printf("func:%s,line:%d.new soap success!\n", __FUNCTION__, __LINE__);
return soap;
}

void del_soap(struct soap *soap) {
//清除soap
soap_end(soap);
soap_free(soap);
}

int discovery(struct soap *soap) {
//发送消息描述
struct wsdd__ProbeType req;
struct __wsdd__ProbeMatches resp;
//描述查找那类的Web消息
struct wsdd__ScopesType sScope;
//soap消息头消息
struct SOAP_ENV__Header header;
//获得的设备信息个数
int count = 0;
//返回值
int res;
//存放uuid 格式(8-4-4-4-12)
char uuid_string[64];

printf("func:%s,line:%d.discovery dev!\n", __FUNCTION__, __LINE__);
if (soap == NULL) {
printf("func:%s,line:%d.soap is nil.\n", __FUNCTION__, __LINE__);
return -1;
}

sprintf(uuid_string, "464A4854-4656-5242-4530-110000000000");
printf("func:%s,line:%d.uuid = %s\n", __FUNCTION__, __LINE__, uuid_string);

//将header设置为soap消息,头属性,暂且认为是soap和header绑定
soap_default_SOAP_ENV__Header(soap, &header);
header.wsa5__MessageID = uuid_string;
header.wsa5__To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
header.wsa5__Action = "http://schemas.xmllocal_soap.org/ws/2005/04/discovery/Probe";
//设置soap头消息的ID
soap->header = &header;

/* 设置所需寻找设备的类型和范围,二者至少设置一个
否则可能收到非ONVIF设备,出现异常
*/
//设置soap消息的请求服务属性
soap_default_wsdd__ScopesType(soap, &sScope);
sScope.__item = "onvif://www.onvif.org";
soap_default_wsdd__ProbeType(soap, &req);
req.Scopes = &sScope;

/* 设置所需设备的类型,ns1为命名空间前缀,在wsdd.nsmap 文件中
{"tdn","http://www.onvif.org/ver10/network/wsdl"}的tdn,如果不是tdn,而是其它,
例如ns1这里也要随之改为ns1
*/
req.Types = "ns1:NetworkVideoTransmitter";

//调用gSoap接口 向 239.255.255.250:3702 发送udp消息
res = soap_send___wsdd__Probe(soap, "soap.udp://239.255.255.250:3702/", NULL, &req);

if (res == -1) {
printf("func:%s,line:%d.soap error: %d, %s, %s \n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
res = soap->error;
} else {
do {
printf("func:%s,line:%d.begin receive probe match, find dev count:%d.... \n", __FUNCTION__, __LINE__,
count);

//接收 ProbeMatches,成功返回0,错误返回-1
res = soap_recv___wsdd__ProbeMatches(soap, &resp);
printf("func:%s,line:%d.result=%d \n", __FUNCTION__, __LINE__, res);
if (res == -1) {
break;
} else {
//读取服务器回应的Probematch消息
printf("soap_recv___wsdd__Probe: __sizeProbeMatch = %d \n", resp.wsdd__ProbeMatches->__sizeProbeMatch);
printf("Target EP Address : %s \n",
resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);
printf("Target Type : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Types);
printf("Target Service Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);
printf("Target Metadata Version: %d \n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);
printf("Target Scope Address : %s \n", resp.wsdd__ProbeMatches->ProbeMatch->Scopes->__item);
count++;
}
} while (1);
}

return res;
}

int set_auth_info(struct soap *soap, const char *username, const char *password) {
if (NULL == username) {
printf("func:%s,line:%d.username is null.\n", __FUNCTION__, __LINE__);
return -1;
}
if (NULL == password) {
printf("func:%s,line:%d.password is nil.\n", __FUNCTION__, __LINE__);
return -2;
}

int result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);

return result;
}

int get_device_info(struct soap *soap, const char *username, const char *password, char *xAddr) {
if (NULL == xAddr) {
printf("func:%s,line:%d.dev addr is nil.\n", __FUNCTION__, __LINE__);
return -1;
}
if (soap == NULL) {
printf("func:%s,line:%d.malloc soap error.\n", __FUNCTION__, __LINE__);
return -2;
}

struct _tds__GetDeviceInformation deviceInformation;
struct _tds__GetDeviceInformationResponse deviceInformationResponse;

set_auth_info(soap, username, password);

int res = soap_call___tds__GetDeviceInformation(soap, xAddr, NULL, &deviceInformation, &deviceInformationResponse);

if (NULL != soap) {
printf("Manufacturer:%s\n", deviceInformationResponse.Manufacturer);
printf("Model:%s\n", deviceInformationResponse.Model);
printf("FirmwareVersion:%s\n", deviceInformationResponse.FirmwareVersion);
printf("SerialNumber:%s\n", deviceInformationResponse.SerialNumber);
printf("HardwareId:%s\n", deviceInformationResponse.HardwareId);
soap_default__tds__GetDeviceInformation(soap, &deviceInformation);
soap_default__tds__GetDeviceInformationResponse(soap, &deviceInformationResponse);
}
return res;
}

int get_capabilities(struct soap *soap, const char *username, const char *password, char *xAddr, char *mediaAddr) {
struct _tds__GetCapabilities capabilities;
struct _tds__GetCapabilitiesResponse capabilitiesResponse;

set_auth_info(soap, username, password);
int res = soap_call___tds__GetCapabilities(soap, xAddr, NULL, &capabilities, &capabilitiesResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
if (capabilitiesResponse.Capabilities == NULL) {
printf("func:%s,line:%d.GetCapabilities failed! result=%d \n", __FUNCTION__, __LINE__, res);
} else {
printf("func:%s,line:%d.Media->XAddr=%s \n", __FUNCTION__, __LINE__,
capabilitiesResponse.Capabilities->Media->XAddr);
strcpy(mediaAddr, capabilitiesResponse.Capabilities->Media->XAddr);
}
return res;
}

int get_profiles(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr) {
struct _trt__GetProfiles profiles;
struct _trt__GetProfilesResponse profilesResponse;
set_auth_info(soap, username, password);
int res = soap_call___trt__GetProfiles(soap, xAddr, NULL, &profiles, &profilesResponse);
if (res == -1)
//NOTE: it may be regular if result isn't SOAP_OK.Because some attributes aren't supported by server.
//any question email leoluopy@gmail.com
{
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}

if (profilesResponse.__sizeProfiles <= 0) {
printf("func:%s,line:%d.Profiles Get Error\n", __FUNCTION__, __LINE__);
return res;
}

for (int i = 0; i < profilesResponse.__sizeProfiles; i++) {
if (profilesResponse.Profiles[i].token != NULL) {
printf("func:%s,line:%d.Profiles token:%s\n", __FUNCTION__, __LINE__, profilesResponse.Profiles->Name);

//默认我们取第一个即可,可以优化使用字符串数组存储多个
if (i == 0) {
strcpy(profileToken, profilesResponse.Profiles[i].token);
}
}
}
return res;
}

int get_rtsp_uri(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr, char *rtsp_uri) {
struct _trt__GetStreamUri streamUri;
struct _trt__GetStreamUriResponse streamUriResponse;
streamUri.StreamSetup = (struct tt__StreamSetup *) soap_malloc(soap, sizeof(struct tt__StreamSetup));
streamUri.StreamSetup->Stream = 0;
streamUri.StreamSetup->Transport = (struct tt__Transport *) soap_malloc(soap, sizeof(struct tt__Transport));
streamUri.StreamSetup->Transport->Protocol = 0;
streamUri.StreamSetup->Transport->Tunnel = 0;
streamUri.StreamSetup->__size = 1;
streamUri.StreamSetup->__any = NULL;
streamUri.StreamSetup->__anyAttribute = NULL;
streamUri.ProfileToken = profileToken;
set_auth_info(soap, username, password);
int res = soap_call___trt__GetStreamUri(soap, xAddr, NULL, &streamUri, &streamUriResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
printf("func:%s,line:%d.RTSP uri is :%s \n", __FUNCTION__, __LINE__, streamUriResponse.MediaUri->Uri);
strcpy(rtsp_uri, streamUriResponse.MediaUri->Uri);
return res;
}

int get_snapshot(struct soap *soap, const char *username, const char *password, char *profileToken, char *xAddr, char *snapshot_uri) {
struct _trt__GetSnapshotUri snapshotUri;
struct _trt__GetSnapshotUriResponse snapshotUriResponse;

set_auth_info(soap, username, password);
snapshotUri.ProfileToken = profileToken;
int res = soap_call___trt__GetSnapshotUri(soap, xAddr, NULL, &snapshotUri, &snapshotUriResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
printf("func:%s,line:%d.Snapshot uri is :%s \n", __FUNCTION__, __LINE__,
snapshotUriResponse.MediaUri->Uri);
strcpy(snapshot_uri, snapshotUriResponse.MediaUri->Uri);
return res;
}

int get_video_source(struct soap *soap, const char *username, const char *password, char *videoSource, char *xAddr) {
struct _trt__GetVideoSources getVideoSources;
struct _trt__GetVideoSourcesResponse getVideoSourcesResponse;

set_auth_info(soap, username, password);

int res = soap_call___trt__GetVideoSources(soap, xAddr, NULL, &getVideoSources, &getVideoSourcesResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
if (getVideoSourcesResponse.__sizeVideoSources <= 0) {
printf("func:%s,line:%d.get video sources failed.\n", __FUNCTION__, __LINE__);
return res;
} else {
for (int i = 0; i < getVideoSourcesResponse.__sizeVideoSources; i++) {
printf("func:%s,line:%d.get video source token:%s\n", __FUNCTION__, __LINE__,
getVideoSourcesResponse.VideoSources[i].token);

//我们暂时只获取第一个videoSourceToken
if (i == 0) {
strcpy(videoSource, getVideoSourcesResponse.VideoSources[i].token);
}
}
}
return res;
}

int ptz(struct soap *soap, const char *username, const char *password, int direction, float speed, char *profileToken,
char *xAddr) {
struct _tptz__ContinuousMove continuousMove;
struct _tptz__ContinuousMoveResponse continuousMoveResponse;
struct _tptz__Stop stop;
struct _tptz__StopResponse stopResponse;
int res;

set_auth_info(soap, username, password);
continuousMove.ProfileToken = profileToken;
continuousMove.Velocity = (struct tt__PTZSpeed *) soap_malloc(soap, sizeof(struct tt__PTZSpeed));
continuousMove.Velocity->PanTilt = (struct tt__Vector2D *) soap_malloc(soap, sizeof(struct tt__Vector2D));

switch (direction) {
case 1:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = speed;
break;
case 2:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = -speed;
break;
case 3:
continuousMove.Velocity->PanTilt->x = -speed;
continuousMove.Velocity->PanTilt->y = 0;
break;
case 4:
continuousMove.Velocity->PanTilt->x = speed;
continuousMove.Velocity->PanTilt->y = 0;
break;
case 5:
continuousMove.Velocity->PanTilt->x = -speed;
continuousMove.Velocity->PanTilt->y = speed;
break;
case 6:
continuousMove.Velocity->PanTilt->x = -speed;
continuousMove.Velocity->PanTilt->y = -speed;
break;
case 7:
continuousMove.Velocity->PanTilt->x = speed;
continuousMove.Velocity->PanTilt->y = speed;
break;
case 8:
continuousMove.Velocity->PanTilt->x = speed;
continuousMove.Velocity->PanTilt->y = -speed;
break;
case 9:
stop.ProfileToken = profileToken;
stop.Zoom = NULL;
stop.PanTilt = NULL;
res = soap_call___tptz__Stop(soap, xAddr, NULL, &stop, &stopResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
*soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}
return res;
case 10:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = 0;
continuousMove.Velocity->Zoom = (struct tt__Vector1D *) soap_malloc(soap, sizeof(struct tt__Vector1D));
continuousMove.Velocity->Zoom->x = speed;
break;
case 11:
continuousMove.Velocity->PanTilt->x = 0;
continuousMove.Velocity->PanTilt->y = 0;
continuousMove.Velocity->Zoom = (struct tt__Vector1D *) soap_malloc(soap, sizeof(struct tt__Vector1D));
continuousMove.Velocity->Zoom->x = -speed;
break;
default:
printf("func:%s,line:%d.Ptz direction unknown.\n", __FUNCTION__, __LINE__);
return -1;
}
res = soap_call___tptz__ContinuousMove(soap, xAddr, NULL, &continuousMove, &continuousMoveResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error, *soap_faultcode(soap),
*soap_faultstring(soap));
return soap->error;
}

return res;
}

int
focus(struct soap *soap, const char *username, const char *password, int direction, float speed, char *videoSourceToken,
char *xAddr) {
struct _timg__Move timgMove;
struct _timg__MoveResponse timgMoveResponse;
struct _timg__Stop timgStop;
struct _timg__StopResponse timgStopResponse;
int res;

set_auth_info(soap, username, password);
timgMove.Focus = (struct tt__FocusMove *) soap_malloc(soap, sizeof(struct tt__FocusMove));
timgMove.Focus->Continuous = (struct tt__ContinuousFocus *) soap_malloc(soap, sizeof(struct tt__ContinuousFocus));
timgMove.VideoSourceToken = videoSourceToken;
switch (direction) {
case 12:
timgMove.Focus->Continuous->Speed = speed;
break;
case 13:
timgMove.Focus->Continuous->Speed = -speed;
break;
case 14:
timgStop.VideoSourceToken = videoSourceToken;
res = soap_call___timg__Stop(soap, xAddr, NULL, &timgStop, &timgStopResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
*soap_faultcode(soap), *soap_faultstring(soap));
return soap->error;
}
return res;
default:
printf("unknown direction");
return -1;
}
res = soap_call___timg__Move(soap, xAddr, NULL, &timgMove, &timgMoveResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
*soap_faultcode(soap), *soap_faultstring(soap));
return soap->error;
}
return res;
}

int preset(struct soap *soap, const char *username, const char *password, int presetAction, char *presetToken,
char *presetName, char *profileToken, char *xAddr) {
int res;
set_auth_info(soap, username, password);
switch (presetAction) {
case 1: {
struct _tptz__GetPresets getPresets;
struct _tptz__GetPresetsResponse getPresetsResponse;
getPresets.ProfileToken = profileToken;
res = soap_call___tptz__GetPresets(soap, xAddr, NULL, &getPresets, &getPresetsResponse);
if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
*soap_faultcode(soap), *soap_faultstring(soap));
return soap->error;
}
if (getPresetsResponse.__sizePreset <= 0) {
printf("func:%s,line:%d.get presets failed.\n", __FUNCTION__, __LINE__);
return res;
}
for (int i = 0; i < getPresetsResponse.__sizePreset; i++) {
printf("func:%s,line:%d.preset token:%s,preset name:%s\n", __FUNCTION__, __LINE__,
getPresetsResponse.Preset[i].token, getPresetsResponse.Preset[i].Name);
}
return res;
}
case 2: {
struct _tptz__SetPreset setPreset;
struct _tptz__SetPresetResponse setPresetResponse;
setPreset.ProfileToken = profileToken;
setPreset.PresetName = presetName;
setPreset.PresetToken = presetToken;
res = soap_call___tptz__SetPreset(soap, xAddr, NULL, &setPreset, &setPresetResponse);
break;
}
case 3: {
struct _tptz__GotoPreset gotoPreset;
struct _tptz__GotoPresetResponse gotoPresetsResponse;
gotoPreset.ProfileToken = profileToken;
gotoPreset.PresetToken = presetToken;
gotoPreset.Speed = NULL;
res = soap_call___tptz__GotoPreset(soap, xAddr, NULL, &gotoPreset, &gotoPresetsResponse);
break;
}
case 4: {
struct _tptz__RemovePreset removePreset;
struct _tptz__RemovePresetResponse removePresetResponse;
removePreset.PresetToken = presetToken;
removePreset.ProfileToken = profileToken;
res = soap_call___tptz__RemovePreset(soap, xAddr, NULL, &removePreset, &removePresetResponse);
break;
}
default:
printf("func:%s,line:%d.Unknown preset action.\n", __FUNCTION__, __LINE__);
}

if (soap->error) {
printf("func:%s,line:%d.soap error: %d, %s, %s\n", __FUNCTION__, __LINE__, soap->error,
*soap_faultcode(soap), *soap_faultstring(soap));
return soap->error;
}

return res;
}

int main(int argc, void *argv[]) {
struct soap *soap = NULL;
soap = new_soap(soap);
const char username[] = "admin";
const char password[] = "@";
char serviceAddr[] = "http://40.40.40.101:80/onvif/device_service";

discovery(soap);

get_device_info(soap, username, password, serviceAddr);

char mediaAddr[200] = {'\0'};
get_capabilities(soap, username, password, serviceAddr, mediaAddr);

char profileToken[200] = {'\0'};
get_profiles(soap, username, password, profileToken, mediaAddr);

char rtspUri[200] = {'\0'};
get_rtsp_uri(soap, username, password, profileToken, mediaAddr, rtspUri);

char snapshotUri[200] = {'\0'};
get_snapshot(soap, username, password, profileToken, mediaAddr, snapshotUri);

char videoSourceToken[200] = {'\0'};
get_video_source(soap, username, password, videoSourceToken, mediaAddr);

int direction = -1;
while (direction != 0) {
printf("请输入数字进行ptz,1-14分别代表上、下、左、右、左上、左下、右上、右下、停止、缩、放、调焦加、调焦减、调焦停止;退出请输入0:");
scanf("%d", &direction);
if ((direction >= 1) && (direction <= 11)) {
ptz(soap, username, password, direction, 0.5f, profileToken, mediaAddr);
} else if (direction >= 12 && direction <= 14) {
focus(soap, username, password, direction, 0.5f, videoSourceToken, mediaAddr);
}
}

int presetAction = -1;
while (presetAction != 0) {
printf("请输入数字进行preset,1-4分别代表查询、设置、跳转、删除预置点;退出输入0:\n");
scanf("%d", &presetAction);
if (1 == presetAction) {
preset(soap, username, password, presetAction, NULL, NULL, profileToken, mediaAddr);
} else if (2 == presetAction) {
printf("请输入要设置的预置点token信息:\n");
char presentToken[10];
scanf("%s", presentToken);
printf("请输入要设置的预置点name信息()长度不超过200:\n");
char presentName[201];
scanf("%s", presentName);
preset(soap, username, password, presetAction, presentToken, presentName, profileToken, mediaAddr);
} else if (3 == presetAction) {
printf("请输入要跳转的预置点token信息:\n");
char presentToken[10];
scanf("%s", presentToken);
preset(soap, username, password, presetAction, presentToken, NULL, profileToken, mediaAddr);
} else if (4 == presetAction) {
printf("请输入要删除的预置点token信息:\n");
char presentToken[10];
scanf("%s", presentToken);
preset(soap, username, password, presetAction, presentToken, NULL, profileToken, mediaAddr);
}
}

del_soap(soap);
}

4.2 CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(onvif-cgo)

set(CMAKE_C_COMPILER /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/bin/aarch64-openwrt-linux-musl-gcc)
set(CMAKE_C_FLAGS "-DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACES")
aux_source_directory(./soap/ SRC_LIST)
include_directories(./ /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/ ./soap/custom /home/zy/work/openssl-1.1.1f/arm64-openwrt/include)
link_directories(/home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl /home/zy/work/openssl-1.1.1f/arm64-openwrt/lib)
add_executable(gsoap-onvif ${SRC_LIST} onvif_cgo_client.c onvif_cgo_client.h ./soap/custom)
target_link_libraries(gsoap-onvif -lpthread -ldl -lssl -lcrypto)
#ADD_LIBRARY(c_onvif SHARED ${SRC_LIST} client.c)
#ADD_LIBRARY(c_onvif_static STATIC ${SRC_LIST} onvif_cgo_client.c onvif_cgo_client.h ./soap/custom)

4.3 运行结果

cgo+gSoap+onvif学习总结:8、arm平台交叉编译运行及常见问题总结_学习

5. 交叉编译cgo程序

go程序和之前类似,根据项目实际情况做了一些修改,c代码去除main函数,cmake修改生成静态库。

此外,还需要注意go使用1.18版本及以上,否则cgo可能发生堆栈分割违例问题导致go和c相互调用时挂掉。

cmake_minimum_required(VERSION 3.0)
project(onvif-cgo)

set(CMAKE_C_COMPILER /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/bin/aarch64-openwrt-linux-musl-gcc)
set(CMAKE_C_FLAGS "-DWITH_DOM -DWITH_OPENSSL -DWITH_NONAMESPACES")
aux_source_directory(./soap/ SRC_LIST)
include_directories(./ /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/ ./soap/custom /home/zy/work/openssl-1.1.1f/arm64-openwrt/include)
link_directories(/home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl /home/zy/work/openssl-1.1.1f/arm64-openwrt/lib)
#add_executable(gsoap-onvif ${SRC_LIST} onvif_cgo_client.c onvif_cgo_client.h ./soap/custom)
#target_link_libraries(gsoap-onvif -lpthread -ldl -lssl -lcrypto)
#ADD_LIBRARY(c_onvif SHARED ${SRC_LIST} client.c)
ADD_LIBRARY(c_onvif_static STATIC ${SRC_LIST} onvif_cgo_client.c onvif_cgo_client.h ./soap/custom)
package main

/*
#cgo CFLAGS: -I ./ -I /usr/ -I ~/work/gsoap-2.8/gsoap/ -I /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/include -I /home/zy/work/openssl-1.1.1f/arm64-openwrt/include
#cgo LDFLAGS: -L /home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/lib -L /home/zy/work/openssl-1.1.1f/arm64-openwrt/lib -L ./build -lc_onvif_static -lpthread -ldl -lssl -lcrypto
//#cgo LDFLAGS: -L ./build -lc_onvif_static -lpthread -ldl -lssl -lcrypto
#include "onvif_cgo_client.h"
#include "malloc.h"
*/
import "C"

cgo代码编译:

CGO_ENABLED=1 CC=/home/zy/work/toolchain-aarch64_generic_gcc-7.5.0_musl/bin/aarch64-openwrt-linux-musl-gcc GOOS=linux GOARCH=arm64 go build -mod=mod

6、常见问题解决

6.1 编译时显示aarch64-openwrt-linux-musl-gcc: error: unrecognized command line option ‘-m64’

可以去掉makefile中的-m64(vim打开后搜索-m64即可,两个位置需要更改),这个是交叉编译的gcc不支持这个配置,如果是版本比较高的gcc是可以支持的,虽然我们是用在arm64上的,但是32位的程序也可以运行使用,就是性能上可能会有一点影响,暂时不用太过在意。

6.2、问题1:程序编译成功,但是运行时显示:xxx not found

这个是c程序依赖的动态库找不到导致的,我们可以使用readelf工具在宿主机上测试程序需要依赖的库,然后拷贝到目标机/lib下即可。

cgo+gSoap+onvif学习总结:8、arm平台交叉编译运行及常见问题总结_学习_02

readelf -l xxx:

cgo+gSoap+onvif学习总结:8、arm平台交叉编译运行及常见问题总结_arm_03

readelf -d xxx:

cgo+gSoap+onvif学习总结:8、arm平台交叉编译运行及常见问题总结_交叉编译_04

6.3、问题2:替换openssl的libssl.so.1.1及libcrypto.so.1.1时python及python相关程序报错

这个是由于系统python的一些模块依赖的openssl相关的库和你编译出来的openssl库使用的交叉编译工具链不同导致的问题,python依赖到openssl库时发现不兼容就会出现段错误进而引起python相关模块出错。

7. 最后

嵌入式开发和普通开发的一个比较麻烦的点就是交叉编译,而且由于依赖的库可能也是系统需要的一些库,所以出点问题可能就会牵扯到系统,因此嵌入式开发一定要具备系统性风险意识,这也和c开发刀尖上跳舞有点像,所以备份和底线意识一定要有,出了问题至少要能回到过去。