Timeline
Timeline
2025-09-27
init
This article introduces the basic concepts and common commands of the GNU LD linker and the AT&T linker script language, and discusses in detail core mechanisms such as section attributes, VMA and LMA addresses, the SECTIONS command, and built-in functions. In addition, this article summarizes the methods of referencing linker symbols in C language, and deeply analyzes the differences between linking address, loading address, and running address through experiments.
- A linker is a program that links one or more object files generated by a compiler or assembler, along with libraries, into an executable file.
- The GNU Linker uses the AT&T linker script language.
ld command
- aarch64-linux-gnu-ld
- Common parameters
- -T specifies the linker script
- -Map outputs a symbol table file
- -o outputs the final executable binary file
A simple example
1 | SECTIONS |
Basic Concepts
- Input section, output section
- Each section includes a name and size
- Section attributes
- allocatable The operating system needs to allocate memory for these sections at runtime
- loadable The contents of these sections need to be loaded into the allocated memory at runtime
- Section address
- VMA(virtual memory address) Virtual address, the address at runtime
- LMA(load memory address) Load address
- Usually, the address of ROM is the load memory address (LMA), while the address of RAM is the virtual memory address (VMA).
Linker script commands
ENTRY(symbol) Sets the entry function of the program
The linker has the following ways to set the entry point:
- Using the -e parameter
- Using ENTRY(symbol)
- At the very beginning of .text
- Address 0
INCLUDE filename Includes the filename linker script
OUTPUT filename outputs a binary file, similar to using “-o filename” on the command line
OUTPUT_FORMAT(bfd) outputs BFD format
OUTPUT_ARCH(bfdarch) outputs processor architecture format
Symbol assignment
- Symbols can also be assigned like in C language

- “.” represents the location counter, indicating the current position

Symbol reference
High-level languages often need to reference symbols defined in the linker script
In C language, define a variable and initialize a variable. For example, int foo = 100
- The compiler defines a symbol foo in the symbol table
- The compiler stores 100 in memory for the symbol
Define a variable in the linker script
- The linker only defines this symbol in the symbol table, without allocating memory to store the variable’s value
Accessing variables defined in the linker script:What is accessed is the address of the variable, not the value of the variable

- We can set some symbols in each section to facilitate C language access to the start and end addresses of each section


SECTIONS command
- SECTIONS command: tells the linker how to map input sections to output sections, and how to place these output sections in memory

- Output section descriptor


LMA load address
- Each segment hasVMA(virtual address, run address) andLMA(load address)
- In the output section descriptorUse “AT” to specify LMA
- If LMA is not specified via “AT”, usually LMA=VMA
- BuildA ROM-based image file often sets the virtual address and load address of the output section to be different


- The load address and link address (virtual address) of the data section are different, so the program initialization needs to copy the data section from the load address in ROM to the virtual address in SDRAM
- Data load address is at _where etext starts, the run address of the data section is at _where data starts, the size of the data section is “_edata-_data”, the following code copies the data section from _where etext starts to _where data starts

Common built-in functions
ADDR(section)
Returns the VMA address of the previously defined section

ALIGN(n)
Returns the next address aligned to n bytes, which calculates the aligned address based on the current location counter
Note that here it is n bytes, not 2^n bytes (distinguish from the assembler’s .align)

SIZEOF(section)
Return the size of a segment

MAX(exp1, exp2) / MIN(exp1, exp2)
Return the maximum or minimum of two expressions
Experiment 1: Print the memory layout of each segment



- The linker-exported symbol is an address, not a variable value
These symbols in the linker script:
1 | _text = .; |
What is defined is not the variable itself, but an address label (symbol address). There is no syntax in C corresponding to an “address label”, so this address can only be referenced indirectly through some kind of “variable”.
Declaring it with char[] actually means:
“This is a memory region starting at _text, and I care about its address, not its specific content.”
char[] is a “smallest unit” memory representation, convenient for pointer arithmetic
char is the smallest addressable unit in C (1 byte).
So by using the char[] type, we can directly perform precise address operations:
1 | extern char _text[], _etext[]; |
If you write it as int[] or void*, this calculation might be wrong, or fail to compile.
- Difference between char[] and char*:The linker symbol is an “array address” rather than a pointer variable
Although you can also write:
1 | extern char *_text; |
But this actually means _text is a “variable pointing to a character”, not an address label.
char *_text; indicates that the compiler needs to “get the value of the variable _text”, it must be assigned by code.
While char _text[]; is “declaring that the linker will provide this address”, it will not generate extra symbols or variables.
So it is recommended to use:
1 | extern char _text[]; |
Experiment 2: Load address is not equal to run address


Need to copy the code from the load address to the run address

Experiment 3: Analyzing the Linker Script of the Linux 5.0 Kernel



Runtime Address, Load Address, Link Address
Link Address
Definition:
The addresses assigned to various sections (such as .text, .data, .bss, etc.) by the compiler and linker when generating executable files (such as ELF files).
Characteristics:
- It is the address set by the linker during the compilation phase.
- It can be explicitly set via the linker script (ld script), for example, . = 0x80000;.
- These addresses are recorded in the section headers or program headers of the executable file.
Example:
1 | .text : { *() } > 0x80000 |
Indicates that the link address of the .text section is 0x80000.
Load Address
Definition:
The location in memory where the contents of the executable file are loaded, i.e., the location where the operating system/bootloader loads the file into memory.
Characteristics:
- Usually equal to the link address, but can differ in certain cases (such as dynamic linking, load address relocation).
- Determined by the operating system or bootloader, and can also be relocated using tools like objcopy.
Example:
- If the .text section link address of your ELF file is 0x80000, and the bootloader loads it to 0x100000, then:
- Link Address ≠ Load Address
- If no relocation is performed, the program will fail to run (because there are absolute addresses in the code).
Runtime/Execution Address
Definition:
The memory address actually accessed by the CPU when the program is executing.
Characteristics:
usually = load address (executes from wherever it is loaded)
If the MMU (Memory Management Unit) is enabled, the run address is a virtual address, which is mapped by the MMU to the physical load address.
In bare-metal programs, generally = link address = load address = run address.
