我正在学习汇编语言。什么是参数推送顺序?我知道这是如何将参数推送到堆栈中,但是“左侧”和“右侧”的含义是什么?相对于什么的左侧或右侧?
还是这只与命令的语义书写方式有关,例如:
mov ebp, esp ;esp is moved into ebp, right to left.
这样正确吗?或者有人能给我解释一下吗?
我正在学习汇编语言。什么是参数推送顺序?我知道这是如何将参数推送到堆栈中,但是“左侧”和“右侧”的含义是什么?相对于什么的左侧或右侧?
还是这只与命令的语义书写方式有关,例如:
mov ebp, esp ;esp is moved into ebp, right to left.
这样正确吗?或者有人能给我解释一下吗?
f(a,b,c)
时,您确实需要将参数'推一些地方'。push c
push b
push a
call f
在x86上,将数据压入栈会使栈顶减少一个字。已经压入了三个字和返回地址,因此被调用的函数可以使用top + 1*W
表示参数a
,使用top + 2*W
表示参数b
,使用top + 3*W
表示参数c
。
你也可以约定:前两个参数放在寄存器ebx
和ecx
中,其余参数放在栈中。只要调用者和被调用者达成一致,就可以正常运行。
除了xtofl的解释,您可能还想查看这个x86调用约定表。关于参数顺序,您会注意到几乎所有的参数都是从右到左推送的(最右边的参数先被推送),除了Pascal。
xtofl没有涵盖的另一种情况是寄存器参数-一些ABI要求某些参数在寄存器中,而不是在堆栈中。例如,在x86_64
系统上,函数:
int add3(int a, int b, int c)
将放置参数:
a -> rdi
b -> rsi
c -> rdx
具体来说,这将看起来像(Intel syntax):
mov rdi, [source-of-a]
mov rsi, [source-of-b]
mov rdx, [source-of-c]
call add3
所以寄存器从左到右填满,然后栈从右到左使用。
正如xtofl所说,只要调用者和被调用者达成一致,你做什么并不重要 - 显然,如果调用者和被调用者意见不合,那将导致不兼容的问题,这实际上不仅是汇编语言的问题,也是高级语言的问题 - 幸运的是,编译器主要是从右到左操作。如果想进一步了解,你可能会对清理栈的被调用者/调用者感兴趣 - 注意在x86_64
中它是如何标准化为一种方法的。
你没有提到你使用的是x86
- 你的架构肯定有一个标准的调用约定,因为没有它很难工作。
ebx-1
是从哪里来的?如果它们应该是地址,那么push
指令会将栈指针减去 4(在 64 位模式下为 8),所以每个栈槽之间相隔 4 或 8 个字节,例如ebx - 4
(c)、ebx - 8
(b)等。而且这只有在 EBX = ESP 的情况下才成立。这很不寻常;如果你要使用任何寄存器进行操作,那么应该使用 EBP。但是 被调用者f
不能假设调用者相对于其传入的堆栈参数在哪里留下了 EBP。它需要查看其返回地址上方的内容,以及[esp+4]
(a)、[esp+8]
(b)等更高的偏移量。或者在推送任何内容后使用更高的偏移量。 - Peter Cordesfastcall
将参数传递到ECX、EDX,或者gcc的regparm(3)
将参数传递到EAX、EDX、ECX。而你之前提到的关于将第一个参数推入栈顶的做法也是正确的,这样它就在最低地址处。 - Peter Cordesc
的地址比a
更高,因为它先被推入了堆栈,而两个推入操作之后进行了两次stack_pointer-=stack_width
。帧指针指向的标准位置是返回地址的正下方,此时a
的地址应该是frame + 2*W
,b
的地址应该是frame + 3*W
。但是不管你的参考点在哪里,c
的地址都比较高,加上更多的值而不是减少。 - Peter CordesW
定义为-4
是不寻常的... 自己记住:开始一个编译器后端的业余项目。 - xtofl