【问题标题】:Assembly: converting to if statement using two fld, fcomp, fnstsw and test 41h汇编:使用两个 fld、fcomp、fnstsw 和 test 41h 转换为 if 语句
【发布时间】:2023-03-20 22:34:02
【问题描述】:

有人可以帮我理解下面的代码吗?

fld qword ptr [L1000F168]
fcomp   qword ptr [L1000A2F0]
fld qword ptr [L1000F168]
fnstsw  ax
test    ah,41h
jnz L100012F0

它是编译器从执行代码转换为汇编的输出。

到目前为止,我发现的是

if(value[L1000F168] != value[L1000A2F0])
   continue following
else
   goto L100012F0

我说的对吗?

我不明白为什么[L1000F168] 被加载了两次,为什么ah 在这里与41h 进行比较?如41h 表示(无效操作)或(堆栈错误)?

【问题讨论】:

  • AH 包含 AX 的 8-15 位,因此它具有状态字的 8-15 位。正如 Peter Cordes 所说,$41 对应于 bit14=C3 和 bit8=C0。

标签: assembly x86 disassembly fpu x87


【解决方案1】:

有关 x87 状态字的信息,请参阅 this page

请注意,16 位状态字存储到ax,但test 指令只查看高 8 位(ah)。所以41h 匹配状态字的C0C3 位。在 ah 上使用 8 位测试可避免英特尔 CPU 因使用 16 位 testimm16 而导致减速。 (操作数大小前缀改变指令其余部分的长度,即所谓的长度改变前缀解码器停顿)。

fld / fcomp / fld / fnstsw:这对我来说也很奇怪。我想知道目标是否是基于内存位置设置状态字中的非规范位,但基于fcomp 设置C0 等等。 (这不是你得到的,因为fld 使C0-3 未定义或设置。)

英特尔的 insn 参考手册说 fld 未定义 C0C3,因此生成此代码的编译器取决于某些特定行为。也许没有fwait,状态字不会从fcomp 更新?我还没有理解整个fwait 的事情。我还没有找到(或努力寻找)关于在旧 CPU 上何时需要 fwait 以及何时不需要的解释。据我了解,您永远不会在 P5 或更高版本上这样做。

无论如何,我认为这段代码的作用是:

if (! ([L1000F168] > [L1000A2F0]))
    goto L100012F0;

测试 not-greater 与测试 <= 或无序相同。这假定fld 实际上并未修改C0C3。也许手册只说它没有定义它们,因为它可以在加载非正规或其他条件时设置它们?或者也许它在当前的硅中根本没有。或者也许这是英特尔手册中的另一个错误,这实际上是众所周知的。或者也许这段代码不再起作用了!最好使用调试器对其进行测试。

我认为获得程序员可能希望的结果的更明智的方法是

fld    qword ptr [L1000F168]
fcom   qword ptr [L1000A2F0]
fnstsw ax
test   ah,41h     ; C0|C3: both zero iff st(0) > [L1000A2F0]
jnz    L100012F0

由于 OP 说代码是编译器输出,我猜这意味着它没有很好的 x87 优化来避免弹出/重新加载相同的值。

For P6 and later CPUs:

fld    qword ptr [L1000F168]
fcomi  qword ptr [L1000A2F0]
jna    L100012F0   ;  jump unless st(0) > [L1000A2F0]

我可能对其中一个比较有倒退的感觉,所以如果有什么不合理的地方,请仔细检查。

这段代码来自哪里?是编译器输出吗?或者可能是手写的 asm?

【讨论】:

  • 这看起来像是反汇编程序的输出。
  • 感谢您的回复。它是编译器生成的可执行文件的代码的一部分。所以正如你所说,它是if(value[L1000F168]>value[L1000A2F0])?我理解正确吗?它是否依赖于之前加载到堆栈中的其他内容?
  • 顺便说一句,在这段代码之前,堆栈没有比函数开始时更多的额外推送值。
  • @RudyVelthuis:是的,我发现这是对二进制文件进行逆向工程,但二进制文件可以通过组装手写 asm 来创建。我猜编译器不够聪明,无法使用fcom 并节省负载。 (那时,fld 并没有留下 C0C3 未定义。或者可能没有 fwait,在那时存储标志确实从比较中得到了标志?)
  • @barej:我更新了我的答案来回答你的 cmets。它不依赖于以前的st(0),它只依赖于fld 的x86 未定义行为,不涉及状态字中的某些位。
【解决方案2】:

fcomp qword ptr [L1000A2F0] 将 stack[0] 与 L1000A2F0 中的值进行比较,并将其弹出到堆栈的末尾。

example
stack[0] = 50
[L1000A2F0] = 45

if (stack[0] > [L1000A2F0])
pop stack[0]; // (stack[7] = stack[0])
else
pop [L1000A2F0]; // (stack[7] = [L1000A2F0])

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-09
    • 2019-07-04
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 2014-10-08
    相关资源
    最近更新 更多