помещать одно и то же значение в стек - PullRequest
0 голосов
/ 12 октября 2019

В x-86, если вы извлекаете значение из регистра (например,% eax), а затем возвращаете, программа передает управление по адресу, соответствующему значению в% eax, насколько я понимаю.

При другом запуске программы, если вы отредактировали код так, что вы поместили его в стек другим способом (например, с помощью разыменования регистра, перемещения этого значения в другой регистр, а затем отправки этого регистра),и затем возврат, программа должна также передать управление по адресу, соответствующему значению, выдвинутому вправо?

В двух разных запусках программы, если выдвинутые значения были эквивалентны (даже если они не были выдвинуты одинаково), тогда будет ли программа вести себя одинаково при каждом запуске?

Я не могу показать свой код, но я хотел убедиться, что мое концептуальное мышление было правильным, поскольку мое выдает ошибки в одном случае, но не в другом. Спасибо!

ex 1:

func1:
/* should push address onto stack when calling */
call func2
...
...

func2:
/* should pop address and transfer control back to func1 */
ret

ex 2:

func1:
...
...

func2:
/* %eax contains value equivalent to address from the first example */
pushl %eax

/* should pop %eax and transfer control to address contained in %eax */
ret

func2 также должен вернуться к func1, верно? но второй пример не работает

1 Ответ

1 голос
/ 12 октября 2019

Да, вы можете не соответствовать 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 не кажется полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...