在64位机下, 函数的前6个参数都是保存在寄存器中,后面的参数才保存在栈中,那可变参数是不是也这样呢?
参考 这里
ebp 栈底 高地址
esp 栈顶 低地址
push %ebp 保存栈底
push %esp %ebp 上一个帧的%esp ,就是本栈的栈底%ebp
....
push %ebp %esp 恢复
pop %ebp
#include <stdio.h> #include <stdlib.h> int add(int a, int b, int c, int d, int e, int f, int g, int h) { // 8 个参数相加 int sum = a + b + c + d + e + f + g + h; return sum; } int main(void) { int i = 10; int j = 20; int k = i + j; int sum = add(11, 22,33, 44, 55, 66, 77, 88); int m = k; // 为了观察 %rax Caller Save 寄存器的恢复 return 0; }
gcc -S print2.c 得到汇编
.text .globl add .type add, @function add: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -20(%rbp) movl %esi, -24(%rbp) movl %edx, -28(%rbp) movl %ecx, -32(%rbp) movl %r8d, -36(%rbp) movl %r9d, -40(%rbp) movl -24(%rbp), %eax movl -20(%rbp), %edx leal (%rdx,%rax), %eax addl -28(%rbp), %eax addl -32(%rbp), %eax addl -36(%rbp), %eax addl -40(%rbp), %eax addl 16(%rbp), %eax addl 24(%rbp), %eax movl %eax, -4(%rbp) movl -4(%rbp), %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size add, .-add .globl main .type main, @function main: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $48, %rsp movl $10, -20(%rbp) movl $20, -16(%rbp) movl -16(%rbp), %eax //-16(%rbp)的值放到%eax中,即%rdx的低32位中 movl -20(%rbp), %edx //-20(%rbp的值放到%edx中,即%rdx的低32位中 leal (%rdx,%rax), %eax //这里是将 %rdx+%rax的值 放到%eax中 movl %eax, -12(%rbp) //将i+j=k的值 放到-12(%rbp)中 movl $88, 8(%rsp) movl $77, (%rsp) movl $66, %r9d movl $55, %r8d movl $44, %ecx movl $33, %edx movl $22, %esi movl $11, %edi call add movl %eax, -8(%rbp) movl -12(%rbp), %eax movl %eax, -4(%rbp) movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-18)" .section .note.GNU-stack,"",@progbits
会发现 函数中局部变量的顺序与其在栈中的顺序相反,
int i 在栈中的位置是 movl $10, -20(%rbp)
int j在栈中的位置 是 movl $20, -16(%rbp)
即在栈中先是j, 然后是i
再看下可变参数
#include <stdio.h> #include <stdlib.h> int test(int a, int b){ int a2=a; int b2=b; int c = a2+b2; return c; } int main(){ int a=1; int b=2; int res = test(a, b); printf("%d\n", res); return 0; }
其汇编为
.text .globl test .type test, @function test: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -20(%rbp) movl %esi, -24(%rbp) movl -20(%rbp), %eax movl %eax, -12(%rbp) movl -24(%rbp), %eax movl %eax, -8(%rbp) movl -8(%rbp), %eax movl -12(%rbp), %edx leal (%rdx,%rax), %eax //做加法 movl %eax, -4(%rbp) movl -4(%rbp), %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size test, .-test .section .rodata .LC0: .string "%d\n" .text .globl main .type main, @function main: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl $1, -12(%rbp) movl $2, -8(%rbp) movl -8(%rbp), %edx movl -12(%rbp), %eax movl %edx, %esi movl %eax, %edi call test movl %eax, -4(%rbp) //将test函数返回值 存储在-4(%rbp)中 movl $.LC0, %eax movl -4(%rbp), %edx movl %edx, %esi //test函数返回值,即res,做为参数先放到%esi中 movq %rax, %rdi //%rax应该是%d\n 做为参数 放在%rdi中 movl $0, %eax call printf //调用printf函数,说明printf中的前6个参数也是优先放在寄存器中的 movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-18)" .section .note.GNU-stack,"",@progbits