【反汇编玩耍1】通过反汇编真正理解函数参数传递过程

过去我一直以为,函数参数就只是通过栈传递的,最近看一些反汇编代码,发现好多时候都是通过寄存器。做了个实验,最终明白了,函数的参数怎样传递,其实是和参数个数有关。

在linux下的objdump反汇编的结果:如果参数很多,有一些参数会通过栈传递,否则通过寄存器。

在windows下的VS反汇编的结果:都通过栈。

 

C代码:

#include <stdio.h>

void func1(int a, int b)
{
    printf("%d", a+b);
}

void func2(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k)
{
    printf("%d",a+b+c+d+e+f+g+h+i+j+k);
}

void main ()
{
    int a = 1;
    int b = 1;
    int c = 1;
    int d = 1;
    int e = 1;
    int f = 1;
    int g = 1;
    int h = 1;
    int i = 1;
    int j = 1;
    int k = 1;
    func1(a, b);
    func2(a, b, c, d, e, f, g, h, i, j, k);
}

 

linux下通过objdump反汇编:

[root@VM_120_194_centos C]# gcc -E 20170704.c -o 20170704.i
[root@VM_120_194_centos C]# gcc -S 20170704.i -o 20170704.s
[root@VM_120_194_centos C]# gcc -c 20170704.s -o 20170704.o
[root@VM_120_194_centos C]# objdump -S 20170704.o

20170704.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <func1>:
   0:    55                       push   %rbp
   1:    48 89 e5                 mov    %rsp,%rbp
   4:    48 83 ec 10              sub    $0x10,%rsp
   8:    89 7d fc                 mov    %edi,-0x4(%rbp)
   b:    89 75 f8                 mov    %esi,-0x8(%rbp)
   e:    8b 45 f8                 mov    -0x8(%rbp),%eax
  11:    8b 55 fc                 mov    -0x4(%rbp),%edx
  14:    01 d0                    add    %edx,%eax
  16:    89 c6                    mov    %eax,%esi
  18:    bf 00 00 00 00           mov    $0x0,%edi
  1d:    b8 00 00 00 00           mov    $0x0,%eax
  22:    e8 00 00 00 00           callq  27 <func1+0x27>
  27:    c9                       leaveq 
  28:    c3                       retq   

0000000000000029 <func2>:
  29:    55                       push   %rbp
  2a:    48 89 e5                 mov    %rsp,%rbp
  2d:    48 83 ec 20              sub    $0x20,%rsp
  31:    89 7d fc                 mov    %edi,-0x4(%rbp)
  34:    89 75 f8                 mov    %esi,-0x8(%rbp)
  37:    89 55 f4                 mov    %edx,-0xc(%rbp)
  3a:    89 4d f0                 mov    %ecx,-0x10(%rbp)
  3d:    44 89 45 ec              mov    %r8d,-0x14(%rbp)
  41:    44 89 4d e8              mov    %r9d,-0x18(%rbp)
  45:    8b 45 f8                 mov    -0x8(%rbp),%eax
  48:    8b 55 fc                 mov    -0x4(%rbp),%edx
  4b:    01 c2                    add    %eax,%edx
  4d:    8b 45 f4                 mov    -0xc(%rbp),%eax
  50:    01 c2                    add    %eax,%edx
  52:    8b 45 f0                 mov    -0x10(%rbp),%eax
  55:    01 c2                    add    %eax,%edx
  57:    8b 45 ec                 mov    -0x14(%rbp),%eax
  5a:    01 c2                    add    %eax,%edx
  5c:    8b 45 e8                 mov    -0x18(%rbp),%eax
  5f:    01 c2                    add    %eax,%edx
  61:    8b 45 10                 mov    0x10(%rbp),%eax
  64:    01 c2                    add    %eax,%edx
  66:    8b 45 18                 mov    0x18(%rbp),%eax
  69:    01 d0                    add    %edx,%eax
  6b:    89 c6                    mov    %eax,%esi
  6d:    bf 00 00 00 00           mov    $0x0,%edi
  72:    b8 00 00 00 00           mov    $0x0,%eax
  77:    e8 00 00 00 00           callq  7c <func2+0x53>
  7c:    c9                       leaveq 
  7d:    c3                       retq   

