嵌入式Linux驱动学习之路(四)u-boot编译分析
u-boot编译分析
在配置完成后,执行make开始编译。这里打开Makefile。
首先在目标all前有一句话首先检查是否有include/config.mk文件来判断是否成功配置过。
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
下面分析“make”命令正常执行的过程。
include/autoconf.mk生成过程
首先包含头文件autoconf.mk.dep、autoconf.mk。这是与开发板相关的一些宏定义。在Makefile执行过程中,需要根据某些宏定义来确定执行哪些操作。
all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk # load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk //配置时生成的
export ARCH CPU BOARD VENDOR SOC
(在配置时会删除autoconf.mk.dep和autoconf.mk这两个文件,在执行make后创建它。这两个文件该如何更新?我在GUN make使用手册中发现这句话。
make 在读入所有 makefile 文件之后,首先将所读取的每个 makefile 作为一个目标,寻找更新它们的规则。如果存在一个更新某一个 makefile 文件明确规则或者隐含规则,就去更新对应的 makefile 文件。完成对所有的 makefile 文件的更新之后,如果之前所读取任何一个 makefile 文件被更新,那么 make 就清除本次执行的状态重新读取一遍所有的 makefile 文件(此过程中,同样在读取完成以后也会去试图更新所有的已经读取的 makefile 文件,但是一般这些文件不会再次被重建,因为它们在时间戳上 已经是最新的)。读取完成以后再开始解析已经读取的 makefile 文件并开始执行必要 的动作。
故在包含头文件后,会把头文件作为目标在Makefile寻找相应的规则,执行后会再次包含更新后的规则。)
$(obj)include/autoconf.mk.dep: $(obj)include/config.h include/common.h
@$(XECHO) Generating $@ ; \
set -e ; \
: Generate the dependancies ; \
$(CC) -x c -DDO_DEPS_ONLY -M $(HOSTCFLAGS) $(CPPFLAGS) \
-MQ $(obj)include/autoconf.mk include/common.h > $@ $(obj)include/autoconf.mk: $(obj)include/config.h
@$(XECHO) Generating $@ ; \
set -e ; \
: Extract the config macros ; \
$(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
sed -n -f tools/scripts/define2mk.sed > $@.tmp && \
mv $@.tmp $@
编译选项-dM的作用是输出 include/common.h中定义的所有宏。然后输出给 tools/scripts/define2mk.sed。而该脚本主要完成了在common.h中查找和处理以CONFIG_开头的宏定义。
然后包含该include/config.mk 配置 ARCH CPU BOARD VENDOR SOC
之后判断主机架构和开发板是否相同,若相同则用主机的架构。
然后包含顶层目录中的config.mk。
# load other configuration
include $(TOPDIR)/config.mk
config.mk文件执行过程。
首先设置src和obj为空。
cc-option保存编译选项
使用FLAGS +$=(call cc-option ,option1, option2)来调用。
然后是指定交叉工具链。
CROSS_COMPILE在 arch/$(ARCH)/config.mk 中指定
包含开发板的相关配置文件。
指定隐含的编译规则。
u-boot镜像生成过程
在顶层目录下的Makefile中有两个all:
第一个all:没有依赖,也没有命令。后面接的是两条
sinclude sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
第二个all:
all: $(ALL)
这个all才是我们真正需要执行的
要理解这里的all 空命令的作用。必须了解下面几个点
1. 当makefile中有两个相同目标的时候,会执行后一个目标。因为后一个目标会重载前一个。比如:
all:
echo all1
all:
echo all2
执行make 会输出后一个all2
2. 伪目标
一个使用地方是 当不需要产生一个实际的目标文件的时候(比如只要执行指令)。
clean:
rm *.o
这样是不产生文件。只需要执行指令。如果当前目录没有一个文件叫做clean。执行make clean是正常的 。如果目录下有一个clean文件。那么由于这个clean文件是最新的,因此就不会执行rm *.o操作。因此需要定义一个伪目标。不管目录下是否存在这个目标。都会执行clean的操作rm *.o
.PHONY clean
clean:
rm *.o
3. 空命令
可以存在依赖文件,但是没有命令行。比如
all:
或者
all: $(obj)
空命令的唯一作用是防止make在执行时,试图为重建这个目标去查找隐含命令
这句话暂时不好理解。先看后面的再说。
4. makefile的执行规则和include 这个是关键点
makefile首先被解析。
遇到include。先去解析include的文件(假设为inc.mk),解析完毕以后再回头来解析原makefile。
关键点是如果这个inc.mk不存在。make不会退出。而是先提出一个警告,继续处理原makefile的内容。
等到原makefile的内容处理完毕以后。会尝试使用规则来重建这个inc.mk文件,也就是尝试将这个inc.mk文件当做一个目标来执行。如果不能重建出这个inc.mk。那么报告一个错误,make退出。如果这个inc.mk重建成功了。那么将原makefile的状态重置,重新开始执行这个makefile。
参考:http://blog.csdn.net/groundhappy/article/details/52708792
可以这样理解。第一个all是为了避免将include中包含的Makefile的第一个目标作为终极目标而设置的。主要是第二个all:,这个才是make中执行的
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
下面分析u-boot.bin文件的生成过程。
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(BOARD_SIZE_CHECK)
而u-boot.bin依赖于u-boot。这里的uboot就是ELF格式的u-boot文件
$(obj)u-boot: depend \
$(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
下面分析u-boot的各个依赖。
1)depend依赖。依次进入$(SUBDIRS) $(CPUDIR) $(dir $(LDSCRIPT))表示的子目录中,并执行make _depend命令,生成各个子目录的.depend文件,在.depend文件中列出每个目标文件的依赖文件。
depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) \
$(obj)include/autoconf.mk \
$(obj)include/generated/generic-asm-offsets.h
for dir in $(SUBDIRS) $(CPUDIR) $(dir $(LDSCRIPT)) ; do \
$(MAKE) -C $$dir _depend ; done
2)$(SUBDIRS)依赖。
SUBDIRS = tools \
examples/standalone \
examples/api
其依赖的规则如下。这表示生成它的方式是执行tool、example/standalone、examples/api目录下的Makefile。
$(SUBDIRS): depend
$(MAKE) -C $@ all
3)$(OBJS)依赖,其变量值是$(CPUDIR) $(dir $(LDSCRIPT)
$(OBJS): depend
$(MAKE) -C $(CPUDIR) $(if $(REMOTE_BUILD),$@,$(notdir $@))
以上规则表明,$(OBJS)表示的目标文件,都是通过进入$(CPUDIR)目录执行make命令编译得到的。
4) $(LIBBOARD) 、$(LIBS) 、$(LDSCRIPT) 同上。
5) $(obj)u-boot.lds依赖。LDSCRIPT定义在arch/arm/config.mk中。board/samsung/$(BOARD)/u-boot.lds
$(obj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@
u-boot.lds内容如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") //指定输出可执行文件是32位ARM指令,小端模式的ELF格式。
OUTPUT_ARCH(arm) //指定输出可执行文件的平台为ARM
ENTRY(_start) //指定程序的入口为_start
SECTIONS
{
. = 0x00000000; //指明目标代码的起始地址从0x0位置开始, ‘.’代表当前位置 . = ALIGN(4); //表示此处4字节对齐
.text :
{
arch/arm/cpu/armv7/start.o (.text) //表示start.o是代码段的第一个.o文件
board/samsung/tiny4412/libtiny4412.o (.text) //这是代码段的其他部分
arch/arm/cpu/armv7/exynos/libexynos.o (.text)
*(.text)
} . = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } //表示这是只读数据段 . = ALIGN(4);
.data : { //这是数据段
*(.data)
} . = ALIGN(4); . = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; . = ALIGN(4); .rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
} .dynsym : {
__dynsym_start = .;
*(.dynsym)
} .bss __rel_dyn_start (OVERLAY) : {
__bss_start = .; //表示_bss_start标号指向bss段的开始位置
*(.bss) //这是bss段
. = ALIGN(4);
_end = .;
} /DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}
.
嵌入式Linux驱动学习之路(四)u-boot编译分析的更多相关文章
- 嵌入式Linux驱动学习之路(五)u-boot启动流程分析
这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...
- 嵌入式Linux驱动学习之路(二十四)Nor Flash驱动程序
Nor Flash和Nand Flash的不同: 类型 NOR Flash Nand Flash 接口 RAM-like,引脚多 引脚少 容量 小(1M.2M...) 大(512M.1G) 读 简 ...
- 嵌入式Linux驱动学习之路(十四)按键驱动-同步、互斥、阻塞
目的:同一个时刻,只能有一个应用程序打开我们的驱动程序. ①原子操作: v = ATOMIC_INIT( i ) 定义原子变量v并初始化为i atomic_read(v) 返回原子变量 ...
- 嵌入式Linux驱动学习之路(一)嵌入式系统的软硬件架构
硬件资料: 操作系统:(非虚拟机) zws@z-pc:~$ lsb_release -aNo LSB modules are available.Distributor ID: Ubuntu Desc ...
- 嵌入式Linux驱动学习之路(二十五)虚拟网卡驱动程序
一.协议栈层次对比 设备无关层到驱动层的体系结构 1).网络协议接口层向网络层协议提供提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过 ...
- 嵌入式Linux驱动学习之路(二十三)NAND FLASH驱动程序
NAND FLASH是一个存储芯片. 在芯片上的DATA0-DATA7上既能传输数据也能传输地址. 当ALE为高电平时传输的是地址. 当CLE为高电平时传输的是命令. 当ALE和CLE都为低电平时传输 ...
- 嵌入式Linux驱动学习之路(二十二)用内存模拟磁盘
安装驱动后,可在/dev/目录下发现已经生成了相应的设备文件. 格式化设备:mkdosfs /dev/ramblock. 挂载设备. 读写设备 . 驱动程序代码: /***************** ...
- 嵌入式Linux驱动学习之路(二十)USB设备驱动
USB在接入系统的时候,以0的设备ID和主机通信,然后由主机为其分配新的ID. 在主机端,D+和D-都是下拉接地的.而设备端的D-接上拉时,表明此设备为高速设备:12M/s. D+接上拉时则是全速设备 ...
- 嵌入式Linux驱动学习之路(十八)LCD驱动
驱动代码: /************************************************************************* > File Name: lcd ...
随机推荐
- [web安全]Web应用漏洞攻击分析与防范
网站攻击主要分为以下几类: (1) sql注入攻击 SQL Injection:就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.它是利 ...
- Win7重装系统遇到的问题以及MysQL的问题解决
连续三天因为系统的错误,android方面的软件一直不能正确运行.而且每次开机的时候QQ 微信等聊天工具也出现损坏.虽然重新下载一个可以保证在电脑不管的情况下正常的运行.可是作为玩电脑时间不长的我来说 ...
- 对URL编码
url支持26个英文字母.数字和少数几个特殊字符,因此,对于url中包含非标准url的字符时,就需要对其进行编码.iOS中提供了函数stringByAddingPercentEscapesUsingE ...
- 【网络编程】TCP/IP、UDP、网络概…
计算机刚刚发明出来的时候,两台计算机之间是无法通信的,为了使计算机之间能够进行数据的交流,制定了OSI(Open SystemInterconnection)开放系统互联模型,而TCP/IP(我们所使 ...
- 我的Android第四章:Android的adb命令使用以及SQlite数据库运用
adb是什么?:adb的全称为Android Debug Bridge,就是起到调试桥的作用. adb有什么用?:借助adb工具,我们可以管理设备或手机模拟器的状态.还可以进行很多手机操作, ...
- Java中的经典算法之选择排序(SelectionSort)
Java中的经典算法之选择排序(SelectionSort) 神话丿小王子的博客主页 a) 原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕.也就是:每一趟 ...
- iOS中block的使用、实现底层、循环引用、存储位置
一.整体介绍 定义:C语言的匿名函数,
- 你离月薪30K还差哪些?
这类标题的文章,是不是很熟悉?你是不是冲着标题进来的? 类似这样的标题党文章,你应该看过很多,多数是泛泛而谈,没啥用- 今天老徐跟大家用几个真实案例,聊点有用的- 看完之后,你至少知道自己的差距是哪些 ...
- Maven基础配置--nexus私服配置
登录nexus私服后台,按照下图1-3的顺序进行添加仓库: 其中步骤3有三种仓库类型(Type)进行选择 1. Hosted Repository:本地仓库,在私服服务器上存放用户自行上传的jar包: ...
- ORACLE口令管理
口令文件介绍 在ORALCE数据库系统中,用户如果要以特权用户身份(SYS/SYSDBA/SYSOPER)登录ORALCE数据库可以有两种身份验证的方法:即使用与操作系统集成的身份验证或使用ORALC ...