Cover image for GNU LD

GNU LD

Words 1.5k
Views
Visitors

Timeline

Timeline

2025-09-27

init

This article introduces the basic functions and usage of the GNU linker (ld), including common command-line parameters (such as -T, -Map, -o) and the role of linker scripts. The article elaborates on the concepts of input sections and output sections, distinguishes between VMA (virtual memory address) and LMA (load memory address), and explains how to handle the inconsistency between the two in ROM-based image files. Subsequently, the article explains key commands in linker scripts, such as ENTRY, INCLUDE, OUTPUT, OUTPUT_FORMAT, etc., as well as symbol assignment and reference methods, emphasizing that symbols defined by the linker represent only addresses rather than variable values, and recommends using the char[] type for address operations. The article also introduces how the SECTIONS command maps input sections to output sections, and the usage of built-in functions such as ADDR, ALIGN, SIZEOF, MAX/MIN. Through three experiments, the article demonstrates printing the memory layout, handling the copy process where the load address and runtime address differ, and analyzes the differences and connections among link address, load address, and runtime address in the Linux 5.0 kernel linker script.

  • 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

12345678
SECTIONS{	. = 0x10000;	.text : {* (.text)}	. = 0x8000000	.data : {*(.data)}	.bss : {*(.bss)}}

Basic Concepts

  • Input section, output section
  • Each section includes a name and a size.
  • Section attributes
    • allocatable: at runtime, the operating system needs to allocate memory for these sections.
    • loadable: at runtime, the contents of these sections need to be loaded into the allocated memory.
  • Section addresses
    • VMAVirtual memory address (VMA), the runtime address
    • LMALoad memory address (LMA)
    • Usually the ROM address is the load address (LMA), while the RAM address is the runtime address (VMA).

Linker script commands

  • ENTRY(symbol) sets the entry function of the program.

  • The linker provides the following ways to set the entry point:

    • Use the -e option
    • Use ENTRY(symbol)
    • At the very beginning of .text
    • Address 0
  • INCLUDE filename includes the linker script file

  • OUTPUT filename outputs a binary file, similar to using ‘-o filename’ on the command line.

  • OUTPUT_FORMAT(bfd) outputs the BFD format.

  • OUTPUT_ARCH(bfdarch) outputs the processor architecture format.

Symbol assignment

  • Symbols can also be assigned values like in C language.

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

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

"." represents the current position.
"." represents the current position.

Symbol references

  • High-level languages often need to reference symbols defined in linker scripts.

  • In C language, define a variable and initialize it. 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 and does not allocate 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 and does not allocate memory to store the variable's value.
The linker only defines this symbol in the symbol table and does not allocate memory to store the variable's value.

  • We can set some symbols in each section to make it convenient for C language to access the start and end addresses of each section.

Set the start and end addresses of sections.
Set the start and end addresses of sections.

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

Output section descriptor explanation
Output section descriptor explanation

LMA load address

  • Each section hasVMA(virtual address, runtime 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 output sections 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 program initialization needs to copy the data section from the ROM load address to the virtual address in SDRAM.
  • The data load address is at_etextthe start, and the runtime address of the data section is at_datathe start, and the size of the data section is_edata - _data, the following code copies the data section from_etextthe start to_datathe start

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

Common built-in functions

ADDR(section)

Returns the VMA address of a previously defined section.

ADDR(section)
ADDR(section)

ALIGN(n)

Returns the next address aligned to n bytes, which is calculated based on the current location (location counter).

Note that here it is n bytes, not 2^n bytes (unlike the assembler’s .align).

ALIGN(n)
ALIGN(n)

SIZEOF(section)

Return the size of a section

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 section

Experiment 1
Experiment 1

Output
Output

Definitions in C to obtain the addresses of linker script definitions
Definitions in C to obtain the addresses of linker script definitions

  1. Linker-exported symbols are addresses, not variable values

These symbols in the linker script:

1
_text = .;

What is defined is not the variable itself, but an address label (symbol address). In C, there is no syntax corresponding to an ‘address label’, so this address can only be referenced indirectly through some kind of ‘variable’.

Declaring it with char[] is actually saying:

“This is a memory region starting at _text; I care about its address, not its specific contents.”

  1. char[] is a ‘smallest unit’ memory representation, convenient for pointer arithmetic

    char is the smallest addressable unit in C (1 byte).

    So with the char[] type, we can directly perform precise address operations:

12
extern char _text[], _etext[];size_t text_size = _etext - _text;  // Calculate section length (bytes)

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

  1. Difference between char[] and char*:Linker symbols are ‘array addresses’, not pointer variables

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;This means the compiler is going to “fetch the variable_text's value”, which must be assigned by code.
Andchar _text[];It is ‘declaring that the linker will provide this address’, and does not generate extra symbols or variables.

Therefore, 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

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

Linker script content
Linker script content

Definition:

The addresses assigned by the compiler and linker to each section (such as .text, .data, .bss, etc.) when generating an executable file (such as an ELF file).

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:

12
. = 0x80000;.text : { *(.text) }

Indicates that the link address of the .text section is 0x80000 (>It should be followed by the memory region name defined by the MEMORY command; you cannot directly write an address).

Load Address

Definition:

The location where the contents of the executable file are loaded into memory, that is, the location where the OS/bootloader loads the file into memory.

Characteristics:

  • It is usually equal to the link address, but in some cases (such as dynamic linking, load address relocation) it can be different.
  • It is determined by the operating system or bootloader, and can also be relocated using tools such as objcopy.

Example:

  • If the link address of the .text section of your ELF file is 0x80000, and the bootloader loads it to 0x100000, then:
    • Link address ≠ load address
    • If relocation is not performed, the program will fail at runtime (because the code contains absolute addresses).
Runtime/Execution Address

Definition:

The memory address that the CPU actually accesses when the program is executing.

Characteristics:

  • Usually = load address (the program executes from where 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.

Loading comments…