Отладка приложений

       

Формат инструкции и адресация памяти


Все инструкции Intel CPU имеют следующий основной формат:

[префикс] инструкция [операнды]

Префикс присутствует только в некоторых строчных инструкциях. Эти ситуации рассмотрены ниже, в разделе "Манипуляции со строками" данной главы. Формат операндов определяет направление действия операции. В инструкциях с двойным операндом источник указывается во втором операнде, а пункт назначения — в первом, так что операнды читаются (и передают данные) справа налево.

Инструкция с единственным операндом:

XXX источник



Инструкция с двумя операндами (разделяемыми запятой):

XXX получатель, источник

Операнд-источник может быть регистром, обращением (ссылкой) к памяти или непосредственным (жестко закодированным) значением. Операнд-получатель может быть регистром или обращением к памяти. Intel CPU не допускают, чтобы и источник, и получатель были обращениями к памяти.

Обращения к памяти — это такие операнды, которые помещаются в квадратные скобки. Например, обращение к памяти [0040l29Ah] означает "получить значение, размещенное в ячейке памяти Ох0040129А". С помощью суффикса b в языке ассемблера указывается шестнадцатеричное число. Запись [0040i29Ah] — означает то же самое, что и доступ через указатель к целому числу в языке С (*pivai)- Обращения к памяти могут храниться в регистрах. Например, [ЕАХ] означает "получить значение, хранящееся в памяти по адресу, указанному в регистре ЕАХ". Другое часто используемое обращение к памяти указывает адрес, добавляя (шестнадцатеричное) смещение к значению регистра. Например, [ЕАХ+ОСh] означает "добавить смещение ОхС к адресу, хранящемуся в ЕАХ, и получить значение, хранящееся в памяти по этому адресу". 

 Эту операцию в С называют "разыменованием указателя", т. е. извлечением из памяти значения по адресу, хранящемуся в указателе. — Пер

Допустимы также обращения к памяти, включающие вычисление адреса по содержимому нескольких регистров (например,[ЕAX+ЕBX*2]), но такая форма оказывается довольно сложной для приложений.


Часто встречается обращение, которому предшествует спецификатор размера указателя, дифференцирующий размеры обращений к памяти. Размеры указателя специфицируются как BYTE PTR, WORD PTR и DWORD PTR (для ссылок размером в байт, слово и двойное слово, соответственно). Можно также представлять их, как приведение типов в C++. Если дизассемблер не указывает размера указателя, то он принимается равным двойному слову.

Иногда применяется прямое обращение к памяти в инструкции, т. е. в нем можно видеть непосредственный адрес соответствующего участка памяти. Например, обращение [ЕВХ] — это просто адрес памяти, содержащийся в регистре ЕВХ, и, чтобы просмотреть его содержимое (т. е. содержащийся в нем адрес), можно просто открыть окно Memory и ввести значение ЕВХ. Однако в других случаях невозможно вычислить ссылку без выполнения сложного шестнадцатеричного умножения. К счастью, окно Registers показывает, на какую память собирается сослаться инструкция.

Обратите внимание на строку "0012F988 = 0012F9D4" в нижней части рис. 6.1, отображающую эффективный адрес. Текущая инструкция (располагающаяся в данном случае по адресу Ox5F42D8B8) ссылается на адрес Ox0012F988 (левая сторона строки). Правая часть строки — это значение Ox0012F9D4, располагающееся по адресу Ox0012F988. Эффективный адрес в окне Registers показывают только те инструкции, которые выполняют обращение к памяти. Поскольку CPU x86 допускают лишь один операнд с обращением к памяти, то, прослеживая эффективный адрес, можно увидеть, к какой ячейке памяти вы собираетесь выполнить доступ и какое значение в ней расположено.

Если доступ к памяти неправомерен, то CPU генерирует исключение (типа "General Protection Fault (GPF — общая ошибка защиты)" или "ошибка страницы"). GPF указывает, что приложение пыталось получить доступ к памяти, к которой оно доступа не имело. Ошибка страницы свидетельствует о попытке получить доступ к позиции памяти, которой не существует. Просматривая строку ассемблера, на которой происходит аварийный останов, обратите внимание на ту ее часть, где находится ссылка на память.Из нее можно узнать, какие значения были недействительными. Например, если этот ссылочный операнд выглядит как [ЕАХ], то нужно просмотреть значение регистра ЕАХ в окне Registers. Если ЕАХ содержит недействительный адрес, необходимо стартовать обратное сканирование листинга ассемблера, чтобы увидеть, какая инструкция устанавливает в ЕАХ неправильное значение. Учтите, для того чтобы найти эту инструкцию, может потребоваться обратный проход через несколько вызовов. Далее (в разделе "Окна Memory и Disassembly" этой главы) показано, как можно пройти стек вручную.



Содержание раздела