英特尔前缀指令,检查优化问题

Intel prefixes instructions, checking optimisations problems

我想通过 x86_64 二进制文件、反汇编指令了解更多关于 ptrace 函数的信息。 目标是检查一个字节是否是指令前缀之一。

我在 Intel® 64 and IA-32 Architectures Software Developer’s Manual(第 2 卷第 2 章)中找到了一些信息。

2.1.1 INSTRUCTION PREFIXES 部分显示以下前缀:

视觉上,this chart 以黄色显示前缀。

如果我想知道一个字节是否是前缀,我会尽量高效并检查是否可以执行二进制操作。

如果我把0x260x360x2E0x3E作为一组。这些以 2 为基数的数字(00100110001101100010111000111110)显示一个公共部分:001XX110.

一个11100111(0xE7)的与二元运算可以找到我的字节是否在这个组中

太好了。现在,如果我选择第二组,其中包含 0x640x650x660x67011001000110010101100110, 01100111), 又找到了一个共同的部分: 011001XX.

然后11111100(0xFC)的与二元运算可以查出该字节是否在第二组

剩余指令前缀(0xF00xF20xF3)的问题:没有公共部分。 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,我可以一次检查吗?

Then, the final question is: Can I check in one operation if the byte is 0xF0, 0xF2 or 0xF3?

最接近一条指令的是:

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

但是,有时前缀不被视为前缀(例如 pause 指令,其编码类似于 rep nop 以与旧 CPU 兼容)。

另请注意,对于高速反汇编程序,最快的方法可能是 "jump table driven",其中一个寄存器指向对应于解码器状态的 table,另一个寄存器包含指令的下一个字节, 比如:

                          ;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

在这种情况下,一些跳转到的代码片段会在不改变当前 table 的情况下设置一些标志(例如,初始 table 中的 0xF3 条目会导致跳转到代码设置一个 "rep prefix was seen" 标志),一些跳转到的代码片段将切换到不同的 table (例如,初始 table 中的 0x0F 条目将导致跳转到代码将 EBX 更改为指向完全不同的 table,用于所有以 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]