U-Boot2018.11 下ARMV7 u-boot-spl.lds详解
u-boot-spl.lds 源码解读
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*/
/* 指定输出可执行文件:"elf32位小端格式" */
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/* 指定输出可执行文件的目标架构:"arm" */
OUTPUT_ARCH(arm)
/* 指定输出可执行文件的入口地址(起始代码段):"_start" */
ENTRY(_start)
SECTIONS
{
/*
* 设置0的原因是,arm内核的处理器,三点后默认是从0x00000000处启动
* 1.以stm32为例片内的nor-flash起始地址是0x08000000,这里是我们烧写u-boot.bin的位置
* 2.上电后,系统会自动将该地址(0x0800:0000)映射到0x0000:0000(硬件完成)
*/
. = 0x00000000;
/*
* 代码以4字节对齐 .text为代码段
* 各个段按先后顺序依次排列
* 在cortex-m的内核中,首地址存放的是主堆栈的地址,其次存放中断向量表
*/
. = ALIGN(4);
.text :
{
__image_copy_start = .; /* u-boot里默认会将u-boot的镜像拷贝到ram(sdram,ddr....)中执行 */
*(.vectors) /* 存放的是中断向量表 */
CPUDIR/start.o (.text*) /* 存放CPUDIR/start.o中的所有.text段 */
*(.text*) /* 存放其他.o中的所有.text段 */
*(.glue*) /* 存放其他.o中的所有.glue段 */
}
/* 从上面的结构中可以发现这种格式都是:地址(.段) 这样的形式出现 */
/*
* .rodata段,确保是以4字节对齐处开始
*/
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } /* 从名称上就可以知道,按名称依次存放其他.o文件中的.rodata */
/*
*data段,确保是以4字节对齐处开始
*/
. = ALIGN(4);
.data : {
*(.data*)
}
/*
* u_boot_list段,确保是以4字节对齐处开始
* 这里存放的都是u_boot_list中的函数
* 例如:base/bdinfo/blkcache/cmp....
* 具体的可参看./u-boot.map .u_boot_list
* tips:要想优化编译出来的u-boot.bin大小,可以参看此文件进行对照裁剪
*/
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
/*
* binman_sym_table段,确保是以4字节对齐处开始
* binman实现的功能是让c代码通过binman_*的函数接口字节调用镜像中的个别函数
* 具体可参看binman_sym.h中的接口
*/
. = ALIGN(4);
.binman_sym_table : {
__binman_sym_start = .;
KEEP(*(SORT(.binman_sym*)));
__binman_sym_end = .;
}
/*
* __image_copy_end段,确保是以4字节对齐处开始
* 镜像拷贝的完成
*/
. = ALIGN(4);
__image_copy_end = .;
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.end :
{
*(.__end)
}
/* bin文件结束 */
_image_binary_end = .;
.bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss*)
. = ALIGN(4);
__bss_end = .;
}
__bss_size = __bss_end - __bss_start;
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.hash : { *(.hash*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
}
#if defined(CONFIG_SPL_MAX_SIZE)
ASSERT(__image_copy_end - __image_copy_start < (CONFIG_SPL_MAX_SIZE), \
"SPL image too big");
#endif
#if defined(CONFIG_SPL_BSS_MAX_SIZE)
ASSERT(__bss_end - __bss_start < (CONFIG_SPL_BSS_MAX_SIZE), \
"SPL image BSS too big");
#endif
#if defined(CONFIG_SPL_MAX_FOOTPRINT)
ASSERT(__bss_end - _start < (CONFIG_SPL_MAX_FOOTPRINT), \
"SPL image plus BSS too big");
#endif
相关知识总结
[汇编]SECTIONS
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
SECTIONS
{
. = 0x00000000; // 应该是存储地址,存储地址(加载的地址)
. = ALIGN(4); // 4字节对齐
.text : //此处应该是secname 段名
{
cpu/arm920t/start.o (.text)//大括号,contents段,指示该段存放的内容
*(.text)
}
. = ALIGN(4); //以下类似
.rodata : { *(.rodata) }
标示 | 规则 |
---|---|
secname | 段名, 比如常见的.text/.data等等 |
contents | 决定哪些内容存放在此段 |
start | 本段的连接地址(实际运行地址) |
AT(ldadr) | 存储地址(加载的地址) |
[汇编]arm的.text段/.code段/.bss段…
-
bss段(bsssegment)
通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS段属于静态内存分配. -
data段(datasegment)
通常是指用来存放程序中已初始化的全局变量的一块内存区域。BSS段属于静态内存分配. -
text段(textsegment)
通常是指用来存放程序执行代码的一块内存区域。就是我们编写的程序代码(函数等等). -
rodata段(rodatasegment)
通常是指用来存放C中的字符串和宏定义的常量.
总结
总的来说,我们能大概的了解到我们编写的c/c++源代码大概经历先单个编译产生.o,这里面包含了这个文件的.text/.bss/.data等等段,而我们的链接文件,最后会大一统,将工程下的所有目标文件,按链接脚本的规则(按各种段分门别类的)把各个文件汇总,最终产生.bin文件.