首页 理论教育 桌面Linux开发工具链-嵌入式系统原理与应用

桌面Linux开发工具链-嵌入式系统原理与应用

时间:2023-11-23 理论教育 版权反馈
【摘要】:它兼容于ANSI C,并且对其进行了扩展,以适合Linux 自身的一些特点。在使用gcc 时,必须要给出必要的选项和文件名。运行上述命令时,首先,gcc 运行预处理程序cpp 来展开sample1.c 中的宏,并在其中插入#include 文件所包含的内容;然后,将预处理后的源代码编译成为目标代码;最后,链接程序ld创建一个名为“sample1”的二进制可执行文件。通过文件的扩展名,gcc能判断文件的类型并决定以何种方式对文件进行处理。

桌面Linux开发工具链-嵌入式系统原理与应用

GNU 开发工具链主要包括GNU Compiler Collection、GNU libc,以及用来编译、测试和分析软件的GNU binutils 这三个大的模块。

(1)gcc 编译器

gcc 是GNU 公司的一个项目,是一个用于编程开发的自由编译器,最初,gcc 只是一个C语言编译器,是“GNU C Compiler”的英文缩写,随着众多自由开发者的加入和gcc 自身的发展,如今的gcc 已经是一个包含众多语言的编译器了,其中包括C、C + +、Ada、Object C 和Java等。 因此,gcc 也由原来的“GNU C Compiler”变为“GNU Compiler Collection”。

gcc 编译器将编译过程分成四个阶段,即预处理、编译、汇编和链接。 使用gcc 编写的代码可在任意一个编译阶段暂停编译,并检查相应的输出信息。 gcc 还能在生成的二进制文件中添加所需数量和种类的调试信息。 编译器同时还可以带上不同的优化选项,以根据不同的需求优化可执行代码。

gcc 是一个交叉平台编译器,能够在异构体系硬件系统的CPU 平台上开发应用程序。 它兼容于ANSI C,并且对其进行了扩展,以适合Linux 自身的一些特点。 gcc 还对普通的C 和C+ +进行了大量扩展,这种做法有助于方便代码的编写,有助于编译器进行代码优化,也有助于提高代码的执行效率,但以降低gcc 的可移植性为代价。

1)gcc 使用简介

gcc 通过在命令行上输入命令完成对源文件的预处理、编译、汇编与链接等操作,它虽然没有Windows 的窗口使用起来方便、简洁,但是它能更深入地与硬件密切结合进行文件处理。gcc 的使用格式如下:

其中的“option”是以“-”开始的各种选项。 第一个“file”,表明了要处理的文件对象,它有时也可以放在“option”之后;第二个“file”,表示完成“option”之后要生成的目标文件名,它有时可以省略,这时使用系统默认的设置。 在使用gcc 时,必须要给出必要的选项和文件名。前面提到gcc 编译过程分四个阶段,具体完成哪一步,是由gcc 后面的开关选项和文件类型决定的。

gcc 编译器有许多选项,但对于普通用户来说,只要知道其中常用的几个就够了。 以下列出几个最常用的选项:

-o 选项,表示要求编译器生成指定文件名的可执行文件。

-E 选项,表示编译器对源文件只进行预处理就停止,而不进行编译、汇编和链接。

-S 选项,表示编译器只进行编译,而不进行汇编和链接。

-c 选项,表示只要求编译器进行编译,而不进行链接,生成以源文件的文件名命名,但将其后缀由“.c”或“cc”变成“.o”的目标文件。

-g 选项,要求编译器在编译时提供以后对程序进行调试的信息。

-O 选项,它是编译器对程序提供的编译优化选项,在编译时使用该选项,可使生成的执行文件的执行效率提高。

-WALL 选项,指定产生全部的警告信息。

-v 选项,显示在编译过程的每一步中用到的命令。

-Static 选项,表示链接静态库,即执行静态链接。

为了加深读者对gcc 应用的了解,举例如下:

在命令行上键入以下命令编译和运行这段程序:

第一行命令告知gcc 对源程序sample1.c 进行预处理、编译和链接,并使用“-o”选项指定创建名为“sample1”的可执行程序,当不用任何选项编译一个程序时,gcc 将创建默认的可执行文件a.out(a.out 是Linux 中使用的一种通用文件格式,现在Linux 的标准格式为ELF 格式)。 第二行命令执行sample1 这个程序,第三行是程序的执行结果。

注意:当使用“-o”选项时,“-o”后面必须跟一个文件名,对输出的可执行文件进行命名。

运行上述命令时,首先,gcc 运行预处理程序cpp 来展开sample1.c 中的宏,并在其中插入#include 文件所包含的内容;然后,将预处理后的源代码编译成为目标代码;最后,链接程序ld创建一个名为“sample1”的二进制可执行文件。

如前所述,在编译过程中,可以通过手工操作重新创建这些步骤,以逐步执行编译过程。比如,gcc 的“-E”选项,可以使gcc 在预处理后停止编译过程。

