C底层机制演示教学_第1页
C底层机制演示教学_第2页
C底层机制演示教学_第3页
C底层机制演示教学_第4页
C底层机制演示教学_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Good is good, but better carries it.精益求精,善益求善。C底层机制-这是一篇介绍C语言中的函数调用是如何用实现的文章。写给那些对C语言各种行为的底层实现感兴趣人的入门级文章。如果你是C语言或者汇编、底层技术的老鸟或是对这个问题不感兴趣,那么这篇文章只会耽误您的时间,您大可不必阅读他。当然如果前辈们愿意为我指出不足,我将十分感谢您的指导,并对耽误您宝贵的时间致歉。好了,废话少说!要研究这个问题,让我们先打开VC+吧。最好是6.0的,:-P。(什么你没有VC+,倒!.赶快装一个!#$,要快!)首先,让我们在VC+里建立一个Win32ConsoleApplicat

2、ion项目,并建立主文件fun.c。并输入以下内容。intfun(inta,intb)a=0 x4455;b=0 x6677;returna+b;intmain()fun(0 x8899,0 x1100);return0;之后,最关键的是在项目设置里关闭优化功能。也就是把Project-Setting-C/C+-Optimizations选为Disabled。编译器的优化在分析底层实现时大多数情况不太受欢迎。按键盘上的F10键,进入单步调试模式(StepOver)。看到你的main函数左侧有个黄色的小箭头了吗?那个就是程序即将执行的语句。按Alt+8。打开反编译窗口,看到汇编语句了吗?是不是想

3、这个样子=00401078push1100h0040107Dpush8899h00401082callILT+5(fun)(0040100a)00401087addesp,8看到两个PUSH指令了吗?再看看后面的数字,不正是我们要传递的参数吗。奇怪阿?我们明明是先传递的0 x8899怎么反倒先push1100h呢?呵呵,这个现象就叫Callingconversion。究竟是何方神圣,我在后面会详细的给你解释的。先别着急。随后的Call指令的作用就是开始调用函数了。接下来关掉反汇编窗口,在源代码窗口按F11(StepInto)进入函数体。当看到那个黄色的小箭头指向函数名的时候再调出反汇编窗口(A

4、lt+8)。你会看到类似下面的代码:1:intfun(inta,intb)00401000pushebp00401001movebp,esp00401003subesp,40h00401006pushebx00401007pushesi00401008pushedi00401009leaedi,ebp-40h0040100Cmovecx,10h00401011moveax,0CCCCCCCCh00401016repstosdwordptredi2:a=0 x4455;00401018movdwordptrebp+8,4455h3:b=0 x6677;0040101Fmovdwordptrebp

5、+0Ch,6677h4:returna+b;00401026moveax,dwordptrebp+800401029addeax,dwordptrebp+0Ch5:0040102Cpopedi0040102Dpopesi0040102Epopebx0040102Fmovesp,ebp00401031popebp00401032retVC+就是好,还在难懂的汇编语句前加入了C语言的源代码。不过同时也有不少我们不需要的代码。因此,你只需要关心红色的部分就可以了。奇怪阿?不是参数都用push传递了吗?怎么没看到被pop出来?问题其实是这样,当你调用Call进入函数的时候Call背着你做了一件事。ca

6、ll把它下一条语句的地址push进了堆栈。(旁人:什么!这是为什么?)原因很简单,因为函数调用完了,要用ret返回。而ret怎么知道返回哪里呢?对了,ret指令pop了call指令push给他的地址(搞清楚这个关系哦),然后返回到了这个地址。call和ret配合的如此绝妙,一个PUSH一个POP肯定不会让堆栈不平衡的(老外叫nostackunwinding)。现在明白了,如果你来个popeax,那eax里面是什么?当然是ret要用的返回地址了。好啦,你要是popeax就等于抢了ret要用的东西了。不论曾程序流程和道德标准上你做的都不对:-P。可是怎么在函数体里使用参数呢?问题其实并不难,既然参

7、数在堆栈里我们就可以使用esp(堆栈指针)来访问了。不过,我相信你也想到了。esp是个经常变化的值。一旦,函数里出现pop或push他就会变化。这样很不容易定位参数的于内存中的位置。因此,我们需要一个不会变化的东西作为访问参数的基准。看看函数体的开头部分:00401000pushebp00401001movebp,esp先用pushebp保存了原来ebp的值再把esp的值给ebp。原来ebp就是用来做基准的。也难怪他被称为ebp(BasePointer)。很自然ret返回前的popebp就是恢复原来ebp的数值喽。当然一定要恢复,因为函数里也可以调用函数嘛。每个函数都用ebp,自然要保证使用完

8、后完璧归赵了。现在当函数执行到movebp,esp后堆栈应该变成这个样子了。/-HigherAddress|参数2:0 x1100h|+-+|参数1:0 x8899h|+-+|函数返回地址|0 x00401087|+-+|ebp|-/LowerAddress=stackpointer&ebpallpointtohere,now由于我们在VC+上使用的int类型是一个32位类型,ebp和函数返回值也是32位的。因此每个量要占去4个字节。另外还需要注意堆栈的扩展方向是高地址到低地址。有了这些指示。我们就可以分析出,第一个参数的地址是ebp+08h,第二个参数就是ebp+0ch。看看反汇编的代码:2

9、:a=0 x4455;00401018movdwordptrebp+8,4455h3:b=0 x6677;0040101Fmovdwordptrebp+0Ch,6677h与我们的计算吻合。之后呢:00401031popebp00401032ret将ebp原来的数值完璧归赵,调用ret指令,ret指令pop出返回地址,之后返回到调用函数的call指令的下一条语句。ret之后,堆栈应该变成这个样子了/-HigherAddress|参数2:0 x1100h|+-+|参数1:0 x8899h|-/LowerAddress=stackpointer哈哈,问题出现了,再函数返回后堆栈出现了不平衡的情况(S

10、tackUnwinding)。怎么办呢?好办啊,直接popcxpopcx把堆栈平衡过来就好了。幸好我们只有两个参数,要是有20个的话,那就要有20个popcx。不说影响美观,程序效率也会很低。所以VC+使用了这个办法解决问题:00401082callILT+5(fun)(0040100a)00401087addesp,8看红色的语句,直接将esp的值加8,让堆栈变成/-HigherAddress=stackpointer|参数2:0 x1100h|+-+|参数1:0 x8899h|-/LowerAddress通过改变esp从根本上解决了Stackunwinding。(push,pop指令本质上

11、不就是通过改变esp来实现堆栈平衡的吗)现在,明白了函数如何传递参数,如何调用,如何返回。下一个问题就是看看函数如何传递返回值了。相信你早就注意到了4:returna+b;00401026moveax,dwordptrebp+800401029addeax,dwordptrebp+0Ch可见,函数正式用eax寄存器来保存返回值的。如果你想使用函数的返回值,那么一定要在函数一返回就把eax寄存器的值读出来。至于为什么不用ebx,ecx.,这个虽然没有规定,但是习惯上大家都是用eax的。而且windows程序中也明确指出了,函数的返回值必须放入eax内。OK,现在来解决什么是callingconv

12、ersion这个历史遗留问题。如果认真思考过,你一定想函数的参数为什么偏用堆栈转递呢,寄存器不也可以传递吗?而且很快阿。参数的传递顺序不一定要是由后到前的,从前到后传递也不会出现任何问题啊?再有为什么一定要等到函数返回了再处理堆栈平衡的问题呢,能否在函数返回前就让堆栈平衡呢?所有上述提议都是绝对可行的,而他们之间不同的组合就造就了函数不同的调用方法。也就是你常看到或听到的stdcall,pascal,fastcall,WINAPI,cdecl等等。这些不同的处理函数调用方式就叫做callingconvention。默认情况下C语言使用的是cdecl方式,也就是上面提到的。参数由右到左进栈,调用

13、函数者处理堆栈平衡。如果你在我们刚才的程序中fun函数前加入_stdcall,再来用上面的方法分析一下。8:fun(0 x8899,0 x1100);00401058push1100h;=参数仍然是由右到左传递的0040105Dpush8899h00401062callfun(00401000);=这里没有了addesp,08h1:int_stdcallfun(inta,intb)00401000pushebp00401001movebp,esp00401003subesp,40h00401006pushebx00401007pushesi00401008pushedi00401009leae

14、di,ebp-40h0040100Cmovecx,10h00401011moveax,0CCCCCCCCh00401016repstosdwordptredi2:a=0 x4455;00401018movdwordptrebp+8,4455h3:b=0 x6677;0040101Fmovdwordptrebp+0Ch,6677h4:returna+b;00401026moveax,dwordptrebp+800401029addeax,dwordptrebp+0Ch5:0040102Cpopedi0040102Dpopesi0040102Epopebx0040102Fmovesp,ebp00401031popebp00401032ret8

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论