为什么参数从右到左进栈

原因是由于可变长参数。

如今GCC已经支持按声明顺序进栈,这里分析传统的函数调用入栈。

前提

了解函数调用,所涉及栈帧分配,见下图1:


图1:函数调用栈帧

这个图2也有意思,把函数调用之间的关系展示了出来。


图2:函数调用关系

理论分析

假设现在有函数f,固定参数m个,可变参数n(未知)个,假设所有参数都是32位整数,如果不是整数,也可以根据参数类型,推出参数地址,为了简单画图,在此使用整数。

自右向左(逆变量声明顺序)

那么调用栈图3:


图3:自右向左入栈的调用栈

虽然不知道可变参数n的大小,但是,依然可以根据固定参数的大小m,找到可变参数的开始位置,然后去访问就可以了。至于访问第n+1个可变参数,会造成什么问题,我们一会讨论。

自左向右(按变量声明顺序)

那么调用栈图4:


图4:自左向右入栈的调用栈

在只知道ebp的情况下,而不知道可变参数n的大小,不能确定可变参数和参数的分解,怎么确定参数1,参数m的地址呢?不确定他们怎么能访问呢?真相就是这样子,你无法确定各参数的地址

如何实现自左向右入栈

使用另外的寄存器指向参数1。那么就可以访问固定参数,已经可变参数的区域也就确定了。

自右向左访问参数超过范围

未定义。

以printf为例:

printf("%d %d\n", 1); 

这条语句输出另个整数,但可变参数只给了一个整数1,那么访问第二个整数的时候必然也就“越界”了。
假设1的地址为ptr,那么会把(char *)ptr+sizeof(int),这个地址上的数据解析为一个整数输出。如果这个区域是它不能访问的,还会造成非法访问,如果可以访问,数据也是错误的。