16 lines
3.7 KiB
Plaintext
16 lines
3.7 KiB
Plaintext
Резюме: Почему нужна специальная обработка рспикс/р12икс и рбпикс/р13икс
|
||
В архитектуре x86-64 байт ModR/M имеет исторические ограничения. Определенные значения в поле r/m (младшие 3 бита) имеют двойное значение в зависимости от поля mod. Если это не учесть, энкодер сгенерирует байты, которые выглядят логично, но процессор декодирует их совершенно иначе.
|
||
🪤 Ловушка №1: рспикс / р12икс (индекс 4)
|
||
|
||
Суть: В ModR/M значение r/m = 100 (десятичное 4) — это не код регистра. Это специальный сигнал процессору: "Следующий байт инструкции — это байт SIB (Scale-Index-Base)".
|
||
Что будет, если проигнорировать: Если закодировать [рспикс] просто как mod=00, r/m=4, процессор не остановится на этом байте. Он прочитает первый байт следующей инструкции (или данных) как байт SIB. Это вызовет сдвиг потока декодирования, превратив последующий код в бессмыслицу, что приведёт к SIGILL (недопустимая инструкция) или SIGSEGV.
|
||
Правильное решение: Для [рспикс] или [р12икс] энкодер обязан сгенерировать ModR/M с r/m=4, а сразу за ним — байт SIB 0x24 (где scale=00, index=100 (нет индекса), base=100 (rsp/r12)).
|
||
|
||
🪤 Ловушка №2: рбпикс / р13икс (индекс 5)
|
||
|
||
Суть: В 64-битном режиме комбинация mod=00 и r/m=101 (десятичное 5) зарезервирована исключительно под RIP-relative адресацию (например, [метка] для доступа к глобальным данным).
|
||
Что будет, если проигнорировать: Если попытаться закодировать честное [рбпикс] как mod=00, r/m=5, процессор проигнорирует тот факт, что вы хотели использовать регистр. Вместо этого он прочитает следующие 4 байта как 32-битное смещение относительно текущего указателя инструкций (RIP). Это приведёт к чтению мусора из памяти или крашу программы.
|
||
Правильное решение: Использовать mod=00 для этих регистров запрещено. Энкодер обязан принудительно переключиться на mod=01 (8-битное смещение) и добавить фиктивный байт смещения 0x00 сразу после ModR/M. Из-за этого инструкция [рбпикс] всегда на 1 байт длиннее, чем [раикс].
|
||
|
||
💡 Итог
|
||
Без этих двух проверок (if base == 4 и if base == 5) косвенная адресация через эти конкретные регистры будет тихо ломать выполнение программы. Это классические "грабли", на которые наступают почти все разработчики своих ассемблеров или JIT-компиляторов под x86-64. |