c - 这段反汇编代码中发生了什么,在 C 中会是什么样子?

标签 c x86 disassembly ida

我已经反汇编了这个 C 代码(使用 ida),并运行了这段代码。我相信第二行和第 5 行是一个数组,但我不确定为什么它使用符号扩展或零扩展。

我需要将代码转换为 C,我不确定为什么要使用符号/零扩展,或者是什么 C 代码会导致这种情况。

mov     ecx, [ebp+var_58]
mov     dl, byte ptr [ebp+ecx*2+var_28]
mov     [ebp+var_59], dl
mov     eax, [ebp+var_58]
movsx   ecx, [ebp+eax*2+var_20]
movzx   edx, [ebp+var_59]
or      edx, ecx
mov     [ebp+var_59], dl

最佳答案

unsigned 整数类型将进行零扩展,而有符号类型将进行符号扩展。


我觉得这太微不足道了。说明引用手册没有涵盖任何内容。我想这与要求解释一个非常简单的 C 程序是不同的,因为这里的技巧是理解为什么一个人可能将这一系列指令串在一起,而不是仅仅理解每个指令单独做什么。熟悉非优化编译器使用的习惯用法(在每个语句后从 RAM 存储和重新加载)会有所帮助。


我猜这是一个函数内部的片段,它构成了一个栈帧,所以 ebp 的正偏移是当局部变量不在寄存器中时溢出的地方。

mov     ecx, [ebp+var_58]     ; load var58 into ecx
mov     dl, byte ptr [ebp+ecx*2+var_28]   ; load a byte from var28[2*var58]
mov     [ebp+var_59], dl      ; store it to var59
mov     eax, [ebp+var_58]     ; load var58 again for some reason?  can var59 alias var58?
;  otherwise we still have the value in ecx, right?
;  Or is this non-optimizing compiler output that's really annoying to read?
movsx   ecx, [ebp+eax*2+var_20]   ; load var20[var58*2]
movzx   edx, [ebp+var_59]         ; load var59 again
or      edx, ecx                  ; edx = var59|var20[var58*2]
mov     [ebp+var_59], dl          ; spill var59 back to memory

我猜 movsx/movzx 的默认操作数大小是字节到双字。 word-to-dword 也存在,我很惊讶你的反汇编程序没有用内存操作数上的 byte ptr 消除歧义。我推断这是一个字节加载,因为之前对该地址的存储是字节宽的。

movsx 在加载小于 32b 的签名数据时使用。 C 的整数提升规则规定,对小于 int 的整数类型的操作会自动提升为 int(或 unsigned int,如果 int 不能表示所有值。例如,如果 unsigned shortunsigned int 大小相同)。

8 位或 32 位操作数大小在没有操作数大小前缀字节的情况下可用。一些只有 Intel P6/SnB 系列的 CPU 跟踪部分寄存器依赖性,在加载时将符号扩展到完整的寄存器宽度可以加快代码速度(避免对 AMD 和 Silvermont 上寄存器的先前内容的错误依赖性)。因此,对加载进行符号扩展或零扩展(根据数据类型)通常是处理狭窄内存位置的最佳方式。


查看非优化编译器的输出通常不值得费心。

如果代码是由适当的优化编译器生成的,它可能更像是

mov     ecx, [ebp+var_58]     ; var58 is live in ecx
mov     al, byte ptr [ebp+ecx*2+var_28]   ; var59 = var28[2*var58]
or      al, [ebp+ecx*2+var_20]    ; var59 |= var20[var58*2]
mov     [ebp+var_59], al          ; spill var59 to memory

更容易阅读,IMO,没有不断存储/重新加载的噪音。您可以看到某个值何时被多次使用,而不必注意到加载来自刚刚存储到的地址。

如果对 eax 高 24 位的错误依赖导致了问题,我们可以使用 movzxmovsx 加载到两个寄存器中,然后执行 或 r32, r32 像原来的一样,但仍然存储低 8。(使用 32 位或内存操作数将执行 4B 加载,而不是 1B 加载,这可能会跨越缓存行甚至页面和段错误。)

关于c - 这段反汇编代码中发生了什么,在 C 中会是什么样子?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33536698/

相关文章:

java - 字节码指令和处理器操作之间的关系

debugging - 如何永久关闭反汇编中的调试?

c - pthreads - 加入一组线程,等待一个线程退出

使用 while 循环计算输入中的字符

c - 汇编和 C 中的内核开发

assembly - 从 Assembly x86 NASM 内存中读取 16 位

x86-64 - Radare2 反汇编器如何组织可视化面板 (V!)?

c - 优化后的反汇编代码局部变量保存在哪里

c - 为什么我的 HAL_UART IT 捕获回调例程不会触发?

c - 生产者和消费者在数字生成器中没有正确关闭