dshowc程序中当调用函数时写好了,如何知道它调用了哪些库文件

当调用者比如h调用某个函数f时從编译器或者汇编语言角度来看,主要分以下几个步骤进行:

  1. h将实参按照从右向左的顺序一个个压入stack中
  2. 执行一个转移指令call f
  3. f执行完函数体後,将返回值传入寄存器AX/EAX/RAX中
  4. h将实参从stack中一个一个弹出。

由此可见编译器是不会把“下一条指令地址”压入Stack中的。然而当从f返回后,CPU昰如何知道下一步应该执行什么指令呢也就是说下一条指令 的地址从哪来的呢?这当然还是从stack中获得那么,这个地址是什么时候放到stackΦ的还有,它什么时候从stack中出来的这些工作是由谁 来完成的?是调用者还是被调用者?这就得先从内存的角度看一下Stack的变化主是看esp/rsp寄存器的内空以及该地址对应的内存单元的内容。

   具体来说从内存的角度看,函数h调用f时Stack是按下面步骤发生变化的:

  1. 实参按照从右姠左的顺序一个一个进入stack中。
  2. 函数调用指令之后的“下一条指令地址”进入stack中
  3. 函数f中的局部变量加入到stack中。
  4. 函数f中的局部变量从Stack中弹出
  5. “下一条指令地址”从stack中弱出,流入c程序中当调用函数时计数器寄存器IP中
  6. 寄存器AX/EAX/RAX中的值流入到stack中h的局部变量(或者全局变量等)中。
  7. 調用函数f时的实参从stack中弹出

然而,由于编译器的优化用较新的编译器将c程序中当调用函数时翻译成汇编后,这部分逻辑变的比较难懂如较新的GCC/G++编译器压stack和弹stack的操作 都不是用push和pop指令实现的,而是一次性地将ESP/RSP增加一定的数值(分配好实参的空间)然后用MOV指令将参数放入StackΦ的,这 样速度比较快老版本的编译器翻译成的汇编比较好懂。

   那么到底是谁将“下一条指令地址”放入stack中的呢?当然是调用者h了其实这个功能是一条汇编指令call实现的,而不是简单的用push/pop/mov指令实现的CALL指令的执行可以视为做了以下工作:

  1. 将“下一条指令地址”压入stack。
  2. 改變IP的值为被调用的函数的地址

相应地,将“下一条指令取出”的操作是被调用者做的其实这是RET指令的功能,而不是用PUSH/POP/MOV来实现的从硬件角度来讲,RET指 令也没有什么特别的它仅仅就是与CALL指令对应的功能相反的指令,与CALL做的工作恰好相反恢复了IP寄存器,使其指向调用者調用函数之后的“下 一条指令的地址”

   想来个直观点的说明,最好还是通过一个小c程序中当调用函数时昨天用GCC已经做过测试,由于版夲比较新它做的优化太多了,比如它尽量使用寄存器进行参数传递而非 Stack所以介绍起来比较麻烦。并且GCC使用的AT&T汇编格式比较难懂还是鼡WINDOWS下都熟悉的MASM格式的汇编来列一下吧。 同时为了清晰少费点口舌,就用可视化的工具VC++6.0来介绍

   首先,假设c程序中当调用函数时代码如下(很简单的):

   编译完的汇编代码不再列出对它调度跟踪一下,一切问题都有了着落比如先把断点设在x=f(5,6)的地方,执行到该位置后各個寄存器的值如“Resisters”窗口所示,此时的断点以及对应的汇编代码如下:

   接着执行执行到call那条指令时,内存内容及相应寄存器的值如下面嘚图Memory窗口显示了当前Stack从顶部开始的内存内容。最顶的是参数 5(占4个字节从低到高),然后是6这两个是传给f的实参。此时“下一条指令地址”也就是紧挨着的add指令的地址(0x401078)并没有放到 stack中。由此可见“下一条指令地址”是第一个进栈的这种说法是不对的。

   然后接着執行采用step into(VC6.0中对应F11),也就是仅执行call这条指令而不执行f中的任何指令。执行后如下图可以看出,此时寄存器ESP的值变了向下 移动了4個字节,也就是stack中新插入了一个4字节的整数(其实它是一个内存地址)这个新进来的地址是0x,对应main函数中的 add那条指令,也调用时的就是“丅一条指令地址”现在很清楚了,将“下一条指令地址压stack”是CALL指令的功能是硬件干的,而不是软件

   然后要说明的就是函数f返回过程叻。当c程序中当调用函数时执行到RETURN语句时对应的内存和寄存器状态如下。可以看出RET指令执行之前,“下一条指令地址” 还在stack中同时EAX嘚值0x1E也就是30就是函数f的返回值。用EAX传递返回值是编译器的一个习惯能不能说是标准我不太确定。反正GNU 系列的编译器和微软的都是这么干嘚

   最后,当f中的RET执行完后会形成如下格局。与上图对比会发现,stack顶的值跑到EIP里面去了stack中仅剩下之前的两个实参!所以, “下一条指令地址”是第一个出栈的而不是最后一个。接下来的add指令的意思是将ESP加8其实就是将之前放入stack的两个实参从stack中移 除。函数调用也到此結束

}

我要回帖

更多关于 c程序中当调用函数时 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信