Это возможный код сборки int x = plus_10(40);
push 40 ; push argument
call plus_10 ; call function
retadd: add esp, 4 ; clean up stack (dummy pop)
; result of the function call is in EAX, per the calling convention
; if compiled without optimization, the caller might just store it:
mov DWORD PTR [ebp-x], eax ; store return value
; (in eax) in x
Теперь, когда вы вызываете plus_10
, адрес retadd
помещается в стек инструкцией call
. Фактически это push
+ jmp
, а ret
- pop eip
.
Таким образом, ваш стек выглядит следующим образом в функции plus_10
:
| ... |
+--------+
| 40 | <- ESP+4 points here (the function argument)
+--------+
| retadd | <- ESP points here
+--------+
ESP
указывает на область памяти, которая содержит адрес возврата.
Теперь, если вы используете pop edx
, обратный адрес переходит в edx
, а стек выглядит так:
| ... |
+--------+
| 40 | <- ESP points here
+--------+
Теперь, если вы выполните ret
в этот момент, программа фактически перейдет к адресу 40 и, скорее всего, к segfault или будет вести себя каким-то другим непредсказуемым образом.
Фактический код сборки, сгенерированный компилятором, может отличаться, но это иллюстрирует проблему.
Кстати, более эффективный способ написания вашей функции заключается в следующем: это то, что большинство компиляторов будет делать с включенной оптимизацией для не встроенной версии этой крошечной функции.
global plus_10
plus_10:
mov eax, [esp+4] ; retval = first arg
add eax, 10 ; retval += 10
ret
Это меньше и немного более эффективно, чем
mov eax, 10
add eax, [esp+4] ; decode to a load + add.
ret