x86 汇编语言:用子例程生成的值填充数组

x86 Assembly Language: Filling an array with values generated by a subroutine

编辑:非常感谢您的帮助!我真的很感激它,因为出于某种原因,我在概念化装配时遇到了一些麻烦,但我正在将它们拼凑在一起。

1) 我正在使用调试器逐行执行。问题,Project2.exe 中 0x0033366B 处的未处理异常:0xC0000005:访问冲突写入位置 0x00335FF8 发生在此行:

1
mov [arrayfib + ebp*4], edx

我认为这可能是因为主过程无法访问来自另一个循环的 return 语句,但不确定 - 很难理解这里发生了什么。

2) 我添加了额外的评论,希望能更清楚地说明这一点。对于上下文:我已经链接了我用来访问斐波那契数的模型,我的目标是用值填充这个数组,从最后一个循环到第一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
.386
.model flat, stdcall
.stack 4096
INCLUDE Irvine32.inc
ExitProcess PROTO, dwExitCode: DWORD

.data
arrayfib DWORD 35 DUP (99h)

COMMENT !
eax =  used to calculate fibonacci numbers
edi =  also used to calculate fibonacci numbers
ebp =  number of fibonacci sequence being calculated  
edx =  return value of fibonacci number requested
!

.code
main PROC  
;stores n'th value to calculate fibonacci sequence to
mov ebp, 30
mov edx, 0
;recursive call to fibonacci sequence procedure
call FibSequence
mov [arrayfib + ebp*4], edx\t\t\t\t\t\t\t\t
dec ebp
jnz FibSequence


mov esi, OFFSET arrayfib
mov ecx, LENGTHOF arrayfib
mov ebx, TYPE arrayfib
call DumpMem
INVOKE ExitProcess, 0
main ENDP

;initializes 0 and 1 as first two fibonacci numbers
FibSequence PROC\t\t\t\t\t\t\t\t\t\t
mov eax, 0\t\t\t\t\t\t\t\t\t\t
mov edi, 1\t

;subrracts 2 from fibonacci number to be calculated. if less than 0, jumps to finish,
;else adds two previous fibonacci numbers together
L1:
sub ebp, 2\t\t\t\t\t\t\t\t\t\t
cmp ebp, 0
js FINISH
add eax, edi
add edi, eax
LOOP L1

;stores odd fibonacci numbers
FINISH:
test eax, 1
jz FINISHEVEN
mov edx, eax
ret    

;stores even fibonacci numbers
FINISHEVEN:
mov edx, edi
ret


FibSequence ENDP
END main


你的斐波那契函数破坏 EBP,返回小于零。

如果您的数组位于页面的开头,则 arrafib + ebp*4] 将尝试访问前一页并出错。注意 0x00335FF8 的故障地址 - 最后 3 个十六进制数字是 4k 虚拟页面内的偏移量,即 0x...3FF8 = 0x...4000 + 4*-2.

所以这正是发生的情况:当您的 mov 存储执行时,EBP = -2。

(在典型的调用约定中,函数调用破坏它们的寄存器参数是正常的,尽管使用 EBP 进行参数传递是不寻常的。通常在 Windows 上,你会在 ECX 和/或 EDX 中传递参数,然后返回在 EAX 中。或者在堆栈上,但这对性能来说很糟糕。)

(还有很多其他的东西对你的斐波那契函数也没有意义,例如我认为你想要 jmp L1 而不是 loop L1 这就像没有设置标志的 dec ecx / jnz L1)。

在汇编语言中,每条指令对架构状态(寄存器值和内存内容)都有特定的影响。您可以在指令集参考手册中查找此效果,例如 https://www.felixcloutier.com/x86/index.html。

没有"魔法"可以为您保留寄存器。 (好吧,如果您编写 proc foo uses EBP 之类的东西,MASM 将为您推送/弹出,但在您了解它为您做什么之前,最好不要让汇编器向您的代码添加指令。)

如果您希望函数保留其调用方的 EBP 值,则需要以这种方式编写函数。 (例如 mov 将值复制到另一个寄存器。)请参阅什么是被调用者和调用者保存的寄存器?了解更多关于这个想法的信息。

maybe this because the return statement from the other loop is not able to be accessed by the main procedure

这没有任何意义。

ret 就是我们写 pop eip 的方式。没有非本地影响,您只需确保 ESP 指向您要跳转到的地址(通过调用推送)。也就是返回地址。