Search code examples
ldriscvbare-metal

LD linker: define section in ELF file with arbitrary unallocated data


I have a very simple linker script for a RISC-V bare-metal target:

link.ld:

MEMORY
{
    ram (rwx) : ORIGIN = 0, LENGTH = 0x1000
}

SECTIONS
{
    . = 0x00000000;
    .text : {
        . = ALIGN(4);
        *(.text)
        *(.text.startup)
        *(.rodata)
    } > ram
    .data : {
        . = ALIGN(4);
        *(.data)
    } > ram
    .bss : {
        . = ALIGN(4);
        __BSS_START__ = .;
        *(.bss*)
        *(COMMON)
        __BSS_END__ = .;
    } > ram
    _end = .;
    _sp_start = 0x00000fff;
    _start = 0;
}

Everything works fine, I can compile and run embedded applications.

I'm trying to define an ELF file section that doesn't count for the allocated space, isn't loaded, etc. Pretty much the same thing as the debug symbols sections, just with arbitrary data.

So, I checked the ld docs about Output Section Type:

DSECT
COPY
INFO
OVERLAY
These type names are supported for backward compatibility, and are rarely used. They all have the same effect: the section should be marked as not allocatable, so that no memory is allocated for the section when the program is run.

So I updated my linker script to:

MEMORY
{
    ram (rwx) : ORIGIN = 0, LENGTH = 0x1000
}

SECTIONS
{
    . = 0x00000000;
    .text : {
        . = ALIGN(4);
        *(.text)
        *(.text.startup)
        *(.rodata)
    } > ram
    .data : {
        . = ALIGN(4);
        *(.data)
    } > ram
    .bss : {
        . = ALIGN(4);
        *(.bss*)
        *(COMMON)
    } > ram
    .metadata (INFO): {
        . = ALIGN(4);
        *(.metadata)
    }
    _end = .;
    _sp_start = 0x00000fff;
    _start = 0;
}

And in my code, I added:

char metadata_buffer[] __attribute__ ((section (".metadata"))) = "METADATA";

Compiling, I see the new .metadata section in the ELF file, but its size isn't 0:

$ readelf -e app.elf 
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V

...

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 001000 000206 00  AX  0   0  2
  [ 2] .eh_frame         PROGBITS        00000208 001208 0000f4 00   A  0   0  4
  [ 3] .data             PROGBITS        000002fc 0012fc 000000 00  WA  0   0  1
  [ 4] .bss              NOBITS          000002fc 0012fc 000000 00  WA  0   0  1
  [ 5] .metadata         PROGBITS        000002fc 0012fc 000009 00   W  0   0  4

...

  [17] .shstrtab         STRTAB          00000000 007deb 0000b4 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x00000000 0x00000000 0x002fc 0x002fc RWE 0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text .eh_frame 

The size of the only program header didn't changed.

If in my code I do:

char metadata_buffer[10000] __attribute__ ((section (".metadata"))) = "METADATA";

I get this error when linking:

riscv32-unknown-elf/bin/ld: pe_gemv_0_0_0.elf section `.metadata' will not fit in region `ram'
riscv32-unknown-elf/bin/ld: region `ram' overflowed by 8716 bytes

Why? The .metadata section is not in ram.

If I define a dummy memory region and put .metadata there, I can link. I don't understand why do I need to do this though.

MEMORY
{
    ram (rwx) : ORIGIN = 0, LENGTH = 0x1000
    other (rwx) : ORIGIN = 0, LENGTH = 0x1000000
}

SECTIONS
{

...

    .metadata (INFO): {
        . = ALIGN(4);
        *(.metadata)
    } > other
}

Any help is appreciated! Cheers!


Solution

  • Regarding why the .metadata output section gets allocated within the MEMORY command's ram region, check out the following text from the MEMORY chapter of the binutils docs:

    The linker will place other sections which are not explicitly mapped into a memory region into the ‘ram’ memory region.

    .. where the 'ram' region alludes to the example provided in that chapter.

    So, if you have not associated an output section within the SECTION command invocation in your linker script to a specific MEMORY region, binutils defaults to using the only MEMORY region that you have specified for that unassociated output section, which is the ram memory region.

    This is also why you get the region ram overflowed error.

    Hope that helps.