转自:http://blog.sina.com.cn/s/blog_70dd16910100zab6.html
u-boot-2010.09/arch/powerpc/cpu/mpc86xx/start.S文件中的创建GOT段的代码片段如下:
// Set up GOT: Global Offset Table
// Use r12 to access the GOT
START_GOT
GOT_ENTRY(_GOT2_TABLE_)
GOT_ENTRY(_FIXUP_TABLE_)
GOT_ENTRY(_start)
GOT_ENTRY(_start_of_vectors)
GOT_ENTRY(_end_of_vectors)
GOT_ENTRY(transfer_to_handler)
GOT_ENTRY(__init_end)
GOT_ENTRY(_end)
GOT_ENTRY(__bss_start)
END_GOT
操作GOT表的函数在u-boot-2010.09/include/ppc_asm.tmpl文件中,相关代码如下:
// These definitions simplify the ugly declarations necessary for GOT
// definitions.
// Stolen from prepboot/bootldr.h, (C) 1998 Gabriel Paubert, [email protected]
// Uses r12 to access the GOT
#define START_GOT \
.section ".got2","aw"; \
.LCTOC1 = .+32768 <1>
#define END_GOT \
.text <2>
#define GET_GOT \ <3>
bl 1f ; \
.text 2 ; \
0: .long .LCTOC1-1f ; \
.text ; \
1: mflr r12 ; \
lwz r0,0b-1b(r12) ; \
add r12,r0,r12 ;
#define GOT_ENTRY(NAME) .L_ ## NAME = . - .LCTOC1 ; .long NAME <4>
#define GOT(NAME) .L_ ## NAME (r12) <5>
分析:
<1>: START_GOT定义了段“got2”,属性为“allocatable and writable”,并定义了变量.LCTOC1,.LCTOC1的值是表的最高地址;如果设表的起始地址为TABLE_START,则.LCTOC1的值为TABLE_START+0x8000;
<2>:END_GOT定义为子节.text 的起始处;
<3>:
GET_GOT用于初始化GOT表。首先程序跳转到标号为“1”的地址处(bl 1f),然后将lr的值赋值给r12(此时lr的值为“1:”的地址值)。然后令r0 = 0b - 1b(r12),0b为“0:”处的地址值,1b为“1:”处的地址值。这样r0就等于“0:”处的值,也就是.LCTOC1-1f。最后r12 = r0 + r12 = .LCTOC1 - 1f + 1f = .LCTOC1,也就是等于GOT表的最高地址。
<4>:
GOT_ENTRY定义了变量.L_NAME,其值为当前表项的地址(.)-.LCTOC1。如果设NAME的表项偏移地址为NAME_OFFSET,那么.L_NAME = . - .LCTOC1 = TABLE_START + NAME_OFFSET – (TABLE_START + 0x8000)= NAME_OFFSET - 0x8000。之后将名字为NAME的offset值写入当前表项,这些offset值是在编译的时候确定的。
备注:##是字符串连接符,比如L_##TOM其实就是字符串L_TOM
<5>:
GOT(NAME)的值定义为.L_NAME(r12),这里面r12的值为表的最高地址,也就是.LCTOC1的值。
这样GOT(NAME) = .L_NAME + r12= .L_NAME + .LCTOC1 = NAME_OFFSET - 0x8000 + TABLE_START + 0x8000 = NAME_OFFSET + TABLE_START,也就是NAME所在表项的地址。这样,通过查表,就可以找到当初存储在表中的名字为NAME的offset值。
小结:
START_GOT用于定义表的开始,END_GOT用于定义表的结束,GOT_ENTRY用于将offset写入表中,GOT用于从表中读出 offset,GET_GOT用于将表进行初始化
动态库要解决的一个问题是代码/变量地址在编译时不能确定,GOT就是用来解决这个问题的技术。 u-boot运行时要从Flash搬到RAM高端,RAM大小是运行时检测出来的,编译时不能确定,这和动态库面对的问题相同,正好可以用GOT技术解决
参考资料:
http://blog.csdn.net/foriner/article/details/5847501
关于GOT表的机制可以参考我的一篇博文:
http://blog.sina.com.cn/s/blog_70dd16910100r1gi.html
第二部分 u-boot.lds解读
2.1:board/freescale/mpc8641hpcn/目录下有两个文件:u-boot.lds和config.mk
u-boot.lds定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置,从中可以找到u-boot的函数入口。config.mk文件用于设置TEXT_BASE的地址,该地址就是代码运行的链接地址。
下面我们来分析u-boot.lds和config.mk文件。
备注:我分析的U-Boot版本为:u-boot-2010.09.tar.bz2
2.2:具体分析及注解如下
OUTPUT_ARCH(powerpc)
//指定输出的可执行文件的平台为powerpc
SECTIONS
{
//指定各个段内容,只读的各个节(section)均放到代码段(text)中
.interp : { *(.interp) }
//定义 .interp段,该段由所有代码的.interp段共同组成
.hash : { *(.hash) }
//.hash段: 由所有代码的.hash段共同组成,hash表允许在不对全表元素进行线性搜索的情况下快速访问所有的符合表项。
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.rel.text : { *(.rel.text) }
.rela.text : { *(.rela.text) }
.rel.data : { *(.rel.data) }
.rela.data : { *(.rela.data) }
.rel.rodata : { *(.rel.rodata) }
.rela.rodata : { *(.rela.rodata) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.bss : { *(.rel.bss) }
.rela.bss : { *(.rela.bss) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init : { *(.init) }
.plt : { *(.plt) }
.text :
//定义文本段
{
arch/powerpc/cpu/mpc86xx/start.o (.text)
//文本段的第一部分start.S,后跟其他做文本段
arch/powerpc/cpu/mpc86xx/traps.o (.text)
arch/powerpc/cpu/mpc86xx/interrupts.o (.text)
arch/powerpc/cpu/mpc86xx/cpu_init.o (.text)
arch/powerpc/cpu/mpc86xx/cpu.o (.text)
arch/powerpc/cpu/mpc86xx/speed.o (.text)
common/dlmalloc.o (.text)
lib/crc32.o (.text)
arch/powerpc/lib/extable.o (.text)
lib/zlib.o (.text)
drivers/bios_emulator/atibios.o (.text)
*(.text)
*(.got1)
}
_etext = .;
PROVIDE (etext = .);
.rodata :
{
*(.eh_frame)
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
}
.fini : { *(.fini) } =0
.ctors : { *(.ctors) }
.dtors : { *(.dtors) }
. = (. + 0x00FF) & 0xFFFFFF00;
//地址对齐(地址低8位为0)
_erotext = .;
PROVIDE (erotext = .);
.reloc :
//从定位段.reloc的组成
{
*(.got) // got段
_GOT2_TABLE_ = .;
//_GOT2_TABLE_值为当前地址,同时也是.got段结束地址,另外也是.got2段起始地址
*(.got2) // got2段
_FIXUP_TABLE_ = .;
//_FIXUP_TABLE_值为当前地址,同时也是.got2段结束地址;另外也是.fixup段起始地
*(.fixup)
}
__got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2;
//__got2_entries符号定义,其值为GOT表项个数,每个表项占4字节
__fixup_entries = (. - _FIXUP_TABLE_) >> 2;
//__fixup_entries值为.fixup的字个数
.data : //数据段
{
*(.data)
*(.data1)
*(.sdata)
*(.sdata2)
*(.dynamic)
CONSTRUCTORS
}
_edata = .; //数据段结束
PROVIDE (edata = .);
. = .;
__u_boot_cmd_start = .; //定义u-boot命令起始地址,board_init_r中有调用
.u_boot_cmd : { *(.u_boot_cmd) } //u_boot_cmd段
__u_boot_cmd_end = .; //定义u-boot命令结束地址
. = .;
__start___ex_table = .;
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
. = ALIGN(256); //256个字节对齐
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
. = ALIGN(256);
__init_end = .;
// start.S中relocate_code函数计算u-boot镜像text长度,利用了__init_end,后面不再搬运,直接清零clear_bss
__bss_start = .; // bss代码段起始地址
.bss (NOLOAD) :
{
*(.sbss) *(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
. = ALIGN(4);
}
_end = . ; //_end 符号定义 bss代码段结束地址,u-boot镜像结束地址
PROVIDE (end = .);
}// SECTIONS 对各段定义完毕
同时在board/freescale/mpc8641hpcn/ config.mk文件中U-Boot在RAM中的程序入口地址,具体内容如下:
#
# mpc8641hpcn board
# default CCSRBAR is at 0xff700000
# assume U-Boot is less than 0.5MB
#
TEXT_BASE = 0xeff00000
解释:TEXT_BASE是U-Boot在RAM中的程序入口地址,U-Boot启动以后,在RAM中就运行在这个地址往上的空间。这个地址是U-Boot的指令最开始的地址(对很多PowerPC,例如我们的MPC8641HPCN,一般在这个地址开始的一个0x100字节的偏移处)。在Flash中启动的话,这个地址就在Flash中。
注意的是:TEXT_BASE是链接地址,UBoot第一个阶段的初始化运行的时间地址,有时候会和这一地址不一致,但是MMU开启之后,所用的地址均是链接地址了。