0
votes

I'm writing a baremetal kernel, and I have trouble understanding the output of ld in the case of a NOLOAD section. I am declaring symbols that only exists with MMU activated, so the VMA and LMA are not the same. I used to declaring such a section like this:

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

Now one of my section's content is loaded by the boot loader, so I only want to declare the VMA symbols to be used at runtime, to access the data, so I tried the NOLOAD attribute:

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

I know I don't care about the LMA in this case, but I expected to see something like, where LMA = VMA (see LD manual):

.foobar         0x000000004002c000     0x353c load address 0x000000004002c000

but I get something with a strange LMA that I don't make sense of:

.foobar         0x000000004002c000     0x353c load address 0x0000000001900000

If I force the VMA = LMA in the script using

.foobar (NOLOAD) : AT(_foobar_start)

everything seems fine and I see only

.foobar         0x000000004002c000     0x353c

Even without forcing VMA = LMA, the resulting ELF is ok, but I get some warnings at compile time because of other sections (toto is another section):

warning: dot moved backwards before `.toto

and I'd like to get read of those.

Is there a reason I don't get LMA = VMA when specifying NOLOAD ?

EDIT: here is full linker file triggering the problem. I added some comments to pin points the issues

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 = .;
}

And here is the output of 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

As you can see: .archive_data and .stack correctly get VMA = LMA but .dyn_archive doesn't. If I remove the AT of .archive_data, I get the same behavior than .dyn_archive with the address 0x1900000

1
Any chance you can post your complete linker file. As well an objdump -x of your linked executable might be useful.Michael Petch
I'll try to make a short version of my linker file which trigger the same behavior. As for the objdump -x, I can't, sorry, but I'll be able to print some sub part. Update soon.Vinz
@Michael-Petch: The output is clean enough, I put it entirely.Vinz
From a new batch of tests, it seems that AT will permanently modify the LMA: if I remove subsequent use of AT and leave the first one, the LMA addresses are still valid. I'm trying to find a reference for this behavior in the manual, but it would explain why I need to use AT for these sections that are "someplace else"Vinz
The linker script manual does have the statement: Otherwise if a memory region can be found that is compatible with the current section, and this region contains at least one section, then the LMA is set so the difference between the VMA and LMA is the same as the difference between the VMA and LMA of the last section in the located region.Hsu Hau

1 Answers

0
votes

It seems my last comment is valid, as confirmed (at least to the extend of our interpretation of the manual) on the binutils ML

The warning where triggered by the "LMA goes backwards" scenario, and correctly updating LMA each time . is updated by hand is the correct way to go.