lesson06 函数调用过程

汇编和可执行文件


程序是从源代码通过编译器转化为机器码(汇编,机器只能识别汇编)

java 不是直接编译成汇编,是在执行的时候一步一步的编译成其他语言。

   从源代码最终到达的是一个可执行文件,可执行文件和汇编是不同的,当代码进行编译后会按照代码的规则生成与之一一对应的汇编代码,但是并不是可执行的,还需要链接(link).

link 在写代码的时候需要使用各种API,大部分API并不是我们自己写与之对应的汇编代码。

   printf("I love mark"); 执行这句话要于显卡交互( 电脑显示器看到的东西都是从通过cpu命令显卡在内存画幅图,展示在显示器上)每一个操作系统有不同的地方,要使用操作系统,就必须与其进行link.

    在任何平台上编译首先会产生一个obj文件(程序产生的汇编代码,没有什么含义,不能被执行,不符合操作系统的要求。)经过 link 生成与平台对应的一些文件的格式。

link 相当于加工的过程,windows平台上是加上PE结构。

 源代码到编译 成OBJ文件,根据不同的平台link 变成了可执行的文件。


编译 :

   把 c/c++代码 翻译成与之对应的 汇编代码  里面的是一一对应的。 如果程序员写了一段废代码,编译器会进行优化,并不会产生汇编代码。在debug下生成的代码和在release时生成的代码是不一样的,因为编译器会进行优化。


编译器就是把复杂逻辑分解成简单逻辑

代码到汇编当中可能就只有 赋值语句 跳转语句  计算语句


汇编代码

  

最简单的c语言代码

int  main()

{

return 0;

}


debug反汇编

int  main()

00F11650  push        ebp      寄存器的名称 栈寄存器

00F11651  mov         ebp,esp  

00F11653  sub         esp,0C0h  

00F11659  push        ebx  

00F1165A  push        esi  

00F1165B  push        edi  

00F1165C  lea         edi,[ebp-0C0h]  

00F11662  mov         ecx,30h  

00F11667  mov         eax,0CCCCCCCCh  

00F1166C  rep stos    dword ptr es:[edi]  

return 0;

00F1166E  xor         eax,eax  

}


release反汇编

return 0;

00D01000  xor         eax,eax  


调试的时候尽量用debug,会对每句代码生成对应的汇编

release会对代码进行优化 发布版 快速 精悍


内存划分 

  栈 堆 代码区  常量区

 

    更加安全  cpu 内存  都是0101的数据,一门能操作内存的语言,c,c++ ,指针指向代码区并修改了,程序就会崩溃。分区后便于把代码放在一个地方进行保护 代码区(只读不写),常量区也是只读的。


