【问题标题】:Intel prefixes instructions, checking optimisations problems英特尔前缀指令,检查优化问题
【发布时间】:2019-09-09 20:40:54
【问题描述】:

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

我在Intel® 64 and IA-32 Architectures Software Developer’s Manual(第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 前缀

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

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

如果我将0x260x360x2E0x3E 作为一组。这些以 2 为底的数字(00100110001101100010111000111110)显示了一个共同部分:001XX110

如果我的字节在此组中,则可以找到11100111 (0xE7) 的与二进制运算。

太好了。现在,如果我选择包含0x640x650x660x6701100100011001010110011001100111)的第二组,我发现了另一个常见部分: 011001XX.

那么,如果该字节在第二组中,则111111000xFC)的与-二进制运算可以找到。

问题在于剩余的指令前缀(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 */
    }
}

来自英特尔,我希望最后一组只能签入一个操作。

那么,最后一个问题是:如果字节是 0xF0、0xF2 或 0xF3,我可以签入一个操作吗?

【问题讨论】:

  • 你可以试着在代码高尔夫上问这个。 codegolf.stackexchange.com
  • 为了让它成为话题,它需要被改写为一个谜题。
  • @prl 几乎没有办法将这个问题重写为适合 PPCG 的内容,而不会完全失去其当前的含义/请求。 PPCG 99% 的时间都是为了比赛,写一篇关于主题的非挑战真的很难。

标签: optimization x86 intel disassembly micro-optimization


【解决方案1】:

那么,最后一个问题是:如果字节是0xF0、0xF2或0xF3,我可以签入一个操作吗?

你可以得到最接近的一条指令是这样的:

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

但是,有时前缀不被视为前缀(例如,pause 指令,为了与旧 CPU 兼容而编码为 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 seen”标志的代码),并且跳转到的一些代码片段将切换到不同的表(例如,初始表中 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]

【讨论】:

  • OP 没有说“1 次操作”,而不是“1 条指令”,而且 bt [mem], reg 很慢(Skylake 上 10 微秒,比使用 shift/mask 手动模拟它来分解更糟糕字节偏移+掩码的位索引。)检查这3个字节可能最好通过范围检查然后bt进入立即位图。就像sub eax, 0xf0 / jnc not_fx_prefix / bt ecx, eax / jnc not_fx_prefix 其中ecx 持有0b1101。但我想OP的面具和拒绝0xf1实际上更好。
  • @PeterCordes:是的; Skylake 很慢,因为一条指令应该只是“5 次操作”(将索引拆分为 dword 编号和位编号、将 dword 编号添加到地址、获取 dword、提取位、设置标志)使用的微指令比它在所有 CPU 上使用的多使用更少的微指令。
  • 公平地说,自 Core2 以来所有 Intel 的速度都差不多。在 Pentium-M 和 P6 上稍快一些。 AMD Ryzen 将其解码为仅 5 uop,但有一些人为瓶颈将其限制为 3 个周期的吞吐量。在 K10 上,它是 5 uops,每 2 个时钟 1 个,但除此之外,最好的吞吐量是 3 的 Ryzen 和 3.5 的 Bulldozer。因此,似乎大多数 CPU 的运行速度都比必要的慢,除非这些只是微编码指令的前端效果。
猜你喜欢
  • 1970-01-01
  • 2016-06-08
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 2020-05-12
  • 2012-11-13
  • 1970-01-01
相关资源
最近更新 更多