Go程序的交叉编译是每一位GO程序猿必须掌握的知识点。但是我没有。
在昨天,我编译了一个项目,生成了二进制,放到目标机器上,执行报错,错误如下:
bash: ./api.linux: cannot execute binary file: Exec format error
提示很明显,可执行文件格式错误,不能执行的二进制文件。
根据经验,直接定位到了,是目标机器的内核跟我的Macbook Pro不匹配;
接着在目标系统(Amazon Linux)下执行:
uname -a
结果是:
amzn2023.aarch64 #1 SMP Tue Jan 2 23:50:25 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
明显跟我的inter芯片的x86_64架构不一样,想到了交叉编译,没什么了不起。
此时我在AI 的指导下,开启了一系列的蛇皮操作
1. 安装 Amazon Linux 的交叉编译工具链: Amazon Linux 系统使用了 musl libc,因此我们需要安装相应的工具链。
$ brew tap aws/tap
$ brew install aws-sam-cli
*安装aws-sam-cli就曲折了很久,好几个包无法访问到。
2. 设置环境变量: 设置环境变量,以便 Go 能够找到 Amazon Linux 的交叉编译工具链。
$ export CGO_ENABLED=0
$ export GOOS=linux
$ export GOARCH=amd64
$ export GO111MODULE=on
3.执行交叉编译
go build -o output_binary_name
将生成的二进制文件,复制到目标系统。
进行测试,继续报错。方法不可行。
怀疑是否是Amazon Linux操作系统执行不了golang 二进制文件,于是我开启了这个小小测试:
第一步:我在目标机器上,复制了本地的一段测试代码。
package main
import (
"fmt"
"math/rand"
"strings"
"time"
)
func main() {
//测试去除空字符串
var a = " a "
var b = " a b "
a = strings.TrimSpace(a)
b = strings.TrimSpace(b)
fmt.Printf("%#v",a)
fmt.Printf("%#v",b)
random := rand.New(rand.NewSource(time.Now().UnixNano()))
x := random.Uint64()
y := random.Uint64()
fmt.Println("x:", x)
fmt.Println("y:", y)
if x >= y {
return
}
defer func() {
fmt.Println("enter defer")
}()
fmt.Println("func end line")
}
编译执行,正常。排除了目标系统执行不了二进制文件的可能。
第二步:接着将我的项目打包上传到了目标系统。进行编译,执行。
结果是正常的!
现在开始蠢蠢怀疑Amazon Linux对GO程序的编译有特殊操作,不管了,程序可以跑即可,后续有时间再查。
今天得空了,我想搞明白这个问题。
因为项目文件大且依赖本地包,移到目标机器的步骤较为复杂,根本没有效率。
于是我就决定开启一个本地Amazon Linux (叫它模拟环境)本地docker镜像,做一个本地文件夹的挂载。
version: '3'
# 定义自定义网络
networks:
docker_net:
external: true
services:
aws-linux2:
image: amazonlinux:2
build:
context: .
dockerfile: Dockerfile
volumes:
- ./data:/data
networks:
- docker_net
environment:
- MY_ENV_VARIABLE=value
command: tail -f /dev/null
我做了一个猜测,系统一致,编译应该可以一致,编译出来的二进制可以在目标系统直接用。
结果:
在这个模拟环境里,进行编译,生成了二进制文件,放到目标系统里执行,还是同样的报错:
bash: ./api.linux: cannot execute binary file: Exec format error
然后我就比对了模拟环境和目标系统的内核:
模拟环境:UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
目标系统:UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
顿时我就醒悟了:
- 我本地的docker,内核当然也是x86_64啊
- 我擦,aarch64 这并不是amd64架构啊。是我们昨天想当然了。
顿悟之后当然就简单了我,将我的编译参数指定更改为:
GOARCH=arm64 GOOS=linux go build -o api.linux .
将编译好的二进制,复制到目标系统,执行成功。到此疑团全部揭开。
总结:
交叉编译只跟目标机器内核有关,跟目标机器的操作系统无关。
最后:
附上GOARCH 设置值与内核对应关系。
amd64 | x86-64 或 x64 |
386 | 32 位 x86 架构 |
arm | ARM 架构 |
arm64 | 64 位 ARM 架构,也称为 AArch64。 |
ppc64 | 64 位 PowerPC 架构 |
ppc64le | 64 位 Little-Endian PowerPC |
s390x | IBM System z 的 64 位版本,也称为 s390x |
留一个问题给你回答, 请问编译windows系统的exe文件,GOARCH该使用什么值,请回答到评论区。