上一篇文章《linux debugging:使用反汇编理解c++程序函数调用栈》没想到能得到那么多人的喜爱,因为那篇文章是以32位的c++普通函数(非类成员函数)为例子写的,因此只是一个特殊的例子。本文将函数调用时的参数传递方法进行一下总结。总结将为c++普通函数、类成员函数;32位和64位进行总结。
建议还是读一下linux debugging:使用反汇编理解c++程序函数调用栈,这样本文的结论将非常容易理解,将非常好的为coredump分析开一个好头。而且,它也是32位c++ 普通函数的调用的比较好的例子,毕竟从汇编的角度,将参数如何传递的进行了比较好的说明。
普通函数的意思是非class member function
main函数的汇编:
1111是第一个参数,放到了esp指向的地址。2222是第二个参数,放到了高地址。因次我们可以知道,在函数func2中,通过ebp+8可以访问到第一个参数1111,通过ebp+12可以访问到第二个参数2222。
下面我们使用gdb通过ebp打印一下传入的参数:
总结:
1. 参数通过栈查传递,底地址传递从左边开始的第一个参数
2. 使用gdb可以很方便打印传入参数
其实32位的程序也可以使用寄存器传递参数。请看下一节。
可以使用gcc的扩展功能__attribute__使得参数传递可以使用寄存器。
修改第一节的函数:
__attribute__((regparm(3)))意思是使用寄存器
可以看到,前三个参数分别通过eax/edx/ecx传递,第四个参数通过栈传递。这种传递方式被称为fastcall
我们通过宏usingstack强制使用栈来传递参数。
通过gdb来打印传入的参数:
可以看到,class成员函数的第一个参数是对象的指针。通过这种方式,成员函数可以和非成员函数以类似的方式进行调用。
在x86-64中,整形和指针型参数的参数从左到右依次保存到rdi,rsi,rdx,rcx,r8,r9中。浮点型参数会保存到xmm0,xmm1……。多余的参数会保持到栈上。
下面这个例子将传递九个参数。可以通过它来验证一下各个寄存器的使用情况:
下面通过gdb验证各个寄存器的使用情况:
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将这些寄存器看成下面这些数据的联合:
打印方式如下:
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)默认的传递方法是使用寄存器。当然也可以强制使用栈,方式:函数声明时使用
2)整形和指针型参数的参数从左到右依次保存到rdi,rsi,rdx,rcx,r8,r9中。浮点型参数会保存到xmm0,xmm1……。多余的参数会保持到栈上。
3)xmm0……是比较特殊的寄存器,访问内容时需要注意。
尊重原创,转载请注明出处: anzhsoft http://blog.csdn.net/anzhsoft/article/details/18739193