Управление стеком времени выполнения в процедуре - PullRequest
3 голосов
/ 14 марта 2019

Я работаю над программой, которая содержит две процедуры.Тот, который выталкивает массив из N двойных слов без знака в стек, а тот, который выталкивает N двойных слов без знака из стека и сохраняет их в массиве.Я могу успешно поместить все элементы в стек, но затем процедура не может вернуться в основную программу, так как указатель стека (регистр esp) был изменен.

Я смог вернуться к main путем манипулирования регистром esp, чтобы сохранить адрес возврата, и я перезагружаю этот адрес в 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 ]

2 голосов
/ 17 марта 2019

Проверьте ваши помещения.В первом абзаце вы говорите о процедуре, которая помещает массив из N без знака двойных слов в стек, но ваш код имеет дело с массивом из N без знака байтов .

Кроме того, я наблюдаю, что ваш вывод на консоли будет в обратном порядке (к массиву) и что ваш код обнуляет входной массив, когда он будет прочитан.Я сохранил все эти вещи в следующих решениях.

Первые 2 фрагмента сохранят ECX и EDX.Они делают клобук 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

Если нет необходимости сохранять регистры ECX и EDX, но при этом допускается сгущение 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 голос
/ 15 марта 2019

Когда процедуре P необходимо хранить данные, срок жизни которых превышает время жизни P, данные не могут быть сохранены в стеке в кадре стека P, потому что, как вы обнаружили, они «уходят», когда P возвращается.

Вот еще пара опций, которые будут работать.

  1. Пусть вызывающая процедура (основная) выделяет пространство в своем кадре стека для данных и передает указатель на пространство в P. Это работает, если вызывающая сторона знает или может определить максимальный размер для количества данных P будет генерировать. Вызывающая сторона всегда должна передавать размер вместе с указателем, чтобы P не превышал выделенное пространство.

  2. Используйте malloc (или некоторый эквивалент) в P и возвращайте указатель на данные вызывающей стороне.

...