Linux 程序编译过程的来龙去脉( 二 )


  • 删除所有注释“//”和“/* */” 。
  • 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号 。
  • 保留所有的#pragma编译器指令,后续编译过程需要使用它们 。使用gcc进行预处理的命令如下
  • $ gcc -E hello.c -o hello.i // 将源文件hello.c文件预处理生成hello.i// GCC的选项-E使GCC在进行完预处理后即停止hello.i文件可以作为普通文本文件打开进行查看,其代码片段如下所示
    // hello.i代码片段extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));# 942 "/usr/include/stdio.h" 3 4# 2 "hello.c" 2# 3 "hello.c"intmain(void){printf("Hello World!" "n");return 0;}2.编译
    编译过程就是对预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码 。
    使用gcc进行编译的命令如下
    $ gcc -S hello.i -o hello.s // 将预处理生成的hello.i文件编译生成汇编程序hello.s// GCC的选项-S使GCC在执行完编译后停止,生成汇编程序上述命令生成的汇编程序hello.s的代码片段如下所示,其全部为汇编代码 。
    // hello.s代码片段main:.LFB0:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6movl $.LC0, %edicall putsmovl $0, %eaxpopq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc3.汇编
    汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o的目标文件中 。由于每一个汇编语句几乎都对应一条处理器指令,因此,汇编相对于编译过程比较简单,通过调用Binutils中的汇编器as根据汇编指令和处理器指令的对照表一一翻译即可 。
    当程序由多个源代码文件构成时,每个文件都要先完成汇编工作,生成.o目标文件后,才能进入下一步的链接工作 。注意:目标文件已经是最终程序的某一部分了,但是在链接之前还不能执行 。
    使用gcc进行汇编的命令如下
    $ gcc -c hello.s -o hello.o // 将编译生成的hello.s文件汇编生成目标文件hello.o// GCC的选项-c使GCC在执行完汇编后停止,生成目标文件//或者直接调用as进行汇编$ as -c hello.s -o hello.o //使用Binutils中的as将hello.s文件汇编生成目标文件注意:hello.o目标文件为ELF(Executable and Linkable Format)格式的可重定向文件 。
    4.链接
    链接也分为静态链接和动态链接,其要点如下
    • 静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行文件会比较大 。链接器将函数的代码从其所在地(不同的目标文件或静态链接库中)拷贝到最终的可执行程序中 。为创建可执行文件,链接器必须要完成的主要任务是:符号解析(把目标文件中符号的定义和引用联系起来)和重定位(把符号定义和内存地址对应起来然后修改所有对符号的引用) 。
    • 动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去 。
      • 在Linux系统中,gcc编译链接时的动态库搜索路径的顺序通常为:首先从gcc命令的参数-L指定的路径寻找;再从环境变量LIBRARY_PATH指定的路径寻址;再从默认路径/lib、/usr/lib、/usr/local/lib寻找 。
      • 在Linux系统中,执行二进制文件时的动态库搜索路径的顺序通常为:首先搜索编译目标代码时指定的动态库搜索路径;再从环境变量LD_LIBRARY_PATH指定的路径寻址;再从配置文件/etc/ld.so.conf中指定的动态库搜索路径;再从默认路径/lib、/usr/lib寻找 。
      • 在Linux系统中,可以用ldd命令查看一个可执行程序依赖的共享库 。
    由于链接动态库和静态库的路径可能有重合,所以如果在路径中有同名的静态库文件和动态库文件,比如libtest.a和libtest.so,gcc链接时默认优先选择动态库,会链接libtest.so,如果要让gcc选择链接libtest.a则可以指定gcc选项-static,该选项会强制使用静态库进行链接 。以Hello World为例: