博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux Debugging(三): C++函数调用的参数传递方法总结(通过gdb+反汇编)
阅读量:7118 次
发布时间:2019-06-28

本文共 8387 字,大约阅读时间需要 27 分钟。

         上一篇文章《》没想到能得到那么多人的喜爱,因为那篇文章是以32位的C++普通函数(非类成员函数)为例子写的,因此只是一个特殊的例子。本文将函数调用时的参数传递方法进行一下总结。总结将为C++普通函数、类成员函数;32位和64位进行总结。

        建议还是读一下,这样本文的结论将非常容易理解,将非常好的为CoreDump分析开一个好头。而且,它也是32位C++ 普通函数的调用的比较好的例子,毕竟从汇编的角度,将参数如何传递的进行了比较好的说明。

1. 32位程序普通函数

普通函数的意思是非class member function

void func2(int a, int b){  a++;  b+ = 2;}int main(){  func2( 1111, 2222);  return 0;}

main函数的汇编:

main:        pushl   %ebp        movl    %esp, %ebp        subl    $8, %esp        movl    $2222, 4(%esp)        movl    $1111, (%esp)        call    func2(int, int)        movl    $0, %eax        leave        ret
1111是第一个参数,放到了esp指向的地址。2222是第二个参数,放到了高地址。因次我们可以知道,在函数func2中,通过ebp+8可以访问到第一个参数1111,通过ebp+12可以访问到第二个参数2222。

func2(int, int):        pushl   %ebp        movl    %esp, %ebp        addl    $1, 8(%ebp)        addl    $2, 12(%ebp)        popl    %ebp        ret

下面我们使用gdb通过ebp打印一下传入的参数:

anzhsoft@ubuntu:~/linuxDebugging/parameter$ gdb a.out GNU gdb 6.8Copyright (C) 2008 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "i686-pc-linux-gnu"...(gdb) b func2Breakpoint 1 at 0x8048597: file m32noclass.cpp, line 6.(gdb) rStarting program: /home/anzhsoft/linuxDebugging/parameter/a.out Breakpoint 1, func2 (a=1111, b=2222) at m32noclass.cpp:6warning: Source file is more recent than executable.6 a++;(gdb) p *(int*)($ebp+8)$1 = 1111(gdb) p *(int*)($ebp+12)$2 = 2222

总结:

1. 参数通过栈查传递,底地址传递从左边开始的第一个参数

2. 使用gdb可以很方便打印传入参数

(gdb) p *(int*)($ebp+8)$1 = 1111(gdb) p *(int*)($ebp+12)$2 = 2222

其实32位的程序也可以使用寄存器传递参数。请看下一节。

2. 32位普通函数-通过寄存器传递参数

可以使用GCC的扩展功能__attribute__使得参数传递可以使用寄存器。

修改第一节的函数:

#define STACKCALL __attribute__((regparm(3))) void STACKCALL func4(int a, int b, int c, int d){  a++;  b += 2;  c += 3;  d += 4;}int main(){  func4(1111, 2222, 3333, 4444);  return 0;}
__attribute__((regparm(3)))意思是使用寄存器

anzhsoft@ubuntu:~/linuxDebugging/parameter$ gdb a.out GNU gdb 6.8Copyright (C) 2008 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.  Type "show copying"and "show warranty" for details.This GDB was configured as "i686-pc-linux-gnu"...(gdb) b mainBreakpoint 1 at 0x80485bb: file m32noclass.cpp, line 14.(gdb) rStarting program: /home/anzhsoft/linuxDebugging/parameter/a.out Breakpoint 1, main () at m32noclass.cpp:1414      func4(1111, 2222, 3333, 4444);(gdb) sfunc4 (a=1111, b=2222, c=3333, d=4444) at m32noclass.cpp:66      a++;(gdb) i reax            0x457    1111ecx            0xd05    3333edx            0x8ae    2222ebx            0xb3eff4    11792372esp            0xbf8e9580    0xbf8e9580ebp            0xbf8e958c    0xbf8e958cesi            0x0    0edi            0x0    0eip            0x80485a3    0x80485a3
eflags         0x282    [ SF IF ]cs             0x73    115ss             0x7b    123ds             0x7b    123es             0x7b    123fs             0x0    0gs             0x33    51(gdb) p *(int*)($ebp+8)$1 = 4444(gdb)
可以看到,前三个参数分别通过eax/edx/ecx传递,第四个参数通过栈传递。这种传递方式被称为fastcall

