Optimization 英特尔为指令添加前缀,检查优化问题

Optimization 英特尔为指令添加前缀,检查优化问题,optimization,x86,intel,disassembly,micro-optimization,Optimization,X86,Intel,Disassembly,Micro Optimization,我想了解更多关于ptrace使用x86_64二进制文件的函数,以及反汇编指令。 目标是检查字节是否是指令前缀之一 我在(第二卷第二章)中找到了一些信息 2.1.1指令前缀部分显示了以下前缀: [0x26]ES段覆盖 [0x36]SS段覆盖前缀 [0x2E]未采用CS段覆盖前缀或分支 [0x3E]DS段覆盖前缀或采用的分支 [0x64]FS段覆盖前缀 [0x65]GS段覆盖前缀 [0x66]操作数大小覆盖前缀 [0x67]地址大小覆盖前缀 [0xF0]锁定前缀 [0xF2]REPNE/REPNZ

我想了解更多关于ptrace使用x86_64二进制文件的函数,以及反汇编指令。 目标是检查字节是否是指令前缀之一

我在(第二卷第二章)中找到了一些信息

2.1.1指令前缀部分显示了以下前缀:

  • [
    0x26
    ]ES段覆盖
  • [
    0x36
    ]SS段覆盖前缀
  • [
    0x2E
    ]未采用CS段覆盖前缀或分支
  • [
    0x3E
    ]DS段覆盖前缀或采用的分支
  • [
    0x64
    ]FS段覆盖前缀
  • [
    0x65
    ]GS段覆盖前缀
  • [
    0x66
    ]操作数大小覆盖前缀
  • [
    0x67
    ]地址大小覆盖前缀
  • [
    0xF0
    ]锁定前缀
  • [
    0xF2
    ]REPNE/REPNZ前缀或BND前缀
  • [
    0xF3
    ]REP或REPE/REPZ前缀
视觉上,以黄色显示前缀

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

如果我将
0x26
0x36
0x2E
0x3E
作为一组。基数2中的这些数字(
00100110
00110110
00101110
00111110
)显示一个公共部分:
001XX110

如果我的字节在此组中,则可以找到
11100111
0xE7
)的and二进制操作

太好了。现在,如果我取第二组,它包含
0x64
0x65
0x66
0x67
01100100
01100101
011011011011
),我找到了另一个公共部分:
011001001xx

然后,如果字节在第二组中,则可以找到
111111 00
0xFC
)的和二进制操作

剩余的指令前缀(
0xF0
0xF2
0xF3
)出现问题:没有公共部分。
111111 00
0xFC
)的and操作将允许字节
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,我可以签入一个操作吗

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

最接近一条指令的是:

                     ;ecx = the byte
    bt [table],ecx   ;Is the byte F0, F2 or F3?
    jc .isF0F2orF3   ; yes
但是,有时前缀不被视为前缀(例如,
pause
指令,其编码类似于
rep nop
,以便与旧CPU兼容)

还请注意,对于高速反汇编程序,最快的方法可能是“跳转表驱动”,其中一个寄存器指向对应于解码器状态的表,另一个寄存器包含指令的下一个字节,如:

                          ;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]

你可以试着在code golf上问这个问题。如果你认为这个问题是非主题的,那么它需要被改写成一个谜题。@prl如果这个问题不完全失去它当前的意义/要求,几乎没有办法改写成适合PPCG的问题。PPCG 99%的时间都是用于比赛的,并且写一篇文章n关于主题的非挑战真的很难。OP没有说“1个操作”,而不是“1条指令”,而且
bt[mem],reg
相当慢(Skylake上的10个UOP,比用shift/mask手动模拟它来将位索引分解为字节偏移量+掩码更糟糕。)只检查这3个字节最好是通过范围检查,然后将
bt
转换成即时位图。例如
sub-eax,0xf0
/
jnc-not\u-fx\u prefix
/
bt-ecx,eax
/
jnc-not\u-fx\u prefix
ecx>中保持
0b1101
。但是我猜OP的掩码和拒绝
0xf1
实际上更好。@PeterCordes:是的;Skylake很慢,因为一条指令只能执行“5次操作”(将索引拆分为dword编号和位编号,将dword编号添加到地址,提取dword,提取位,设置标志)与所有使用较少UOP的CPU相比,它使用的UOP更多。公平地说,自Core2以来,所有Intel上的UOP速度都很慢。奔腾M和P6上的速度稍快。AMD Ryzen将其解码为仅5 UOP,但存在一些人为瓶颈,将其限制为3个周期的吞吐量。在K10上,它是5 UOP,每2个时钟一个,但在其他方面是最好的吞吐量Ryzen为3,推土机为3.5。因此,似乎大多数CPU的运行速度比需要的慢,除非这些只是微代码指令的前端效果。