编译c源码-Linux下C语言程序编译过程解读

size:列出可执行文件各部分的规格和总规格、代码段、数据段、总大小等,使用size的具体使用示例请参考下面。

C 运行时库

C语言标准主要由两部分组成:一部分描述C的语法,另一部分描述C标准库。 C标准库定义了一组标准头文件,每个头文件都包含一些相关的函数、变量、类型声明和宏定义。 例如,常见的printf函数是C标准库函数,其原型定义在stdio头文件中。

C语言标准只定义了C标准库函数的原型,并没有提供实现。 因此,C语言编译器一般需要C运行时库(C Run Time Library,CRT)的支持。 C 运行时库通常简称为 C 运行时库。 与C语言类似,C++也定义了自己的标准并提供了相关的支持库,称为C++运行时库。

准备

由于GCC工具链主要使用在Linux环境下,因此本文也将使用Linux系统作为工作环境。 为了演示整个编译过程,本节首先以一个用C语言编写的简单Hello程序为例,其源码如下:

#include  
//此程序很简单,仅仅打印一个Hello World的字符串。
int main(void)
{
  printf("Hello World! n");
  return 0;
}

编译过程

1. 预处理

编译c源码-Linux下C语言程序编译过程解读

预处理过程主要包括以下过程:

$ gcc -E hello.c -o hello.i // 将源文件hello.c文件预处理生成hello.i
                        // GCC的选项-E使GCC在进行完预处理后即停止

hello.i文件可以像普通文本文件一样打开查看,其代码片段如下:

C语言学习资源汇总[最新版]

// 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.编译

编译过程就是对预处理后的文件进行一系列词法分析、语法分析、语义分析和优化编译c源码,生成相应的汇编代码。

用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中那样调用汇编器,可以根据汇编指令和处理器指令的对照表进行一一翻译。

当程序由多个源代码文件组成时,每个文件首先要完成汇编工作,只有生成.o目标文件后,才能进行下一步的链接工作。 注意:目标文件已经是最终程序的一部分,但在链接之前无法执行。

编译c源码-Linux下C语言程序编译过程解读

用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(可执行和可链接格式)格式的可重定向文件。

4.链接

链接也分为静态链接和动态链接,要点如下:

由于动态库和静态库链接的路径可能会重叠,因此如果路径中存在同名的静态库文件和动态库文件,例如libtest。 .so,如果你想让gcc选择链接libtest.a,你可以指定gcc选项-static,这将强制使用静态库进行链接。 以Hello World为例:

链接器最终生成的文件是ELF格式的可执行文件。 ELF可执行文件通常链接成不同的段,例如.text、.data、.rodata和.bss。

分析ELF文件

1. ELF文件的各个部分

ELF文件格式如下图所示。 ELF 头和节头表之间的节是节。 典型的 ELF 文件包含以下部分:

可以使用readelf -S查看各节的信息,如下:

$ 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 反汇编它编译c源码,如下所示:

$ 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) ……



如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,限10个名额!

▲扫描二维码-免费领取

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 编译c源码-Linux下C语言程序编译过程解读 https://www.wkzy.net/game/145845.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务