Skip to content

Commit 6d2cdbc

Browse files
The127claude
andauthored
fix: refuse --elf when building for arm64 (#40)
* fix: say why --elf refuses a kernel that is no bzImage Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: karo <karolin.kostial@gmail.com> * ci: run the unit tests on arm64 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: karo <karolin.kostial@gmail.com> * revert: drop the --elf hint for kernels that are no bzImage Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: karo <karolin.kostial@gmail.com> * fix: refuse --elf when building for arm64 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: karo <karolin.kostial@gmail.com> --------- Signed-off-by: karo <karolin.kostial@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0ebfd6a commit 6d2cdbc

9 files changed

Lines changed: 48 additions & 16 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ jobs:
5454
files: ./coverage.out
5555
flags: unit
5656

57-
# the built miso must also behave on arm64 hosts, which the amd64 runner
58-
# cannot show
59-
cli-arm64:
57+
# miso must also behave on arm64 hosts, which the amd64 runner cannot show
58+
arm64:
6059
runs-on: ubuntu-24.04-arm
6160
steps:
6261
- uses: actions/checkout@v7
@@ -72,4 +71,9 @@ jobs:
7271
assert-install: false
7372
detik-install: false
7473
file-install: false
75-
- run: just test-cli
74+
# the qemu tests claim a CID on the host's real vsock device
75+
- name: open the vsock device
76+
run: |
77+
sudo modprobe vhost_vsock
78+
sudo chmod o+rw /dev/vhost-vsock
79+
- run: just test test-cli

‎internal/build/bootpart_test.go‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ func TestAnElfOptionWithAValueFailsAtItsLineNamingIt(t *testing.T) {
7676
assert.ErrorContains(t, err, "--elf")
7777
}
7878

79+
func TestAnElfOptionForAnArm64ImageFailsAtItsLine(t *testing.T) {
80+
// arrange
81+
source := planned(t, "FROM debian:13\nOUTPUT kernel --elf vmlinux\n")
82+
source.Arch = "arm64"
83+
84+
// act
85+
_, err := build.Requests(source, network)
86+
87+
// assert
88+
require.ErrorIs(t, err, build.ErrELFNeedsBzImage)
89+
assert.ErrorContains(t, err, "line 2")
90+
assert.ErrorContains(t, err, "--elf")
91+
}
92+
7993
func TestAKernelOutputWithAPathAsksForThatFileOfTheImage(t *testing.T) {
8094
// arrange
8195
source := planned(t, "FROM debian:13\nOUTPUT kernel --path=/src/linux/arch/x86/boot/bzImage vmlinuz\n")

‎internal/build/output.go‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import (
1616
// A disk, an ISO or a rootfs takes the tools from where their stage ends, the
1717
// kernel and the initrd are taken from the image, and an unpacked kernel
1818
// takes the tools too.
19-
func outputRequest(step plan.Step, instruction imagefile.Output, under rootfs, tools rootfs, inputs diskInputs) (protocol.Message, error) {
20-
refused := refusal(instruction)
19+
func outputRequest(step plan.Step, instruction imagefile.Output, under rootfs, tools rootfs, inputs diskInputs, arch string) (protocol.Message, error) {
20+
refused := refusal(instruction, arch)
2121
if refused == nil && instruction.Kind == plan.KindUpdate {
2222
refused = updateRefusal(instruction, inputs.partitions)
2323
}
@@ -39,8 +39,9 @@ func outputRequest(step plan.Step, instruction imagefile.Output, under rootfs, t
3939
return protocol.Disk{Key: step.Key, Layers: under.layers, Tools: tools.layers, ElTorito: instruction.Kind == plan.KindISO, Split: instruction.Kind == plan.KindUpdate, Partitions: slices.Clone(inputs.partitions), Cmdline: strings.Join(inputs.cmdline, " ")}, nil
4040
}
4141

42-
// refusal is why miso cannot make an output, or nil when it can.
43-
func refusal(instruction imagefile.Output) error {
42+
// refusal is why miso cannot make an output for an image of arch, or nil
43+
// when it can.
44+
func refusal(instruction imagefile.Output, arch string) error {
4445
unknown, hasUnknown := unknownOption(instruction.Options, plan.Options(instruction.Kind))
4546
switch {
4647
case !plan.Known(instruction.Kind):
@@ -49,6 +50,8 @@ func refusal(instruction imagefile.Output) error {
4950
return fmt.Errorf("--%s: %w", unknown, ErrUnknownOption)
5051
case instruction.Options[plan.OptionELF] != "":
5152
return fmt.Errorf("--%s: %w", plan.OptionELF, ErrOptionTakesNoValue)
53+
case plan.Unpacks(instruction) && arch != "" && arch != "amd64":
54+
return fmt.Errorf("--%s: %w", plan.OptionELF, ErrELFNeedsBzImage)
5255
case pathGiven(instruction.Options) && !path.IsAbs(instruction.Options[plan.OptionPath]):
5356
return fmt.Errorf("--%s=%s: %w", plan.OptionPath, instruction.Options[plan.OptionPath], ErrPathNotAbsolute)
5457
case plan.TakesFormat(instruction.Kind) && !slices.Contains(formats, formatOf(instruction.Options)):

‎internal/build/request.go‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ var ErrPathNotAbsolute = errors.New("path is not absolute")
3030
// ErrOptionTakesNoValue is an option that only says yes, given a value.
3131
var ErrOptionTakesNoValue = errors.New("option takes no value")
3232

33+
// ErrELFNeedsBzImage is --elf on an image for an architecture whose kernel
34+
// is no bzImage.
35+
var ErrELFNeedsBzImage = errors.New("needs an x86 bzImage, other kernels are unpacked already")
36+
3337
// ErrUnknownFormat is a file system a rootfs cannot be.
3438
var ErrUnknownFormat = errors.New("unknown file system")
3539

@@ -126,7 +130,7 @@ func requestsUntil(planned plan.Plan, network protocol.Network, shell shellAt) (
126130

127131
inputs.add(step.Instruction)
128132

129-
message, err := messageOf(step, under, ends, network, inputs)
133+
message, err := messageOf(step, under, ends, network, inputs, planned.Arch)
130134
if err != nil {
131135
return nil, false, err
132136
}
@@ -174,14 +178,14 @@ func importOf(stage plan.Stage) Request {
174178

175179
// messageOf is what the agent is asked for a step on the root file system
176180
// under it, or nothing for a step the agent has no part in.
177-
func messageOf(step plan.Step, under rootfs, ends map[string]rootfs, network protocol.Network, inputs diskInputs) (protocol.Message, error) {
181+
func messageOf(step plan.Step, under rootfs, ends map[string]rootfs, network protocol.Network, inputs diskInputs, arch string) (protocol.Message, error) {
178182
switch instruction := step.Instruction.(type) {
179183
case imagefile.Run:
180184
return protocol.Run{Key: step.Key, Layers: under.layers, Env: under.env, Command: instruction.Command, Network: networkFor(instruction, network)}, nil
181185
case imagefile.Copy:
182186
return copyRequest(step, instruction, under, ends[instruction.From])
183187
case imagefile.Output:
184-
return outputRequest(step, instruction, under, ends[plan.BuiltinTools], inputs)
188+
return outputRequest(step, instruction, under, ends[plan.BuiltinTools], inputs, arch)
185189
}
186190

187191
return nil, nil

‎internal/plan/plan.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import "github.com/The127/miso/internal/imagefile"
66
type Plan struct {
77
Agent string
88

9+
// the architecture the image is built for, empty is amd64
10+
Arch string
11+
912
// the bases a build fetches before it plans again, in FROM order
1013
Downloads []string
1114
Stages []Stage

‎site/src/examples.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ boot it. The initrd is made again with the driver of the virtual disk. The
4646
Result: `out/os.ext4`, `out/vmlinuz` and `out/initrd.img`
4747

4848
The `Imagefile` has a `qemu-system-x86_64` command that boots them. Firecracker
49-
needs the kernel as an ELF file, so write `OUTPUT kernel vmlinux --elf`
49+
on x86 needs the kernel as an ELF file, so write `OUTPUT kernel vmlinux --elf`
5050
instead of `OUTPUT kernel vmlinuz`. The `Imagefile` also has a Firecracker
5151
configuration that boots the result to a login prompt.
5252

‎site/src/imagefile.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ to `miso build -o`.
246246
| `sysext` | A system extension in a disk image with one root partition. | `--format` |
247247
| `confext` | A configuration extension in a disk image with one root partition. | `--format` |
248248
| `update` | A directory with the files `systemd-sysupdate` uses. | `--version=<version>` (required) |
249-
| `kernel` | The kernel of the image. | `--elf` unpacks it to an ELF file, `--path=<file>` names it |
249+
| `kernel` | The kernel of the image. | `--elf` unpacks an x86 kernel to an ELF file, `--path=<file>` names it |
250250
| `initrd` | The initrd of the image. | `--path=<file>` names it |
251251

252252
The kernel and the initrd are found in the image. miso uses the newest

‎site/src/outputs.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ OUTPUT initrd initrd.img --path=/src/initrd.img
9999

100100
The kernel and the initrd of the image, copied as they are.
101101

102-
`--elf` unpacks the kernel to the ELF file it contains. Firecracker needs
103-
that form.
102+
`--elf` unpacks an x86 bzImage to the ELF file it contains, which
103+
Firecracker needs on x86. An arm64 kernel is an `Image` that is unpacked
104+
already, and Firecracker takes it as it is, so an arm64 build refuses
105+
`--elf`.
104106

105107
`--path` names the file in the image, for a kernel or an initrd that a `RUN`
106108
built somewhere else. It must be an absolute path. It works together with
@@ -214,6 +216,8 @@ It also gives the reason. These are the ones from the `OUTPUT` line itself:
214216
- `unknown kind of output`
215217
- `--x: unknown option`
216218
- `--elf: option takes no value`
219+
- `--elf: needs an x86 bzImage, other kernels are unpacked already`, on an
220+
arm64 image
217221
- `--path=x: path is not absolute`
218222
- `--format=x: unknown file system`
219223
- `not a plain file name`, when the name has a `/` in it

‎test/build.bats‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ own_arch() {
2525
[[ "$output" == *"amd64"* ]]
2626
}
2727

28-
# only a host that is not amd64 tells the two runs apart, see the cli-arm64 job
28+
# only a host that is not amd64 tells the two runs apart, see the arm64 job
2929
@test "a build without --arch is a build for the arch miso runs on" {
3030
# the cache of the test, which is not the user's
3131
export XDG_CACHE_HOME="$BATS_TEST_TMPDIR/cache"

0 commit comments

Comments
 (0)