Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2014- СП 3.0 ЛАБЫ ОБЯЗАТ.doc
Скачиваний:
99
Добавлен:
01.03.2016
Размер:
896 Кб
Скачать
      1. Возврат управления в основную программу

Итак, мы худо-бедно ушли в процедуру, выполнили ее код (команды к31 – к34) и потом надо вернуться на команду к5 и продолжить выполнение основной программы. Как это можно сделать?

Ну, «лобовое» решение состоит в повторении того же подхода, что и при вызове процедуры - выполнить после к34 команду JMPна адрес 142. Но это решение плохое по таким причинам.

Дело в том, что оно (такое решение) излишне жёсткое. Да и как на этапе программирования обеспечить такой переход? Решение, конечно, возможно. Например, перед командой к5 поставить явным образом какую-нибудь метку:

Метка_к5:

К5

А в процедуре после к34 написать обратный возврат

Jmp Метка_к5

Возврат состоится. Но он будет производиться всегда только на метку Метка_к5 .

Негодность такого решения становится понятной, если представить, что процедура Р55 в основной программе вызывается несколько раз, из разных точек кода. Тогда и возврат в основную программу для ее продолжения потребуется производить тоже в разные точки. А Jmp Метка_к5 эту проблему не решает никак. Жёстко – только на к5 и всё. Не годится.

Как же обеспечить возвраты всегда на правильное место продолжения, откуда бы ни была вызвана процедура?

Идея правильного решения проста. Надо перед передачей управления адрес возврата куда-то записать (сохранить), а в конце работы процедуры восстановить EIPиз этого сохранённого места.

Но тут мы опять «наступаем на те же грабли» в виде невозможности прямого присвоения регистру EIPтребуемого значения. Это значит, что если мы даже и сохранимEIPвозврата в какую-то переменнуюMyVar, то возврат операторомmov EIP, MyVarне может быть откомпилирован как недопустимый. Так что же, безвыходная ситуация?

Отнюдь. Предусмотрительные инженеры Intelвключили в систему команд специальную команду возврата из процедурыRET.

Она читает число с вершины системного стека и записывает его в EIP. Старое значениеEIP, естественно, перетирается. Это и есть передача управления куда надо.

Значит, задача программиста состоит, во-первых, в сохранении EIPвозврата в стек и, во-вторых в том, чтобы обеспечить положение указателя стекаESPточно на сохраненном адресе возврата в момент выполнения в процедуре командыRET.

Ну, первая подзадача решается просто:

Push EIP; кто не помнит:Pushзаносит операнд в стек.

Втораяже подзадача может быть решена только дисциплиной программиста. Само собой (типа автоматически) этого не сделать никак. То есть, работая со стеком внутри процедуры, программист должен позаботиться о том, чтобы когда дело дойдет до выполнения RET, указатель стекаESPдолжен обязательно указывать именно на ту ячейку стека, в которую было занесеноEIPвозврата во время вызова процедуры.

Подведем итог.

Для корректного обеспечения возврата надо уходить в процедуру примерно так:

Push (EIP + длина_команды_передачи_управления)

JMP P55

Странный операнд команды Pushобъясняется тем, что мы должны занести в стек адрес команды, следующей заJMPP55. Мы этот оператор написали, но откомпилировать его невозможно, потому что выражения как операнд командыPushкомпилятор не пропустит. В ассемблере допускаются только такие выражения, которые сводятся к константе, а тут не этот случай. Неужели опять безвыходная ситуация?

Конечно же, нет. В системе команд процессора имеется специальная команда, специально сконструированная для вызова процедур –