或指令组装到ECX寄存器中 [英] OR instruction in assembly into ECX register
问题描述
在我正在阅读的书中,给出了以下代码片段和问题:
in a book I'm reading, we are given the following snippet and problem:
此功能使用SCAS和STOS的组合来完成其工作.第一的, 在第1行和第8行中说明[EBP + 8]和[EBP + C]的类型是什么, 分别.接下来,解释一下此代码段的作用:
This function uses a combination SCAS and STOS to do its work. First, explain what is the type of the [EBP+8] and [EBP+C] in line 1 and 8, respectively. Next, explain what this snippet does:
01: 8B 7D 08 mov edi, [ebp+8]
02: 8B D7 mov edx, edi
03: 33 C0 xor eax, eax
04: 83 C9 FF or ecx, 0FFFFFFFFh
05: F2 AE repne scasb
06: 83 C1 02 add ecx, 2
07: F7 D9 neg ecx
08: 8A 45 0C mov al, [ebp+0Ch]
09: 8B AA mov edi, edx
10: F3 AA rep stosb
11: 8B C2 mov eax, edx
I had nearly figured out everything after checking with an online solution (https://johannesbader.ch/2014/05/practical-reverse-engineering-exercises-page-11/), however, one step in this snippet still does not make sense ot me.
根据在线解决方案,当我们在第4行上运行命令or ecx, 0FFFFFFFFh
时,它说
According to the online solution, when we run the command or ecx, 0FFFFFFFFh
at line 4, it says
我们[现在]将ECX解释为有符号整数-1
We [now] interpret ECX as a signed integer -1
为了知道or
命令的结果是什么,我们是否不需要事先知道ECX
的值是什么?为什么值是-1?
In order to know what the result is going to be for the or
command, wouldn't we need to know previously what the value of ECX
is? And why is the value -1?
谢谢
推荐答案
32位两个-1
的补码表示形式是0xFFFFFFFF
(全1). 1 OR x
始终为1
,因此这会无条件地将ecx
设置为-1.该技巧仅适用于-1,因为OR只能设置位,而不能将其清除为零.
The 32-bit two's complement representation of -1
is 0xFFFFFFFF
(all-ones). 1 OR x
is always 1
, so this unconditionally sets ecx
to -1. This trick only works for -1, because OR can only set bits, not clear them to zero.
您引用的解决方案部分,关于将"ecx
解释为有符号整数-1",仅在以下gdb命令的上下文中才有意义:(gdb) p/d $ecx
-> $7 = -1
.
The part of the solution that you quote, about interpreting "ecx
as a signed integer -1", is only sensible in the context of the gdb command that follows: (gdb) p/d $ecx
-> $7 = -1
.
rep
前缀将ecx视为无符号计数器.将ecx设置为-1/UINT_MAX表示 repne scasb
只会在发现内存为零时停止,而不是因为ecx
一直递减计数. (从理论上讲,如果没有零,它将倒计数并以这种方式结束,但是实际上,它将首先出现段错误.-1
对于rep
来说不是特殊情况.)
rep
prefixes treat ecx as an unsigned counter. Setting ecx to -1 / UINT_MAX means repne scasb
will only stop when it finds a zero in memory, not because ecx
counted down all the way. (In theory, if there was no zero, it would count down and end that way, but in practice it would segfault first. -1
isn't a special-case for rep
).
将寄存器设置为 mov r32, imm32
insn ,例如B9 FF FF FF FF mov ecx,-1
.
The "normal" way to set a register to anything other than zero is with a 5 byte mov r32, imm32
insn, for example B9 FF FF FF FF mov ecx,-1
.
如果您更关心代码大小而不是速度,或者您知道对ecx
的错误依赖关系不是问题,则可以使用符号扩展的8位立即数来节省两个字节: or r/m32, imm8
.
If you care more about code-size than speed, or you know that a false dependency on ecx
isn't a problem here, you can save two bytes by using a sign-extended 8-bit immediate: or r/m32, imm8
.
83 C9 FF or ecx, 0FFFFFFFFh
结果中的任何位实际上都不取决于ecx的旧值,因为.但是,实际的CPU并没有特殊情况,因此只有在ecx
就绪之前,乱序执行才能开始.这是对ecx旧值的虚假依赖. mov
打破对先前值的依赖性. (有关此内容的更多信息,请参见x86 标签Wiki ,尤其是 Agner Fog的指南).
None of the bits in the result actually depend on the old value of ecx, because. However, real CPUs don't special-case this, so out-of-order execution can't get started until ecx
is ready. This is a false dependency on the old value of ecx. mov
breaks the dependency on the previous value. (For more about this, see the x86 tag wiki, especially Agner Fog's guides).
or ecx, imm8
需要一个ModRM字节将目标编码为ecx,这与mov
的形式不同,在mov
中,每个目标寄存器都有一个单独的操作码.不幸的是,没有mov r/m32, imm8
的操作码,它可以在许多指令中节省2个字节的代码.
or ecx, imm8
needs a ModRM byte to encode the destination as ecx, unlike that form of mov
where there's a separate opcode for each destination register. There's unfortunately no opcode for mov r/m32, imm8
, which would save 2 bytes of code in many instructions.
如果Intel愿意放弃向后兼容无证说明,他们可以添加它. (8086没有它,因为当将立即数移动到内存时,它只会帮助16位代码.他们已经将8个操作码专用于mov r16, imm16
,这在16位模式下为3字节,不需要使用操作数大小的前缀,就像不存在的mov r/m16, imm8
一样.)
If Intel had been willing to drop backwards compatibility with undocumented instructions, they could have added it. (8086 didn't have it, because it would only help 16-bit code when moving an immediate to memory. They already dedicated 8 opcodes to mov r16, imm16
, which is 3 bytes in 16-bit mode where it doesn't need an operand-size prefix, just like the non-existent mov r/m16, imm8
would be.)
因此这是优化代码大小时的有用习惯,例如引导加载程序或 https://codegolf.stackexchange.com/上的机器代码答案. (是的,就是这样.)
So this is a useful idiom when optimizing for code-size, e.g. for a bootloader, or a machine-code answer on https://codegolf.stackexchange.com/. (Yes, that's a thing.)
另一个相关的技巧是,如果另一个寄存器中已经有另一个常量,则使用3字节的lea
来创建一个常量.对于 x86-64 Adler32,我需要两个清零寄存器和一个1
,所以我用
Another related trick is using a 3-byte lea
to create a constant, if you already have another constant in another register. e.g. for x86-64 Adler32, I needed two zeroed registers and a 1
, so I used
401120: 31 c0 xor eax,eax
401122: 99 cdq # zero rdx by sign-extending eax (0) into edx
401123: 8d 7a 01 lea edi,[rdx+0x1] # edi=0+1, using a reg + disp8 addressing mode
这篇关于或指令组装到ECX寄存器中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!