diff mbox series

gdb-xml: Fix size of EFER register on i386 architecture when debugged by GDB

Message ID TY0PR0101MB4285F637209075C9F65FCDA6A4479@TY0PR0101MB4285.apcprd01.prod.exchangelabs.com
State New
Headers show
Series gdb-xml: Fix size of EFER register on i386 architecture when debugged by GDB | expand

Commit Message

TaiseiIto Sept. 13, 2022, 12:06 p.m. UTC
Before this commit, there were contradictory descriptions about size of EFER
register.
Line 113 says the size is 8 bytes.
Line 129 says the size is 4 bytes.

As a result, when GDB is debugging an OS running on QEMU, the GDB cannot
read 'g' packets correctly. This 'g' packet transmits values of each
registers of machine emulated by QEMU to GDB. QEMU, the packet sender,
assign 4 bytes for EFER in 'g' packet based on the line 113.
GDB, the packet receiver, extract 8 bytes for EFER in 'g' packet based on
the line 129. Therefore, all registers located behind EFER in 'g' packet
has been shifted 4 bytes in GDB.

After this commit, GDB can read 'g' packets correctly.

Signed-off-by: TaiseiIto <taisei1212@outlook.jp>
---
 gdb-xml/i386-32bit.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Alex Bennée Nov. 3, 2022, 3:59 p.m. UTC | #1
TaiseiIto <taisei1212@outlook.jp> writes:

> Before this commit, there were contradictory descriptions about size of EFER
> register.
> Line 113 says the size is 8 bytes.
> Line 129 says the size is 4 bytes.
>
> As a result, when GDB is debugging an OS running on QEMU, the GDB cannot
> read 'g' packets correctly. This 'g' packet transmits values of each
> registers of machine emulated by QEMU to GDB. QEMU, the packet sender,
> assign 4 bytes for EFER in 'g' packet based on the line 113.
> GDB, the packet receiver, extract 8 bytes for EFER in 'g' packet based on
> the line 129. Therefore, all registers located behind EFER in 'g' packet
> has been shifted 4 bytes in GDB.

I can't get the failure to read in my case:

  ./qemu-system-i386 -monitor none -display none \
    -chardev stdio,id=out -device isa-debugcon,chardev=out \
    -device isa-debug-exit,iobase=0xf4,iosize=0x4 \
    -kernel ./tests/tcg/i386-softmmu/memory -s -S

and then with gdb:

  ➜  gdb ./tests/tcg/i386-softmmu/memory -ex "target remote localhost:1234"
  Reading symbols from ./tests/tcg/i386-softmmu/memory...
  Remote debugging using localhost:1234
  0x0000fff0 in ?? ()
  (gdb) info registers efer
  efer           0x0                 [ ]

What am I missing?

>
> After this commit, GDB can read 'g' packets correctly.
>
> Signed-off-by: TaiseiIto <taisei1212@outlook.jp>
> ---
>  gdb-xml/i386-32bit.xml | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/gdb-xml/i386-32bit.xml b/gdb-xml/i386-32bit.xml
> index 872fcea9c2..7a66a02b67 100644
> --- a/gdb-xml/i386-32bit.xml
> +++ b/gdb-xml/i386-32bit.xml
> @@ -110,7 +110,7 @@
>  	<field name="PKE" start="22" end="22"/>
>    </flags>
>  
> -  <flags id="i386_efer" size="8">
> +  <flags id="i386_efer" size="4">
>  	<field name="TCE" start="15" end="15"/>
>  	<field name="FFXSR" start="14" end="14"/>
>  	<field name="LMSLE" start="13" end="13"/>
Paolo Bonzini Nov. 3, 2022, 6:38 p.m. UTC | #2
On 9/13/22 14:06, TaiseiIto wrote:
> Before this commit, there were contradictory descriptions about size of EFER
> register.
> Line 113 says the size is 8 bytes.
> Line 129 says the size is 4 bytes.
> 
> As a result, when GDB is debugging an OS running on QEMU, the GDB cannot
> read 'g' packets correctly. This 'g' packet transmits values of each
> registers of machine emulated by QEMU to GDB. QEMU, the packet sender,
> assign 4 bytes for EFER in 'g' packet based on the line 113.
> GDB, the packet receiver, extract 8 bytes for EFER in 'g' packet based on
> the line 129. Therefore, all registers located behind EFER in 'g' packet
> has been shifted 4 bytes in GDB.
> 
> After this commit, GDB can read 'g' packets correctly.

Queued, thanks.

Paolo
TaiseiIto Nov. 5, 2022, 3:14 a.m. UTC | #3
Thanks for the reply. Below are details about the patch.

1. A bug

