基础知识:

指令规则:movl $0 %eax,把常数0加载到eax寄存中。这里的后缀字母l表示long:4字节,此外还有w表示word:2字节(为了服从传统而已,其实现代意义的word就是4字节了),b表示byte:1字节。寄存器前面都得加个%,我猜这个是从编译器的指令模板来的,编译的指令模板就是movl %0 %1这样,其中%0表示占位符,也就是待替入的寄存器。

x86的栈是高地址--->低地址增长的形式。

函数中的局部变量都是在栈中动态分配的,定位的方法就是距离当前栈底ebp的偏移。(尤其是调试器需要这样的信息,比如调试时要看某个局部变量的值调试器gdb等就是这样读取的)

x86寄存器名字不好记,没有arm的叫法容易区分(arm是R0-R15,多好听呀)。一般使用的有8个寄存器:eax、ebx、 ecx、 edx、 esp、 ebp、esi 和 edi(具体还有哪些寄存器,自己可以去搜一下,总共好像只有15个)

  • eip寄存器:就说我们常说的pc寄存器,存的是下一条待执行指令地址
  • ebp寄存器:每个栈帧的栈底地址,从下面的调用规则可知,栈底指进入新函数后保存了ebp、ebx、esi 、edi四大寄存器后的栈的位置
  • esp寄存器:总是存放栈的栈顶地址
  • eax寄存器:常用作函数返回值

发生了函数调用:

  • 调用者,需要先把实参压入栈,再把返回地址压入栈(call f指令,会自己做了)
  • 被调函数要先保存一下ebp(因为进入新函数了,需要有新的栈底了呀,所以外层函数的栈底得先保存起来,即pushl %ebp),和 ebx,esi 和 edi 的值(即pushl %ebx; pushl %esi; pushl %edi,这是为了简单嘛,不管是否都用到了),这三个压栈。
    函数正在执行.............。
    执行即将结束,函数即将返回,把返回值放入eax寄存器(即movl $0 %eax),且把栈底的位置设为当前栈顶(即movl %ebp %esp,此时这两个寄存器的值是相等的,也就是一个函数执行完后,栈顶指针总能回到进入时的位置),然后需弹栈(即pop %edi; pop %esi; pop %ebx)。此外外层函数的栈底也有值也要恢复嘛,即pop %ebp。然后是ret指令了,该指令会把外层函数压入的返回地址弹出,且载入给eip寄存器,实现函数条转回去。
  • 调用者,弹出之前压入的实参,通过addl $n %esp实现的,其中的n就是压入了多少字节实参,就是几个,这个调用者自己能知道。

接下来看个例子:

x86汇编知识以及应用详解_x86函数调用过程分析

x86汇编知识以及应用详解_单片机_02

x86汇编知识以及应用详解_x86函数调用过程分析_03

空了4个字节的原因就是:

        SK_Temp/SK_Variable are in fsym->locals. In fact, some SK_Temp are allocated to register,

        but UCC always keep their stack position when the  function is active. Of course , this cause a waste of stack memory, but it made the management of temporaries  simple. 也就是说这些变量可能是被分配到寄存器里去了(优化的过程),但是为了好管理,还是在栈里给它们空了出来。

接下来,再来分析一个带浮点数的例子,很好理解,就不细说了 ,如下:

x86汇编知识以及应用详解_加载_04

x86汇编知识以及应用详解_寄存器_05

由于常数会以“立即数”的形式存在于代码区中,当程序运行时,

CPU 会从代码区里预读机器指令,从而把立即数也加载入 CPU,因此当操作数 SRC2 是常

数时,我们可以不必把 SRC1 的值加载到寄存器中,这不会违反“同一条 X86 汇编指令

两个操作数不可以都在内存中”的寻址要求。这意味着我们可以生成形如“cmpl $3, a”的

比较指令,但不可以生成形如“cmpl b, a”的比较指令

 可以参考三年前我写的文章,那时候理解得不是很明白,直到自己深入了处理器运行架构和现在学了编译器源码。​​C语言函数调用时候内存中栈的动态变化详细分析_标biao的博客-CSDN博客_函数调用内存变化​​