英特尔前缀指令,检查优化问题
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
部分显示以下前缀:
- [
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 以黄色显示前缀。
如果我想知道一个字节是否是前缀,我会尽量高效并检查是否可以执行二进制操作。
如果我把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
)的问题:没有公共部分。 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]
我想通过 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 以黄色显示前缀。
如果我想知道一个字节是否是前缀,我会尽量高效并检查是否可以执行二进制操作。
如果我把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
)的问题:没有公共部分。 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]