Да, вы можете не соответствовать call / ret и делать их вручную (при большом коде производительности), но вы должны правильно эмулировать их (включая то, что они делают с указателем стека), если вы не хотите ничего ломать.
, но второй пример не работает
push
/ ret
эквивалентно jmp
абсолютному адресу. (За исключением производительности: это всегда вызывает неверный прогноз ветвления и несовпадение предиктора call / ret).
Вы прыгаете в правильное место (предположительно), но забыли выбросить адрес возврата из стека . Таким образом, вы «возвращаетесь» с ESP, указывающим на неправильное место. Это может легко привести к сбою у большинства абонентов;их собственные ret
могут выдавать неправильный адрес возврата.
(Вызывающий абонент, который использовал EBP в качестве указателя кадра и не имел доступа к чему-либо относительно ESP до leave
/ ret
, может не заметить. например, если asm вызывающей стороны был сгенерирован компилятором C. в режиме отладки. Даже в этом случае современный Linux требует выравнивания стека 16 байт перед call
, и вызовы функций, сделанные после вызова вашего func2
, сломали его ESP больше не будутсделано с 16-байтовым выровненным стеком. Некоторые функции libc могут аварийно завершить работу при нарушении ABI таким образом.)
Обычно вы бы описали вещи как:
call target
= push $retaddr
;jmp target
;retaddr:
ret
= pop %tmp
;jmp *%tmp
. Это косвенная ветвь.
(Подумайте ret
как способ написания pop %eip
)
Но да, если у функции есть только один вызывающий объект, вы можете жестко закодировать адрес возврата и использоватьadd $esp, 4
/ jmp after_call
вместо ret
. (Снова нарушая предсказание ветвления для будущих повторов, не используя ret
для возврата из call
.)
Или замените call
вызывающего абонента на jmp
и jmp
обратно. Это фактически делает func2
блоком, который является той частью func1
для некоторых способов посмотреть на него. Он не может быть call
извлечен откуда-либо еще, потому что он не принимает адрес возврата.
Берет адрес возврата (т. Е. Удаляет его из стека перед переходом назад), но игнорирует его и всегда переходит кжестко запрограммированное местоположение в func1
не кажется полезным.