Home / Alt manpages / proc_kallsyms(5)

  • proc_kallsyms(5)
  • File format
  • linux

Read /proc/kallsyms and Explain Hidden Addresses

In a few minutes, you will be able to inspect the running kernel's exported symbol table, find a named symbol, and tell the difference between a readable symbol list and a deliberately hidden address. This is a read-only investigation: none of the commands below changes the kernel or loads a module.

Before you start

You need a Linux system with /proc mounted and a shell. The examples were checked on Linux 6.8.0-139-generic with the locally installed manpages package version 6.7-2. Expect the initial read to take a moment on a busy system, because the file can contain many thousands of lines. You do not need root for the examples.

Checkpoint

If /proc/kallsyms does not exist, stop here and check that /proc is mounted. Do not create a regular file at that path: it would hide the real diagnostic rather than fix the kernel interface.

1. Confirm that the interface is present

The manpage describes /proc/kallsyms as the kernel's exported symbol definitions, used by the module tools to dynamically link and bind loadable modules. It has existed since Linux 2.5.71. The older /proc/ksyms name belongs to Linux 1.1.23 through 2.5.47; do not substitute it in a current script.

test -r /proc/kallsyms && echo "readable" || echo "missing or unreadable"
stat -c '%A %U:%G %s %n' /proc/kallsyms

On the checked machine, the result is a world-readable procfs entry owned by root:root, with a reported size of zero. That zero size is normal for a procfs pseudo-file and is not evidence that the table is empty. Test the content by reading it, not by relying on stat.

2. Read a small, useful sample

Each normal line has three whitespace-separated fields: an address, a symbol type, and a symbol name. Use head for a bounded sample so a terminal or log does not fill unexpectedly.

head -n 20 /proc/kallsyms

A typical line resembles this shape:

ffffffff81000000 T example_symbol

The address is the kernel's symbol address as exposed by this interface, the one-letter type comes from the kernel symbol table, and the final field is the symbol name. The exact names and addresses depend on the running kernel, its configuration, loaded modules, architecture and build. Do not copy an address from one boot and treat it as stable on another.

Checkpoint

You have verified that the file can be read and that its output has the expected three-column shape. If the command prints nothing, record the kernel version and investigate the procfs and kernel configuration before writing tooling around the result.

3. Search for a symbol safely

Search the stream instead of making a complete copy. The expression below looks for an exact symbol name in the final column, so a name such as printk is not confused with a longer name that merely contains that text.

awk '$3 == "printk" { print }' /proc/kallsyms

On some kernels there will be no exact match. That is a valid result: the symbol may not be exported, may have been renamed, or may not exist in this particular build. To inspect several related names while keeping the output bounded, use:

awk '$3 ~ /^(printk|vprintk)/ { print }' /proc/kallsyms | head -n 20

Use fixed strings when the name comes from a script argument, or validate that argument before placing it in an awk program. Avoid treating symbol names as shell commands. Reading this file is passive; it does not resolve a symbol for a program and it does not load a module.

4. Explain zero addresses before debugging further

A common, security-conscious result is a table full of addresses such as 0000000000000000. The symbol names can still be present. On the checked host, the first lines had this form, so the interface was working even though the addresses were concealed.

Check the kernel's pointer exposure setting without changing it:

printf 'kptr_restrict='; cat /proc/sys/kernel/kptr_restrict

The kernel documentation defines kptr_restrict as a control on exposing kernel addresses through procfs and related interfaces. A value of 2 replaces the addresses protected by this mechanism with zeroes regardless of privilege. A value of 1 applies an additional privilege and identity check, while 0 uses the less restrictive hashed representation described by the kernel documentation. Other kernel configuration and security controls can also affect what you see.

Security boundary

Do not "fix" a zeroed table by writing a new value to /proc/sys/kernel/kptr_restrict. That is a system-wide security change and requires elevated privileges. It can expose kernel address information that an administrator intentionally chose to conceal. For ordinary symbol-name inspection, zero addresses are sufficient.

5. Use the result in a diagnostic

Capture only the evidence needed for the incident: the kernel release, the pointer restriction value, and a bounded symbol match. This avoids creating a large, sensitive dump in a ticket or log.

uname -r
cat /proc/sys/kernel/kptr_restrict
awk '$3 == "printk" { print }' /proc/kallsyms | head -n 5

Keep the output tied to the same boot. A module may be loaded or unloaded, addresses may be hidden or hashed, and kernel builds do not promise the same symbol set. If a diagnostic tool needs a symbol address, check its documentation for the required privileges and kernel-specific assumptions rather than parsing this file as if it were a stable API.

Done means

  • /proc/kallsyms exists and can be read without elevated privileges.
  • You can recognise the address, type and name columns in a sample line.
  • You can perform an exact symbol-name search without copying the whole table.
  • You checked kptr_restrict before treating zero addresses as a fault.
  • You kept the investigation read-only and did not weaken kernel pointer protections.