或指令组装到ECX寄存器中 [英] OR instruction in assembly into ECX register

查看:198
本文介绍了或指令组装到ECX寄存器中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我正在阅读的书中,给出了以下代码片段和问题:

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).

将寄存器设置为

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打破对先前值的依赖性. (有关此内容的更多信息,请参见标签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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