Linux Kernel Symbols

Kernel symbols are names of functions and variables. Global symbols are those
which are available outside the file they are declared in. Global symbols
in the Linux kernel currently running on a system are available through
`/proc/kallsyms` file. This includes symbols defined inside kernel modules
currently loaded.

Global symbols are of two types:

  1. those explicitly exported through EXPORT_SYMBOL_GPL and EXPORT_SYMBOL
    macros, and
  2. those which are not declared with `static` C keyword and hence visible to
    code which is statically linked with the kernel itself and may be available
    outside the kernel image.

The first type, explicitly exported ones, are denoted with capital letter in
output of `cat /proc/kallsyms` – e.g. T if the symbol is in text section, i.e.
a function name. The second type are denoted with small letter – e.g. t for a
function which isn’t exported via EXPORT_SYMBOL_GPL or EXPORT_SYMBOL.

Inside kernel code, we can access symbols which are exported explicity by
simply using them like other variables, e.g. by calling printk() function.

For global symbols which aren’t explicitly exported, but are still available,
we can attempt to access them by calling kallsyms_lookup_name() function,
defined in kernel/kallsyms.c:

unsigned long kallsyms_lookup_name(const char *name);

This takes symbol name as argument and returns its address in memory, i.e. a
pointer to it. The calling code can dereference the pointer to make use of that
symbol. If the symbol isn’t found, the function returns NULL.


Dynamic Memory Allocation on the Stack

Static memory is memory that is defined at compile time and dynamic memory is memory that is defined at run time. Most programmers are aware that static memory allocation takes place on the stack whereas dynamic memory allocation usually takes place on the heap. What isn’t very common knowledge is the fact that memory can be dynamically allocated on the stack too.  This however comes with its own benefits and caveats.

C programmers will be familiar with malloc() function which is used to dynamically allocate memory on heap. C language also provides alloca() function which dynamically allocates memory on the stack. Usage of alloca() is the same as malloc(). In following sections we will discuss benefits and caveats of alloca().


Two key benefits of alloca() are automatic memory cleanup and better performance. They are explained below.

Memory Cleanup

To prevent memory leaks, every chunk of memory acquired by calling malloc() should be released by calling free(). In contrast, when memory is acquired by calling alloca() it doesn’t need to be released by calling an equivalent of free. This is because before the function starts executing its instructions, the frame pointer is saved. This stage is called function prolog. Then when the function completes execution the frame pointer is restored, effectively freeing up everything that was allocated on the stack for that function. This last stage is called function epilog. Thus prolog and epilog ensure that memory leaks don’t happen.


Memory allocation on the heap involves walking the heap and searching for a suitable chunk of memory to allocate. Memory allocation on stack involves just updating a CPU register. This is the stack pointer register (ESP on 32-bit and RSP on 64-bit CPUs). This makes alloc() much faster than malloc(). Moreover, since stack memory isn’t paged in and out, it doesn’t suffer from page faults. Memory on heap may encounter page faults which will put a small but noticeable drag on performance.


Some would say the caveat of alloca() don’t necessarily outweigh its benefits. However, it is a very important caveat with practical implications. Stack memory is a lot smaller than heap. Therefore, it is more precious. What this means is that:

  1. it should be used sparingly and
  2. it shouldn’t be occupied for too long

Therefore, alloca() is suitable for small amounts of memory used by short-lived functions.


Given its advantages, alloca() is surprisingly little known. That however could be due to legacy reasons from the days when memory came at a premium. Now-a-days with 8GB RAMs in common use and RAM sizes having the potential to go much higher*, certainly programmers can do away with more frequent use dynamic stack memory.


*With the prevalent 64-bit architectures, a program can now potentially address up to 16 exabytes* (i.e. 16 giga-gigabytes) of memory. Most hardware architectures however allow 48-52 bits for addressing rather than all 64 bits. That still means a hefty 281GB – 4503 GB of addressable memory!