Linux上C语言程序编译过程详解
发布时间:2022-11-16 11:21:06 所属栏目:Linux 来源:
导读: size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等,请参见后文了解使用size的具体使用实例。
C运行库
C语言标准主要由两部分组成:一部分描述C的语法,另一部分描述C标准
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) …… (编辑:云计算网_汕头站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
站长推荐


浙公网安备 33038102330478号