U-Boot完美解读(1)——程序的布局和地址解析

0、前言

        在网上看到N多“大牛”们的移植文档,什么S3C2410,S3C6410,ARM7,ARM9的,一大堆一大堆,大致看了下,百分之九十以上的文章都是介绍了在哪儿修改代码,至于为什么这样修改却是只字未提。当然,这样的文档对于做产品是好样的,因为产品只追求结果,开发人员如何实现,为什么要这样实现已经不重要了。所以,本系列计划与大家分享移植如何实现,为什么要这样移植作详细介绍,由于个人并非天才,所以在写作过程中需要阅读大量的datasheet,甚至于反复读,所以更新不会太快。当然如有不当之处,敬请各位不吝指正。

 

1、入门第一天得学会站好位置

1.1、同学们都在哪儿呢?

刚入手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下查找。相信学嵌入式的都是强人,所以此处略去三千字的说明。偷懒的感觉就是好,嘿嘿........

1.2、到操场集合了

 选择一个文件打开,这里以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 = .;
}

 

1.3、对号入座

直接贴出上面的一段代码,不知道亲们看明白了多少呢?如果能很轻松过了,那只能说恭喜你,已经入门了,下面的内容请直接跳过。上一节卖了一个关子,接下来针对不明白的亲们进行逐行分析:

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段,堆,栈段的介绍,将随后补一文章内存管理。

你可能感兴趣的:(U-Boot完美解读(1)——程序的布局和地址解析)