第一个命令生成sample1.cpp 文件,这个文件中包含了文件stdio.h 的内容以及其他一些预处理符号信息。 第二个命令是由预处理文件sample1.cpp 生成目标代码文件sample1.o,其中的选项“-c”表示编译器只编译不链接,“-x”选项表示编译器从指定的步骤开始编译,本例是从预处理后的源代码开始编译。 第三个命令就是链接目标文件生成二进制代码文件。

由预处理文件sample1.cpp 生成目标代码文件sample1. o 时,可以不指定目标代码文件名,在默认情况下,gcc 会通过预处理文件名加上扩展名“. o”得到。 通过文件的扩展名,gcc能判断文件的类型并决定以何种方式对文件进行处理。 gcc 可以处理扩展名为以下类型的文件:

以上展示了分步获得源代码文件的可执行代码的步骤,这里分步执行只是说明在必要时可以在编译的任何阶段停止或开始编译过程。 在开发应用时,并不推荐分步对源代码进行操作,只有在某些特殊情况下才控制编译过程,以实现特定的功能。

2)常用命令行选项

①函数库和包含文件

如果需要链接的函数库或包含的文件不在标准目录下,可以使用“-L{DIRNAME}和-I{DIRNAME}”选项指定文件所在的目录,以确保该目录的搜索顺序在标准目录之前。 例如,如果读者将自定义头文件放置在/usr/local/include/killerapp 目录下,则为了使gcc 能够找到这些文件,可以使用下面的命令行:

#gcc sample1.c -I /usr/local/include/killerapp

再如,程序开发者需要测试在/home/fred/lib 目录下的新函数库libnew.so,同时所有需要的头文件在/home/fred/include 目录下,可以使用下面的代码行链接该函数库与定制头文件:

#gcc sample2.c -L /home/fred/lib -I /home/fred/include -lnew

“-I”选项使得链接程序使用指定的函数库中的目标代码,也就是本例中的libnew.so。

在默认情况下,gcc 只链接动态共享库,如果需要链接静态库文件,应使用“-Static”选项声明。 由于静态库中的可执行程序要比动态库大很多,因此会占用更多的内存。 链接静态库后,程序在任何情况下都可以运行,而链接动态共享库的程序只有在系统内包含了所需的共享库时才可以运行,也就是当程序运行时才动态地链接动态库,否则,运行就会失败。

②优化选项

用gcc 编译C/C+ +代码时,它会试着用最少的时间完成编译,并且使编译后的代码易于调试。 易于调试,意味着编译后的代码与源代码有同样的控制流程,编译后的代码没有经过优化。(www.xing528.com)

通过更长的编译时间和在编译期间使用更多的内存,gcc 的代码优化可以提高程序的执行效率。 gcc 提供了许多优化选项,最典型的是“-O”(即optimize,优化之意)和“-O2”选项。

“-O”选项告诉gcc 对源代码的代码长度和执行时间进行优化。 “-O”选项同“-O1”,一般包括线程直接跳转和这两种优化。 线程直接跳转优化可以减少跳转的次数,针对每次函数调用完成后,都需要弹出栈中的函数参数,延迟退栈是通过在嵌套调用时推迟退栈的时间而优化运行效率,优化后在栈中保留了参数,直到完成所有递归调用后才同时弹出栈中累积的函数参数。 “-O2”选项告知gcc 产生尽可能小而快的代码。 gcc 执行这个选项时,编译器保证处理器在等待其他指令的结果或来自高速缓存或主内存的数据延迟时,仍然有可执行的指令,其实现与处理器密切相关。 “-O2”选项将使编译的速度比使用“-O”时慢,但通常产生的代码执行速度更快。 “-O3”包括所有“O2”级优化,循环展开,涉及其他的与处理器有关的优化。

除了“-O”和“-O2”选项外,还有一些其他选项用于产生更快的代码。 这些选项非常特殊,而且只有完全理解这些选项将会对编译后的代码产生什么样的效果时再去使用。 这些选项的详细描述,参考gcc 的指南页,在命令行上键入“man gcc”命令,可以得到gcc 的帮助。

③调试选项

在程序执行过程中,或多或少的错误是不可避免的,为了在遇到问题时gcc 能够自动地调试代码,在执行文件中可以包含标准调试符号(使用“-g”选项)。 在编译后的程序中插入调试信息,可以更方便地调试程序。

可以通过在“-g”选项后附加1、2 或3 来指定在代码中加入调试信息的数量级别,1 级选项仅生成必要的信息,以创建回退和堆栈转储;缺省的级别是2(“-g2”),此时调试信息应该包括扩展的符号表、行号和局部或外部变量信息。 3 级选项包含所有2 级的调试信息及所有的宏定义。 命令如下:

如果所使用的调试器是GNU Debugger,gdb,需要用-ggdb 选项来创建额外调试信息,以方便gdb 的使用。 但是,这样做也使得程序不能被其他调试器调试。

