MEMORY | INTERRUPT | TIMER | 并发与同步 | 进程管理 | 调度 | uboot | DTB | ARMV8 | ATF | Kernel Data Structure | PHY | LINUX2.6 | 驱动合集 | UART子系统 | USB专题 |

UBOOT编译--- UBOOT的编译和链接选项详解(六)

1. 前言

 UBOOT版本:uboot2018.03,开发板myimx8mmek240。

2. 函数 cc-option

编译选项变量cc-option 定义在 scripts/Kbuild.include中:

# scripts/Kbuild.include
# output directory for tests below
TMPOUT := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/)

# try-run
# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
# Exit code chooses option. "$$TMP" is can be used as temporary file and
# is automatically cleaned up.
# modifed for U-Boot: prevent cc-option from leaving .*.su files
try-run = $(shell set -e;		\
	TMP="$(TMPOUT).$$$$.tmp";	\
	TMPO="$(TMPOUT).$$$$.o";	\
	TMPSU="$(TMPOUT).$$$$.su";	\
	if ($(1)) >/dev/null 2>&1;	\
	then echo "$(2)";		\
	else echo "$(3)";		\
	fi;				\
	rm -f "$$TMP" "$$TMPO" "$$TMPSU")
	
# cc-option
# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)

cc-option = $(call try-run,\
	$(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2))

2.1 command >/dev/null 2>&1

command >/dev/null 2>&1 == command 1>/dev/null 2>&1

  • ‘command’ : 表示shell命令或者为一个可执行程序;
  • ‘>’ : 表示重定向到哪里;
  • ‘/dev/null’ : 表示Linux的空设备文件;
  • ‘2’ : 表示标准错误输出;
  • ‘&1’ : &表示等同于的意思,2>&1,表示2的输出重定向等于于1;

(1)1>/dev/null:表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,不显示任何信息。
(2)2>&1:表示标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。

这条命令的意思就是:将错误输出2重定向到标准输出1,然后将标准输出1全部放到/dev/null文件,也就是清空。所以可以看出" >/dev/null 2>&1 "常用来避免shell命令或者程序等运行中有内容输出到终端。

2.2 cc-option解析

举例:

PLATFORM_CPPFLAGS += $(call cc-option,-marm,)

函数cc-option

  • 第一个参数赋给$ (1)(这里是指-marm),
  • 第二个参数给$(2)(这里为空)。

变量cc-option的值是函数try-run的执行结果,函数try-run又是$(shell ....)输出的结果;也就是if ...else...的结果。在函数try-run中

  • $(1) : $(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "
  • $(2) : 函数cc-option的 $(1) (这里是指-marm)
  • $(3) : 函数cc-option的 $(2) (这里为空)

如果函数try-run的$(1)能执行,那么echo $(2),否则echo $(3);echo 的值正是cc-option的值

综上:该例子的意思是如果交叉编译工具$(CC)支持cc-optionl函数的参数一表示的选项(也就是指-marm),那么cc-option函数的返回就是该选项(指-marm),否则返回的是call函数的参数二表示的选项。

cc-option:检测$(CC) 是否支持给定的选项

3. 平台代码重定位需要的编译选项$(PLATFORM_RELFLAGS))

#(1) 顶层config.mk
PLATFORM_RELFLAGS :=

#(2) arch/arm/config.mk
PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections \
		     -fno-common -ffixed-r9
PLATFORM_RELFLAGS += $(call cc-option, -msoft-float) \
      $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,))  //空
......
PLATFORM_RELFLAGS	+= $(LLVM_RELFLAGS)     //空
  
#(3) arch/arm/cpu/armv8/config.mk
PLATFORM_RELFLAGS += -fno-common -ffixed-x18

展开为:

PLATFORM_RELFLAGS= -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18

参数讲解见本文末尾参考小节

4. 平台代码预处理需要的编译选项$(PLATFORM_CPPFLAGS)