3. 32位 class member function 参数传递方式

我们通过宏USINGSTACK强制使用栈来传递参数。

#define USINGSTACK __attribute__((regparm(0)))class Test{public:  Test():number(3333){}  void USINGSTACK func2(int a, int b)  {    a++;    b += 2;  }private:  int number;};int main(int argc, char* argv[]){  Test tInst;  tInst.func2(1111, 2222);  return 0;}
通过gdb来打印传入的参数:

anzhsoft@ubuntu:~/linuxDebugging/parameter$ gdb a.out GNU gdb 6.8Copyright (C) 2008 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "i686-pc-linux-gnu"...(gdb) b mainBreakpoint 1 at 0x804859d: file m32class.cpp, line 18.(gdb) rStarting program: /home/anzhsoft/linuxDebugging/parameter/a.out Breakpoint 1, main (argc=1, argv=0xbf98a454) at m32class.cpp:1818 Test tInst;(gdb) n19 tInst.func2(1111, 2222);(gdb) sTest::func2 (this=0xbf98a39c, a=1111, b=2222) at m32class.cpp:99 a++;(gdb) p *(int*)($ebp+12)$1 = 1111(gdb) p *(int*)($ebp+16)$2 = 2222(gdb) p *this$3 = {number = 3333}
可以看到,class成员函数的第一个参数是对象的指针。通过这种方式,成员函数可以和非成员函数以类似的方式进行调用。

4. x86-64 class member function 的参数传递

在x86-64中,整形和指针型参数的参数从左到右依次保存到rdi,rsi,rdx,rcx,r8,r9中。浮点型参数会保存到xmm0,xmm1……。多余的参数会保持到栈上。

下面这个例子将传递九个参数。可以通过它来验证一下各个寄存器的使用情况:

class Test{public:  Test():number(5555){}  void func9(int a, int b, int c, int d,char*str, long e, long f, float h, double i)  {    a++;    b += 2;    c += 3;    d += 4;  }private:  int number;};int main(int argc, char* argv[]){  Test tInst;  tInst.func9(1111, 2222, 3333, 4444, "hello, world!", 6666,7777, 8.888, 9.999);  return 0;}
下面通过gdb验证各个寄存器的使用情况:

khawk-dev-zhanga12:~/study/c++callstack # gdb a.outGNU gdb (GDB) SUSE (7.0-0.4.16)Copyright (C) 2009 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-suse-linux".For bug reporting instructions, please see:
...Reading symbols from /root/study/c++callstack/a.out...done.(gdb) b mainBreakpoint 1 at 0x40071b: file m64class.cpp, line 19.(gdb) rStarting program: /root/study/c++callstack/a.outBreakpoint 1, main (argc=1, argv=0x7fffffffe188) at m64class.cpp:1919 Test tInst;(gdb) n20 tInst.func9(1111, 2222, 3333, 4444, "hello, world!", 6666,7777, 8.888, 9.999);(gdb) sTest::func9 (this=0x7fffffffe0a0, a=1111, b=2222, c=3333, d=4444, str=0x400918 "hello, world!", e=6666, f=7777, h=8.88799953, i=9.9990000000000006) at m64class.cpp:88 a++;(gdb) info regrax 0x400918 4196632rbx 0x400830 4196400rcx 0xd05 3333rdx 0x8ae 2222rsi 0x457 1111rdi 0x7fffffffe0a0 140737488347296rbp 0x7fffffffe070 0x7fffffffe070rsp 0x7fffffffe070 0x7fffffffe070r8 0x115c 4444r9 0x400918 4196632r10 0xffffffffffffffff -1r11 0x7ffff733d890 140737340758160r12 0x400620 4195872r13 0x7fffffffe180 140737488347520r14 0x0 0r15 0x0 0rip 0x4007ff 0x4007ff
eflags 0x202 [ IF ]cs 0x33 51ss 0x2b 43ds 0x0 0es 0x0 0fs 0x0 0gs 0x0 0fctrl 0x37f 895fstat 0x0 0ftag 0xffff 65535fiseg 0x0 0fioff 0x0 0foseg 0x0 0fooff 0x0 0fop 0x0 0mxcsr 0x1f80 [ IM DM ZM OM UM PM ](gdb) p *this$1 = {number = 5555}(gdb) p *(char*)$r9@13$2 = "hello, world!"(gdb) p *(int*)($rbp+16)$4 = 6666(gdb) p *(int*)($rbp+24)$5 = 7777(gdb) p *(int*)$rdi$6 = 5555
r9存储的是指针型char *str的字符串。因为寄存器只能存储6个整形、指针型参数,注意,Test 对象的指针占用了一个。因此
参数e和f只能通过栈传递。注意每个地址空间占8个字节。因此rbp+2*8存储的是e,rbp+3*8存储的是f。

