在过程中操作运行时堆栈

问题描述 投票:3回答:2

我正在开发一个包含两个程序的程序。一个将N个无符号双字数组推送到堆栈,另一个从堆栈中弹出N个无符号双字并将它们存储在数组中。我能够将所有元素成功地推送到堆栈,但是程序无法返回到主程序,因为堆栈指针(esp寄存器)已被更改。

我已经能够通过操纵esp寄存器返回main,以便保存返回地址,并在返回之前将该地址重新加载到esp中。但是,在调用下一个过程时,我推送到堆栈的条目已被覆盖。

在处理过程中是否有正确的方法将数据保存在堆栈中?

这是我的一些代码:

主要程序:

main PROC
main_loop:
; Main menu
    mov edx, offset choicePrompt
    call WriteString

    read_input:
        call ReadInt
        jno good_input
        jmp main_loop

    good_input:
        cmp eax, 0
        je create_vector
        cmp eax, 1
        je array_stack
        cmp eax, 2
        je stack_array
        cmp eax, -1
        je end_program

        call crlf
        jmp main_loop

    create_vector:
        call CreateVector
        jmp end_options

    array_stack:
        call ArrayToStack
        jmp end_options

    stack_array:
        call StackToArray
        jmp end_options

    end_options:
        call crlf
        jmp main_loop

end_program:
    mov edx, offset exitPrompt
    call WriteString
    call crlf
exit
main ENDP

在ArrayToStack过程中将数组推送到堆栈:

mov esi, offset Vector
mov ebx, N
dec ebx
mov eax, 0
mov ecx, -1

push_array_loop:
    inc ecx
    mov al, [esi + ecx]
    push eax
    mov [esi + ecx], byte ptr(0)
    cmp ecx, ebx
    jl push_array_loop

在StackToArray过程中将堆栈写入控制台:

mov eax, N
mov ebx, 4
mul ebx
mov ebx, eax
mov ecx, 0
write_stack_loop:
    mov eax, [esp + ecx]
    add ecx, 4
    call WriteDec
    mov edx, offset spacePrompt
    call WriteString
    cmp ecx, ebx
    jl write_stack_loop
assembly x86 masm irvine32 irvine16
2个回答
2
投票

检查你的房屋。在第一段中,您将讨论将N个无符号双字数组推送到堆栈的过程,但是您的代码处理的是N个无符号字节的数组。

此外,我观察到控制台上的输出将以相反的顺序(到arrray),并且您的代码在读取时将输入数组归零。我把所有这些东西保存在下面的解决方案中。

前2个片段将保留ECXEDX。他们做clobber EAX

编码问题的真正解释当然是看每个步骤如何修改堆栈。仔细观察!

ArrayToStack:

                                                  [ ret ]
                                                  ^ esp

    mov     eax, N                 ; The number of array elements is a runtime value
    dec     eax
    shl     eax, 2
    sub     esp, eax

                             <-- eax = (N-1)*4 -->
                             [     ][ ... ][     ][ ret ]
                             ^ esp

    push    dword ptr [esp + eax]

                      [ ret ][     ][ ... ][     ][     ]
                      ^ esp

    push    ecx
    push    edx

        [ edx ][ ecx ][ ret ][     ][ ... ][     ][     ]
        ^ esp

    xor     ecx, ecx
  ToStack:
    xor     edx, edx
    xchg    dl, [Vector + ecx]     ; Reading byte-sized element while zeroing the source
    mov     [esp + 12 + eax], edx
    inc     ecx
    sub     eax, 4
    jnb     ToStack

        [ edx ][ ecx ][ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
        ^ esp                ^ esp+12

    pop     edx
    pop     ecx

                      [ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
                      ^ esp

    ret                            ; EAX ends at -4

                             [ a_N ][ ... ][ a_2 ][ a_1 ]
                             ^ esp


StackToConsoleProcedure:

                      [ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
                      ^ esp

    push    ecx
    push    edx

        [ edx ][ ecx ][ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
        ^ esp                ^ esp+12

    xor     ecx, ecx
  FromStack:
    mov     eax, [esp + 12 + ecx*4]
    call    WriteDec
    mov     edx, offset spacePrompt
    call    WriteString
    inc     ecx
    cmp     ecx, N
    jb      FromStack
    shl     ecx, 2                 ; ECX = N*4
    mov     eax, [esp + 8]         ; Fetch return address
    mov     [esp + 8 + ecx], eax

                      <-------- ecx = N*4 ------->
        [ edx ][ ecx ][     ][ a_N ][ ... ][ a_2 ][ ret ]
        ^ esp         ^ esp+8

    mov     eax, ecx
    pop     edx
    pop     ecx

                      <-------- eax = N*4 ------->
                      [     ][ a_N ][ ... ][ a_2 ][ ret ]
                      ^ esp

    add     esp, eax

                                                  [ ret ]
                                                  ^ esp


    ret                            ; EAX ends at N*4

如果没有必要保留ECXEDX寄存器,但仍然允许EAX被破坏:

ArrayToStack:
    mov     eax, N
    dec     eax
    shl     eax, 2
    sub     esp, eax
    push    dword ptr [esp + eax]
    xor     ecx, ecx
  ToStack:
    xor     edx, edx
    xchg    dl, [Vector + ecx]
    mov     [esp + 4 + eax], edx
    inc     ecx
    sub     eax, 4
    jnb     ToStack
    ret

StackToConsoleProcedure:
    xor     ecx, ecx
  Fromtack:
    mov     eax, [esp + 4 + ecx*4]
    call    WriteDec
    mov     edx, offset spacePrompt
    call    WriteString
    inc     ecx
    cmp     ecx, N
    jb      FromStack
    shl     ecx, 2
    pop     eax
    add     esp, ecx
    push    eax
    ret

1
投票

当过程P需要存储寿命超过P的生命周期的数据时,数据不能存储在P的堆栈帧内的堆栈中,因为正如您所发现的,当P返回时它会“消失”。

以下是其他一些可行的选项。

  1. 让调用过程(main)在其堆栈帧中为数据分配空间,并将指向空间的指针传递给P.如果调用者知道或可以确定P将生成多少数据的最大大小,则这种方法有效。调用者应始终传递一个大小和指针,因此P不会超过分配的空间。
  2. 在P中使用malloc(或一些等价物)并将指向数据的指针返回给调用者。
© www.soinside.com 2019 - 2024. All rights reserved.