#(1) 顶层config.mk
PLATFORM_CPPFLAGS :=
......
ifdef FTRACE
PLATFORM_CPPFLAGS += -finstrument-functions -DFTRACE  //调试时使用,一般不会打开
endif
# Allow use of stdint.h if available
ifneq ($(USE_STDINT),)
PLATFORM_CPPFLAGS += -DCONFIG_USE_STDINT   //空
endif
RELFLAGS := $(PLATFORM_RELFLAGS)          //见上一小节

PLATFORM_CPPFLAGS += $(RELFLAGS)
PLATFORM_CPPFLAGS += -pipe
export PLATFORM_CPPFLAGS

#(2) arch/arm/config.mk
# Choose between ARM/Thumb instruction sets
ifeq ($(CONFIG_$(SPL_)SYS_THUMB_BUILD),y)
AFLAGS_IMPLICIT_IT	:= $(call as-option,-Wa$(comma)-mimplicit-it=always)
PF_CPPFLAGS_ARM		:= $(AFLAGS_IMPLICIT_IT) \
			$(call cc-option, -mthumb -mthumb-interwork,\
			$(call cc-option,-marm,)\
			$(call cc-option,-mno-thumb-interwork,)\
		)
else //走else分支
PF_CPPFLAGS_ARM := $(call cc-option,-marm,) \
		$(call cc-option,-mno-thumb-interwork,) //空
endif
......
# Try if EABI is supported, else fall back to old API,
# i. e. for example:
# - with ELDK 4.2 (EABI supported), use:
#	-mabi=aapcs-linux
# - with ELDK 4.1 (gcc 4.x, no EABI), use:
#	-mabi=apcs-gnu
# - with ELDK 3.1 (gcc 3.x), use:
#	-mapcs-32
PF_CPPFLAGS_ABI := $(call cc-option,\      //空
			-mabi=aapcs-linux,\
			$(call cc-option,\
				-mapcs-32,\
				$(call cc-option,\
					-mabi=apcs-gnu,\
				)\
			)\
		)
......		
PLATFORM_CPPFLAGS += -D__ARM__ //定义宏__ARM__
PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_ARM) $(PF_CPPFLAGS_ABI) //空

ifneq ($(CONFIG_SPL_BUILD),y)   //编译SPL的时候才会传纳-DCONFIG_SPL_BUILD
......
# The movt / movw can hardcode 16 bit parts of the addresses in the
# instruction. Relocation is not supported for that case, so disable
# such usage by requiring word relocations.
PLATFORM_CPPFLAGS += $(call cc-option, -mword-relocations)
PLATFORM_CPPFLAGS += $(call cc-option, -fno-pic)  //不编译SPL的时候支持
endif

#(3) arch/arm/cpu/armv8/config.mk
PF_NO_UNALIGNED := $(call cc-option, -mstrict-align) //支持
PLATFORM_CPPFLAGS += $(PF_NO_UNALIGNED)

#(4) arch/arm/Makefile
# This selects which instruction set is used.
arch-$(CONFIG_ARM64)		=-march=armv8-a -mgeneral-regs-only

# On Tegra systems we must build SPL for the armv4 core on the device
# but otherwise we can use the value in CONFIG_SYS_ARM_ARCH
ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TEGRA),yy)
arch-y += -D__LINUX_ARM_ARCH__=4
else
arch-y += -D__LINUX_ARM_ARCH__=$(CONFIG_SYS_ARM_ARCH)
endif

# Evaluate arch cc-option calls now
arch-y := $(arch-y) //-march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8

# Evaluate tune cc-option calls now
tune-y := $(tune-y)

PLATFORM_CPPFLAGS += $(arch-y) $(tune-y)

machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) //我使用的单板未定义machine-y

PLATFORM_CPPFLAGS += $(patsubst %,-I$(srctree)/%include,$(machdirs))//空

