Cover image for GNU LD

GNU LD

Words 1.4k
Views
Visitors

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
2
3
4
5
6
7
8
SECTIONS
{
. = 0x10000;
.text : {* (.text)}
. = 0x8000000
.data : {*(.data)}
.bss : {*(.bss)}
}

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

Symbols can also be assigned like in C language
Symbols can also be assigned like in C language

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

"." indicates the current position
"." indicates 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

The linker only defines this symbol in the symbol table, without allocating memory to store the variable's value
The linker only defines this symbol in the symbol table, without allocating memory to store the variable's value

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

Setting the start and end addresses of a section
Setting the start and end addresses of a section

Example in the Linux kernel
Example in the Linux kernel

SECTIONS command

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

SECTIONS command
SECTIONS command

  • Output section descriptor

Output section descriptor
Output section descriptor

Explanation of output section descriptor
Explanation of 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

Specify LMA via "AT"
Specify LMA via "AT"

The virtual address and load address of the output section are different
The virtual address and load address of the output section are 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

Copy the data section from the load address in ROM to the virtual address in SDRAM
Copy the data section from the load address in ROM to the virtual address in SDRAM

Common built-in functions

ADDR(section)

Returns the VMA address of the previously defined section

ADDR(section)
ADDR(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)

ALIGN(n)
ALIGN(n)

SIZEOF(section)

Return the size of a segment

SIZEOF(section)
SIZEOF(section)

MAX(exp1, exp2) / MIN(exp1, exp2)

Return the maximum or minimum of two expressions

Experiment 1: Print the memory layout of each segment

Experiment 1
Experiment 1

Output result
Output result

Definition in C to get the address of the linker script's definition
Definition in C to get the address of the linker script's definition

  1. 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.”

  1. 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
2
extern char _text[], _etext[];
size_t text_size = _etext - _text; // Calculate segment length (bytes)

If you write it as int[] or void*, this calculation might be wrong, or fail to compile.

  1. 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

Experiment 2
Experiment 2

Linker script
Linker script

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

Copy the code from the load address to the run address
Copy the code from the load address to the run address

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

Experiment 3
Experiment 3

Overall Framework of vmlinux.lds.S
Overall Framework of vmlinux.lds.S

Linker File Content
Linker File Content

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 : { *(.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.