Skip to content

aarch64-linux sample environments can't build under QEMU #96

Description

@thomasjm

Summary

Building packages.aarch64-linux.sample_environments_farm (and the individual mega / julia110 / julia111 environments) on an x86_64-linux host via QEMU user-mode (binfmt) emulation fails in the Julia kernel build. The Julia julia-depot / julia-symbols derivations die during package precompilation with:

nested task error: IOError: TTY: invalid argument (EINVAL)
  [2] Base.TTY(fd::RawFD) @ Base ./stream.jl:251
  [3] open_fake_pty() @ REPL.Precompile.FakePTYs .../REPL/src/precompile.jl ...

The root cause is not in codedown-languages or Julia. It is a QEMU user-mode emulation bug: with glibc ≥ 2.42, isatty() uses the TCGETS2 ioctl, which QEMU does not translate, so isatty() on a PTY master returns 0 under emulation. This is enough to break libuv's uv_tty_init, which Julia's REPL precompile workload relies on.

We are filing this to track the situation and pick up an eventual QEMU fix, rather than carrying a workaround.

Environment

  • Build host: x86_64-linux, with aarch64-linux enabled via Nix extra-platforms + QEMU binfmt (/run/binfmt/aarch64-linuxqemu-aarch64-binfmt-P).
  • QEMU: 10.1.2
  • nixpkgs: release-25.11 (flake input; rev f5190b6…), which ships glibc 2.42
  • Julia: 1.11.9 (source build; interpreter linked against glibc-2.42-61)
  • Affected derivations: julia-depotjulia-symbolsjuliajulia110 / julia111 / megasample_environments_farm

Root cause chain

  1. nixpkgs 25.11 ships glibc 2.42.
  2. glibc 2.42 reimplemented isatty() to issue ioctl(fd, TCGETS2) instead of TCGETS.
  3. QEMU user-mode's ioctl translation table (linux-user/ioctls.h) handles TCGETS but not TCGETS2, so the ioctl is silently dropped and isatty() fails.
  4. Julia's REPL precompile workload (stdlib/REPL/src/precompile.jl, guarded by if Base.generating_output()) calls open_fake_pty() (share/julia/test/testhelpers/FakePTYs.jl), which builds a PTY master and wraps it with Base.TTY(RawFD(fdm)) → libuv uv_tty_init.
  5. Because isatty(master) is 0 under QEMU, libuv classifies the fd as a non-tty and returns EINVAL. The REPL precompile dies, cascading to every package that depends on REPL (IJulia, Pkg, LanguageServer, SymbolServer, Plots, …).

Reproduction (isolates it to QEMU + isatty)

Run a minimal version of open_fake_pty() with the project's aarch64 Julia (auto-runs under QEMU via binfmt):

# /tmp/pty_repro.jl
O_RDWR = Base.Filesystem.JL_O_RDWR; O_NOCTTY = Base.Filesystem.JL_O_NOCTTY
fdm = ccall(:posix_openpt, Cint, (Cint,), O_RDWR | O_NOCTTY)
println("posix_openpt -> ", fdm)
println("grantpt  -> ", ccall(:grantpt,  Cint, (Cint,), fdm))
println("unlockpt -> ", ccall(:unlockpt, Cint, (Cint,), fdm))
println("isatty(master) -> ", ccall(:isatty, Cint, (Cint,), fdm))
try; Base.TTY(RawFD(fdm)); println("Base.TTY OK")
catch e; println("Base.TTY FAILED: ", sprint(showerror, e)); end

Output under QEMU (aarch64):

posix_openpt -> 17
grantpt  -> 0
unlockpt -> 0
isatty(master) -> 0          # <-- wrong; should be 1
Base.TTY FAILED: IOError: TTY: invalid argument (EINVAL)

strace -f -e trace=ioctl on the same run shows only TIOCGPTN and TIOCSPTLCK forwarded to the host for the master fd — the TCGETS2 from isatty() never appears (QEMU drops it).

Native control (compiled C on the x86_64 host, no QEMU): isatty(master) = 1.

Upstream status

  • Tracked as Ubuntu LP #2133804 ("QEMU does not emulate IOCTL TCGETS2") and LP #2133188.
  • A proper fix requires host_to_target_termios2() / target_to_host_termios2() in linux-user/syscall.c.
  • Not fixed in any released QEMU, nor in QEMU master as of this writing — linux-user/syscall.c only has a PowerPC #define TCGETS2 TCGETS alias, no general termios2 translation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions