gcc编译器学习

gcc编译器

  1. -o [file] [origin_file] 将源代码文件编译生成file文件。如gcc -o hello hello.c,也可以直接将 gcc origin_file,那么gcc将会生成a.out文件。
  2. gcc编译器在编译一个C语言程序文件时需要经过4步:

² 将源程序文件经过预处理生成.i文件

² 将.i文件进行汇编生成.s文件

² 将.s文件编译生成.o文件

² 将各个模块的.o文件链接生成程序可执行文件

  1. gcc编译器中的几种常用的编译选项:

a) -c  编译、汇编指定的源程序,但是不进行链接

b) -S  编译指定的源文件,但是不进行汇编

c) -E  预处理指定的源程序,但是不进行汇编

d) -o  [file] [origin_file]将origin_file编译生成可执行文件file

e) -I  directory 指定include包含文件的搜索目录

f) -g  生成调试信息,这些信息可以被调试器捕捉调试

  1. gcc编译器区分大小写,譬如-o选项和-O选项两者之间是不一样的,-o选项表示将origin_fie编译链接后生成file可执行文件,而-O选项表示编译生成并且进行代码优化。
  2. 汇编源程序后指的是生成.o二进制文件,编译源文件指的是生成.s文件
  3. 如果使用gcc编译器的除-o选项外生成文件,再后跟-o选项,则生成的文件为-o 选项指定文件名称。如gcc -S hello.c -o hello.s则生成指定hello.s汇编代码文件。
  4. gcc -o 选项不仅可以接收输入为源代码文件,同时还可以接收目标文件如 gcc main.c test.c -o test
  5. -I 后跟参数为指定的源程序包含代码头文件所在的位置
  6. 链接器的任务:将多个目标文件连接成为一个完整,可加载,可执行的目标文件,其输入是可重定位的目标文件。链接主要任务如下:

a) 符号解析:将目标文件内的引用符号和该符号的定义联系起来

b) 将符号定义与存储器的位置联系起来,修改对这些符号的引用。

  1. 目标文件可分为三种类型:

a) 可重定位的目标文件:这种文件中包含了二进制代码和数据,这些代码和数据已经转换成了机器指令代码和数据,但是这种目标文件还不能够立刻被执行,原因是这些指令和模块使用了别的模块中的内容,但是却还没有连接这些模块,其他模块的符号对于本模块来说是未知的,这些符号的解析需要链接器进行链接,可重定位的目标文件后面通常后缀是.o

b) 可执行目标文件:这种文件也包含二进制代码和数据,但是这些文件已经经过了链接操作,和所有模块之间已经产生了联系,在链接器的解析和链接后,所有的目标文件中的符号都已经得到了解析和重定位,因此每个符号都是已经知道的,该文件可以被直接执行。

c) 共享目标文件:这是一种特殊类型的可重定位的目标文件,可以在需要它的程序运行或者加载时,动态地加载到内存之中运行,这种文件又被称作动态库文件或者共享库文件,后缀是.so

  1. ELF格式是Linux环境下最常用的目标文件格式,在大多数情况下,无论是可执行目标文件还是重定位目标文件,都使用该种格式进行存储,ELF格式目标文件由两部分组成:ELF文件头和目标文件的段。ELF文件头的前16个字节存储该文件系统的字长和字节序,剩下的部分包含了该文件的其他信息,包括ELF文件头的大小,目标文件的类型,目标机的类型,段头部表在目标文件内的文件偏移位置等。
  2. ELF文件的段部分包含:

a) 可执行文件和重定位文件均有部分

  1. .text: 代码段,存储二进制机器指令,这些指令可以直接被执行
  2. .rodata: 只读数据端。存储程序中使用的复杂常量,例如字符串
  3. .data: 数据段,存储程序中已经明确初始化的全局数据,如果某一全局变量初始化为0或者未被初始化,则会被放置于块存储段中,C语言的局部变量保存在栈上,不保存在数据段中。
  4. .bss: 块存储段。存储未被明确初始化的全局数据,在目标文件中这个段并不占用实际的空间,而只是一个占位符,告知指定位置应该预留全局数据的空间,块存储段存在的原因是为了提高磁盘空间的利用率。

