GCC学习笔记

  1. gcc编译的4个步骤

学习教程:https://zhuanlan.zhihu.com/p/404682058

下面给出一个练习程序示例“hello.c”:

 #include <stdio.h>
 int main() 
 {
     printf("hello, world! This is a C program.\n");
     for(int i=0;i<10;i++ ){
         printf("output i=%d\n",i);
     }
     return 0;
 }

(1)预处理阶段(头文件代码汇总成C代码)

预处理过程中,对源代码文件中的文件包含 (include)、 预编译语句 (如宏定义define等)进行展开,生成“ .i ”文件。 可理解为把头文件的代码、宏之类的内容转换成更纯粹的C代码,不过生成的文件以“.i”为后缀。

表 1.1 GCC的参数使用

参数

用途

-E

让编译器生成“ .i ”文件

-o

后面指定输出文件的名字

具体命令如下:

 # 预处理——可理解为把头文件的代码汇总成C代码

 # 把 *.c 文件转换得到 *.i 文件
 gcc –E hello.c –o hello.i

生成的“hello.i”内容如下:

相当于它把原C代码中包含的头文件中引用的内容汇总到一处, 如果原C代码有宏定义,它把宏定义展开成具体的内容。

(2)编译阶段(C语言转换成汇编语言)

把预处理后的.i文件通过编译成为汇编语言,生成.s文件,即把代码从C语言转换成汇编语言,这是GCC编译器完成的工作。在这个过程,GCC会检查各个源文件的语法,即使我们调用了一个没有定义的函数,也不会报错。

表 2.1 GCC参数

参数

用途

-S

让编译器生成汇编语言的代码文件(“ .s ”文件)

-o

后面指定输出文件的名字

具体命令如下:

#编译——把C代码转换为汇编代码

# 1)把 *.i 文件转换得到 *.s 文件
gcc –S hello.i –o hello.s

# 2)也可以直接以C文件作为输入进行编译,与上面的命令是等价的
 gcc –S hello.c –o hello.s

编译生成的“hello.s”文件内容如下:

汇编语言是跟平台相关的,由于本示例的GCC目标平台是x86,所以此处生成的汇编文件是x86的汇编代码。

(3)汇编阶段(汇编语言转换成机器码)

将汇编语言文件经过汇编,生成目标文件.o文件,每一个源文件都对应一个目标文件。即把汇编语言的代码转换成机器码,这是as汇编器完成的工作。

表 3.1 GCC参数

参数

用途

-c

只编译(compile)源文件但不链接,将源程序编译成目标文件(“.o”文件)

-o

后面指定输出文件的名字

计算机只认识0或者1,不懂得C语言,也不懂得汇编语言,经过编译汇编之后,生成的目标文件包含着机器代码,这部分代码就可以直接被计算机执行。一般情况下,可以直接使用参数“c”,跳过上述的两个过程,具体命令 如下:

1)汇编,可理解为把汇编代码转换为机器码,把*.s转换得到*.o,即目标文件

gcc–chello.s–ohello.o

2)也可以直接以C文件作为输入进行汇编,与上面的命令是等价的

gcc–chello.c–ohello.o

Linux下生成的 *.o目标文件、*so动态库文件以及下一小节链接阶段生成最终的可执行文件都是elf格式的, 可以使用“readelf”工具来查看它们的内容。

具体命令如下:

readelf -a hello.o

readelf 的工具输出的信息如下:

可以了解到目标文件包含ELF头、程序头、节等内容, 对于*.o目标文件或*.so库文件,编译器在链接阶段利用这些信息把多个文件组织起来, 对于可执行文件,系统在运行时根据这些信息加载程序运行。

(4)链接阶段(生成可执行程序文件)

最后将每个源文件对应的目标.o文件链接起来,就生成一个可执行程序文件,这是链接器ld完成的工作。

例如一个工程里包含了A和B两个代码文件,在链接阶段, 链接过程需要把A和B之间的函数调用关系理顺,也就是说要告诉A在哪里能够调用到fun函数, 建立映射关系,所以称之为链接。若链接过程中找不到fun函数的具体定义,则会链接报错。

虽然本示例只有一个hello.c文件,但它调用了C标准代码库的printf函数, 所以链接器会把它和printf函数链接起来,生成最终的可执行文件。

