首页 > 其他分享 >GCC基本使用

GCC基本使用

时间:2023-06-11 14:11:28浏览次数:40  
标签:基本 文件 GCC 链接 编译 编译器 使用 hello

本文重点讲解GCC的基本概念和在嵌入式环境下的使用。

1. GCC工具

GCC编译器:

GCC(GNU Compiler Collection)是由 GNU 开发的编程语言编译器。 GCC最初代表“GNU C Compiler”,当时只支持C语言。 后来又扩展能够支持更多编程语言,包括 C++、Fortran 和 Java 等。 因此,GCC也被重新定义为“GNU Compiler Collection”,成为历史上最优秀的编译器, 其执行效率与一般的编译器相比平均效率要高 20%~30%。

GCC的官网地址为:https://gcc.gnu.org/,在Ubuntu系统下系统默认已经安装好GCC编译器,可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径:

GCC编译工具链:

GCC编译工具链(toolchain),是指以GCC编译器为核心的一整套工具。它主要包含以下三部分内容:

  • gcc-core:即GCC编译器,用于完成预处理和编译过程,把C代码转换成汇编代码。
  • Binutils :除GCC编译器外的一系列小工具包括了链接器ld,汇编器as、目标文件格式查看器readelf等。
  • glibc:包含了主要的 C语言标准函数库,C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。

在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。

Binutils工具集:

Binutils(bin utility),是GNU二进制工具集,通常跟GCC编译器一起打包安装到系统,它的官方说明网站地址为: https://www.gnu.org/software/binutils/ 。

在进行程序开发的时候通常不会直接调用这些工具,而是在使用GCC编译指令的时候由GCC编译器间接调用。下面是其中一些常用的工具:

  • as:汇编器,把汇编语言代码转换为机器码(目标文件)。
  • ld:链接器,把编译生成的多个目标文件组织成最终的可执行程序文件。
  • readelf:可用于查看目标文件或可执行程序文件的信息。
  • nm : 可用于查看目标文件中出现的符号。
  • objcopy: 可用于目标文件格式转换,如.bin 转换成 .elf 、.elf 转换成 .bin等。
  • objdump:可用于查看目标文件的信息,最主要的作用是反汇编。
  • size:可用于查看目标文件不同部分的尺寸和总尺寸,例如代码段大小、数据段大小、使用的静态内存、总大小等。

系统默认的Binutils工具集位于/usr/bin目录下,可使用如下命令查看系统中存在的Binutils工具集:

# 在Ubantu上执行如下命令
ls /usr/bin/ | grep linux-gnu-

 

glibc库:

glibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库,因为绝大部分C程序都依赖该函数库,该文件甚至会直接影响到系统的正常运行,例如常用的文件操作函数read、write、open,打印函数printf、动态内存申请函数malloc等。

在Ubuntu系统下,libc.so.6是glibc的库文件,可直接执行该库文件查看版本,在主机上执行如下命令:

# 在Ubantu上执行如下命令
# 以下是Ubuntu 64位机的glibc库文件路径,可直接执行
/lib/x86_64-linux-gnu/libc.so.6

 


2. GCC编译

编写HelloWorld文件:

 #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;
 }

编译并执行:

Ubuntu默认安装GCC编译工具链,写好程序后可以直接进行编译,执行以下命令:

 # 在Ubantu的hello_c目录下执行如下命令
 gcc hello.c –o hello #使用gcc把hello.c编译成hello程序
 ​
 ls        #查看目录下的文件
 ./hello   #执行生成的hello程序
 ​
 # 若提示权限不够或不是可执行文件,执行如下命令再运行hello程序
 chmod u+x hello #给hello文件添加可执行权限

GCC 编译工具链在编译一个C源文件时需要经过以下 4 步:

  • 预处理:为把头文件的代码、宏之类的内容转换成生成的.i文件,还是C代码。
  • 编译:把预处理后的.i文件通过编译成.s文件,汇编语言。
  • 汇编:将汇编语言文件生成目标文件.o文件,机器码。
  • 链接:将每个源文件对应的.o文件链接起来,就生成一个可执行程序文件。

(1)预处理阶段

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

使用GCC的参数 “-E”,可以让编译器生成 .i 文件,参数 “-o”,可以指定输出文件的名字。

具体命令如下:

 # 预处理,可理解为把头文件的代码汇总成C代码,把*.c转换得到*.i文件
 gcc –E hello.c –o hello.i

编译生成的hello.i文件内容如下:

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

(2)编译阶段

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

GCC可以使用-S选项,让编译程序生成汇编语言的代码文件(.s后缀)。

具体命令如下:

 # 编译,可理解为把C代码转换为汇编代码,把*.i转换得到*.s文件
 gcc –S hello.i –o hello.s
 ​
 # 也可以直接以C文件作为输入进行编译,与上面的命令是等价的
 gcc –S hello.c –o hello.s

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

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

(3)汇编阶段

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

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

 # 汇编,可理解为把汇编代码转换为机器码,把*.s转换得到*.o,即目标文件
 gcc –c hello.s –o hello.o
 ​
 # 也可以直接以C文件作为输入进行汇编,与上面的命令是等价的
 gcc –c hello.c –o hello.o

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

请试执行如下命令:

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

(4)链接阶段

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

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

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

