gcc常用命令及背后故事

一、gcc编译器背后的故事

1. gcc不是一个人在战斗

gcc不是一个人在战斗,gcc背后其实有一堆战友。

gcc(GNU C Compiler)是编译工具,将C/C++编写的程序转化为处理器能够执行的二进制代码的过程。

gcc中Binutils是一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、ldd、readelf、size等。这一组工具是开发和调试不可缺少的工具,分别简介如下。

addr2line:用来将程序地址转换成其他对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置。
as:主要用于汇编。
ld:主要用于连接。
ar:主要用于创建静态库。
ldd:可以用于查看一个可执行程序依赖的共享库。
objcopy:将一种对象文件翻译成另一种格式,譬如将.bin转化成.elf、或者将.elf转化成.bin等。
objdump:主要的作用是反汇编。
readelf:显示有关ELF文件的信息。
size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等。

2. ELF文件分析

ELF文件格式如下图所示,位于ELF Header和Section Header Table之间的都是段(Section)。一个典型的ELF文件包含下面几个段。

在这里插入图片描述

text:已编译程序的指令代码段。
rodata:ro代表read only,即只读数据。
data:已初始化的C程序全局变量和静态局部变量。
bss:未初始化的C程序全局变量和静态局部变量。
debug:调试符号表,调试器用此段的信息帮助调试。

二、Linux GCC常用命令

1. 简介

GCC的意思也只是GUN C Compiler而已。 经过了这么多年的发展,GCC已经不仅仅能支持C语言,它现在还支持Ada语言、C++语言、Java语言、Objective语言、Pascal语言、COBOL语言,以及支持函数式编程和逻辑的Mercury语言等等。而GCC已经不单是GNU C语言编译器的意思了,而是变成了GNU Compiler Collection,也是GNU编译器家族的意思。另一方面,说到GCC对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。

2. 简单编译

示例程序如下

test.c

#include<stdio.h>
int main(void)
{
printf{"Hello World!\n"};
return 0;
}

这个程序,一步到位的编译指令是:

gcc test.c -o test

在这里插入图片描述

实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。

2.1 预处理

gcc -E test.c -o test.i

gcc -E test.c

在这里插入图片描述

可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口输出预处理后的代码。

gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h文件中的内容插入到test.c中了。

2.2 编译为汇编代码(Compilation)

预处理之后,可直接对生成的test.i文件编译,生成汇编代码:

gcc -S test.i -o test.s

在这里插入图片描述

gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。

2.3 汇编(Assembly)

对于2.2生成的汇编代码文件test.s,gcc汇编器负责将其编译为目标文件,如下:

gcc -c test.s -o test.o

结果遇到如下错误

在这里插入图片描述

经过多番排查,最终发现是在编译为汇编代码时"gcc -s test.i -o test.s"中的"-s"应该大写的S,修改后就能成功编译

在这里插入图片描述

2.4 连接(Linking)

gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。

对于2.3生成的.o文件test.o,将其与C标准输入输出库进行连接,最终生成程序test。

gcc test.o -o test

然后在命令行窗口中用./执行test

./test

在这里插入图片描述

3. 多个程序文件的编译

通常整个程序是由多个源文件组成的,相应的也就形成了多个编译单元,使用gcc能够很好地管理这些编译单元。假设有一个由test1.c和test.2两个源文件组成的程序,可使用如下命令将它们编译为可执行文件。

gcc test1.c test2.c -o test

如果同时处理的文件不止一个,gcc仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行下面这三条命令:

gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test

4. 检错

gcc -pedantic illcode.c -o illcode

-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准完全兼容,它仅仅只能用来帮助程序员发现一些不符合上述标准的代码,但不是全部,只有ANSI/ISO C语言标准中要求进行编译诊断的那些情况会被gcc发现并提出警告。

除了-pedantic之外,gcc还有一些其他的编译选项也能产生有用的警告信息,这些选项大多以-W开头,其中最优价值的当属-Wall了,使用它能够使gcc产生尽可能多的警告信息。

gcc -Wall illcode.c -o illcode

gcc给出的警告信息虽然从严格意义上说不能算作错误,但却很可能成为错误的栖身之所。作为一个优秀的程序员,我们需要使自己的代码始终保持标准、健壮的特性。在编译时可以加上-Werror选项,那么gcc会在所有产生警告的地方停止编译,使程序员修改自己的代码,如下:

gcc -Werror test.c -o test

5. 库文件连接

开发软件时,完全不使用第三方函数库的情况是很少见的 ,通常来讲都需要接著许多函数库的支持才能够完成相应的功能。

5.1 编译成可执行文件

首先我们要进行编译test.c为目标文件,这个时候需要执行

gcc -c -I /usr/dev/mysql/include test.c -o test.o

5.2 链接

把所有目标文件链接成可执行文件:

gcc -L /usr/dev/mysql/lib -lmysqlclient test.o -o test

Linux下的库文件分为两大类分别是动态链接库(.so结尾)和静态链接库(.a结尾),二者的区别在于程序执行时所需的代码是在运行时动态加载的,还是编译时静态加载的。

5.3 强制链接时使用静态链接库

默认情况下,gcc在链接时优先使用动态库,只有当动态库不存在时才考虑使用静态库,如果需要的话可以在编译时加上-static选项,强制使用静态库。

gcc -L /usr/dev/mysql/lib -static -lmysqlclient test.o -o test

静态库链接时搜索路径顺序:

1.ld会去找GCC命令中的参数-L
2.再找gcc的环境变量LIBRARY_ PATH
3.再找内定目录/lib /usrib /ustrlocalib这是当初compile gcc时写在程序内的

动态链接时、执行时搜索路径顺序:

1.编译目标代码时指定的动态库搜索路径
2.环境变量LD_ LIBRARY PATH 指定的动态库搜索路径
3.配置文件/etcld.so.conf中指定的动态库搜索路径
4.默认的动态库搜索路径/lib
5.默认的动态库搜索路径/usr/lib

三、总结

通过以上实操,可以知道在默认情况下,gcc在链接时优先使用动态库。Linux下在命令行窗口输入命令和gcc编译程序的时候要注意区分大小写,不然会造成不必要的错误,导致大量消耗时间。

四、参考文献

https://blog.csdn.net/weixin_44941716/article/details/118340186?spm=1001.2014.3001.5501

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值