栈区  (1024kb

栈溢出: 数组超过了1024kb的大小,放在堆上解决。

栈内存储临时变量,临时变量占用一定的内存,每次都要释放。如果每次都分配一块空间, 记录下来,释放的时候再去擦除,会浪费很多性能。因此直接分配了一块内存,就是栈,不进行释放 在栈底部定义一个游标,(ebp寄存器记录),每一次使用临时变量的时候占用一块的内存空间,再用另一个游标(esp)来指示使用的最高点。当使用完成后,就将最高点的游标和最底层的游标想重合(就相当于临时变量被释放了)。所有栈没有被清空,只是反复的使用了。再存入数据时,esp游标上移,将一块空间的值改成要存入的数据。


栈中通过push操作,把先插入的数据放在底部,后插入的放顶部。

栈中不仅存储临时变量,还有跳转后返回地址。


调用函数

例如 

printf("I love mark");

printf("I love mark");

程序按顺序执行,最开始通过程序计数器,当遇到call_printf 会跳到另一块去执行printf,执行完后跳回去,这时需要使用栈。 

 在栈存储了一些东西,这时碰到了call,会把当前的地址放到栈里,esp往上走,再ebp移到同一地方,相当于栈底发生了改变,在改变之前得记录原栈顶,之后会还原。   call执行完后,esp移回来。


 push

在esp上添加数据,再把esp上移

pop

根据esp删除删除,esp下移



实例 :栈如何运行

int  main()

{

(断点)printf("I love mark");

printf("I love mark");

return 0;

}



转到反汇编

printf("I Love Mark!!!");

009117AE  push        offset string "I Love Mark!!!" (0916B30h)  

009117B3  call        _printf (0911316h)  

009117B8  add         esp,4  

printf("I Love Mark too!!!");

009117BB  push        offset string "I Love Mark too!!!" (0916B44h)  

009117C0  call        _printf (0911316h)  

009117C5  add         esp,4  

return 0;



寄存器


通用寄存器

EAX = CCCCCCCC 

EBX = 7FB7E000

ECX = 00000000 

EDX = 00919578

ESI = 00911046 

EDI = 00C1FC28



EIP = 009117AE

ESP = 00C1FB5C  栈顶

EBP = 00C1FC28 栈底

EFL = 00000202 



(F11)执行下一句时:


  寄存器

  EIP = 009117B3  指向了下一条语句,说明执行到了这里(009117B3  call        _printf (0911316h)  )

  ESP = 00C1FB58   往上面走了一位  (0x00C1FB58  30 6b 91 00 反着放的   offset string "I Love Mark!!!"                                                                        (0C36B30h) 地址  )

  内存

0x00C1FB58  30 6b 91 00  

0x00C1FB5C  46 10 91 00  

0x00C1FB60  46 10 91 00  

0x00C1FB64  00 e0 b7 7f  .





 继续往下(F11)指向 esp 继续发生改变

EIP = 00911316 (00911316  jmp         printf (0911800h))

ESP = 00C1FB54   发生了改变,说明在之前往后执行了一位,又往栈压入了东西



内存内容

0x00C1FB54  b8 17 91 00  ?.?.

0x00C1FB58  30 6b 91 00  0k?.

0x00C1FB5C  46 10 91 00  F.?.

0x00C1FB60  46 10 91 00  F.?.

0x00C1FB64  00 e0 b7 7f



继续执行(F11)


保存ebp,为了还原

00911800  push      ebp       EIP = 00911801 ESP = 00C1FB50 

内存内容

0x00C1FB50  28 fc c1 00          ebp 放进了内存  EBP = 00C1FC28

0x00C1FB54  b8 17 91 00  .

0x00C1FB58  30 6b 91 00  

0x00C1FB5C  46 10 91 00  

0x00C1FB60  46 10 91 00  

0x00C1FB64  00 e0 b7 7f



00911801  mov         ebp,esp       ESP = 00C1FB50 EBP =00C1FB50

内存内容

0x00C1FB50  28 fc c1 00  (??.    

0x00C1FB54  b8 17 91 00  ?.?.

0x00C1FB58  30 6b 91 00  0k?.

0x00C1FB5C  46 10 91 00  F.?.

0x00C1FB60  46 10 91 00  F.?.

0x00C1FB64  00 e0 b7 7f

00911803  sub         esp,0D8h 


继续执行


0091185D  pop         edi     开始pop edi发生改变

0091185E  pop         esi         esi    发生改变 

0091185F  pop         ebx  ebx 发生改变

00911860  add         esp,0D8h    加上后,栈顶会往下掉 和改变后的栈底持平   

                                                             ESP = 00C1FB50 EBP =00C1FB50

0x00C1FB50  28 fc c1 00          原来存储的ebp的值

0x00C1FB54  b8 17 91 00   函数的返回地址

0x00C1FB58  30 6b 91 00   .  传进来的参数

0x00C1FB5C  46 10 91 00   .

0x00C1FB60  46 10 91 00  .

0x00C1FB64  00 e0 b7 7f 


00911866  cmp         ebp,esp  

0x00C1FB4C00911868  call        __RTC_CheckEsp (091110Eh)  

0091186D  mov         esp,ebp    ebp的值给esp 还原到了没有push ebp的状态

0091186F  pop         ebp 还原到了call的状态

00911870  ret  


009117B3  call        _printf (0911316h)  

009117B8  add         esp,4      esp再下移一位  ,此时全部还原   ESP = 00C1FB5C EBP = 00C1FC28


0x00C1FB4C  6d 18 91 00   

0x00C1FB50  28 fc c1 00  

0x00C1FB54  b8 17 91 00  . 

0x00C1FB58  30 6b 91 00    上面的值没清空 下次使用时直接修改

0x00C1FB5C  46 10 91 00     esp

0x00C1FB60  46 10 91 00  

0x00C1FB64  00 e0 b7 7f 


栈内函数调用的过程:

   栈顶 esp 开辟空间 后 ,向里面存储数据 。同时保存原来的栈底ebp(pop)。之后ebp上移,和esp相等。继续执行,esp发生改变,执行完后,调到已经改变的ebp。

根据之前保存的值,将改变的ebp还原(pop),再使esp下移到ebp。 还原后esp上的值依然存在,没有进行释放。下次使用时直接修改即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值