据此:What are the calling conventions for UNIX & Linux system calls on i386 and x86-64,在x64-amd系统V ABI中,args依次传递到以下寄存器:%rdi, %rsi, %rdx, %rcx, %r8 and %r9
,按此顺序。第七和更高的arg在堆栈上传递。所以问题是,被叫方怎么知道,pop
剩余的(第7个及更多)参数有多少个,按什么顺序?被呼叫者是否从argc
知道吗?我有一个例子:
#include <stdio.h>
#include <stdlib.h>
int main(){
int i=0;
printf("\n%i;%i;%i;%i;%i;%i;%i\n",i,i+1,i+2,i+3,i+4,i+5,i+6 );
}
未经优化而编译:
.text
.section .rodata
.LC0:
.string "\n%i;%i;%i;%i;%i;%i;%i\n"
.text
.globl main
.type main, @function
main:
pushq %rbp #
movq %rsp, %rbp #,
subq $16, %rsp #,
# a.c:5: int i=0;
movl $0, -4(%rbp) #, i
# a.c:6: printf("\n%i;%i;%i;%i;%i;%i;%i\n",i,i+1,i+2,i+3,i+4,i+5,i+6 );
movl -4(%rbp), %eax # i, tmp95
leal 6(%rax), %edi #, _1
movl -4(%rbp), %eax # i, tmp96
leal 5(%rax), %esi #, _2
movl -4(%rbp), %eax # i, tmp97
leal 4(%rax), %r9d #, _3
movl -4(%rbp), %eax # i, tmp98
leal 3(%rax), %r8d #, _4
movl -4(%rbp), %eax # i, tmp99
leal 2(%rax), %ecx #, _5
movl -4(%rbp), %eax # i, tmp100
leal 1(%rax), %edx #, _6
movl -4(%rbp), %eax # i, tmp101
pushq %rdi # _1
pushq %rsi # _2
movl %eax, %esi # tmp101,
leaq .LC0(%rip), %rdi #,
movl $0, %eax #,
call printf@PLT #
addq $16, %rsp #,
movl $0, %eax #, _10
# a.c:7: }
leave
ret
.size main, .-main
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
.section .note.GNU-stack,"",@progbits
这里不知何故对那些寄存器进行了排序,从最后开始:(我不是在这里考虑寄存器的大小,只是说明问题)按下di->si->r9->r8->cx->dx
,然后按下si
,di
并重新分配给字符串地址和第一个参数(i
)。因此,现在看来顺序正确。那么,被调用方函数如何知道,弹出以及以什么顺序弹出? (si
应该在di
之前,因为si
包含5
和di
6
)
printf
不知道有多少个参数。它必须相信格式字符串与您实际传递的内容匹配,并且如果错误,它将最终跳过某些内容或从堆栈中读取其他随机内容。不采用格式字符串的Varargs函数使用另一种方法来表示结束(例如,像NULL
这样使用的execlp
前哨或程序员手动传递的计数变量)。同样,如果您未正确标记结尾,则会读取错误数量的参数。