C/C++源程序到可执行程序的过程

  源程序.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申请和释放

              从低地址向高地址增长

              链式存储

                   效率比栈低

  栈区:由操作系统自动分配和释放,存储函数的参数值、局部变量的值等

                  从高地址向低地址增长

        连续内存

                  最大容量固定

猜你喜欢

转载自www.cnblogs.com/taoXiang/p/12370138.html