golang使用cgo编译报collect2: error: ld returned 1 exit status求助
你核查gcc和ld的方向是正确的,这类链接阶段未定义引用报错本质是链接器无法定位符号实现,和编译链工具行为、依赖库配置的相关性远高于Go版本本身。
排查思路
- 首先核查
libXYZ.so本身的依赖完整性- 执行
ldd -r src/foobar/lib/libXYZ.so检查该共享库的依赖是否完整,重点关注输出中标记为not found的依赖库,同时输出的未定义符号列表可以确认feenableexcept、floor、memoMalloc等报错符号是否在共享库层面就处于未定义状态。 - 回溯
libXYZ.so的编译参数:floor属于数学库,编译时需要加-lm参数;feenableexcept是glibc的GNU扩展函数,编译时需要定义_GNU_SOURCE宏并正确链接libc;memoMalloc/memoFree属于自定义函数,需要确认对应实现库是否已链接到libXYZ.so,或者是否在最终链接时声明。
- 执行
- 核查cgo的链接参数配置
- 检查Go代码中
#cgo LDFLAGS的配置,重点确认链接顺序:高版本gcc/ld对链接顺序的要求更严格,依赖库必须放在被依赖对象的后方,例如libXYZ.so依赖数学库,那-lm参数必须写在-lXYZ的后面才能被正确识别。 - 对比正常构建和失败构建环境的cgo默认参数,执行
go env CGO_LDFLAGS CGO_CFLAGS查看差异,高版本Go镜像可能默认开启了-Wl,--no-undefined参数,该参数会要求链接阶段所有符号都有对应实现,旧版本默认允许共享库携带未定义符号,等最终运行时再解析,开启后就会直接触发报错。
- 检查Go代码中
- 核查编译链工具的行为差异
- 对比不同环境下ld的默认参数,执行
ld --verbose查看默认开启的选项差异,高版本binutils的ld默认会开启--as-needed、--no-allow-shlib-undefined等参数,也是导致旧构建流程失效的常见原因。 - 可以临时在cgo的LDFLAGS中添加
-Wl,--allow-shlib-undefined尝试构建,如果构建成功就可以确认是ld默认参数变更导致的问题,注意该参数仅用于验证,不要作为最终解决方案,避免后续运行时出现符号找不到的崩溃。
- 对比不同环境下ld的默认参数,执行
- 核查运行环境的底层系统差异
- 查看不同版本golang镜像的底层发行版版本,例如golang:1.16.6基于Debian Buster,而golang:1.16.7可能切换到了Debian Bullseye,底层的glibc版本、binutils版本都存在较大差异,默认的编译链接规则也会有调整,和你在Ubuntu 20.04上复现问题的表现一致。
内容的提问来源于stack exchange,提问作者Matthias Wiedemann
相关产品推荐
相关产品推荐

