使用 Golang 的交叉编译
创始人
2024-03-02 07:19:54
0

走出舒适区,我了解了 Go 的交叉编译功能。

在 Linux 上测试软件时,我使用各种架构的服务器,例如 Intel、AMD、Arm 等。当我 分配了一台满足我的测试需求的 Linux 机器,我仍然需要执行许多步骤:

  1. 下载并安装必备软件
  2. 验证构建服务器上是否有新的测试软件包
  3. 获取并设置依赖软件包所需的 yum 仓库
  4. 下载并安装新的测试软件包(基于步骤 2)
  5. 获取并设置必需的 SSL 证书
  6. 设置测试环境,获取所需的 Git 仓库,更改配置,重新启动守护进程等
  7. 做其他需要做的事情

用脚本自动化

这些步骤非常常规,以至于有必要对其进行自动化并将脚本保存到中央位置(例如文件服务器),在需要时可以在此处下载脚本。为此,我编写了 100-120 行的 Bash shell 脚本,它为我完成了所有配置(包括错误检查)。这个脚本通过以下方式简化了我的工作流程:

  1. 配置新的 Linux 系统(支持测试的架构)
  2. 登录系统并从中央位置下载自动化 shell 脚本
  3. 运行它来配置系统
  4. 开始测试

学习 Go 语言

我想学习 Go 语言 有一段时间了,将我心爱的 Shell 脚本转换为 Go 程序似乎是一个很好的项目,可以帮助我入门。它的语法看起来很简单,在尝试了一些测试程序后,我开始着手提高自己的知识并熟悉 Go 标准库。

我花了一个星期的时间在笔记本电脑上编写 Go 程序。我经常在我的 x86 服务器上测试程序,清除错误并使程序健壮起来,一切都很顺利。

直到完全转换到 Go 程序前,我继续依赖自己的 shell 脚本。然后,我将二进制文件推送到中央文件服务器上,以便每次配置新服务器时,我要做的就是获取二进制文件,将可执行标志打开,然后运行二进制文件。我对早期的结果很满意:

$ wget http://file.example.com//bins/prepnode
$ chmod  +x ./prepnode
$ ./prepnode

然后,出现了一个问题

第二周,我从资源池中分配了一台新的服务器,像往常一样,我下载了二进制文件,设置了可执行标志,然后运行二进制文件。但这次它出错了,是一个奇怪的错误:

$ ./prepnode
bash: ./prepnode: cannot execute binary file: Exec format error
$

起初,我以为可能没有成功设置可执行标志。但是,它已按预期设置:

$ ls -l prepnode
-rwxr-xr-x. 1 root root 2640529 Dec 16 05:43 prepnode

发生了什么事?我没有对源代码进行任何更改,编译没有引发任何错误或警告,而且上次运行时效果很好,因此我仔细查看了错误消息 format error

我检查了二进制文件的格式,一切看起来都没问题:

$ file prepnode
prepnode: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

我迅速运行了以下命令,识别所配置的测试服务器的架构以及二进制试图运行的平台。它是 Arm64 架构,但是我编译的二进制文件(在我的 x86 笔记本电脑上)生成的是 x86-64 格式的二进制文件:

$ uname -m
aarch64

脚本编写人员的编译第一课

在那之前,我从未考虑过这种情况(尽管我知道这一点)。我主要研究脚本语言(通常是 Python)以及 Shell 脚本。在任何架构的大多数 Linux 服务器上都可以使用 Bash Shell 和 Python 解释器。总之,之前一切都很顺利。

但是,现在我正在处理 Go 这种编译语言,它生成可执行的二进制文件。编译后的二进制文件由特定架构的 指令码 或汇编指令组成,这就是为什么我收到格式错误的原因。由于 Arm64 CPU(运行二进制文件的地方)无法解释二进制文件的 x86-64 指令,因此它抛出错误。以前,shell 和 Python 解释器为我处理了底层指令码或特定架构的指令。

Go 的交叉编译

我检查了 Golang 的文档,发现要生成 Arm64 二进制文件,我要做的就是在运行 go build 命令编译 Go 程序之前设置两个环境变量。

GOOS 指的是操作系统,例如 Linux、Windows、BSD 等,而 GOARCH 指的是要在哪种架构上构建程序。

$ env GOOS=linux GOARCH=arm64 go build -o prepnode_arm64

构建程序后,我重新运行 file 命令,这一次它显示的是 ARM AArch64,而不是之前显示的 x86。因此,我在我的笔记本上能为不同的架构构建二进制文件。

$ file prepnode_arm64
prepnode_arm64: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped

我将二进制文件从笔记本电脑复制到 ARM 服务器上。现在运行二进制文件(将可执行标志打开)不会产生任何错误:

$ ./prepnode_arm64  -h
Usage of ./prepnode_arm64:
  -c    Clean existing installation
  -n    Do not start test run (default true)
  -s    Use stage environment, default is qa
  -v    Enable verbose output

其他架构呢?

x86 和 Arm 是我测试软件所支持的 5 种架构中的两种,我担心 Go 可能不会支持其它架构,但事实并非如此。你可以查看 Go 支持的架构:

$ go tool dist list

Go 支持多种平台和操作系统,包括:

  • AIX
  • Android
  • Darwin
  • Dragonfly
  • FreeBSD
  • Illumos
  • JavaScript
  • Linux
  • NetBSD
  • OpenBSD
  • Plan 9
  • Solaris
  • Windows

