LD 和 NOLOAD 部分:了解奇怪的 LMA 值

LD and NOLOAD sections: Understanding strange LMA value

我正在编写一个裸机内核,在 NOLOAD 部分的情况下,我无法理解 ld 的输出。我正在声明仅在激活 MMU 时才存在的符号,因此 VMA 和 LMA 不相同。 我曾经这样声明这样的部分:

_foobar_start = .;
.foobar : AT(ADDR(.foobar) - VA_PA_OFFSET)
{
    *.o(.foobar.section*)
}
_foobar_end = .;

现在我的部分内容之一由引导加载程序加载,所以我只想声明要在运行时使用的 VMA 符号,以访问数据,所以我尝试了 NOLOAD 属性:

_foobar_start = .;
.foobar (NOLOAD) :
{
    . += SIZE_OF_FOOBAR;
}
_foobar_end = .;

我知道在这种情况下我不关心 LMA,但我希望看到类似的内容,其中 LMA = VMA(参见 LD manual):

.foobar         0x000000004002c000     0x353c load address 0x000000004002c000

但是我得到了一些我不明白的奇怪的 LMA:

.foobar         0x000000004002c000     0x353c load address 0x0000000001900000

如果我使用

在脚本中强制 VMA = LMA
.foobar (NOLOAD) : AT(_foobar_start)

一切正常,我只看到了

.foobar         0x000000004002c000     0x353c

即使不强制 VMA = LMA,生成的 ELF 也可以,但由于其他部分(toto 是另一部分),我在编译时收到一些警告:

