Intel префиксы инструкции, проверка проблем с оптимизацией - PullRequest
2 голосов
/ 19 апреля 2019

Я хотел бы узнать больше о функциях ptrace с двоичными файлами x86_64, инструкциях по разборке.Цель состоит в том, чтобы проверить, является ли байт одним из префиксов инструкций.

Я нашел некоторую информацию в Руководстве разработчика программного обеспечения для архитектур Intel® 64 и IA-32 * (том 2, глава 2).

В разделе 2.1.1 INSTRUCTION PREFIXES показаны следующие префиксы:

  • [0x26] Переопределение сегмента ES
  • [0x36] Префикс переопределения сегмента SS
  • [0x2E] Префикс переопределения сегмента CS или Ветвь не занята
  • [0x3E] Префикс переопределения сегмента DS или Взятая ветвь
  • [0x64] Префикс переопределения сегмента FS
  • [0x65] Префикс переопределения сегмента GS
  • [0x66] Префикс переопределения размера операнда
  • [0x67] Префикс переопределения размера адреса
  • [0xF0] Префикс LOCK
  • [0xF2] Префикс REPNE / REPNZ или Префикс BND
  • [0xF3] Префикс REP или REPE / REPZ

Визуально, этот график показывает префиксы желтым цветом.

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

Если я беру 0x26, 0x36, 0x2E и 0x3E в качестве группы.Эти числа в базе 2 (00100110, 00110110, 00101110 и 00111110) показывают общую часть: 001XX110.

Двоичная операция 11100111 (0xE7)) можно найти, если мой байт находится в этой группе.

Отлично.Теперь, если я возьму вторую группу, которая содержит 0x64, 0x65, 0x66 и 0x67 (01100100, 01100101, 01100110, 01100111), я найду другую общую часть:011001XX.

Затем можно найти двоичную операцию 11111100 (0xFC), если байт находится во второй группе.

Проблема возникает из-за оставшихся префиксов инструкций(0xF0, 0xF2 и 0xF3): здесь нет общей части.Операция and 11111100 (0xFC) позволила бы байту 0xF1.

Одним из решений было бы проверить, не является ли байт 0xF1.

Итак, возможная реализация в C будет выглядеть следующим образом:

if ((byte & 0xE7) == 0x26) {
    /* This `byte` is a ES, SS, CS or DS segment override prefix */
}
if ((byte & 0xFC) == 0x64) {
    /* This `byte` is a FS, GS, Operand-size or address-size override prefix */
}
if ((byte & 0xFC) == 0xF0) {
    if (byte != 0xF1) {
        /* This `byte` is a LOCK, REPN(E/Z) or REP(_/E/Z) prefix */
    }
}

Исходя из Intel, я хотел бы, за исключением того, что в этой последней группе можно будет проверить только одну операцию.

Затем, окончательнаявопрос: могу ли я проверить за одну операцию, если байт равен 0xF0, 0xF2 или 0xF3?

1 Ответ

2 голосов
/ 19 апреля 2019

Тогда, последний вопрос: могу ли я проверить за одну операцию, является ли байт 0xF0, 0xF2 или 0xF3?

Самое близкое, что вы можете получить к одной инструкции, это что-то вроде:

                     ;ecx = the byte
    bt [table],ecx   ;Is the byte F0, F2 or F3?
    jc .isF0F2orF3   ; yes

Однако иногда префикс не считается префиксом (например, pause инструкция, которая кодируется как rep nop для совместимости со старыми ЦП).

Также обратите внимание, что для высокоскоростного дизассемблера наиболее быстрый подход, скорее всего, "управляемый таблицей переходов", когда один регистр указывает на таблицу, соответствующую состоянию декодера, а другой регистр содержит следующий байт инструкции, например:

                          ;ebx = address of table corresponding to the decoder's current state
    movzx eax,byte [esi]  ;eax = next byte of the instruction
    inc esi               ;esi = address of byte after the next byte of this instruction
    jmp [ebx+eax*4]       ;Go to the code that figures out what to do

В этом случае некоторые фрагменты кода, к которым был применен переход, установили бы некоторые флаги без изменения текущей таблицы (например, запись для 0xF3 в исходной таблице вызовет переход к коду, который устанавливает флаг «rep prefix was visible») ), а некоторые фрагменты кода, к которым был выполнен переход, переключились бы на другую таблицу (например, запись для 0x0F в исходной таблице вызовет переход к коду, который изменит EBX, чтобы указать на совершенно другую таблицу, используемую для всех инструкций, которые начать с 0x0F, ...); и некоторые фрагменты кода, к которым был применен переход, отображали команду (и сбрасывали состояние декодера).

Например; для pause код может быть:

table0entryF3:
    or dword [prefixes],REP
    movzx eax,byte [esi]                ;eax = next byte of the instruction
    inc esi                             ;esi = address of byte after the next byte
    jmp [ebx+eax*4]

table0entry90:
    mov edx,instructionNameString_NOP
    test dword [prefixes],REP           ;Was it a PAUSE or NOP?
    je doneInstruction_noOperands       ; NOP, current name is right
    and dword [prefixes],~REP           ; PAUSE, pretend the REP prefix wasn't there
    mov edx,instructionNameString_PAUSE ;        and use the right name
    jmp doneInstruction_noOperands

doneInstruction_noOperands:
    call displayPrefixes
    call displayInstructionName
    mov dword [prefixes],0              ;Reset prefixes
    mov ebx,table0                      ;Switch current table back to the initial table
    movzx eax,byte [esi]                ;eax = first byte of next instruction
    inc esi                             ;esi = address of byte after the next byte
    jmp [ebx+eax*4]
...