以上四个段会被实际地加载到内存中,是实际的程序段,目标文件中还有一些其他段,这些信息不会加载到内存中,在生成可执行文件时已经被删除了。

b) 其他不一定存在的程序段

  1. .symtab: 符号表 存储定义和引用的函数和全局变量,所有引用本模块的全局符号,以及其他模块的符号,都会在符号表中有一个登记,用于链接器查找解析和重定位的位置查找。每个重定位目标文件都需要有这样一个表。
  2. .rel.text 代码段需要重定位的信息 存储需要靠重定位操作修改位置的符号表(在源代码文件中被声明但是并没有被定义的函数)
  3. .rel.data:数据段需要重定位的信息。存储需要靠重定位操作修改位置的符号汇总,这些符号在数据段中,是一些全局变量。
  4. .debug:调试信息。存储用于调试时产生的符号表,在编译程序时通过-g才能生成这个段。
  5. .line:源程序调试时的行号映射,存储源程序中每个语句的行号,在编译程序时通过-g选项才会生成。
  6. .strtab: 字符串表, 存储.symtab符号表和.debug符号表中符号的名字,这些名字是一些字符串,并且以’\0’结尾。
  7. 目标文件中的符号表:每个可重定位的目标文件都会有一个符号表,在这个符号表中存储的符号可以分为三种类型:

a) 本模块中引用的其他模块的全局符号

b) 本模块中定义的全局符号

c) 本模块中定义和引用的局部符号

注意:局部符号和局部变量两者是不同的概念,局部变量仅存在在内存中,局部符号则是包括了局部静态变量和局部标号,这些内容可能存在于磁盘中的文件中。分析符号表可以采用工具readelf。

  1. GNU链接器也可以实现将多个重定位目标文件链接成为一个可执行目标文件,如:ld add.o main.o -o app
  2. 链接的第一步,寻找所有参与链接的目标文件的符号,查找是否符号在所有可重定位目标文件中未被找到,若有,则输出信息给用户,若无,则进行链接生成可执行文件。如果没有问题,则进行第二步重定位操作
  3. 重定位概念:当符号解析结束后,每个符号的定义位置和大小都是确定的,重定位操作可以将这些符号链接起来,在这个过程中,链接器会将需要参与链接的目标文件进行合并,不同可重定位目标的代码段之间进行合并,不同的目标文件的数据段之间进行合并,通过这一步,所有的代码段和数据都有一个统一且唯一的地址。
  4. 重定位符号引用。由于目标文件中相同的段已经合并,因此程序中对符号的引用位置就都作废了,这时候链接器会修改这些引用符号的地址,使其指向正确的地址。
  5. 从链接方式上区分,可以将程序库分为动态库(共享库)和静态库两种

a) 静态库:在可执行程序运行前已经加入到执行码中,成为可执行程序的一部分来执行。

b) 共享库:在执行程序启动时加载到执行程序中,可以被多个执行程序共享使用。

LINUX下一般程序库采用的是ELF格式,但gcc编译器同时还支持别的文件格式的程序库。

  1. 静态库是一些目标代码的集合,Linux环境下的静态库目标文件一般以.a作为目标文件的扩展名,使用ar命令创建静态库,静态库的优点在于使用简单,但是不必再编译,节省编译时间,以最短的时间生成可执行程序,静态库比使用动态库快约1%~5%,但是对于现今计算机处理速度而言,这点时间微不足道。
  2. 生成静态库可以包含两步:

a) 在shell中编译源文件,生成一个可重定位的目标文件

gcc -c hello.c

b) 将目标文件后加入到静态库中

ar rcs libhello.a hello.o

