linux - 如果内部没有描述或记录,为什么浮点比较有效?这是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 compare、 Assembly: 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 情况下搞砸了中间步骤。