Проблема со стеком

Тема в разделе "WASM.ASSEMBLER", создана пользователем 4DA, 28 дек 2008.

  1. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Доброго времени суток. Вопрос к знатокам ассемблера.

    Моя функция вызывает мое же программное прерывание 65h.
    Переход осуществляется успешно.

    В обработчик прерывания через стек я передаю 2 слова(по 16 бит каждое).
    Я беру эти слова, подняв указатель стека вверх на 6 байт (6 байт - вроде FLAGS+CS:IP).

    mov bp, sp ;сохранить указатель на стек
    add sp, 6

    pop di
    pop cx

    так вот при {mov bp,sp, add sp,6} почему-то содержимое стека вокруг SS
    (в файле выделено) меняется, в результате, в конце фунция iret передает
    управление на непонятное место.

    Если поможете разобраться, буду очень благодарен, поскольку проблема для
    меня интересная=)

    Обновление, опустим пока прерывания.
    В зипе два скриншота программы в турбодебаггере. 1ый - программа до прибавления к SP, 6. Второй - после.

    Внизу видно, что память меняется (выделено красным)

    Спасибо
     
  2. MSoft

    MSoft New Member

    Публикаций:
    0
    Регистрация:
    16 дек 2006
    Сообщения:
    2.854
    круто

    а к есп, если я правильно понял твою идею, надо прибавлять не 6, а 8, т.к. в стеке кроме адреса возврата должен лежать еще и регистр флагов (этим iret и отличается от ret). Дос я не помню, поэтому посмотри сам, где передается в стек (и передается ли вообще) регистр с флагами
     
  3. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Перепутал, слово машинное в 16 бит, а не байт))).
    6 байт - вроде FLAGS(2) + CS:IP(2+2)

    Проблема не в этом, а том что почему-то стек меняется, если писать в BP или SP.
    Может я чего-то не догоняю, но мне непонятно почему так происходит)))
     
  4. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    MSoft
    Насколько я помню, все же 6, т.к. в 16битном режиме нет выравнивания на четыре.

    4DA
    А ты отнимаешь 6 от sp перед iret? При выполнении iret значения flags/cs/ip берутся с вершины стека, т.е. оттуда, куда указывает sp. Если прибавить к нему шесть и не вернуть на место, то естественно будет проблема. Я думаю, что лучше либо передавать параметры в регистрах, либо выполнять обработчик прерывания в отдельном стеке, либо сохранять flags/cs/ip где-то в памяти и потом восстанавливать их в стеке.
     
  5. MSoft

    MSoft New Member

    Публикаций:
    0
    Регистрация:
    16 дек 2006
    Сообщения:
    2.854
    я не про выравнивание говорил, а про лишний параметр в стеке (или его отсутствие). Вообще, я б советовал автору не извращаться так и делать прямое обращение к параметрам через mov cx,[bp+xxx], чтобы не было путаницы
     
  6. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Нет, я как раз из BP записывал в SP перед выходом. Проблема в том, что у меня стек портится непонятно почему
     
  7. meduza

    meduza New Member

    Публикаций:
    0
    Регистрация:
    15 авг 2008
    Сообщения:
    212
    4DA
    Перед сменой ss/sp надо запретить прерывания (cli)! После - разрешить (sti).
     
  8. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Ух-ты не знал. Это всегда надо делать?

    Проверил - не помогает. Просто тупо в обычной функции добавляю к SP 6. Байт 10 сразу перезаписываются в стеке. Может я что-то не правильно понимаю в работе МП?
     
  9. meduza

    meduza New Member

    Публикаций:
    0
    Регистрация:
    15 авг 2008
    Сообщения:
    212
    4DA
    я не совсем четко выразился, я имел ввиду смену ss и sp надо производить при отключенных прерываниях, а то может получится так, что некоторое внешнее прерывание (напр. таймер) сработает после смены ss, но до смены sp, и ss:sp будет указывать в небо.

    Я не внимательно посмотрел на твой код, у тебя действительно ошибка не в этом. Ты изменяешь только sp.

    А вообще, лучше в прерывание аргументы передавать через регистры, а не через стек.

    Ты наверное что-то не так делаешь. После add sp,6 изменится только sp.
     
  10. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    meduza
    При изменении ss происходит автоматическая блокировка аппаратных прерываний на время выполнения следующей инструкции. Может когда-то давно это было не так, но уже начиная с 486 (как минимум) cli/sti не обязательны. В защищенном режиме чуть сложнее -- там переключение стека надо производить с помощью lss, т.к. страничное прерывание не относится к аппаратному и может "проскочить" за счет предвыборки кода. Правда, делать так надо только в нулевом кольце т.к. переключение стека при смене уровня привилегий происходит актоматиечски (берется из TSS).

    4DA
    Покажи хотя бы код обработчика прерываний. М.б. изменения в памяти -- результат работы отладчика, а ошибка есть в коде?
     
  11. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Mika0x65, почему страничное прерывание может проскочить за счет предвыборки? Где про это почитать?

    Что именно не так? Вот именно только SP должен измениться, а у меня еще меняется стек=).
     
  12. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    4DA
    Я читал об этом у Григорьева и в Intel Manual. Точно страницу не помню сейчас. Суть в том, что процессор выбирает код "забегая вперед", т.е. не только по текущему значению EIP. Значит, может создаться такая ситуация:

    1. Инструкция 'mov ss, ax', например, находится в конце страницы.
    2. Следующая страница отсутствует в памяти.
    3. Мы находимся в нулевом кольце.

    Процессор выполняет 'mov ss, ax' попутно выбирая из памяти следующую команду (подробнее об этом лучше спросить у leo :) ). Происходит #PF, несмотря на то, что прерывания запрещены. Т.к. смены привилегий не происходит, то обработчик прерываний выполняется с некорректным стеком -- ss изменен, esp -- нет. Это, конечно, маловероятно и редко бывает, и если сегменты плоские, то ничего страшного, но лучше уберечься :).

    P.S. А код ты так и не привел.
     
  13. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    В древние времена реального режима и DOSа, к которым и относится вопрос топика, стек был только один и использовался и самой программой, и операционной системой, и обработчиками прерываний от устройств, и отладчиками. Соответственно всё, что в стеке находится ниже sp (по меньшим адресам), может быть в любой момент перезаписано без всякого участия со стороны программы просто из-за того, что не вовремя пришло аппаратное прерывание. Здесь они, впрочем, ни при чём, поскольку стек перезаписывает отладчик, под которым весь этот код проверяется в пошаговом режиме.
     
  14. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Сорри. К первому топику приложил зип, там 2 скриншота Turbo Debugger'a.
    Красным выделил что меняется.

    Забьем пока на прерывания. Там просто обычный код, который прибавляет к SP 6.

    Что там может быть не так?
     
  15. meduza

    meduza New Member

    Публикаций:
    0
    Регистрация:
    15 авг 2008
    Сообщения:
    212
    4DA
    diamond уже все разъяснил:
     
  16. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    diamond, вполне возможно, что отладчик перезаписывает стек по нижним адресам, но почему перезаписаывается и что выше SP?? Кстати, почему отладчик перезаписывает стек?
     
  17. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    То, что выше sp, не перезаписывается (на скриншотах видно, что начиная с ss:0x206 память не изменяется, а sp=0x206 на втором). Но после команды "add sp,6" адрес возврата оказывается ниже sp и тут же затирается.
    Перезапись происходит по следующей причине. Отобрать управление у программы можно только по какому-нибудь прерыванию. В пошаговом режиме (неэмулирующий) отладчик отбирает управление себе после каждой инструкции программы. Таким образом, происходит прерывание. В реальном режиме процессор при прерывании кладёт в стек (ss:sp, который один на всех) cs:ip:flags, которые затирают то, что находилось в стеке раньше, и передаёт управление обработчику прерывания. Обработчик может тут же установить собственный стек, но дело уже сделано - 6 байт ниже sp затёрты.
     
  18. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Ага, супер, теперь все понятно, но почему в перезаписанном стеке не фигурируют
    000A (флаги) и 18F2 (CS) и 0008(адрес следующей инструкции)?

    Они же сохраняются при int 3, которое отладчик, вставляет вместе каждой команды?

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

    Или по-другому все устроено?
     
  19. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Хороший вопрос. Проверил поведение TD на тестовой программе. Старые версии действительно помещают в стек ниже sp значения флагов и cs:ip. В версии поновее Borland, видимо, многое изменила, и теперь TD выходит в защищённый режим (использует DPMI для работы), а там всё по-другому и есть разделённые стеки, так что cs:ip:flags помещаются в стек не самой программы, а гипервизора и можно организовать отладчик так, что он вообще не будет трогать стек отлаживаемой программы (по такому принципу организован, например, DG aka DeGlucker). Почему TD стек всё-таки затирает... наиболее вероятный вариант объяснения (не гарантирую, что всё происходит именно так, но похоже на то) следующий: старые версии TD использовали стек программы не только из-за особенностей процессора, но ещё и для каких-то своих нужд, затирая больше 6 байт; когда модифицировали старую версию, решили как можно меньше трогать старый работающий код, так что гипервизор, обнаружив отладочное прерывание, просто передаёт управление основному коду отладчика, который по-прежнему исполняется в той же среде, что и программа, и по-прежнему частично использует её стек - отсюда кое-что всё-таки затирается, но уже на 6 байт меньше, потому что cs:ip:flags лежат в стеке гипервизора, который не видно.
     
  20. 4DA

    4DA New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2008
    Сообщения:
    15
    Адрес:
    Санкт-Петербург
    Ага, ну понятно_) Спасибо большое diamond-у, meduza, Mika0x65, MSoft-у