000000000000007e <main>:
  7e:    55                       push   %rbp
  7f:    48 89 e5                 mov    %rsp,%rbp
  82:    48 83 ec 60              sub    $0x60,%rsp
  86:    c7 45 fc 01 00 00 00     movl   $0x1,-0x4(%rbp)
  8d:    c7 45 f8 01 00 00 00     movl   $0x1,-0x8(%rbp)
  94:    c7 45 f4 01 00 00 00     movl   $0x1,-0xc(%rbp)
  9b:    c7 45 f0 01 00 00 00     movl   $0x1,-0x10(%rbp)
  a2:    c7 45 ec 01 00 00 00     movl   $0x1,-0x14(%rbp)
  a9:    c7 45 e8 01 00 00 00     movl   $0x1,-0x18(%rbp)
  b0:    c7 45 e4 01 00 00 00     movl   $0x1,-0x1c(%rbp)
  b7:    c7 45 e0 01 00 00 00     movl   $0x1,-0x20(%rbp)
  be:    c7 45 dc 01 00 00 00     movl   $0x1,-0x24(%rbp)
  c5:    c7 45 d8 01 00 00 00     movl   $0x1,-0x28(%rbp)
  cc:    c7 45 d4 01 00 00 00     movl   $0x1,-0x2c(%rbp)
  d3:    8b 55 f8                 mov    -0x8(%rbp),%edx
  d6:    8b 45 fc                 mov    -0x4(%rbp),%eax
  d9:    89 d6                    mov    %edx,%esi
  db:    89 c7                    mov    %eax,%edi
  dd:    e8 00 00 00 00           callq  e2 <main+0x64>
  e2:    44 8b 4d e8              mov    -0x18(%rbp),%r9d
  e6:    44 8b 45 ec              mov    -0x14(%rbp),%r8d
  ea:    8b 4d f0                 mov    -0x10(%rbp),%ecx
  ed:    8b 55 f4                 mov    -0xc(%rbp),%edx
  f0:    8b 75 f8                 mov    -0x8(%rbp),%esi
  f3:    8b 45 fc                 mov    -0x4(%rbp),%eax
  f6:    8b 7d d4                 mov    -0x2c(%rbp),%edi
  f9:    89 7c 24 20              mov    %edi,0x20(%rsp)
  fd:    8b 7d d8                 mov    -0x28(%rbp),%edi
 100:    89 7c 24 18              mov    %edi,0x18(%rsp)
 104:    8b 7d dc                 mov    -0x24(%rbp),%edi
 107:    89 7c 24 10              mov    %edi,0x10(%rsp)
 10b:    8b 7d e0                 mov    -0x20(%rbp),%edi
 10e:    89 7c 24 08              mov    %edi,0x8(%rsp)
 112:    8b 7d e4                 mov    -0x1c(%rbp),%edi
 115:    89 3c 24                 mov    %edi,(%rsp)
 118:    89 c7                    mov    %eax,%edi
 11a:    e8 00 00 00 00           callq  11f <main+0xa1>
 11f:    c9                       leaveq 
 120:    c3                       retq   

各局部变量:

f     -0x18(%rbp)

e     -0x14(%rbp)

d    -0x10(%rbp)
c       -0xc(%rbp)
b     -0x8(%rbp)
a      -0x4(%rbp)
k      -0x2c(%rbp)
j      -0x28(%rbp)
i      -0x24(%rbp)
h       -0x20(%rbp)
g       -0x1c(%rbp)

 

func1和func2两个函数的反汇编代码不用管,我们看main函数里的。可以清晰看到,86 - cc是给变量(栈空间内存)赋值的过程,也就是

int a = 1;int b = 1;int c = 1;int d = 1;int e = 1;int f = 1;int g = 1;int h = 1;int i = 1;int j = 1;int k = 1;

d3 - dd是func1调用的过程。需要两个参数,赋值给edi和esi寄存器。也就是说,这里函数参数是存在寄存器里的。(2023年12月补充,%ebx,%esi,%edi三个寄存器,是被调用者需要保存的寄存器)

 

func1里面,则是先将edi和esi的内容copy到自己的栈帧空间。这就是C语言书里说的所谓“值传递”吧?

 

func1的调用是,main函数的e2 - 11a,这个很有趣,前一部分变量是存在寄存器里(和上面讲的一个道理),而寄存器不够的时候(参数太多了),就通过rsp栈指针寄存器,直接存到下一个要调用的函数的栈帧空间里面。好有趣。(2023年12月补充,%rbp指向当前栈帧的底,%rsp指向当前栈帧的顶,在栈顶下面存参数,开辟的下一个栈帧,通过%rbp在栈帧底取参数)

 

 2023年12月补充:x86-64的机器中,很多情况,调用函数,并不一定开辟新的栈帧,因为大多数参数用寄存器传递即可。毕竟,相比之前的IA32,x86-64的寄存器数量翻倍为16个。

 

 《深入理解操作系统》一书的图:

 

posted on 2017-07-04 16:09  J·Marcus  阅读(2950)  评论(0编辑  收藏  举报

导航