From 513a964696fe813c3f027bd43da7c8ffd0ae98a2 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 23 Jan 2023 20:36:04 -0600 Subject: [PATCH 1/5] Add support for arm64 hosts This adds support for arm64 host systems, removing the previously present build flags & adding some architecture detection in Machine instead. Signed-off-by: Ryan Gonzalez --- backend.go | 4 ++-- backend_qemu.go | 26 ++++++++++++++++++++++---- backend_uml.go | 9 +++++++-- machine.go | 46 +++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/backend.go b/backend.go index 134ca01..d394678 100644 --- a/backend.go +++ b/backend.go @@ -1,5 +1,5 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux +// +build linux package fakemachine diff --git a/backend_qemu.go b/backend_qemu.go index db4425a..cd56bb7 100644 --- a/backend_qemu.go +++ b/backend_qemu.go @@ -1,5 +1,5 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux +// +build linux package fakemachine @@ -35,8 +35,18 @@ func (b qemuBackend) Supported() (bool, error) { return true, nil } +var qemuBinaryNames = map[Arch]string{ + Amd64: "qemu-system-x86_64", + Arm64: "qemu-system-aarch64", +} + func (b qemuBackend) QemuPath() (string, error) { - return exec.LookPath("qemu-system-x86_64") + binary, ok := qemuBinaryNames[b.machine.arch] + if !ok { + return "", fmt.Errorf("unsupported arch for qemu: %s", b.machine.arch) + } + + return exec.LookPath(binary) } func (b qemuBackend) KernelRelease() (string, error) { @@ -173,7 +183,7 @@ func (b qemuBackend) StartQemu(kvm bool) (bool, error) { } memory := fmt.Sprintf("%d", m.memory) numcpus := fmt.Sprintf("%d", m.numcpus) - qemuargs := []string{"qemu-system-x86_64", + qemuargs := []string{qemuBinaryNames[m.arch], "-smp", numcpus, "-m", memory, "-kernel", kernelPath, @@ -187,6 +197,14 @@ func (b qemuBackend) StartQemu(kvm bool) (bool, error) { "-enable-kvm") } + if m.arch == Arm64 { + qemuargs = append(qemuargs, "-machine", "virt") + if !kvm { + // QEMU uses a 32-bit CPU by default, so override it. + qemuargs = append(qemuargs, "-cpu", "cortex-a53") + } + } + kernelargs := []string{"console=ttyS0", "panic=-1", "systemd.unit=fakemachine.service"} diff --git a/backend_uml.go b/backend_uml.go index 3f0bc6e..c185817 100644 --- a/backend_uml.go +++ b/backend_uml.go @@ -1,5 +1,5 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux +// +build linux package fakemachine @@ -27,6 +27,11 @@ func (b umlBackend) Name() string { } func (b umlBackend) Supported() (bool, error) { + // only support amd64 + if b.machine.arch != Amd64 { + return false, fmt.Errorf("unsupported arch: %s", b.machine.arch) + } + // check the kernel exists if _, err := b.KernelPath(); err != nil { return false, err diff --git a/machine.go b/machine.go index 9f70239..cba3036 100644 --- a/machine.go +++ b/machine.go @@ -1,5 +1,5 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux +// +build linux package fakemachine @@ -142,6 +142,18 @@ func realDir(path string) (string, error) { return filepath.Dir(p), nil } +type Arch string + +const ( + Amd64 Arch = "amd64" + Arm64 = "arm64" +) + +var archMap = map[string]Arch{ + "amd64": Amd64, + "arm64": Arm64, +} + type mountPoint struct { hostDirectory string machineDirectory string @@ -155,6 +167,7 @@ type image struct { } type Machine struct { + arch Arch backend backend mounts []mountPoint count int @@ -181,6 +194,11 @@ func NewMachineWithBackend(backendName string) (*Machine, error) { var err error m := &Machine{memory: 2048, numcpus: runtime.NumCPU()} + var ok bool + if m.arch, ok = archMap[runtime.GOARCH]; !ok { + return nil, fmt.Errorf("unsupported arch %s", runtime.GOARCH) + } + m.backend, err = newBackend(backendName, m) if err != nil { return nil, err @@ -667,21 +685,39 @@ func (m *Machine) startup(command string, extracontent [][2]string) (int, error) return -1, err } - /* Amd64 dynamic linker */ - err = w.CopyFile("/lib64/ld-linux-x86-64.so.2") + var dynamicLinker string + if m.arch == Arm64 { + // arm64 dynamic linker is in /lib: + // https://sourceware.org/bugzilla/show_bug.cgi?id=25129 + dynamicLinker = "/lib/ld-linux-aarch64.so.1" + } else { + dynamicLinker = "/lib64/ld-linux-x86-64.so.2" + } + + /* dynamic linker */ + err = w.CopyFile(dynamicLinker) if err != nil { return -1, err } /* C libraries */ - libraryDir, err := realDir("/lib64/ld-linux-x86-64.so.2") + libraryDir, err := realDir(dynamicLinker) if err != nil { return -1, err } + err = w.CopyFile(libraryDir + "/libc.so.6") + if err != nil && errors.Is(err, os.ErrNotExist) && m.arch == Arm64 && strings.HasSuffix(libraryDir, "/lib") { + // Because the dynamic linker is in /lib, on systems with a separate + // lib64, it might actually be a in a different folder from libc. In + // that case, try using the lib64 directory. + libraryDir += "64" + err = w.CopyFile(libraryDir + "/libc.so.6") + } if err != nil { return -1, err } + err = w.CopyFile(libraryDir + "/libresolv.so.2") if err != nil { return -1, err From a8405045221a097446d293256f36667f0ac01536 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 23 Jan 2023 20:36:04 -0600 Subject: [PATCH 2/5] cpio: Wrap errors with %w This lets callers distinguish the type of error that occurred. Signed-off-by: Ryan Gonzalez --- cpio/writerhelper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpio/writerhelper.go b/cpio/writerhelper.go index 72cce4d..eeef5d8 100644 --- a/cpio/writerhelper.go +++ b/cpio/writerhelper.go @@ -197,7 +197,7 @@ func (w *WriterHelper) CopyFileTo(src, dst string) error { f, err := os.Open(src) if err != nil { - return fmt.Errorf("open failed: %s - %v", src, err) + return fmt.Errorf("open failed: %s - %w", src, err) } defer f.Close() From a0da57a0802442f09882e9e9f4ef225c4857c68c Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 23 Jan 2023 20:36:04 -0600 Subject: [PATCH 3/5] Fix boot failures on merged-/usr systems These were resulting in the creation of /bin & /lib folderr, which broke the symlinks to /usr/{bin,lib}. Signed-off-by: Ryan Gonzalez --- machine.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/machine.go b/machine.go index cba3036..fde1f02 100644 --- a/machine.go +++ b/machine.go @@ -681,7 +681,7 @@ func (m *Machine) startup(command string, extracontent [][2]string) (int, error) } /* Ensure systemd-resolved is available */ - if _, err := os.Stat("/lib/systemd/systemd-resolved"); err != nil { + if _, err := os.Stat(prefix + "/lib/systemd/systemd-resolved"); err != nil { return -1, err } @@ -689,9 +689,9 @@ func (m *Machine) startup(command string, extracontent [][2]string) (int, error) if m.arch == Arm64 { // arm64 dynamic linker is in /lib: // https://sourceware.org/bugzilla/show_bug.cgi?id=25129 - dynamicLinker = "/lib/ld-linux-aarch64.so.1" + dynamicLinker = prefix + "/lib/ld-linux-aarch64.so.1" } else { - dynamicLinker = "/lib64/ld-linux-x86-64.so.2" + dynamicLinker = prefix + "/lib64/ld-linux-x86-64.so.2" } /* dynamic linker */ From 8b28222f2c62c8e4944433c87ac615ff9bd1f83b Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 23 Jan 2023 20:36:04 -0600 Subject: [PATCH 4/5] Fix broken busybox on recent Fedora releases Busybox on Fedora is now "statically" linked to musl but still requires the dynamic linker. Signed-off-by: Ryan Gonzalez --- machine.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/machine.go b/machine.go index fde1f02..1222ec5 100644 --- a/machine.go +++ b/machine.go @@ -685,23 +685,31 @@ func (m *Machine) startup(command string, extracontent [][2]string) (int, error) return -1, err } - var dynamicLinker string + var glibcDynamicLinker, muslDynamicLinker string if m.arch == Arm64 { // arm64 dynamic linker is in /lib: // https://sourceware.org/bugzilla/show_bug.cgi?id=25129 - dynamicLinker = prefix + "/lib/ld-linux-aarch64.so.1" + glibcDynamicLinker = prefix + "/lib/ld-linux-aarch64.so.1" + muslDynamicLinker = prefix + "/lib/ld-musl-aarch64.so.1" } else { - dynamicLinker = prefix + "/lib64/ld-linux-x86-64.so.2" + glibcDynamicLinker = prefix + "/lib64/ld-linux-x86-64.so.2" + muslDynamicLinker = prefix + "/lib64/ld-musl-x86_64.so.1" } - /* dynamic linker */ - err = w.CopyFile(dynamicLinker) + /* dynamic linkers */ + err = w.CopyFile(glibcDynamicLinker) if err != nil { return -1, err } + // Fedora's busybox needs musl + err = w.CopyFile(muslDynamicLinker) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return -1, err + } + /* C libraries */ - libraryDir, err := realDir(dynamicLinker) + libraryDir, err := realDir(glibcDynamicLinker) if err != nil { return -1, err } From 3eabc10755b7ece64ec70b6ecf8ce3cca244fed1 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 23 Jan 2023 20:36:04 -0600 Subject: [PATCH 5/5] Fix SSL failures on Fedora systems - SSL certs on Fedora is in /etc/pki - gnutls config used by wget is in /etc/crypto-policies Signed-off-by: Ryan Gonzalez --- machine.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/machine.go b/machine.go index 1222ec5..18babb1 100644 --- a/machine.go +++ b/machine.go @@ -217,10 +217,18 @@ func NewMachineWithBackend(backendName string) (*Machine, error) { if _, err := os.Stat("/etc/ca-certificates"); err == nil { m.AddVolume("/etc/ca-certificates") } + if _, err := os.Stat("/etc/pki"); err == nil { + m.AddVolume("/etc/pki") + } if _, err := os.Stat("/etc/ssl"); err == nil { m.AddVolume("/etc/ssl") } + // Mounts for gnutls (used by wget). + if _, err := os.Stat("/etc/crypto-policies"); err == nil { + m.AddVolume("/etc/crypto-policies") + } + // Dbus configuration if _, err := os.Stat("/etc/dbus-1"); err == nil { m.AddVolume("/etc/dbus-1")