uboot的链接文件分析

摘要:uboot中的子目录Makefile认知 u-boot.lds分析与联想 ld脚本加强学习
详细:今天结合uboot的README帮助文件打开各各uboot的文件包,看到基本上每个文件包中都有Makefile,于是我开了一个smdk2410下的Makefile看,哇,好简单啊!打开其他目录下的Makefile,格式基本都是一致的,都很简单。并且如果我要自己添加一个.c或.S文件我也知道应该怎么修改这个Makefile文件了。我的第二轮uboot学习阶段一(Makefile地图的认识)已经成功完成了。

接着要进入第二轮uboot学习的第二阶段(看懂源码结构,加强源码的理解,知道移植需要修改哪些地方)先找u-boot.lds文件。用find . -type f|ls -l|grep *.lds会有很多个路径下的u-boot.lds。再去Makefile这个地图里去找啦,最终搜索到u-boot.lds的路径是在主目录下的config.mk中找到线索的

ifndef LDSCRIPT
#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
else
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
endif
假如没有定义LDSCRIPT则什么也不做。那么LDSCRIPT为空,然后再往下看
sinclude $(TOPDIR)/arch/$(ARCH)/config.mk # include architecture dependend rules
sinclude $(TOPDIR)/$(CPUDIR)/config.mk  # include  CPU specific rules

于是在\arch\arm中的config.mk中最后一行看到
LDSCRIPT := $(SRCTREE)/$(CPUDIR)/u-boot.lds

那么我就到arch/arm/cpu/arm920t下找到了u-boot.lds文件,打开开始分析。昨天已经了解了些lds基本规则及命令,今天

结合实际案例自己分析。第1句:OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
天呢!我第一句就看不懂,定义输出格式为什么有3个选择?于是到官网查出如下帮助说明
===========================================================================
OUTPUT_FORMAT(bfdname)
OUTPUT_FORMAT(default, big, little)
The OUTPUT_FORMAT command names the BFD format to use for the output file (see BFD). Using OUTPUT_FORMAT(bfdname) is exactly like using `--oformat bfdname' on the command line (see Command Line Options). If both are used, the command line option takes precedence.
You can use OUTPUT_FORMAT with three arguments to use different formats based on the `-EB' and `-EL' command line options. This permits the linker script to set the output format based on the desired endianness.

If neither `-EB' nor `-EL' are used, then the output format will be the first argument, default. If `-EB' is used, the output format will be the second argument, big. If `-EL' is used, the output format will be the third argument, little.
=============================================================================
原来如此定义输出格式。根据ld后面的参数 -EB和-EL或不带参数来决定输出文件的格式,所以有3种选择。ok
第2句:OUTPUT_ARCH(arm)
应该就是指定构架,那么除了arm还能有其他什么参数吗?到官网查出如下
=============================================================================
OUTPUT_ARCH(bfdarch)
Specify a particular output machine architecture. The argument is one of the names used by the BFD library (see BFD). You can see the architecture of an object file by using the objdump program with the `-f' option.
=============================================================================
我到BFD中搜索了下,没搜索到。于是想起来了README中的/arch目录下又arm还有avr32,i386等,应该都可以作为参数的,先跳过,继续
第3句:ENTRY(_start)
应该是指定入口函数。官网搜索了下
===========================================================================
The first instruction to execute in a program is called the entry point. You can use the ENTRY linker script command to set the entry point. The argument is a symbol name:

     ENTRY(symbol)
