CPU Mode switches in qemu emulated machine. Undefined behavior. 16 bit code gets executed as 32 bit mode after a far jump

2026-09-06

Stack Overflow: View Question

Tags: linker, embedded, kernel, elf, real-mode

Score: 0 | Views: 203

The asker is building a hobby kernel (MapleKernel) and trying to transition the CPU from 32-bit protected mode back to 16-bit real mode. After executing a far jump into what should be 16-bit code, the CPU keeps decoding the instructions as 32-bit. They suspect it may be a build/link problem — some special technique for producing mixed-mode binaries that they're missing — but it could also be QEMU or the mode-switch sequence itself.

Why this is genuinely hard: "Going back to real mode" is not the inverse of "going to protected mode." It's a documented multi-step dance in the Intel SDM (Vol. 3, "Switching Back to Real-Address Mode"), and any missed step leaves the CPU in a hybrid state where the CS descriptor cache still holds 32-bit attributes even though the mode bits say otherwise. The classic symptom is exactly what the asker describes: bytes assembled as 16-bit get decoded as 32-bit because the D flag of the cached CS descriptor is still 1.

The correct sequence looks roughly like:

The build side: The linker/assembler part is real but secondary. You need .code16 directives (or [bits 16] in NASM) for the destination block, and the code must be linked at an address reachable by a real-mode segmented pointer (below 1MB, ideally page-aligned to something like 0x1000 so CS=0x0100, IP=0 works cleanly). If you're using GCC, you cannot really produce 16-bit code with it — that section must be hand-written assembly. Objcopy the ELF to a flat binary; ELF loaders aren't going to help you here.

Gotchas:

The challenge: Returning from protected mode to real mode requires flushing the CS descriptor cache through an intermediate 16-bit protected-mode segment — skip that hop and the CPU keeps decoding 16-bit bytes as 32-bit instructions even though the mode bit says otherwise.

All newsletters