How to get the size of an input section (to place it at the end of memory)
I have the following linker script:
.data
& .bss
are placed into ram, then a .heap
section fills the remaining memory.
Now I want to add a .noinit
section that always gets placed at the end of the memory. That is so it gets ignored by bootloaders and the like.
I still want my .heap
section to occupy all available space between .bss
and .noinit
, but for this I need to know the size of the .noinit
section.
A naive approach failed:
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
{
. = ALIGN(4);
_sbss = . ;
_szero = .;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = . ;
_ezero = .;
} > ram
/* heap section */
.heap (NOLOAD) :
{
. = ALIGN(4);
_sheap = . ;
. = ORIGIN(ram) + LENGTH(ram) - SIZEOF(.noinit);
_eheap = . ;
} > ram
/*
* collect all uninitialized sections that go into RAM
*/
.noinit (NOLOAD) :
{
. = ALIGN(4);
__noinit_start = .;
*(.noinit)
__noinit_end = .;
} > ram
Here SIZEOF(.noinit)
is always 0 because the section is defined after that statement.
But in fact what I want is SIZEOF(*(.noinit))
- however this is a syntax error.
So how do I get the size of an input section without placing it into an output section first?
Solution 1:
Not exactly kosher, but after digging through the source code for GNU ld it looks like you can specify the address of section after it is defined.
The following linker script should give you the desired behavior:
MEMORY {
ram (rwx) : ORIGIN = 0x10000, LENGTH = 0x0002000
}
SECTIONS {
.bss (NOLOAD) : {
. = ALIGN(4);
_sbss = . ;
_szero = .;
*(.bss .bss.*)
*(COMMON)
/* Note: Just for example - padding .bss by 0x100 bytes so its not empty */
. = 0x100;
. = ALIGN(4);
_ebss = . ;
_ezero = .;
} > ram
/* create .noinit section */
.noinit (NOLOAD): {
. = ALIGN(4);
__noinit_start = .;
/* Note: Just for example - padding .noinit by 0x100 bytes so its not empty */
. = 0x100;
*(.noinit)
__noinit_end = .;
} > ram
/* place .heap after .bss */
.heap _ebss (NOLOAD) : {
. = ALIGN(4);
_sheap = . ;
. = ABSOLUTE(ORIGIN(ram) + LENGTH(ram) - SIZEOF(.noinit));
_eheap = . ;
} > ram
/* update .noinit to be placed at end of heap */
.noinit _eheap (NOLOAD) : { } > ram
}
The output of an empty binary linked with the above script show the correct placement of sections and symbols:
echo | gcc -x c++ - -nostartfiles -T linkerScript.ld -Xlinker --orphan-handling=discard -fuse-ld=bfd && objdump -th a.out
a.out: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .bss 00000100 0000000000010000 0000000000010000 00001000 2**0
ALLOC
1 .noinit 00000100 0000000000011f00 0000000000011f00 00001000 2**0
ALLOC
2 .heap 00001e00 0000000000010100 0000000000010100 00001000 2**0
ALLOC
SYMBOL TABLE:
0000000000010000 l d .bss 0000000000000000 .bss
0000000000011f00 l d .noinit 0000000000000000 .noinit
0000000000010100 l d .heap 0000000000000000 .heap
0000000000010100 g .heap 0000000000000000 _sheap
0000000000010000 g .bss 0000000000000000 _sbss
0000000000010000 g .bss 0000000000000000 _szero
0000000000010100 g .bss 0000000000000000 _ebss
0000000000010100 g .bss 0000000000000000 _ezero
0000000000011f00 g .noinit 0000000000000000 __noinit_start
0000000000012000 g .noinit 0000000000000000 __noinit_end
0000000000011f00 g .heap 0000000000000000 _eheap
Note: I couldn't find any documentation regarding this behavior so I can't guarantee this trick will work in future revisions of ld.
Note: This trick doesn't work for the gold linker.