前言
网上对于嵌入式发展的介绍,以及ARM处理器的介绍等都已经有很多了,所以这里也不进行更多的介绍了。同时对于环境的搭建,后期单独发一篇博客进行操作的展示。本文主要是从基础的第一节开始,对交叉编译工具链和gcc的基本概念进行讲解。 参考书籍《嵌入式Linux应用开发》韦东山著
一、交叉编译工具链的配置
因为我们最终是在开发板上运行我们的程序,而我们的PC用的是x86架构(CISC,复杂指令集架构),而我们的开发板是基于(RISC, 精简指令集架构)。所以我们直接在虚拟机上通过
gcc -o xxx xxxx.c
生成的可执行文件在开发板上是无法直接运行的。所以我们需要配置交叉编译工具链,生成适用于ARM架构的可执行文件。
具体的配置方法: 下载百问网的任意一块板子的开发资料 http://download.100ask.net/boards/St/100ask_stm32mp157_pro/index.html 其中有这样的一个文件 打开后,里面对应有一个开发板完全手册 打开,找到第8章就是对应的配置方法 特别注意:在最后导入这个环境变量的时候,一定要将这个用户名更改一下,他的是book,你要改为你的用户名,否则后序进行检测没有效果 对于arm-buildroot-linux-gunueabihf-gcc的选项和使用下面我们将进行介绍。
二、编译器的使用
1.gcc编译的过程:
一个文件要经过预处理、编译、汇编、链接4个步骤才能变成一个可执行文件。 这是我写的一个程序hello.c 运行结果: 希望你能对这个main的参数有一个简单的理解。
你可以使用指令来查看生成可执行文件的整个过程
gcc -o hello hello.c -v
这里面有很多的配置信息,你可以参考下图去验证这个过程
2.gcc编译选项
在只知道了我们编译的整个过程后,就会有疑问了,为什么我们看不见这个过程中生成的xxx.i,xxx.S,xxx.o文件呢?
因为我们平时使用的gcc -o xxx xxx.c指令-c这一个选项就把整个预处理,编译,汇编,链接给都实现了,那么期间生成的临时文件自然就没必要留着只需要保存最后的可执行文件。
所以我们如果要查看这个过程中的文件该怎么办呢?
- 预处理xxx.i文件的生成
预处理的作用:预处理的作用是为了展开我们的宏,以及导入我们的库文件比如我们的<stdio.h> 和定义的MAX,MIN。以及优化掉没有实际用的代码(如果你就是要保留,不允许编译器优化,可以使用volatile关键字)hello.i文件内容见图三 使用指令如下
图一
gcc -E -o hello.i hello.c
图二 图三 上面部分就是头文件的引入,期间的宏定义因为我没有使用,所以编译器就给我优化掉了,如果我使用了,他就会把MAX,MIN展开为20和10.
- 编译 xxx.S文件的生成
编译的作用:编译的作用主要是对编译的代码中有语法错误,进行检测。同时将文件生成一个汇编代码文件。(见图三) 使用指令(图一)
图一
gcc -S -o hello.s hello.i
图二 图三 里面都是一些汇编指令,特别注意,语法错误是在编译过程中,不是预处理。预处理只是进行优化。
- 汇编
汇编的功能:汇编的功能就是将.S中的汇编码转换为机器读的机器码。(见图三) 使用指令(见图一) 图一
gcc -c -o hello.o hello.S
图二 图三
- 链接
链接的作用:链接的作用就是在你执行你的文件的时候,还需要使用到其他的文件的函数。那么就需要将另一个文件和自己写的这个文件链接起来,这样编译器才知道你这个函数是怎么用的,这个程序才能正确的执行() 使用指令(图一) 图一
gcc -o test hello.o xxx.o
这里就不展示了,后面有时间再回来给大家修改验证 链接的时候,又要分链接的是静态库,还是动态库 静态库:静态库就是在你程序执行的时候需要用,完整的把这个库文件代码考入到你的可执行文件中。如果你的这个库被多次调用相应的你的文件占用的内存也就越多 动态库:动态库就是在你程序执行的时候,系统动态自动加载到内存,不用完整拷贝,所以很节省内存。
制作和使用静态库的方法: 使用指令
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
ar crs libsub.a sub.o sub2.o sub3.o(可以使用多个.o生成静态库)
gcc -o test main.o libsub.a (如果.a不在当前目录下,需要指定它的绝对或相对路径)
生成的是一个xxx.a文件
制作和使用动态库的方法
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -shared -o libsub.so sub.o sub2.o sub3.o(可以使用多个.o生成动态库)
gcc -o test main.o -lsub -L /libsub.so/所在目录/
生成的是一个xxx.so文件
- 头文件的介绍
在我们编写程序的时候,常常会引入头文件,比如 #include<stdio.h> #include “xxx.h”
这里的 “xxx.h"中的” "表示的是该文件在当前目录下 这个的<stdio.h>的<>表示的是在系统目录下
。 通常我们也可以使用一些选项来指定我们的头文件路径,同理我们链接时的库文件也是一个道理。 方法一:通过指令 -L选项来指定链接的库文件的目录位置,-l选项来指定哪一个库文件 - I来指定头文件的位置。
方法二:你可以将你需要链接的文件,或者头文件放到上面的这个系统目录中
gcc -E main.c // 查看预处理结果,比如头文件是哪个
gcc -E -dM main.c > 1.txt // 把所有的宏展开,存在1.txt里
gcc -Wp,-MD,abc.dep -c -o main.o main.c // 生成依赖文件abc.dep,后面Makefile会用
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)
总结
补充选项
-
Wall 这个选项基本打开了所有需要注意的警告信息,比如没有指定类型的声明、在声明之前就使用的函数、局部变量除了声明就没再使用等。 这个警告虽然对程序没有坏的影响,但是有些警告需要加以关注,比如类型匹配的警告等。 -
-g调试 以操作系统的本地格式(stabs,COFF,XCOFF,或DWARF)产生调试信息,GDB能够使用这些调试信息。在大多数使用stabs格式的系统上,-g'选项加入只有GDB才使用的额外调试信息。可以使用下面的选项来生成额外的信息: -gstabs+’,-gstabs', -gxcoff+’,-gxcoff', -gdwarf+‘或`-gdwarf’,具体用法请读者参考GCC手册。 -
-O或-O1 优化选项 优化:对于大函数,优化编译的过程将占用稍微多的时间和相当大的内存。不使用-O'或 -O1’选项的目的是减少编译的开销,使编译结果能够调试、语句是独立的:如果在两条语句之间用断点中止程序,可以对任何变量重新赋值,或者在函数体内把程序计数器指到其他语句,以及从源程序中精确地获取你所期待的结果。 不使用-O'或 -O1’选项时,只有声明了register的变量才分配使用寄存器。 使用了-O'或 -O1’选项,编译器会试图减少目标码的大小和执行时间。如果指定了-O'或 -O1’选项,,-fthread-jumps'和 -fdefer-pop’选项将被打开。在有delay slot的机器上,-fdelayed-branch'选项将被打开。在即使没有帧指针 (frame pointer)也支持调试的机器上, -fomit-frame-pointer’选项将被打开。某些机器上还可能会打开其他选项。 -O2 多优化一些。除了涉及空间和速度交换的优化选项,执行几乎所有的优化工作。例如不进行循环展开(loop unrolling)和函数内嵌(inlining)。和-O'或 -O1’选项比较,这个选项既增加了编译时间,也提高了生成代码的运行效果。 -O3 优化的更多。除了打开-O2所做的一切,它还打开了-finline-functions选项 -O0 不优化
希望上面的内容能对你有多帮助!!!
|