注意:程序调试和代码优化是不相容的两个过程,代码优化会破坏程序的控制流,使程序无法调试,因此,应在完成程序调试后再进行代码的优化工作。 这里所说的“优化”,仅指利用编译器gcc 所做的优化,在程序设计过程中的设计优化不算在此列。 实际上,如果有了简洁的设计和快速的算法,也就不需要再由编译器来优化代码。

(2)gcc 扩展

gcc 是Linux 专用的C 语言编译工具,它兼容于ANSI C 并在很多方面对ANSI C 进行了扩展。 本节将简单介绍Linux 的系统头文件和源代码中经常出现的扩展语法,而这些语法往往是常用的。 如果要了解gcc 的详细扩展语法,可以参考它的信息页。

gcc 使用long long 数据类型来创建64 位存储单元。 在x86 平台上利用long long 定义一个变量,就可以为它分配64 位存储空间。

linux 头文件中提供了内联函数。 内联函数会像宏一样在预处理阶段展开,这样就减少了调用的开销且提高了代码的执行速度。 另外,由于编译器在编译时能够对内联函数进行类型检查,因此使用内联函数比使用宏更安全。 不过,在编译时至少要使用“-O”,这样的优化选项才能使用内联函数。

gcc 扩展了关键字Attribute。 关键字Attribute 通过指明代码信息来帮助gcc 完成优化。许多标准的库函数没有返回值,比如:exit()和abort(),编译器会为没有返回值的函数生成较为优化的代码。 如果用户编写了没有返回值的函数,可以通过Attribute 来指明这一点,以生成较优化的代码,gcc 提供了noreturn 属性来标识这些函数,如果有了个名为“function_without_return()”且无返回值的函数,则可以使用如下关键字和属性来定义此函数,这样可告知gcc对此函数进行优化:

void function_without_return(void)__Attribute__((noreturn));

这个关键字还可以用于指定变量的属性。 例如,可以使用属性aligned 指定编译器在分配内存空间时按规定的边界对齐。 例如,

gcc 对case 语句也作了扩展。 在ANSI C 中,case 语句只能对应单个值的情形。 gcc 中的case 语句可以对应于一个范围。 边界值之间可以用空格或省略号分开,表示对应于一个范围。 例如:如果要对整型变量i 使用case 语句,则可以写成:

这段代码相当于ANSI C 中的如下代码:

由此可以看出,gcc 的case 区间的这种用法只是传统语法switch 语句的一种简写方式而已。

(2)Glibc

任何一个Unix 体系的操作系统都需要一个C 库,用于定义系统调用和其他一些基本的函数调用,例如open、malloc、printf 和exit 等,GNU Glibc 就是要提供这样一个用于GNU 系统,特别是GNU/ Linux 系统的C 库,glibc 最初设计就是可移植的,尽管它的源码体系非常复杂,但是仍然可以通过简单的configure/ make 来生成对应平台的C 函数库

(3)GNU binutils

GNU binutils 是一套用来构造和使用二进制文件所需要的工具,其中两个最为关键的binutils 是GNU 链接器和GNU 汇编程序,这两个工具是GNU 工具链中的两个完整部分,通常是由gcc 前端进行驱动的。 binutils 包含的程序通常有:

addr2line:将程序地址转换为文件名和行号,在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息,指出在给出的地址上是哪个文件以及行号。

ar:建立、修改、提取归档文件,归档文件是包含多个文件内容的一个大文件,其结构保证了可以恢复原始文件内容。

As:主要用来编译GNU C 编译器gcc 输出的汇编文件,产生的输出文件由链接器ld链接。

gasp:它是一个汇编语言宏预处理器。

gprof:显示程序调用段各种数据。

ld:将一些目标和归档文件结合在一起,重新定位数据,并链接符号引用。 通常,建立一个新编译程序的最后一步就是调用ld。

nm:列出目标文件中的符号。

biopy:将一个目标文件中的内容复制到另一个目标文件,objcopy 使用GNU BFD 库来读写目标文件,源文件和目的文件可以是不同的格式。

objdump:显示一个或更多目标文件的信息。 使用选项来控制其显示的信息。

ranlib:产生归档文件索引,并将其保存到这个归档文件中。

readelf:显示elf 格式的可执行文件的信息。

size:列出目标文件每一段的大小以及总体的大小,在默认情况下,对于每个目标文件或者一个归档文件中的每个模块只产生一行输出。

Strings:打印某个文件的可打印字符串,这些字符串最少4 个字符长,也可以使用选项“-n”设置字符串的最小长度。 在默认情况下,它只打印目标文件初始化和可加载段中的可打印字符:对于其他类型的文件,它打印整个文件的可打印字符,这个程序对了解非文本文件的内容很有帮助。

Strip:丢弃某些目标文件中的全部或特定符号,这些目标文件中可以包括归档文件,它至少需要一个目标文件名作为参数,它直接修改参数指定的文件,不为修改后的文件重新命名。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