如何确定位置计数器“.”的基地址VMA和LMA什么时候不同?

How to decide the base address for the location counter "." when VMA and LMA are different?

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

Note: . actually refers to the byte offset from the start of the current containing object. Normally this is the SECTIONS statement, whose start address is 0, hence . can be used as an absolute address. If . is used inside a section description however, it refers to the byte offset from the start of that section, not an absolute address. Thus in a script like this:

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

The '.text' section will be assigned a starting address of 0x100 and a size of exactly 0x200 bytes, even if there is not enough data in the `.text' input sections to fill this area.

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

Every loadable or allocatable output section has two addresses. The first is the VMA, or virtual memory address. This is the address the section will have when the output file is run. The second is the LMA, or load memory address. This is the address at which the section will be loaded. In most cases the two addresses will be the same. An example of when they might be different is when a data section is loaded into ROM, and then copied into RAM when the program starts up (this technique is often used to initialize global variables in a ROM based system). In this case the ROM address would be the LMA, and the RAM address would be the VMA.

所以我的问题是:

如果一个输出段指定了不同的VMA和LMA,字节偏移的基地址是什么. ?

在下面的示例中,.data 部分具有不同的 VMA 和 LMA。我的理解是PLACE 1指定LMAROM2,而PLACE 2指定VMARAM?那么 .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*/

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

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

The following linker script creates three output sections: one called .text, which starts at 0x1000, one called .mdata, which is loaded at the end of the .text section even though its VMA is 0x2000, and one called .bss to hold uninitialized data at address 0x3000. The symbol _data is defined with the value 0x2000, which shows that the location counter holds the VMA value, not the LMA value.

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

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

. actually refers to the byte offset from the start of the current containing object. Normally this is the SECTIONS statement, whose start address is 0, hence . can be used as an absolute address. If . is used inside a section description however, it refers to the byte offset from the start of that section, not an absolute address.

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

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

  • SECTIONS语句的起始地址——即0——如果我们在输出段定义之外引用.
  • 一个输出部分的VMA,如果我们参考里面的. 该输出部分

至于您示例中的 .data 部分,您是对的:PLACE 1 指定 LMA 在 ROM2PLACE 2 指定 VMA 在 RAM.

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

后来又看了几个ld脚本,有了以下的认识:

我写了下面的片段来说明 部分声明语句

因此,如上所示,ld 脚本中的 section 声明有 2 个重要事项需要指定:

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

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

  • 精确到特定地址
  • 或附加到内存区域

这2种方式在段声明语句中的位置不同,如上所示,在大括号之前或之后。

一个部分本身就是一个刚体。位置计数器 . ONLY 代表其包含对象内的偏移量。 不多也不少既不是VMA也不是LMA。

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

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

  .data_flash     : {_data_flash = .;} >CODE