如何确定位置计数器的基址"。"当VMA和LMA不同时?

时间:2016-07-13 00:55:22

标签: gnu ld binutils

根据ld手册on the special symbol ., i.e.the Location Counter

  

注意:。实际上是指从开始的字节偏移量   当前包含对象。通常这是SECTIONS声明,   因此,其起始地址为0。可以用作绝对地址。   如果。然而,在部分描述中使用它,它指的是   从该部分的开头起的字节偏移量,而不是绝对地址。   因此在这样的脚本中:

 SECTIONS
 {
     . = 0x100
     .text: {
       *(.text)
       . = 0x200
     }
     . = 0x500
     .data: {
       *(.data)
       . += 0x600
     }
 }
     

' .text' section将被分配一个起始地址0x100和a   正好大小为0x200字节,即使数据中没有足够的数据   `.text区段'输入部分来填补这个区域。

ld手册也说output section's VMA and LMA

  

每个可加载或可分配的输出部分都有两个地址。该   首先是VMA或虚拟内存地址。这是地址了   部分将在输出文件运行时生成。第二个是LMA,   或加载内存地址。这是该部分的地址   加载。在大多数情况下,两个地址将是相同的。一个   它们可能不同的例子是数据部分   加载到ROM中,然后在程序启动时复制到RAM中   (此技术通常用于初始化ROM中的全局变量   基于系统)。在这种情况下,ROM地址将是LMA,并且   RAM地址是VMA。

所以我的问题是:

如果使用不同的VMA和LMA指定输出节,那么字节偏移.的基址是什么?

在下面的示例中,.data部分具有不同的VMA和LMA。我的理解是PLACE 1指定LMA位于ROM2,而PLACE 2指定VMA位于RAM?那么.部分中.data符号的基址是什么?

SECTIONS
{
    .text :
    {
        *(.text)
    } > REGION_TEXT

    .rodata :
    {
        *(.rodata)
        rodata_end = .;
    } > REGION_RODATA

    .data : AT (rodata_end) <=========== PLACE 1
    {
        data_start = .;
        *(.data)
    } > REGION_DATA <=========== PLACE 2

    data_size = SIZEOF(.data);
    data_load_start = LOADADDR(.data);

    .bss :
    {
        *(.bss)
    } > REGION_BSS
}

内存布局如下:

MEMORY
    {
        ROM : ORIGIN = 0, LENGTH = 2M            /*0M ~ 2M*/
        ROM2 : ORIGIN = 0x10000000, LENGTH = 1M  /*256M ~ 257M*/
        RAM : ORIGIN = 0x20000000, LENGTH = 1M   /*512M ~ 513M*/
    }

REGION_ALIAS("REGION_TEXT", ROM);     /*0M ~ 2M*/
REGION_ALIAS("REGION_RODATA", ROM2);  /*256M ~ 257M*/
REGION_ALIAS("REGION_DATA", RAM);     /*512M ~ 513M*/
REGION_ALIAS("REGION_BSS", RAM);      /*512M ~ 513M*/

2 个答案:

答案 0 :(得分:2)

要回答您的问题,可以使用官方ld文档中的两个事实。

来自Output Section LMA的第一个事实。

  

以下链接描述文件创建三个输出节:一个调用   .text,从0x1000开始,一个名为.mdata,已加载   在.text部分的末尾,即使其VMA为0x2000,也是如此   一个名为.bss的人在地址0x3000处保留未初始化的数据。该   符号_data定义为值0x2000,表示   位置计数器保存VMA值,而不是LMA值

 SECTIONS
   {
   .text 0x1000 : { *(.text) _etext = . ; }
   .mdata 0x2000 :
     AT ( ADDR (.text) + SIZEOF (.text) )
     { _data = . ; *(.data); _edata = . ;  }
   .bss 0x3000 :
     { _bstart = . ;  *(.bss) *(COMMON) ; _bend = . ;}
 }

来自The Location Counter的第二个事实。

  

即可。实际上是指从当前开始的字节偏移量   包含对象。通常这是SECTIONS语句,其中   起始地址为0,因此.可用作绝对地址。如果.   然而,在部分描述中使用它,它指的是字节   偏离该部分的开头,而不是绝对地址。

将这两条信息放在一起,可以说位置计数器通过给出当前包含对象(SECTIONS语句或输出部分)的起始地址的偏移量来指定VMA值。

所以位置计数器的绝对基地址是

  • SECTIONS语句的起始地址 - 即0 - 如果我们在输出部分定义之外引用.
  • 如果我们在内部引用.,则输出部分的VMA 那个输出部分

对于您示例中的.data部分,您是对的:PLACE 1指定LMA位于ROM2,而PLACE 2指定VMA位于{{1} }}

由于位置计数器在部分描述中使用时,指的是从该部分开始的字节偏移量,RAM部分中.符号的基址是{{1 }}。但是,这是一个相对地址,它对应于.data部分的VMA绝对地址0。顺便说一句,这与上述事实一致0x20000000指定VMA位于.data内存区域(别名PLACE 2)。
如果您的示例是真实示例,则可以使用RAM链接描述文件内置函数轻松检查刚刚声明的内容,以获取REGION_DATA部分的VMA。

答案 1 :(得分:0)

在我阅读了几个ld脚本之后,我得出了以下理解:

为了说明部分声明声明,我写了下面的代码片段。

enter image description here

如上所示,ld脚本中的private String android_id = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID); 声明有两个要指定的重要事项:

  • 加载时的LMA
  • 运行时的VMA

我们可以通过两种方式指定这些地址:

  • 正好在特定地址
  • 或附加到内存区域

这两种方式在部分声明声明中具有不同的位置,如上所示,在花括号之前或之后。

部分本身是 RIGID BODY 。位置计数器section 代表其包含对象内的偏移量。 不多,不少既不是VMA也不是LMA。

偏移的基数可以是包含部分的VMA或LMA,取决于它是您正在谈论的运行时或加载时间。

但是在下面的符号赋值语句或章节声明中的其他符号操作中, VMA被用作. 的基础。

.