写的代码的源程序都是以字节序列存储在文件中的,比如#include<iostream> #是35程序第一个整数值为35 每行结尾是以\n换行符结束代表ascll码10;.cpp .c的文件实际由ascll码构成的文本文件。
代码如何转化为可执行程序(编译转化成机器识别的二进制文件)运行?
gcc -o hello hello.cpp
预处理:将头文件内容替换展开,宏替换注释去掉。
gcc -E server.c -o server.i
.i文件内容如下图:
编译阶段:编译器将文本文件hello.i转化为hello.s,汇编语言(低级机器语言指令)
指令:gcc -S server.i -o server.s
汇编阶段:将低级机器语言转换为机器语言
指令:gcc -c server.s -o server.o
链接器:(将库函数的代码链接到程序中比printf如pintf.o链接到server.o中)变成了可执行程序
指令:gcc serer.o -o server
ld -o prog
链接:将各种代码和数据片段收集组合成一个单一文件的过程。
链接可用于编译(源代码翻译成机器代码),也可用于加载(程序被加载器加载到内存并执行)。
ld命令(链接)需要指定所需的动态库,缺少一个都会连接出错。
正常编译程序一步gcc即可生成可执行程序,相当于里边把这些过程执行一遍。一步步来目的是为了详细描述过程。shell中有一个加载器的函数,将可执行文件prog的代码和数据复制到内存中,将控制转移到程序的开头。
1.2系统硬件的组成
(1)总线(贯穿整个系统的电子管道),大多数机器字长要么是4字节,要么是8字节。
(2)I/O设备(系统与外部世界联系的通道),用户输入鼠标键盘,输出显示器,长期存储数据的磁盘(可执行程序存在磁盘中)
I/O设备通过控制器或适配器以I/O总线相连,控制器是I/O设备本身或者系统主印制电路板上的芯片组(主板),适配器是插在主板卡槽上的卡。控制器适配器是为两者传递信息。
(3)主存(动态随机存储器DRAM芯片组成)
是临时存储设备,在处理器执行程序,来存放数据,逻辑上线行字节数组。也是常说的内存。
(4)处理器CPU
是解释存储在主存指令的引擎,cpu的核心是程序计数器pc,任何时刻pc指向主存某条机器语言指令(含有该条指令地址)
系统从通电的开始至结束一直都在执行着,pc指向的指令再更新程序技术器。
输入一个可执行程序的流程:将用户输入的hello放到内存中
按下回车后:shell执行指令将可执行文件从磁盘复制到主存中。
内存复制完程序之后,cpu开始执行main程序的机器指令,将数据复制到寄存器上,在显示到最终屏幕上。
目标文件的三种形式:
可重定位目标文件。(目标代码和二进制文件)在编译时与其他可重定位文件结合起来。
可执行文件。包含二进制文件和代码。可直接复制到内存中并执行
共享目标文件。特殊类型的可重定位目标文件。(在加载时,或运行动态的加载进内存)
链接分类:静态,动态
静态链接:由连接器在链接时将库内容加入到可执行程序中
优点:运行时对环境的依赖较小,兼容性较好。
缺点:生成的程序较大,需要更多的系统资源,装入内存消耗更多的时间。
库函数更新,重新编译应用程序。
静态库:目标代码的集合,可执行程序运行前就加入代码中,成为可执行程序的一部分。
一般格式libXXX.a
制作静态库:(1)将源文件生成.o文件(二进制文件)g++ -c
(2)建立索引用打包工具
ar - rsc lib.shuai.a test.o 可以加多个.o文件
(3)静态库制作完成之后,需要将.a文件和头文件一起发布给用户
使用方法:g++ main.cpp -L ./ -I ./ -l test -o main
-L动态库所在目录 -I 头文件所在录 -l(小L) 动态库名(前去掉lib,后去掉.a)
静态库缺点:容易造成空间浪费。(如果有多个比如500程序用到这个静态库,在内存中有多个浪费空间,造成内存不足)
动态库很好解决这个问题
(1)生成目标文件:加编译选项-fPIC(命令是为了在多个程序中共享)
g++ -fPIC -c test.cpp 会自动生成test.o文件
(2) 生成共享库,此时要加链接器选项: -shared
g++ -shared test.o -o libtest.so
(3)nm 配合grep 查看相应函数
(4)编译cpp文件链接动态库 g++ main.cpp -L -I -I 和静态库使用方法一样 )
(5)执行出错
ldd查看依赖动态库信息(需要添加动态库的绝对路径)
系统加载可执行代码,执行通过动态载入器(dynamic linker)完成,elf格式程序通过ld-linux.so*来完成的
解决办法:
(1)将动态库的绝对路径加入到 /etc/ld.so.conf 配置文件中
(2)执行sudo ldconfig -v
(在运行可执行程序就解决了)
可定位目标文件ELF
ELF头16字节序列开始
(1)系统字的大小和字节顺序
(2)帮助链接器进行语法分析解释目标文件信息(ELF头大小,目标文件类型等)。
.text区域已编译程序机器代码。
.rodata 只读数据,printf的格式串,开关语句跳转表
.data 已初始化的全局变量区和静态C变量区。
.bss 未初始化全局变量和静态C变量区,以及所有未初始化为0的全局或静态变量区域。(在节中不占用实际空间,只是一个占位符)
.symtab 存放程序被定义和引用的函数和全局变量信息。
链接器上下文有三种不同符号:
由模块定义并能被其他模块引用的全局符号。(非静态c函数,和全局变量)
由其他模块定义并被模块m引用的全局符号。外部符号(非静态c函数,和全局变量)
只被模块m定义引用的局部符号。对应带static属性的c函数,和全局变量。不能被其他模块引用