searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

x86_64架构下函数的参数存放在哪些寄存器上

2023-05-16 07:48:58
5
0

        函数调用时,在硬件层面我们需要关注的通常是cpu 的通用寄存器。每个寄存器通常都是有建议的使用方法的,而编译器也通常依照CPU架构的建议来使用这些寄存器,那么x86_64架构下函数调用时参数存放在哪些寄存器上呢?

1, x86_64 有16个通用寄存器

按照通用寄存器的定义这六个寄存器rdi,rsi,rdx,rcx,r8,r9分别放函数传入的第1到第6个参数。

 

2,如果函数的参数超过6个参数会怎么存放呢?

写个简单程序看看:

#include<stdio.h>

int stack_64(int a,int b,int c,int d,int e,int f,int g,int h,int k)
{
          return a+b+c+d+e+f+g+h+k;
}
int main()
{
          stack_64(1,2,3,4,5,6,7,8,9);
          return 0;
}

编译后dump出汇编代码分析:gcc -o out test.c; objdump -d out

      0000000000400563 <stack_64>:
      400563:	55                   	push   %rbp //把main函数的栈地址压栈
      400564:	48 89 e5             	mov    %rsp,%rbp
      400567:	89 7d fc             	mov    %edi,-0x4(%rbp)
      40056a:	89 75 f8             	mov    %esi,-0x8(%rbp)
      40056d:	89 55 f4             	mov    %edx,-0xc(%rbp)
      400570:	89 4d f0             	mov    %ecx,-0x10(%rbp)
      400573:	44 89 45 ec          	mov    %r8d,-0x14(%rbp)
      400577:	44 89 4d e8          	mov    %r9d,-0x18(%rbp)
      40057b:	8b 45 f8             	mov    -0x8(%rbp),%eax
      40057e:	8b 55 fc             	mov    -0x4(%rbp),%edx
      400581:	01 c2                	add    %eax,%edx
      400583:	8b 45 f4             	mov    -0xc(%rbp),%eax
      400586:	01 c2                	add    %eax,%edx
      400588:	8b 45 f0             	mov    -0x10(%rbp),%eax
      40058b:	01 c2                	add    %eax,%edx
      40058d:	8b 45 ec             	mov    -0x14(%rbp),%eax
      400590:	01 c2                	add    %eax,%edx
      400592:	8b 45 e8             	mov    -0x18(%rbp),%eax
      400595:	01 c2                	add    %eax,%edx
      400597:	8b 45 10             	mov    0x10(%rbp),%eax //这里从栈向上偏移16个字节取第7个参数值, 因为之前栈里压了下一条指令和main函数的栈地址向下增增长了16个字节所以这里要向上偏移16个字节取参数
      40059a:	01 c2                	add    %eax,%edx
      40059c:	8b 45 18             	mov    0x18(%rbp),%eax
      40059f:	01 c2                	add    %eax,%edx
      4005a1:	8b 45 20             	mov    0x20(%rbp),%eax
      4005a4:	01 d0                	add    %edx,%eax
      4005a6:	5d                   	pop    %rbp
      4005a7:	c3                   	retq   

      0000000000400532 <main>:
      400532:       55                      push   %rbp
      400533:       48 89 e5                mov    %rsp,%rbp
      400536:       48 83 ec 18             sub    $0x18,%rsp //栈上预留3x8个字节用于存第7,8,9个参数
      40053a:       c7 44 24 10 09 00 00    movl   $0x9,0x10(%rsp)
      400541:       00 
      400542:       c7 44 24 08 08 00 00    movl   $0x8,0x8(%rsp)
      400549:       00 
      40054a:       c7 04 24 07 00 00 00    movl   $0x7,(%rsp)
      400551:       41 b9 06 00 00 00       mov    $0x6,%r9d
      400557:       41 b8 05 00 00 00       mov    $0x5,%r8d
      40055d:       b9 04 00 00 00          mov    $0x4,%ecx
      400562:       ba 03 00 00 00          mov    $0x3,%edx
      400567:       be 02 00 00 00          mov    $0x2,%esi
      40056c:       bf 01 00 00 00          mov    $0x1,%edi
      400571:       e8 77 ff ff ff          callq  4004ed <stack_64> //默认会把下一条指令压栈

    - 可以看到前6个参数分别放到edi,esi,edx, ecx,r8d, r9d上, 从第7个参数开始放到rsp指向的栈内存上
    - 栈内存是向下生长的, 开始sub $0x18 %rsp, 先在栈上预留24个字节的空间用来存第7~9的参数, 分别存到(%rsp),0x8(%rsp),0x10(%rsp)
    - 调用call stack_64函数默认会把stack_64的下一条指令压栈, 同时stack_64函数开始会把man函数的栈地址rbp压栈push $rbp, 所以rsp栈地址会自动向下生长16(0x10)个字节长度
    - 所以在stack_64函数里加的是0x10(%rbp),0x18(%rbp),0x20(%rbp)

2, 结论

x86-64架构下函数传入的参数如果小于等于6, 一次存在rdi,rsi, rdx, rcx, r8, r9; 如果超过6个参数, 从第7个开始存在栈内, 栈内会预留相应的空间存超出6个的参数。

0条评论
0 / 1000
lll
4文章数
0粉丝数
lll
4 文章 | 0 粉丝
原创

