笔记|组成原理:原理篇(函数调用与静态链接)
2021-07-01 09:01:40 0 举报
徐文浩大佬的组成原理课
作者其他创作
大纲/内容
如何利用函数内联进行性能优化
call指令后面跟着的,仍为跳转后的程序地址
保存程序的代码&指令
1.扫描所有输入的目标文件2.收集所有符号表里的信息,构成全局符号表3.根据重定位表,把所有不确定要跳转的地址的代码,根据符号表里面存储的地址进行一次修正,4.把所有的目标文件的对应段进行一次合并,变成最终的可执行代码
data Section 数据段
保留当前文件里,哪些跳转地址其实是我们不知道的
符号表:函数名称;自定义的全局可以访问的变量名称...
可能出现问题:(死循环)函数A:“call function B”函数B:“call function A”
把程序变成可执行文件,要装载程序就变得容易很多装载器不用再考虑地址跳转的问题,只需要解析ELF文件,把对应的指令合数据,加载到内存里面共CPU执行就OK
如何构造一个stack overflow
与if…else和for/while循环不同点:前者跳转后不再回来,在跳转后的新地方开始顺序执行指令函数调用的跳转:在对应函数的指令执行完后,回到函数调用的地方,继续执行call之后的指令
text Section /代码段/指令段CodeSection
1.函数开始:执行了一条push指令、一条move指令;【压栈】2.函数结束:执行了一条pop指令、一条ret指令【出栈】
可执行dump出来的内容
内联的代价:如果一个函数在很多地方都被调用了,那么就会展开很多次,整个程序占用的空间就会变大。
rel.text Section重定位表
symtab Section符号表
装载器Loader把可执行文件装载Load到内存中;CPU从内存中读取指令和数据,开始真正执行程序
在GCC编译时,加上对应的让编译器自动优化的参数 -O,编译器就会在可行的情况下,进行指令替换
链接器
维护好rbp和rsp,就能管理好不同函数间的跳转。
编译、链接和装载:拆解程序执行
$ gcc -g -c function_example.c$ objdump -d -M intel -S function_example.o
+File Header:表示这个文件的基本属性(是否是可执行文件?对应的CPU?OS?...)
可能方案2:专门设立一个“程序调用寄存器”,存储接下来要跳转回来执行的指令地址。等到函数调用结束,再从这个寄存器里取出地址,再跳转到这个记录的地址,继续执行
08ELF和静态链接
ELF文件格式把各种信息分成一个一个Section 保存起来
与if…else和for/while循环相似点:[顺序执行的指令过程中]执行一个内存地址的跳转指令,让指令从原来顺序执行的过程里跳开,从新的跳转后的位置开始执行
ELF格式和链接:理解链接的过程
不仅存放了编译成的汇编指令、保留了很多数据
保存程序里面设置好的初始化信息
为什么我们需要程序栈
≈地址簿(关联名字&地址)
可能出现问题:(很多需要调用的子任务/函数)函数A:“call function B;C;D;...”CPU:“我寄存器没那么多啊”
让函数自己调用自己,在不断的压栈过程中,将整个栈空间填满,最后遇上stack overflow
都不是可执行文件,而是目标文件。只有通过链接器Linker把多个目标文件以及调用的各种函数库链接起来,我们才得到一个可执行文件
方案2的改进:让编译器自动优化(把实际调用函数产生的指令,直接插入到相应的位置,来替换对应的函数调用指令)
当前文件里面定义的函数名称和对应地址的地址簿
可执行文件和目标文件使用的文件格式:ELF(Execuatable and Linkable File Format)
C语言代码→汇编代码→机器码
可能方案3:在内存中单独开辟一段空间,用栈这个后进先出的数据结构(函数调用前:进栈)(函数执行结束:出栈)
原理篇:指令和运算
tips1:1.每次函数调用前,我们都把调用后返回的地址写在一个球上,然后塞进球桶(进栈)2.如果函数执行完了,我们从球桶中取出最上面那个球(出栈)3.在真实的程序中,压栈的不只有函数调用完成后返回的地址,还有一些函数调用过程用需要传递的参数。>>整个函数占用的所有的内存空间为该函数的栈帧(Stack Frame)4.实际的栈布局,顶部和底部反过来(因为栈底的内存地址从一开始就固定),一层层呀战后,栈顶的内存地址是在逐渐变小而不是逐渐变大
如何可以不跳回原来开始的地方来实现函数的调用?
$ gcc -g -c add_lib.c link_example.c$ objdump -d -M intel -S add_lib.o$ objdump -d -M intel -S link_example.o
07函数调用(CPU 指令层面)
可能方案1:把调用的函数指令,直接插入在调用函数的地方
编译、汇编、链接:生成可执行文件
0 条评论
下一页