Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Микропроцессоры серии 80x86 поддерживают операнды трех типов: регистр, непосредственное значение, непосредственный указатель. Тип операнда явно задается в специальном поле машинной инструкции, именуемом mod
, поэтому никаких проблем в идентификации типов операндов не возникает. Регистр — ну, все мы знаем, как выглядят регистры; указатель по общепринятому соглашению заключается в квадратные скобки, а непосредственное значение записывается без них. Например:
MOV ECX, EAX; ← регистровые операнды
MOV ECX, 0x666; ← левый операнд регистровый, правый — непосредственный
MOV [0x401020], EAX; ← левый операнд — указатель, правый — регистр
Кроме этого, микропроцессоры серии 80x86 поддерживают два вида адресации памяти: непосредственную и косвенную. Тип адресации определяется типом указателя. Если операнд — непосредственный указатель, то и адресация непосредственна. Если же операнд‑указатель — регистр, то такая адресация называется косвенной. Например:
MOV ECX,[0x401020] ← непосредственная адресация
MOV ECX, [EAX]← косвенная адресация
Для инициализации регистрового указателя разработчики микропроцессора ввели специальную команду, вычисляющую значение адресного выражения addr
и присваивающую его регистру REG
, — LEA
. Например:
LEA EAX, [0x401020] ; Регистру EAX присваивается значение указателя 0x401020
MOV ECX, [EAX]; Косвенная адресация — загрузка в ECX двойного слова,
; расположенного по смещению 0x401020
Правый операнд команды LEA
всегда представляет собой ближний (near) указатель (исключение составляют случаи использования LEA
для сложения констант — подробнее об этом см. в одноименном пункте). И все было бы хорошо... да вот, оказывается, внутреннее представление ближнего указателя эквивалентно константе того же значения. Отсюда LEA
равносильно MOV
. В силу определенных причин MOV
значительно обогнал в популярности LEA
, практически вытеснив последнюю инструкцию из употребления.
Отказ от LEA
породил фундаментальную проблему ассемблирования — проблему OFFSET’a. В общих чертах ее суть заключается в синтаксической неразличимости констант и смещений (ближних указателей). Конструкция MOV
может грузить в EAX
и константу, равную 0x401020
(пример соответствующего C-кода: a=0x401020
), и указатель на ячейку памяти, расположенную по смещению 0x401020
(пример соответствующего C-кода: a=&x
). Согласись, a=0x401020
совсем не одно и то же, что a=&x
! А теперь представь, что произойдет, если в повторно ассемблированной программе переменная х
окажется расположена по иному смещению, а не 0x401020
? Правильно — программа рухнет, ибо указатель a
по‑прежнему указывает на ячейку памяти 0x401020
, но здесь теперь «проживает» совсем другая переменная!
Почему переменная может изменить свое смещение? Основных причин тому две. Во‑первых, язык ассемблера неоднозначен и допускает двоякую интерпретацию. Например, конструкции ADD
соответствуют две машинные инструкции: 83
и 05
длиной три и пять байт соответственно. Транслятор может выбрать любую из них, и не факт, что ту же самую, которая была в исходной программе (до дизассемблирования). Неверно «угаданный» размер вызовет смещение всех остальных инструкций, а вместе с ними и данных. Во‑вторых, смещение не замедлит вызвать модификацию программы (разумеется, речь идет не о замене JZ
на JNZ
, а о настоящей адаптации или модернизации), и все указатели тут же «посыплются».
Вернуть работоспособность программы помогает директива offset
. Если MOV
действительно загружает в EAX
указатель, а не константу, по смещению 0x401020
следует создать метку, именуемую, скажем, loc_401020
. Также нужно MOV
заменить на MOV
. Теперь указатель EAX
связан не с фиксированным смещением, а с меткой!
А что произойдет, если предварить директивой offset
константу, ошибочно приняв ее за указатель? Программа откажет или станет работать некорректно. Допустим, число 0x401020
выражало собой объем бассейна, в который вода втекает через одну трубу, а вытекает через другую. Если заменить константу указателем, то объем бассейна станет равен... смещению метки в заново ассемблированной программе и все расчеты полетят к черту.
Таким образом, очень важно определить типы всех непосредственных операндов, и еще важнее определить их правильно. Одна ошибка может стоить программе жизни (в смысле работоспособности), а в типичной программе тысячи и десятки тысяч операндов!
Отсюда возникает два вопроса:
|
|