【问题标题】:x86 Assembly Language: Filling an array with values generated by a subroutinex86 汇编语言:用子例程生成的值填充数组
【发布时间】:2019-10-29 23:25:26
【问题描述】:

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

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

mov [arrayfib + ebp*4], edx     

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

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

.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								
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										 
mov eax, 0										
mov edi, 1	

;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										
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 

【问题讨论】:

  • 不确定“注释语法”指的是什么,但mov [DWORD + (edx-1)],edx 没有意义。您的意思可能是mov [arrayfib + ebp], edxLOOP L1 也毫无意义,尤其是因为您甚至还没有初始化 ecx。目前尚不清楚您希望代码做什么。注释您的代码,特别是如果您希望其他人提供帮助。 PS:FibSequence ENDP 之前的最后 3 个mov 指令可能属于更上一层,在call DumpMem 之前。
  • 更有可能是mov [arrayfib + ebp*4], edx,因为您有一个 4 字节双字数组,您想用ebp 对其进行索引。此外,您可能希望使用 EAX 作为返回值。这是 x86 调用约定的传统方式。在汇编中手工编写意味着您可以编写任何您想要的调用约定,例如编写在 EDX 中返回的函数,只要您像您正在做的那样使用 cmets 记录它。所以你用EDX没有错,只是标新立异。
  • 至于调试问题;你用的是什么调试器?标记和/或编辑您的问题。如果您使用单步指令(不是“继续”),您应该能够一次单步执行一条指令,包括查看指令何时出错。
  • @PeterCordes 我的问题已被相应地编辑。谢谢
  • 你在用汇编而不是 JavaScript 编写。不要对您的代码使用“JS 代码 sn-p”格式;这不能在浏览器中运行。我已经修复了您问题的先前版本,但您重新引入了 JS 格式。只需使用缩进(如果您选择代码并点击 control-k 或 gui 图标,则默认使用),或三次反引号。此外,在您的代码中,您应该在标签和指令的右侧将指令缩进一个制表位。更具可读性。请参阅 this code-review 了解良好风格的示例。

标签: arrays assembly x86 fill subroutine


【解决方案1】:

您的斐波那契函数会破坏 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 将值复制到另一个寄存器。)有关此想法的更多信息,请参阅 What are callee and caller saved registers?

这可能是因为主过程无法访问来自另一个循环的返回语句

这没有任何意义。

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

【讨论】:

    猜你喜欢
    • 2021-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2019-02-13
    相关资源
    最近更新 更多