warning: dot moved backwards before `.toto

我想阅读这些内容。

指定 NOLOAD 时我没有得到 LMA = VMA 是有原因的吗?

编辑:这是触发问题的完整链接器文件。我添加了一些评论来指出问题

OUTPUT_ARCH(CONFIG_LINKER_ARCH)
OUTPUT_FORMAT(CONFIG_LINKER_FORMAT)

_kern_phys_base = OCRAM_BASE_PA + OCRAM_OFFSET;

_kern_offset = KERNEL_VA_PA_OFFSET;

SECTIONS
{
    . = _kern_phys_base;

    _start_image_addr = .;

    . = ALIGN(0x1000);
    _early_text_start = .;
    .early_text :
    {
        KEEP(*(.text.boot))
        KEEP(*(.text.mmu))
    }

    . = ALIGN(4);
    .early_rodata :
    {
        *(.rodata.boot*)
        *(.rodata.mmu*)
    }
    _early_text_end = .;

    . = ALIGN(0x1000);
    _early_data_start = .;
    .early_data :
    {
        *(.data.boot)
    }
    _early_data_end = .;

    . = ALIGN(0x4000);
    _early_bss_start = .;
    .early_bss :
    {
        *(.bss.mmu)
        *(.bss.boot)
    }
    . = ALIGN(16);
    _early_bss_end = .;

    _early_end = .;

    /*
     * The following part is accessed only once the MMU has been
     * activated, so we first need to jump into "high" memory
     */
    . += _kern_offset;

    . = ALIGN(0x1000);
    _text_start = .;
    .text : AT(ADDR(.text) - _kern_offset)
    {
        *(.text .text.*)
    }
    _text_end = .;

    . = ALIGN(0x1000);
    _rodata_start = .;
    .rodata : AT(ADDR(.rodata) - _kern_offset)
    {
        *(.rodata*)
    }
    _rodata_end = .;

    . = ALIGN(4);
    _arm_extab_start = .;
    .ARM.extab : AT(ADDR(.ARM.extab) - _kern_offset)
    {
        *(.ARM.extab)
    }
    _arm_extab_end = .;

    . = ALIGN(4);
    _arm_exidx_start = .;
    .ARM.exidx : AT(ADDR(.ARM.exidx) - _kern_offset)
    {
        *(.ARM.exidx)
    }
    _arm_exidx_end = .;

    . = ALIGN(4);
    _kernel_debug_info_start = .;
    _kernel_debug_info_end = .;

    . = ALIGN(4);
    _emergency_code_vstart = .;
    _emergency_code_vend = .;

    /*
     * This is where I use the NOLOAD, with AT this time
     * This 'archive' part is not located in OCRAM, but some
     * where else in RAM
     */
    _archive_point_save = .;
    . = DDR_BASE_VA;
    . = ALIGN(512);
    _archive_start = .;
    .archive_data (NOLOAD) : AT(_archive_start)
    {
        codes.o(.rawdata*)
    }
    _archive_end = .;
    . = _archive_point_save;

    /* Back to OCRAM VMA */
    . = ALIGN(0x1000);
    _data_start = .;
    .data : AT(ADDR(.data) - _kern_offset)
    {
        *(.data*)
    }
    _data_end = .;

    . = ALIGN(32);
    _bss_start = .;
    .bss : AT(ADDR(.bss) - _kern_offset)
    {
        *(.bss .bss.*)
    }
    . = ALIGN(16);
    _bss_end = .;

    /*
     * Second location, also in RAM, just after the '.archive_data' section
     * This time I didn't put the AT to show the difference in output
     */
    _dyn_archive_point_save = .;
    . = _archive_end;
    . = ALIGN(0x1000);
    _dyn_archive_start = .;
    .dyn_archive (NOLOAD) :
    {
        . += _dyn_archive_space;
    }
    _dyn_archive_end = .;
    . = _dyn_archive_point_save;

    /* Back to OCRAM VMA */
    . = ALIGN(0x1000);
    _kernel_stack_guard = .;
    . += 0x1000;

    .stack (NOLOAD) :
    {
        _kernel_stack_end = .;
        /* 2 pages of 4 kB */
        . += 0x2000;
        _kernel_stack_start = .;
    }

    _kernel_image_end = .;
}

这里是 objdump -x 的输出:

build/kernel/kernel.elf:     file format elf32-littlearm
build/kernel/kernel.elf
architecture: arm, flags 0x00000102:
EXEC_P, D_PAGED
start address 0x00910000

Program Header:
0x70000001 off    0x0003072c vaddr 0x4003072c paddr 0x0093072c align 2**2
         filesz 0x000000a0 memsz 0x000000a0 flags r--
    LOAD off    0x00010000 vaddr 0x00910000 paddr 0x00910000 align 2**16
         filesz 0x00002828 memsz 0x00008000 flags rwx
    LOAD off    0x00018000 vaddr 0x40018000 paddr 0x00918000 align 2**16
         filesz 0x000199e0 memsz 0x0001fa28 flags rwx
    LOAD off    0x00038000 vaddr 0x41028000 paddr 0x01928000 align 2**16
         filesz 0x00000000 memsz 0x00100000 flags rw-
    LOAD off    0x00039000 vaddr 0x40039000 paddr 0x40039000 align 2**16
         filesz 0x00000000 memsz 0x00002000 flags rw-
    LOAD off    0x00040000 vaddr 0x41000000 paddr 0x41000000 align 2**16
         filesz 0x00000000 memsz 0x00028000 flags rw-
private flags = 5000200: [Version5 EABI] [soft-float ABI]

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .early_text   000015c0  00910000  00910000  00010000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .early_rodata 00000030  009115c0  009115c0  000115c0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .early_data   00000828  00912000  00912000  00012000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  3 .early_bss    00004000  00914000  00914000  00012828  2**14
                  ALLOC
  4 .text         000142d8  40018000  00918000  00018000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  5 .rodata       000036c0  4002d000  0092d000  0002d000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .ARM.extab    0000006c  400306c0  009306c0  000306c0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .ARM.exidx    000000a0  4003072c  0093072c  0003072c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .archive_data 00028000  41000000  41000000  00040000  2**0
                  ALLOC
  9 .data         000009e0  40031000  00931000  00031000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 10 .bss          00006048  400319e0  009319e0  000319e0  2**3
                  ALLOC
 11 .dyn_archive  00100000  41028000  01928000  00038000  2**0
                  ALLOC
 12 .stack        00002000  40039000  40039000  00039000  2**0
                  ALLOC
 13 .comment      0000002d  00000000  00000000  000319e0  2**0
                  CONTENTS, READONLY
 14 .ARM.attributes 00000037  00000000  00000000  00031a0d  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
no symbols

如您所见:.archive_data.stack 正确地得到 VMA = LMA 但 .dyn_archive 没有。如果我删除 .archive_dataAT,我得到的行为与地址 0x1900000

.dyn_archive 相同

看来我最后的评论是有效的,正如确认的那样(至少在我们对手册的解释范围内)on the binutils ML

由 "LMA goes backwards" 场景触发的警告,每次手动更新 . 时正确更新 LMA 是正确的方法。