我最近开始使用汇编语言进行C ++编程。我想澄清一些事情。
根据我的阅读,指令指针从retn
指令获取接下来应该执行的地址。它与执行jmp
不一样,因为jmp
也设置了指令指针吗?
如果我是对的,retn
和jmp
之间有什么区别?如果我错了,有人可以解释C伪代码吗?
无限循环的装配等价物是什么?
我读到EAX,EBX,ECX,EDX
可以互换,但它们有什么区别吗?如果是这样,我应该在哪种情况下专门使用EAX/EBX/ECX/EDX
。
答案 0 :(得分:3)
你似乎在谈论子程序调用,所以这就是它的下行。
当你调用子程序时,它看起来像这样(地址会有所不同,但我不想让你混淆可变长度指令):
1234 call 8888
1235 <next instruction>
call
首先将下一个指令指针1235
置于堆栈(后进先出数据结构)中,然后将指令指针设置为您所需的任何内容在这种情况下调用8888
。
稍后,返回8889
:
8888 mov eax, 0
8889 ret
返回的作用只是弹出堆栈的第一个值(即1235
,它被调用推送)并将其加载到指令指针中。所以不是 return 告诉你去哪里,它是调用在堆栈上推送的信息。
如果在子程序结束时有一条jmp
指令,它只能返回代码中的一个点(折扣你现在可以用其他寻址模式做的所有精彩事情):
8889 jmp 1235
通过使用返回,无论你身在何处,都会回到原地。
无限循环的汇编程序可以简单如下:
loopy:
jmp loopy
对于寄存器,eax
,ebx
,ecx
和edx
被视为通用寄存器。这使它们与诸如堆栈指针,基指针,源和目标索引等更特殊用途的寄存器区别开来,它们根据其用途具有专门的指令。
ax
可能在x86架构的早期迭代中有一些额外的功能,但我不确定是否仍然如此。如果你正在编写自己的东西,你应该能够互换使用它们。如果您正在关注API或ABI,则需要遵循它所施加的规则(例如eax
保存系统调用号的Linux系统调用接口。)
答案 1 :(得分:1)
根据C / C ++函数,ret
和jmp
的区别与此类似:
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
。
通常,汇编程序知道哪些寄存器对于一个inxtruction有效并且会给你一个错误信息,当然,你应该查看手册以确定,因为如果汇编程序不能抛出错误,你可能会得到意想不到的结果。
答案 2 :(得分:0)
eax
和edx
是除法中的隐式操作数,并且是乘法的宽结果版本。还有一些特殊的符号扩展指令,仅对rax
(操作码98)的部分操作或将eax
符号扩展为edx:eax
(操作码99)。十进制数学指令都适用于eax
的部分内容。
ecx
(确实cl
非常接近)是唯一可以在Haswell引入之前转移的注册表(引入sarx
,shlx
和{{1}所有这些都可以通过任何GPR移动)。 shrx
也用作重复前缀的计数器。 ecx
将长度设为pcmp*stri
。
许多专用指令没有明确的操作数,而是为某些GPR赋予特殊含义,例如ecx
,cpuid
,rdpmc
,rdtsc
,{{1 },wrmsr
。通常xgetbv
,通常xsave
,很少edx:eax
。你可能不需要处理这些问题。