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 ----------