Управление стеком времени выполнения в процедуре
Я работаю над программой, которая содержит две процедуры. Тот, который выталкивает массив из N двойных слов без знака в стек, а тот, который выталкивает N двойных слов без знака из стека и сохраняет их в массиве. Я могу успешно поместить все элементы в стек, но затем процедура не может вернуться к основной программе, потому что указатель стека (esp
Регистрация) была изменена.
Я смог вернуться к основному, манипулируя 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 ответа
Проверьте ваши помещения. В первом параграфе вы говорите о процедуре, которая помещает массив из 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
Когда процедуре P необходимо хранить данные, срок жизни которых превышает время жизни P, данные не могут быть сохранены в стеке в кадре стека P, поскольку, как вы обнаружили, они "уходят", когда возвращается P.
Вот еще пара вариантов, которые будут работать.
Пусть вызывающая процедура (основная) выделит пространство в своем кадре стека для данных и передаст указатель на пространство в P. Это работает, если вызывающая сторона знает или может определить максимальный размер для того, сколько данных P сгенерирует. Вызывающая сторона всегда должна передавать размер вместе с указателем, чтобы P не превышал выделенное пространство.
Используйте malloc (или некоторый эквивалент) в P и возвращайте указатель на данные вызывающей стороне.