How to Fix a Linux Modules Version Error: “Invalid module format”

When I first started writing Linux kernel modules, everything felt new and exciting until I hit the dreaded “Invalid module format” error. If you’ve seen this before, you probably know how frustrating it can be. In this post, I’ll walk you through my journey, starting from a simple kernel module, how I reproduced the error, why it happens, and finally, how I fixed it.

First Example Code

To get started, I wrote the simplest possible kernel module. It doesn’t do anything fancy just prints messages when loaded and unloaded.

hello_module.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Me");
MODULE_DESCRIPTION("Simple Hello World Kernel Module");
MODULE_VERSION("1.0");

static int __init hello_init(void) {
    printk(KERN_INFO "Hello: Kernel module loaded.\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Hello: Kernel module unloaded.\n");
}

module_init(hello_init);
module_exit(hello_exit);

Example Makefile

Here’s the Makefile I used to compile it:

obj-m += hello_module.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

And to run:

make
sudo insmod hello_module.ko
dmesg | tail -n 10

At this point, everything worked fine I saw the “Hello: Kernel module loaded.” message in dmesg

The Error I Saw

When I moved on to a more complex module (oxen_aggregator_module.ko), things broke.
When I tried inserting it:

insmod: ERROR: could not insert module oxen_aggregator_module.ko: Invalid module format

And checking dmesg:

oxen_aggregator_module: version magic '3.17.8-gentoo-r1 SMP mod_unload modversions '
should be '3.17.8-gentoo-r1 SMP mod_unload '

What This Mean

That line gave me the clue:

  • My module was compiled with CONFIG_MODVERSIONS enabled.
  • But my running kernel was compiled without CONFIG_MODVERSIONS.

The kernel checks something called vermagic (a version magic string embedded in modules). If it doesn’t match exactly, it refuses to load the module — hence Invalid module format.

Why CONFIG_MODVERSIONS Matters

So, what’s the deal with this flag?

  • If CONFIG_MODVERSIONS=y, the kernel uses CRC checksums for exported symbols. That means if a kernel symbol changes, your module won’t silently misbehave — the version mismatch will catch it.
  • If it’s disabled, the kernel doesn’t expect CRCs.

That’s why my vermagic didn’t match.

How I Fix It

I had two choices:

Rebuild the Kernel With CONFIG_MODVERSIONS

  1. Go into kernel source: cd /usr/src/linux make menuconfig
  2. Enable: General setup → Enable loadable module support → Module versioning support
  3. Rebuild kernel + modules, reboot, and recompile my external module.

This way, both kernel and modules use symbol versioning consistently.

Build Module Without CONFIG_MODVERSIONS

If I didn’t want to rebuild my kernel, I could disable symbol versioning just for my module.

In my Makefile:

EXTRA_CFLAGS := -UCONFIG_MODVERSIONS

This forced my module to compile without symbol versioning, matching the running kernel.

More Practice Functionality

Once I fixed the mismatch, I wanted to practice more by adding:

  • A parameter input I can pass when loading the module.
  • An exported symbol other modules could use.

practice_module.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Me");
MODULE_DESCRIPTION("Practice Kernel Module with Parameters");
MODULE_VERSION("1.1");

static int number = 1;
module_param(number, int, 0);
MODULE_PARM_DESC(number, "An integer passed to the module");

int my_exported_function(void) {
    return number * 10;
}
EXPORT_SYMBOL(my_exported_function);

static int __init practice_init(void) {
    printk(KERN_INFO "Practice module loaded with number=%d\n", number);
    return 0;
}

static void __exit practice_exit(void) {
    printk(KERN_INFO "Practice module unloaded\n");
}

module_init(practice_init);
module_exit(practice_exit);

Now, I could load it like this:

sudo insmod practice_module.ko number=42
dmesg | tail -n 5

And it would print:

Practice module loaded with number=42

Final Thought

When I first saw the “Invalid module format” error, it felt like a brick wall. But after digging into dmesg and learning about CONFIG_MODVERSIONS and vermagic, everything made sense. The key takeaway is: your module must be built with the same configuration as your kernel. If not, you’ll see this mismatch. By fixing that, not only did my module load successfully, but I also learned how to extend it with parameters and exported symbols which made me more confident in kernel module development.

Related blog posts