float h是通过xmm0,double i是通过xmm1传递的。这类寄存器的size大小是128bits,当然128个bits可以不填满。GDB将这些寄存器看成下面这些数据的联合:

union{  float   v4_float[4];  double  v2_double[2];  int8_t  v16_int8[16];  int16_t v8_int16[8];  int32_t v4_int32[4];  int64_t v2_int64[2];  int128_t unit128;}xmm0,xmm1,xmm2,xmm3,xmm4,xmm5,xmm6,xmm7;
打印方式如下:

(gdb) p $xmm0.v4_float[0]$7 = 8.88799953(gdb) p $xmm1.v2_double[0]$8 = 9.9990000000000006

总结:

在x86-64中,整形和指针型参数的参数从左到右依次保存到rdi,rsi,rdx,rcx,r8,r9中。浮点型参数会保存到xmm0,xmm1……。多余的参数会保持到栈上。

5. 总结

32位:

  1)默认的传递方法不使用寄存器,使用ebp+8可以访问第一个参数,ebp+16可以访问第二个参数。。。

  2) 可以使用GCC扩展__attribute__将参数放到寄存器上,依次使用的寄存器是eax/edx/ecx

  3)对于class 的member function,调用时第一个参数为this(对象指针)地址。因此函数的第一个参数使用ebp+16才可以访问到。

  4)ebp存储的是上层的bp的地址。ebp+4存储的是函数返回时的地址指令。

64位:

  1)默认的传递方法是使用寄存器。当然也可以强制使用栈,方式:函数声明时使用

__attribute__((regparm(0)))
  2)整形和指针型参数的参数从左到右依次保存到rdi,rsi,rdx,rcx,r8,r9中。浮点型参数会保存到xmm0,xmm1……。多余的参数会保持到栈上。

  3)xmm0……是比较特殊的寄存器,访问内容时需要注意。

尊重原创,转载请注明出处: anzhsoft http://blog.csdn.net/anzhsoft/article/details/18739193

转载于:https://www.cnblogs.com/wuwa/p/6190836.html

你可能感兴趣的文章
JS设计模式初识(四)-迭代器模式
查看>>
详解promise、async和await的执行顺序
查看>>
Windows多屏开发小记
查看>>
区块链开发中的9大应用场景
查看>>
使用webpack.require优化vue项目的路由
查看>>
CSS盒子模型
查看>>
什么是TensorBoard?
查看>>
JavaScript语言精粹:对象——读书笔记
查看>>
java版spring cloud+spring boot+redis社交电子商务平台(八)消息总线(Spring Cloud Bus)
查看>>
spring cloud构建互联网分布式微服务云平台- Netflix
查看>>
区块链软件公司:区块链技术的应用
查看>>
PHP 的魔术方法及其应用
查看>>
(九)企业分布式微服务云SpringCloud SpringBoot mybatis-服务链路追踪(Spring Cloud Sleuth)...
查看>>
springcloud(四):熔断器Hystrix
查看>>
Centos6.3搭建cacti&nagios
查看>>
[SQL Server][FILESTREAM] -- Using INSERT, UPDATE and DELETE to manage SQL Server FILESTREAM Data
查看>>
成为Linux内核高手的四个方法
查看>>
教你使用squid搭配dante做代理
查看>>
ecshop调用文章显示上一篇下一篇_无需整理
查看>>
cisco ***笔记
查看>>