The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →A linker does not normally allocate a program’s heap or create runtime objects. It combines object files, assigns addresses to sections in the output image, resolves symbols, and applies relocations. A loader, startup code, and runtime system then make that layout usable: for example, an operating system maps executable segments, while firmware startup code may copy initialized data from Flash to RAM and clear zero-initialized data.
What “memory allocation” means at link time
The phrase can refer to three different things. The linker itself uses host memory while processing files; the target program has an image layout whose addresses the linker assigns; and, after the program starts, its loader and runtime manage mappings and allocations. Confusing these layers leads to the common but misleading claim that the linker allocates RAM for every variable.
- Linker process memory: The linker uses the computer’s RAM to read object files, build symbol tables, process relocations, and write output. GNU
ldcan trade speed for lower working-memory use with--no-keep-memory; this affects the linker process, not the target program’s memory layout. See GNU ld options and behavior. - Target image address space: The linker assigns addresses and sizes to code, constants, global data, tables, and other sections in the output.
- Runtime memory: The loader, startup code, operating system, and runtime library establish mappings and manage areas such as stacks, heaps, shared libraries, thread-local storage, and memory-mapped files.
malloc()is a runtime operation, not something the linker performs.
The concise mental model is: the linker decides where image contents are intended to live; a loader or startup code establishes that layout; a runtime allocator manages objects requested later.
How object files become a program image
Compilers place code and data into input sections in object files. The linker combines compatible input sections into output sections, assigns their addresses, resolves symbol references, and applies relocations. It also creates format-specific metadata that tells a loader or programmer how to use the image.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
source code
↓ compiler
object files with input sections
↓ linker
output sections, addresses, and load metadata
↓ loader or startup code
runtime memory
For example, input sections named .text from several object files may be combined into one output .text. GNU ld uses a linker script to control how input sections map to output sections and where the output sections go. It uses a built-in default script if none is supplied; the exact default depends on the target and link mode. See GNU ld linker scripts and the SECTIONS command.
What common sections contain
Section names are conventions, and their exact contents, permissions, and grouping vary with the platform, compiler, and linker. Sections are useful for understanding the linker’s organization, but they are not all runtime memory.
| Section | Typical contents | File payload | Runtime storage | Typical permissions |
|---|---|---|---|---|
.text |
Machine instructions | Yes | Yes | Read/execute |
.rodata |
String literals, constants, read-only tables | Usually yes | Yes | Read-only |
.data |
Initialized writable globals and statics | Yes | Yes | Read/write |
.bss |
Zero-initialized or uninitialized globals and statics | Usually no zero-filled payload | Yes | Read/write |
.tdata |
Initialized thread-local data | Yes | Per-thread | Read/write |
.tbss |
Zero-initialized thread-local data | Usually no zero-filled payload | Per-thread | Read/write |
.init_array / .fini_array |
Constructor and destructor pointers | Yes | Normally yes | Read-only or writable, depending on format and toolchain |
.debug_* |
Debugging information | Yes when retained | Not normally loaded as program data | Not runtime data |
Permissions in the table are typical, not guarantees. Loaders commonly apply protections to loadable segments, which may contain multiple sections, rather than setting protection independently for every section.
How the linker chooses addresses
A linker script can direct the placement process. In GNU linker scripts, the location counter is written as .. A simplified script might look like this:
SECTIONS
{
.text :
{
*(.text)
}
.rodata :
{
*(.rodata)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
*(COMMON)
}
}
Broadly, the linker follows the script’s placement rules, honors section alignment, lays out contents, updates the location counter, and checks address and region constraints. It then resolves symbols and relocations using the resulting addresses. Actual scripts often handle many more sections, including exception metadata, dynamic-linking data, TLS, notes, constructor arrays, and platform-specific content. To inspect GNU ld‘s active default script, use ld --verbose or invoke the compiler driver with gcc -Wl,--verbose.
Alignment and padding
Input sections can require aligned addresses. The linker may insert padding to meet those requirements or explicit script boundaries, so the space consumed by a layout can exceed the sum of the sections’ raw contents. For example:
. = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
If .text ends at 0x13F0, a following section aligned to 0x1000 may start at 0x2000. The gap can affect image size, virtual-address layout, segment boundaries, and Flash or RAM usage. The exact effect depends on whether that gap is represented in the output file or only in the address layout. For ELF, output-section alignment must be compatible with the section address; LLD describes how output alignment relates to requested and input-section alignment in its ELF linker-script documentation.
Symbols and relocations
Object files can contain references whose final addresses are not yet known. After assigning addresses, the linker resolves symbols and applies relocation records, updating instructions or data that refer to those symbols. Moving a section can therefore change references, instruction encodings, or whether a target is within a branch’s range. Some architectures and linkers may relax instruction sequences or insert thunks or veneers to handle placement constraints.
Not every address is necessarily fixed absolutely at link time. Position-independent executables, shared libraries, and dynamically linked programs can retain relocations for the runtime loader; address-space layout randomization can also change runtime addresses.
How firmware scripts place sections in Flash and RAM
In bare-metal firmware, a linker script commonly describes physical memory regions and assigns sections to them. These addresses and sizes must match the actual device memory map.
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.text :
{
*(.text*)
*(.rodata*)
} > FLASH
.data :
{
*(.data*)
} > RAM AT > FLASH
.bss :
{
*(.bss*)
*(COMMON)
} > RAM
}
> FLASHassigns the section’s runtime address to the Flash region.> RAM AT > FLASHassigns.dataa runtime address in RAM and a load address in Flash.> RAMputs.bssin RAM; startup code typically clears it to zero.
GNU ld‘s MEMORY command describes target memory blocks and lets the script assign sections to them. If a region is too small, the linker can report an overflow; it does not generally rearrange sections intelligently to make them fit. See GNU ld documentation.
VMA and LMA: where a section runs and where its bytes are stored
Every output section has a VMA (virtual memory address), where it is expected to exist during execution, and an LMA (load memory address), where its initial contents are stored in the image. GNU ld supports explicit load-address control with AT(address) or AT>region; absent a separate load address, the linker generally uses or derives an LMA from the section’s layout. See GNU ld’s VMA and LMA documentation.
Recommended Free Tools
Rank #3
Flash image: .text | .rodata | initial .data bytes
│
│ startup copies
▼
RAM at runtime: .data | .bss | heap | stack
For a typical firmware image, code and read-only constants execute from Flash, initialized writable data has its VMA in RAM but its initial bytes in Flash, and zero-initialized data occupies RAM. A script can define symbols for startup code:
.data :
{
__data_start__ = .;
*(.data*)
__data_end__ = .;
} > RAM AT > FLASH
__data_load_start__ = LOADADDR(.data);
.bss :
{
__bss_start__ = .;
*(.bss*)
*(COMMON)
__bss_end__ = .;
} > RAM
The symbols provide addresses and bounds. They do not themselves copy .data or clear .bss: startup code, or a loader specifically designed to do so, must perform those operations. If that startup work is missing or uses the wrong bounds, initialized globals may contain incorrect values even when the link succeeds.
Why .bss uses runtime memory without equivalent file bytes
.bss represents storage that must exist at runtime and begin as zero. Rather than storing a long run of zero bytes in the executable, many formats record the required memory size separately from the file payload. In ELF, a loadable segment can have a larger memory size than file size (p_memsz greater than p_filesz); the additional memory is supplied as zero-filled storage by the loader or startup environment.
This is why a firmware image can have modest file size but substantial RAM use, and why a RAM-region overflow can occur without a corresponding increase in the binary’s stored bytes. Exact details depend on the output format and image type. A raw binary may omit .bss contents while an ELF file describes the runtime storage.
Sections are not the same as segments
Sections organize content for linking and inspection: examples include .text, .data, and .debug_info. Segments group address ranges for loading. In ELF, program headers describe how loadable portions of a program are mapped; one loadable segment can include several sections. Consequently, seeing a section at an expected address does not, by itself, establish that an ELF loader will map it as intended. GNU ld documents program headers and the PHDRS command for controlling them in its linker documentation.
readelf -S app.elf # section headers
readelf -l app.elf # program headers and segments
objdump -h app.elf # section addresses, sizes, and flags
Use readelf -S to inspect section addresses, sizes, offsets, alignment, and flags; use readelf -l to inspect loadable segments. GNU readelf documents these inspection options at readelf documentation.
Who establishes the heap and stack?
For hosted programs, the operating system and runtime establish process mappings and stack behavior. The linker may contribute metadata or symbols, but it cannot know how many later malloc() calls the program will make. The allocator and operating system handle dynamic allocations and their failures.
In firmware, a script may reserve space or define boundaries such as __heap_start, __heap_end, or a stack-top symbol. Startup code and the C runtime can use those boundaries. The linker has described a layout; runtime code still governs stack use, heap growth, allocator metadata, collision checks, and allocation failure. Exact conventions vary by toolchain and startup implementation.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Inspect and diagnose a layout
Use the map file, section headers, segment headers, and region report together. The compiler driver invokes the linker and can add startup objects, libraries, and default options; -Wl, passes linker options through that driver.
Generate a map file and memory report
gcc main.o -Wl,-Map=app.map -o app
GNU ld‘s -Map=mapfile writes a link map that records output sections, addresses, sizes, input contributions, and symbols. Formatting varies by linker. For embedded GCC, a typical command is:
arm-none-eabi-gcc objects.o
-T firmware.ld
-Wl,-Map=firmware.map,--print-memory-usage
-o firmware.elf
--print-memory-usage reports used size, total region size, and percentage for regions declared with MEMORY. The figures below are illustrative, not a promised output format or a measurement of a particular firmware:
Memory region Used Size Region Size %age Used
FLASH: 42 KB 512 KB 8.20%
RAM: 11 KB 128 KB 8.59%
GNU documents map files and memory reporting in its linker options reference.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchCheck sections, segments, and symbol sizes
readelf -S firmware.elf
objdump -h firmware.elf
readelf -l firmware.elf
objdump -p firmware.elf
Compare section sizes and addresses with the memory-region assignments in the map. In the program headers, compare each loadable segment’s file size and memory size; a larger memory size can indicate zero-filled storage such as .bss, but interpret it alongside the segment’s actual contents. To find large symbols in a GNU-compatible toolchain, sort the symbol table by size:
nm -S --size-sort firmware.elf
Inspect the largest contributors and check whether they belong in Flash, RAM, or a different memory region. To place an output section at an absolute address, GNU ld also offers --section-start=sectionname=org, though a linker script is usually clearer for a nontrivial layout. See the GNU ld options reference.
Work through overflow, overlap, and unexpected size
Messages such as region 'RAM' overflowed by ... bytes, section '.text' will not fit in region 'FLASH', or an LMA-overlap diagnostic indicate different problems. Start by identifying the affected region or address range rather than assuming all memory is exhausted.
- Locate the failing region or section. Distinguish Flash/LMA overflow from RAM/VMA overflow and from a host linker process running out of memory.
- Read the map file. Find large output sections and the object files or libraries contributing to them.
- Check symbols and alignment. Look for large arrays or buffers, unexpected alignment gaps, and sections placed after a large boundary.
- Check what is loadable. Confirm that debug or metadata sections were not accidentally placed in a loadable region, and inspect program headers when working with ELF.
- Verify startup accounting. Ensure that
.bss, reserved heap, and stack boundaries are included correctly in the RAM layout, and verify that startup code copies and clears the intended ranges. - Choose a real remedy. Depending on the cause, remove unused code, enable suitable dead-section elimination, move constants to read-only memory, put buffers in external RAM, reduce unnecessary alignment, or use overlays for mutually exclusive buffers. Increase a declared region only if the hardware actually provides that memory.
With --gc-sections, a linker can discard unreferenced input sections, but code reached indirectly or by hardware conventions may need explicit retention, often with KEEP() in a script. Orphan input sections—those not explicitly matched in a custom script—may be placed by linker-specific rules, so check the map rather than assuming their location. A custom script also needs to preserve sections and conventions required by the ABI, exception handling, constructors, TLS, and the selected output mode.
Free tools Windows power users keep installed
One-click scans. No signup required.
Hosted executables, firmware, and other formats
The core idea—assigning an image layout—is shared, but placement rules and runtime behavior are format- and platform-specific.
- Hosted ELF applications: The linker lays out the executable or shared object. The operating-system loader maps loadable segments; the OS and runtime also handle stack, heap, libraries, and other mappings. PIE and dynamic linking can leave work for the runtime loader.
- Bare-metal firmware: A script commonly places image contents into physical Flash and RAM regions. Startup code is responsible for operations such as copying initialized data and zeroing
.bss. - Windows PE/COFF: GNU linker scripts do not directly describe the Windows image layout. The linker assigns section virtual addresses and alignment in the PE image, and the Windows loader uses image headers and section information. Microsoft documents these rules, including
SectionAlignment, in the PE format reference. - Other formats: Mach-O and WebAssembly have their own image and loading conventions. Do not assume ELF section, segment, or script details transfer unchanged.
GNU ld also uses host memory while linking, independently of the target regions described in a script. A host out-of-memory failure is therefore not the same as a message that a target RAM region overflowed.
Quick Recap
Common misconceptions
- “The linker allocates RAM for variables.” It assigns addresses and sizes in the image; a loader or startup environment establishes storage at runtime.
- “
.bsstakes no memory.” It normally needs runtime storage even though it may have no equivalent zero-filled file payload. - “
AT > FLASHcopies data into RAM.” It specifies a load address; startup code or a capable loader must perform the copy. - “The section table tells the loader what to map.” For ELF, loadable program headers describe mappings; section headers are primarily for linking and inspection.
- “All addresses are fixed once linking finishes.” Dynamic relocation, shared libraries, PIE, and ASLR can make runtime addresses differ.
- “The linker will rearrange sections until they fit.” Do not rely on that. Diagnose the assigned layout and change the script or content deliberately.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

