Skip to content

Commit 62f0448

Browse files
lersekjensdrenhaus
authored andcommitted
mkuimage: advise GOAMD64=v1 for GOARCH=amd64
If the user builds an initrd for amd64, and their golang toolchain is configured for an amd64 ABI version greater than 1, then issue a warning. Binaries built for GOAMD64=v2+ may crash with an #UD exception on old CPUs, or exit immediately (gracefully) with an error message like "This program can only be run on AMD64 processors with v2 microarchitecture support" [1]. Such a dysfunctional "init" (gobusybox) binary renders a u-root initrd effectively unbootable. qemu-system-x86_64 with no particular "-cpu" option is affected by this, for example. GOAMD64 defaults to v1 [2], but it may be overridden in at least three places (in increasing order of importance): - the system-wide "go.env" file, such as "/usr/lib/golang/go.env", - the user's "go.env" file, such as "$HOME/.config/go/env", - the env var GOAMD64. For example, RHEL9 and its derivative distros set GOAMD64 to v2 in "/usr/lib/golang/go.env". It seems like the canonical way for deducing GOAMD64 programmatically is to invoke "go env GOAMD64" [3]. [1] golang/go@8c8baad927b2 [2] https://go.dev/wiki/MinimumRequirements#amd64 [3] golang/go#72791 Signed-off-by: Laszlo Ersek <[email protected]>
1 parent fda9d6a commit 62f0448

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,33 @@ $ mkuimage -files "$HOME/hello.ko:etc/hello.ko" -files "$HOME/hello2.ko:etc/hell
260260
$ qemu-system-x86_64 -kernel /boot/vmlinuz-$(uname -r) -initrd /tmp/initramfs.linux_amd64.cpio
261261
```
262262

263+
## AMD64 Architecture Level
264+
265+
Before building for AMD64, verify that the command
266+
267+
```shell
268+
go env GOAMD64
269+
```
270+
271+
prints `v1`. A [`GOAMD64` setting](https://go.dev/wiki/MinimumRequirements#amd64)
272+
of any higher version may produce such binaries that don't execute on old AMD64
273+
processors (including the default CPU model of QEMU).
274+
275+
`GOAMD64` can be reset to `v1` with one of the following methods:
276+
277+
* through the `GOAMD64` environment variable:
278+
279+
```shell
280+
export GOAMD64=v1
281+
```
282+
283+
* through `go env` (only takes effect if the `GOAMD64` environment variable
284+
is not set):
285+
286+
```shell
287+
go env -w GOAMD64=v1
288+
```
289+
263290
## Cross Compilation (targeting different architectures and OSes)
264291

265292
Just like standard Go tooling, cross compilation is easy and supported.

uimage/mkuimage/cmd.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package mkuimage
66

77
import (
8+
"bytes"
89
"errors"
910
"fmt"
1011
"os"
12+
"os/exec"
1113
"runtime"
1214
"strings"
1315

16+
"github.com/u-root/gobusybox/src/pkg/golang"
1417
"github.com/u-root/mkuimage/uimage"
1518
"github.com/u-root/mkuimage/uimage/builder"
1619
"github.com/u-root/mkuimage/uimage/templates"
@@ -56,6 +59,32 @@ func uimageOpts(l *llog.Logger, m []uimage.Modifier, tpl *templates.Templates, f
5659
return uimage.OptionsFor(append(m, more...)...)
5760
}
5861

62+
func checkAmd64Level(l *llog.Logger, env *golang.Environ) {
63+
if env.GOARCH != "amd64" {
64+
return
65+
}
66+
67+
// Looking for "amd64.v2" in "env.ToolTags" is unreliable; see
68+
// <https://github.com/golang/go/issues/72791>. Invoke "go env" instead.
69+
var bad string
70+
abiLevel, err := exec.Command("go", "env", "GOAMD64").Output()
71+
if err == nil {
72+
if bytes.Equal(abiLevel, []byte("v1\n")) {
73+
return
74+
}
75+
bad = "is not"
76+
} else {
77+
if exerr, isExErr := err.(*exec.ExitError); isExErr {
78+
l.Warnf("\"go env\" failed: %s", exerr.Stderr)
79+
} else {
80+
l.Warnf("couldn't execute \"go env\": %s", err)
81+
}
82+
bad = "may not be"
83+
}
84+
l.Warnf("GOAMD64 %s set to v1; on older CPUs, binaries built into " +
85+
"the initrd may crash or refuse to run.", bad)
86+
}
87+
5988
// CreateUimage creates a uimage with the given base modifiers and flags, using args as the list of commands.
6089
func CreateUimage(l *llog.Logger, base []uimage.Modifier, tf *TemplateFlags, f *Flags, args []string) error {
6190
tpl, err := tf.Get()
@@ -95,6 +124,8 @@ func CreateUimage(l *llog.Logger, base []uimage.Modifier, tf *TemplateFlags, f *
95124
l.Warnf("GOOS is not linux. Did you mean to set GOOS=linux?")
96125
}
97126

127+
checkAmd64Level(l, env);
128+
98129
v, err := env.Version()
99130
if err != nil {
100131
l.Infof("Could not get environment's Go version, using runtime's version: %v", err)

0 commit comments

Comments
 (0)