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

smw*_*dia 5 gnu ld binutils

根据ld手册上的特殊符号.,即Location Counter

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

 SECTIONS
 {
     . = 0x100
     .text: {
       *(.text)
       . = 0x200
     }
     . = 0x500
     .data: {
       *(.data)
       . += 0x600
     }
 }
Run Code Online (Sandbox Code Playgroud)

即使“.text”输入节中没有足够的数据来填充该区域,“.text”节也将被分配起始地址 0x100 和正好 0x200 字节的大小。

ld 手册还提到了输出部分的 VMA 和 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
}
Run Code Online (Sandbox Code Playgroud)

内存布局如下:

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*/
Run Code Online (Sandbox Code Playgroud)

mw2*_*215 4

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

\n\n

来自输出部分 LMA 的第一个事实。

\n\n
\n

以下链接器脚本创建三个输出节:一个称为\n .text,从 开始0x1000,一个称为\n ,即使其VMA 为,也会.mdata在该节的末尾加载\n ,以及\n 一个调用以保存地址 处的未初始化数据。符号由值 定义,这表明位置计数器保存的是 VMA 值,而不是 LMA 值.text0x2000.bss0x3000_data0x2000

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

来自位置计数器的第二个事实。

\n\n
\n

。实际上指的是距当前包含 object 的开头的字节偏移量。通常这是一条SECTIONS语句,其起始地址为0,因此.可以用作绝对地址。但是,如果.在节描述内使用 \n,则它指的是距该节开头的字节\n 偏移量,而不是绝对地址。

\n
\n\n

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

\n\n

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

\n\n
    \n
  • 如果我们在输出节定义之外引用,则语句 \xe2\x80\x93的起始地址SECTIONS为\xe2\x80\x930.
  • \n
  • 输出节的 VMA(如果我们在输出.节内部引用\n)
  • \n
\n\n

至于.data您示例中的部分,您\xe2\x80\x99是正确的:PLACE 1指定LMA位于ROM2,同时PLACE 2指定VMA位于RAM.

\n\n

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

\n