链接分为两种:

  • 动态链接:GCC编译时的默认选项。动态是指在应用程序运行时才去加载外部的代码库,不同的程序可以共用代码库。 所以动态链接生成的程序比较小,占用较少的内存

  • 静态链接:链接时使用选项 “--static”,它在编译阶段就会把所有用到的库打包到自己的可执行程序中。 所以静态链接的优点是具有较好的兼容性,不依赖外部环境,但是生成的程序比较大

通过执行如下命令体验静态链接与动态链接的区别

动态链接命令

# 1) 动态链接,生成名为hello的可执行文件
 ​gcc hello.o –o hello
 ​
 # 2) 也可以直接使用C文件一步生成,与上面的命令等价
 gcc hello.c -o hello

静态链接命令

# 1) 静态链接,使用--static参数,生成名为hello_static的可执行文件
 gcc hello.o –o hello_static --static
 ​
 # 2) 也可以直接使用C文件一步生成,与上面的命令等价
 gcc hello.c -o hello_static --static

如下图所示,动态链接生成的“hello”程序16.7KB,静态链接生成的“hello_static”程序871KB

  • 在Ubuntu下,可以使用 ldd 工具查看动态文件的库依赖,尝试执行如下命令:

动态链接生成的hello程序依赖于库文件linux-vdso.so.1、libc.so.6 以及ld-linux-x86-64.so.2,其中的libc.so.6就是我们常说的C标准代码库, 我们的程序中调用了它的printf库函数。

静态链接生成的hello_static没有依赖外部库文件。

(5)总结与归纳

(1)预处理:把头文件的代码、宏之类的内容转换成生成的“.i”文件——C代码。

gcc -E hello.c -o hello.i

(2)编译:把预处理后的“.i”文件通过编译,生成“.s”文件——汇编语言。

gcc -S hello.i -o hello.s

(3)汇编:将汇编语言文件生成目标文件“.o”文件——机器码。

gcc -c hello.s -o hello.o

(4)链接:将每个源文件对应的“.o”文件链接起来,就生成一个可执行程序文件。

gcc hello.o -o hello

通常写完程序,也可用一个命令直接完成以上四个步骤。

 gcc hello.c -o hello
  1. 交叉编译

如果希望编译器运行在x86架构平台上,然后编译生成ARM架构的可执行程序,

这种编译器目标程序运行在不同架构的编译过程,被称为 交叉编译

(1)安装ARM-GCC

  • 在主机上执行如下命令,并键入管理员密码

sudo apt install gcc-arm-linux-gnueabihf

这个安装过程比较漫长,耐心等待

根据提示输入“Y”

  • 安装完成后,使用如下命令,查看版本

arm-linux-gnueabihf-gcc –v
  • 查看可用的相关命令,使用如下命令,再按两下TAB键

arm-linux-gnueabihf-

如下图包含了ARM-GCC工具链Binutils的各种工具。

(2)交叉编译程序

交叉编译器与本地编译器使用起来并没有多大区别。

在hello.c程序所在的目录执行如下命令

 arm-linux-gnueabihf-gcc hello.c –o hello_arm

同样的C代码文件,使用交叉编译器编译后,生成的hello已经变成了ARM平台的可执行文件,可以通过readelf工具来查看具体的程序信息。

readelf工具在系统安装GCC编译工具链时一起被安装了,我们可以直接使用。在主机上执行以下命令:

 readelf -a hello_arm

最后把hello_arm通过NFS拷贝到开发板上,就能手动运行了。

(3)GCC编译器命名格式

除了我们安装的arm-linux-gnueabihf-gcc外,编译器还有很多版本,如arm-linux-gnueabi-gcc,本地编译器gcc全名为x86_64-linux-gnu-gcc,这些编译器是有一定的命名规则的:

arch [-os] [-(gnu)eabi(hf)] -gcc

其中的各字段如下表所示:

字段

含义

arch

目标芯片架构

os

操作系统

gnu

C标准库类型

eabi

应用二进制接口

hf

浮点模型

  • 以我们安装的arm-linux-gnueabihf-gcc编译器为例:

其中的各字段如下表所示:

字段

含义

arm

表示它的目标芯片架构为ARM

linux

目标操作系统为Linux

gnu

使用GNU的C标准库即glibc

eabi

使用嵌入式应用二进制接口(eabi)

hf

编译器的浮点模式为硬浮点hard-float

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值