So linker is for linking object code and library to elf file, like the following figure.
Why can’t compiler do this?
The linker needs a script to discript how does the different sections in object should be merged.
Linker script includes:
x86 use ENTRY(_start) for default.
.:Location counter
ENTRY($ENTRY_NAME)
MEMORY
{
$name(attribute): ORIGIN=$start_address, LENGTH=$memory_size
}
ORIGIN, LENGTH are keywords.
$name, $start_address, $memory_size are varialbes
Attribute will be:
attirbute | meaning |
---|---|
R | read-only |
W | read/write |
X | contain executeable code |
A | Allocated section |
I | Initialize section |
L | the same as I |
! | invert ther sense of any of the following attributes. |
Example: linkcmds.memory
MEMORY
{
FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 1024K
SRAM1(rwx) : ORIGIN = 0x20000000, LENGTH = 116K
SRAM2(rwx) : ORIGIN = 0x20000000+116K-4, LENGTH = 16K
}
REGION_ALIAS("REGION_TEXT", SRAM1);
REGION_ALIAS("REGION_RODATA", FLASH);
REGION_ALIAS("REGION_DATA", SRAM1);
REGION_ALIAS("REGION_BSS", SRAM2);
You can use expression to Calculate the starting address.
SECTION [address] [(type)] :
[AT(lma)]
[ALIGN(section_align) | ALIGN_WITH_INPUT]
[SUBALIGN(subsection_align)]
[constraint]
{
output-section-command
output-section-command
…
} [>region] [AT>lma_region] [:phdr :phdr …] [=fillexp]
LMA (Load Memory Address) | VMA (Virtual Memory Address) | |
---|---|---|
memory | ROM/Flash | RAM |
meaning | saving the code section | executing the code section |
Example:
INCLUDE linkcmds.memory
SECTIONS
{
.text :
{
// .=VMA
// _stext = .;
*(.text)
end_of_text = .;
// _etext = .;
} > REGION_TEXT
.rodata :
{
*(.rodata)
rodata_end = .;
// _erodata = .;
} > REGION_RODATA
.data : AT (rodata_end)
{
data_start = .;
*(.data)
_edata = .;
} > REGION_DATA
data_size = SIZEOF(.data); // this can export the data_size to other c file.
data_load_start = LOADADDR(.data);
.bss :
{
_sbss = .;
*(.bss)
} > REGION_BSS
// This is for only some specific files
// you want to add in this section
.include_file :
{
foo.o(.include_file)
}
// This is for only some specific files
// you do not want to add in this section
.exclude_file
{
*(EXCLUDE_FILE(*foo.o *bar.o).exclude_file)
}
}
for export value, we can use these symbol.
extern uint32_t _sidata;
extern uint32_t data_size;
PROVIDE(_sdata = .): using the command can prevent redefine problem in C and linker script.
Let we take a example
test.ld
MEMORY
{
FLASH(rx): ORIGIN = 0x08000000, LENGTH = 128K
SRAM1(rwx): ORIGIN = 0x20000000, LENGTH = 20K
}
SECTIONS
{
. = 0x10000;
.text : {
*(.text)
} > FLASH
. = 0x8000000;
.data :
{
*(.data)
} > FLASH AT > SRAM1
.bss :
{
*(.bss)
} AT > SRAM1
}
Use ld -T test.ld main.o test.o to build output file, the using objdump -h a.out to get sections.
a.out: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000004f 0000000008000000 0000000008000000 00001000 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .eh_frame 00000058 0000000008000050 0000000008000050 00001050 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.property 00000020 00000000080000a8 00000000080000a8 000010a8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .data 00000004 00000000080000c8 0000000020000000 000010c8 2**2
CONTENTS, ALLOC, LOAD, DATA
4 .bss 00000004 0000000020000004 0000000020000004 00002004 2**2
ALLOC
5 .comment 0000002b 0000000000000000 0000000000000000 000010cc 2**0
CONTENTS, READONLY
we can see that ".text" at VMA address is 0000000008000000
, this is mapping to FLASH(rx): ORIGIN = 0x08000000, LENGTH = 128K
and .text : { *(.text) } > FLASH
the ".data" section is the same, VMA is following prevous section and LMA maps to 0000000020000000
So, the the program will be loaded by follow layout
gcc -Wl,-verbose file.c
can help you to read gcc linker script.
from Source to Binary: How GNU Toolchain Works - jserv
How A Compiler Works: GNU Toolchain - jserv
Linker Script初探 - GNU Linker Ld手冊略讀
Bare metal embedded lecture-4: Writing linker scripts and section placement