展开为:

PLATFORM_CPPFLAGS= -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8

参数讲解见本文末尾参考小节

5. 编译系统kbuild需要的预处理选项$(KBUILD_CPPFLAGS)

# /scripts/Makefile.spl
KBUILD_CPPFLAGS += -DCONFIG_SPL_BUILD

#(1) 顶层config.mk
KBUILD_CPPFLAGS := -D__KERNEL__ -D__UBOOT__ //定义__KERNEL__和__UBOOT__ 宏

# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
KBUILD_CPPFLAGS += $(KCPPFLAGS)
......

展开为:

KBUILD_CPPFLAGS=-D__KERNEL__ -D__UBOOT__

参数讲解见本文末尾参考小节

6. 编译系统kbuild需要的编译选项$(KBUILD_CFLAGS)

# /scripts/Makefile.spl
KBUILD_CFLAGS   := -Wall -Wstrict-prototypes \
		   -Wno-format-security \
		   -fno-builtin -ffreestanding
KBUILD_CFLAGS	+= -fshort-wchar
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS	+= -Os
else
KBUILD_CFLAGS	+= -O2
endif

KBUILD_CFLAGS += $(call cc-option,-fno-stack-protector)
KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks)

KBUILD_CFLAGS	+= -g
KBUILD_CFLAGS += $(call cc-option,-Wno-format-nonliteral)

# Prohibit date/time macros, which would make the build non-deterministic
KBUILD_CFLAGS   += $(call cc-option,-Werror=date-time)

# Report stack usage if supported
ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-stack-usage.sh $(CC)),y)
	KBUILD_CFLAGS += -fstack-usage
endif

#(1) 顶层config.mk
KBUILD_CFLAGS += $(KCFLAGS)

展开为:

KBUILD_CFLAGS=-Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time

参数讲解见本文末尾参考小节

7. 预处理需要的编译选项$(cpp_flags)

#(1) 顶层config.mk
# Use UBOOTINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
UBOOTINCLUDE    := \
		-Iinclude \
		$(if $(KBUILD_SRC), -I$(srctree)/include) \
		$(if $(CONFIG_$(SPL_)SYS_THUMB_BUILD), \
			$(if $(CONFIG_HAS_THUMB2),, \
				-I$(srctree)/arch/$(ARCH)/thumb1/include),) \
		-I$(srctree)/arch/$(ARCH)/include \          //-I./arch/arm/include
		-include $(srctree)/include/linux/kconfig.h  //-include ./include/linux/kconfig.h
		
......		

NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) 
// $(CC) -print-file-name=include : 打印GCC默认搜索include头文件的路径

......		

# FIX ME
cpp_flags := $(KBUILD_CPPFLAGS) $(PLATFORM_CPPFLAGS) $(UBOOTINCLUDE) $(NOSTDINC_FLAGS)

预处理编译选项$(cpp_flags)定义在顶层Makefile中,主要由:编译系统kbuild需要的预处理选项 $(KBUILD_CPPFLAGS)、平台代码预处理需要的编译选项 $(PLATFORM_CPPFLAGS)、UBOOT要使用的头文件路径、CC默认搜索include头文件的路径组成。

展开为:

cpp_flags=-D__KERNEL__ -D__UBOOT__ -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include

8. 编译需要的编译选项$(c_flags)

#(1) 顶层config.mk
c_flags := $(KBUILD_CFLAGS) $(cpp_flags)

编译选项$(c_flags)定义在顶层Makefile中,主要由 $(KBUILD_CFLAGS) (编译系统kbuild需要的编译选项)和 $(cpp_flags)(uboot整体代码预处理需要的编译选项)组成。

展开为:

c_flags=-Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__KERNEL__ -D__UBOOT__ -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include

参数讲解见本文末尾参考小节

9. 链接需要的编译选项$(LDFLAGS_FINAL)

