--------------------------------------------------------------------------------"
aaronwong: u-boot中代码的疑问(_armboot_start与_start)?12Gm
---------------------------=j
我使用的是u-boot-1.3.0-rc2。在cpu/pxa/start.S中,有如下的标号定义: w'
_TEXT_BASE: 7B
.word TEXT_BASE k&BnQf
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 A%
.globl _armboot_start /'b&%
_armboot_start: 50m B8
.word _start 2inlX
~1
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 ?Ud=F}
.globl _bss_start W8
_bss_start: 4V1kfj
.word __bss_start 5`
====================== XM
按照上面的理解,__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是__bss_start - _start;在relocate代码段中计算uboot的大小时,也体现了这一点。 fHK'f0
实际上,_armboot_start并没有实际意义,它只是在"ldr r2, _armboot_start"中用來寻址_start的值而已,_bss_start也是一样的道理,真正有意义的应该是_start和 __bss_start本身。 ;{I
但是,令我不解的是,在C入口函数start_armboot()中(对应文件为lib_arm/board.c),有如下代码: =-yz!
void start_armboot (void) 6#F[C
{ ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 dCb
......... *=
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); //第一句话 7
.......... xfw,,
monitor_flash_len = _bss_start - _armboot_start; //第二句话 =r1m,
............... =cN^x+
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //第三句话 W
.......... ?
} ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 u
============================================== v#HG/
按照上面的理解,_armboot_start与_bss_start都是没有实际意义的,它们只是一个地址,有实际意义的是地址中的内容_start和 __bss_start(虽然也还是地址)。象第一句话,其“意图”很明显,是把gd作为全局数据结构体的指针,并初始化为“SDRAM中的uboot起始地址(即TEXT_BASE)-CFG_MALLOC_LEN-全局数据结构体大小”。 <BgA
要实现这个“意图”,应该是写成:gd = (gd_t*)(_start - CFG_MALLOC_LEN - sizeof(gd_t));或者gd = (gd_t*)(TEXT_BASE- CFG_MALLOC_LEN - sizeof(gd_t));才对阿?用_armboot_start来作运算应该是没有任何意义才对!? #0gYd?
第二句话也是一样的道理,它的意图是要计算u-boot映像的大小,应该写成__bss_start - _start才对阿? @`PVq
我使用readelf工具查看编译所得到的uboot映像文件得到信息如下: NK7,G
[aaronwong@localhost build]$ readelf -s u-boot|grep _start G
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start !Qgo}
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start W
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start b9>
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start m[<B2Q
上面我删除了与该讨论无关的包含“_start""t的标号信息。 &:gP
显然,我前面的理解应该是正确的(_start=TEXT_BASE=0xa170 0000)。那么u-boot源代码中的monitor_flash_len=_bss_start - _armboot_start=0xa1700048 - 0xa1700044 = 4,有什么意义?? p
迷茫中,期盼大虾指点迷津,谢谢~!!! <
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 M6fJvX
--------------------------------------------------------------------------------%:#-
eltshan: [Re: aaronwong]9o22#P
-----------------Zi
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start D3dY(
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start _mAq>
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start QNr+Pc
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start +=
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 E-Y>
我想: FCNh{M
_start所在的地址是a1700000, eHEsMt
_armboot_start 所在的地址是a1700044, ?
那么 根据这句: 7.Iy
_armboot_start: .word _start }<U
所以_armboot_start的值应该是a1700000 w34ok:
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 gh/
所以 ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 ka
monitor_flash_len = _bss_start - _armboot_start = a171b070 - a1700000 = 1b070 ~=w
而不是你说的 = 4 FYxAA@
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 *E*4z
以上个人意见.Q~St
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 ^p`Sc
--------------------------------------------------------------------------------Nkh
aaronwong: [Re: eltshan];DQlk5
-------------------p4
谢谢,eltshan!你的理解是正确的,不过我看了之后还是没能想得很明白,因为我在想,按你所说,那么_start的值应该是多少呢?难道是“b reset”这条指令的机器码?所以我对ELF格式的u-boot映像文件作了反汇编,分析之后终于找到了症结所在。以下是部分分析过程,首先是反汇编: 24
arm-iwmmxt-linux-gnueabi-objectdump -D u-boot > u-boot.s *{|(q#
并提取了monitor_flash_len = _bss_start - _armboot_start;这条语句相关的反汇编代码如下: /
============================== ^o#c7
a1700044 <_armboot_start>: b?
a1700044: a1700000 .word 0xa1700000 e{Zn
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 >
a1700048 <_bss_start>: l#"{w
a1700048: a171b070 .word 0xa171b070 U3sK
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 !.
a171b070 <monitor_flash_len>: R
a171b070: 00000000 .word 0x00000000 Q^$
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 MeE9
..... m4
a1700f40: e59f41d0 ldr r4, [pc, #464] ; a1701118 <start_armboot+0x1dc> lF-4
//r4=[a1701118]=a1700044 2/NL_;
..... EW0Th
a1700f7c: e59f3198 ldr r3, [pc, #408] ; a170111c <start_armboot+0x1e0> [T4Uwy
//r3=[a1700044]=a1700048 D
a1700f80: e5942000 ldr r2, [r4] 2/0N0
//r2=[a1700044]=a1700000 mV
a1700f84: e59f4194 ldr r4, [pc, #404] ; a1701120 <start_armboot+0x1e4> bWFU
//r4=[a1701120]=a1719d24 #Bnq
a1700f88: e5933000 ldr r3, [r3] *
//r3=[a1700048]=a171b070 <?
a1700f8c: e0623003 rsb r3, r2, r3 $e8I:
//r3= r3-r2 = a171b070-a1700000 = 1b070; q|
a1700f90: e59f218c ldr r2, [pc, #396] ; a1701124 <start_armboot+0x1e8> f1XV
//r2=[a1701124]=a171b070 }
a1700f94: e5823000 str r3, [r2] h`lC]
//monitor_flash_len=[r2]=r3=1b070 mJT:HJ
...... =op4
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 9
a1701118: a1700044 .word 0xa1700044 Z0
a170111c: a1700048 .word 0xa1700048 fr3g(
a1701120: a1719d24 .word 0xa1719d24 EpcDe
a1701124: a171b070 .word 0xa171b070 XT&
======================================== :
上面//是我自己的注释。这表明,你的理解的确是正确的。 :}6
经过这个过程之后,我终于认识到自己的误解在哪里了。原来,我是把"汇编语言中LDR伪指令对符号的引用"与"C语言中对汇编程序中符号/常量/变量的引用"搞混淆了。我想说明以下几点:`[I
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 WY
(1) readelf以及u-boot.map和System.map所给出的符号表中符号的值,实际上是表示符号所在的地址,而不是指符号本身的值。 E?F'R
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 u
(2) 汇编语言中没有指针的概念,因此对符号的引用是"赤裸裸"的。例如: M"
========== wM
.globl _armboot_start J%
_armboot_start: .word _start d_
ldr r2, _armboot_start Kf
========== ,
实际上反汇编以后是: 466
============ ;/g-oE
a1700044 <_armboot_start>: }b
a1700044: a1700000 .word 0xa1700000 R
a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044 <_armboot_start> b b}/4
============ [7A
也就是说,_armboot_start是一个地址0xa1700044,其中的内容是0xa1700000,上面对_armboot_start的引用是直接将其替换为其表示的地址0xa1700044,而非其中的内容0xa1700000。这就是"赤裸裸"的引用。_armboot_start变量的地址值由连接器而定。 m
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 )bR;
(3) C语言则不同,对变量/符号/常量的引用必须要通过地址来寻址,不管是全局变量还是局部变量,不同的是局部变量在生命期结束后,所占的地址空间会被释放而已。即使是函数调用时的参数传递,虽然是将实参的值"拷贝"给形参,但"拷贝"的过程也是通过实参和形参的地址来对两者进行访问的。 p
所以,在C语言中的 "monitor_flash_len = _bss_start - _armboot_start" 这句话中对_armboot_star的引用,实际上是把它用作了指针,把它作为访问对象的地址来使用,通过这个地址即a1700044 来访问对应存储空间所存放的内容亦即0xa1700000,_bss_start也是同样的道理。所以这句话实际上是monitor_flash_len =[a1700048]-[0xa1700044]=a171b070-a1700000 = 1b070,这样就得到了正确的结果。 eNe#ij
©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 MnK-47
现在,我们再回答最前面的问题:_start的值是什么?_start表示地址0xa1700000 ,在汇编语言中,对_start的"绝对引用"(这里是与用相对寻址进行跳转进行区别)就是将其替换为0xa1700000,但其中存放的内容的的确确就是"b reset"这条指令的机器码,所以如果在C语言中引用_start,得到的结果反而就是这个指令的机器码了。其实这个问题很简单,只是和C语言的引用搅在一起,一些概念被偷换了而已。 *
//*********************************************************************************************//
摘自另一篇博客
u-boot 1.1.6 start.S 代码学习
/*
参考了别人的一些笔记。
看完了启动代码。
本文档记录在看代码时碰到的困难。
将这些曾经困扰的问题记录下来。
以备今后之用。分析时不重要的代码被删除了。
*/
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
/*
_start是整个u-boot程序的入口点,即链接后。
该处是整个程序的第一条指令。程序的入口点是由链接脚本所指定,比如对于smdk2410的板子(下面都以smdk241为例),脚本文件位于board/smdk2140/u-boot.lds。在该脚本文件中:ENTRY(_start) 即指定程序的入口地址。globl _start 定义一个外部可以引用的变量,比如说,在其它源代码文档中。
就可以直指引用_start这个变量。如int entry=_start; 那此处entry值将是多少呢?因为_start相当于一个变量,entry的值就是_start处存储的值。
即 b reset机器码值。关于globl定义的变量得注意的地方。
后面还有记录
这段代码处理中断向量表。
关于 balignal 16,0xdeadbeef的网上资料:
.align伪操作用于表示对齐方式:通过添加填充字节使当前位置满足一定的对齐方式。
.balign的作用同.align。
.align {alignment} {,fill} {,max}
其中:alignment用于指定对齐方式,可能的取值为2的次幂,缺省为4。fill是填充内容。
缺省用0填充。max是填充字节数最大值,如果填充字节数超过max,就不进行对齐.
例如: .align 4, 指定对齐方式为字对齐
deadbeef一般用来表示没用的或是已经被释放掉的内存
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
/*
备注这几个由.word伪操作符定义变量的作用及其取值
_TEXT_BASE:
.word TEXT_BASE
_TEXT_BASE:此处定义一汇编语言标签,更好的理解就是:告诉编译器。
为_TEXT_BASE分配存储空间。
该空间的名字就叫_TEXT_BASE,该空间中存储的值就是由.word后面确定的TEXT_BASEC(即0x33F80000)。
相当于C语言中 long _TEXT_BASE=TEXT_BASE; TEXT_BASE定义在board/smdk2410/config.mk文件中。该值的作用是告诉链接器,本程序运行的基地址为TEXT_BASE。U-boot编译后。
烧在FLASH的第一个块中。
CPU复位上电后,电脑寄存器为0x0000。怎么会跑到TEXT_BASE处执行呢?
事实上,CPU上电后。
从地址0x0000处执行。
而U-BOOT的最起始代码。
即本文件中从_start开始的代码是与地址不相关的。
这段代码放在任何空间执行的结果都是一样(当然不是绝对,假设u-boot代码段是100K。
则放在TEXT_BASE-80K处。
搬运时就会把u-boot代码后面20K部分覆盖为最前面的20K)。
.globl _armboot_start
_armboot_start:
.word _start
定义外部可以引用的变量_armboot_start,。即相当于C long _armboot_start=&_start; _armboot_start值是多少?是TEXT_BASE,即0x33F80000!等价的那条C语句,取的是_start变量地址。
而不是_start本身。在C语言中。
定义一变量 int x=100;就是告诉编译器。给我一个int大小的存储空间。
该空间存储的值就是100,这个空间在哪呢?即空间地址是多少呢?我们可以通过&x知道。
在汇编语言中,理解上有点不一样。上面三行语句。
第一句,告诉编译器。
向外面输出变量_armboot_start
第二句,_armboot_start变量在此处,到底在哪。
要到链接时才能确定。
凡正现在知道有这么一个变量了。
第三句,_armboot_start变量空间放的数据为_start标签的值。这点与C语言的理解有点不一样了。此处引用的是_start标签对应处的地址。在汇编语言中,标签代表的就是那行所在的地址。
图1是从u-boot编译后生成的u-boot.map截图的。从此文件中知道,_armboot_start这个变量地址为0x33f80044,
*/
图1
/*
.globl _bss_start
_bss_start:
.word __bss_start
此三句。
特别要备注的是__bss_start这个符号。
它的值为多少?该值定义在board/smdk2410/u-boot.lds文件中
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
上面的__bss_start=.; 表示__bss_start值就是当前位置的值。当前位置是多少呢?从下面一句
.bss:{*(.ss)}知道。紧接该位置后面马上就是放bss段数据了。所以。
当然就是bss段的起始地址。_end就是bss段的结束地址。
参考:http://blog.chinaunix.net/u1/58780/showart_462971. html
bss是这个链接脚本的最后一个段。start.S就是以这个段的起始地址来计算要搬运u-boot大小的代码的。即。
这个段前面的所有数据都将被搬到TEXT_BASE处。然后跑到start_armboot处,即C语言的入口代码。__bss_start这个值是多少? Smdk2410我编译后值是0x33f96f20。可以从图2的map文件中查到,bss段从0x33f96f20开始分配的。
再次验证一下0x33f96f20就是__bss_start值。
我们可以从图1处可以看到有个变量_bss_start该变量就是由下面三条语句所定义,_bss_start处保存的值就是__bss_start:
.globl _bss_start
_bss_start:
.word __bss_start
用UltraEdit打开生成的u-boot.bin,定位到文件偏移0x48(0x48由_bss_start所在地址0x33f80048-TEXT_BASE得到),如图3,此处值确实是20 6F F9 33(注意大小端)
*/
图2
图3
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/*
上面几行进入SVC模式,通过装入CPSR到r0。
修改相应位后再载入到CPSR。
*/
/* turn off the watchdog 关闭WATCH DOG,具体寄存器地址看芯片DATASHEET即知*/
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR default
* 设置中断屏蔽寄存器相应位。
关闭中断。
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
/*
下面是重定位代码。
即将整个u-boot搬到TEXT_BASE地址处的代码
/*
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
/*
adr r0, _start这句代码是将_start标签处运行时的地址值装进r0,这条指令到底取一个什么值呢?假设运行到该指令时pc寄存器的值是X,start标签相对此本指令有Y的偏移,则r0=X+Y(Y可能是负值).
针对该程序来说。
当被装载到0x0000处运行时,r0就是0,假设装载到TEXT_BASE处运行。
则r0=TEXT_BASE.
几条指令分析如下:
求正在运行的程序_start标签处地址到r0。
将_TEXT_BASE变量值装到r1,即将0x33f80000装到r1,比较r0/r1是否相等。
这个比较就是确定当前是不是从Flash中执行的,还是这段代码已装载到0x33f80000处执行的。如果不等于0x33f80000,则将FLASH中的u-boot代码搬到0x33f80000处。
具体计算如下:
ldr r2,_armboot_startàr2=0x33f80000
ldr r3,_ss_startàr3=bss段的起始地
*/
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
/*
上面几行将r0地址处的代码复制到r1处。
即从0x0000搬到0x33f80000(假设CPU复位从0x0000执行的)。
*/
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack 设置堆栈 */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
/*
上面代码中,指定从(TEXT_BASE - CFG_MALLOC_LEN)开始地址。
大小为CFG_MALLOC_LEN的内存预留给由malloc函数。因为u-boot中其它地方调用了malloc函数。
该空间就是供其动态分配的。
再向低端又预留了CFG_GBL_DATA_SIZE字节的数据(注:为global_data结构体预留的空间,看board.c)
然后为IRQ及FIQ中断预留空间,
最后将预留空间的低端地址赋给sp。
ARM堆栈由低到高增长的??
*/
clear_bss: /*将bss段数据清零*/
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
/*下面一句跳到C语言的入口处start_armboot(函数定义在board.c中) */
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/*关键代码分析结束*/