其中rcs参数起着至关重要的作用,r参数表示将目标文件加入到静态库中,c参数表明若是静态库文件不存在,则创建,s参数更新静态库索引,使其包含目标文件中的内容

  1. 静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,拓展名为.a
  2. 静态库创建完成后,为了使程序能够正确使用该库中的符号,因此需要制作一个包含该静态库中全局符号声明的头文件,这个头文件可以包含在应用程序的头文件中,这样就可以使用静态库了。使用静态库有以下步骤:

a) 制作头文件对该静态库中的函数进行生命,并将在需要使用到该静态库中函数的源码中包含该头文件。

b) 使用gcc的-l选项来指定静态库或者使用-L选项来制定库文件的搜索路径,-l和-L之后都是直接带参数不加空格,如:

gcc -L. main.c -ltest -o hello

在使用gcc链接器进行静态库链接时,需要注意参数所处的位置

-l是链接器选项,一定要放在编译的源文件名称的后面,也就是放在main.c之后,若放在前面则会报错。gcc也支持-static选项对静态库进行链接。

gcc main.c -static ./libtest.a -o hello

  1. 动态库在工程中较经常使用,Windows环境底下的DLL文件和Linux环境底下的so文件,动态库是在程序启动时被装载的,当一个应用程序使用该静态库时,其他应用程序也依然可以使用这个静态库,这个被多进程同时使用的动态库在内存中只有一个副本。
  2. 动态库优点:

a) 是代码,相比于二进制文件更好更新和管理

b) 便于程序的发布

c) 易于程序模块的更新,更新并不影响程序使用旧的,非向后兼容的版本。

d) 在执行特定程序时,可以覆盖整个库或更新库中的特定函数。

e) 更新操作不会影响正在运行的程序,他们仍然会使用已经装载的库

  1. Linux环境下使用gcc创建动态库,因为动态库可以被多个进程共享加载,因此需要生成和位置无关的目标文件,这时需要使用gcc编译器的-fPIC选项,该选项可以生成与位置无关的代码,同时还需要使用-shared 选项,该选项将与位置无关的代码制作成为动态库

gcc -fPIC -shared -o [动态库文件的名称] [源码名称]

其中[动态库文件的名称]选项中是以.so结尾的即将生成或已经存在的动态库名,[源码名称]是指源码所在位置的文件名称
26. 动态库的使用:动态库与静态库一样,需要制作一个包含该动态文件中全局符号声明的头文件,这个头文件应包含在源码中。使用时如下:

gcc main.c ./test.so -o app

同时也可以使用-l选项来链接动态库,对动态库文件名有要求。

  1. gcc工具:

a) Binutils:一组可以用于编译、链接、汇编和其他调试目的的程序,包括ar、as、ld等

b) gcc:GNU编译器,Linux环境下默认C语言编译器

c) Glibc:GNU的C语言标准库。

原文地址:https://www.cnblogs.com/Jetson-xie/p/12186628.html

时间: 2024-08-25 13:25:31

gcc编译器学习的相关文章

vi 编辑器,gcc 编译器的使用

vi编辑器是我们在linux下经常使用的文本编辑器,这个东西也是专门为程序员准备的,基本上每个Unix和Linux系统都为我们提供了这个软件,我们可以使用vi来编写我们的代码.在vi编辑器下所有操作都必须通过键盘和特定的快捷键组合来完成,刚开始学习的新手会感觉不太容易,但是这个东西很重要,我们先来学习如何使用它,掌握了这个东西去面试的时候也算是一门技能.而vim是vi的增强版,新增加了很多功能. 在shell下输入命令vi 文件名,例如:vi first.c,就开始编辑first.c文件了.vi

C语言的编译过程、安装gcc编译器以及设置环境变量

