用汇编的眼光看C++ (之x86汇编)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
说到用汇编的眼光看C++语言,那么怎么阅读汇编代码就成了我们需要解决的一个问题。其实,实话说,汇编其实不难。只是我们需要明白这样几个问题:
(1)汇编是什么语言?
(2)汇编中的主要内容有哪些?
(3)汇编语言是怎么和实际C/C++语言代码一一对应的?
(1)汇编是什么语言
其实汇编语言是CPU指令码的一种标记符号。不同的CPU具有不同的指令集,普通PC上的CPU一般来自AMD或者是INTEL,使用的也就是我们今天所要说的X86指令集。其他类似的CPU还有POWERPC,主要来自电信企业的交换机、路由器使用;ARM类型,主要是智能终端或者仪器仪表类的设备使用;SUN SPARC类型,主要来供SUN服务器使用。因为CPU指令集和二进制码几乎是一一对应的,所以汇编语言不但可以帮助我们快速了解机器的硬件,也方便我们了解程序是怎么在设备上面运行的。
(2)汇编语言的内容有哪些?
汇编语言的内容有很多,但是真正和我们C/C++语言相关的内容其实并不多。大体上你需要了解的只有寄存器、段地址、堆栈、寄存器之间的基本操作、地址访问这些就足够了。
(3)汇编语言是怎么和实际语言一一对应的?
我们从一个范例开始。一般来说,一条语句都需要拆分成若干条汇编语句。比如说下面这一段话:
int m = 10;
int n = 20;
int p = m + n;
我们这里假设m、n、p都是处在一个函数之中,所以事实上三个变量都是临时变量,在进入到函数之前,ebp和esp之间都要腾出空间为这些临时变量做准备。那么这三句话应该是这样解释的。
43: int m = 10;
004012E8 mov dword ptr [ebp-4],0Ah
44: int n = 20;
004012EF mov dword ptr [ebp-8],14h
45: int p = m + n;
004012F6 mov eax,dword ptr [ebp-4]
004012F9 add eax,dword ptr [ebp-8]
004012FC mov dword ptr [ebp-0Ch],eax
我们可以通过上面的代码直观地看到汇编语句和C语言之间的对应关系。第一句,m赋值为10,内存正是ebp向下的内存;第二句和第一句类似;第三句稍微有点复杂,我们可以分析一下。首先我们看到,CPU从堆栈中把m的数据找了出来,也就是[ebp-4]地址处的数据,接着CPU用同样的方法把n的数据也找了出来,直接加到寄存器eax上,最后一步比较简单,就是把eax的数据保存在[ebp-0c]处的地址上。只要是函数内部的临时变量,就会看到这样的形式。临时变量就是依靠ebp的偏移地址获取的。
大家有没有想过,如果p是全局变量呢?
45: int m = 10;
004012E8 mov dword ptr [ebp-4],0Ah
46: int n = 20;
004012EF mov dword ptr [ebp-8],14h
47: p = m + n;
004012F6 mov eax,dword ptr [ebp-4]
004012F9 add eax,dword ptr [ebp-8]
004012FC mov [p (0042b0b4)],eax
看到上面的代码,我们发现m、n的赋值方向没有发生什么样的变化。变化的是,最后寄存器eax的数值被赋值给了一个绝对地址0x42b0b4。这说明了一个问题,在程序加载到内存后,全局变量是有独立地址空间,并不会随着堆栈的浮动发生变化。
前面我们说过,在函数内部的所有变量都会保存在ebp到esp之间的堆栈空间上,那么代码是怎么做的呢?我们可以看这样一段汇编代码?
41: void process()
42: {
004012D0 push ebp
004012D1 mov ebp,esp
004012D3 sub esp,4Ch
004012D6 push ebx
004012D7 push esi
004012D8 push edi
004012D9 lea edi,[ebp-4Ch]
004012DC mov ecx,13h
004012E1 mov eax,0CCCCCCCCh
004012E6 rep stos dword ptr [edi]
43: int m = 10;
004012E8 mov dword ptr [ebp-4],0Ah
44: int n = 20;
004012EF mov dword ptr [ebp-8],14h
45: int p = m + n;
004012F6 mov eax,dword ptr [ebp-4]
004012F9 add eax,dword ptr [ebp-8]
004012FC mov dword ptr [ebp-0Ch],eax
46: }
我们把刚才一段函数的完整代码打印出来。我们发现,事实上在临时变量m运算之前,函数做了很多预备操作,其主要目的有两个:(1)为临时变量准备空间;(2)对函数运算中使用到的寄存器进行保存处理,这是因为寄存器是所有函数共有的资源,如果不记录好原来的数据,那么在函数返回后,寄存器就会忘记原来的数值,不能在原来的状态下继续正确地运算了。从地址0x4012D0到地址0x4012E6之间共有10句话,其实意思并不困难。第一句,ebp压栈;第二句esp复制给ebp;第三句esp自减4C大小,这个大小一般是按照函数内部定义了多少临时变量决定的;第四句,ebx压栈;第五句,esi压栈;第六句,edi压栈;第七句到第十句,把[ebp-4C]处向上的0x4C个字节全部设置成CC,edi为起始地址,ecx为循环次数0x13次,dword表示每次设置4个字节。
那么函数在返回前做了一些什么呢?
46: }
004012FF pop edi
00401300 pop esi
00401301 pop ebx
00401302 mov esp,ebp
00401304 pop ebp
00401305 ret
其实函数返回的时候,做的内容特别简单。第一句edi出栈;第二句esi出栈;第三句ebx出栈,和前面寄存器进栈的顺序是完全相反的。最后三句特别关键,我们看到ebp复制给esp,ebp出栈,函数返回,这样一切都恢复到函数调用之前的状态了。
那么函数调用的时候,入参是怎么处理的呢?
53: process(20);
0040EFA4 push 14h
0040EFA6 call @ILT+40(process) (0040102d)
0040EFAB add esp,4
上面一段代码就是process函数含有一个参数时候的情形。函数调用后esp+4,堆栈恢复。堆栈+4,主要是因为参数的空间就是4个字节。所以用一幅图说明一下,函数调用的时候堆栈空间应该是这样的:
| 函数参数 |
| 返回地址 |
| 临时变量 | <------------------------ ebp
| 压栈寄存器 |
| 栈顶 | <-------------------------esp
其他知识:
(1) 全局运算cpu寄存器很多,一般有eax,ebx,ecx,edx等等。我们通常说的ax,bx,cx,dx指的只是他们的低位部分。
(2)段寄存器寄存了程序的代码段,数据段和堆栈段。代码段保存了全部的程序代码,数据段保存了全据变量的代码,而堆栈则是全部堆栈空间。
(3)目前 vc编译器支持嵌入式汇编,大家有兴趣的话可以在函数内部试试身手。下面的代码只是一个范例:
void process(int* q)
{_asm {push eaxpush ebxpush ecxmov eax, 0x10mov ebx, 0x15add eax, ebxmov ecx, qmov [ecx], eaxpop ecxpop ebxpop eax}
}
(全部完)
【预告: 下面一篇博客主要介绍汇编语言和指针】
用汇编的眼光看C++ (之x86汇编)相关推荐
- 用汇编的眼光看C++(之退出流程)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 无论是在判断还是在循环的过程中,通常在遇到合适的条件的时候就会退出相应的模块.跳出模块运行的方 ...
- 用汇编的眼光看C++(之 总结篇)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 早在八月份的时候,就陆陆续续写了二十多篇用汇编语言看C++的博客内容.在此为了做一个概括,也为 ...
- 用汇编的眼光看C++(开篇)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 很多朋友,包括我自己在内,对C++语言的很多特性不是很明白.特别是几年前找工作的时候,为了应付 ...
- 从汇编的眼光看C++(之指针拷贝)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 指针是编程人员的梦魇,对C语言的开发者是如此,对C++的开发者也是如此.特别是在C++中,如果 ...
- 用汇编的眼光看C++(之判断流程)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 在我们平常的编程当中,用于判断的地方很多,但主要有下面三种方式:if-else:switch: ...
- 用汇编的眼光看C++(之拷贝、赋值函数)
拷贝构造函数和复制函数是类里面比较重要的两个函数.两者有什么区别呢?其实也很简单,我们可以举个例子,加入有这样一个类的定义: [cpp] view plaincopy class apple { pu ...
- 用汇编的眼光看C++(之算术符重载陷阱)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 在算术符重载里面,"="重载可能是最经常使用的一种.但是好多人就误以为在函 ...
- 从汇编的眼光看C++(之递归函数与模板类)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 递归,相信有过基本C语言经验的朋友都明白,就是函数自己调用自己.所以,本质上说,它和普通的函数 ...
- 用汇编的眼光看C++(之模板类)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 如果类是一种确定的数据类型,那么模板就是一种对类的抽象.假设有这么一种类,它需要进行数据的计算 ...
最新文章
- android gpuimage显示的缩放和剪裁模式
- javascript 面向对象编程(工厂模式、构造函数模式、原型模式)
- TextView使用实例
- 为PHP5.4开启Zend OPCode缓存
- C语言连接PostgreSQL数据库
- oracle双重for循环sql,Oracle 11g中for循环中的PL / SQL限制
- Semaphore源码解读
- 国际会议poster: 海报制作流程 格式介绍
- mysql的建表语句_三种常用的MySQL建表语句
- SoundPool循环播放
- 如何计算虚拟化vcpu_【科普】CPU和内存虚拟化
- 学习帮——懒人菜谱,电饭煲可以做的菜!
- nestjs+vue+ts打造一个酷炫的星空聊天室
- 思科实验-生成树协议STP
- Virtualbox加载虚拟机镜像
- 电脑屏幕亮度随背景颜色变化而变化
- Spring Cloud教程(十二)加密和解密
- AUTOSAR OS详细介绍以及配置说明
- uniapp给webview进行传参。
- 计算机在智能交通系统中应用,浅议计算机技术在现代智能交通系统中的应用