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 goversion

库代码如下:

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

5 回复

你好,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函数用于构建共享库
}

关键点说明

  1. GOARM=5 确保生成ARMv5兼容的代码
  2. 正确的交叉编译器 必须使用与目标平台匹配的C工具链
  3. 运行时兼容性 Go运行时必须为目标架构正确编译

树莓派2 Model B使用ARMv7架构,支持更广泛的指令集,因此相同的代码在那里可以正常运行。对于ARMv5目标平台,必须明确指定架构版本。

如果问题仍然存在,检查交叉编译工具链是否完全支持ARMv5TEJ架构,并验证生成的汇编代码中不包含ARMv5不支持的指令。

回到顶部