Linux X86架构参数传递规则

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010039418/article/details/85275211

背景

突然好奇x86架构下函数参数怎么传递的,之前只是听别人说过通过寄存器,但是怎么传,什么顺序都没有仔细研究过,也没有实际测试过,因此就想着用实践来检验一下咯。

传参顺序

在32位和64位机器上,寄存器名称不同,64位机器为rxx,32位机器为exx。传参顺序如下,
在这里插入图片描述
64位系统有16个寄存器,32位系统只有8个。e开头的寄存器命名依然可以直接运用于相应寄存器的低32位。而剩下的寄存器名则是从r8 - r15,其低位分别用d,w,b指定长度。

测试程序

我们通过简单的程序编译后,使用gdb反汇编看下参数传入是否和上面描述的相符合。

先看看32位程序是怎么传递参数的,

#include <stdio.h>

static int test32(int arg1, int arg2, int arg3, int arg4,int arg5,
                int arg6, int arg7, int arg8)
{
        return 0;
}

int main(void)
{
        test32(1, 2, 3, 4, 5, 6, 7, 8);

        return 0;
}

将函数入参设置成8个,使用gdb反汇编观察参数具体传递情况。

[root@CentOS-7-2 /home/register]# gcc -g -m32 test.c -o test
[root@CentOS-7-2 /home/register]# gdb --quiet
(gdb) file test
Reading symbols from /home/register/test...done.
(gdb) disassemble /m main
Dump of assembler code for function main:
10	{
   0x080483fa <+0>:	push   %ebp
   0x080483fb <+1>:	mov    %esp,%ebp
   0x080483fd <+3>:	sub    $0x20,%esp

11		test32(1, 2, 3, 4, 5, 6, 7, 8);
   0x08048400 <+6>:	movl   $0x8,0x1c(%esp)
   0x08048408 <+14>:	movl   $0x7,0x18(%esp)
   0x08048410 <+22>:	movl   $0x6,0x14(%esp)
   0x08048418 <+30>:	movl   $0x5,0x10(%esp)
   0x08048420 <+38>:	movl   $0x4,0xc(%esp)
   0x08048428 <+46>:	movl   $0x3,0x8(%esp)
   0x08048430 <+54>:	movl   $0x2,0x4(%esp)
   0x08048438 <+62>:	movl   $0x1,(%esp)
   0x0804843f <+69>:	call   0x80483f0 <test32>

12	
13		return 0;
   0x08048444 <+74>:	mov    $0x0,%eax

14	}
   0x08048449 <+79>:	leave  
   0x0804844a <+80>:	ret    

End of assembler dump.

由上可见,32位系统中并没有通过寄存器传参,而是直接将参数入栈,而且是按照参数顺序从右向左依次入栈。

现在再来看下64为系统,

#include <stdio.h>

static int test64(long int arg1, long int arg2, long int arg3, long int arg4,
                long int arg5, long int arg6, long int arg7, long int arg8)
{
        return 0;
}

int main(void)
{
        test64(11111111111L, 22222222222L, 33333333333L, 44444444444L,
                55555555555L, 66666666666L, 77777777777L, 88888888888L);

        return 0;
}

这里入参使用long int类型,是因为如果使用int型,编译器会默认使用32位寄存器,即exx寄存器,不直观。

[root@CentOS-7-2 /home/register]# gcc -g -m64 test.c -o test
[root@CentOS-7-2 /home/register]# gdb --quiet
(gdb) file test
Reading symbols from /home/register/test...done.
(gdb) disassemble /m main
Dump of assembler code for function main:
10	{
   0x0000000000400513 <+0>:	push   %rbp
   0x0000000000400514 <+1>:	mov    %rsp,%rbp
   0x0000000000400517 <+4>:	sub    $0x10,%rsp

11		test64(11111111111L, 22222222222L, 33333333333L, 44444444444L,
   0x000000000040051b <+8>:	movabs $0x14b230ce38,%rax
   0x0000000000400525 <+18>:	mov    %rax,0x8(%rsp)
   0x000000000040052a <+23>:	movabs $0x121beab471,%rax
   0x0000000000400534 <+33>:	mov    %rax,(%rsp)
   0x0000000000400538 <+37>:	movabs $0xf85a49aaa,%r9
   0x0000000000400542 <+47>:	movabs $0xcef5e80e3,%r8
   0x000000000040054c <+57>:	movabs $0xa5918671c,%rcx
   0x0000000000400556 <+67>:	movabs $0x7c2d24d55,%rdx
   0x0000000000400560 <+77>:	movabs $0x52c8c338e,%rsi
   0x000000000040056a <+87>:	movabs $0x2964619c7,%rdi
   0x0000000000400574 <+97>:	callq  0x4004f0 <test64>

12	                55555555555L, 66666666666L, 77777777777L, 88888888888L);
13	
14		return 0;
   0x0000000000400579 <+102>:	mov    $0x0,%eax

15	}
   0x000000000040057e <+107>:	leaveq 
   0x000000000040057f <+108>:	retq   

End of assembler dump.

为了更直观,我们把64位的入参转换为16进制,

十进制 16进制
11111111111 0x2964619c7
22222222222 0x52c8c338e
33333333333 0x7c2d24d55

从这我们就能看出,参数传递确实是按照rdi、rsi、rdx、rcx、r8、r9的顺序存放第一个参数到第六个参数。对于超出6个参数的入参还是和32位机器一样放入栈中。

相比于参数入栈,通过寄存器传递参数肯定更为高效。

猜你喜欢

转载自blog.csdn.net/u010039418/article/details/85275211