要查找其支持的特定 Linux 架构,运行:

$ go tool dist list | grep linux

如下面的输出所示,Go 支持我使用的所有体系结构。尽管 x86_64 不在列表中,但 AMD64 兼容 x86-64,所以你可以生成 AMD64 二进制文件,它可以在 x86 架构上正常运行:

$ go tool dist list | grep linux
linux/386
linux/amd64
linux/arm
linux/arm64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x

处理所有架构

为我测试的所有体系结构生成二进制文件,就像从我的 x86 笔记本电脑编写一个微小的 shell 脚本一样简单:

#!/usr/bin/bash
archs=(amd64 arm64 ppc64le ppc64 s390x)

for arch in ${archs[@]}
do
        env GOOS=linux GOARCH=${arch} go build -o prepnode_${arch}
done
$ file prepnode_*
prepnode_amd64:   ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=y03MzCXoZERH-0EwAAYI/p909FDnk7xEUo2LdHIyo/V2ABa7X_rLkPNHaFqUQ6/5p_q8MZiR2WYkA5CzJiF, not stripped
prepnode_arm64:   ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, Go BuildID=q-H-CCtLv__jVOcdcOpA/CywRwDz9LN2Wk_fWeJHt/K4-3P5tU2mzlWJa0noGN/SEev9TJFyvHdKZnPaZgb, not stripped
prepnode_ppc64:   ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, Go BuildID=DMWfc1QwOGIq2hxEzL_u/UE-9CIvkIMeNC_ocW4ry/r-7NcMATXatoXJQz3yUO/xzfiDIBuUxbuiyaw5Goq, not stripped
prepnode_ppc64le: ELF 64-bit LSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, Go BuildID=C6qCjxwO9s63FJKDrv3f/xCJa4E6LPVpEZqmbF6B4/Mu6T_OR-dx-vLavn1Gyq/AWR1pK1cLz9YzLSFt5eU, not stripped
prepnode_s390x:   ELF 64-bit MSB executable, IBM S/390, version 1 (SYSV), statically linked, Go BuildID=faC_HDe1_iVq2XhpPD3d/7TIv0rulE4RZybgJVmPz/o_SZW_0iS0EkJJZHANxx/zuZgo79Je7zAs3v6Lxuz, not stripped

现在,每当配置一台新机器时,我就运行以下 wget 命令下载特定体系结构的二进制文件,将可执行标志打开,然后运行:

$ wget http://file.domain.com//bins/prepnode_
$ chmod +x ./prepnode_
$ ./prepnode_

为什么?

你可能想知道,为什么我没有坚持使用 shell 脚本或将程序移植到 Python 而不是编译语言上来避免这些麻烦。所以有舍有得,那样的话我不会了解 Go 的交叉编译功能,以及程序在 CPU 上执行时的底层工作原理。在计算机中,总要考虑取舍,但绝不要让它们阻碍你的学习。


via: https://opensource.com/article/21/1/go-cross-compiling

作者:Gaurav Kamathe 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

“漂亮饭”占领三里屯
文|潮汐商业评论 听说三里屯新开了不少漂亮饭餐厅,生意很火,想坐...
2025-07-02 19:11:33
Google 这款免费 A...
在不久前结束的 Google I/O 上,Google 悄咪咪地在...
2025-06-16 07:41:52
谷歌突袭发布AI应用,无需...
本播客由扣子空间(coze.cn)一键生成 整理|冬梅 近日,据...
2025-06-02 16:42:20
Centos7 XFS(d...
在k8s的道路上我们都是小白,每天启动虚机都会遇到各...
2025-06-01 19:21:59
Golang每日一练(le...
目录 34. 查找元素首末位置 Find-first-and-la...
2025-06-01 07:25:54
【Java|golang】...
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 n...
2025-06-01 01:57:50

热门资讯

Helix:高级 Linux ... 说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。这并不意味着没有其他...
使用 KRAWL 扫描 Kub... 用 KRAWL 脚本来识别 Kubernetes Pod 和容器中的错误。当你使用 Kubernet...
JStock:Linux 上不... 如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受...
通过 SaltStack 管理... 我在搜索Puppet的替代品时,偶然间碰到了Salt。我喜欢puppet,但是我又爱上Salt了:)...
Epic 游戏商店现在可在 S... 现在可以在 Steam Deck 上运行 Epic 游戏商店了,几乎无懈可击! 但是,它是非官方的。...
《Apex 英雄》正式可在 S... 《Apex 英雄》现已通过 Steam Deck 验证,这使其成为支持 Linux 的顶级多人游戏之...
如何在 Github 上创建一... 学习如何复刻一个仓库,进行更改,并要求维护人员审查并合并它。你知道如何使用 git 了,你有一个 G...
2024 开年,LLUG 和你... Hi,Linuxer,2024 新年伊始,不知道你是否已经准备好迎接新的一年~ 2024 年,Lin...
什么是 KDE Connect... 什么是 KDE Connect?它的主要特性是什么?它应该如何安装?本文提供了基本的使用指南。科技日...
Opera 浏览器内置的 VP... 昨天我们报道过 Opera 浏览器内置了 VPN 服务,用户打开它可以防止他们的在线活动被窥视。不过...