GDB cannot print the x87 FPU registers correctly when the GDB is debugging
an OS working on qemu-system-i386.

1.1 My host environment

Ubuntu 22.04.1 LTS

1.2 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. Below is "result 1"
reproducing the bug.

---------- Begin of result 1 ----------

$ make test
(gdb) target remote localhost:2159
(gdb) break *0x7c00
(gdb) continue
(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) break *0x7c10
(gdb) continue
(gdb) info float
  R7: Valid   0x0000037f3fff80000000 +9.185555460266999444e-4934 Denormal
  R6: Valid   0x000000004000d49a784b +2.56521115372749819e-4937  Denormal
  R5: Valid   0xcd1b8afe3fffb8aa3b29 -1.960870532096122377e+1010
  R4: Valid   0x5c17f0bc4000c90fdaa2 +1.914514747773691148e+2165
  R3: Valid   0x2168c2353ffd9a209a84 +6.533729021523866343e-2358
  R2: Valid   0xfbcff7993ffeb17217f7 -4.548119628708025252e+4609
  R1: Valid   0xd1cf79ac000000000000 Unsupported
=>R0: Valid   0x00000000000000000000 +0

Status Word:         0x0000
                       TOP: 0
Control Word:        0x0800
                       PC: Single Precision (24-bits)
                       RC: Round up
Tag Word:            0x0000
Instruction Pointer: 0x00:0x00000000
Operand Pointer:     0x00:0x00000000
Opcode:              0x0000

---------- End of result 1 ----------

First, in GDB interface, connect localhost:2159 where QEMU is waiting for
GDB. Second, Proceed to 0x7c00 and confirm there are intentional
instructions. Finally, Proceed to 0x7c10 and confirm contents of the x87
FPU registers. There are values different from what the OS pushed in
registers from "R0" to "R7", the x87 FPU stack. Furthermore, the control
word seems to be wrong. The first instruction "fninit" in the OS makes the
control word 0x037f but it is 0x0800 in the above result.

2. Cause

There is a cause in exchanging 'g' packet between QEMU and GDB. This packet
sends registers of machine emulated by QEMU to GDB. And its format is
defined in "qemu/gdb-xml/i386-32bit.xml". Size of EFER register is defined
in two places, line 113 and 129.

---------- qemu/gdb-xml/i386-32bit.xml line 113 ----------

  <flags id="i386_efer" size="8">

----------------------------------------------------------

The above line says size of EFER is 8 bytes.

---------- qemu/gdb-xml/i386-32bit.xml line 129 ----------

  <reg name="efer" bitsize="32" type="i386_efer"/>

----------------------------------------------------------

The above line says size of EFER is 32 bits. These lines contradict each
other. QEMU, the packet sender, assign 4 bytes for EFER in 'g' packet based
on the line 113. GDB, the packet receiver, extract 8 bytes for EFER in 'g'
packet based on the line 129. As a result, all registers located after EFER
in 'g' packet has been shifted 4 bytes in GDB.

3. Modification

Unify size of EFER 4 bytes.

4. Result

Below is "result 2" doing same operation as "result 1" using the modified
xml.

---------- Begin of result 2 ----------

$ make test
(gdb) target remote localhost:2159
(gdb) break *0x7c00
(gdb) continue
(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) break *0x7c10
(gdb) continue
(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 2 ----------

There are more reasonable values in the FPU stack than previous. Control
word gets correct, too. But order of FPU stack is wrong. Correct FPU stack
should be like below.

---------- Begin of Correct FPU stack ----------

  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

---------- End of Correct FPU stack ----------

The floor of the stack is "R7" and should have value "1.0" that the OS
pushed first. The top of the stack is "R1" and should have value "0.0", the
last pushed value. The stack of the second result seems to have been
rotated up 1 element. I think there are confusing between physical
registers from "R0" to "R7" and stack top relative pointer from "ST0" to
"ST7". But I think this problem should be handled as another bug. I am
looking into this problem and have not yet determined whether should I send
a patch to QEMU or GDB yet. So this patch includes only modification of
size of EFER register.

Taisei Ito

Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows
diff mbox series

Patch

diff --git a/gdb-xml/i386-32bit.xml b/gdb-xml/i386-32bit.xml
index 872fcea9c2..7a66a02b67 100644
--- a/gdb-xml/i386-32bit.xml
+++ b/gdb-xml/i386-32bit.xml
@@ -110,7 +110,7 @@ 
 	<field name="PKE" start="22" end="22"/>
   </flags>
 
-  <flags id="i386_efer" size="8">
+  <flags id="i386_efer" size="4">
 	<field name="TCE" start="15" end="15"/>
 	<field name="FFXSR" start="14" end="14"/>
 	<field name="LMSLE" start="13" end="13"/>