Course contentsShow
← Linux
Lesson 1786 of 1,81915. Kernel Concepts for Systems AdministratorsFree lesson

Symbol tables and module linking

You will investigate how kernel modules resolve symbols against the core kernel image.

In this lesson, you will learn how the Linux kernel resolves references to functions and data structures when you load a new kernel module into a running system.

The Symbol Table Mechanism

When a kernel module is compiled, it often requires access to functions residing inside the core kernel image (like printk or kmalloc). However, these addresses aren't fixed; they shift based on your kernel configuration and memory layout. To bridge this gap, the kernel maintains a symbol table—a massive registry that maps human-readable function names to their current memory addresses.

When you run insmod or modprobe, the kernel performs a process similar to dynamic linking in user space. It parses the module's "undefined" symbols—the placeholders where it needs core kernel code—and searches its internal global symbol table to fill in those memory addresses.

Resolving Symbols

The kernel uses two specific structures to manage this:

  1. The Core Kernel Symbol Table: A table of all EXPORT_SYMBOL symbols defined in the main vmlinux image.
  2. Module Symbol Tables: Once a module is loaded, it registers its own exported symbols, allowing other subsequently loaded modules to depend on it.

You can observe this mapping yourself using the /proc/kallsyms file:

# Search for the address of a specific function
grep "printk" /proc/kallsyms

If you ever attempt to load a module that references a symbol not found in this table, the kernel loader will abort the operation with an "Unknown symbol" error. This is a deliberate protection mechanism: if the kernel cannot guarantee the existence of a requested function, it refuses to link the module to prevent undefined behavior or memory corruption.

The Role of Linking

Module linking is essentially a "final adjustment" phase. The kernel loader copies the module’s binary code into kernel memory and then performs a relocation pass. It overwrites the placeholder addresses in the module’s code with the actual absolute addresses found in the kernel's symbol table. This ensures the module’s calls correctly jump into the live, running kernel memory space.

Key Takeaway: The kernel resolves dependencies at load time by mapping undefined module symbols to absolute memory addresses located in the central kernel symbol table.