3 min read

Docker ARM Container

Docker lets the embedded developer run Linux on ARM in a very convenient way using QEMU.

AdobeStock_385434674

Experimental Docker Engine

Configure the Docker engine for experimental work. Engine settings should appear in Docker Desktop as follows. Experimental changes from false to true.

{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": true,
  "features": {
    "buildkit": true
  }
}

This enables the buildx Docker commands for extended build capabilities including cross-platform support. Usage below.

Usage:  docker buildx [OPTIONS] COMMAND

Extended build capabilities with BuildKit

Options:
      --builder string   Override the configured builder instance

Management Commands:
  imagetools  Commands to work on images in registry

Commands:
  bake        Build from a file
  build       Start a build
  create      Create a new builder instance
  du          Disk usage
  inspect     Inspect current builder instance
  ls          List builder instances
  prune       Remove build cache
  rm          Remove a builder instance
  stop        Stop builder instance
  use         Set the current builder instance
  version     Show buildx version information

Run 'docker buildx COMMAND --help' for more information on a command.

Buildx Plugin by Docker Inc.

After switching to experimental mode, Docker’s plugins now include buildx at version 0.8.2.

Name Version
buildx v0.10.4
compose v2.17.2
dev v0.1.0
extension v0.2.19
init v0.1.0-beta.2
sbom 0.6.0
scan v0.25.0
scout v0.9.0

Multi-platform capable Docker ready to go.

Alpine 32-Bit ARM

Docker’s platforms also now enable the following, covering 64-bit ARM, 32-bit ARM with Thumb (v6) and with Thumb-2.

  • linux/amd64
  • linux/arm64
  • linux/riscv64
  • linux/ppc64le
  • linux/s390x
  • linux/386
  • linux/arm/v7
  • linux/arm/v6

Launch an Alpine container for ARM v7. Remove it on exit and connect it to the terminal interactively, options --rm and -it respectively.

docker run --platform linux/arm/v7 --rm -it alpine

Asking for the process table from within the container shows QEMU running the BusyBox shell. The ARM emulator runs the shell, /usr/bin/qemu-arm. References to /bin/sh become /bin/busybox by symbolic link on BusyBox-based distributions; one binary to rule them all!—J.R.R. Tolkien, 1954–1955

/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 {sh} /usr/bin/qemu-arm /bin/sh /bin/sh
    8 root      0:00 ps

Thumb-2 Disassembly

NO DISASSEMBLE!—Johnny Five, Short Circuit (1986)

Is it really true though? Add build base for the objdump object file dump utility, inside the Alpine container.

apk add build-base

Object dump on BusyBox binary. The disassembly lists Thumb-2, below.

/ # objdump -d /bin/busybox | head

/bin/busybox:     file format elf32-littlearm

Disassembly of section .init:

000055b0 <_init>:
    55b0:       b501            push    {r0, lr}
    55b2:       e8bd 4001       ldmia.w sp!, {r0, lr}
    55b6:       4770            bx      lr

Push R0 and link register, load Multiple Increment After, ‘branch and exchange instruction set’ as the initial part of an initialisation function prologue, all in 32-bit little-endian ARM.

One important little point remains to highlight, that /proc/cpuinfo still reports reality: the host CPU, not the platform emulation. Eight cores of Intel i7 in my case.

model name      : 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz

Underneath the bonnet, the binfmt_misc kernel feature is launching a user-land wrapper because the binary format mismatches the expected magic sequence. Hey presto!