文章目录
上篇文章:ARM 嵌入式 编译系列 2 – GCC 编译过程介绍
下篇文章:ARM 嵌入式 C 入门及渐进 3 – GCC attribute((weak)) 弱符号使用
1.1 GCC 编译参数
上篇文章 ARM 嵌入式 编译系列 2 – GCC 编译过程介绍 已经介绍过了具体的编译流程,本篇文章主要介绍变过程中常见的一些编译参数。
接下来还是以上篇文章中的编译脚本中的内容来展,会逐个介绍脚本中的编译参数及其使用背景。
if PLATFORM == 'gcc':
# toolchains
PREFIX = 'arm-none-eabi-' //指定交叉编译工具为arm-noe-eabi-
CC = PREFIX + 'gcc'
AS = PREFIX + 'gcc' //用于编译
AR = PREFIX + 'ar' //用于lib/.a文件的处理:添加/删除/生成/解压等
CXX = PREFIX + 'g++'
LINK = PREFIX + 'gcc'
TARGET_EXT = 'elf' //用于读取elf 头信息
SIZE = PREFIX + 'size'
OBJDUMP = PREFIX + 'objdump' //用于对elf文件进行反汇编等操作
OBJCPY = PREFIX + 'objcopy' //主要用于将elf文文件copy为binary文件
TOOLS_PATH = TOP_ROOT + '/tools/misc' //指定编译工具所在的路径
GENIMG = TOOLS_PATH + '/gen_rtos_img.sh' //指定mkimage工具所在的脚本
DEVICE = ' -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard \
-ffunction-sections -fdata-sections' //设定编译参数
CFLAGS = DEVICE + ' -Dgcc'
AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -Wa,-mimplicit-it=thumb '
LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rtthread.map,-cref,
-u,Reset_Handler -T board/link.lds' \
CPATH = ''
LPATH = ''
if BUILD == 'debug':
CFLAGS += ' -O0 -gdwarf-2 -g'
AFLAGS += ' -gdwarf-2' // 调试信息的存放格式按照 dwarf2标准来
else:
CFLAGS += ' -O2'
CXXFLAGS = CFLAGS
CFLAGS += ' -std=gnu99' //指定支持的C标准
CFLAGS += ' -Werror' // 将将警告升级为错误
上面编译脚本主要分为以下介个部分:
- 设置交叉编译工具链,这里使用的编译工具套件是
arm-noe-eabi-
; - CFLAGS 编译参数设置;
- LFLAGS 编译参数设置;
- AFLAGS 编译参数设置;
- CXXFLGAS 编译参数设置。
1.1.1 GCC arm-noe-eabi- 介绍
arm-none-eabi-
是一个通常用于嵌入式ARM开发的工具链前缀,其中:
- arm 表示这个工具链是为ARM架构设计的;
- none 表示这个工具链不是为特定的操作系统设计的;
- eabi 表示这个工具链支持"
Embedded Application Binary Interface
",这是ARM为嵌入式应用定义的一种二进制接口标准。
这个工具链通常包括GCC、GDB、Binutils等工具,每个工具的名称通常都会带有 arm-none-eabi-
这个前缀,例如 arm-none-eabi-gcc
、arm-none-eabi-gdb
、arm-none-eabi-as
等。
下面是一个使用 arm-none-eabi-gcc
编译ARM程序的例子:
arm-none-eabi-gcc -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard \
-ffunction-sections -fdata-sections -o output source.c
在这个命令中:
-mcpu=cortex-m7
告诉编译器目标CPU是Cortex-M7,,-mcpu
选项只是指定了CPU的类型,但并没有指定指令集。如果你需要指定指令集,可以使用-march
选项(用于指定指令集架构)和-mthumb
或-marm
选项(用于在ARM和Thumb指令集之间选择)。-mthumb
告诉编译器生成Thumb指令(ARM架构有两种指令集:ARM和Thumb);-mfpu=fpv5-d16
见:ARM 浮点计算单元 FPU (FPA,VFP,NEON)介绍;-mfloat-abi=hard
见:ARM 浮点计算单元 FPU (FPA,VFP,NEON)介绍;-ffunction-sections
:这个选项会让GCC为每个函数生成一个独立的段(section)。这样做的好处是,在链接时,如果某个函数没有被使用(即没有被其他代码引用),链接器可以将其丢弃,从而减小最终生成的可执行文件或库文件的大小。这在嵌入式系统编程中特别有用,因为嵌入式系统的存储空间通常比较有限。-fdata-sections
:这个选项会让GCC为每个数据对象(如全局变量)生成一个独立的段(section)。这样做的好处同样是在链接时,如果某个数据对象没有被使用,链接器可以将其丢弃。
需要注意的是,如果你使用了-fdata-sections 和 -ffunction-sections 这两个选项,那么在链接时,你需要使用链接器的–gc-sections选项,告诉链接器进行未使用的段的丢弃。
1.1.1.1 ARM 和 Thumb 指令集区别
ARM和Thumb是ARM处理器支持的两种指令集。它们之间的主要区别在于指令的大小和复杂性:
-
ARM指令集:ARM指令集中的指令是32位的,它提供了大量的寻址模式和操作类型,因此可以编写出非常复杂和高效的代码。但是,由于指令的大小是32位,所以它需要更多的存储空间和更大的代码缓存。
-
Thumb指令集:Thumb指令集中的指令是16位的,它的设计目标是实现代码的高密度,即在同样大小的存储空间中可以存储更多的代码。但是,由于指令的大小只有16位,因此Thumb指令集的寻址模式和操作类型都比ARM指令集少。这意味着,相同的任务,使用Thumb指令集可能需要更多的指令来完成。
从ARMv7开始,ARM处理器引入了Thumb-2技术,这是ARM和Thumb的混合指令集,它在实现高代码密度的同时,也提供了与ARM指令集相当的性能。
在编写代码或者配置编译器时,可以根据需要选择使用ARM指令集或者Thumb指令集。例如,如果存储空间非常紧张,可能会优先选择Thumb指令集;而如果对性能有较高要求,可能会选择ARM指令集。
1.1.2 GCC CFLAGS 介绍
在使用GCC编译ARM架构的程序时,使用CFLAGS环境变量来指定编译器选项。这些选项可以控制优化级别、调试信息、警告级别等。
例如,你可以如下设置 CFLAGS
(上面一节中的内容也属于CFLAGS):
CFLAGS="-O2 -g -Wall -Werror -std=gnu99 -Dgcc"
在这个例子中:
-
-O2
:这是一个优化级别选项。O2表示使用二级优化,这是GCC默认的优化级别,会提供很好的性能,同时不会牺牲太多的编译时间和可调试性;还可以选择-O3
-O0
。 -
-g
:这个选项会让GCC生成调试信息。这样,即使你的程序被优化,你仍然可以使用gdb等调试器来调试它。 -
-Wall
:这个选项会让GCC生成所有的警告信息。使用这个选项可以帮助你发现代码中的潜在问题。 -
-Werror
:这个选项会将所有的警告转变为错误。也就是说,如果编译器遇到任何警告,它都会停止编译过程。这个选项可以帮助你确保代码中没有任何可能的问题,因为所有的警告都必须被解决。 -
-std=gnu99
:这个选项指定了C语言的标准版本。gnu99代表GNU版本的C99标准。C99标准引入了许多新的语言特性,如内联函数、变长数组、新的数据类型等。gnu99指定了一些额外的GNU特性,如asm
、typeof
等。 -
-D
:这是一个预处理器选项,用于定义宏。例如,-Dgcc
会定义一个名为gcc的宏,其值为1
。你可以在源代码中使用#ifdef gcc
、#if defined(gcc)
或者#if gcc
来检查这个宏是否被定义。
上面编译参数设置好后,可以使用GCC来编译你的程序,如:
arm-none-eabi-gcc ${CFLAGS} -o output source.c
这将会编译source.c
并生成一个可执行文件output
,并使用你在CFLAGS
中指定的编译器选项。
注意,arm-none-eabi-gcc是用于编译ARM架构代码的GCC版本,你需要确保你的系统中已经安装了它。你也需要根据你的实际情况调整CFLAGS的设置。
1.1.3 GCC LDFLAGS 介绍
在使用GCC编译ARM架构的程序时,可以使用LDFLAGS
环境变量来指定链接器选项。这些选项可以控制链接器的行为,如指定要链接的库、库的搜索路径、生成 Map 文件等。
如下设置 LDFLAGS
:
export LDFLAGS="-lm -L/my/lib/path -Wl,--gc-sections,-Map=rtthread.map,-cref,-u, \
Reset_Handler -T board/link.lds"
在这个例子中:
-
-lm
:这个选项告诉链接器链接数学库(libm)。在你的程序中,如果使用到了数学函数,如sqrt、sin等,需要链接数学库; -
-L/my/lib/path
:这个选项用于指定库的搜索路径。链接器会在这个路径中查找需要的库。你需要根据你的实际情况指定这个路径; -
–gc-sections
:见 1.1.1 接介绍; -
-Wl,-Map,output.map
:这个选项会生成一个Map
文件output.map
,其中包含了链接后的程序的内存布局信息。这对于调试和分析程序很有帮助。关于map 文件详细信息见:ARM 嵌入式 编译系列 2 – GCC 编译过程介绍 -
-cref
:这个选项会告诉链接器生成一个交叉引用表,这个表列出了哪些符号定义在哪里,哪些地方使用了这些符号。这个信息对于调试和分析程序非常有帮助。生成的交叉引用表会在链接器的输出中显示; -
-u symbol
:这个选项会告诉链接器,无论symbol是否被使用,都将其从库中链接到最终的可执行文件中。这对于强制链接未被直接引用的符号非常有用。-u Reset_Handler
告诉链接器,无论Reset_Handler
是否被使用,都将其从库中链接到最终的可执行文件中; -
-T
选项可以用来指定自定义的链接脚本,在链接过程中,链接器需要一个链接脚本来控制输出文件的内存布局。链接脚本定义了输出文件中节(section)的顺序和位置,以及如何将输入文件的节映射到输出文件的节。可以在链接脚本中定义内存区域,指定各个节的位置和顺序,定义全局符号等。
然后,你可以使用GCC来编译你的程序,如:
arm-none-eabi-gcc ${CFLAGS} ${LDFLAGS} -o output source.c
这将会编译 source.c
并生成一个可执行文件output
,并使用你在CFLAGS
和LDFLAGS
中指定的编译器和链接器选项。
注意,arm-none-eabi-gcc是用于编译ARM架构代码的GCC版本,你需要确保你的系统中已经安装了它。你也需要根据你的实际情况调整 LDFLAGS 的设置。
1.1.4 CXXFLAGS 介绍
上文编译脚本中没有怎么用到CXXFLAGS,这里只是简单介绍下。
CXXFLAGS
:这个变量用于C++编译器。当你使用G++或类似的工具编译C++程序时,会使用这个变量中的选项。
在编译过程中,如果C和C++源文件混合在一起,那么C文件会使用CFLAGS,C++文件会使用CXXFLAGS。因此,你可以为C和C++编译器指定不同的选项,例如,你可能想要在编译C++代码时启用某些只有C++编译器支持的选项。
注意,虽然许多选项在C和C++编译器之间是通用的,但也有一些选项只对其中一个有效。所以你应根据需要为CFLAGS和CXXFLAGS选择合适的选项。
上篇文章:ARM 嵌入式 编译系列 2 – GCC 编译过程介绍
下篇文章:ARM 嵌入式 C 入门及渐进 3 – GCC attribute((weak)) 弱符号使用