0

我正在阅读这篇文章,我注意到他们的示例显示 fld 将值加载到不同的位置(st0、st1,然后返回到 st0),而没有指定加载到的位置。我假设 fild 以类似的方式工作,因为它只是加载整数的方法(或者这是我的理解),但我可能是错的。所以我的问题是:fld,更具体地说,将值加载到哪里?是否有一个参数来指定要使用哪个 fpu 寄存器,或者它只是循环通过 8,还是我缺少一些完全不同的方式?

我专门使用的代码是尝试将 3 个数字相乘。我假设一种方法是加载到 st0,然后加载到 st1,然后加载到 st2,然后是 fmul st0 和 st1(导致 st0),然后是 fmul st0 和 st2。代码如下:

mov dword [ebp-8], 4
mov dword [ebp-12], ecx
fild dword [ebp-4]
fild dword [ebp-8]
fild dword [ebp-12]
fmul st0, st1
fmul st0, st2
fistp dword [ebp-8]
mov eax, dword [ebp-8]

ecx = 5 和 [ebp-4] = 5

此代码崩溃,使用 OllyDbg 我看到在 00000069 处存在访问冲突,但当前未包含在任何寄存器中。

所以是的,有没有办法指定 fild 加载值的位置,有没有一种很好的方法来确定它们应该去哪里,如果我在循环中运行它会改变什么吗?

--EDIT 3-- 大部分是固定的,排序。一件大事是它fmul不会将值推送到st0,它只会覆盖 中的任何内容st0。新代码:

mov dword [ebp-8], ecx
fild dword [ebp-4]
fild dword [four]
fild dword [ebp-8]
fmul st1
fmul st2
fistp dword [ebp-12]
mov eax, dword [ebp-12]

这个循环和递减直到ecx== 2 然后尝试fild2 和 1 给出与bad -NAN FFFF C0000000 00000000之前相同的结果。我不确定 3 与 2 或 1 有何不同(除了更小),但那是它开始给出错误值的时候。我应该注意到 ERROR_MOD_NOT_FOUND 被抛出了,虽然我不确定这意味着什么,因为所有的 cpu 和 fpu 寄存器都应该是可访问的。

--EDIT 2--修复了 Parham Alvani 与文档内容一起显示的流行内容:

mov dword [ebp-8], ecx      ; moves eax (starts as 5) into local var (fild can't take a cpu register)
fild dword [ebp-4]          ; starts as 5, moves down with outer loop
fild dword [four]           ; the integer 4
fild dword [ebp-8]          ; starts as 5, moves down with inner loop
fmul st0, st1               ; 0 := 0 * i
fmul st0, st3
fistp dword [ebp-12]        ; move st0 to local var
mov eax, dword [ebp-12]     ; move local var to eax

这将推动 5,然后推动bad -NAN FFFF C0000000 00000000两次。 fmul似乎什么也没做(可能是因为价值观不好)。有没有更好的方法来加载值?似乎我做错了什么fild,但根据第一个链接中提供的示例,并且定义为herefild无论你给它什么,都只是将它推到 st0 上。

--EDIT 1-- 正如 Jester 所建议的,我现在每个循环都从 fpu 堆栈中弹出:

mov dword [ebp-8], 4
mov dword [ebp-12], ecx
fild dword [ebp-4]
fild dword [ebp-8]
fild dword [ebp-12]
fmul st0, st1
fmul st0, st2
fstp st2
fstp st1
fistp dword [ebp-8]
mov eax, dword [ebp-8]

此代码仍然崩溃。00000009 处的访问冲突,st0-4 为 0,st5 = 100,st6 = 4,st7 = 4

4

1 回答 1

1

您的原始代码是正确的。fld并将fild加载的值推送到 x87 堆栈上。此推送始终将值放入 st0,将 st0 的旧值移动到 st1,将旧 st1 移动到 st2,依此类推。

fild dword [ebp-4]   ; st0 = x
fild dword [ebp-8]   ; st1 = x, st2 = y
fild dword [ebp-12]  ; st2 = x, st1 = y, st0 = z
fmul st0, st1        ; st2 = x, st1 = y, st0 = z * y
fmul st0, st2        ; st2 = x, st1 = y, st0 = z * y * x
fistp dword [ebp-8]  ; st1 = x, st0 = y

您的代码可能会因为ebp指向错误的地方而崩溃,或者因为代码的另一部分(而不是您发布的部分)有错误。你没有说哪个指令崩溃了。在崩溃的那一刻,程序计数器(pc)指向崩溃指令。

我将您的代码放在一个简短的程序中,并在我的 OpenBSD/amd64 机器上的 gdb 中成功运行它。

section .data
    dd 0
    dd 0
    dd 5
space:

section .text
global main
main:
    mov ebp, space
    mov ecx, 5
    mov dword [ebp-8], 4
    mov dword [ebp-12], ecx
    fild dword [ebp-4]
    fild dword [ebp-8]
    fild dword [ebp-12]
    fmul st0, st1
    fmul st0, st2
    fistp dword [ebp-8]
    mov eax, dword [ebp-8]
    int 3

组装和运行:

$ nasm -felf64 fmul3.s && gcc -nopie -o fmul3 fmul3.o 
$ gdb fmul3
...
(gdb) run
...
Program received signal SIGTRAP, Trace/breakpoint trap.
...
(gdb) x/3wd (char *)&space - 12
0x601000 <__data_start>:        5       100     5
(gdb) print (int)$rax
$1 = 100

程序成功乘以 5 * 4 * 5 = 100。

于 2018-01-08T23:47:43.647 回答