Skip to content

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

@thomasjm

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions