x86 Assembly 16-битный относительный вызов

Я заметил, что в x86 доступны следующие две инструкции по сборке:

E8 cw    CALL rel16
E8 cd    CALL rel32

Я не понимаю, как процессор инструкций может различать эти два вызова. Единственное, о чем я могу думать, это то, что если приложение 16-битное, IP предполагает первое, а если приложение 32-битное, IP предполагает второе. Верна ли моя интерпретация или есть способ закодировать CALL rel16 в 32-битное приложение?


person Jeff G    schedule 17.10.2018    source источник
comment
Размер операнда (который определяет, является ли он rel16 или rel32) имеет другой размер по умолчанию в зависимости от режима процессора (реальный режим/32-битный защищенный режим/16-битный защищенный режим/64-битный длинный режим/режим V8086 и т. д.) . Подробнее см. в этой диаграмме. Вы можете переопределить значение по умолчанию с помощью префикса операнда 0x66. В 32-битном режиме вы можете использовать префикс инструкции 0x66, чтобы изменить размер операнда по умолчанию с 32-битного на 16-битный.   -  person Michael Petch    schedule 18.10.2018
comment
@MichaelPetch Итак, если я вас правильно понял, я могу использовать 0x66E8YYYY (где 66E8 — с прямым порядком байтов, а YYYY — с прямым порядком байтов), который будет интерпретироваться как CALL rel16 при содержании в 32-битном приложении. Это правильно?   -  person Jeff G    schedule 18.10.2018
comment
Это будет 0x66 0xe8 0xYYYY, где 0xYYYY — относительное значение с прямым порядком байтов.   -  person Michael Petch    schedule 18.10.2018
comment
@MichaelPetch Когда я пытаюсь это сделать, мое оборудование очищает старшее полуслово EIP после запуска 16-битной относительной версии. В настоящее время я нахожусь по адресу 0x56559053, затем запустите эту команду с 0x66E8EBFF, после чего для EIP будет установлено значение 0x00009042. Очевидно, это не то, что я хотел, поэтому я думаю, что ответ будет отрицательным, вы не можете использовать эту инструкцию в 32-битном приложении, чтобы сделать что-то значимое.   -  person Jeff G    schedule 19.10.2018
comment
Вы не спросили о побочных эффектах или о том, как это работает. Вы спрашивали про кодировку. Тот факт, что он очистил верхнюю часть EIP, задокументирован и задуман. В справочнике по набору инструкций для 16-битного относительного вызова говорится об EIP tempEIP ← (EIP + DEST) AND 0000FFFFH; (* DEST равен rel16 *) . Вы заметите, что старшие биты установлены на 0. Вы можете прочитать ISA для call здесь: felixcloutier.com/x86/CALL.html   -  person Michael Petch    schedule 19.10.2018
comment
Вы бы использовали этот тип кодирования в очень-очень редких случаях, я, вероятно, ожидал увидеть это только в том случае, если вы писали ОС и использовали комбинацию 32-битного и 16-битного кода при переходе в реальный режим ( даже тогда я бы использовал другой механизм). Я бы не ожидал, что кто-то будет использовать это в 32-битной программе пользовательского режима (я не могу придумать для этого разумного варианта использования)   -  person Michael Petch    schedule 19.10.2018
comment
Это похоже на проблему XY. Почему вас интересует кодировка rel16 в 32-битном коде?   -  person Michael Petch    schedule 19.10.2018
comment
Я просто пытаюсь понять, сколько байтов я могу получить для приложения. Кажется расточительным, что единственный способ получить значение EIP — потратить 5 байт на инструкцию вызова. Я надеялся, что есть эквивалент, такой как инструкции JMP, которые используют константу относительного адреса менее 32 бит.   -  person Jeff G    schedule 19.10.2018
comment
С кодировкой rel16 вы должны учитывать нюансы обработки EIP. Если весь ваш код не находится в первых 64 КБ сегмента, такая кодировка не очень полезна.   -  person Michael Petch    schedule 19.10.2018


Ответы (1)


Этот ответ был предоставлен Майклом Петчем в комментариях к вопросу:

Размер операнда (который определяет, является ли он rel16 или rel32) имеет другой размер по умолчанию в зависимости от режима процессора (реальный режим/32-битный защищенный режим/16-битный защищенный режим/64-битный длинный режим/режим V8086 и т. д.) . Подробнее см. на этой диаграмме. Вы можете переопределить значение по умолчанию с помощью префикса операнда 0x66. В 32-битном режиме вы можете использовать префикс инструкции 0x66, чтобы изменить размер операнда по умолчанию с 32-битного на 16-битный.

Я заметил, что старшие 16 бит EIP обнуляются при использовании этой инструкции. Следовательно, 16-битный относительный вызов 0x66 0xE8 имеет следующую семантику C++:

int16_t offset = ...;
EIP = (EIP + offset) & 0xFFFF;

Так что да, можно использовать 16-битную инструкцию относительного вызова в 32-битном приложении.

person Jeff G    schedule 24.10.2018