组装到ECX寄存器中的OR指令

时间:2016-07-27 17:07:17

标签: assembly

在我正在阅读的一本书中,我们得到以下片段和问题:

  

此功能使用SCAS和STOS的组合来完成其工作。第一,   解释第1行和第8行中[EBP + 8]和[EBP + C]的类型,   分别。接下来,解释这个代码片段的作用:

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

在使用在线解决方案(https://johannesbader.ch/2014/05/practical-reverse-engineering-exercises-page-11/)检查后,我几乎找不到所有内容,但是,此代码段中的一步仍然没有意义。

根据在线解决方案,当我们在第4行运行命令or ecx, 0FFFFFFFFh时,它说

  

我们[现在]将ECX解释为有符号整数-1

为了知道or命令的结果,我们之前不需要知道ECX的值是多少?为什么值为-1?

由于

2 个答案:

答案 0 :(得分:4)

-1的32位two's complement representation0xFFFFFFFF(全1)。 1 OR x始终为1,因此无条件地将ecx设置为-1。此技巧仅适用于-1,因为OR只能设置位,而不能将它们清零。

您引用的解决方案的一部分,关于将#34; ecx解释为有符号整数-1",只有在后面的gdb命令的上下文中才是合理的:{{1} } - > (gdb) p/d $ecx

$7 = -1前缀将ecx视为未签名的计数器。将ecx设置为-1 / UINT_MAX意味着repne scasb只会在内存中找到零时停止,而不是因为rep一直倒计时。 (理论上,如果没有零,那么它会倒计时并以此方式结束,但在实践中它会首先发生段错误。ecx不是-1的特殊情况。

为什么使用rep:代码大小

"正常"将寄存器设置为anything other than zero的方法是使用5字节mov r32, imm32 insn,例如or

如果你更关心代码大小而不是速度,或者你知道B9 FF FF FF FF mov ecx,-1ecx的错误依赖不是问题,你可以通过使用符号扩展的8位立即节省两个字节:or r/m32, imm8

83 C9 FF    or ecx, 0FFFFFFFFh

结果中的所有位实际上都不依赖于ecx的旧值,因为。但是,真正的CPU并不是特殊情况,因此在ecx准备就绪之前无法执行无序执行。这是对ecx 的旧值的错误依赖。 mov打破了对先前值的依赖。 (有关详情,请参阅代码wiki,尤其是Agner Fog's guides)。

or ecx, imm8需要一个ModRM字节来将目标编码为ecx,这与mov的形式不同,其中每个目标寄存器都有一个单独的操作码。遗憾的是,mov r/m32, imm8没有操作码,可以在许多指令中保存2个字节的代码。

如果英特尔愿意放弃backwards compatibility with undocumented instructions,他们可以添加它。 (8086没有它,因为它只会帮助16位代码将立即移动到内存。他们已经将8个操作码专用于mov r16, imm16,这是16位模式下的3个字节,它不会#39; t需要一个操作数大小的前缀,就像不存在的mov r/m16, imm8一样。)

所以在优化代码大小时,这是一个有用的习惯用法,例如用于引导加载程序,或https://codegolf.stackexchange.com/上的机器代码答案。 (是的,这是一件事。)

另一个相关技巧是使用3字节lea创建一个常量,如果你已经在另一个寄存器中有另一个常量。,例如对于x86-64 Adler32, I needed two zeroed registers and a 1,所以我使用了

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

答案 1 :(得分:2)

十六进制中的

F是二进制的1111,因此0ffffffffh是一个DWORD,所有位都设置为1。 根据{{​​1}}的真值表,如果您OR OR的任何内容,您仍会得到1。 因此,无论先前持有的1是什么,在此特定ECX操作之后,其所有位都将设置为OR,即1。这一点在Jose Manuel Abarca Rodriguez在评论中提供的第一个链接中进行了解释。

英特尔架构(以及大多数硬件架构)使用所谓的“二进制补码表示”来建立寄存器或内存中数字和位模式之间的映射。在二进制补码表示中,0ffffffffh表示为-1。这一点在Jose Manuel Abarca Rodriguez在评论中提供的第二个链接中进行了解释。