首页 > 解决方案 > 如果内部没有描述或记录,为什么浮点比较有效?这是gdb的怪癖吗?

问题描述

我一直在研究 Richard Blum 的专业汇编语言,它的初始浮点比较程序可以工作,但是在 Linux 上通过 gdb 运行时,一些中间步骤是出乎意料的

去体验:

这是程序(添加了我的评论,我删除了一个在 dwarf 调试中不需要的 nop):

.section .data
value1:
    .float 10.923
value2:
    .float 4.5532

.section .text
.globl _start
_start:

    flds value1     # Load a 32-bit floating-point value into the FPU stack
    fcoms value2    # Compare the value in ST0 with mem address &value2

        # Retrieving the value of the FPU status register (with no argument ...
    fstsw   # ... this defaults to AX)

    sahf    # Store AH into Flags: 00 => CF, 02 => PF, 06 => ZF
    ja greater
    jb lessthan

    movl $1, %eax   # Exit (condition: value1 = value2)
    movl $0, %ebx
    int $0x80

greater:
    movl $1, %eax   # Exit (condition: value1 is greater than value2)
    movl $2, %ebx
    int $0x80
lessthan:
    movl $1, %eax   # Exit (condition: value1 is less than value2)
    movl $1, %ebx
    int $0x80

(如果有人想试一试,我还翻译了它的一个版本并以 nasm 语法运行——但我对此的快速测试似乎没有产生不同的结果。)

它的构建和链接:

as -o fcomtest.o fcomtest.s --32 --gdwarf-2
ld -o fcomtest fcomtest.o -m elf_i386

我正在使用以下 gdb-input-script(您可能必须在最后几行中添加或删除“step”命令,具体取决于 value1 和 value2 的设置以及采用哪个分支):

# suppressing the output when setting the breakpoint at _start 
set logging file /dev/null
set logging redirect on
set logging on
br _start
set logging off

printf "\nFirst outputting values1 and 2:\n"
x/f &value1
x/f &value2

# suppressing the output when the breakpoint is triggered
set logging on
run
set logging off

printf "Now, ST0 = %f\n", $st0

printf "\nChecking the intial value of the FPU status register...\n"
print/t $fstat

printf "\nfcoms value2  # Comparing the value in ST0 with mem address &value2\n\n"

set logging on
step
set logging off

printf "The FPU status register FSTAT now contains...\n"
print/t $fstat

printf "\nBefore copying FSTAT, the AX register contains: "
print/t $ax

printf "\nfstsw     #  Copying (16-bit) FPU status register, FSTAT, to AX"
set logging on
step
set logging off

printf "\n\nNow the contents of the AX register are: \n"
print/t $ax

printf "\nInitially, the EFLAGS register in binary is: \n"
print/t $eflags

printf "\nThat is, the flags set are: "
print $eflags

printf "\nsahf      # store the high 8-bits of AX into the corresponding flags "

set logging on
step
set logging off

printf "\n\nNow, the EFLAGS register contains: \n"
print/t $eflags

printf "\nIn other words, these flags are set: "
print $eflags

printf "\nNow branch according to the CF and ZF flags...\n"
step
step
step
step
q

最后,为了检查调试器输出并轻松重现,我使用了这个命令行:

$ gdb -q -x gdb-input-script > gdb.output fcomtest

然后,您可以看到生成的输出:

$ cat gdb.output.

理论在这些帖子 中讨论了它应该如何工作的机制: x86 assembler: floating point compareAssembly: JA and JB work wrongly以及链接的文章。

特别是,FCOM 应该将 FPU 堆栈的 ST0 中的值与另一个值进行比较,并根据以下公式更改 FPU 状态寄存器的 C3、C2 和 C0 代码位:

+-----------------+----+-----+----+
| Condition       | C3 |  C2 | C0 |
+-----------------+----+-----+----+
| ST0 > argument  | 0  |  0  | 0  |
| ST0 < argument  | 0  |  0  | 1  |
| ST0 = argument  | 1  |  0  | 0  |
+-----------------+----+-----+----+

此外,SAHF 应该将特定位映射到 EFLAG。相反:这里概述了这些值输入实际发生的情况(“手册”是英特尔文档):

**奇怪的部分**

Case 4II (value1 +ve, value2 +ve; val1 greater magnitude, val2 lesser magnitude)

15      14      13      12      11      10      09      08  -- FPU status reg.
FPU     C3      SP      SP      SP      C2      C1      C0

07      06      05      04      03      02      01      00  -- AH register

1       1       1       0       0       0       0       0

[In this result of FCOM:  C3 has been set to 1 -- not what was expected.  The
expected result was C3 = 0, C2 = 0, C0 = 0]

SAHF instruction:
From the manual: 07 => SF, 06 => ZF, 04 => AF, 02 => PF, 00 => CF
From the textbook: 06 => ZF, 02 => PF, 00 => CF
Actual behavior: -07 => SF, -06 => ZF , -04 => AF, 02 => PF ?, 00 => CF ? ]

EFLAGS register:

0       0       0       0       0       0       1       0   -- Before SAHF

SF      ZF      -       AF      -       PF      -       CF

0       0       0       1       0       0       1       0   -- After SAHF

[Here, CF = 0 and ZF = 0, so the JA branch is taken and the result is as
desired]

我知道这有点长(但它显示了如何很容易地重建它)。总之:如果您更改 values1 和 2 并重新编译,我发现实际上似乎正在发生以下情况——至少在 gnu-debugger 的输出中:


C3      C2      C0
1       0       0   -- Equal numbers case (behavior as described in the documentation)

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

1       1       0   -- Value1 < Value2, actual
    (SAHF acts as though it has a NOT applied before altering the flags)

(The status register itself seems to be the inverse of the expected:)
0       0       1   -- expected

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

1       0       0   -- Value1 > Value2

0       0       0   -- expected

(Output of SAHF contorts somehow to permit CF = ZF = 0 )

我已经描述了如何重现这个,所以它只是一个复制粘贴来查看结果。是否只是一些关于 gdb 的怪异改变了 C3、C2 和 C0 的值,然后调整以使标志起作用? 在所有情况下,最终都会采用正确的分支......我没有尝试过(没有练习过)其他调试器,以查看调试器是否只是在 value1 != value2 情况下搞砸了中间步骤。

标签: linuxassemblyx86gdbx87

解决方案


推荐阅读