Debugging Issues with GDB on ARM Macs in Intel Docker Containers

The course ships its exercises as a Docker image. On x86_64 Linux and Intel Macs this works out of the box. On Apple Silicon Macs — where Docker Desktop has to translate the linux/amd64 image to run on an ARM64 host — GDB inside the container fails with errors like:

1
2
3
4
warning: linux_ptrace_test_ret_to_nx: Cannot PTRACE_GETREGS: Input/output error
warning: linux_ptrace_test_ret_to_nx: PC 0x1 is neither near return address 0x7ffffffc7000 nor is the return instruction 0x5555558faf61!
Couldn't get CS register: Input/output error.
Exception occurred: Error: Couldn't get registers: Input/output error. (<class 'gdb.error'>)

This post explains why, and what to do about it.

Why this happens

This is not a bug that will be fixed by updating QEMU, Docker, or GDB. It is an architectural property of user-mode binary translation, and it applies to both emulation backends Docker Desktop can use on Apple Silicon.

GDB drives process control and register inspection through the ptrace(2) system call. When the container runs under cross-architecture emulation, the emulator would need to either forward ptrace to the host or emulate it over its own translated guest state. Neither backend does this:

  • QEMU user-mode has no ptrace implementation. The official QEMU manual states this directly: the built-in strace-like facility exists precisely because the real strace (which also relies on ptrace) cannot run under qemu-user.1 Implementing ptrace correctly would require QEMU to emulate a second debugging layer over its translated register and memory state, which upstream has long treated as impractical.2
  • Rosetta for Linux (the default amd64 backend when “Use Rosetta for x86/amd64 emulation” is enabled in Docker Desktop) likewise does not permit standard ptrace-based debugging of translated binaries. It does expose an undocumented GDB debug server via the ROSETTA_DEBUGSERVER_PORT environment variable, but this is a remote-debug-only path and is known to be fragile — shared-library resolution, stepping, and breakpoints regularly break across macOS versions.34

The OrbStack maintainers arrived at the same conclusion when this was raised in 2023: there is no good answer at the user-mode-emulator layer.5

In short: no local execution path on Apple Silicon restores the normal gdb ./challenge experience inside the course container.

A note on why we can’t “just use ARM”

Binary exploitation is architecture-specific. The calling conventions, register layout, instruction encoding, ROP gadgets, shellcode, and syscall numbers of x86_64 are nothing like ARM64. The course’s challenge binaries must be executed as x86_64 machine code, and the exploits you develop against them only make sense as x86_64 exploits. Switching the container to linux/arm64 is therefore not an option — it would be a different course.

The question is not which architecture. The question is which x86_64 machine runs the container.

What to do on Apple Silicon

The course does not provide an x86_64 execution environment. If your personal machine is Apple Silicon, it is your responsibility to get access to one. The image stays exactly the same; only where you run it changes.

Some options, roughly in order of cost:

1. A local x86_64 machine you already have access to

An Intel Mac, an x86_64 Linux laptop or desktop, a lab machine you have a login on, a spare PC at home. SSH in from your Apple Silicon Mac, or use it directly. Pull the course image, docker run as usual.

2. A small x86_64 cloud VM

Any provider with Intel or AMD instances works — Hetzner, Scaleway, AWS, DigitalOcean, Contabo, OVH. A low-end instance (2 vCPU, 4 GB RAM) is sufficient for the course’s challenges and costs on the order of single-digit euros per month; stop the VM when you’re not using it.

1
2
ssh you@your-vm
docker run -it --rm --cap-add=SYS_PTRACE course/abe:latest

3. GitHub Codespaces or a similar hosted dev-container service

Codespaces runs containers on x86_64 by default, supports the VS Code remote workflow, and accepts the course’s Dockerfile directly. The free tier is usually enough for a term’s worth of exercises. Equivalent services: Gitpod, Coder.

In all three cases, the container runs natively on an x86_64 kernel, GDB and the target are native x86_64 processes, and no emulation is involved. pwndbg, pwntools, ptrace, and the course material work unchanged.

What does not work reliably on Apple Silicon

  • Running gdb inside the course container locally via docker run --platform linux/amd64 ..., regardless of whether Docker Desktop is configured to use QEMU user-mode or Rosetta. This is the setup that produces the error at the top of this post.
  • Waiting for a QEMU, Docker, or GDB update to resolve this. The limitation is a design property of user-mode binary translation, not a deferred feature on anyone’s roadmap.

References


If none of these solutions work for your scenario, please contact the ABE Teaching Team for additional assistance.


  1. QEMU Project, User Mode Emulation (current manual). https://www.qemu.org/docs/master/user/main.html — “the actual ‘strace’ program will not work because the user space emulator hasn’t implemented ptrace”. ↩︎

  2. qemu-devel mailing list thread, Regarding PTRACE implementation in QEMU user mode. https://qemu-devel.nongnu.narkive.com/3OHTvVB6/regarding-ptrace-implementation-in-qemu-user-mode ↩︎

  3. sporks.space, Debugging an x86 application in Rosetta for Linux (2023-04-12). https://sporks.space/2023/04/12/debugging-an-x86-application-in-rosetta-for-linux/ ↩︎

  4. docker/for-mac Issue #6921, Unable to debug amd64 binaries on apple silicon. https://github.com/docker/for-mac/issues/6921 ↩︎

  5. OrbStack Discussion #27, Problem with gdb. https://github.com/orgs/orbstack/discussions/27 ↩︎