关注+星标公众号,不错过精彩内容
来源 | 瑞萨嵌入式小百科
1
GCC工具安装与目录结构
GCC工具的安装比较简单,解压即可。对于开发者,我们只关心它的3个目录:bin、include、lib。bin目录下是各类GCC工具,include目录下是头文件,lib目录下是库文件。
以RemiPI为例,它的bin目录如下:
执行如下命令,可以看到RemiPI工具链的include目录、lib目录:
source /opt/remi-sdk/environment-setup-aarch64-poky-linux
echo 'main(){}'| $CC -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)
include目录如下:
lib目录如下:
2
GCC编译过程与编译选项
以下内容为例便于操作以GCC为例,对于交叉编译工具链也是一样的。
一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等4步才能变成可执行文件。
图9.1 编译过程
通过不同的gcc选项可以控制这些过程:
图9.2 gcc选项过程说明
在日常交流中通常使用“编译”统称这4个步骤,如果不是特指这4个步骤中的某一个,本教程也依惯例使用“编译”这个统称。
gcc使用示例:
gcc hello.c // 输出一个名为 a.out 的可执行程序,然后可以执行./a.out
gcc -o hello hello.c // 输出名为 hello 的可执行程序,然后可以执行./hello
gcc -o hello hello.c -static // 静态链接
gcc -c -o hello.o hello.c // 先编译(不链接)
gcc -o hello hello.o // 再链接
执行“gcc -o hello hello.c -v”时,可以查看到这些步骤:
int ioctl(cc1 main.c -o /tmp/ccXCx1YG.s
as -o /tmp/ccZfdaDo.o /tmp/ccXCx1YG.s
cc1 sub.c -o /tmp/ccXCx1YG.s
as -o /tmp/ccn8Cjq6.o /tmp/ccXCx1YG.sint fd, unsigned long request, ...);
collect2 -o test /tmp/ccZfdaDo.o /tmp/ccn8Cjq6.o ....
可以手工执行以下命令体验一下:
gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o
2.1 预处理
C/C++源文件中,以“#”开头的命令被称为预处理命令, 如包含命令“#include”、宏定义命令“#define”、条件编译命令“#if”、“#ifdef”等。预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些东西输出到一个“.i”文件中等待进一步处理。
2.2 编译
编译就是把C/C++代码(比如上述的“.i”文件)“翻译”成汇编代码,所用到的工具为cc1(它的名字就是cc1,x86有自己的cc1命令,ARM板也有自己的cc1命令)。
2.3 汇编
汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,Linux系统上一般表现为ELF目标文件(OBJ文件),用到的工具为as。x86有自己的as命令,ARM 版也有自己的as命令,也可能是xxxx-as(比如arm- linux-as)。
“反汇编”是指将机器代码转换为汇编代码,这在调试程序时常常用到。
2.4 链接
链接就是将上步生成的OBJ文件和系统库的OBJ文件、库文件链接起来,最终生成了可以在特定平台运行的可执行文件,用到的工具为ld或collect2。
编译程序时,加上-v选项就可以看到这几个步骤。比如:
gcc -o hello hello.c -v
可以看到很多输出结果,我们把其中的主要信息摘出来:
cc1 hello.c -o /tmp/cctETob7.s
as -o /tmp/ccvv2KbL.o /tmp/cctETob7.s
collect2 -o hello crt1.o crti.o crtbegin.o /tmp/ccvv2KbL.o crtend.o crtn.o
以上3个命令分别对应于编译步骤中的预处理+编译、汇编和链接,ld被collect2调用来链接程序。预处理和编译被放在了一个命令(cc1)中进行的,可以把它再次拆分为以下两步:
cpp -o hello.i hello.c
cc1 hello.i -o /tmp/cctETob7.s
我们不需要手工去执行cpp、cc1、collect2等命令,我们直接执行gcc并指定不同的参数就可以了。
可以通过如错误!未找到引用源。中的各种选项来控制 gcc的动作。
3
GCC常用命令
以下内容为例便于操作以GCC为例,对于交叉编译工具链也是一样的。
3.1 怎么编译多个文件
①一起编译、链接:
gcc -o test main.c sub.c
②分开编译,统一链接:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
3.2 制作、使用动态库
第1步 制作、编译:
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/所在目录/
第2步 运行:
①先把libsub.so放到Ubuntu的/lib目录,然后就可以运行 test程序。
②如果不想把libsub.so放到/lib,也可以放在某个目录比如/a,然后如下执行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/a
./test
3.3 制作、使用静态库
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 不在当前目录下,需要指定它的绝对或相对路径)
运行:不需要把静态库libsub.a放到板子上。
注意:
执行arm-buildroot-linux-gnueabihf-gcc -c -o sub.o sub.c交叉编译需要在最后面加上-fPIC参数。
3.4 很有用的选项
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)
单片机和PHY芯片连接时的注意事项
瑞萨RA8系列教程 | RA8输出PWM实现方法
Cortex-M3与M85单片机的SysTick使用上有区别吗?