我正在用MSVC ++中的机器代码进行一些实验,并创建了一个函数,允许我在带有符号位移的寄存器周围构建mov操作。一切顺利,直到我的功能构建mov [esp-4], eax
。突然,我的程序开始崩溃了。在看完拆卸后,我发现了一些相当奇怪的东西。对于此mov
操作符取消引用ESP
寄存器的偏移量,在参数字节和有符号位移之间放置了一个额外的字节。这个字节似乎总是0x24。因此,出于好奇,我拆解了以下__asm
块并得到了一些有趣的结果:
mov [eax - 4], eax
mov [ecx - 4], eax
mov [edx - 4], eax
mov [ebx - 4], eax
mov [esp - 4], eax
mov [ebp - 4], eax
mov [esi - 4], eax
mov [edi - 4], eax
机器代码将上述内容翻译成:
89 40 FC
89 41 FC
89 42 FC
89 43 FC
89 44 24 FC <--- WAT!
89 45 FC
89 46 FC
89 47 FC
我在Windows计算器中键入了十六进制24并将其切换为二进制。结果是00100100
。 出现的是两个虚拟位,然后是ESP
寄存器两次。
有人能说清楚为什么会这样吗?我认为这超出了MS C ++编译器的奇怪怪癖,直接进入70年代或80年代(90年代?)的遗留功能领域,但我找不到任何关于为什么ESP
的在线参考这种mov
操作的例外是一个例外。谢谢!
答案 0 :(得分:9)
[esp]
不能仅使用modr/m
字节进行编码,而是需要SIB
个字节。见表2-2。在intel指令集参考中使用ModR / M字节的32位寻址表。
modr / m的44
值对另一个操作数为eax
进行编码,并且后跟一个SIB
字节和位移字节。 SIB
的{{1}}值编码24
,请参阅表2-3。带有SIB字节的32位寻址表。
[esp]
也可以编码所有其他变体,但由于这长1个字节,因此汇编程序不会使用该表单。以下是参考清单:
SIB
这本身不应该导致您的崩溃,但随机尝试覆盖内存中的内容可能会。
答案 1 :(得分:2)
如果查看指令的第二个字节,可以看到3位值的递增序列,0到7,表示寄存器操作数,4除外.4的值用于更通用的操作数类型和Intel选择将esp放在通用列表中,因为ebp更常用于基于堆栈的引用。