Многобайтовый NOP x86 и префикс инструкции

Напомним, что архитектура x86 определяет 0x0F 0x1F [mod R/M] как многобайтовый NOP.

Теперь я смотрю на конкретный случай 8-байтового NOP: у меня есть

0x0F 0x1F 0x84 0x__ 0x__ 0x__ 0x__ 0x__

где последние 5 байтов получили произвольные значения.

Третий байт, [mod R/M]Расщепление дает:

modrm

  • mod = 10b: аргумент reg1 + смещение в формате DWORD
  • reg2 = 000b: (нам все равно)
  • reg1 = 100b: указывает, что аргумент вместо SIB байт + смещение в формате DWORD.

Теперь, в качестве конкретного примера, если я возьму

0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A

у меня есть

  • SIB = 0x12
  • displacement = 0x9A785634: DWORD

Теперь я добавляю 0x66 префикс инструкции, указывающий, что смещение должно быть WORD вместо DWORD:

0x66 0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A

Я жду 0x78 0x9A быть "отрезанным" и рассматриваться как новая инструкция. Однако при компиляции и запуске objdump в полученном исполняемом файле он по-прежнему использует все 4 байта (DWORD) в качестве смещения.

Я неправильно понимаю значение слова "смещение" в этом контексте? Или 0x66 префикс не влияет на многобайтовые инструкции NOP?

1 ответ

66H Префикс переопределяет размер операнда до 16 бит.
Он не отменяет размер адреса, если вы хотите, чтобы вы использовали 67H

Вот список всех операндов.

        F0h = LOCK  -- locks memory reads/writes
        String prefixes
        F3h = REP, REPE  
        F2h = REPNE      
        Segment overrides
        2Eh = CS
        36h = SS
        3Eh = DS
        26h = ES
        64h = FS
        65h = GS
        Operand override 
        66h. Changes size of data expected to 16-bit
        Address override 
        67h. Changes size of address expected to 16-bit

Однако лучше не создавать свои собственные инструкции nop, а придерживаться рекомендуемых (многобайтовых) nops.

Согласно AMD рекомендуемые многобайтовые числа следующие:

Таблица 4-9. Рекомендуемая многобайтовая последовательность инструкции NOP

bytes  sequence                encoding

 1      90H                            NOP
 2      66 90H                         66 NOP
 3      0F 1F 00H                      NOP DWORD ptr [EAX]
 4      0F 1F 40 00H                   NOP DWORD ptr [EAX + 00H]
 5      0F 1F 44 00 00H                NOP DWORD ptr [EAX + EAX*1 + 00H]
 6      66 0F 1F 44 00 00H             NOP DWORD ptr [AX + AX*1 + 00H]
 7      0F 1F 80 00 00 00 00H          NOP DWORD ptr [EAX + 00000000H]
 8      0F 1F 84 00 00 00 00 00H       NOP DWORD ptr [AX + AX*1 + 00000000H]
 9      66 0F 1F 84 00 00 00 00 00H    NOP DWORD ptr [AX + AX*1 + 00000000H]

Intel не возражает против до 3 избыточных префиксов, поэтому nop может иметь до 11 байтов.

 10     66 66 0F 1F 84 00 00 00 00 00H     NOP DWORD ptr [AX + AX*1 + 00000000H] 
 11     66 66 66 0F 1F 84 00 00 00 00 00H  NOP DWORD ptr [AX + AX*1 + 00000000H]

Конечно, вы также можете исключить nops, добавив префиксы к обычным инструкциям с избыточными префиксами.

например

rep mov reg,reg //one extra byte

или заставить процессор использовать более длинные версии одной и той же инструкции.

test r8d,r8d is one byte longer than: test edx,edx

Инструкции с непосредственными операндами имеют короткие и длинные версии.

and edx,7 //short
and edx,0000007  //long

Большинство ассемблеров будут старательно сокращать все инструкции для вас, поэтому вам придется самостоятельно кодировать более длинные инструкции db

Распределение их в стратегических местах может помочь вам выровнять цели прыжка, не вызывая задержек из-за декодирования или выполнения nop.

Помните, что на большинстве процессоров nop все еще использует ресурсы.

Другие вопросы по тегам