在过程中操作运行时堆栈

时间:2019-03-14 20:02:53

标签: assembly x86 masm irvine32 irvine16

我正在研究一个包含两个过程的程序。一个将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

2 个答案:

答案 0 :(得分:2)

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

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

前2个摘要将保留ECXEDX。他们会破坏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 :(得分:1)

当过程P需要存储其寿命超过P寿命的数据时,该数据不能存储在P的堆栈帧内的堆栈上,因为,正如您所发现的,当P消失时,该数据会消失返回。

还有其他两个可行的选择。

  1. 让调用过程(主程序)在其堆栈帧中为数据分配空间,并将指向该空间的指针传递给P。如果调用者知道或可以确定多少数据P的最大大小,则此方法有效将产生。调用方应始终将大小与指针一起传递,以使P不会超出分配的空间。

  2. 在P中使用malloc(或某些等效项)并将指向数据的指针返回给调用方。