在网上看到N多“大牛”们的移植文档,什么S3C2410,S3C6410,ARM7,ARM9的,一大堆一大堆,大致看了下,百分之九十以上的文章都是介绍了在哪儿修改代码,至于为什么这样修改却是只字未提。当然,这样的文档对于做产品是好样的,因为产品只追求结果,开发人员如何实现,为什么要这样实现已经不重要了。所以,本系列计划与大家分享移植如何实现,为什么要这样移植作详细介绍,由于个人并非天才,所以在写作过程中需要阅读大量的datasheet,甚至于反复读,所以更新不会太快。当然如有不当之处,敬请各位不吝指正。
刚入手bootloader的人可能都在想这样一个问题,把编译好的编程下载到目标平台后,目标板怎么知道从哪儿运行呢?刚开始时,我也在想这个的问题,有问题可不是什么丢人的问题哟,关键不知道而且还假装没有问题可就有点业余了哟!废话少说,直接让内存集合,看看U-Boot把内存分配并组织起来。
有过Linux编程的朋友都知道,单一文件可以直接通过GCC命令行方式进行,但如果有多个文件或者是上百个以至于上千文件的项目,用这种方式只能对他说一句“兄弟,我真的服咯U”。Makefile提供了对源文件进行管理的方式,通过查看U-Boot根目录下的Makefile文件,可以看到U-Boot的编译过程。对于内存的管理,我们只需要关注最后的链接方式,在Makefile中有如下部分:
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds $(GEN_UBOOT) ifeq ($(CONFIG_KALLSYMS),y) smap=`$(call SYSTEM_MAP,u-boot) | \ awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \ $(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \ -c common/system_map.c -o $(obj)common/system_map.o $(GEN_UBOOT) $(obj)common/system_map.o endif
不知道有没有看到一个u-boot.lds呢,当然如果直接在源码中找的话,会找到一大堆的链接文件,这个是需要配置的,需要移植到什么样的平台,可以查看源码根目录cpu/对应平台/u-boot.lds即可。如果目标板是ARM的处理器,可以到cpu/arm926ejs、arm920t等目录下查找与处理器对应的文件,如果是mips处理器,则对应到cpu/mips下查找。相信学嵌入式的都是强人,所以此处略去三千字的说明。偷懒的感觉就是好,嘿嘿........
选择一个文件打开,这里以cpu\arm_cortexa8\u-boot.lds为例,其它文件类似。打开文件后,代码并不多,加文件说明、空行、括号都不足百行,是不是觉得有点窃喜呢?既然如此,亲,我就直接把源码贴出来了哟,一会再逐个“包邮”哟!
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { cpu/arm_cortexa8/start.o (.text) *(.text) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .got : { *(.got) } __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(4); __bss_start = .; .bss : { *(.bss) } _end = .; }
直接贴出上面的一段代码,不知道亲们看明白了多少呢?如果能很轻松过了,那只能说恭喜你,已经入门了,下面的内容请直接跳过。上一节卖了一个关子,接下来针对不明白的亲们进行逐行分析:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm"")该行代码指定输出的可执行文件是elf格式(理解为二进制的文件就行),32位ARM指令,其中处理器支持为小端(目前ARM平台均为小端模式,大端与小端的差别请参考大小端字节序)
OUTPUT_ARCH(arm)指定输出可执行文件的运行平台为ARM处理器平台,当然如果为其它平台如mips,ppc等,这里作相应的变更。
ENTRY(_start)指定输出可执行文件的起始代码段为_start.
SECTIONS { . = 0x00000000指明目标代码的起始地址从0x0位置开始,其中前面的"."代表的是当前位置,ARM平台起始地址都是从0x00000000开始执行,这是由硬件属性决定的,是平台上后上能找到的第一个地址。
. = ALIGN(4)代码对齐方式,这里是以4字节对齐,如果设置过大,则会找成资源浪费,如果设置过小,则降低系统性能,如取一个数据时需要多次访问内存,后面重复该代码将不再作解释。
.text : { cpu/arm_cortexa8/start.o (.text) *(.text) }其中.text是声明为代码段,在存储里的代码段,数据段等分别用不同的符号说明,而cpu/arm_cortexa8/start.o (.text)是指定的一个目录,这里是针对cortex-A8的平台,在对应u-boot根目录下,可以找到所指的文件,这里说明引导文件的位置在cpu/arm_cortexa8/下(由于U-Boot对代码组织结构进行调整,新U-Boot位置位于arch/arm/cup/arm_cortexa8),对应start.s编译后的目标文件,start.s文件将在下一节作详细分析。
.rodata : { *(.rodata) }指定只读数据段,RO段。
.data : { *(.data) }指定读/写数据段,RW段。
.got : { *(.got) }指定got段, got段式是uboot自定义的一个段, 非标准段。
__u_boot_cmd_start = .右边的“.”在前面有解释,表示当前位置,这里把__u_boot_cmd_start赋值为当前位置,即起始位置。
.u_boot_cmd : { *(.u_boot_cmd) }指定u_boot_cmd段,,uboot把所有的uboot命令放在该段,除了uboot默认的命令外,可以自己增加新的命令,后面再作详细介绍。
__u_boot_cmd_end = .意思同上,把__u_boot_cmd_end赋值为当前位置,即结束位置。
__bss_start = .把__bss_start赋值为当前位置,即bss段的开始位置,BSS段的作用参见内存管理。
.bss : { *(.bss) }指定bss段
_end = . }把_end赋值为当前位置,即bss段的结束位置,最后一个括号表示定义的SECTIOS段的结束
OK,本节提到的还留下几个问题。
问题一:大小端字节序,将随后补一文章单独介绍。
问题二:start.s文件的解析,将在下一节作详细分析。
问题三:BSS段,堆,栈段的介绍,将随后补一文章内存管理。