Linux Kernel Module – What are Module Macros, Kernel Headers and the modinfo Command
MODULE_LICENSE
MODULE_AUTHOR
MODULE_DESCRIPTION
MODULE_VERSION
modinfo command
linux-headers package
build symlink
In the previous part we saw the overall structure of a Linux Kernel Module (LKM). Now let us go deeper and understand two important things — where the kernel gets its header files from during compilation, and what those MODULE_FOO() lines at the top of every module actually do.
These are small things but they matter a lot when you write real modules or when you are debugging a build failure. Let us take it step by step.
When you write a kernel module, you include header files like #include <linux/module.h> or #include <linux/init.h>. These are NOT the same as normal C library headers. They come from the kernel source tree itself.
But the complete kernel source is huge — several gigabytes. You do not need all of it just to compile a module. You only need a limited portion — the headers and Makefiles. That is exactly what the linux-headers package gives you.
On Ubuntu or Debian systems you can install it like this:
sudo apt install linux-headers-$(uname -r)
Once installed, check what is available under your module directory:
ls -l /lib/modules/$(uname -r)/
You will see something like this (kernel 6.x example on Ubuntu 24.04):
lrwxrwxrwx 1 root root 42 Jan 10 09:00 build -> /usr/src/linux-headers-6.8.0-51-generic/
drwxr-xr-x 2 root root 4096 Jan 10 09:00 initrd/
drwxr-xr-x 3 root root 4096 Jan 10 09:00 kernel/
drwxr-xr-x 2 root root 4096 Jan 10 09:00 modules.dep
...
The important thing here is the build symlink. It points to the kernel headers directory under /usr/src/. This is what your Makefile uses to locate header files during compilation.
| Your Module Source | → | Makefile uses KDIR |
#include <linux/module.h> |
↓ | /lib/modules/$(uname -r)/build |
| GCC resolves to → | /usr/src/linux-headers-6.x.x/include/linux/module.h |
So when you write #include <linux/init.h>, the compiler follows the build symlink and finds the actual header file inside /usr/src/linux-headers-<version>/include/linux/init.h.
You can verify this yourself:
ls /usr/src/linux-headers-$(uname -r)/include/linux/init.h
Every kernel module has a set of metadata macros at the top. These look like function calls but they are actually macros that embed information directly into the compiled .ko file. This metadata can be read without loading the module.
Think of it like an ID card for your kernel module. Anyone can read the card without actually “using” the module.
| Macro | What it stores | Example |
|---|---|---|
MODULE_AUTHOR() |
Who wrote this module | "Ravi Kumar <ravi@example.com>" |
MODULE_DESCRIPTION() |
What this module does | "Demo GPIO driver for RPi" |
MODULE_LICENSE() |
Under which license | "GPL" |
MODULE_VERSION() |
Version string | "1.0" |
Here is how they look inside a real module source file:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
MODULE_AUTHOR("Ravi Kumar <ravi@example.com>");
MODULE_DESCRIPTION("A simple demo kernel module");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
This is a simple string that names the person or team who wrote the module. You can have more than one MODULE_AUTHOR() line if there are multiple contributors. It has no effect on how the module runs — it is purely informational.
MODULE_AUTHOR("Alice <alice@linux.org>");
MODULE_AUTHOR("Bob <bob@linux.org>");
A short one-line description of what your module does. This was optional in older kernels, but starting from kernel 6.10, the kernel build system generates a warning if you skip it. In kernel 6.15 and later, it is a hard build error — your module will not compile without it.
Best practice: always include it, even if you are building for an older kernel.
MODULE_DESCRIPTION("Simple character device driver demo");
MODULE_DESCRIPTION() produces a build warning. From kernel 6.15 onwards it is a build error. Always include it.This is the most important macro from the kernel’s point of view. The kernel uses this to decide whether your module has access to GPL-only kernel symbols.
If you omit it, the build will fail — the kernel build tool called modpost enforces this.
The two most common values:
| License String | Access to GPL symbols | Kernel taint flag |
|---|---|---|
"GPL" |
✅ Full access | No taint — kernel remains clean |
"GPL v2" |
✅ Full access | No taint |
"Proprietary" |
❌ No GPL symbols | ⚠️ Kernel gets TAINT_PROPRIETARY_MODULE flag |
Other valid strings: "Dual BSD/GPL", "Dual MIT/GPL", "Dual MPL/GPL".
For learning and open-source development, always use "GPL".
MODULE_LICENSE("GPL");
A simple version string for your module. There is no fixed format — use whatever makes sense for your project. It is optional but good practice.
MODULE_VERSION("2.1.0");
All this metadata that the macros embed into the .ko file can be read using the modinfo command — even without loading the module into the kernel. This is very useful for system administrators to audit what is running on a machine.
Suppose you compiled a module called hello.ko. Running modinfo on it gives:
modinfo hello.ko
Output will look like:
filename: /path/to/hello.ko
license: GPL
description: A simple demo kernel module
author: Ravi Kumar <ravi@example.com>
version: 1.0
srcversion: A1B2C3D4E5F
depends:
name: hello
vermagic: 6.8.0-51-generic SMP preempt mod_unload
Notice the vermagic field — this is the kernel version the module was built for. If you try to load a module built for a different kernel version, it will fail with a version mismatch error. This protects the system from loading incompatible modules.
You can also run modinfo on already-installed system modules:
# Read metadata of the bluetooth module
modinfo bluetooth
# Read only the license field
modinfo -F license bluetooth
# Read only the description
modinfo -F description bluetooth
In products and enterprise environments, teams often run modinfo with grep on all installed modules to audit licenses — this helps legal teams ensure GPL compliance across a product.
Here is a clean template that follows all the best practices for kernel 6.x. Notice the SPDX identifier at the top — this is the preferred way to express file licensing in modern kernel code:
// SPDX-License-Identifier: GPL-2.0
/*
* hello_mod.c - A simple Hello World kernel module
* Updated for Linux Kernel 6.x
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
MODULE_AUTHOR("Your Name <your@email.com>");
MODULE_DESCRIPTION("Hello World - Demo LKM for Linux 6.x");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
static int __init hello_init(void)
{
pr_info("hello_mod: module loaded successfully\n");
return 0;
}
static void __exit hello_exit(void)
{
pr_info("hello_mod: module removed\n");
}
module_init(hello_init);
module_exit(hello_exit);
// SPDX-License-Identifier: GPL-2.0) and MODULE_LICENSE("GPL") serve different purposes. SPDX is for file-level license tracking tools. MODULE_LICENSE() is what the kernel actually checks at runtime. You need both.Answer: MODULE_LICENSE() tells the kernel what license this module is released under. It is mandatory — the build will fail without it. If the license is not a GPL-compatible string, the kernel sets a taint flag when the module is loaded, indicating the kernel is running with proprietary code. GPL-licensed modules get full access to all exported kernel symbols including those exported with EXPORT_SYMBOL_GPL().
Answer: The kernel checks the vermagic field embedded in the .ko file. If it does not match the running kernel, the module load fails with an error like ERROR: could not insert module: Invalid module format. This prevents binary incompatibility and system crashes from mismatched ABIs.
Answer: The Makefile points to KDIR = /lib/modules/$(uname -r)/build. This is a symbolic link that points to the installed kernel headers directory, typically /usr/src/linux-headers-<version>/. The compiler resolves all #include <linux/...> paths from within this directory.
Answer: Yes. You can repeat MODULE_AUTHOR() as many times as needed. Each call adds another author: entry to the module’s .modinfo section. All entries are visible in the modinfo output.
Answer: In older kernels and up to kernel 6.9, MODULE_DESCRIPTION() was optional. From kernel 6.10 onwards, the kernel build system (modpost) generates a warning if it is missing when you build with make W=1. From kernel 6.15 onwards, a missing MODULE_DESCRIPTION() is a hard build error — the module will not compile at all. Best practice is to always include it.
In the next tutorial we will understand module_init(), module_exit(), the __init and __exit macros, and the important 0/-E return convention used in the Linux kernel.