x86_64架构下函数的参数存放在哪些寄存器上

2023-05-16 07:48:58
5
0

        函数调用时,在硬件层面我们需要关注的通常是cpu 的通用寄存器。每个寄存器通常都是有建议的使用方法的,而编译器也通常依照CPU架构的建议来使用这些寄存器,那么x86_64架构下函数调用时参数存放在哪些寄存器上呢?

1, x86_64 有16个通用寄存器

按照通用寄存器的定义这六个寄存器rdi,rsi,rdx,rcx,r8,r9分别放函数传入的第1到第6个参数。

 

2,如果函数的参数超过6个参数会怎么存放呢?

写个简单程序看看:

#include<stdio.h>

int stack_64(int a,int b,int c,int d,int e,int f,int g,int h,int k)
{
          return a+b+c+d+e+f+g+h+k;
}
int main()
{
          stack_64(1,2,3,4,5,6,7,8,9);
          return 0;
}

编译后dump出汇编代码分析:gcc -o out test.c; objdump -d out

      0000000000400563 <stack_64>:
      400563:	55                   	push   %rbp //把main函数的栈地址压栈
      400564:	48 89 e5             	mov    %rsp,%rbp
      400567:	89 7d fc             	mov    %edi,-0x4(%rbp)
      40056a:	89 75 f8             	mov    %esi,-0x8(%rbp)
      40056d:	89 55 f4             	mov    %edx,-0xc(%rbp)
      400570:	89 4d f0             	mov    %ecx,-0x10(%rbp)
      400573:	44 89 45 ec          	mov    %r8d,-0x14(%rbp)
      400577:	44 89 4d e8          	mov    %r9d,-0x18(%rbp)
      40057b:	8b 45 f8             	mov    -0x8(%rbp),%eax
      40057e:	8b 55 fc             	mov    -0x4(%rbp),%edx
      400581:	01 c2                	add    %eax,%edx
      400583:	8b 45 f4             	mov    -0xc(%rbp),%eax
      400586:	01 c2                	add    %eax,%edx
      400588:	8b 45 f0             	mov    -0x10(%rbp),%eax
      40058b:	01 c2                	add    %eax,%edx
      40058d:	8b 45 ec             	mov    -0x14(%rbp),%eax
      400590:	01 c2                	add    %eax,%edx
      400592:	8b 45 e8             	mov    -0x18(%rbp),%eax
      400595:	01 c2                	add    %eax,%edx
      400597:	8b 45 10             	mov    0x10(%rbp),%eax //这里从栈向上偏移16个字节取第7个参数值, 因为之前栈里压了下一条指令和main函数的栈地址向下增增长了16个字节所以这里要向上偏移16个字节取参数
      40059a:	01 c2                	add    %eax,%edx
      40059c:	8b 45 18             	mov    0x18(%rbp),%eax
      40059f:	01 c2                	add    %eax,%edx
      4005a1:	8b 45 20             	mov    0x20(%rbp),%eax
      4005a4:	01 d0                	add    %edx,%eax
      4005a6:	5d                   	pop    %rbp
      4005a7:	c3                   	retq   

      0000000000400532 <main>:
      400532:       55                      push   %rbp
      400533:       48 89 e5                mov    %rsp,%rbp
      400536:       48 83 ec 18             sub    $0x18,%rsp //栈上预留3x8个字节用于存第7,8,9个参数
      40053a:       c7 44 24 10 09 00 00    movl   $0x9,0x10(%rsp)
      400541:       00 
      400542:       c7 44 24 08 08 00 00    movl   $0x8,0x8(%rsp)
      400549:       00 
      40054a:       c7 04 24 07 00 00 00    movl   $0x7,(%rsp)
      400551:       41 b9 06 00 00 00       mov    $0x6,%r9d
      400557:       41 b8 05 00 00 00       mov    $0x5,%r8d
      40055d:       b9 04 00 00 00          mov    $0x4,%ecx
      400562:       ba 03 00 00 00          mov    $0x3,%edx
      400567:       be 02 00 00 00          mov    $0x2,%esi
      40056c:       bf 01 00 00 00          mov    $0x1,%edi
      400571:       e8 77 ff ff ff          callq  4004ed <stack_64> //默认会把下一条指令压栈

    - 可以看到前6个参数分别放到edi,esi,edx, ecx,r8d, r9d上, 从第7个参数开始放到rsp指向的栈内存上
    - 栈内存是向下生长的, 开始sub $0x18 %rsp, 先在栈上预留24个字节的空间用来存第7~9的参数, 分别存到(%rsp),0x8(%rsp),0x10(%rsp)
    - 调用call stack_64函数默认会把stack_64的下一条指令压栈, 同时stack_64函数开始会把man函数的栈地址rbp压栈push $rbp, 所以rsp栈地址会自动向下生长16(0x10)个字节长度
    - 所以在stack_64函数里加的是0x10(%rbp),0x18(%rbp),0x20(%rbp)

2, 结论

x86-64架构下函数传入的参数如果小于等于6, 一次存在rdi,rsi, rdx, rcx, r8, r9; 如果超过6个参数, 从第7个开始存在栈内, 栈内会预留相应的空间存超出6个的参数。

文章来自个人专栏
oio
4 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0