【问题标题】:x86 Assembly "Access violation executing location 0x00000000"x86 程序集“访问冲突执行位置 0x00000000”
【发布时间】:2023-03-23 16:37:01
【问题描述】:

我必须编写一个汇编程序,该程序将调用一个外部汇编文件来生成用户指定的素数数量。现在我想生成小于 10 的素数,然后将其报告回屏幕,稍后再担心用户输入。 genprimes.asm 中的代码将正确生成素数并将它们存储到数组中。我想将它们压入堆栈(参见 genprimes.asm 中的第 79 行),并在调用 seive.asm 中的该函数返回后恢复它们。如果我注释掉“push ecx;”,我的代码在 genprimes.asm 中有效在第 79 行。 sieve.asm 在第 32 行中断,这是代码“调用 genPrimes”。我尝试单步执行“genprimes.asm”中的所有代码,然后进入 return 语句,然后 Visual Studio 说我无法进一步调试,因为它不支持跳回主文件。我不知道为什么会这样。我怎样才能让我的素数回到我称之为“genprimes.asm”的主文件“sieve.asm”?

筛子.asm:

.586
.MODEL FLAT
INCLUDE io.h
EXTERN GenPrimes2:PROC
PUBLIC genPrimes
.STACK 4096                 ; reserve 4096-byte stack
.DATA                       ; reserve storage for data

count    DWORD   ?
sieve    BYTE    10000 DUP(1)
string   BYTE    40 DUP (?)
prompt1  BYTE    "Enter number of primes: ", 0
prompt2  BYTE    "prime number: ", 0
primenum BYTE    11 DUP (?), 0

.CODE

genPrimes   PROC
           ; push   ebp                  ; save base pointer
           ; mov    ebp, esp             ; establish stack frame
           ; push   ebx
            ; CODE
            call GenPrimes2  ;*** breaks here ***

           ; pop    ebx
           ; pop    ebp

            ret                         ;exit genPrimes
genPrimes    ENDP

_sieve  PROC                            ; start of sieve program code

      input   prompt1, string, 40       ; read ASCII characters

      call  genPrimes

      output  prompt2, primenum         ; output label and sum

      mov   eax, 0          ; exit with return code 0
      ret

_sieve  ENDP

END

genprimes.asm:

.586
.MODEL FLAT


.STACK 4096                 
n=10
.data
    prime DWORD n DUP(?)

.code
GenPrimes2  PROC
mov ebx, 4
mov ecx, 0
loopArray:
    inc ecx
    mov prime[ebx], ecx
    add ebx, 4
    cmp ecx, n
jb  loopArray

mov eax, 3
mov ebx, 2
mov edx, 0

mov ecx,3

sieve_loop:

    cmp eax,ebx
    je skip

    mov edx, 0 ;zero out remainder
    div  ebx
    cmp edx,0 ; if remainder 0, not a prime
    je    NotPrime ;Jump if is a factor, since it cant be prime

; compare eax with n, if equal increment ebx
    cmp ecx,n
    jge    incrementEbx

;  compare ebx with n, if equal end sieve
    cmp ebx, n
    je sieve_end

    inc ecx
    mov eax, ecx

jmp sieve_loop

skip:
inc eax
jmp sieve_loop


NotPrime:
    mov eax, ecx ; store count in eax
    imul ecx, 4
    mov prime[ecx],0
    mov ecx, eax
    inc ecx ; increment ecx count
    inc eax ; increment eax divisor
    jmp sieve_loop

incrementEbx:
inc ebx
mov eax, 3 ; dividend
mov ecx, 3 ; counter

jmp sieve_loop


sieve_end:
    mov ebx, 4
    mov eax, 0
; *************  Add break point on print loop, ecx will be loading with primes and 0's  ********************
; *************  All non-prime numbers have been changed to a 0                          ********************
    PrintLoop:
    mov ecx, prime[ebx] ; Prime numbers are the non-zeros in this Array
    push   ecx  ; **This is somehow breaking it, but doesnt throw and error  
                 ;** when steeping through in Visual Studio?!?
    add ebx, 4
    cmp ebx, 40
    jb  PrintLoop


    mov   eax, 0          ; exit with return code 0
    ret
GenPrimes2 ENDP
END

【问题讨论】:

  • 不,我将代码粘贴到记事本++中以获取行数。它的代码调用“GenPrimes2 ;*** 在此处中断 ***” 这意味着错误应该在 genprimes.asm 中?所以我在文件中放了一个断点,它没有中断,一直到返回语句。 Visual Studio 不会让我使用 return 语句跳回来。所以我无法确定它为什么会破裂。我认为这是我如何在代码底部的循环中将值推送到 genprimes.asm 中的堆栈。如果我将其注释掉,我不会收到任何错误,但我可以使用调试功能逐步完成循环而不会出错

标签: x86


【解决方案1】:

push ecx 指令使您的堆栈失衡。没有对应的pop。您将返回到从 ecx 推送的最后一个值给出的地址,而不是返回地址。您要么需要弹出您推送的内容,要么在进入时保存 sp 并在退出返回之前恢复它。

【讨论】:

    【解决方案2】:

    您需要了解调用约定。

    简而言之:当您执行call 时,下一条指令的 IP 将被放入堆栈。 ret 将从堆栈顶部获取地址,并直接到达该地址。

    但是您推送的是PUSH CX,所以ret 会找到错误的返回地址。通常您返回 AX(作为返回值)或将数据放在返回地址之前。这应该由调用者设置。

    【讨论】:

      【解决方案3】:

      有几种不同的方法可以从数组中的函数返回信息。

      1. 在调用者中分配数组并将指向数组的指针及其大小传递给函数。请注意,此分配可以在堆栈上、静态或动态(例如,使用 malloc)。
      2. 在函数中声明一个静态数组,并从函数返回指向该数组的指针。
      3. 在函数中动态分配数组并返回指针。
      4. 在高级语言中,返回一个动态数组。

      这些方法各有利弊。例如,使用选项 3,调用者必须知道它负责释放数组。

      在您的情况下,由于调用者将指定要生成多少个值,因此选项 1 可能是最好的。如果只有被调用函数知道它要返回多少项,选项 3 或 4 通常是最好的。


      更多关于利弊的细节:

      选项 1:调用者必须知道要保留多少空间。如果没有足够的空间,该函数必须返回一个错误或一些指示还有更多项目。 sprintf 就是一个例子。

      选项 2:当所需空间量固定时,这可能是一个不错的选择。但是调用者必须知道,如果它调用该函数两次,第一个值将被覆盖。这方面的一个例子是 C 库函数 ctime,它总是返回一个固定长度的字符串。

      选项 3:调用者必须知道它在处理完数组后负责释放数组,并且它需要使用与用于分配数组的函数相同的分配器来释放数组。 strdup 就是一个例子。

      选项 4:如果您使用支持它的语言,这通常是最佳选项。对于汇编语言编程,实现这一点的基础设施可能非常复杂,除非它在整个项目中得到足够的使用以使其物有所值。

      【讨论】:

        猜你喜欢
        • 2013-10-11
        • 2015-07-15
        • 1970-01-01
        • 2013-04-15
        • 2022-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多