欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

链接脚本文件、C源文件、arm汇编源文件中 地址常量 如何相互引用

程序员文章站 2022-03-23 09:11:11
1...

 这里用到了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

相关标签: linux