System.map
System.map is a symbol table file generated during the compilation of the Linux kernel, containing a list of kernel symbols—such as function names, global variables, and data structures—along with their corresponding memory addresses in the kernel binary (vmlinux).[1][2] This file serves as a critical debugging aid, enabling the translation of raw memory addresses from kernel logs, crash reports, or oops messages into human-readable symbol names, which helps developers and system administrators diagnose issues like kernel panics or driver faults.[1][2] Typically located in directories such as/boot/System.map-<kernel-version> or the root of the kernel source tree after compilation, System.map is produced using tools like nm to extract symbols from the uncompressed kernel image.[1][2] It must be updated with each new kernel build, as symbol addresses can shift due to compilation variations, ensuring compatibility with the running kernel.[2] Beyond core debugging, the file supports specific kernel subsystems, including certain device drivers that rely on it for symbol resolution during operation, and utilities like klogd (the kernel log daemon) that use it to decode logs in real-time.[1][2]
In modern Linux distributions, System.map complements dynamic alternatives like /proc/kallsyms, a virtual file in the proc filesystem that provides a runtime view of kernel symbols without requiring a static file.[2] However, System.map remains essential for offline analysis, such as examining saved crash dumps or when /proc/kallsyms is unavailable in a panicked or non-booted system.[2] Its format is a simple text-based list of entries in the structure <address> <type> <symbol_name>, where the type indicates whether the symbol is a function ('T'), variable ('t'), or other kernel entity.[1] This straightforward design has made it a longstanding component of Linux kernel tooling since early versions, underscoring its role in maintaining the kernel's debuggability amid evolving complexity.[1]
Overview
Definition
The System.map file serves as a static symbol table for the Linux kernel, mapping symbols such as functions, global variables, and data structures to their corresponding virtual memory addresses within the uncompressed kernel binary, vmlinux.[1][3] This mapping provides a fixed reference of the kernel's internal layout as determined during compilation.[4] Key characteristics of System.map include its static nature, meaning the address mappings are determined and fixed at build time rather than runtime; its text-based format; and its generation tailored to each specific kernel version.[1] The addresses listed are kernel virtual addresses, which differ from physical memory addresses and reflect the layout in the vmlinux binary before any compression or loading adjustments.[3] Unlike dynamic runtime symbol resolution mechanisms, such as /proc/kallsyms, which incorporate loaded modules and can shift due to features like Kernel Address Space Layout Randomization (KASLR), System.map offers a build-time snapshot without such variability.[5] Entries in the file typically follow a simple format consisting of a hexadecimal address, a symbol type indicator, and the symbol name, such as "c041bc90 t my_function" where "t" denotes a text (function) symbol.[1] This structure facilitates quick lookups for debugging purposes, such as decoding kernel oops messages by correlating reported addresses to meaningful symbol names.[1]Primary Purposes
The System.map file serves primarily as a static symbol table that maps kernel memory addresses to human-readable symbol names, enabling developers to decode kernel crash reports such as oopses and panics without requiring access to a running kernel instance.[4][1] This translation is crucial during post-mortem analysis, where kernel logs often contain raw hexadecimal addresses that are meaningless without such a mapping, allowing identification of the exact functions, variables, or code locations involved in failures.[1] By providing this offline capability, System.map facilitates efficient troubleshooting in environments where the kernel cannot be easily restarted or debugged interactively.[4] In addition to crash debugging, System.map supports kernel module loading processes by assisting in the resolution of symbols between loadable modules and the static kernel binary, particularly for modules linked against kernel headers rather than the full kernel image.[4] It also aids tools like ksymoops and kallsyms in decoding logs statically, ensuring that symbol information remains available even if runtime mechanisms fail.[1] Historically, System.map emerged as a post-build artifact to enable static analysis of kernel binaries, decoupling debugging from the dynamic state of a live system and promoting portability across installations using the identical kernel build.[1] As a static complement to runtime interfaces like /proc/kallsyms, System.map ensures persistent access to symbol data independent of kernel execution, enhancing reliability in maintenance and development workflows.[4]Generation
Kernel Build Integration
The System.map file is automatically generated as part of the Linux kernel's build process, integrated into the Kbuild system managed by the top-level Makefile. This occurs during the final linking stage, after the vmlinux kernel image—the uncompressed resident kernel binary—has been produced from object files and libraries. The build invokes thescripts/link-vmlinux.sh script, which handles the linking and subsequent post-processing steps to create the System.map alongside vmlinux.[6][7]
Key Makefile targets, such as vmlinux or architecture-specific ones like bzImage for x86, trigger this generation as part of the vmlinux linking process in scripts/link-vmlinux.sh, using utilities like nm to parse the vmlinux ELF file without altering the core build flow. Some architectures may perform additional post-link processing via files like arch/*/Makefile.postlink after vmlinux and System.map generation, but this typically does not affect the symbol addresses in System.map. While genksyms is involved in generating symbol version tables for loadable modules during the build, it does not directly participate in System.map creation, which focuses on the monolithic kernel image.[6]
Generating a complete System.map with full symbol details requires compiling the kernel source tree with debug information enabled, typically via the CONFIG_DEBUG_INFO=y option in the kernel configuration (accessed through make menuconfig or similar). This option compiles the kernel with debugging symbols (using GCC's -g flag), ensuring that all function names, variables, and other symbols are preserved in vmlinux for extraction; without it, the output may lack certain non-exported or inline symbols, limiting utility for debugging. Similar options, such as CONFIG_KALLSYMS_ALL, can enhance symbol visibility but are more relevant for runtime exposure via /proc/kallsyms.[8][6]
The resulting System.map file follows a standard naming convention of System.map-<kernel-version>, where <kernel-version> matches the built kernel's release string (e.g., System.map-6.11.0 for kernel version 6.11.0), ensuring compatibility with the corresponding vmlinux or compressed image like vmlinuz. This file is produced directly in the kernel source root directory during the build.[4]
Building System.map requires a standard kernel source tree environment, typically set up in a directory like /usr/src/linux or a custom path, with GNU Make and the kernel's build dependencies (e.g., GCC, binutils) installed. The process assumes a clean or prepared source tree post-configuration, and no additional flags are needed beyond standard make invocation for the target image.[6]
Symbol Extraction Process
The symbol extraction process for the System.map file begins with the use of thenm utility from the GNU Binutils package, which parses the vmlinux ELF binary—the uncompressed kernel image generated during the build—to dump its symbol table. Specifically, the command nm -n [vmlinux](/page/Vmlinux) is executed, where the -n flag instructs nm to sort the output numerically in ascending order by symbol address, producing lines in the format <address> <type> <symbol_name> for each symbol found across ELF sections such as .text (code), .data (initialized data), and .bss (uninitialized data). These addresses represent the virtual memory locations assigned during kernel linking, serving as link-time offsets relative to the kernel's base load address, without adjustment for runtime relocation mechanisms like Kernel Address Space Layout Randomization (KASLR).
The raw output from nm is then processed through a filtering pipeline to remove irrelevant or uninteresting symbols, ensuring the System.map contains only useful entries for debugging and module loading. This is achieved by piping the nm output to a sed script via the command nm -n vmlinux | sed -f "${srctree}/scripts/mksysmap" > System.map, where scripts/mksysmap applies a series of pattern-matching rules to exclude symbols based on type and name. For symbol types, it discards local absolute symbols (a), debugging symbols (N), undefined globals (U), and local weak symbols (w), focusing instead on global and static symbols that are relevant to the kernel's operation. Name-based filtering eliminates entries starting with prefixes like $, .L, __efistub_, __crc_, or __kstrtab_, as well as those ending in suffixes such as _from_arm or _veneer, and exact matches like __UNIQUE_ID_modinfo[0-9]*, to avoid clutter from compiler-generated artifacts, checksums, or module metadata.
Regarding duplicates, the process inherently resolves them by retaining the entry with the lowest address due to the pre-sorted nm -n output; if multiple symbols share the same address, the first occurrence (lowest in the sorted list) is kept, as the sed filter does not explicitly deduplicate but processes lines sequentially. The resulting filtered and sorted symbols are written directly to System.map in a plain-text format, providing a concise mapping without further compression.
In configurations where kernel debug information is disabled (e.g., CONFIG_DEBUG_INFO=n), the vmlinux binary is stripped of detailed debugging symbols during the build, limiting nm's output to essential runtime symbols and resulting in a smaller, more focused System.map file. This optional stripping enhances the kernel's footprint while preserving core symbol visibility for basic debugging needs.
Format and Contents
File Structure
The System.map file is organized as a plain text file, with each line representing a single kernel symbol and fields separated by spaces or tabs.[1] Each line consists of three standard columns: a hexadecimal memory address in the first column, a single character denoting the symbol type in the second column, and the symbol name in the third column.[1] For instance, the linec010b860 T start_kernel shows the address c010b860, type T (indicating a global text symbol), and the function name start_kernel.[9]
The file lacks a formal header and commences immediately with entries sorted numerically by memory address (ascending).[1]
As a text file generated from kernel binaries, it uses ASCII encoding compatible with UTF-8.
Symbol Types
The symbol types in System.map are single-character indicators derived from the ELF symbol classifications processed by thenm utility during kernel build, specifying the section or binding of each symbol.[10] These types categorize symbols into code, data, or other categories, with relevance limited to kernel binaries (excluding user-space dynamic linker types like those for shared libraries).
The types follow standard nm conventions, as documented in the kernel's System.map generation script: uppercase letters denote global or exported symbols (visible across object files or modules), while lowercase letters indicate local symbols (file-specific and not exported). Common types include:
- A/a: Absolute symbols, with fixed values independent of linking or relocation.[10]
- B/b: Uninitialized data in the BSS section (zero-initialized at runtime).
- D/d: Initialized data in the data section.
- R/r: Read-only data, such as constants in the
.rodatasection; lowercase 'r' specifically applies to symbols marked read-only after initialization (e.g., via__ro_after_initannotation for post-boot immutability). - T/t: Executable code in the text section (functions or instructions).
- W/w: Weak symbols, which can be overridden by strong definitions during linking.[10]
c041bc90 t packet_sklist indicates a local text (code) symbol at address c041bc90, representing a non-exported kernel function or code label related to packet handling. This format aids in mapping addresses to meaningful names during debugging, without including extraneous user-space or debugging symbols like N (debug info).[10]
Location and Access
Standard Filesystem Paths
The System.map file is conventionally installed in the/boot directory as /boot/System.map-<kernel-version>, where <kernel-version> corresponds to the specific kernel release, such as /boot/System.map-6.1.0-18-amd64.[4]
During the kernel build process, System.map is generated at the root of the kernel source or object tree, typically named System.map without a version suffix, for example in a directory like linux-6.1/.[6]
This file is then copied to its standard installation location during execution of the make install target in the kernel build system or through distribution-specific packaging scripts that handle kernel deployment.[4]
System.map is typically owned by root:root with permissions 644 (rw-r--r--), making it world-readable to allow any user to access it for kernel debugging.[11]
To ensure correct symbol-to-address mapping, the installed System.map must precisely match the version of the running kernel and its associated vmlinux image, as mismatches can lead to invalid address resolutions.[2]
Variations Across Distributions
In Debian and Ubuntu distributions, the System.map file is provided by the linux-image packages and installed at /boot/System.map-Usage
Debugging Applications
System.map facilitates the diagnosis of kernel oops and panic events by enabling the translation of raw memory addresses in error logs to human-readable kernel symbols and functions. In a typical kernel oops, the system logs display fault details, including the program counter (PC on various architectures, EIP on x86, or RIP on x86_64), which points to the instruction causing the issue. By consulting System.map, developers can resolve these addresses to identify the offending code path, aiding in root cause analysis without requiring a full rebuild or live system access.[15] A standard workflow for decoding an oops begins with capturing the log output viadmesg or console serial output, where an entry might read "PC is at faulty_function+0x1a/0x80". The base address of faulty_function is located by grepping System.map (e.g., grep faulty_function /boot/System.map yields "c0123456 T faulty_function"), and the offset (0x1a) is added to compute the precise fault location (c0123470). This resolved address can then be disassembled using objdump -d vmlinux | grep c0123470 to reveal the assembly instruction, such as an invalid memory access, providing context for the bug.[15]
For automated processing, legacy tools like ksymoops parse oops logs against System.map to produce annotated traces, though it requires the matching System.map and module symbol files; ksymoops has been deprecated since kernel 2.6, with modern kernels favoring embedded symbol resolution. In crash dump scenarios, makedumpfile compresses kdump-generated vmcore files, and the crash utility integrates System.map for symbol lookup when the vmlinux debuginfo package is unavailable—invoked as crash vmcore /boot/vmlinux -S /boot/System.map to analyze registers, stacks, and variables interactively. Custom scripts, often in awk or Python, can automate dmesg parsing by matching hexadecimal addresses against System.map entries to output symbolized traces, streamlining repeated analyses.[16][17]
Key limitations arise from System.map's static nature: it must precisely match the crashed kernel's build, as address changes from recompilation, optimizations, or configuration differences invalidate lookups. It proves ineffective for dynamically loaded modules without applying module-specific offsets from lsmod or /proc/modules, and relocated kernels (e.g., via KASLR) require manual adjustment, often necessitating complementary runtime data from /proc/kallsyms.[15]
Best practices include archiving System.map alongside the kernel image in /boot during installation or updates to ensure availability for immediate post-mortem review, prioritizing its use in offline debugging where live tools are inaccessible.[4]
Support for Kernel Modules and Drivers
depmod can optionally use System.map (via the -F flag) to report unresolved symbols by matching against kernel symbols during the generation of dependency files like modules.dep and modules.symbols, which modprobe and insmod consult to ensure dependencies are met before inserting the module into the kernel. Without proper symbol information from sources like Module.symvers, modules referencing kernel functions may fail to load due to unresolved references.[18][4] In rare legacy cases, some out-of-tree drivers linked against kernel headers may require a matching System.map for symbol resolution.[2] Kernel symbols made available to modules via the EXPORT_SYMBOL() macro appear as entries in System.map, enabling reference for build-time verification. This mechanism supports out-of-tree drivers by allowing them to link against the kernel's public interface at build time.[19] Symbol mismatches, often detected during depmod processing, trigger "unknown symbol" errors upon module insertion via modprobe or insmod, enforcing version and compatibility verification. Such errors arise when a module expects a symbol not present or altered in the kernel's export list, prompting administrators to align module builds with the kernel.[20] Prior to the enhanced dynamic kallsyms support in later kernels, modules relied on the static /proc/ksyms interface for runtime symbol lookup, with System.map serving primarily as a debugging aid rather than a direct resolution source.[21]Comparisons
With /proc/kallsyms
The/proc/kallsyms is a virtual file in the proc filesystem that provides a list of kernel symbols and their current virtual addresses at runtime, facilitating dynamic linking for loadable kernel modules.[21] It includes symbols from both the core kernel and any currently loaded modules, reflecting the live state of the running system.[22]
Key differences between System.map and /proc/kallsyms lie in their static versus dynamic nature: System.map is a build-time, read-only file containing only the symbol table for the static kernel image (vmlinux), generated during compilation and installed alongside the kernel binary.[23] In contrast, /proc/kallsyms is generated on-the-fly by the running kernel, requires a booted system, and incorporates runtime changes such as module loading; access to it is restricted to root users or those with the CAP_SYSLOG capability, or when the kptr_restrict sysctl is set to 0.[24]
Both files share a similar format—listing hexadecimal addresses, symbol types (e.g., 'T' for text/code), and symbol names—allowing them to complement each other in kernel analysis workflows.[21] System.map supports offline examination of kernel builds without needing a running instance, while /proc/kallsyms enables live inspection for immediate diagnostics.
System.map offers advantages in scenarios requiring precise, unrandomized addresses for a specific kernel build, as it avoids the offsets introduced by features like Kernel Address Space Layout Randomization (KASLR) that affect runtime addresses in /proc/kallsyms.[23] Conversely, /proc/kallsyms provides adjusted addresses for relocatable kernels under KASLR, ensuring accuracy for the current boot.[24]
System.map is typically used for verifying symbols during kernel compilation or post-build analysis, whereas /proc/kallsyms is preferred for troubleshooting issues on active systems, such as debugging crashes or tracing module interactions.[21]