加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_汕头站长网 (https://www.0754zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux上C语言程序编译过程详解

发布时间:2022-11-16 11:21:06 所属栏目:Linux 来源:
导读:  size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等,请参见后文了解使用size的具体使用实例。

  C运行库

  C语言标准主要由两部分组成:一部分描述C的语法,另一部分描述C标准
  size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等,请参见后文了解使用size的具体使用实例。
 
  C运行库
 
  C语言标准主要由两部分组成:一部分描述C的语法,另一部分描述C标准库。C标准库定义了一组标准头文件linux编码,每个头文件中包含一些相关的函数、变量、类型声明和宏定义,譬如常见的printf函数便是一个C标准库函数,其原型定义在stdio头文件中。
 
  C语言标准仅仅定义了C标准库函数原型,并没有提供实现。因此,C语言编译器通常需要一个C运行时库(C Run Time Libray,CRT)的支持。C运行时库又常简称为C运行库。与C语言类似,C++也定义了自己的标准,同时提供相关支持库,称为C++运行时库。
 
  准备工作
 
  由于GCC工具链主要是在Linux环境中进行使用,因此本文也将以Linux系统作为工作环境。为了能够演示编译的整个过程,本节先准备一个C语言编写的简单Hello程序作为示例,其源代码如下所示:
 
  #include  
 
  //此程序很简单,仅仅打印一个Hello World的字符串。
  int main(void)
  {
   ?printf("Hello World! \n");
   ?return 0;
  }
  编译过程
 
  1.预处理
 
  预处理的过程主要包括以下过程:
 
  $ 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"
  int
  main(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_startproc
   ? ?pushq ? %rbp
   ? ?.cfi_def_cfa_offset 16
   ? ?.cfi_offset 6, -16
   ? ?movq ? ?%rsp, %rbp
   ? ?.cfi_def_cfa_register 6
   ? ?movl ? ?$.LC0, %edi
   ? ?call ? ?puts
   ? ?movl ? ?$0, %eax
   ? ?popq ? ?%rbp
   ? ?.cfi_def_cfa 7, 8
   ? ?ret
   ? ?.cfi_endproc
  3.汇编
 
  汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.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.链接
 
  链接也分为静态链接和动态链接,其要点如下:
 
  由于链接动态库和静态库的路径可能有重合,所以如果在路径中有同名的静态库文件和动态库文件,比如libtest.a和libtest.so,gcc链接时默认优先选择动态库,会链接libtest.so,如果要让gcc选择链接libtest.a则可以指定gcc选项-static,该选项会强制使用静态库进行链接。以Hello World为例:
 
  链接器链接后生成的最终文件为ELF格式可执行文件,一个ELF可执行文件通常被链接为不同的段,常见的段譬如.text、.data、.rodata、.bss等段。
 
  分析ELF文件
 
  1.ELF文件的段
 
  ELF文件格式如下图所示,位于ELF Header和Section Header Table之间的都是段(Section)。一个典型的ELF文件包含下面几个段:
 
  linux ftp设置编码_修改linux服务器编码_linux编码
 
  可以使用readelf -S查看其各个section的信息如下:
 
  $ readelf -S hello
  There are 31 section headers, starting at offset 0x19d8:
 
  Section Headers:
   ?[Nr] Name ? ? ? ? ? ? ?Type ? ? ? ? ? ? Address ? ? ? ? ? Offset
   ? ? ? Size ? ? ? ? ? ? ?EntSize ? ? ? ? ?Flags ?Link ?Info ?Align
   ?[ 0] ? ? ? ? ? ? ? ? ? NULL ? ? ? ? ? ? 0000000000000000 ?00000000
   ? ? ? 0000000000000000 ?0000000000000000 ? ? ? ? ? 0 ? ? 0 ? ? 0
  ……
   ?[11] .init ? ? ? ? ? ? PROGBITS ? ? ? ? 00000000004003c8 ?000003c8
   ? ? ? 000000000000001a ?0000000000000000 ?AX ? ? ? 0 ? ? 0 ? ? 4
  ……
   ?[14] .text ? ? ? ? ? ? PROGBITS ? ? ? ? 0000000000400430 ?00000430
   ? ? ? 0000000000000182 ?0000000000000000 ?AX ? ? ? 0 ? ? 0 ? ? 16
   ?[15] .fini ? ? ? ? ? ? PROGBITS ? ? ? ? 00000000004005b4 ?000005b4
  ……
  2.反汇编ELF
 
  由于ELF文件无法被当做普通文本文件打开,如果希望直接查看一个ELF文件包含的指令和数据,需要使用反汇编的方法。
 
  使用objdump -D对其进行反汇编如下:
 
  $ objdump -D hello
  ……
  0000000000400526
  : ?// main标签的PC地址
  //PC地址:指令编码 ? ? ? ? ? ? ? ? ?指令的汇编格式
   ?400526: ? ?55 ? ? ? ? ? ? ? ? ? ? ? ? ?push ? %rbp
   ?400527: ? ?48 89 e5 ? ? ? ? ? ? ? ?mov ? ?%rsp,%rbp
   ?40052a: ? ?bf c4 05 40 00 ? ? ? ? ?mov ? ?$0x4005c4,%edi
   ?40052f: ? ?e8 cc fe ff ff ? ? ? ? ?callq ?400400
   ?400534: ? ?b8 00 00 00 00 ? ? ? ? ?mov ? ?$0x0,%eax
   ?400539: ? ?5d ? ? ? ? ? ? ? ? ? ? ?pop ? ?%rbp
   ?40053a: ? ?c3 ? ? ? ? ? ? ? ? ? ? ? ? ?retq ?
   ?40053b: ? ?0f 1f 44 00 00 ? ? ? ? ?nopl ? 0x0(%rax,%rax,1)
  ……
  使用objdump -S将其反汇编并且将其C语言源代码混合显示出来:
 
  $ gcc -o hello -g hello.c //要加上-g选项
  $ objdump -S hello
  ……
  0000000000400526
  :
  #include
 
  int
  main(void)
  {
   ?400526: ? ?55 ? ? ? ? ? ? ? ? ? ? ? ? ?push ? %rbp
   ?400527: ? ?48 89 e5 ? ? ? ? ? ? ? ?mov ? ?%rsp,%rbp
   ?printf("Hello World!" "\n");
   ?40052a: ? ?bf c4 05 40 00 ? ? ? ? ?mov ? ?$0x4005c4,%edi
   ?40052f: ? ?e8 cc fe ff ff ? ? ? ? ?callq ?400400
   ?return 0;
   ?400534: ? ?b8 00 00 00 00 ? ? ? ? ?mov ? ?$0x0,%eax
  }
   ?400539: ? ?5d ? ? ? ? ? ? ? ? ? ? ? ? ?pop ? ?%rbp
   ?40053a: ? ?c3 ? ? ? ? ? ? ? ? ? ? ? ? ?retq ?
   ?40053b: ? ?0f 1f 44 00 00 ? ? ? ? ?nopl ? 0x0(%rax,%rax,1)
  ……
 

(编辑:云计算网_汕头站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!