OR 指令汇编到 ECX 寄存器中

2024-02-21

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

该函数使用 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/ https://johannesbader.ch/2014/05/practical-reverse-engineering-exercises-page-11/),然而,这个片段中的一个步骤对我来说仍然没有意义。

根据网上的解决方案,当我们运行命令时or ecx, 0FFFFFFFFh at line 4, 它说

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

为了知道结果会是什么or命令,我们不需要事先知道它的值是什么吗ECX是?为什么值为-1?

Thanks


32 位二进制补码表示法 https://en.wikipedia.org/wiki/Two%27s_complement of -1 is 0xFFFFFFFF(全一)。1 OR x总是1,所以这无条件地设置ecx至-1。这个技巧仅适用于 -1,因为 OR 只能设置位,而不能将它们清除为零。


您引用的解决方案中关于解释的部分“ecx作为有符号整数 -1",仅在以下 gdb 命令的上下文中才有意义:(gdb) p/d $ecx->$7 = -1.

rep前缀将 ecx 视为无符号计数器。将 ecx 设置为 -1 / UINT_MAX 意味着repne scasb http://www.felixcloutier.com/x86/REP:REPE:REPZ:REPNE:REPNZ.html仅当它在内存中找到零时才会停止,而不是因为ecx一路倒数。 (理论上,如果没有零,它会倒计时并以这种方式结束,但实际上它会首先出现段错误。-1不是一个特例rep).


Why use or: 代码大小

将寄存器设置为的“正常”方法除零以外的任何值 https://stackoverflow.com/questions/33666617/which-is-best-way-to-set-a-register-to-zero-in-x86-assembly-xor-mov-or-and/33668295#33668295是一个5字节的mov r32, imm32 insn http://www.felixcloutier.com/x86/MOV.html, 例如B9 FF FF FF FF mov ecx,-1.

如果您更关心代码大小而不是速度,或者您知道对ecx这里不是问题,您可以使用符号扩展的 8 位立即数来保存两个字节:or r/m32, imm8 http://www.felixcloutier.com/x86/OR.html.

83 C9 FF    or ecx, 0FFFFFFFFh

结果中的任何位实际上都不依赖于 ecx 的旧值,因为。然而,真正的 CPU 不会对此进行特殊处理,因此乱序执行直到ecx准备好了。这是一个对 ecx 旧值的错误依赖. mov打破了对前一个值的依赖。 (有关此内容的更多信息,请参阅x86 /questions/tagged/x86标签维基,特别是阿格纳·福格的指南 http://www.agner.org/optimize/).

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

如果英特尔愿意放弃向后兼容未记录的指令 http://www.agner.org/optimize/blog/read.php?i=25,他们本来可以添加它。 (8086 没有它,因为在将立即数移动到内存时它只会帮助 16 位代码。他们已经专用了 8 个操作码mov r16, imm16,在 16 位模式下为 3 个字节,不需要操作数大小前缀,就像不存在的一样mov r/m16, imm8将会。)


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

另一个相关的技巧是使用 3 字节lea如果另一个寄存器中已经有另一个常量,则创建一个常量。例如为了x86-64 Adler32,我需要两个归零寄存器和一个1 https://codegolf.stackexchange.com/questions/78896/compute-the-adler-32-checksum/78972#78972,所以我用了

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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OR 指令汇编到 ECX 寄存器中 的相关文章

随机推荐