链接分为两种:

  • 动态链接:GCC编译时的默认选项。动态是指在应用程序运行时才去加载外部的代码库,不同的程序可以共用代码库。 所以动态链接生成的程序比较小,占用较少的内存。
  • 静态链接:链接时使用选项 “--static”,它在编译阶段就会把所有用到的库打包到自己的可执行程序中。 所以静态链接的优点是具有较好的兼容性,不依赖外部环境,但是生成的程序比较大。

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

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

从图中可以看到,使用动态链接生成的hello程序才8.3KB, 而使用静态链接生成的hello_static程序则高达845KB。

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

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

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


3. 交叉编译

如果我们希望编译器运行在x86架构平台上,然后编译生成ARM架构的可执行程序,这种编译器和目标程序运行在不同架构的编译过程,被称为 交叉编译。

(1)安装ARM-GCC

 # 在主机上执行如下命令
 sudo apt install gcc-arm-linux-gnueabihf
 ​
 # 安装完成后使用如下命令查看版本
 arm-linux-gnueabihf-gcc –v

安装完成后输入“arm-linux-gnueabihf-”,再按两下TAB键,终端会提示可用的相关命令,如下图包含了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

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

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

  • arm:表示它的目标芯片架构为ARM
  • linux:目标操作系统为Linux
  • gnu:使用GNU的C标准库即glibc
  • eabi:使用嵌入式应用二进制接口(eabi)
  • hf:编译器的浮点模式为硬浮点hard-float

好了,以上是GCC的基本概念和使用,希望对你有用。

标签:基本,文件,GCC,链接,编译,编译器,使用,hello
From: https://www.cnblogs.com/peifx/p/17472888.html

相关文章

  • Nginx用作反向代理服务器使用!
        Nginx("enginex")是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。Nginx是由IgorSysoev为俄罗斯访问量第二的Rambler.ru站点开发的,它已经在该站点运行超过三年了。Igor将源代码以类BSD许可证的形式发布。Nginx超越Apache的高性......
  • Linux下六个有关file使用的实例
    简介file的官方解释为:file - determine file type也就是说可以识别文件类型的意思,也可用来辨别一些文件的编码格式。它是通过查看文件的头部信息来获取文件类型,而不是像Windows通过扩展名来确定文件类型的,所以加不加后缀真的无所谓,谁会爱上谁,说起Windows吗,啥也不说了。实例下面......
  • golang之channel的使用
    golang之channel的使用在当今快速发展的软件开发领域,使用高效且可靠的编程语言变得尤为重要。而golang(又称Go语言)正是一种备受欢迎的编程语言,它的简洁、高效以及并发处理能力使得它在开发者中越来越受欢迎。在本文中,我们将探讨golang中的一个重要特性——channel,并介绍如何利用它......
  • selenim使用
    安装篇设备:MACphantomjs:https://phantomjs.org/download.html安装seleniumpipinstallselenium安装 chromedriver首先确认下载的是 与chrome版本相对应 的chromedriverhttps://chromedriver.storage.googleapis.com/index.html把chromedriver.exe文件放入/usr/local......
  • odoo 开发入门教程系列-模型和基本字段
    模型和基本字段在上一章的末尾,我们创建一个odoo模块。然而,此时它仍然是一个空壳,不允许我们存储任何数据。在我们的房地产模块中,我们希望将与房地产相关的信息(名称(name)、描述(description)、价格(price)、居住面积(livingarea)…)存储在数据库中。odoo框架提供了数据库交互的工具......
  • phonegap3.1.0自学笔记01_命令行界面(CLI)简单使用
    要使用phonegap的CLI必须首先安装好phonegap,phonegap的安装还请参看我的另外一篇文章:windows7搭建phonegap3Android开发环境。本篇文章介绍CLI的简单使用,由于本人水平有限,还请大侠不要拍砖。 phonegap3.1.0使用命令行去创建应用程序的框架,然后我们可以基于命令行创建的程序再去进......
  • [IOS]开源库RegexKitLite正则表达式的使用
    1.去RegexKitLite下载类库,解压出来会有一个例子包及2个文件,其实用到的就这2个文件,添加到工程中。2.工程中添加libicucore.dylibframeworks。友情提醒:一般人导入RegexKitLite编译报错,正是因为没有导入这个类库,加上这个就OK了3.现在所有的nsstring对象就可以调用RegexKitLite中......
  • 使用dbghelp获取调用堆栈--release下的调试方法学
    当软件作为release模式被发布给用户时,当程序崩溃时我们很难去查找原因。常见的手法是输出LOG文件,根据LOG文件分析程序崩溃时的运行情况。我们可以通过SEH来捕获程序错误,然后输出一些有用的信息作为我们分析错误的资料。一般我们需要输出的信息包括:系统信息、CPU寄存器信息、堆栈......
  • 【Linux中断】中断下半部-软中断softirq的原理与使用
    软中断软中断是中断下半部的典型处理机制,是随着SMP的出现应运而生的,也是tasklet实现的基础,软中断的出现是为了满足中断上半部和下半部的区别,使得对时间不敏感的任务延后执行,而且可以在多个CPU上并行执行,使得总的系统效率可以更高。软中断有以下特性:产生后并不是马上可以执行,必......
  • 在Transformers 中使用约束波束搜索引导文本生成
    引言本文假设读者已经熟悉文本生成领域波束搜索相关的背景知识,具体可参见博文如何生成文本:通过Transformers用不同的解码方法生成文本。与普通的波束搜索不同,约束波束搜索允许我们控制所生成的文本。这很有用,因为有时我们确切地知道输出中需要包含什么。例如,在机器翻译任......