#(1) 顶层config.mk
LDFLAGS_FINAL :=
......
LDFLAGS_FINAL += -Bstatic

export LDFLAGS_FINAL

#(2) arch/arm/config.mk
LDFLAGS_FINAL += --gc-sections

展开为:

LDFLAGS_FINAL= --gc-sections -Bstatic  //指定从-L指定的目录列表中查找libfoo.a

参数讲解见本文末尾参考小节

10. 参考

[1] -Wall:打开全部编译告警;
[2] -Wstrict-prototypes:如果在未指定参数类型的情况下声明或定义函数,则发出警告。 (如果前面有一个指定参数类型的声明,则允许在没有警告的情况下进行旧式函数定义);
[3] -Wformat、-Wformat=n、-Wformat-security:Wformat、-Wformat=n:检查对 printf 和 scanf 等的调用,以确保提供的参数具有适合指定格式字符串的类型,并且格式字符串中指定的转换是有意义的。这包括 printf、scanf、strftime 和 strfmon(X/Open 扩展,不在 C 标准中)系列(或其他特定于目标的系列)中的标准函数和其他由格式属性(请参阅函数属性)指定的函数。在没有指定格式属性的情况下检查哪些函数取决于所选的标准版本,并且对没有指定属性的函数的这种检查被 -ffreestanding 或 -fno-builtin 禁用;-Wformat-security:如果指定了“-Wformat”,还会警告使用表示可能的安全问题的格式函数。目前,这会警告调用 printf 和 scanf 函数,其中格式字符串不是字符串文字并且没有格式参数,如 printf (foo);
[4] -Wformat-nonliteral、-Wno-format-nonliteral :如果指定了 -Wformat,如果格式字符串不是字符串文字并且因此无法检查,也会发出警告,除非格式函数将其格式参数作为 va_list;
[5] -fno-builtin、-fno-builtin-function:不识别不以“builtin”作为前缀的内置函数。;
[6] -ffreestanding:编译输出(可执行文件)是运行在一个 freestanding environment 环境下。这个选项 包含 -fno-builtin 选项。等同于 -fno-hosted;
[7] -O -O0 -O1 -O2 -O3 -Os -Ofast -Og -Oz:-Os相当于-O2.5。是使用了所有-O2的优化选项,但又不缩减代码尺寸的方法;
[8] -fstack-protector、-fno-stack-protector、-fstack-protector-all:-fstack-protector:启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码;-fstack-protector-all:启用堆栈保护,为所有函数插入保护代码;-fno-stack-protector:禁用堆栈保护。GCC中的堆栈保护机制
[9] -fdelete-null-pointer-checks/-fno-delete-null-pointer-checks:delete-null-pointer-checks是一种优化手段,通过全局的数据流分析来识别和删除所有对空指针的检测操作;编译器假定对空指针的解引用会造成程序终止。但因为有些环境下,这一结论并不一定成立,而O2,O3和Os时会开启此优化。因此GCC增加了选项-fno-delete-null-pointer-checks;
[10] -g:告诉gcc生成并嵌入调试信息;
[11] -fstack-usage:为每一个函数输出栈使用信息,每一个源码文件生成一个.su (Stack Usage)文件, su文件中有每一个函数的栈使用信息;
[12] -Wstack-usage=byte-size:如果函数的堆栈使用量可能超过字节大小,则发出警告。;
[13] -Wdate-time:当遇到宏__TIME
_、 宏__DATE__ 、 宏__TIMESTAMP__时发出警告;
[14] -Werror=date-time:将指定的警告变成错误;
[15] -fpic、-FPIC、-fpie、-fPIE相同点:都是为了在动态库中生成位置无关的代码。通过全局偏移表(GOT)访问所有常量地址。程序启动时动态加载程序解析GOT条目。不同点:如果链接的可执行文件的GOT大小超过计算机特定的最大大小,则会从链接器收到错误消息,指示-fpic不起作用;在这种情况下,请使用-fPIC重新编译。GOT大小根据操作系统的不同而大小不一样,SPARC上为8k,在AArch64上为28k,在m68k和RS / 6000上为32k。x86没有此限制;-fpie、-fPIE这些选项类似于 -fpic 和 -fPIC,但生成的与位置无关的代码只能链接到可执行文件中。 通常这些选项用于编译将使用 -pie GCC 选项链接的代码。-fpie 和 -fPIE 都定义了宏 piePIE。 宏的 -fpie 值为 1,-fPIE 值为 2。-fno-pic :生产位置有关代码。
[16] -ffixed-reg:生成的代码不要用寄存器r9,uboot中用来指另做它用;
[17] -ffunction-sections、-fdata-sections:在链接生成最终可执行文件时,如果带有-Wl,--gc-sections参数,并且之前编译目标文件时带有-ffunction-sections、-fdata-sections参数,则链接器ld不会链接未使用的函数,从而减小可执行文件大小;
[18] -fshort-wchar:强制将wchar_t指定成两个字节,使用这个字段常常是因为wchar_t类型在Windows和Linux平台下字节大小的不同,但这样做只会改变代码中实现的部分,而内部库或者是第三方库中用到的接口和函数都是没有变的,仍然采用的是4字节编码
[19] --gc-sections -gc-sections:同上
[20] -fcommon、-fno-common: 指定编译器将未初始化的全局变量放在对象文件的BSS部分。
[21] -mthumb、-marm、-mthumb-interwork、-mno-thumb-interwork:-marm 和-mthumb用来执行生成的代码在arm模式还是thumb模式执行,-mno-thumb-interwork 是指没有ARM/Thumb之间的切换
[22] -mword-relocations:由于使用pic时movt / movw指令会硬编码16bit的地址域,而uboot的relocation并不支持这个, 所以arm平台使用mword-relocations来生成位置无关代码
[23] -mabi=name:这些' -m '选项是为ARM端口定义的,为指定的ABI(Application Binary Interface)生成代码。允许的值是:' apcs-gnu ',' atpcs ', ' aapcs ', ' aapcs-linux '和' iwmmxt '。
[24] -mstrict-align、-mno-strict-align:是否允许非对齐访问;
[25] -finstrument-functions:生成函数的进入和退出的检测调用;
[26] -pipe:编译的各个阶段之间使用管道而不是临时文件进行通信。这在汇编程序无法从管道读取数据的某些系统上无法工作;但是GNU汇编器没有问题;
[27] -march=name:指定目标架构的名称,以及一个或多个可选的特性修饰符;
[28] -mgeneral-regs-only:生成只使用通用寄存器的代码。这将防止编译器使用浮点寄存器和高级SIMD寄存器,但不会对汇编程序施加任何限制;
[29] -Bdynamic、-Bstatic:-Bdynamic and -Bstatic这两个选项是position-dependent选项,影响后面命令行出现的-lname。-Bdynamic (default):在-L指定的目录列表中查找libfoo.so和libfoo.a;-Bstatic:在-L指定的目录列表中查找libfoo.a。注意,历史上-Bstatic和-static同义。编译器driver的-static是个不同的选项,除了传递-static给ld外,还会去除预设的--dynamic-linker,影响libgcc libc等的链接;
[30] 下面以uboot的编译和链接过程arm gcc相关的参数。具体内容可以参考:官方文档pdf版本下载
[31] arm gcc 编译与链接参数
[uboot] (番外篇)uboot relocation介绍
[32] 一个问题引出的>/dev/null 2>&1详解
[33] linuxC编译参数CPPFLAGS、CFLAGS、LDFLAGS参数的理解
[34] Linux内核移植 part1:arm gcc 编译与链接参数

posted on 2022-11-02 22:23  BSP-路人甲  阅读(1193)  评论(0编辑  收藏  举报

导航