Golang中使用C-shared库在Nuvoton ARM926EJ-S rev5上导致段错误问题
Golang中使用C-shared库在Nuvoton ARM926EJ-S rev5上导致段错误问题 使用Go代码构建的C共享库无法在Nuvoton ARM926EJ-S rev 5芯片上使用。当二进制文件使用此共享库时,运行时会出现段错误。
我在Linux amd64环境下构建C共享库,Go版本为1.11.5

库代码如下:
package main
/*
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
#include<stdbool.h>
#define RETURN_OK 1
#define RETURN_ERROR_INPUTPARAM 1001
typedef struct{
char version[256];
uint16_t size;
}VersionInfo;
*/
import "C"
import "fmt"
//export GetVersion
func GetVersion() C.int {
fmt.Println("GetVersion In")
return C.int(1)
}
func main() {
}
保存为versiontest.go
使用"go build -buildmode=c-shared -o libversiontest.so versiontest.go"命令构建libversiontest.so和libversiontest.h
GCC使用的是Nuvoton交叉编译器,我设置了以下环境变量:
GOARCH=arm GOARM=5 //因为架构是ARMV5TEJ CGO_ENABLED=1
其他Go环境变量保持默认值。
C共享库测试代码如下:
#include "libversiontest.h"
int main(){
GetVersion();
return 1;
}
构建为可执行文件testversion。
然后将testversion和libversiontest.so复制到Nuvoton芯片。
当运行testversion二进制文件时,会出现"段错误"。
使用GDB调试时,输出如下:
程序收到信号SIGILL,非法指令。 _rt0_arm_lib () at /usr/local/go/src/runtime/asm_arm.s:46 46 MOVD F8, (40+80)(R13)*
我检查了asm_arm.s代码,该行是:
MOVD F8, (40+8*0)(R13)
但相同的代码在树莓派2 Model B(armV7)上可以正常运行。
更多关于Golang中使用C-shared库在Nuvoton ARM926EJ-S rev5上导致段错误问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好,skillian
很抱歉回复晚了。
这意味着,当我为ARMV5TEJ架构使用默认构建模式编译Go代码时,程序可以正常运行。
但是当我为ARMV5TEJ架构使用共享库模式(构建模式为c-share)编译Go代码时,程序无法运行,并产生错误(段错误)。
谢谢
更多关于Golang中使用C-shared库在Nuvoton ARM926EJ-S rev5上导致段错误问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
嗨,skillian
感谢您的回复。
我添加了 -a 标志强制重新构建所有内容,但问题依旧存在。
我得到的信息是 ARMV5TEJ 不支持硬件浮点指令。
我还检查了当 GOARM=5 时,可执行二进制文件可以在 ARMV5TEJ 芯片上运行。
gibbsqi:
我还验证了当设置 GOARM=5 时,可执行文件能在 ARMV5TEJ 芯片上运行
你能否澄清一下这里的含义?根据我的理解,这听起来像是在设置 GOARM=5 时程序可以正常运行,而设置 GOARM=5 是为了让运行时模拟浮点指令(GOARM >=6 则假定存在硬件浮点支持)。
我很难确认这一点,但我怀疑问题在于ARMV5TEJ不支持浮点指令,而出错的指令可能正是浮点指令(F8是指这个意思吗?它看起来像是Go汇编器特有的助记符,我不熟悉Go汇编器的ARM语法)。我的猜测是运行时最初是用GOARM=7构建的,即使您使用GOARM=5构建,它仍然被重复使用。您尝试过在设置GOARM=5时给go build添加-a标志来强制重新构建所有内容吗?
func main() {
fmt.Println("hello world")
}
这是一个典型的交叉编译目标架构不匹配导致的段错误问题。问题出现在Go运行时初始化阶段,具体是在ARM运行时入口点_rt0_arm_lib执行时遇到非法指令。
问题分析
Nuvoton ARM926EJ-S rev5使用的是ARMv5TEJ架构,而你的Go工具链可能默认编译为ARMv7或更高版本。MOVD指令是ARMv6K及更高版本引入的双字加载/存储指令,在ARMv5架构上不被支持。
解决方案
需要确保Go工具链为目标平台生成正确的ARM指令集。以下是修正后的构建方法:
1. 设置正确的环境变量
export GOARCH=arm
export GOARM=5
export CGO_ENABLED=1
export CC=arm-linux-gnueabi-gcc # 使用你的Nuvoton交叉编译器
2. 修改构建命令
使用正确的目标架构参数:
GOARCH=arm GOARM=5 CGO_ENABLED=1 CC=arm-linux-gnueabi-gcc go build -buildmode=c-shared -o libversiontest.so versiontest.go
3. 验证目标架构
构建后检查生成的共享库架构:
file libversiontest.so
readelf -A libversiontest.so | grep -i tag
应该显示ARMv5相关的架构标识。
4. 完整的构建脚本示例
#!/bin/bash
export GOARCH=arm
export GOARM=5
export CGO_ENABLED=1
export CC=/path/to/your/arm-linux-gnueabi-gcc
# 构建共享库
go build -buildmode=c-shared -o libversiontest.so versiontest.go
# 构建测试程序
arm-linux-gnueabi-gcc -o testversion testversion.c -L. -lversiontest -Wl,-rpath,.
echo "构建完成"
5. Go代码优化建议
虽然你的示例代码很简单,但在实际使用中建议:
package main
/*
#include<stdio.h>
#include<stdint.h>
#define RETURN_OK 1
typedef struct{
char version[256];
uint16_t size;
}VersionInfo;
*/
import "C"
import (
"fmt"
"runtime"
)
//export GetVersion
func GetVersion() C.int {
fmt.Printf("GetVersion called, Go version: %s\n", runtime.Version())
return C.RETURN_OK
}
func main() {
// 空main函数用于构建共享库
}
关键点说明
- GOARM=5 确保生成ARMv5兼容的代码
- 正确的交叉编译器 必须使用与目标平台匹配的C工具链
- 运行时兼容性 Go运行时必须为目标架构正确编译
树莓派2 Model B使用ARMv7架构,支持更广泛的指令集,因此相同的代码在那里可以正常运行。对于ARMv5目标平台,必须明确指定架构版本。
如果问题仍然存在,检查交叉编译工具链是否完全支持ARMv5TEJ架构,并验证生成的汇编代码中不包含ARMv5不支持的指令。

