Linux X86架构参数传递规则

背景

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

传参顺序

在32位和64位机器上,寄存器名称不同,64位机器为rxx,32位机器为exx。传参顺序如下,

64位系统有16个寄存器,32位系统只有8个。e开头的寄存器命名依然可以直接运用于相应寄存器的低32位。而剩下的寄存器名则是从r8 - r15,其低位分别用d,w,b指定长度。

测试程序

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

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

#include

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

12

13 return 0;

  0x08048444 <+74>: mov    $0x0,%eax

14 }

  0x08048449 <+79>: leave 

  0x0804844a <+80>: ret   

End of assembler dump.

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

现在再来看下64为系统,

#include

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

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位机器一样放入栈中。

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

你可能感兴趣的:(Linux X86架构参数传递规则)