源程序.cpp 预处理得到
预处理文件.i 编译得到
汇编文件.S 汇编得到
目标文件.o 链接得到
可执行文件
例子:main.cpp fun.cpp fun.h
1 #include <iostream> 2 #include "fun.h" 3 using namespace std; 4 5 #define PI 3.14 6 7 int main() 8 { 9 print(); 10 cout<<PI<<endl; 11 return 0; 12 }
1 #ifndef _FUN_H_ 2 #define _FUN_H_ 3 void print(); 4 #endif
1 #include <iostream> 2 #include "fun.h" 3 4 void print() 5 { 6 std::cout<<"hello,world"<<std::endl; 7 }
1. 预处理
g++ -E main.cpp -o main.i
main.i、fun.i:
对源程序其中的伪指令(以#开头的指令)和特殊符号进行处理
(1)宏定义指令
如 main.cpp中有 #define PI 3.14,预处理之后进行了替换
(2)条件编译指令
#ifdef、#ifndef、#else、#elif、#endif等,根据宏定义决定对哪些代码进行处理,避免重复的引用
(3)头文件包含指令
#include <xx.h> #include "xx.h"等
这些头文件中有大量的宏定义
(4)特殊符号
1 printf("Date:%s,Time:%s,File:%s,Line:%d,Func:%s\n",__DATE__,__TIME__,__FILE__,__LINE__,__FUNCTION__);
经过预处理,得到的.i文件没有宏定义、没有条件编译指令、没有特殊符号
2. 编译
g++ -S main.i -o main.S
预处理之后的文件只有一些数字、字符串及关键字的定义,经过g++编译程序:词法分析、语法分析、优化,生成汇编文件
3. 汇编
汇编代码汇编成机器指令
4. 链接
多个.o文件以及库文件链接成可执行文件
ld 一堆库文件 fun.o main.o -o a.out
必要的库可通过 g++ -v main.o 查看
g++ 最终通过调用 collect2来链接文件,collect2是对ld的封装
(1)静态链接
以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件。
将链接库的代码复制到可执行程序中
静态链接做的事:
①符号解析:将目标文件符号引用和定义联系起来(因为某些符号是引用其他模块的符号)
②重定位:编译器、汇编器生成从地址0开始的代码和数据,链接器把每个符号定义和一个存储器位置联系起来,然后修改所有对这些符号的引用,使得从另一个位置开始执行。
(2)动态链接
函数的定义在动态链接库或共享对象的目标文件中,在链接阶段,动态链接库只提供符号表等少量信息保证所有符号引用都有定义(不像静态链接直接复制过去),保证编译顺利通过。在可执行文件执行时,动态连接库将函数等内容映射到运行时相应进程的虚地址空间。
(3)目标文件
①可重定位目标文件:含二进制代码、数据,因引用了其他模块的符号而不能执行
②共享目标文件/动态库: .so文件
③可执行文件
(4)目标文件的格式 ELF文件
ELF头:描述文件系统字长、字节序、ELF头大小、目标文件类型、目标机类型等
.text:代码段,可执行二进制机器指令
.rodata:只读数据段,存常量如字符串等
.data:数据段,以明确初始化的全局数据(全局变量、静态变量),是静态内存分配
.bss:块存储段,未被明确初始化的全局数据,这些全局数据会初始化为0,是静态内存分配
上面的四个段会加载到内存中
.symtab:符号表,定义和引用的函数和全局变量
.rel.text:代码段需要重定位的信息,存储需要靠重定位修改位置的符号的汇总
.rel.data:数据段需要重定位的信息
.debug:gcc -g选项会生成此段
.line:源程序的行号映射 用于调试
.strtab:字符串表存储symtab、debug符号表中符号的名字
查看ELF文件内容、各段大小的命令:
1 readelf -a main
2 size main
gcc命令基本选项:
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
库的生成与使用:
(1)静态库
ar rcs fun.a fun1.o fun2.o
选项:r:把列表中的目标文件加入到静态库
c:若指定的静态库不存在则创建该文件
s:更新静态文件的索引,使之包含新加入的目标文件的内容
链接时:
gcc main.c -lfun.a -o main
gcc -L. main.c -o main
-L紧跟静态库路径
(2)动态库
gcc -shared -fPIC -o lib.so lib,c
选项的含义:
-shared:生成动态库
-fPIC:生成位置无关代码
链接时:
gcc main.c ./lib.so -o main
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
可执行文件在运行时:
除了代码段、数据段、BSS段,还有堆区和栈区
堆区:用于动态分配内存,用 malloc、free申请和释放
从低地址向高地址增长
链式存储
效率比栈低
栈区:由操作系统自动分配和释放,存储函数的参数值、局部变量的值等
从高地址向低地址增长
连续内存
最大容量固定
原文:https://www.cnblogs.com/taoXiang/p/12370138.html