链接脚本文件、C源文件、arm汇编源文件中 地址常量 如何相互引用
这里用到了u-boot工程中的一些例子进行说明。这些地址常量就是为了在链接脚本lds文件和C语言文件(或者汇编文件)之间传递地址值,有两种方法:
1. 在链接脚本文件中定义地址常量,然后再C文件或者汇编文件中使用;(以下一、二为这种方式)
2.在C文件或者汇编文件中自定义空段,在空段中放入空数组(C文件),直接操作数组名就是空段的地址。(以下三、四为这种方式)
一、lds中的地址常量在C文件中调用
这里以清除.bss段为例,
__bss_start = .;
.bss : { *(.bss) }
_end = .;
在.bss段(默认就存在的段,用于存放未初始化的全局变量),开头和结尾引入两个地址常量__bss_start 和 _end , 在C代码中可以直接使用这两个地址常量,需要先用extern进行生命,如下:
void clean_bss(void)
{
extern int __bss_start, _end;
int *p = &__bss_start;
for (; p < &_end; p++)
*p = 0;
}
详细参见:https://blog.csdn.net/thisway_diy/article/details/101016296
二、lds中的地址常量在汇编文件中调用
仍然以清除.bss段为例,只不过这次使用汇编代码清除.bss段,连接脚本如下(参考正点原子的例程):
SECTIONS{
. = 0x87800000;
.text :
{
objs/start.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : {*(.data)}
. = ALIGN(4);
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(.COMMON) }
__bss_end = .;
}
在汇编中调用如下:
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word __bss_end
/* 复位中断 */
Reset_Handler:
/*清除BSS段*/
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0
bss_loop:
stmia r0!, {r2}
cmp r0, r1
ble bss_loop
可以看到要先将常量通过.word存放到内存中,然后再读入寄存器中进行操作,当然,由于__bss_start __bss_end本来就是常量,所以也可以借助伪指令ldr直接读入寄存器,如下(详见:https://blog.csdn.net/zhoudengqing/article/details/41346679?utm_source=blogxgwz2):
@++++clear the BSS section++++
ldr r2,=__bss_start__
ldr r3,=__bss_end__
mov r12,#0
bss_loop:
cmp r2,r3
stmltia r2!,{r12}
blt bss_loop
@----clear the BSS section----
三、C文件中的地址常量如何在lds文件中调用
其实lds是不能直接使用C文件中的地址常量的,要借助自定义段名来实现,如下__bss_end常量是C文件sections.c中定义的,如下:
char __bss_start[0] __attribute__((section(".__bss_start")));
char __bss_end[0] __attribute__((section(".__bss_end")));
可以看到__bss_end是一个长度为0的数组,作用只是用来记录地址,所以长度为0,使用 __attribute__((section(".__bss_end")))的作用是讲这个长度为0的数组放到段名为.__bss_end的段里面,由于数组长度为0,所以.__bss_end段同样不占用内存空间,.__bss_end段可以通过lds文件的配置,放在特定的位置,这里放在默认的.bss段结尾,这样我们就可以在c文件中通过数组的地址,来知道.bss段的实际结束地址,如下:gd->mon_len = (ulong)&__bss_end - (ulong)_start; 用来获取整个u-boot镜像的大小。
static int setup_mon_len(void)
{
#if defined(__ARM__) || defined(__MICROBLAZE__)
gd->mon_len = (ulong)&__bss_end - (ulong)_start;
#elif defined(CONFIG_SANDBOX) || defined(CONFIG_EFI_APP)
gd->mon_len = (ulong)&_end - (ulong)_init;
#elif defined(CONFIG_BLACKFIN) || defined(CONFIG_NIOS2)
gd->mon_len = CONFIG_SYS_MONITOR_LEN;
#elif defined(CONFIG_NDS32)
gd->mon_len = (ulong)(&__bss_end) - (ulong)(&_start);
#else
/* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */
gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
#endif
return 0;
}
在u-boot.lds文件中对.__bss_end段配置如下:
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
.bss_start段可以看成一个外层的段,里面只放了一个内层的段,段名是.__bss_start,由于.__bss_start段没有内容,所以.bss_start也没有内容,只是为了在C文件中能过获取接下来的真正有用的段.bss段的起始地址。
可以看到.bss段是真正存放未初始化全局变量的段,接下来又有一个.bss_end段,里面的内层段就是上述我们C代码中提到的段.__bss_end段。
.bss_start 和.bss_end都不占内存,空间排布上一个位于.bss段开头,一个位于.bss段结尾, 作用是把.bss段的开始地址和结束地址传递给C程序。
四、汇编文件中的地址常量如何在lds文件中调用
汇编文件中地址常量和C文件类似,也是借助段名来让lds文件使用,如下vectors.S文件中
.globl _start
/*
*************************************************************************
*
* Vectors have their own section so linker script can map them easily
*
*************************************************************************
*/
.section ".vectors", "ax"
定义了段名为.vectors的段,这里.section伪指令就不详解了,可以参考https://www.iteye.com/blog/xp9802-2012231
段.vectors在u-boot.lds文件中的调用如下:
.text :
{
*(.__image_copy_start)
*(.vectors)
arch/arm/cpu/armv7/start.o (.text*)
*(.text*)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*)
}
在外层段.text(默认就有的段)中,第一个内层段是是.__image_copy_start段(C源码中自定义的段),第二个内层段是.vectors,这个是上述汇编代码中定义的段,接下来第三个内存段是start.o文件的.text段,*表示所有start.o文件中所有已.text开头的段(文本段),第四个是其他所有汇编文件、C文件中的.text段(文本段),其实.__image_copy_start段和.vectors段都不占空间,只是为了为汇编程序或者C程序提供源码的开头地址,然后将arch/arm/cpu/armv7/start.o文件中的文本段单独列出来,是为了将中断向量表放在整个代码的最开头,接下来才是其他所有文件的文本段。
本文地址:https://blog.csdn.net/xiaoyink/article/details/108348928