我最近开始使用汇编语言进行C ++编程。我想澄清一些事情。
根据我的阅读,指令指针从retn
指令获取它应该执行的地址。它不会像做jmp
一样,因为jmp
也设置指令指针?
如果我是对的,retn
和jmp
有什么区别?如果我错了,有人可以解释C伪代码吗?
什么是无限循环的装配等价物?
我读到EAX,EBX,ECX,EDX
可以互换,但它们有什么区别吗?如果是这样,我应该在哪种情况下专门使用EAX/EBX/ECX/EDX
。
你似乎在谈论子程序调用,所以这里是低调。
当你调用子程序时,它看起来像这样(地址会有所不同,但我不想让你混淆可变长度的指令):
1234 call 8888
1235 <next instruction>
会发生的是,call
首先将下一个指令指针1235
放入堆栈(后进先出数据结构),然后将指令指针设置为您正在调用的任何内容,在这种情况下为8888
。
之后,回归在8889
完成:
8888 mov eax, 0
8889 ret
返回的作用就是从堆栈中弹出第一个值(即1235
,它被调用推送)并将其加载到指令指针中。所以这不是告诉你去哪里的回报,而是通过电话推送到堆栈的信息。
如果你在子程序结束时有一个jmp
指令,它只能返回到代码中的一个点(折扣你现在可以用其他寻址模式做的所有精彩的事情):
8889 jmp 1235
通过使用返回,您可以返回到您来自的任何地方,无论它在哪里。
无限循环的汇编程序可以简单如下:
loopy:
jmp loopy
至于寄存器,eax
,ebx
,ecx
和edx
被认为是通用寄存器。这将它们与更特殊用途的寄存器区分开来,例如堆栈指针,基指针,源和目标索引等,它们根据其用途具有专门的指令。
ax
可能在x86架构的早期迭代中有一些额外的功能,但我不确定是否仍然如此。如果你正在编写自己的东西,你应该能够互换使用它们。如果您正在关注API或ABI,则需要遵循它所施加的规则(例如eax
保存系统调用号的Linux系统调用接口)。
ret
和jmp
在C / C ++函数方面的差异与此类似:
int foo()
{
int x = 3+4;
if(x < 10)
goto Quit; <- similar to jmp
x += 10;
Quit:
return x; <- similar to ret
}
当你在C中执行return
时,在机器级别上实际发生的事情要复杂得多,因为经常会执行额外的代码,例如将返回值放在eax
中并清理堆栈。在C ++中,本地对象也会被删除,但是ultiate和函数将是ret
指令。
什么是无限循环的装配等价物?
while(1);
就好像
000000 jmp 000000
或者更高级
00000 inc ecx
00001 jmp 00000
通用寄存器。在某些情况下,您可以混合寄存器并根据需要使用它们。对于某些指令,他们希望使用特定的寄存器。您必须查阅说明手册以查看情况。
一个例子是movsw
,它要求你使用(E)SI
和(E)DI
,所以在这种情况下你不能自由选择。如果你使用rep movsw
,也可以使用额外的(E)CX
。通常,汇编程序知道哪些寄存器对于forxtruction有效并且会给出错误消息,但当然,您应该查看手册以确保,因为如果汇编程序不能抛出错误,您可能会得到意外的结果。
eax
和edx
是除法中的隐式操作数,并且是乘法的宽结果版本。还有一些特殊的符号扩展指令,仅对rax
(操作码98)的部分操作或将eax
符号扩展为edx:eax
(操作码99)。十进制数学指令都适用于eax
的部分。
ecx
(井cl
真的但足够接近)是唯一可以在Haswell(它引入sarx
,shlx
和shrx
,所有这些都可以通过任何GPR转移)之前转移的寄存器。 ecx
也被重复前缀用作计数器。 pcmp*stri
把长度放在ecx
。
许多专用指令没有明确的操作数,而是为某些GPR指定特殊含义,例如cpuid
,rdpmc
,rdtsc
,wrmsr
,xgetbv
,xsave
。通常edx:eax
,经常ecx
,很少ebx
。你可能不需要处理这些问题。