这是混合C和汇编的PDP-11代码。在下面,u.u_rsav是一个数组指针,
savu(u.u_rsav);
此函数的汇编代码为
_savu: bis $340,PS
mov (sp)+,r1
mov (sp),r0
mov sp,(r0)+
mov r5,(r0)+
bic $340,PS
jmp (r1)
似乎在进入过程之前,它首先推送参数,然后推送返回点 PC 值。因此,r1 存储 PC,r0 存储参数。我的难题是sp(堆栈指针)在汇编代码跳回到调用点之前不会恢复到原始值。它仍然指向参数在堆栈中的存储位置。
在C语言中,尤其是任何PDP-11编译器都可能使用的K&R C,被调用的函数无法知道调用函数在堆栈上放置了多少个参数。这就是 var args 函数过去的工作方式。例如,printf
将在stdio.h
中声明,如下所示:
int printf();
定义将像这样开始:
int printf(fmt)
char *fmt;
{
/* function body */
}
然后调用者可以只做(例如)
printf("%d %dn", a, b);
因此,必须由调用函数负责从堆栈中删除参数,而不是被调用函数。
为了使事情更清楚,并且它不仅仅是可变参数函数,在K&R C中,以下内容是完全合法的,并且会打印3。
int add();
int main()
{
int sum;
sum = add(1, 2, 3, 4);
printf("%dn", sum);
return 0;
}
int add(a, b)
int a;
int b;
{
return a + b;
}
它不是 C,而是应用程序二进制接口约定(您通常有多个语言实现或编译器遵循相同的ABI,并且您过去确实在同一系统上使用具有不同ABI 约定的各种编译器)。它是特定于体系结构和操作系统的。顺便说一句,调用约定是 ABI 的一部分。
有关示例,请参阅与 x86 相关的示例
您可以找到考古 PDP11 计算机(以及编译器和操作系统)的 ABI 约定,例如请参阅 PDP11 常见问题解答和 C 调用约定。一些 ABI 使用堆栈,在寄存器上具有各种调用方安全/被调用方安全约定。
我的难题是sp(堆栈指针)在汇编代码跳回到调用点之前不会恢复到原始值。
某些 ABI 或调用约定要求被调用函数还原堆栈指针。其他人希望调用函数这样做。