在反汇编练习中,我观察到以下代码:
TEST.CPP:
#include <stdio.h>
int main(int argc, char * argv[]) {
for (int i = 0; i < 10 ; ++i) {
printf("%i\n", i);
}
int i = 0;
while ( i < 10) {
printf("%i\n", i);
++i;
}
return 0;
}
使用vc ++ 2008进行优化编译:
cl /Ox test.cpp
主要功能的反汇编:
.text:00401000 var_4 = dword ptr -4 ; BTW, IDA fails to see that esi is pushed to save it, not to allocate space to local variable
.text:00401000
.text:00401000 push esi
.text:00401001 xor esi, esi
.text:00401003
.text:00401003 loc_401003: ; CODE XREF: sub_401000+15j
.text:00401003 push esi
.text:00401004 push offset byte_40A150
.text:00401009 call sub_401038 ; printf
.text:0040100E inc esi
.text:0040100F add esp, 8
.text:00401012 cmp esi, 0Ah
.text:00401015 jl short loc_401003
.text:00401017 xor esi, esi
.text:00401019 lea esp, [esp+0]
.text:00401020
.text:00401020 loc_401020: ; CODE XREF: sub_401000+32j
.text:00401020 push esi
.text:00401021 push offset unk_40A154
.text:00401026 call sub_401038 ; printf
.text:0040102B inc esi
.text:0040102C add esp, 8
.text:0040102F cmp esi, 0Ah
.text:00401032 jl short loc_401020
.text:00401034 xor eax, eax
.text:00401036 pop esi
.text:00401037 retn
现在,我不是一个专家,正如你可以从示例代码中看到的那样,但考虑到我编写了原始代码,我能够弄清楚这个汇编列表。唯一困扰我的是以下几行:
.text:00401019 lea esp, [esp+0]
为什么编译器会这样做?它不会影响任何寄存器或标志,它看起来像冗余代码。我唯一能想到的是编译器在第二个循环中对齐jmp所在的代码(loc_401020)这可能是原因吗?
答案 0 :(得分:3)
是的,看起来它正在插入填充以对齐跳跃目标。如果使用/Fa
让编译器生成汇编输出,那么它将显示为npad 7
,明确表示它正在插入填充。从那里开始,由汇编程序选择最有效的指令序列,它可以在尽可能少的CPU时间内使用指定的空间。
答案 1 :(得分:1)
无用指令就在标签之前,标签对齐。对我来说它看起来像nop
(汇编程序可以生成各种长度的单指令nop
,因为它比执行时的几个“标准”单字节nop
更有效。通过它们。)
答案 2 :(得分:1)
你的猜测是正确的。该指令实际上是一个7字节的NOP,用于将循环开始处的标签与16字节边界对齐。如果您要查看该指令的实际编码,您可能会注意到它不仅对任何寄存器或标志没有任何影响,它还会使用4字节编码来实现0的立即偏移量。就像使用较短的编码一样容易。所有这些都是消耗正确数量的代码字节,同时使指令尽可能高效地执行。