在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

1 回复

更多关于在AIX 7.2上通过静态库方式将Golang项目编译为共享库的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在AIX 7.2上通过静态库方式创建Go共享库确实面临挑战,因为Go 1.16的c-shared模式不支持AIX平台。以下是基于您当前方法的解决方案:

关键问题分析:

  1. Go的c-archive默认生成非位置无关代码,而AIX共享库需要PIC
  2. AIX的线程本地存储(TLS)模型与Go运行时存在冲突

解决方案步骤:

  1. 修改Go源码以支持PIC编译:
// 在sharedLibTest.go中添加编译指示
// #cgo CFLAGS: -maix64 -fPIC
// #cgo LDFLAGS: -Wl,-bbigtoc
import "C"
  1. 使用外部链接模式并指定TLS模型:
go build -buildmode=c-archive \
         -ldflags="-linkmode=external -extldflags='-Wl,-bnoobjreorder -Wl,-bnoentry -Wl,-bexport:symbols.export'" \
         -o sharedLibTest.a sharedLibTest.go
  1. 创建正确的导出符号文件:
# 需要导出Go运行时初始化函数
cat > symbols.export << EOF
MyPackage_Init
MyPackage_Create
_rt0_ppc64_aix
runtime.rt0_go
EOF
  1. 编译共享库时指定正确的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
  1. 创建AIX格式的归档文件:
# 使用AIX特定的归档格式
ar -X64 -q libsharedLibTest.a libsharedLibTest.so
ranlib libsharedLibTest.a
  1. 编译测试程序:
gcc -maix64 -fPIC \
    -Wl,-bnoobjreorder \
    -Wl,-brtl \
    -L. \
    -o testapp \
    main.c \
    -lsharedLibTest \
    -lpthread
  1. 设置运行时环境并执行:
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共享库。

回到顶部