以我对C语言编译过程的了解,我用了一点时间画了一个图,提供给大家参考一下,希望有些能对您的问题提上帮助. 前几天刚初步学习了C语言的编译过程,感触挺深的.在C语言中头文件其实起了一个很大的作用. 1.头文件可以不需要编译 2.可以查看具体的声明 3.头文件加上实现文件的o文件提交给使用者即可 ,不需要知道源代码 4..o文件预先编译,所以整个项目编译时,会大大提高编译的时间 . 5.当一个文件(A.c文件)依赖于头文件(b.h)时 ,如果b.c编译之后形成的b.o文件重新编译后,a.o的文件不需

基于Linux下的GCC编译器的内部预宏定义与__attribute__属性

***************************************************************************************************************************** 作者:EasyWave                                                                                    时间:2015.02.20 类别:Linux应用-GCC编

如何使用gcc编译器

要想读懂本文,你需要对C语言有基本的了解,本文将介绍如何使用gcc编译器. 首先,我们介绍如何在命令行的方式下使用编译器编译简单的C源代码. 然后,我们简要介绍一下编译器究竟作了哪些工作,以及如何控制编译的过程. 我们也简要介绍了调试器的使用方法. gcc介绍 你能想象使用封闭源代码的私有编译器编译自由软件吗?你怎么知道编译器在你的可执行文件中加入了什么?可能会加入各种后门和木马.Ken Thompson是一个著名的黑客,他编写了一个编译器,当编译器编译自己时,就在'login'程序中留下后门和

gcc编译器对宽字符的识别

最早是使用VC++工具来学习C++,学的越多就越对VC挡住的我看不见的东西好奇,总想多接触一些开发环境,今日抽空摸索了一下CodeBlocks这个开源的IDE使用方法,配置的编译器是MinGW的gcc编译器,gcc编译多字节集的字符还没什么问题,但遇到宽字符,发现编译不通过,经过多方查找,发现gcc对宽字符的支持分规则同我用熟了的cl编译器有一些区别,具体用法如下: GNU   C/C++编译器gcc也可以正确支持wchar_t字符和字符串,但是源代码的保存格式必须符合下面条件:        

基于嵌入式Linux下GCC编译器__sync_sub_and_fetch_4错误的解决心得

******************************************************************************************************************************************************* 作者:EasyWave                                                                                      

Linux中gcc编译器的用法

在Linux环境下进行开发,gcc是非常重要的编译工具,所以学习gcc的基本常见用法时非常有必要的. 一.首先我们先说明下gcc编译源文件的后缀名类型 .c为后缀的文件,C语言源代码文件:  .a为后缀的文件,是由目标文件构成的档案库文件:  .C,.cc或.cxx 为后缀的文件,是C++源代码文件:  .h为后缀的文件,是程序所包含的头文件:  .i 为后缀的文件,是已经预处理过的C源代码文件:  .ii为后缀的文件,是已经预处理过的C++源代码文件:  .m为后缀的文件,是Objective

GCC编译器入门

GCC编译器(GNU C Compiler)是GNU组织的一款开源 编译器,它是Linux环境下的默认C语言编译器.它处理能够高效的编译C语言以外,还可以编译其他语言.并且,现在的GCC已经不光包括编译器本身,还包含了编译过程中的工具链. 1 GCC编译流程 在学习使用GCC编译程序之前,首先要知道编译C程序的基本流程,一般情况下分为下面四步: (1) 对C语言进行预处理,生成*.i文件. (2) 将上一步生成的*.i文件编译生成汇编语言文件,后缀名为*.s (3) 将汇编语言文件*.s经过汇编

GCC编译器编译链接

在gcc编译器环境下,常见的文件扩展名的含义如下: .c:C源程序,经过预编译后的源程序也为.c文件,它可以通过-E参数输出. .h:头文件 .s:经过编译得到的汇编程序代码,它可以通过-S参数输出. .o:目标文件 .a:函数库 Gcc编译器常见语法: -c:只进行编译,不进行链接,输出的是与源文件同名的.o文件. -o:指定生成的文件的名称.链接生成可执行文件,这个参数后可以带可执行文件的名字,如果没有指定可执行文件的名字,则会默认为a.out. -S:输出汇编代码文件,输出一个与源文件同名