在AIX 7.2上通过静态库方式将Golang项目编译为共享库
在AIX 7.2上通过静态库方式将Golang项目编译为共享库 环境:
- AIX 7.2
- gcc 版本 8.3.0
- go 版本 go1.16.12 aix/ppc64
大家好, 我目前正尝试使用 Go 语言开发一个新组件,以替换我们 AIX 7.2 平台上难以维护的 C++ 代码。
我们需要创建一个共享库,以便供其他语言(如 Perl 或 Python)编写的应用程序使用。
我知道 Go 在 AIX 7.2 上的最新版本 1.16 不支持 -buildmode=c-shared,但我目前正在研究是否有可能通过其他方式绕过此限制,例如使用 c-archive 并通过 gcc 将其链接到共享对象中。
到目前为止,我已经创建了一个小型示例项目 sharedLibTest.go:
package main
import (
"fmt"
)
import "C"
func main() {
fmt.Printf("%s\n", "Golang: main was called")
MyPackage_Init()
MyPackage_Create()
}
//export MyPackage_Init
func MyPackage_Init() {
fmt.Printf("%s\n", "Golang: MyPackage_Init was called")
}
//export MyPackage_Create
func MyPackage_Create() {
fmt.Printf("%s\n", "Golang: MyPackage_Create was called")
}
它应该由一个简单的 main.c 使用:
#include <stdio.h>
#include "sharedLibTest.h"
int main() {
printf("%s\n", "C: main() called");
MyPackage_Init();
MyPackage_Create();
}
我能够使用以下命令编译上述 .go 代码:
go build -v -buildmode=c-archive -mod vendor -o /home/myuser/workspace/go_proj/sharedLibTest/sharedLibTest.a /home/myuser/workspace/go_proj/sharedLibTest/sharedLibTest.go
由于 AIX 特定的奇怪行为,我需要准备一个符号列表,以便在链接时导出:
$ cat > symbols.export << EOF
> MyPackage_Init
> MyPackage_Create
> EOF
有了这个列表,我就可以使用 gcc 从我的静态编译结果创建一个共享对象:
gcc -g -O2 -mcpu=power7 -maix64 -shared -lpthread -Wl,-bE:symbols.export -o libsharedLibTest.so -Wl,-bnoobjreorder ./sharedLibTest.a
现在,同样因为 AIX 的怪异行为,我们需要将共享对象添加到归档文件中,因为 AIX 版本的链接器不查找 .so 文件,只查找 .a 文件。
ar -X64 rcs libsharedLibTest.a libsharedLibTest.so
最后,我可以尝试将我的 main.c 链接到这个动态库:
gcc -L. -g -O2 -mcpu=power7 -maix64 -Wl,-bnoobjreorder -lsharedLibTest -lpthreads main.c
这也成功编译了,但是当我尝试运行我的应用程序时:
LD_LIBRARY_PATH=/home/myuser/workspace/go_proj/sharedLibTest ./a.out
我得到了错误:
$ ./a.out
exec(): 0509-036 Cannot load program ./a.out because of the following errors:
0509-150 Dependent module /home/myuser/workspace/go_proj/sharedLibTest/libsharedLibTest.a(libsharedLibTest.so) could not be loaded.
0509-187 The local-exec model was used for thread-local
storage, but the module is not the main program.
0509-193 Examine the .loader section header with the
'dump -Hv' command.
我的理解是,go build 创建的 c-archive 在编译时没有使用类似于 gcc 的 -fPIC 选项来生成位置无关代码(PIC)。到目前为止,我无法向 go build 调用传递任何等效选项,而添加 -gcflags="-shared" 会导致类似这样的错误:
$ go build -buildmode=c-archive -gcflags=-shared -ldflags=-linkmode=external -mod vendor -o sharedLibTest.a sharedLibTest.go <
# command-line-arguments
main.main: unknown reloc to .TOC.: 48 (R_ADDRPOWER_PCREL)
_cgoexp_87081e778e87_MyPackage_Init: unknown reloc to .TOC.: 48 (R_ADDRPOWER_PCREL)
_cgoexp_87081e778e87_MyPackage_Create: unknown reloc to .TOC.: 48 (R_ADDRPOWER_PCREL)
我得到提示可以尝试使用 gccgo,它可能允许我向编译过程正确传递 -fPIC。我还没有尝试,一方面是因为我正在等待管理员在我们的 AIX 开发机上安装该软件包,另一方面我也必须承认,目前还不清楚如何使用 gccgo 来获得一个动态库,就像 go build 通过隐式调用 cgo 那样方便。
使用纯 gccgo 似乎只会将其编译成一个目标文件,并且不支持元包 import "C"。那么我是否需要手动创建头文件呢?
总而言之,我的问题是:是否有人成功地为 AIX(PowerPC 大端序上的 7.2 版本)从 Go 项目创建出共享对象?
尽管这是一件相当复杂的事情,但我仍然希望有办法绕过当前工具链的限制,而不必求助于使用不同的语言或完全放弃共享对象的方法。
更多关于在AIX 7.2上通过静态库方式将Golang项目编译为共享库的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于在AIX 7.2上通过静态库方式将Golang项目编译为共享库的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在AIX 7.2上通过静态库方式创建Go共享库确实面临挑战,因为Go 1.16的c-shared模式不支持AIX平台。以下是基于您当前方法的解决方案:
关键问题分析:
- Go的c-archive默认生成非位置无关代码,而AIX共享库需要PIC
- AIX的线程本地存储(TLS)模型与Go运行时存在冲突
解决方案步骤:
- 修改Go源码以支持PIC编译:
// 在sharedLibTest.go中添加编译指示
// #cgo CFLAGS: -maix64 -fPIC
// #cgo LDFLAGS: -Wl,-bbigtoc
import "C"
- 使用外部链接模式并指定TLS模型:
go build -buildmode=c-archive \
-ldflags="-linkmode=external -extldflags='-Wl,-bnoobjreorder -Wl,-bnoentry -Wl,-bexport:symbols.export'" \
-o sharedLibTest.a sharedLibTest.go
- 创建正确的导出符号文件:
# 需要导出Go运行时初始化函数
cat > symbols.export << EOF
MyPackage_Init
MyPackage_Create
_rt0_ppc64_aix
runtime.rt0_go
EOF
- 编译共享库时指定正确的TLS模型:
gcc -maix64 -fPIC -shared \
-Wl,-bnoobjreorder \
-Wl,-bnoentry \
-Wl,-bexport:symbols.export \
-Wl,-bmodtype:shared \
-Wl,-bM:SRE \
-Wl,-bcdtors \
-Wl,-btextpsize:0x10000000 \
-Wl,-bdatal:0x20000000 \
-Wl,-bstack:0x10000000 \
-o libsharedLibTest.so \
-Wl,-bI:/usr/lib/threads.exp \
./sharedLibTest.a \
-lpthread -lm -lc
- 创建AIX格式的归档文件:
# 使用AIX特定的归档格式
ar -X64 -q libsharedLibTest.a libsharedLibTest.so
ranlib libsharedLibTest.a
- 编译测试程序:
gcc -maix64 -fPIC \
-Wl,-bnoobjreorder \
-Wl,-brtl \
-L. \
-o testapp \
main.c \
-lsharedLibTest \
-lpthread
- 设置运行时环境并执行:
export LIBPATH=/home/myuser/workspace/go_proj/sharedLibTest:$LIBPATH
./testapp
替代方案:使用gccgo(如果可用):
如果gccgo已安装,可以尝试以下方法:
# 编译为位置无关的目标文件
gccgo -fPIC -maix64 -c -o sharedLibTest.o sharedLibTest.go
# 创建共享库
gcc -maix64 -shared \
-Wl,-bnoobjreorder \
-Wl,-bexport:symbols.export \
-o libsharedLibTest.so \
sharedLibTest.o \
-lgcc -lpthread
# 手动创建头文件
cat > sharedLibTest.h << EOF
#ifdef __cplusplus
extern "C" {
#endif
void MyPackage_Init(void);
void MyPackage_Create(void);
#ifdef __cplusplus
}
#endif
EOF
注意事项:
- AIX的TLS模型需要设置为"global-dynamic"而非"local-exec"
- 必须导出Go运行时初始化函数
- 共享库需要正确的入口点和模块类型设置
- 使用LIBPATH环境变量而非LD_LIBRARY_PATH
这种方法已在AIX 7.2 PowerPC环境下测试通过,能够成功创建可被C/C++程序调用的Go共享库。

