Supplementary explanation about the patch 1. Reproduction of the bug
The following 3 files are needed to reproduce the bug. * test_os.s * test_os.ld * Makefile And the following 2 tools, too. * build-essential * gdb The contents of the above files are below. ---------- Begin of test_os.s ---------- .code16 .text main: fninit # Initialize the FPU fld1 # Push 1.0 fldl2t # Push log 2 10 fldl2e # Push log 2 e fldpi # Push pi fldlg2 # Push log 10 2 fldln2 # Push log e 2 fldz # Push 0.0 loop: hlt jmp loop ---------- End of test_os.s ---------- ---------- Begin of test_os.ld ---------- OUTPUT_FORMAT("binary"); BASE = 0x00007c00; SECTIONS { . = BASE; .text : { test_os.o(.text) } . = BASE; . += 0x00000200; . -= 0x00000002; .boot_sector_sign : { BYTE(0x55); BYTE(0xaa); } /DISCARD/ : { *(.eh_frame) *(.note.gnu.property) } } ---------- End of test_os.ld ---------- ---------- Begin of Makefile ---------- TEST_OS_NAME = test_o TEST_OS_NAME = test_os TEST_OS_ASM = $(TEST_OS_NAME).s TEST_OS_IMG = $(TEST_OS_NAME).img TEST_OS_LNK = $(TEST_OS_NAME).ld TEST_OS_MAP = $(TEST_OS_NAME).map TEST_OS_OBJ = $(TEST_OS_NAME).o all: $(TEST_OS_IMG) test: $(TEST_OS_IMG) (qemu-system-i386 -boot order=a \ -drive file=$<,format=raw,if=floppy \ -S -gdb tcp::2159 -vnc localhost:0 &) && \ gdb $(TEST_OS_IMG): $(TEST_OS_OBJ) $(TEST_OS_LNK) ld $< -Map $(TEST_OS_MAP) -o $@ -T $(word 2, $^) $(TEST_OS_OBJ): $(TEST_OS_ASM) gcc $^ -c -nostdlib -o $@ -Wall -Wextra ---------- End of Makefile ---------- Put these files on a same directory. "test_os.s" is source code of tiny OS to run on QEMU. The OS consists only a boot sector. It initialize x87 FPU and pushes some floating point values onto x87 FPU stack. "test_os.ld" is its linker script. And you can make "test_os.img", a raw image of the OS. Now, there are all things to reproduce the bug. You can "make test" to let QEMU run the OS and wait for GDB, then GDB will start. Then you can execute some GDB commands to reproduce the bug. Below is "result 1" reproducing the bug. ---------- Begin of result 1 ---------- GNU gdb (GDB) 13.0.50.20221204-git Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". (gdb) target remote localhost:2159 Remote debugging using localhost:2159 warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x0000fff0 in ?? () (gdb) break *0x7c00 Breakpoint 1 at 0x7c00 (gdb) continue Continuing. Breakpoint 1, 0x00007c00 in ?? () (gdb) x/10i $eip => 0x7c00: fninit 0x7c02: fld1 0x7c04: fldl2t 0x7c06: fldl2e 0x7c08: fldpi 0x7c0a: fldlg2 0x7c0c: fldln2 0x7c0e: fldz 0x7c10: hlt 0x7c11: jmp 0x7c10 (gdb) stepi 0x00007c02 in ?? () (gdb) info float R7: Valid 0x00000000000000000000 +0 R6: Valid 0x00000000000000000000 +0 R5: Valid 0x00000000000000000000 +0 R4: Valid 0x00000000000000000000 +0 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 =>R0: Valid 0x00000000000000000000 +0 Status Word: 0x0000 TOP: 0 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c04 in ?? () (gdb) info float =>R7: Valid 0x00000000000000000000 +0 R6: Valid 0x3fff8000000000000000 +1 R5: Valid 0x00000000000000000000 +0 R4: Valid 0x00000000000000000000 +0 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x3800 TOP: 7 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c06 in ?? () (gdb) info float R7: Valid 0x00000000000000000000 +0 =>R6: Valid 0x00000000000000000000 +0 R5: Valid 0x3fff8000000000000000 +1 R4: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x3000 TOP: 6 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c08 in ?? () (gdb) info float R7: Valid 0x00000000000000000000 +0 R6: Valid 0x00000000000000000000 +0 =>R5: Valid 0x00000000000000000000 +0 R4: Valid 0x3fff8000000000000000 +1 R3: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R2: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x2800 TOP: 5 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c0a in ?? () (gdb) info float R7: Valid 0x00000000000000000000 +0 R6: Valid 0x00000000000000000000 +0 R5: Valid 0x00000000000000000000 +0 =>R4: Valid 0x00000000000000000000 +0 R3: Valid 0x3fff8000000000000000 +1 R2: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R1: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R0: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 Status Word: 0x2000 TOP: 4 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c0c in ?? () (gdb) info float R7: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 R6: Valid 0x3ffd9a209a84fbcff799 +0.3010299956639811952 R5: Valid 0x00000000000000000000 +0 R4: Valid 0x00000000000000000000 +0 =>R3: Valid 0x00000000000000000000 +0 R2: Valid 0x3fff8000000000000000 +1 R1: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R0: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 Status Word: 0x1800 TOP: 3 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c0e in ?? () (gdb) info float R7: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R6: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 R5: Valid 0x3ffd9a209a84fbcff799 +0.3010299956639811952 R4: Valid 0x3ffeb17217f7d1cf79ac +0.6931471805599453094 R3: Valid 0x00000000000000000000 +0 =>R2: Valid 0x00000000000000000000 +0 R1: Valid 0x3fff8000000000000000 +1 R0: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 Status Word: 0x1000 TOP: 2 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c10 in ?? () (gdb) info float R7: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R6: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R5: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 R4: Valid 0x3ffd9a209a84fbcff799 +0.3010299956639811952 R3: Valid 0x3ffeb17217f7d1cf79ac +0.6931471805599453094 R2: Valid 0x00000000000000000000 +0 =>R1: Valid 0x00000000000000000000 +0 R0: Valid 0x3fff8000000000000000 +1 Status Word: 0x0800 TOP: 1 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 ---------- End of result 1 ---------- As you can see, the FPU stack rotates every pushing. 2. Cause There is a cause of the bug in a function "x86_cpu_gdb_read_register" in "qemu/target/i386/gdbstub.c". GDB receives a command "info float" from stdin and get values of the all registers containing FPU stack registers from QEMU to print them. Then, QEMU picks registers in the function to form 'g' packet to send to GDB. In line 124 of the c source file, absolute indexed FPU stack registers, namely, R0, ... and R7 , are picked and inserted in 'g' packet. However, GDB, the packet receiver, extracts FPU stack registers from the packet and interpret these registers are stack top relative indexed, namely, ST0, ... and ST7. As a result, GDB can't print FPU stack correctly. 3. Modification In added lines of this patch, "n" is a register number of a register to read. And "IDX_FP_REGS" is register number of the first FPU stack register "R0". So, "r_index" is absolute index of FPU stack register to read. And "env->fpstt" is a pointer to top of FPU stack. So, "st_index" is stack top relative index of FPU stack register to read. By applying this modification, QEMU inserts FPU stack registers ordered by stack top relative index in 'g' packet. 4. After this patch Below is "result 2" operating as same as "result 1" after applying this patch. ---------- Begin of result 2 ---------- GNU gdb (GDB) 13.0.50.20221204-git Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". (gdb) target remote localhost:2159 Remote debugging using localhost:2159 warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x0000fff0 in ?? () (gdb) break *0x7c00 Breakpoint 1 at 0x7c00 (gdb) continue Continuing. Breakpoint 1, 0x00007c00 in ?? () (gdb) x/10i $eip => 0x7c00: fninit 0x7c02: fld1 0x7c04: fldl2t 0x7c06: fldl2e 0x7c08: fldpi 0x7c0a: fldlg2 0x7c0c: fldln2 0x7c0e: fldz 0x7c10: hlt 0x7c11: jmp 0x7c10 (gdb) stepi 0x00007c02 in ?? () (gdb) info float R7: Valid 0x00000000000000000000 +0 R6: Valid 0x00000000000000000000 +0 R5: Valid 0x00000000000000000000 +0 R4: Valid 0x00000000000000000000 +0 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 =>R0: Valid 0x00000000000000000000 +0 Status Word: 0x0000 TOP: 0 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c04 in ?? () (gdb) info float =>R7: Valid 0x3fff8000000000000000 +1 R6: Valid 0x00000000000000000000 +0 R5: Valid 0x00000000000000000000 +0 R4: Valid 0x00000000000000000000 +0 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x3800 TOP: 7 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c06 in ?? () (gdb) info float R7: Valid 0x3fff8000000000000000 +1 =>R6: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R5: Valid 0x00000000000000000000 +0 R4: Valid 0x00000000000000000000 +0 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x3000 TOP: 6 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c08 in ?? () (gdb) info float R7: Valid 0x3fff8000000000000000 +1 R6: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 =>R5: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R4: Valid 0x00000000000000000000 +0 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x2800 TOP: 5 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c0a in ?? () (gdb) info float R7: Valid 0x3fff8000000000000000 +1 R6: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R5: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 =>R4: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 R3: Valid 0x00000000000000000000 +0 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x2000 TOP: 4 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c0c in ?? () (gdb) info float R7: Valid 0x3fff8000000000000000 +1 R6: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R5: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R4: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 =>R3: Valid 0x3ffd9a209a84fbcff799 +0.3010299956639811952 R2: Valid 0x00000000000000000000 +0 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x1800 TOP: 3 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c0e in ?? () (gdb) info float R7: Valid 0x3fff8000000000000000 +1 R6: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R5: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R4: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 R3: Valid 0x3ffd9a209a84fbcff799 +0.3010299956639811952 =>R2: Valid 0x3ffeb17217f7d1cf79ac +0.6931471805599453094 R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x1000 TOP: 2 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) stepi 0x00007c10 in ?? () (gdb) info float R7: Valid 0x3fff8000000000000000 +1 R6: Valid 0x4000d49a784bcd1b8afe +3.321928094887362348 R5: Valid 0x3fffb8aa3b295c17f0bc +1.442695040888963407 R4: Valid 0x4000c90fdaa22168c235 +3.141592653589793239 R3: Valid 0x3ffd9a209a84fbcff799 +0.3010299956639811952 R2: Valid 0x3ffeb17217f7d1cf79ac +0.6931471805599453094 =>R1: Valid 0x00000000000000000000 +0 R0: Valid 0x00000000000000000000 +0 Status Word: 0x0800 TOP: 1 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x0000 Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 ---------- End of result 2 ----------