红联Linux门户
Linux帮助

从汇编看c++对象在栈空间内的构造

发布时间:2016-07-21 15:18:00来源:linux网站作者:caoyan_12727
我们知道,类对象是c++中很重要的一部分,那么它的大小以及构造方式在汇编代码中是如何实现的呢?在一个程序的虚拟进程空间中,类对象的分布是怎样的呢?它们的构造方式和普通的内置类型有什么区别?下面我们就一起从汇编的角度来看看编译器是如何实现类对象的构造的,本文的所有系统环境环境为ubuntu16.04,G++编译。
 
1.让我们来看看最简单的情况:
#include<stdio.h>  
#include<iostream>  
using namespace std;  
class cy{  
public:  
int a;  
int b;  
int add(int x,int y){  
int sum;  
sum=a+b;  
return sum;  
}  
cy(int m,int n):a(m),b(n){}  
};  
int main(){  
int sum;  
cy aa(1,2);  
printf("%d\n",aa.a);  
return 0;  
}
让我们来看看这个简单对象构造的汇编代码:
080485fb <main>:  
80485fb:   8d 4c 24 04             lea    0x4(%esp),%ecx  
80485ff:   83 e4 f0                and    $0xfffffff0,%esp  
8048602:   ff 71 fc                pushl  -0x4(%ecx)  
8048605:   55                      push   %ebp  
8048606:   89 e5                   mov    %esp,%ebp //main函数的堆栈开始;  
8048608:   51                      push   %ecx //累加寄存器  
8048609:   83 ec 14                sub    $0x14,%esp //预留20字节空间  
804860c:   65 a1 14 00 00 00       mov    %gs:0x14,%eax //这条指令暂时没懂  
8048612:   89 45 f4                mov    %eax,-0xc(%ebp)  
8048615:   31 c0                   xor    %eax,%eax  //寄存器清零  
8048617:   83 ec 04                sub    $0x4,%esp  //调整栈顶指针  
804861a:    6a 02                  push   $0x2  //因为要调用构造函数,这里将构造函数的参数2入栈,从右向左  
804861c:   6a 01                   push   $0x1  //参数1入栈  
804861e:   8d 45 ec                lea    -0x14(%ebp),%eax  //对象aa的地址,位于ebp寄存器的下方第20字节的地方  
8048621:   50                      push   %eax  //将对象aa的地址入栈  
8048622:   e8 91 00 00 00          call   80486b8 <_ZN2cyC1Eii>  //调用类cy的构造函数来初始化对象aa  
8048627:   83 c4 10                add    $0x10,%esp //函数返回,调整栈顶指针   
804862a:   8b 45 ec                mov    -0x14(%ebp),%eax  //将对象aa的地址放入到eax寄存器  
804862d:   83 ec 08                sub    $0x8,%esp  
8048630:   50                      push   %eax  //将对象aa的地址入栈  
8048631:   68 50 87 04 08          push   $0x8048750 //将字符串常量%d\n的地址入栈  
8048636:   e8 95 fe ff ff          call   80484d0 <printf@plt> //调用printf函数,这里是动态链接  
804863b:   83 c4 10                add    $0x10,%esp  //调整栈顶指针  
804863e:   b8 00 00 00 00          mov    $0x0,%eax  
8048643:   8b 55 f4                mov    -0xc(%ebp),%edx  
8048646:   65 33 15 14 00 00 00    xor    %gs:0x14,%edx  
804864d:   74 05                   je     8048654 <main+0x59>  
804864f:   e8 8c fe ff ff          call   80484e0 <__stack_chk_fail@plt>  
8048654:   8b 4d fc                mov    -0x4(%ebp),%ecx  
8048657:   c9                      leave    
8048658:   8d 61 fc                lea    -0x4(%ecx),%esp  
804865b:   c3                      ret
以及cy对象的构造函数:
080486b8 <_ZN2cyC1Eii>:  
80486b8:   55                      push   %ebp  //上一级函数的栈底地址入栈  
80486b9:   89 e5                   mov    %esp,%ebp //将栈底指针移到当前栈顶指针  
80486bb:   8b 45 08                mov    0x8(%ebp),%eax //将相对与ebp指针向上偏移8个单位地址的值传送到eax寄存器,因为此时ebp之上的内容  
//分别为oldebp(函数的返回地址上一级栈帧的ebp,-0x0(%ebp)),ret(函数的返回地址,-0x04(%ebp),aa对象的地址(-0x08(%ebp)),所以这条指令的意思就是将aa对  
//象的地址放到eax寄存器
80486be:  8b 55 0c                mov    0xc(%ebp),%edx //将入栈的1的值传送到edx寄存器  
80486c1:   89 10                   mov    %edx,(%eax)  //将1存放到aa对象的前4个字节中  
80486c3:  8b 45 08                mov    0x8(%ebp),%eax //将aa对象地址再次放到eax寄存器  
80486c6:   8b 55 10                mov    0x10(%ebp),%edx //将2的值放到edx寄存器  
80486c9:   89 50 04                mov    %edx,0x4(%eax) //将寄存器edx的值放到相对域aa地址向高地址方向偏移4个字节的地址中(就是aa对象 //成员b的地址)  
80486cc:   90                      nop  
80486cd:   5d                      pop    %ebp  
80486ce:   c3                      ret      
80486cf:   90                      nop  
 
2.以对象为参数,返回一个整形值:
class cy{  
public:  
int a;  
int b;  
int add(int x,int y){  
int sum;  
sum=a+b;  
return sum;  
}  
cy(int m,int n):a(m),b(n){}  
};  
int add(cy tmp){  
int sum=0;  
sum=tmp.a+tmp.b;  
return sum;  
}
int main(){  
int sum;  
cy aa(1,2);  
sum=add(aa);  
printf("%d\n",sum);  
printf("%p\n",&aa);  
return 0;  
}
主要汇编码如下:
8048622:    55                      push   %ebp  
8048623:    89 e5                   mov    %esp,%ebp  
8048625:    51                      push   %ecx  
8048626:    83 ec 14                sub    $0x14,%esp  
8048629:    65 a1 14 00 00 00       mov    %gs:0x14,%eax  
804862f:    89 45 f4                mov    %eax,-0xc(%ebp)  
8048632:    31 c0                   xor    %eax,%eax  
8048634:    83 ec 04                sub    $0x4,%esp  
8048637:    6a 02                   push   $0x2  
8048639:    6a 01                   push   $0x1  
804863b:    8d 45 ec                lea    -0x14(%ebp),%eax //类对象aa的地址  
804863e:    50                      push   %eax  
804863f:    e8 b8 00 00 00          call   80486fc <_ZN2cyC1Eii> //调用类cy构造函数  
8048644:    83 c4 10                add    $0x10,%esp   
8048647:    83 ec 08                sub    $0x8,%esp  
804864a:    ff 75 f0                pushl  -0x10(%ebp)  //将2的值入栈  
804864d:    ff 75 ec                pushl  -0x14(%ebp)   //将1的值入栈,那么相当与对象aa入栈  
8048650:    e8 a6 ff ff ff          call   80485fb <_Z3add2cy>  
8048655:    83 c4 10                add    $0x10,%esp  
8048658:    89 45 e8                mov    %eax,-0x18(%ebp) //eax保存返回值,然后将返回值赋值sum,课件变量sum的地址为-0x18(%ebp)  
我们可以看到类对象作参数时,将对象的每个变量的值依次入栈。
 
本文永久更新地址:http://www.linuxdiyf.com/linux/22588.html