============================================================================
_start是一个.o文件吗?program?于是网上搜索了下,起始段的段名是_start
第4句开始:
SECTIONS
{
 . = 0x00000000;

 . = ALIGN(4);
 .text :
 {
  arch/arm/cpu/arm920t/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 (NOLOAD) : { *(.bss) . = ALIGN(4); }
 _end = .;
}
根据我昨天看的ld脚本基础,以上内容我都明白什么意思。但是还有一些设计方面的疑问。
1,got是什么段不清楚。
2,另外把段这样定义的目的,如果是我自己定义,我可能也不知道如果定义,在内存中就一定要先放test段吗,然后再放ro段吗?
3,段应该怎样放置才最好,另外段名一共有多少种?
4,u_boot_cmd_start定义后,将来会用吗?有什么好处。
5,定义.bss 为(NOLOAD)的目的?
我现在网上找个不错的分析。
=======================================================
下面是u-boot-1.3.4的u-boot.lds(/cpu/arm920t/),简单分析如下:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*指定输出可执行文件是elf格式,32位ARM指令,小端 */
/*OUTPUT_FORMAT("elf32-arm","elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)   /* 指定输出文件的平台体系是ARM */
ENTRY(_start)             /*指定可执行映像文件的起始段的段名是_start*/
SECTIONS
{
/*指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置。必须使编译器知道这个地址,通常都是修改此处来完成*/
. = 0x00000000;         /* 起始地址为0x00000000 */

. = ALIGN(4);             /* 字对齐,即就是4字节对齐*/
.text :                          /* 代码段*/
{
cpu/arm920t/start.o (.text)                  /* 代码段第一部分代码*/
board/fs2410/lowlevel_init.o (.text)     /* 代码段第二部分,这段由自己添加,由于在编译连接时发现,lowlevel_init.o代码段总是被连接在4kB之后,导致start.s执行到该段代码时,总是无法找到这段代码(注明:从nandflash启动才会存在这个问题)。*/
*(.text)                        /*其余代码段*/
}

. = ALIGN(4);
.rodata : { *(.rodata) } /* 只读数据段,所有的只读数据段都放在这个位置*/

. = ALIGN(4);
.data : { *(.data) }       /* 可读写数据段,所有的可读写数据段都放在这里*/

. = ALIGN(4);
.got : { *(.got) }            /*指定got段,got段式是uboot自定义的一个段,非标准段*/

. = .;
__u_boot_cmd_start = .; /*把__u_boot_cmd_start赋值为当前位置,即起始位置*/
.u_boot_cmd : { *(.u_boot_cmd) } /* u_boot_cmd段,所有的u-boot命令相关的定义都放在这个位置,因为每个命令定义等长,所以只要以__u_boot_cmd_start为起始地址进行查找就可以很快查找到某一个命令的定义,并依据定义的命令指针调用相应的函数进行处理用户的任务*/
__u_boot_cmd_end = .;   /*u_boot_cmd段结束位置,由此可以看出,这段空间的长度并没有严格限制,用户可以添加一些u-boot的命令,最终都会在连接是存放在这个位置。*/

. = ALIGN(4);
__bss_start = .;               /*把__bss_start赋值为当前位置,即bss段的开始位置*/
.bss (NOLOAD) : { *(.bss) } /*指定bss段,这里NOLOAD的意思是这段不需装载,仅在执行域中才会有这段*/
_end = .;                         /*把_end赋值为当前位置,即bss段的结束位置*/
}

===================================================================
看来上面的介绍,我那5个问题中有2个问题已经解决了,接着我先到官网去查查段有多少种类型
===================================================================
We have four output sections:

.text program code;
.rodata read-only data;
.data read-write initialized data;
.bss read-write zero initialized data.
====================================================================
原来只有4种。又一个问题解决了。
    接着我自己想了想BSS段通常是指用来存放程序中未初始化的全局变量的一块内存区。如果加载了就会浪费内存空间,因为没初始化,所以到运行的时候加载也可以,从节约内存的角度来看,所以设计者加了NOLOAD,当然,如果我把NOLOAD删除后,应该也没问题,就是编译出来的bin文件大小会变大。我之后会试一下。
    最后一个问题。为什么要这样排列段?不同的段排列是否会影响到程序的运行?暂时不知道。当然,我可以把之前移植完成的uboot中的.lds文件的段修改下,看看会有什么不同的结果。
    不过,今天我还不会去尝试,因为那属于很浪费时间的猜测性学习,今天的还有一个任务就是找些实例来学习objdump,readelf,objcopy命令的实际操作做到灵活运行。这样会对我今后的调试起到很大的帮助。
1,objdump实例学习
首先,想到了刚才搜索bss段的时候看到的一个bss段说明的实例中
程序1:
===================================================
int ar[30000];
void main()
{
    ......
}


程序2:

int ar[300000] =  {1, 2, 3, 4, 5, 6 };
void main()
{
    ......
}


发现程序2编译之后所得的.exe文件比程序1的要大得多。当下甚为不解,于是手工编译了一下,并使用了/FAs编译选项来查看了一下其各自的.asm,发现在程序1.asm中ar的定义如下:

_BSS SEGMENT
     ?ar@@3PAHA DD 0493e0H DUP (?)    ; ar
_BSS ENDS


而在程序2.asm中,ar被定义为:

_DATA SEGMENT
     ?ar@@3PAHA DD 01H     ; ar
                DD 02H
                DD 03H
                ORG $+1199988
_DATA ENDS


区别很明显,一个位于.bss段,而另一个位于.data段,两者的区别在于:全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

============================================================
那么就想到了我自己在ubuntu下可以编写个.c文件,然后再自己写一个lds连接文件,最后编译
1,用gcc -S 选项生成汇编代码
2,用objdump查看汇编代码,同时熟悉此命令的参数
3,这个时候修改不同段的位置设置。调试看效果。
4,把.c文件编译成elf文件。然后用readelf分析其elf结构。
5,最后再用objcopy命令把elf文件格式复制出一共二进制文件。并且通过使用objcopy参数,生成不同大小的二进制文件。
ok,已经为自己设置了要求,接着就是去完成这个要求。2个小时过去了,明天继续。

另外,看了objdump帮助里面比较吸引眼球的参数如下
=========================================================
-t
--syms
Print the symbol table entries of the file. This is similar to the information provided by the `nm' program, although the display format is different. The format of the output depends upon the format of the file being dumped, but there are two main types. One looks like this:
          [  4](sec  3)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x00000000 .bss
          [  6](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000 fred
    
where the number inside the square brackets is the number of the entry in the symbol table, the sec number is the section number, the fl value are the symbol's flag bits, the ty number is the symbol's type, the scl number is the symbol's storage class and the nx value is the number of auxilary entries associated with the symbol. The last two fields are the symbol's value and its name.

 

本文转自  http://blog.163.com/cailing_07@126/blog/static/3391508720111023104948907/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值